2 * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "QTMovieTask.h"
30 #include "QTMovieWinTimer.h"
35 #include <QuickTimeComponents.h>
36 #include <wtf/Assertions.h>
37 #include <wtf/Noncopyable.h>
38 #include <wtf/Vector.h>
42 static const long minimumQuickTimeVersion = 0x07300000; // 7.3
44 static const long closedCaptionTrackType = 'clcp';
45 static const long subTitleTrackType = 'sbtl';
46 static const long mpeg4ObjectDescriptionTrackType = 'odsm';
47 static const long mpeg4SceneDescriptionTrackType = 'sdsm';
48 static const long closedCaptionDisplayPropertyID = 'disp';
49 static LPCTSTR fullscreenQTMoviePointerProp = TEXT("fullscreenQTMoviePointer");
51 // Resizing GWorlds is slow, give them a minimum size so size of small
52 // videos can be animated smoothly
53 static const int cGWorldMinWidth = 640;
54 static const int cGWorldMinHeight = 360;
56 static const float cNonContinuousTimeChange = 0.2f;
63 static Vector<CFStringRef>* gSupportedTypes = 0;
64 static SInt32 quickTimeVersion = 0;
66 class QTMoviePrivate : public Noncopyable, public QTMovieTaskClient {
74 void createMovieController();
75 void cacheMovieScale();
79 MovieController m_movieController;
82 Vector<QTMovieClient*> m_clients;
86 float m_lastMediaTime;
87 double m_lastLoadStateCheckTime;
92 float m_widthScaleFactor;
93 float m_heightScaleFactor;
94 CFURLRef m_currentURL;
95 float m_timeToRestore;
96 float m_rateToRestore;
102 QTMoviePrivate::QTMoviePrivate()
105 , m_movieController(0)
111 , m_lastLoadStateCheckTime(0)
116 , m_widthScaleFactor(1)
117 , m_heightScaleFactor(1)
119 , m_timeToRestore(-1.0f)
120 , m_rateToRestore(-1.0f)
123 , m_scaleCached(false)
128 QTMoviePrivate::~QTMoviePrivate()
131 if (m_movieController)
132 DisposeMovieController(m_movieController);
134 DisposeMovie(m_movie);
136 CFRelease(m_currentURL);
139 void QTMoviePrivate::startTask()
142 QTMovieTask::sharedTask()->addTaskClient(this);
145 QTMovieTask::sharedTask()->updateTaskTimer();
148 void QTMoviePrivate::endTask()
151 QTMovieTask::sharedTask()->removeTaskClient(this);
154 QTMovieTask::sharedTask()->updateTaskTimer();
157 void QTMoviePrivate::task()
162 if (m_movieController)
163 MCIdle(m_movieController);
165 MoviesTask(m_movie, 0);
168 // GetMovieLoadState documentation says that you should not call it more often than every quarter of a second.
169 if (systemTime() >= m_lastLoadStateCheckTime + 0.25 || m_loadError) {
170 // If load fails QT's load state is QTMovieLoadStateComplete.
171 // This is different from QTKit API and seems strange.
172 long loadState = m_loadError ? QTMovieLoadStateError : GetMovieLoadState(m_movie);
173 if (loadState != m_loadState) {
174 // we only need to erase the movie gworld when the load state changes to loaded while it
175 // is visible as the gworld is destroyed/created when visibility changes
176 bool shouldRestorePlaybackState = false;
177 bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded;
178 m_loadState = loadState;
179 if (movieNewlyPlayable) {
181 shouldRestorePlaybackState = true;
184 if (!m_movieController && m_loadState >= QTMovieLoadStateLoaded)
185 createMovieController();
187 for (size_t i = 0; i < m_clients.size(); ++i)
188 m_clients[i]->movieLoadStateChanged(m_movieWin);
190 if (shouldRestorePlaybackState && m_timeToRestore != -1.0f) {
191 m_movieWin->setCurrentTime(m_timeToRestore);
192 m_timeToRestore = -1.0f;
193 m_movieWin->setRate(m_rateToRestore);
194 m_rateToRestore = -1.0f;
202 m_lastLoadStateCheckTime = systemTime();
205 bool ended = !!IsMovieDone(m_movie);
206 if (ended != m_ended) {
209 for (size_t i = 0; i < m_clients.size(); ++i)
210 m_clients[i]->movieEnded(m_movieWin);
214 float time = m_movieWin->currentTime();
215 if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) {
217 for (size_t i = 0; i < m_clients.size(); ++i)
218 m_clients[i]->movieTimeChanged(m_movieWin);
220 m_lastMediaTime = time;
225 QTMovieTask::sharedTask()->updateTaskTimer();
228 void QTMoviePrivate::createMovieController()
236 if (m_movieController)
237 DisposeMovieController(m_movieController);
239 GetMovieBox(m_movie, &bounds);
240 flags = mcTopLeftMovie | mcNotVisible;
241 m_movieController = NewMovieController(m_movie, &bounds, flags);
242 if (!m_movieController)
245 // Disable automatic looping.
246 MCDoAction(m_movieController, mcActionSetLooping, 0);
249 void QTMoviePrivate::cacheMovieScale()
254 GetMovieNaturalBoundsRect(m_movie, &naturalRect);
255 GetMovieBox(m_movie, &initialRect);
257 float naturalWidth = naturalRect.right - naturalRect.left;
258 float naturalHeight = naturalRect.bottom - naturalRect.top;
261 m_widthScaleFactor = (initialRect.right - initialRect.left) / naturalWidth;
263 m_heightScaleFactor = (initialRect.bottom - initialRect.top) / naturalHeight;
265 m_scaleCached = true;
269 QTMovie::QTMovie(QTMovieClient* client)
270 : m_private(new QTMoviePrivate())
272 m_private->m_movieWin = this;
274 m_private->m_clients.append(client);
275 initializeQuickTime();
283 void QTMovie::disableComponent(uint32_t cd[5])
285 ComponentDescription nullDesc = {'null', 'base', kAppleManufacturer, 0, 0};
286 Component nullComp = FindNextComponent(0, &nullDesc);
287 Component disabledComp = 0;
289 while (disabledComp = FindNextComponent(disabledComp, (ComponentDescription*)&cd[0]))
290 CaptureComponent(disabledComp, nullComp);
293 void QTMovie::addClient(QTMovieClient* client)
296 m_private->m_clients.append(client);
299 void QTMovie::removeClient(QTMovieClient* client)
301 size_t indexOfClient = m_private->m_clients.find(client);
302 if (indexOfClient != notFound)
303 m_private->m_clients.remove(indexOfClient);
308 m_private->m_timeToRestore = -1.0f;
310 if (m_private->m_movieController)
311 MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)GetMoviePreferredRate(m_private->m_movie));
313 StartMovie(m_private->m_movie);
314 m_private->startTask();
317 void QTMovie::pause()
319 m_private->m_timeToRestore = -1.0f;
321 if (m_private->m_movieController)
322 MCDoAction(m_private->m_movieController, mcActionPlay, 0);
324 StopMovie(m_private->m_movie);
325 QTMovieTask::sharedTask()->updateTaskTimer();
328 float QTMovie::rate() const
330 if (!m_private->m_movie)
332 return FixedToFloat(GetMovieRate(m_private->m_movie));
335 void QTMovie::setRate(float rate)
337 if (!m_private->m_movie)
339 m_private->m_timeToRestore = -1.0f;
341 if (m_private->m_movieController)
342 MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate));
344 SetMovieRate(m_private->m_movie, FloatToFixed(rate));
345 QTMovieTask::sharedTask()->updateTaskTimer();
348 float QTMovie::duration() const
350 if (!m_private->m_movie)
352 TimeValue val = GetMovieDuration(m_private->m_movie);
353 TimeScale scale = GetMovieTimeScale(m_private->m_movie);
354 return static_cast<float>(val) / scale;
357 float QTMovie::currentTime() const
359 if (!m_private->m_movie)
361 TimeValue val = GetMovieTime(m_private->m_movie, 0);
362 TimeScale scale = GetMovieTimeScale(m_private->m_movie);
363 return static_cast<float>(val) / scale;
366 void QTMovie::setCurrentTime(float time) const
368 if (!m_private->m_movie)
371 m_private->m_timeToRestore = -1.0f;
373 m_private->m_seeking = true;
374 TimeScale scale = GetMovieTimeScale(m_private->m_movie);
375 if (m_private->m_movieController) {
376 QTRestartAtTimeRecord restart = { time * scale , 0 };
377 MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart);
379 SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale));
380 QTMovieTask::sharedTask()->updateTaskTimer();
383 void QTMovie::setVolume(float volume)
385 if (!m_private->m_movie)
387 SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256));
390 void QTMovie::setPreservesPitch(bool preservesPitch)
392 if (!m_private->m_movie || !m_private->m_currentURL)
398 error = QTGetMovieProperty(m_private->m_movie, kQTPropertyClass_Audio, kQTAudioPropertyID_RateChangesPreservePitch,
399 sizeof(kQTAudioPropertyID_RateChangesPreservePitch), static_cast<QTPropertyValuePtr>(&prop), 0);
401 if (error || prop == preservesPitch)
404 m_private->m_timeToRestore = currentTime();
405 m_private->m_rateToRestore = rate();
406 load(m_private->m_currentURL, preservesPitch);
409 unsigned QTMovie::dataSize() const
411 if (!m_private->m_movie)
413 return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie));
416 float QTMovie::maxTimeLoaded() const
418 if (!m_private->m_movie)
421 GetMaxLoadedTimeInMovie(m_private->m_movie, &val);
422 TimeScale scale = GetMovieTimeScale(m_private->m_movie);
423 return static_cast<float>(val) / scale;
426 long QTMovie::loadState() const
428 return m_private->m_loadState;
431 void QTMovie::getNaturalSize(int& width, int& height)
435 if (m_private->m_movie)
436 GetMovieNaturalBoundsRect(m_private->m_movie, &rect);
437 width = (rect.right - rect.left) * m_private->m_widthScaleFactor;
438 height = (rect.bottom - rect.top) * m_private->m_heightScaleFactor;
441 void QTMovie::load(const UChar* url, int len, bool preservesPitch)
443 CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len);
444 CFURLRef cfURL = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0);
446 load(cfURL, preservesPitch);
449 CFRelease(urlStringRef);
452 void QTMovie::load(CFURLRef url, bool preservesPitch)
457 if (m_private->m_movie) {
458 m_private->endTask();
459 if (m_private->m_movieController)
460 DisposeMovieController(m_private->m_movieController);
461 m_private->m_movieController = 0;
462 DisposeMovie(m_private->m_movie);
463 m_private->m_movie = 0;
464 m_private->m_loadState = 0;
467 // Define a property array for NewMovieFromProperties. 8 should be enough for our needs.
468 QTNewMoviePropertyElement movieProps[8];
469 ItemCount moviePropCount = 0;
471 bool boolTrue = true;
473 // Disable streaming support for now.
474 CFStringRef scheme = CFURLCopyScheme(url);
475 bool isRTSP = CFStringHasPrefix(scheme, CFSTR("rtsp:"));
479 m_private->m_loadError = noMovieFound;
483 if (m_private->m_currentURL) {
484 if (m_private->m_currentURL != url) {
485 CFRelease(m_private->m_currentURL);
486 m_private->m_currentURL = url;
490 m_private->m_currentURL = url;
494 // Add the movie data location to the property array
495 movieProps[moviePropCount].propClass = kQTPropertyClass_DataLocation;
496 movieProps[moviePropCount].propID = kQTDataLocationPropertyID_CFURL;
497 movieProps[moviePropCount].propValueSize = sizeof(m_private->m_currentURL);
498 movieProps[moviePropCount].propValueAddress = &(m_private->m_currentURL);
499 movieProps[moviePropCount].propStatus = 0;
502 movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation;
503 movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_DontAskUnresolvedDataRefs;
504 movieProps[moviePropCount].propValueSize = sizeof(boolTrue);
505 movieProps[moviePropCount].propValueAddress = &boolTrue;
506 movieProps[moviePropCount].propStatus = 0;
509 movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation;
510 movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_AsyncOK;
511 movieProps[moviePropCount].propValueSize = sizeof(boolTrue);
512 movieProps[moviePropCount].propValueAddress = &boolTrue;
513 movieProps[moviePropCount].propStatus = 0;
516 movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty;
517 movieProps[moviePropCount].propID = kQTNewMoviePropertyID_Active;
518 movieProps[moviePropCount].propValueSize = sizeof(boolTrue);
519 movieProps[moviePropCount].propValueAddress = &boolTrue;
520 movieProps[moviePropCount].propStatus = 0;
523 movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty;
524 movieProps[moviePropCount].propID = kQTNewMoviePropertyID_DontInteractWithUser;
525 movieProps[moviePropCount].propValueSize = sizeof(boolTrue);
526 movieProps[moviePropCount].propValueAddress = &boolTrue;
527 movieProps[moviePropCount].propStatus = 0;
530 movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation;
531 movieProps[moviePropCount].propID = '!url';
532 movieProps[moviePropCount].propValueSize = sizeof(boolTrue);
533 movieProps[moviePropCount].propValueAddress = &boolTrue;
534 movieProps[moviePropCount].propStatus = 0;
537 movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation;
538 movieProps[moviePropCount].propID = 'site';
539 movieProps[moviePropCount].propValueSize = sizeof(boolTrue);
540 movieProps[moviePropCount].propValueAddress = &boolTrue;
541 movieProps[moviePropCount].propStatus = 0;
544 movieProps[moviePropCount].propClass = kQTPropertyClass_Audio;
545 movieProps[moviePropCount].propID = kQTAudioPropertyID_RateChangesPreservePitch;
546 movieProps[moviePropCount].propValueSize = sizeof(preservesPitch);
547 movieProps[moviePropCount].propValueAddress = &preservesPitch;
548 movieProps[moviePropCount].propStatus = 0;
551 ASSERT(moviePropCount <= sizeof(movieProps) / sizeof(movieProps[0]));
552 m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, 0, &m_private->m_movie);
555 m_private->startTask();
556 // get the load fail callback quickly
557 if (m_private->m_loadError)
558 QTMovieTask::sharedTask()->updateTaskTimer(0);
560 OSType mode = kQTApertureMode_CleanAperture;
562 // Set the aperture mode property on a movie to signal that we want aspect ratio
563 // and clean aperture dimensions. Don't worry about errors, we can't do anything if
564 // the installed version of QT doesn't support it and it isn't serious enough to
566 QTSetMovieProperty(m_private->m_movie, kQTPropertyClass_Visual, kQTVisualPropertyID_ApertureMode, sizeof(mode), &mode);
570 void QTMovie::disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount)
572 if (!m_private->m_movie) {
574 enabledTrackCount = 0;
578 static HashSet<OSType>* allowedTrackTypes = 0;
579 if (!allowedTrackTypes) {
580 allowedTrackTypes = new HashSet<OSType>;
581 allowedTrackTypes->add(VideoMediaType);
582 allowedTrackTypes->add(SoundMediaType);
583 allowedTrackTypes->add(TextMediaType);
584 allowedTrackTypes->add(BaseMediaType);
585 allowedTrackTypes->add(closedCaptionTrackType);
586 allowedTrackTypes->add(subTitleTrackType);
587 allowedTrackTypes->add(mpeg4ObjectDescriptionTrackType);
588 allowedTrackTypes->add(mpeg4SceneDescriptionTrackType);
589 allowedTrackTypes->add(TimeCodeMediaType);
590 allowedTrackTypes->add(TimeCode64MediaType);
593 long trackCount = GetMovieTrackCount(m_private->m_movie);
594 enabledTrackCount = trackCount;
595 totalTrackCount = trackCount;
597 // Track indexes are 1-based. yuck. These things must descend from old-
598 // school mac resources or something.
599 for (long trackIndex = 1; trackIndex <= trackCount; trackIndex++) {
600 // Grab the track at the current index. If there isn't one there, then
601 // we can move onto the next one.
602 Track currentTrack = GetMovieIndTrack(m_private->m_movie, trackIndex);
606 // Check to see if the track is disabled already, we should move along.
607 // We don't need to re-disable it.
608 if (!GetTrackEnabled(currentTrack))
611 // Grab the track's media. We're going to check to see if we need to
612 // disable the tracks. They could be unsupported.
613 Media trackMedia = GetTrackMedia(currentTrack);
617 // Grab the media type for this track. Make sure that we don't
618 // get an error in doing so. If we do, then something really funky is
621 GetMediaHandlerDescription(trackMedia, &mediaType, nil, nil);
622 OSErr mediaErr = GetMoviesError();
623 if (mediaErr != noErr)
626 if (!allowedTrackTypes->contains(mediaType)) {
628 // Different mpeg variants import as different track types so check for the "mpeg
629 // characteristic" instead of hard coding the (current) list of mpeg media types.
630 if (GetMovieIndTrackType(m_private->m_movie, 1, 'mpeg', movieTrackCharacteristic | movieTrackEnabledOnly))
633 SetTrackEnabled(currentTrack, false);
637 // Grab the track reference count for chapters. This will tell us if it
638 // has chapter tracks in it. If there aren't any references, then we
639 // can move on the next track.
640 long referenceCount = GetTrackReferenceCount(currentTrack, kTrackReferenceChapterList);
641 if (referenceCount <= 0)
644 long referenceIndex = 0;
646 // If we get nothing here, we've overstepped our bounds and can stop
647 // looking. Chapter indices here are 1-based as well - hence, the
650 Track chapterTrack = GetTrackReference(currentTrack, kTrackReferenceChapterList, referenceIndex);
654 // Try to grab the media for the track.
655 Media chapterMedia = GetTrackMedia(chapterTrack);
659 // Grab the media type for this track. Make sure that we don't
660 // get an error in doing so. If we do, then something really
663 GetMediaHandlerDescription(chapterMedia, &mediaType, nil, nil);
664 OSErr mediaErr = GetMoviesError();
665 if (mediaErr != noErr)
668 // Check to see if the track is a video track. We don't care about
669 // other non-video tracks.
670 if (mediaType != VideoMediaType)
673 // Check to see if the track is already disabled. If it is, we
674 // should move along.
675 if (!GetTrackEnabled(chapterTrack))
678 // Disabled the evil, evil track.
679 SetTrackEnabled(chapterTrack, false);
685 bool QTMovie::isDisabled() const
687 return m_private->m_disabled;
690 void QTMovie::setDisabled(bool b)
692 m_private->m_disabled = b;
696 bool QTMovie::hasVideo() const
698 if (!m_private->m_movie)
700 return (GetMovieIndTrackType(m_private->m_movie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
703 bool QTMovie::hasAudio() const
705 if (!m_private->m_movie)
707 return (GetMovieIndTrackType(m_private->m_movie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
710 QTTrackArray QTMovie::videoTracks() const
715 while (Track theTrack = GetMovieIndTrackType(m_private->m_movie, trackIndex++, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly))
716 tracks.append(QTTrack::create(theTrack));
721 bool QTMovie::hasClosedCaptions() const
723 if (!m_private->m_movie)
725 return GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType);
728 void QTMovie::setClosedCaptionsVisible(bool visible)
730 if (!m_private->m_movie)
733 Track ccTrack = GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType);
737 Boolean doDisplay = visible;
738 QTSetTrackProperty(ccTrack, closedCaptionTrackType, closedCaptionDisplayPropertyID, sizeof(doDisplay), &doDisplay);
741 static void initializeSupportedTypes()
746 gSupportedTypes = new Vector<CFStringRef>;
747 if (quickTimeVersion < minimumQuickTimeVersion) {
748 LOG_ERROR("QuickTime version %x detected, at least %x required. Returning empty list of supported media MIME types.", quickTimeVersion, minimumQuickTimeVersion);
752 // QuickTime doesn't have an importer for video/quicktime. Add it manually.
753 gSupportedTypes->append(CFSTR("video/quicktime"));
755 for (int index = 0; index < 2; index++) {
756 ComponentDescription findCD;
758 // look at all movie importers that can import in place and are installed.
759 findCD.componentType = MovieImportType;
760 findCD.componentSubType = 0;
761 findCD.componentManufacturer = 0;
762 findCD.componentFlagsMask = cmpIsMissing | movieImportSubTypeIsFileExtension | canMovieImportInPlace | dontAutoFileMovieImport;
764 // look at those registered by HFS file types the first time through, by file extension the second time
765 findCD.componentFlags = canMovieImportInPlace | (index ? movieImportSubTypeIsFileExtension : 0);
767 long componentCount = CountComponents(&findCD);
772 while (comp = FindNextComponent(comp, &findCD)) {
773 // Does this component have a MIME type container?
774 ComponentDescription infoCD;
775 OSErr err = GetComponentInfo(comp, &infoCD, nil /*name*/, nil /*info*/, nil /*icon*/);
778 if (!(infoCD.componentFlags & hasMovieImportMIMEList))
780 QTAtomContainer mimeList = 0;
781 err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList);
782 if (err || !mimeList)
785 // Grab every type from the container.
786 QTLockContainer(mimeList);
787 int typeCount = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, kMimeInfoMimeTypeTag);
788 for (int typeIndex = 1; typeIndex <= typeCount; typeIndex++) {
789 QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, 0);
794 if (noErr != QTGetAtomDataPtr(mimeList, mimeTag, &typeLength, &atomData))
797 char typeBuffer[256];
798 if (typeLength >= sizeof(typeBuffer))
800 memcpy(typeBuffer, atomData, typeLength);
801 typeBuffer[typeLength] = 0;
803 // Only add "audio/..." and "video/..." types.
804 if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6))
807 CFStringRef cfMimeType = CFStringCreateWithCString(0, typeBuffer, kCFStringEncodingUTF8);
811 // Only add each type once.
812 bool alreadyAdded = false;
813 for (int addedIndex = 0; addedIndex < gSupportedTypes->size(); addedIndex++) {
814 CFStringRef type = gSupportedTypes->at(addedIndex);
815 if (kCFCompareEqualTo == CFStringCompare(cfMimeType, type, kCFCompareCaseInsensitive)) {
821 gSupportedTypes->append(cfMimeType);
823 CFRelease(cfMimeType);
825 DisposeHandle(mimeList);
830 unsigned QTMovie::countSupportedTypes()
832 initializeSupportedTypes();
833 return static_cast<unsigned>(gSupportedTypes->size());
836 void QTMovie::getSupportedType(unsigned index, const UChar*& str, unsigned& len)
838 initializeSupportedTypes();
839 ASSERT(index < gSupportedTypes->size());
841 // Allocate sufficient buffer to hold any MIME type
842 static UniChar* staticBuffer = 0;
844 staticBuffer = new UniChar[32];
846 CFStringRef cfstr = gSupportedTypes->at(index);
847 len = CFStringGetLength(cfstr);
848 CFRange range = { 0, len };
849 CFStringGetCharacters(cfstr, range, staticBuffer);
850 str = reinterpret_cast<const UChar*>(staticBuffer);
854 CGAffineTransform QTMovie::getTransform() const
856 ASSERT(m_private->m_movie);
857 MatrixRecord m = {0};
858 GetMovieMatrix(m_private->m_movie, &m);
860 ASSERT(!m.matrix[0][2]);
861 ASSERT(!m.matrix[1][2]);
862 CGAffineTransform transform = CGAffineTransformMake(
863 Fix2X(m.matrix[0][0]),
864 Fix2X(m.matrix[0][1]),
865 Fix2X(m.matrix[1][0]),
866 Fix2X(m.matrix[1][1]),
867 Fix2X(m.matrix[2][0]),
868 Fix2X(m.matrix[2][1]));
872 void QTMovie::setTransform(CGAffineTransform t)
874 ASSERT(m_private->m_movie);
876 {X2Fix(t.a), X2Fix(t.b), 0},
877 {X2Fix(t.c), X2Fix(t.d), 0},
878 {X2Fix(t.tx), X2Fix(t.ty), fract1},
881 SetMovieMatrix(m_private->m_movie, &m);
882 m_private->cacheMovieScale();
885 void QTMovie::resetTransform()
887 ASSERT(m_private->m_movie);
888 SetMovieMatrix(m_private->m_movie, 0);
889 m_private->cacheMovieScale();
893 bool QTMovie::initializeQuickTime()
895 static bool initialized = false;
896 static bool initializationSucceeded = false;
899 // Initialize and check QuickTime version
900 OSErr result = InitializeQTML(kInitializeQTMLEnableDoubleBufferedSurface);
902 result = Gestalt(gestaltQuickTime, &quickTimeVersion);
903 if (result != noErr) {
904 LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
907 if (quickTimeVersion < minimumQuickTimeVersion) {
908 LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion);
912 initializationSucceeded = true;
914 return initializationSucceeded;
917 Movie QTMovie::getMovieHandle() const
919 return m_private->m_movie;
922 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
925 case DLL_PROCESS_ATTACH:
927 case DLL_PROCESS_DETACH:
928 case DLL_THREAD_ATTACH:
929 case DLL_THREAD_DETACH:
932 ASSERT_NOT_REACHED();