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.

518 lines
15KB

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