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.

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