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.

485 lines
14KB

  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. using namespace QTOLibrary;
  19. using namespace QTOControlLib;
  20. bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle);
  21. static bool isQTAvailable = false;
  22. //==============================================================================
  23. class QuickTimeMovieComponent::Pimpl
  24. {
  25. public:
  26. Pimpl() : dataHandle (0)
  27. {
  28. }
  29. ~Pimpl()
  30. {
  31. clearHandle();
  32. }
  33. void clearHandle()
  34. {
  35. if (dataHandle != 0)
  36. {
  37. DisposeHandle (dataHandle);
  38. dataHandle = 0;
  39. }
  40. }
  41. IQTControlPtr qtControl;
  42. IQTMoviePtr qtMovie;
  43. Handle dataHandle;
  44. };
  45. //==============================================================================
  46. QuickTimeMovieComponent::QuickTimeMovieComponent()
  47. : movieLoaded (false),
  48. controllerVisible (true)
  49. {
  50. pimpl = new Pimpl();
  51. setMouseEventsAllowed (false);
  52. }
  53. QuickTimeMovieComponent::~QuickTimeMovieComponent()
  54. {
  55. closeMovie();
  56. pimpl->qtControl = 0;
  57. deleteControl();
  58. pimpl = nullptr;
  59. }
  60. bool QuickTimeMovieComponent::isQuickTimeAvailable() noexcept
  61. {
  62. if (! isQTAvailable)
  63. isQTAvailable = (InitializeQTML (0) == noErr) && (EnterMovies() == noErr);
  64. return isQTAvailable;
  65. }
  66. //==============================================================================
  67. void QuickTimeMovieComponent::createControlIfNeeded()
  68. {
  69. if (isShowing() && ! isControlCreated())
  70. {
  71. const IID qtIID = __uuidof (QTControl);
  72. if (createControl (&qtIID))
  73. {
  74. const IID qtInterfaceIID = __uuidof (IQTControl);
  75. pimpl->qtControl = (IQTControl*) queryInterface (&qtInterfaceIID);
  76. if (pimpl->qtControl != nullptr)
  77. {
  78. pimpl->qtControl->Release(); // it has one ref too many at this point
  79. pimpl->qtControl->QuickTimeInitialize();
  80. pimpl->qtControl->PutSizing (qtMovieFitsControl);
  81. if (movieFile != File::nonexistent)
  82. loadMovie (movieFile, controllerVisible);
  83. }
  84. }
  85. }
  86. }
  87. bool QuickTimeMovieComponent::isControlCreated() const
  88. {
  89. return isControlOpen();
  90. }
  91. bool QuickTimeMovieComponent::loadMovie (InputStream* movieStream,
  92. const bool isControllerVisible)
  93. {
  94. const ScopedPointer<InputStream> movieStreamDeleter (movieStream);
  95. movieFile = File::nonexistent;
  96. movieLoaded = false;
  97. pimpl->qtMovie = 0;
  98. controllerVisible = isControllerVisible;
  99. createControlIfNeeded();
  100. if (isControlCreated())
  101. {
  102. if (pimpl->qtControl != 0)
  103. {
  104. pimpl->qtControl->Put_MovieHandle (0);
  105. pimpl->clearHandle();
  106. Movie movie;
  107. if (juce_OpenQuickTimeMovieFromStream (movieStream, movie, pimpl->dataHandle))
  108. {
  109. pimpl->qtControl->Put_MovieHandle ((long) (pointer_sized_int) movie);
  110. pimpl->qtMovie = pimpl->qtControl->GetMovie();
  111. if (pimpl->qtMovie != 0)
  112. pimpl->qtMovie->PutMovieControllerType (isControllerVisible ? qtMovieControllerTypeStandard
  113. : qtMovieControllerTypeNone);
  114. }
  115. if (movie == 0)
  116. pimpl->clearHandle();
  117. }
  118. movieLoaded = (pimpl->qtMovie != 0);
  119. }
  120. else
  121. {
  122. // You're trying to open a movie when the control hasn't yet been created, probably because
  123. // you've not yet added this component to a Window and made the whole component hierarchy visible.
  124. jassertfalse;
  125. }
  126. return movieLoaded;
  127. }
  128. void QuickTimeMovieComponent::closeMovie()
  129. {
  130. stop();
  131. movieFile = File::nonexistent;
  132. movieLoaded = false;
  133. pimpl->qtMovie = 0;
  134. if (pimpl->qtControl != 0)
  135. pimpl->qtControl->Put_MovieHandle (0);
  136. pimpl->clearHandle();
  137. }
  138. File QuickTimeMovieComponent::getCurrentMovieFile() const
  139. {
  140. return movieFile;
  141. }
  142. bool QuickTimeMovieComponent::isMovieOpen() const
  143. {
  144. return movieLoaded;
  145. }
  146. double QuickTimeMovieComponent::getMovieDuration() const
  147. {
  148. if (pimpl->qtMovie != 0)
  149. return pimpl->qtMovie->GetDuration() / (double) pimpl->qtMovie->GetTimeScale();
  150. return 0.0;
  151. }
  152. void QuickTimeMovieComponent::getMovieNormalSize (int& width, int& height) const
  153. {
  154. if (pimpl->qtMovie != 0)
  155. {
  156. struct QTRECT r = pimpl->qtMovie->GetNaturalRect();
  157. width = r.right - r.left;
  158. height = r.bottom - r.top;
  159. }
  160. else
  161. {
  162. width = height = 0;
  163. }
  164. }
  165. void QuickTimeMovieComponent::play()
  166. {
  167. if (pimpl->qtMovie != 0)
  168. pimpl->qtMovie->Play();
  169. }
  170. void QuickTimeMovieComponent::stop()
  171. {
  172. if (pimpl->qtMovie != 0)
  173. pimpl->qtMovie->Stop();
  174. }
  175. bool QuickTimeMovieComponent::isPlaying() const
  176. {
  177. return pimpl->qtMovie != 0 && pimpl->qtMovie->GetRate() != 0.0f;
  178. }
  179. void QuickTimeMovieComponent::setPosition (const double seconds)
  180. {
  181. if (pimpl->qtMovie != 0)
  182. pimpl->qtMovie->PutTime ((long) (seconds * pimpl->qtMovie->GetTimeScale()));
  183. }
  184. double QuickTimeMovieComponent::getPosition() const
  185. {
  186. if (pimpl->qtMovie != 0)
  187. return pimpl->qtMovie->GetTime() / (double) pimpl->qtMovie->GetTimeScale();
  188. return 0.0;
  189. }
  190. void QuickTimeMovieComponent::setSpeed (const float newSpeed)
  191. {
  192. if (pimpl->qtMovie != 0)
  193. pimpl->qtMovie->PutRate (newSpeed);
  194. }
  195. void QuickTimeMovieComponent::setMovieVolume (const float newVolume)
  196. {
  197. if (pimpl->qtMovie != 0)
  198. {
  199. pimpl->qtMovie->PutAudioVolume (newVolume);
  200. pimpl->qtMovie->PutAudioMute (newVolume <= 0);
  201. }
  202. }
  203. float QuickTimeMovieComponent::getMovieVolume() const
  204. {
  205. if (pimpl->qtMovie != 0)
  206. return pimpl->qtMovie->GetAudioVolume();
  207. return 0.0f;
  208. }
  209. void QuickTimeMovieComponent::setLooping (const bool shouldLoop)
  210. {
  211. if (pimpl->qtMovie != 0)
  212. pimpl->qtMovie->PutLoop (shouldLoop);
  213. }
  214. bool QuickTimeMovieComponent::isLooping() const
  215. {
  216. return pimpl->qtMovie != 0 && pimpl->qtMovie->GetLoop();
  217. }
  218. bool QuickTimeMovieComponent::isControllerVisible() const
  219. {
  220. return controllerVisible;
  221. }
  222. void QuickTimeMovieComponent::parentHierarchyChanged()
  223. {
  224. createControlIfNeeded();
  225. QTCompBaseClass::parentHierarchyChanged();
  226. }
  227. void QuickTimeMovieComponent::visibilityChanged()
  228. {
  229. createControlIfNeeded();
  230. QTCompBaseClass::visibilityChanged();
  231. }
  232. void QuickTimeMovieComponent::paint (Graphics& g)
  233. {
  234. if (! isControlCreated())
  235. g.fillAll (Colours::black);
  236. }
  237. //==============================================================================
  238. static Handle createHandleDataRef (Handle dataHandle, const char* fileName)
  239. {
  240. Handle dataRef = 0;
  241. OSStatus err = PtrToHand (&dataHandle, &dataRef, sizeof (Handle));
  242. if (err == noErr)
  243. {
  244. Str255 suffix;
  245. #if JUCE_MSVC
  246. #pragma warning (push)
  247. #pragma warning (disable: 4244 4996)
  248. #endif
  249. suffix[0] = strlen (fileName);
  250. strncpy ((char*) suffix + 1, fileName, 128);
  251. #if JUCE_MSVC
  252. #pragma warning (pop)
  253. #endif
  254. err = PtrAndHand (suffix, dataRef, suffix[0] + 1);
  255. if (err == noErr)
  256. {
  257. long atoms[3];
  258. atoms[0] = EndianU32_NtoB (3 * sizeof (long));
  259. atoms[1] = EndianU32_NtoB (kDataRefExtensionMacOSFileType);
  260. atoms[2] = EndianU32_NtoB (MovieFileType);
  261. err = PtrAndHand (atoms, dataRef, 3 * sizeof (long));
  262. if (err == noErr)
  263. return dataRef;
  264. }
  265. DisposeHandle (dataRef);
  266. }
  267. return 0;
  268. }
  269. static CFStringRef juceStringToCFString (const String& s)
  270. {
  271. return CFStringCreateWithCString (kCFAllocatorDefault, s.toUTF8(), kCFStringEncodingUTF8);
  272. }
  273. static bool openMovie (QTNewMoviePropertyElement* props, int prop, Movie& movie)
  274. {
  275. Boolean trueBool = true;
  276. props[prop].propClass = kQTPropertyClass_MovieInstantiation;
  277. props[prop].propID = kQTMovieInstantiationPropertyID_DontResolveDataRefs;
  278. props[prop].propValueSize = sizeof (trueBool);
  279. props[prop].propValueAddress = &trueBool;
  280. ++prop;
  281. props[prop].propClass = kQTPropertyClass_MovieInstantiation;
  282. props[prop].propID = kQTMovieInstantiationPropertyID_AsyncOK;
  283. props[prop].propValueSize = sizeof (trueBool);
  284. props[prop].propValueAddress = &trueBool;
  285. ++prop;
  286. Boolean isActive = true;
  287. props[prop].propClass = kQTPropertyClass_NewMovieProperty;
  288. props[prop].propID = kQTNewMoviePropertyID_Active;
  289. props[prop].propValueSize = sizeof (isActive);
  290. props[prop].propValueAddress = &isActive;
  291. ++prop;
  292. MacSetPort (0);
  293. jassert (prop <= 5);
  294. OSStatus err = NewMovieFromProperties (prop, props, 0, 0, &movie);
  295. return err == noErr;
  296. }
  297. bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle)
  298. {
  299. if (input == nullptr)
  300. return false;
  301. dataHandle = 0;
  302. bool ok = false;
  303. QTNewMoviePropertyElement props[5] = { 0 };
  304. int prop = 0;
  305. DataReferenceRecord dr;
  306. props[prop].propClass = kQTPropertyClass_DataLocation;
  307. props[prop].propID = kQTDataLocationPropertyID_DataReference;
  308. props[prop].propValueSize = sizeof (dr);
  309. props[prop].propValueAddress = &dr;
  310. ++prop;
  311. FileInputStream* const fin = dynamic_cast <FileInputStream*> (input);
  312. if (fin != nullptr)
  313. {
  314. CFStringRef filePath = juceStringToCFString (fin->getFile().getFullPathName());
  315. QTNewDataReferenceFromFullPathCFString (filePath, (QTPathStyle) kQTNativeDefaultPathStyle, 0,
  316. &dr.dataRef, &dr.dataRefType);
  317. ok = openMovie (props, prop, movie);
  318. DisposeHandle (dr.dataRef);
  319. CFRelease (filePath);
  320. }
  321. else
  322. {
  323. // sanity-check because this currently needs to load the whole stream into memory..
  324. jassert (input->getTotalLength() < 50 * 1024 * 1024);
  325. dataHandle = NewHandle ((Size) input->getTotalLength());
  326. HLock (dataHandle);
  327. // read the entire stream into memory - this is a pain, but can't get it to work
  328. // properly using a custom callback to supply the data.
  329. input->read (*dataHandle, (int) input->getTotalLength());
  330. HUnlock (dataHandle);
  331. // different types to get QT to try. (We should really be a bit smarter here by
  332. // working out in advance which one the stream contains, rather than just trying
  333. // each one)
  334. const char* const suffixesToTry[] = { "\04.mov", "\04.mp3",
  335. "\04.avi", "\04.m4a" };
  336. for (int i = 0; i < numElementsInArray (suffixesToTry) && ! ok; ++i)
  337. {
  338. /* // this fails for some bizarre reason - it can be bodged to work with
  339. // movies, but can't seem to do it for other file types..
  340. QTNewMovieUserProcRecord procInfo;
  341. procInfo.getMovieUserProc = NewGetMovieUPP (readMovieStreamProc);
  342. procInfo.getMovieUserProcRefcon = this;
  343. procInfo.defaultDataRef.dataRef = dataRef;
  344. procInfo.defaultDataRef.dataRefType = HandleDataHandlerSubType;
  345. props[prop].propClass = kQTPropertyClass_DataLocation;
  346. props[prop].propID = kQTDataLocationPropertyID_MovieUserProc;
  347. props[prop].propValueSize = sizeof (procInfo);
  348. props[prop].propValueAddress = (void*) &procInfo;
  349. ++prop; */
  350. dr.dataRef = createHandleDataRef (dataHandle, suffixesToTry [i]);
  351. dr.dataRefType = HandleDataHandlerSubType;
  352. ok = openMovie (props, prop, movie);
  353. DisposeHandle (dr.dataRef);
  354. }
  355. }
  356. return ok;
  357. }
  358. bool QuickTimeMovieComponent::loadMovie (const File& movieFile_,
  359. const bool isControllerVisible)
  360. {
  361. const bool ok = loadMovie (static_cast <InputStream*> (movieFile_.createInputStream()), isControllerVisible);
  362. movieFile = movieFile_;
  363. return ok;
  364. }
  365. bool QuickTimeMovieComponent::loadMovie (const URL& movieURL,
  366. const bool isControllerVisible)
  367. {
  368. return loadMovie (static_cast <InputStream*> (movieURL.createInputStream (false)), isControllerVisible);
  369. }
  370. void QuickTimeMovieComponent::goToStart()
  371. {
  372. setPosition (0.0);
  373. }
  374. void QuickTimeMovieComponent::setBoundsWithCorrectAspectRatio (const Rectangle<int>& spaceToFitWithin,
  375. const RectanglePlacement& placement)
  376. {
  377. int normalWidth, normalHeight;
  378. getMovieNormalSize (normalWidth, normalHeight);
  379. const Rectangle<int> normalSize (0, 0, normalWidth, normalHeight);
  380. if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty()))
  381. setBounds (placement.appliedTo (normalSize, spaceToFitWithin));
  382. else
  383. setBounds (spaceToFitWithin);
  384. }