OSDN Git Service

am 02f67ae0: am ae583467: add meta-files about 3rd party projects
[android-x86/external-webkit.git] / WebCore / platform / graphics / win / QTMovie.cpp
1 /*
2  * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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. 
24  */
25 #include "config.h"
26
27 #include "QTMovie.h"
28
29 #include "QTMovieTask.h"
30 #include "QTMovieWinTimer.h"
31 #include <GXMath.h>
32 #include <Movies.h>
33 #include <QTML.h>
34 #include <QuickTimeComponents.h>
35 #include <wtf/Assertions.h>
36 #include <wtf/Noncopyable.h>
37 #include <wtf/Vector.h>
38
39 using namespace std;
40
41 static const long minimumQuickTimeVersion = 0x07300000; // 7.3
42
43 static const long closedCaptionTrackType = 'clcp';
44 static const long subTitleTrackType = 'sbtl';
45 static const long mpeg4ObjectDescriptionTrackType = 'odsm';
46 static const long mpeg4SceneDescriptionTrackType = 'sdsm';
47 static const long closedCaptionDisplayPropertyID = 'disp';
48 static LPCTSTR fullscreenQTMoviePointerProp = TEXT("fullscreenQTMoviePointer");
49
50 // Resizing GWorlds is slow, give them a minimum size so size of small 
51 // videos can be animated smoothly
52 static const int cGWorldMinWidth = 640;
53 static const int cGWorldMinHeight = 360;
54
55 static const float cNonContinuousTimeChange = 0.2f;
56
57 union UppParam {
58     long longValue;
59     void* ptr;
60 };
61
62 static Vector<CFStringRef>* gSupportedTypes = 0;
63 static SInt32 quickTimeVersion = 0;
64
65 class QTMoviePrivate : public Noncopyable, public QTMovieTaskClient {
66 public:
67     QTMoviePrivate();
68     ~QTMoviePrivate();
69     void task();
70     void startTask();
71     void endTask();
72
73     void createMovieController();
74     void cacheMovieScale();
75
76     QTMovie* m_movieWin;
77     Movie m_movie;
78     MovieController m_movieController;
79     bool m_tasking;
80     bool m_disabled;
81     Vector<QTMovieClient*> m_clients;
82     long m_loadState;
83     bool m_ended;
84     bool m_seeking;
85     float m_lastMediaTime;
86     double m_lastLoadStateCheckTime;
87     int m_width;
88     int m_height;
89     bool m_visible;
90     long m_loadError;
91     float m_widthScaleFactor;
92     float m_heightScaleFactor;
93     CFURLRef m_currentURL;
94     float m_timeToRestore;
95     float m_rateToRestore;
96 #if !ASSERT_DISABLED
97     bool m_scaleCached;
98 #endif
99 };
100
101 QTMoviePrivate::QTMoviePrivate()
102     : m_movieWin(0)
103     , m_movie(0)
104     , m_movieController(0)
105     , m_tasking(false)
106     , m_loadState(0)
107     , m_ended(false)
108     , m_seeking(false)
109     , m_lastMediaTime(0)
110     , m_lastLoadStateCheckTime(0)
111     , m_width(0)
112     , m_height(0)
113     , m_visible(false)
114     , m_loadError(0)
115     , m_widthScaleFactor(1)
116     , m_heightScaleFactor(1)
117     , m_currentURL(0)
118     , m_timeToRestore(-1.0f)
119     , m_rateToRestore(-1.0f)
120     , m_disabled(false)
121 #if !ASSERT_DISABLED
122     , m_scaleCached(false)
123 #endif
124 {
125 }
126
127 QTMoviePrivate::~QTMoviePrivate()
128 {
129     endTask();
130     if (m_movieController)
131         DisposeMovieController(m_movieController);
132     if (m_movie)
133         DisposeMovie(m_movie);
134     if (m_currentURL)
135         CFRelease(m_currentURL);
136 }
137
138 void QTMoviePrivate::startTask() 
139 {
140     if (!m_tasking) {
141         QTMovieTask::sharedTask()->addTaskClient(this);
142         m_tasking = true;
143     }
144     QTMovieTask::sharedTask()->updateTaskTimer();
145 }
146
147 void QTMoviePrivate::endTask() 
148 {
149     if (m_tasking) {
150         QTMovieTask::sharedTask()->removeTaskClient(this);
151         m_tasking = false;
152     }
153     QTMovieTask::sharedTask()->updateTaskTimer();
154 }
155
156 void QTMoviePrivate::task() 
157 {
158     ASSERT(m_tasking);
159
160     if (!m_loadError) {
161         if (m_movieController)
162             MCIdle(m_movieController);
163         else
164             MoviesTask(m_movie, 0);
165     }
166
167     // GetMovieLoadState documentation says that you should not call it more often than every quarter of a second.
168     if (systemTime() >= m_lastLoadStateCheckTime + 0.25 || m_loadError) { 
169         // If load fails QT's load state is QTMovieLoadStateComplete.
170         // This is different from QTKit API and seems strange.
171         long loadState = m_loadError ? QTMovieLoadStateError : GetMovieLoadState(m_movie);
172         if (loadState != m_loadState) {
173             // we only need to erase the movie gworld when the load state changes to loaded while it
174             //  is visible as the gworld is destroyed/created when visibility changes
175             bool shouldRestorePlaybackState = false;
176             bool movieNewlyPlayable = loadState >= QTMovieLoadStateLoaded && m_loadState < QTMovieLoadStateLoaded;
177             m_loadState = loadState;
178             if (movieNewlyPlayable) {
179                 cacheMovieScale();
180                 shouldRestorePlaybackState = true;
181             }
182
183             if (!m_movieController && m_loadState >= QTMovieLoadStateLoaded)
184                 createMovieController();
185
186             for (size_t i = 0; i < m_clients.size(); ++i)
187                 m_clients[i]->movieLoadStateChanged(m_movieWin);
188             
189             if (shouldRestorePlaybackState && m_timeToRestore != -1.0f) {
190                 m_movieWin->setCurrentTime(m_timeToRestore);
191                 m_timeToRestore = -1.0f;
192                 m_movieWin->setRate(m_rateToRestore);
193                 m_rateToRestore = -1.0f;
194             }
195
196             if (m_disabled) {
197                 endTask();
198                 return;
199             }
200         }
201         m_lastLoadStateCheckTime = systemTime();
202     }
203
204     bool ended = !!IsMovieDone(m_movie);
205     if (ended != m_ended) {
206         m_ended = ended;
207         if (ended) {
208             for (size_t i = 0; i < m_clients.size(); ++i)
209                m_clients[i]->movieEnded(m_movieWin);
210         }
211     }
212
213     float time = m_movieWin->currentTime();
214     if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) {
215         m_seeking = false;
216         for (size_t i = 0; i < m_clients.size(); ++i)
217             m_clients[i]->movieTimeChanged(m_movieWin);
218     }
219     m_lastMediaTime = time;
220
221     if (m_loadError)
222         endTask();
223     else
224         QTMovieTask::sharedTask()->updateTaskTimer();
225 }
226
227 void QTMoviePrivate::createMovieController()
228 {
229     Rect bounds;
230     long flags;
231
232     if (!m_movie)
233         return;
234
235     if (m_movieController)
236         DisposeMovieController(m_movieController);
237
238     GetMovieBox(m_movie, &bounds);
239     flags = mcTopLeftMovie | mcNotVisible;
240     m_movieController = NewMovieController(m_movie, &bounds, flags);
241     if (!m_movieController)
242         return;
243 }
244
245 void QTMoviePrivate::cacheMovieScale()
246 {
247     Rect naturalRect;
248     Rect initialRect;
249
250     GetMovieNaturalBoundsRect(m_movie, &naturalRect);
251     GetMovieBox(m_movie, &initialRect);
252
253     float naturalWidth = naturalRect.right - naturalRect.left;
254     float naturalHeight = naturalRect.bottom - naturalRect.top;
255
256     if (naturalWidth)
257         m_widthScaleFactor = (initialRect.right - initialRect.left) / naturalWidth;
258     if (naturalHeight)
259         m_heightScaleFactor = (initialRect.bottom - initialRect.top) / naturalHeight;
260 #if !ASSERT_DISABLED
261     m_scaleCached = true;
262 #endif
263 }
264
265 QTMovie::QTMovie(QTMovieClient* client)
266     : m_private(new QTMoviePrivate())
267 {
268     m_private->m_movieWin = this;
269     if (client)
270         m_private->m_clients.append(client);
271     initializeQuickTime();
272 }
273
274 QTMovie::~QTMovie()
275 {
276     delete m_private;
277 }
278
279 void QTMovie::addClient(QTMovieClient* client)
280 {
281     if (client)
282         m_private->m_clients.append(client);
283 }
284
285 void QTMovie::removeClient(QTMovieClient* client)
286 {
287     size_t indexOfClient = m_private->m_clients.find(client);
288     if (indexOfClient != notFound)
289         m_private->m_clients.remove(indexOfClient);
290 }
291
292 void QTMovie::play()
293 {
294     m_private->m_timeToRestore = -1.0f;
295
296     if (m_private->m_movieController)
297         MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)GetMoviePreferredRate(m_private->m_movie));
298     else
299         StartMovie(m_private->m_movie);
300     m_private->startTask();
301 }
302
303 void QTMovie::pause()
304 {
305     m_private->m_timeToRestore = -1.0f;
306
307     if (m_private->m_movieController)
308         MCDoAction(m_private->m_movieController, mcActionPlay, 0);
309     else
310         StopMovie(m_private->m_movie);
311     QTMovieTask::sharedTask()->updateTaskTimer();
312 }
313
314 float QTMovie::rate() const
315 {
316     if (!m_private->m_movie)
317         return 0;
318     return FixedToFloat(GetMovieRate(m_private->m_movie));
319 }
320
321 void QTMovie::setRate(float rate)
322 {
323     if (!m_private->m_movie)
324         return;    
325     m_private->m_timeToRestore = -1.0f;
326     
327     if (m_private->m_movieController)
328         MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate));
329     else
330         SetMovieRate(m_private->m_movie, FloatToFixed(rate));
331     QTMovieTask::sharedTask()->updateTaskTimer();
332 }
333
334 float QTMovie::duration() const
335 {
336     if (!m_private->m_movie)
337         return 0;
338     TimeValue val = GetMovieDuration(m_private->m_movie);
339     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
340     return static_cast<float>(val) / scale;
341 }
342
343 float QTMovie::currentTime() const
344 {
345     if (!m_private->m_movie)
346         return 0;
347     TimeValue val = GetMovieTime(m_private->m_movie, 0);
348     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
349     return static_cast<float>(val) / scale;
350 }
351
352 void QTMovie::setCurrentTime(float time) const
353 {
354     if (!m_private->m_movie)
355         return;
356
357     m_private->m_timeToRestore = -1.0f;
358     
359     m_private->m_seeking = true;
360     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
361     if (m_private->m_movieController) {
362         QTRestartAtTimeRecord restart = { time * scale , 0 };
363         MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart);
364     } else
365         SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale));
366     QTMovieTask::sharedTask()->updateTaskTimer();
367 }
368
369 void QTMovie::setVolume(float volume)
370 {
371     if (!m_private->m_movie)
372         return;
373     SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256));
374 }
375
376 void QTMovie::setPreservesPitch(bool preservesPitch)
377 {
378     if (!m_private->m_movie || !m_private->m_currentURL)
379         return;
380
381     OSErr error;
382     bool prop = false;
383
384     error = QTGetMovieProperty(m_private->m_movie, kQTPropertyClass_Audio, kQTAudioPropertyID_RateChangesPreservePitch,
385                                sizeof(kQTAudioPropertyID_RateChangesPreservePitch), static_cast<QTPropertyValuePtr>(&prop), 0);
386
387     if (error || prop == preservesPitch)
388         return;
389
390     m_private->m_timeToRestore = currentTime();
391     m_private->m_rateToRestore = rate();
392     load(m_private->m_currentURL, preservesPitch);
393 }
394
395 unsigned QTMovie::dataSize() const
396 {
397     if (!m_private->m_movie)
398         return 0;
399     return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie));
400 }
401
402 float QTMovie::maxTimeLoaded() const
403 {
404     if (!m_private->m_movie)
405         return 0;
406     TimeValue val;
407     GetMaxLoadedTimeInMovie(m_private->m_movie, &val);
408     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
409     return static_cast<float>(val) / scale;
410 }
411
412 long QTMovie::loadState() const
413 {
414     return m_private->m_loadState;
415 }
416
417 void QTMovie::getNaturalSize(int& width, int& height)
418 {
419     Rect rect = { 0, };
420
421     if (m_private->m_movie)
422         GetMovieNaturalBoundsRect(m_private->m_movie, &rect);
423     width = (rect.right - rect.left) * m_private->m_widthScaleFactor;
424     height = (rect.bottom - rect.top) * m_private->m_heightScaleFactor;
425 }
426
427 void QTMovie::load(const UChar* url, int len, bool preservesPitch)
428 {
429     CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len);
430     CFURLRef cfURL = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0);
431
432     load(cfURL, preservesPitch);
433
434     CFRelease(cfURL);
435     CFRelease(urlStringRef);
436 }
437
438 void QTMovie::load(CFURLRef url, bool preservesPitch)
439 {
440     if (!url)
441         return;
442
443     if (m_private->m_movie) {
444         m_private->endTask();
445         if (m_private->m_movieController)
446             DisposeMovieController(m_private->m_movieController);
447         m_private->m_movieController = 0;
448         DisposeMovie(m_private->m_movie);
449         m_private->m_movie = 0;
450         m_private->m_loadState = 0;
451     }  
452
453     // Define a property array for NewMovieFromProperties. 8 should be enough for our needs. 
454     QTNewMoviePropertyElement movieProps[8]; 
455     ItemCount moviePropCount = 0; 
456
457     bool boolTrue = true;
458     
459     // Disable streaming support for now. 
460     CFStringRef scheme = CFURLCopyScheme(url);
461     bool isRTSP = CFStringHasPrefix(scheme, CFSTR("rtsp:"));
462     CFRelease(scheme);
463
464     if (isRTSP) {
465         m_private->m_loadError = noMovieFound;
466         goto end;
467     }
468
469     if (m_private->m_currentURL) {
470         if (m_private->m_currentURL != url) {
471             CFRelease(m_private->m_currentURL);
472             m_private->m_currentURL = url;
473             CFRetain(url);
474         }
475     } else {
476         m_private->m_currentURL = url;
477         CFRetain(url);
478     }
479
480     // Add the movie data location to the property array 
481     movieProps[moviePropCount].propClass = kQTPropertyClass_DataLocation; 
482     movieProps[moviePropCount].propID = kQTDataLocationPropertyID_CFURL; 
483     movieProps[moviePropCount].propValueSize = sizeof(m_private->m_currentURL); 
484     movieProps[moviePropCount].propValueAddress = &(m_private->m_currentURL); 
485     movieProps[moviePropCount].propStatus = 0; 
486     moviePropCount++; 
487
488     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; 
489     movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_DontAskUnresolvedDataRefs; 
490     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
491     movieProps[moviePropCount].propValueAddress = &boolTrue; 
492     movieProps[moviePropCount].propStatus = 0; 
493     moviePropCount++; 
494
495     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; 
496     movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_AsyncOK; 
497     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
498     movieProps[moviePropCount].propValueAddress = &boolTrue; 
499     movieProps[moviePropCount].propStatus = 0; 
500     moviePropCount++; 
501
502     movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; 
503     movieProps[moviePropCount].propID = kQTNewMoviePropertyID_Active; 
504     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
505     movieProps[moviePropCount].propValueAddress = &boolTrue; 
506     movieProps[moviePropCount].propStatus = 0; 
507     moviePropCount++; 
508
509     movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; 
510     movieProps[moviePropCount].propID = kQTNewMoviePropertyID_DontInteractWithUser; 
511     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
512     movieProps[moviePropCount].propValueAddress = &boolTrue; 
513     movieProps[moviePropCount].propStatus = 0; 
514     moviePropCount++; 
515
516     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation;
517     movieProps[moviePropCount].propID = '!url';
518     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
519     movieProps[moviePropCount].propValueAddress = &boolTrue; 
520     movieProps[moviePropCount].propStatus = 0; 
521     moviePropCount++; 
522
523     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; 
524     movieProps[moviePropCount].propID = 'site';
525     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
526     movieProps[moviePropCount].propValueAddress = &boolTrue; 
527     movieProps[moviePropCount].propStatus = 0; 
528     moviePropCount++;
529
530     movieProps[moviePropCount].propClass = kQTPropertyClass_Audio; 
531     movieProps[moviePropCount].propID = kQTAudioPropertyID_RateChangesPreservePitch;
532     movieProps[moviePropCount].propValueSize = sizeof(preservesPitch); 
533     movieProps[moviePropCount].propValueAddress = &preservesPitch; 
534     movieProps[moviePropCount].propStatus = 0; 
535     moviePropCount++; 
536
537     ASSERT(moviePropCount <= sizeof(movieProps) / sizeof(movieProps[0]));
538     m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, 0, &m_private->m_movie);
539
540 end:
541     m_private->startTask();
542     // get the load fail callback quickly 
543     if (m_private->m_loadError)
544         QTMovieTask::sharedTask()->updateTaskTimer(0);
545     else {
546         OSType mode = kQTApertureMode_CleanAperture;
547
548         // Set the aperture mode property on a movie to signal that we want aspect ratio
549         // and clean aperture dimensions. Don't worry about errors, we can't do anything if
550         // the installed version of QT doesn't support it and it isn't serious enough to 
551         // warrant failing.
552         QTSetMovieProperty(m_private->m_movie, kQTPropertyClass_Visual, kQTVisualPropertyID_ApertureMode, sizeof(mode), &mode);
553     }
554 }
555
556 void QTMovie::disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount)
557 {
558     if (!m_private->m_movie) {
559         totalTrackCount = 0;
560         enabledTrackCount = 0;
561         return;
562     }
563
564     static HashSet<OSType>* allowedTrackTypes = 0;
565     if (!allowedTrackTypes) {
566         allowedTrackTypes = new HashSet<OSType>;
567         allowedTrackTypes->add(VideoMediaType);
568         allowedTrackTypes->add(SoundMediaType);
569         allowedTrackTypes->add(TextMediaType);
570         allowedTrackTypes->add(BaseMediaType);
571         allowedTrackTypes->add(closedCaptionTrackType);
572         allowedTrackTypes->add(subTitleTrackType);
573         allowedTrackTypes->add(mpeg4ObjectDescriptionTrackType);
574         allowedTrackTypes->add(mpeg4SceneDescriptionTrackType);
575         allowedTrackTypes->add(TimeCodeMediaType);
576         allowedTrackTypes->add(TimeCode64MediaType);
577     }
578
579     long trackCount = GetMovieTrackCount(m_private->m_movie);
580     enabledTrackCount = trackCount;
581     totalTrackCount = trackCount;
582
583     // Track indexes are 1-based. yuck. These things must descend from old-
584     // school mac resources or something.
585     for (long trackIndex = 1; trackIndex <= trackCount; trackIndex++) {
586         // Grab the track at the current index. If there isn't one there, then
587         // we can move onto the next one.
588         Track currentTrack = GetMovieIndTrack(m_private->m_movie, trackIndex);
589         if (!currentTrack)
590             continue;
591         
592         // Check to see if the track is disabled already, we should move along.
593         // We don't need to re-disable it.
594         if (!GetTrackEnabled(currentTrack))
595             continue;
596
597         // Grab the track's media. We're going to check to see if we need to
598         // disable the tracks. They could be unsupported.
599         Media trackMedia = GetTrackMedia(currentTrack);
600         if (!trackMedia)
601             continue;
602         
603         // Grab the media type for this track. Make sure that we don't
604         // get an error in doing so. If we do, then something really funky is
605         // wrong.
606         OSType mediaType;
607         GetMediaHandlerDescription(trackMedia, &mediaType, nil, nil);
608         OSErr mediaErr = GetMoviesError();    
609         if (mediaErr != noErr)
610             continue;
611         
612         if (!allowedTrackTypes->contains(mediaType)) {
613
614             // Different mpeg variants import as different track types so check for the "mpeg 
615             // characteristic" instead of hard coding the (current) list of mpeg media types.
616             if (GetMovieIndTrackType(m_private->m_movie, 1, 'mpeg', movieTrackCharacteristic | movieTrackEnabledOnly))
617                 continue;
618
619             SetTrackEnabled(currentTrack, false);
620             --enabledTrackCount;
621         }
622         
623         // Grab the track reference count for chapters. This will tell us if it
624         // has chapter tracks in it. If there aren't any references, then we
625         // can move on the next track.
626         long referenceCount = GetTrackReferenceCount(currentTrack, kTrackReferenceChapterList);
627         if (referenceCount <= 0)
628             continue;
629         
630         long referenceIndex = 0;        
631         while (1) {
632             // If we get nothing here, we've overstepped our bounds and can stop
633             // looking. Chapter indices here are 1-based as well - hence, the
634             // pre-increment.
635             referenceIndex++;
636             Track chapterTrack = GetTrackReference(currentTrack, kTrackReferenceChapterList, referenceIndex);
637             if (!chapterTrack)
638                 break;
639             
640             // Try to grab the media for the track.
641             Media chapterMedia = GetTrackMedia(chapterTrack);
642             if (!chapterMedia)
643                 continue;
644         
645             // Grab the media type for this track. Make sure that we don't
646             // get an error in doing so. If we do, then something really
647             // funky is wrong.
648             OSType mediaType;
649             GetMediaHandlerDescription(chapterMedia, &mediaType, nil, nil);
650             OSErr mediaErr = GetMoviesError();
651             if (mediaErr != noErr)
652                 continue;
653             
654             // Check to see if the track is a video track. We don't care about
655             // other non-video tracks.
656             if (mediaType != VideoMediaType)
657                 continue;
658             
659             // Check to see if the track is already disabled. If it is, we
660             // should move along.
661             if (!GetTrackEnabled(chapterTrack))
662                 continue;
663             
664             // Disabled the evil, evil track.
665             SetTrackEnabled(chapterTrack, false);
666             --enabledTrackCount;
667         }
668     }
669 }
670
671 bool QTMovie::isDisabled() const
672 {
673     return m_private->m_disabled;
674 }
675
676 void QTMovie::setDisabled(bool b)
677 {
678     m_private->m_disabled = b;
679 }
680
681
682 bool QTMovie::hasVideo() const
683 {
684     if (!m_private->m_movie)
685         return false;
686     return (GetMovieIndTrackType(m_private->m_movie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
687 }
688
689 bool QTMovie::hasAudio() const
690 {
691     if (!m_private->m_movie)
692         return false;
693     return (GetMovieIndTrackType(m_private->m_movie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
694 }
695
696
697 bool QTMovie::hasClosedCaptions() const 
698 {
699     if (!m_private->m_movie)
700         return false;
701     return GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType);
702 }
703
704 void QTMovie::setClosedCaptionsVisible(bool visible)
705 {
706     if (!m_private->m_movie)
707         return;
708
709     Track ccTrack = GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType);
710     if (!ccTrack)
711         return;
712
713     Boolean doDisplay = visible;
714     QTSetTrackProperty(ccTrack, closedCaptionTrackType, closedCaptionDisplayPropertyID, sizeof(doDisplay), &doDisplay);
715 }
716
717 static void initializeSupportedTypes() 
718 {
719     if (gSupportedTypes)
720         return;
721
722     gSupportedTypes = new Vector<CFStringRef>;
723     if (quickTimeVersion < minimumQuickTimeVersion) {
724         LOG_ERROR("QuickTime version %x detected, at least %x required. Returning empty list of supported media MIME types.", quickTimeVersion, minimumQuickTimeVersion);
725         return;
726     }
727
728     // QuickTime doesn't have an importer for video/quicktime. Add it manually.
729     gSupportedTypes->append(CFSTR("video/quicktime"));
730
731     for (int index = 0; index < 2; index++) {
732         ComponentDescription findCD;
733
734         // look at all movie importers that can import in place and are installed. 
735         findCD.componentType = MovieImportType;
736         findCD.componentSubType = 0;
737         findCD.componentManufacturer = 0;
738         findCD.componentFlagsMask = cmpIsMissing | movieImportSubTypeIsFileExtension | canMovieImportInPlace | dontAutoFileMovieImport;
739
740         // look at those registered by HFS file types the first time through, by file extension the second time
741         findCD.componentFlags = canMovieImportInPlace | (index ? movieImportSubTypeIsFileExtension : 0);
742         
743         long componentCount = CountComponents(&findCD);
744         if (!componentCount)
745             continue;
746
747         Component comp = 0;
748         while (comp = FindNextComponent(comp, &findCD)) {
749             // Does this component have a MIME type container?
750             ComponentDescription infoCD;
751             OSErr err = GetComponentInfo(comp, &infoCD, nil /*name*/, nil /*info*/, nil /*icon*/);
752             if (err)
753                 continue;
754             if (!(infoCD.componentFlags & hasMovieImportMIMEList))
755                 continue;
756             QTAtomContainer mimeList = 0;
757             err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList);
758             if (err || !mimeList)
759                 continue;
760
761             // Grab every type from the container.
762             QTLockContainer(mimeList);
763             int typeCount = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, kMimeInfoMimeTypeTag);
764             for (int typeIndex = 1; typeIndex <= typeCount; typeIndex++) {
765                 QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, 0);
766                 if (!mimeTag)
767                     continue;
768                 char* atomData;
769                 long typeLength;
770                 if (noErr != QTGetAtomDataPtr(mimeList, mimeTag, &typeLength, &atomData))
771                     continue;
772
773                 char typeBuffer[256];
774                 if (typeLength >= sizeof(typeBuffer))
775                     continue;
776                 memcpy(typeBuffer, atomData, typeLength);
777                 typeBuffer[typeLength] = 0;
778
779                 // Only add "audio/..." and "video/..." types.
780                 if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6))
781                     continue;
782
783                 CFStringRef cfMimeType = CFStringCreateWithCString(0, typeBuffer, kCFStringEncodingUTF8);
784                 if (!cfMimeType)
785                     continue;
786
787                 // Only add each type once.
788                 bool alreadyAdded = false;
789                 for (int addedIndex = 0; addedIndex < gSupportedTypes->size(); addedIndex++) {
790                     CFStringRef type = gSupportedTypes->at(addedIndex);
791                     if (kCFCompareEqualTo == CFStringCompare(cfMimeType, type, kCFCompareCaseInsensitive)) {
792                         alreadyAdded = true;
793                         break;
794                     }
795                 }
796                 if (!alreadyAdded)
797                     gSupportedTypes->append(cfMimeType);
798                 else
799                     CFRelease(cfMimeType);
800             }
801             DisposeHandle(mimeList);
802         }
803     }
804 }
805
806 unsigned QTMovie::countSupportedTypes()
807 {
808     initializeSupportedTypes();
809     return static_cast<unsigned>(gSupportedTypes->size());
810 }
811
812 void QTMovie::getSupportedType(unsigned index, const UChar*& str, unsigned& len)
813 {
814     initializeSupportedTypes();
815     ASSERT(index < gSupportedTypes->size());
816
817     // Allocate sufficient buffer to hold any MIME type
818     static UniChar* staticBuffer = 0;
819     if (!staticBuffer)
820         staticBuffer = new UniChar[32];
821
822     CFStringRef cfstr = gSupportedTypes->at(index);
823     len = CFStringGetLength(cfstr);
824     CFRange range = { 0, len };
825     CFStringGetCharacters(cfstr, range, staticBuffer);
826     str = reinterpret_cast<const UChar*>(staticBuffer);
827     
828 }
829
830 bool QTMovie::initializeQuickTime() 
831 {
832     static bool initialized = false;
833     static bool initializationSucceeded = false;
834     if (!initialized) {
835         initialized = true;
836         // Initialize and check QuickTime version
837         OSErr result = InitializeQTML(kInitializeQTMLEnableDoubleBufferedSurface);
838         if (result == noErr)
839             result = Gestalt(gestaltQuickTime, &quickTimeVersion);
840         if (result != noErr) {
841             LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
842             return false;
843         }
844         if (quickTimeVersion < minimumQuickTimeVersion) {
845             LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion);
846             return false;
847         }
848         EnterMovies();
849         initializationSucceeded = true;
850     }
851     return initializationSucceeded;
852 }
853
854 Movie QTMovie::getMovieHandle() const 
855 {
856     return m_private->m_movie;
857 }
858
859 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
860 {
861     switch (fdwReason) {
862     case DLL_PROCESS_ATTACH:
863         return TRUE;
864     case DLL_PROCESS_DETACH:
865     case DLL_THREAD_ATTACH:
866     case DLL_THREAD_DETACH:
867         return FALSE;
868     }
869     ASSERT_NOT_REACHED();
870     return FALSE;
871 }