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.

342 lines
9.5KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #if JUCE_QUICKTIME
  18. struct NonInterceptingQTMovieViewClass : public ObjCClass <QTMovieView>
  19. {
  20. NonInterceptingQTMovieViewClass() : ObjCClass <QTMovieView> ("JUCEQTMovieView_")
  21. {
  22. addMethod (@selector (hitTest:), hitTest, "@@:", @encode (NSPoint));
  23. addMethod (@selector (acceptsFirstMouse:), acceptsFirstMouse, "c@:@");
  24. registerClass();
  25. }
  26. private:
  27. static NSView* hitTest (id self, SEL, NSPoint point)
  28. {
  29. if (! [(QTMovieView*) self isControllerVisible])
  30. return nil;
  31. objc_super s = { self, [QTMovieView class] };
  32. return objc_msgSendSuper (&s, @selector (hitTest:), point);
  33. }
  34. static BOOL acceptsFirstMouse (id, SEL, NSEvent*)
  35. {
  36. return YES;
  37. }
  38. };
  39. //==============================================================================
  40. #define theMovie (static_cast <QTMovie*> (movie))
  41. //==============================================================================
  42. QuickTimeMovieComponent::QuickTimeMovieComponent()
  43. : movie (0)
  44. {
  45. setOpaque (true);
  46. setVisible (true);
  47. static NonInterceptingQTMovieViewClass cls;
  48. QTMovieView* view = [cls.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)];
  49. setView (view);
  50. [view setNextResponder: [view superview]];
  51. [view setWantsLayer: YES]; // prevents the view failing to redraw correctly when paused.
  52. [view release];
  53. }
  54. QuickTimeMovieComponent::~QuickTimeMovieComponent()
  55. {
  56. closeMovie();
  57. setView (nil);
  58. }
  59. bool QuickTimeMovieComponent::isQuickTimeAvailable() noexcept
  60. {
  61. return true;
  62. }
  63. static QTMovie* openMovieFromStream (InputStream* movieStream, File& movieFile)
  64. {
  65. // unfortunately, QTMovie objects can only be created on the main thread..
  66. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  67. QTMovie* movie = nil;
  68. if (FileInputStream* const fin = dynamic_cast <FileInputStream*> (movieStream))
  69. {
  70. movieFile = fin->getFile();
  71. movie = [QTMovie movieWithFile: juceStringToNS (movieFile.getFullPathName())
  72. error: nil];
  73. }
  74. else
  75. {
  76. MemoryBlock temp;
  77. movieStream->readIntoMemoryBlock (temp);
  78. static const char* const suffixesToTry[] = { ".mov", ".mp3", ".avi", ".m4a" };
  79. for (int i = 0; i < numElementsInArray (suffixesToTry); ++i)
  80. {
  81. movie = [QTMovie movieWithDataReference: [QTDataReference dataReferenceWithReferenceToData: [NSData dataWithBytes: temp.getData()
  82. length: temp.getSize()]
  83. name: [NSString stringWithUTF8String: suffixesToTry[i]]
  84. MIMEType: nsEmptyString()]
  85. error: nil];
  86. if (movie != 0)
  87. break;
  88. }
  89. }
  90. return movie;
  91. }
  92. bool QuickTimeMovieComponent::loadMovie (const File& file, const bool showController)
  93. {
  94. return loadMovie (file.createInputStream(), showController);
  95. }
  96. bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream, const bool showController)
  97. {
  98. const ScopedPointer<InputStream> movieStreamDeleter (movieStream);
  99. closeMovie();
  100. if (getPeer() == nullptr)
  101. {
  102. // To open a movie, this component must be visible inside a functioning window, so that
  103. // the QT control can be assigned to the window.
  104. jassertfalse;
  105. return false;
  106. }
  107. if (movieStream == nullptr)
  108. return false;
  109. movie = openMovieFromStream (movieStream, movieFile);
  110. [theMovie retain];
  111. QTMovieView* view = (QTMovieView*) getView();
  112. [view setMovie: theMovie];
  113. controllerVisible = showController;
  114. [view setControllerVisible: controllerVisible];
  115. setLooping (looping);
  116. return movie != nil;
  117. }
  118. bool QuickTimeMovieComponent::loadMovie (const URL& movieURL, const bool showController)
  119. {
  120. // unfortunately, QTMovie objects can only be created on the main thread..
  121. jassert (MessageManager::getInstance()->isThisTheMessageThread());
  122. closeMovie();
  123. if (getPeer() == nullptr)
  124. {
  125. // To open a movie, this component must be visible inside a functioning window, so that
  126. // the QT control can be assigned to the window.
  127. jassertfalse;
  128. return false;
  129. }
  130. NSURL* url = [NSURL URLWithString: juceStringToNS (movieURL.toString (true))];
  131. NSError* err;
  132. if ([QTMovie canInitWithURL: url])
  133. movie = [QTMovie movieWithURL: url error: &err];
  134. [theMovie retain];
  135. QTMovieView* view = (QTMovieView*) getView();
  136. [view setMovie: theMovie];
  137. controllerVisible = showController;
  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 = (long long) (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. RectanglePlacement placement)
  245. {
  246. int normalWidth, normalHeight;
  247. getMovieNormalSize (normalWidth, normalHeight);
  248. const Rectangle<int> normalSize (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&)
  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