The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
9.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #if JUCE_QUICKTIME
  19. struct NonInterceptingQTMovieViewClass : public ObjCClass <QTMovieView>
  20. {
  21. NonInterceptingQTMovieViewClass() : ObjCClass <QTMovieView> ("JUCEQTMovieView_")
  22. {
  23. addMethod (@selector (hitTest:), hitTest, "@@:", @encode (NSPoint));
  24. addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "c@:@");
  25. registerClass();
  26. }
  27. private:
  28. static NSView* hitTest (id self, SEL, NSPoint point)
  29. {
  30. if (! [(QTMovieView*) self isControllerVisible])
  31. return nil;
  32. objc_super s = { self, [QTMovieView class] };
  33. return objc_msgSendSuper (&s, @selector (hitTest:), point);
  34. }
  35. static BOOL acceptsFirstMouse (id, SEL, NSEvent*)
  36. {
  37. return YES;
  38. }
  39. };
  40. //==============================================================================
  41. #define theMovie (static_cast <QTMovie*> (movie))
  42. //==============================================================================
  43. QuickTimeMovieComponent::QuickTimeMovieComponent()
  44. : movie (0)
  45. {
  46. setOpaque (true);
  47. setVisible (true);
  48. static NonInterceptingQTMovieViewClass cls;
  49. QTMovieView* view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)];
  50. setView (view);
  51. [view setNextResponder: [view superview]];
  52. [view setWantsLayer: YES]; // prevents the view failing to redraw correctly when paused.
  53. [view release];
  54. }
  55. QuickTimeMovieComponent::~QuickTimeMovieComponent()
  56. {
  57. closeMovie();
  58. setView (nil);
  59. }
  60. bool QuickTimeMovieComponent::isQuickTimeAvailable() noexcept
  61. {
  62. return true;
  63. }
  64. static QTMovie* openMovieFromStream (InputStream* movieStream, File& movieFile)
  65. {
  66. // unfortunately, QTMovie objects can only be created on the main thread..
  67. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  68. QTMovie* movie = nil;
  69. FileInputStream* const fin = dynamic_cast <FileInputStream*> (movieStream);
  70. if (fin != nullptr)
  71. {
  72. movieFile = fin->getFile();
  73. movie = [QTMovie movieWithFile: juceStringToNS (movieFile.getFullPathName())
  74. error: nil];
  75. }
  76. else
  77. {
  78. MemoryBlock temp;
  79. movieStream->readIntoMemoryBlock (temp);
  80. const char* const suffixesToTry[] = { ".mov", ".mp3", ".avi", ".m4a" };
  81. for (int i = 0; i < numElementsInArray (suffixesToTry); ++i)
  82. {
  83. movie = [QTMovie movieWithDataReference: [QTDataReference dataReferenceWithReferenceToData: [NSData dataWithBytes: temp.getData()
  84. length: temp.getSize()]
  85. name: [NSString stringWithUTF8String: suffixesToTry[i]]
  86. MIMEType: nsEmptyString()]
  87. error: nil];
  88. if (movie != 0)
  89. break;
  90. }
  91. }
  92. return movie;
  93. }
  94. bool QuickTimeMovieComponent::loadMovie (const File& movieFile_,
  95. const bool isControllerVisible_)
  96. {
  97. return loadMovie ((InputStream*) movieFile_.createInputStream(), isControllerVisible_);
  98. }
  99. bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream,
  100. const bool controllerVisible_)
  101. {
  102. closeMovie();
  103. if (getPeer() == nullptr)
  104. {
  105. // To open a movie, this component must be visible inside a functioning window, so that
  106. // the QT control can be assigned to the window.
  107. jassertfalse;
  108. return false;
  109. }
  110. movie = openMovieFromStream (movieStream, movieFile);
  111. [theMovie retain];
  112. QTMovieView* view = (QTMovieView*) getView();
  113. [view setMovie: theMovie];
  114. [view setControllerVisible: controllerVisible_];
  115. setLooping (looping);
  116. return movie != nil;
  117. }
  118. bool QuickTimeMovieComponent::loadMovie (const URL& movieURL,
  119. const bool isControllerVisible_)
  120. {
  121. // unfortunately, QTMovie objects can only be created on the main thread..
  122. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  123. closeMovie();
  124. if (getPeer() == nullptr)
  125. {
  126. // To open a movie, this component must be visible inside a functioning window, so that
  127. // the QT control can be assigned to the window.
  128. jassertfalse;
  129. return false;
  130. }
  131. NSURL* url = [NSURL URLWithString: juceStringToNS (movieURL.toString (true))];
  132. NSError* err;
  133. if ([QTMovie canInitWithURL: url])
  134. movie = [QTMovie movieWithURL: url error: &err];
  135. [theMovie retain];
  136. QTMovieView* view = (QTMovieView*) getView();
  137. [view setMovie: theMovie];
  138. [view setControllerVisible: controllerVisible];
  139. setLooping (looping);
  140. return movie != nil;
  141. }
  142. void QuickTimeMovieComponent::closeMovie()
  143. {
  144. stop();
  145. QTMovieView* view = (QTMovieView*) getView();
  146. [view setMovie: nil];
  147. [theMovie release];
  148. movie = 0;
  149. movieFile = File::nonexistent;
  150. }
  151. bool QuickTimeMovieComponent::isMovieOpen() const
  152. {
  153. return movie != nil;
  154. }
  155. File QuickTimeMovieComponent::getCurrentMovieFile() const
  156. {
  157. return movieFile;
  158. }
  159. void QuickTimeMovieComponent::play()
  160. {
  161. [theMovie play];
  162. }
  163. void QuickTimeMovieComponent::stop()
  164. {
  165. [theMovie stop];
  166. }
  167. bool QuickTimeMovieComponent::isPlaying() const
  168. {
  169. return movie != 0 && [theMovie rate] != 0;
  170. }
  171. void QuickTimeMovieComponent::setPosition (const double seconds)
  172. {
  173. if (movie != 0)
  174. {
  175. QTTime t;
  176. t.timeValue = (uint64) (100000.0 * seconds);
  177. t.timeScale = 100000;
  178. t.flags = 0;
  179. [theMovie setCurrentTime: t];
  180. }
  181. }
  182. double QuickTimeMovieComponent::getPosition() const
  183. {
  184. if (movie == 0)
  185. return 0.0;
  186. QTTime t = [theMovie currentTime];
  187. return t.timeValue / (double) t.timeScale;
  188. }
  189. void QuickTimeMovieComponent::setSpeed (const float newSpeed)
  190. {
  191. [theMovie setRate: newSpeed];
  192. }
  193. double QuickTimeMovieComponent::getMovieDuration() const
  194. {
  195. if (movie == 0)
  196. return 0.0;
  197. QTTime t = [theMovie duration];
  198. return t.timeValue / (double) t.timeScale;
  199. }
  200. void QuickTimeMovieComponent::setLooping (const bool shouldLoop)
  201. {
  202. looping = shouldLoop;
  203. [theMovie setAttribute: [NSNumber numberWithBool: shouldLoop]
  204. forKey: QTMovieLoopsAttribute];
  205. }
  206. bool QuickTimeMovieComponent::isLooping() const
  207. {
  208. return looping;
  209. }
  210. void QuickTimeMovieComponent::setMovieVolume (const float newVolume)
  211. {
  212. [theMovie setVolume: newVolume];
  213. }
  214. float QuickTimeMovieComponent::getMovieVolume() const
  215. {
  216. return movie != 0 ? [theMovie volume] : 0.0f;
  217. }
  218. void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const
  219. {
  220. width = 0;
  221. height = 0;
  222. if (movie != 0)
  223. {
  224. NSSize s = [[theMovie attributeForKey: QTMovieNaturalSizeAttribute] sizeValue];
  225. width = (int) s.width;
  226. height = (int) s.height;
  227. }
  228. }
  229. void QuickTimeMovieComponent::paint (Graphics& g)
  230. {
  231. if (movie == 0)
  232. g.fillAll (Colours::black);
  233. }
  234. bool QuickTimeMovieComponent::isControllerVisible() const
  235. {
  236. return controllerVisible;
  237. }
  238. //==============================================================================
  239. void QuickTimeMovieComponent::goToStart()
  240. {
  241. setPosition (0.0);
  242. }
  243. void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle<int>& spaceToFitWithin,
  244. const RectanglePlacement& placement)
  245. {
  246. int normalWidth, normalHeight;
  247. getMovieNormalSize (normalWidth, normalHeight);
  248. const Rectangle<int> normalSize (0, 0, normalWidth, normalHeight);
  249. if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty()))
  250. setBounds (placement.appliedTo (normalSize, spaceToFitWithin));
  251. else
  252. setBounds (spaceToFitWithin);
  253. }
  254. //==============================================================================
  255. #if ! (JUCE_MAC && JUCE_64BIT)
  256. bool juce_OpenQuickTimeMovieFromStream (InputStream* movieStream, Movie& result, Handle& dataHandle)
  257. {
  258. if (movieStream == nullptr)
  259. return false;
  260. File file;
  261. QTMovie* movie = openMovieFromStream (movieStream, file);
  262. if (movie != nil)
  263. result = [movie quickTimeMovie];
  264. return movie != nil;
  265. }
  266. #endif
  267. #endif