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.

509 lines
15KB

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