OSDN Git Service

am e0482608: am 5cff132a: change webkit text select to orange
[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 <FixMath.h>
32 #include <GXMath.h>
33 #include <Movies.h>
34 #include <QTML.h>
35 #include <QuickTimeComponents.h>
36 #include <wtf/Assertions.h>
37 #include <wtf/Noncopyable.h>
38 #include <wtf/Vector.h>
39
40 using namespace std;
41
42 static const long minimumQuickTimeVersion = 0x07300000; // 7.3
43
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");
50
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;
55
56 static const float cNonContinuousTimeChange = 0.2f;
57
58 union UppParam {
59     long longValue;
60     void* ptr;
61 };
62
63 static Vector<CFStringRef>* gSupportedTypes = 0;
64 static SInt32 quickTimeVersion = 0;
65
66 class QTMoviePrivate : public Noncopyable, public QTMovieTaskClient {
67 public:
68     QTMoviePrivate();
69     ~QTMoviePrivate();
70     void task();
71     void startTask();
72     void endTask();
73
74     void createMovieController();
75     void cacheMovieScale();
76
77     QTMovie* m_movieWin;
78     Movie m_movie;
79     MovieController m_movieController;
80     bool m_tasking;
81     bool m_disabled;
82     Vector<QTMovieClient*> m_clients;
83     long m_loadState;
84     bool m_ended;
85     bool m_seeking;
86     float m_lastMediaTime;
87     double m_lastLoadStateCheckTime;
88     int m_width;
89     int m_height;
90     bool m_visible;
91     long m_loadError;
92     float m_widthScaleFactor;
93     float m_heightScaleFactor;
94     CFURLRef m_currentURL;
95     float m_timeToRestore;
96     float m_rateToRestore;
97 #if !ASSERT_DISABLED
98     bool m_scaleCached;
99 #endif
100 };
101
102 QTMoviePrivate::QTMoviePrivate()
103     : m_movieWin(0)
104     , m_movie(0)
105     , m_movieController(0)
106     , m_tasking(false)
107     , m_loadState(0)
108     , m_ended(false)
109     , m_seeking(false)
110     , m_lastMediaTime(0)
111     , m_lastLoadStateCheckTime(0)
112     , m_width(0)
113     , m_height(0)
114     , m_visible(false)
115     , m_loadError(0)
116     , m_widthScaleFactor(1)
117     , m_heightScaleFactor(1)
118     , m_currentURL(0)
119     , m_timeToRestore(-1.0f)
120     , m_rateToRestore(-1.0f)
121     , m_disabled(false)
122 #if !ASSERT_DISABLED
123     , m_scaleCached(false)
124 #endif
125 {
126 }
127
128 QTMoviePrivate::~QTMoviePrivate()
129 {
130     endTask();
131     if (m_movieController)
132         DisposeMovieController(m_movieController);
133     if (m_movie)
134         DisposeMovie(m_movie);
135     if (m_currentURL)
136         CFRelease(m_currentURL);
137 }
138
139 void QTMoviePrivate::startTask() 
140 {
141     if (!m_tasking) {
142         QTMovieTask::sharedTask()->addTaskClient(this);
143         m_tasking = true;
144     }
145     QTMovieTask::sharedTask()->updateTaskTimer();
146 }
147
148 void QTMoviePrivate::endTask() 
149 {
150     if (m_tasking) {
151         QTMovieTask::sharedTask()->removeTaskClient(this);
152         m_tasking = false;
153     }
154     QTMovieTask::sharedTask()->updateTaskTimer();
155 }
156
157 void QTMoviePrivate::task() 
158 {
159     ASSERT(m_tasking);
160
161     if (!m_loadError) {
162         if (m_movieController)
163             MCIdle(m_movieController);
164         else
165             MoviesTask(m_movie, 0);
166     }
167
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) {
180                 cacheMovieScale();
181                 shouldRestorePlaybackState = true;
182             }
183
184             if (!m_movieController && m_loadState >= QTMovieLoadStateLoaded)
185                 createMovieController();
186
187             for (size_t i = 0; i < m_clients.size(); ++i)
188                 m_clients[i]->movieLoadStateChanged(m_movieWin);
189             
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;
195             }
196
197             if (m_disabled) {
198                 endTask();
199                 return;
200             }
201         }
202         m_lastLoadStateCheckTime = systemTime();
203     }
204
205     bool ended = !!IsMovieDone(m_movie);
206     if (ended != m_ended) {
207         m_ended = ended;
208         if (ended) {
209             for (size_t i = 0; i < m_clients.size(); ++i)
210                m_clients[i]->movieEnded(m_movieWin);
211         }
212     }
213
214     float time = m_movieWin->currentTime();
215     if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) {
216         m_seeking = false;
217         for (size_t i = 0; i < m_clients.size(); ++i)
218             m_clients[i]->movieTimeChanged(m_movieWin);
219     }
220     m_lastMediaTime = time;
221
222     if (m_loadError)
223         endTask();
224     else
225         QTMovieTask::sharedTask()->updateTaskTimer();
226 }
227
228 void QTMoviePrivate::createMovieController()
229 {
230     Rect bounds;
231     long flags;
232
233     if (!m_movie)
234         return;
235
236     if (m_movieController)
237         DisposeMovieController(m_movieController);
238
239     GetMovieBox(m_movie, &bounds);
240     flags = mcTopLeftMovie | mcNotVisible;
241     m_movieController = NewMovieController(m_movie, &bounds, flags);
242     if (!m_movieController)
243         return;
244
245     // Disable automatic looping.
246     MCDoAction(m_movieController, mcActionSetLooping, 0);
247 }
248
249 void QTMoviePrivate::cacheMovieScale()
250 {
251     Rect naturalRect;
252     Rect initialRect;
253
254     GetMovieNaturalBoundsRect(m_movie, &naturalRect);
255     GetMovieBox(m_movie, &initialRect);
256
257     float naturalWidth = naturalRect.right - naturalRect.left;
258     float naturalHeight = naturalRect.bottom - naturalRect.top;
259
260     if (naturalWidth)
261         m_widthScaleFactor = (initialRect.right - initialRect.left) / naturalWidth;
262     if (naturalHeight)
263         m_heightScaleFactor = (initialRect.bottom - initialRect.top) / naturalHeight;
264 #if !ASSERT_DISABLED
265     m_scaleCached = true;
266 #endif
267 }
268
269 QTMovie::QTMovie(QTMovieClient* client)
270     : m_private(new QTMoviePrivate())
271 {
272     m_private->m_movieWin = this;
273     if (client)
274         m_private->m_clients.append(client);
275     initializeQuickTime();
276 }
277
278 QTMovie::~QTMovie()
279 {
280     delete m_private;
281 }
282
283 void QTMovie::disableComponent(uint32_t cd[5])
284 {
285     ComponentDescription nullDesc = {'null', 'base', kAppleManufacturer, 0, 0};
286     Component nullComp = FindNextComponent(0, &nullDesc);
287     Component disabledComp = 0;
288
289     while (disabledComp = FindNextComponent(disabledComp, (ComponentDescription*)&cd[0]))
290         CaptureComponent(disabledComp, nullComp);
291 }
292
293 void QTMovie::addClient(QTMovieClient* client)
294 {
295     if (client)
296         m_private->m_clients.append(client);
297 }
298
299 void QTMovie::removeClient(QTMovieClient* client)
300 {
301     size_t indexOfClient = m_private->m_clients.find(client);
302     if (indexOfClient != notFound)
303         m_private->m_clients.remove(indexOfClient);
304 }
305
306 void QTMovie::play()
307 {
308     m_private->m_timeToRestore = -1.0f;
309
310     if (m_private->m_movieController)
311         MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)GetMoviePreferredRate(m_private->m_movie));
312     else
313         StartMovie(m_private->m_movie);
314     m_private->startTask();
315 }
316
317 void QTMovie::pause()
318 {
319     m_private->m_timeToRestore = -1.0f;
320
321     if (m_private->m_movieController)
322         MCDoAction(m_private->m_movieController, mcActionPlay, 0);
323     else
324         StopMovie(m_private->m_movie);
325     QTMovieTask::sharedTask()->updateTaskTimer();
326 }
327
328 float QTMovie::rate() const
329 {
330     if (!m_private->m_movie)
331         return 0;
332     return FixedToFloat(GetMovieRate(m_private->m_movie));
333 }
334
335 void QTMovie::setRate(float rate)
336 {
337     if (!m_private->m_movie)
338         return;    
339     m_private->m_timeToRestore = -1.0f;
340     
341     if (m_private->m_movieController)
342         MCDoAction(m_private->m_movieController, mcActionPrerollAndPlay, (void *)FloatToFixed(rate));
343     else
344         SetMovieRate(m_private->m_movie, FloatToFixed(rate));
345     QTMovieTask::sharedTask()->updateTaskTimer();
346 }
347
348 float QTMovie::duration() const
349 {
350     if (!m_private->m_movie)
351         return 0;
352     TimeValue val = GetMovieDuration(m_private->m_movie);
353     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
354     return static_cast<float>(val) / scale;
355 }
356
357 float QTMovie::currentTime() const
358 {
359     if (!m_private->m_movie)
360         return 0;
361     TimeValue val = GetMovieTime(m_private->m_movie, 0);
362     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
363     return static_cast<float>(val) / scale;
364 }
365
366 void QTMovie::setCurrentTime(float time) const
367 {
368     if (!m_private->m_movie)
369         return;
370
371     m_private->m_timeToRestore = -1.0f;
372     
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);
378     } else
379         SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale));
380     QTMovieTask::sharedTask()->updateTaskTimer();
381 }
382
383 void QTMovie::setVolume(float volume)
384 {
385     if (!m_private->m_movie)
386         return;
387     SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256));
388 }
389
390 void QTMovie::setPreservesPitch(bool preservesPitch)
391 {
392     if (!m_private->m_movie || !m_private->m_currentURL)
393         return;
394
395     OSErr error;
396     bool prop = false;
397
398     error = QTGetMovieProperty(m_private->m_movie, kQTPropertyClass_Audio, kQTAudioPropertyID_RateChangesPreservePitch,
399                                sizeof(kQTAudioPropertyID_RateChangesPreservePitch), static_cast<QTPropertyValuePtr>(&prop), 0);
400
401     if (error || prop == preservesPitch)
402         return;
403
404     m_private->m_timeToRestore = currentTime();
405     m_private->m_rateToRestore = rate();
406     load(m_private->m_currentURL, preservesPitch);
407 }
408
409 unsigned QTMovie::dataSize() const
410 {
411     if (!m_private->m_movie)
412         return 0;
413     return GetMovieDataSize(m_private->m_movie, 0, GetMovieDuration(m_private->m_movie));
414 }
415
416 float QTMovie::maxTimeLoaded() const
417 {
418     if (!m_private->m_movie)
419         return 0;
420     TimeValue val;
421     GetMaxLoadedTimeInMovie(m_private->m_movie, &val);
422     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
423     return static_cast<float>(val) / scale;
424 }
425
426 long QTMovie::loadState() const
427 {
428     return m_private->m_loadState;
429 }
430
431 void QTMovie::getNaturalSize(int& width, int& height)
432 {
433     Rect rect = { 0, };
434
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;
439 }
440
441 void QTMovie::load(const UChar* url, int len, bool preservesPitch)
442 {
443     CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len);
444     CFURLRef cfURL = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0);
445
446     load(cfURL, preservesPitch);
447
448     CFRelease(cfURL);
449     CFRelease(urlStringRef);
450 }
451
452 void QTMovie::load(CFURLRef url, bool preservesPitch)
453 {
454     if (!url)
455         return;
456
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;
465     }  
466
467     // Define a property array for NewMovieFromProperties. 8 should be enough for our needs. 
468     QTNewMoviePropertyElement movieProps[8]; 
469     ItemCount moviePropCount = 0; 
470
471     bool boolTrue = true;
472     
473     // Disable streaming support for now. 
474     CFStringRef scheme = CFURLCopyScheme(url);
475     bool isRTSP = CFStringHasPrefix(scheme, CFSTR("rtsp:"));
476     CFRelease(scheme);
477
478     if (isRTSP) {
479         m_private->m_loadError = noMovieFound;
480         goto end;
481     }
482
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;
487             CFRetain(url);
488         }
489     } else {
490         m_private->m_currentURL = url;
491         CFRetain(url);
492     }
493
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; 
500     moviePropCount++; 
501
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; 
507     moviePropCount++; 
508
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; 
514     moviePropCount++; 
515
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; 
521     moviePropCount++; 
522
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; 
528     moviePropCount++; 
529
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; 
535     moviePropCount++; 
536
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; 
542     moviePropCount++;
543
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; 
549     moviePropCount++; 
550
551     ASSERT(moviePropCount <= sizeof(movieProps) / sizeof(movieProps[0]));
552     m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, 0, &m_private->m_movie);
553
554 end:
555     m_private->startTask();
556     // get the load fail callback quickly 
557     if (m_private->m_loadError)
558         QTMovieTask::sharedTask()->updateTaskTimer(0);
559     else {
560         OSType mode = kQTApertureMode_CleanAperture;
561
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 
565         // warrant failing.
566         QTSetMovieProperty(m_private->m_movie, kQTPropertyClass_Visual, kQTVisualPropertyID_ApertureMode, sizeof(mode), &mode);
567     }
568 }
569
570 void QTMovie::disableUnsupportedTracks(unsigned& enabledTrackCount, unsigned& totalTrackCount)
571 {
572     if (!m_private->m_movie) {
573         totalTrackCount = 0;
574         enabledTrackCount = 0;
575         return;
576     }
577
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);
591     }
592
593     long trackCount = GetMovieTrackCount(m_private->m_movie);
594     enabledTrackCount = trackCount;
595     totalTrackCount = trackCount;
596
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);
603         if (!currentTrack)
604             continue;
605         
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))
609             continue;
610
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);
614         if (!trackMedia)
615             continue;
616         
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
619         // wrong.
620         OSType mediaType;
621         GetMediaHandlerDescription(trackMedia, &mediaType, nil, nil);
622         OSErr mediaErr = GetMoviesError();    
623         if (mediaErr != noErr)
624             continue;
625         
626         if (!allowedTrackTypes->contains(mediaType)) {
627
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))
631                 continue;
632
633             SetTrackEnabled(currentTrack, false);
634             --enabledTrackCount;
635         }
636         
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)
642             continue;
643         
644         long referenceIndex = 0;        
645         while (1) {
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
648             // pre-increment.
649             referenceIndex++;
650             Track chapterTrack = GetTrackReference(currentTrack, kTrackReferenceChapterList, referenceIndex);
651             if (!chapterTrack)
652                 break;
653             
654             // Try to grab the media for the track.
655             Media chapterMedia = GetTrackMedia(chapterTrack);
656             if (!chapterMedia)
657                 continue;
658         
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
661             // funky is wrong.
662             OSType mediaType;
663             GetMediaHandlerDescription(chapterMedia, &mediaType, nil, nil);
664             OSErr mediaErr = GetMoviesError();
665             if (mediaErr != noErr)
666                 continue;
667             
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)
671                 continue;
672             
673             // Check to see if the track is already disabled. If it is, we
674             // should move along.
675             if (!GetTrackEnabled(chapterTrack))
676                 continue;
677             
678             // Disabled the evil, evil track.
679             SetTrackEnabled(chapterTrack, false);
680             --enabledTrackCount;
681         }
682     }
683 }
684
685 bool QTMovie::isDisabled() const
686 {
687     return m_private->m_disabled;
688 }
689
690 void QTMovie::setDisabled(bool b)
691 {
692     m_private->m_disabled = b;
693 }
694
695
696 bool QTMovie::hasVideo() const
697 {
698     if (!m_private->m_movie)
699         return false;
700     return (GetMovieIndTrackType(m_private->m_movie, 1, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
701 }
702
703 bool QTMovie::hasAudio() const
704 {
705     if (!m_private->m_movie)
706         return false;
707     return (GetMovieIndTrackType(m_private->m_movie, 1, AudioMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly));
708 }
709
710 QTTrackArray QTMovie::videoTracks() const
711 {
712     QTTrackArray tracks;
713     long trackIndex = 1;
714
715     while (Track theTrack = GetMovieIndTrackType(m_private->m_movie, trackIndex++, VisualMediaCharacteristic, movieTrackCharacteristic | movieTrackEnabledOnly))
716         tracks.append(QTTrack::create(theTrack));
717
718     return tracks;
719 }
720
721 bool QTMovie::hasClosedCaptions() const 
722 {
723     if (!m_private->m_movie)
724         return false;
725     return GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType);
726 }
727
728 void QTMovie::setClosedCaptionsVisible(bool visible)
729 {
730     if (!m_private->m_movie)
731         return;
732
733     Track ccTrack = GetMovieIndTrackType(m_private->m_movie, 1, closedCaptionTrackType, movieTrackMediaType);
734     if (!ccTrack)
735         return;
736
737     Boolean doDisplay = visible;
738     QTSetTrackProperty(ccTrack, closedCaptionTrackType, closedCaptionDisplayPropertyID, sizeof(doDisplay), &doDisplay);
739 }
740
741 static void initializeSupportedTypes() 
742 {
743     if (gSupportedTypes)
744         return;
745
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);
749         return;
750     }
751
752     // QuickTime doesn't have an importer for video/quicktime. Add it manually.
753     gSupportedTypes->append(CFSTR("video/quicktime"));
754
755     for (int index = 0; index < 2; index++) {
756         ComponentDescription findCD;
757
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;
763
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);
766         
767         long componentCount = CountComponents(&findCD);
768         if (!componentCount)
769             continue;
770
771         Component comp = 0;
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*/);
776             if (err)
777                 continue;
778             if (!(infoCD.componentFlags & hasMovieImportMIMEList))
779                 continue;
780             QTAtomContainer mimeList = 0;
781             err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList);
782             if (err || !mimeList)
783                 continue;
784
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);
790                 if (!mimeTag)
791                     continue;
792                 char* atomData;
793                 long typeLength;
794                 if (noErr != QTGetAtomDataPtr(mimeList, mimeTag, &typeLength, &atomData))
795                     continue;
796
797                 char typeBuffer[256];
798                 if (typeLength >= sizeof(typeBuffer))
799                     continue;
800                 memcpy(typeBuffer, atomData, typeLength);
801                 typeBuffer[typeLength] = 0;
802
803                 // Only add "audio/..." and "video/..." types.
804                 if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6))
805                     continue;
806
807                 CFStringRef cfMimeType = CFStringCreateWithCString(0, typeBuffer, kCFStringEncodingUTF8);
808                 if (!cfMimeType)
809                     continue;
810
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)) {
816                         alreadyAdded = true;
817                         break;
818                     }
819                 }
820                 if (!alreadyAdded)
821                     gSupportedTypes->append(cfMimeType);
822                 else
823                     CFRelease(cfMimeType);
824             }
825             DisposeHandle(mimeList);
826         }
827     }
828 }
829
830 unsigned QTMovie::countSupportedTypes()
831 {
832     initializeSupportedTypes();
833     return static_cast<unsigned>(gSupportedTypes->size());
834 }
835
836 void QTMovie::getSupportedType(unsigned index, const UChar*& str, unsigned& len)
837 {
838     initializeSupportedTypes();
839     ASSERT(index < gSupportedTypes->size());
840
841     // Allocate sufficient buffer to hold any MIME type
842     static UniChar* staticBuffer = 0;
843     if (!staticBuffer)
844         staticBuffer = new UniChar[32];
845
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);
851     
852 }
853
854 CGAffineTransform QTMovie::getTransform() const
855 {
856     ASSERT(m_private->m_movie);
857     MatrixRecord m = {0};
858     GetMovieMatrix(m_private->m_movie, &m);
859
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]));
869     return transform;
870 }
871
872 void QTMovie::setTransform(CGAffineTransform t)
873 {
874     ASSERT(m_private->m_movie);
875     MatrixRecord m = {{
876         {X2Fix(t.a), X2Fix(t.b), 0},
877         {X2Fix(t.c), X2Fix(t.d), 0},
878         {X2Fix(t.tx), X2Fix(t.ty), fract1},
879     }};
880
881     SetMovieMatrix(m_private->m_movie, &m);
882     m_private->cacheMovieScale();
883 }
884
885 void QTMovie::resetTransform()
886 {
887     ASSERT(m_private->m_movie);
888     SetMovieMatrix(m_private->m_movie, 0);
889     m_private->cacheMovieScale();
890 }
891
892
893 bool QTMovie::initializeQuickTime() 
894 {
895     static bool initialized = false;
896     static bool initializationSucceeded = false;
897     if (!initialized) {
898         initialized = true;
899         // Initialize and check QuickTime version
900         OSErr result = InitializeQTML(kInitializeQTMLEnableDoubleBufferedSurface);
901         if (result == noErr)
902             result = Gestalt(gestaltQuickTime, &quickTimeVersion);
903         if (result != noErr) {
904             LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
905             return false;
906         }
907         if (quickTimeVersion < minimumQuickTimeVersion) {
908             LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", quickTimeVersion, minimumQuickTimeVersion);
909             return false;
910         }
911         EnterMovies();
912         initializationSucceeded = true;
913     }
914     return initializationSucceeded;
915 }
916
917 Movie QTMovie::getMovieHandle() const 
918 {
919     return m_private->m_movie;
920 }
921
922 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
923 {
924     switch (fdwReason) {
925     case DLL_PROCESS_ATTACH:
926         return TRUE;
927     case DLL_PROCESS_DETACH:
928     case DLL_THREAD_ATTACH:
929     case DLL_THREAD_DETACH:
930         return FALSE;
931     }
932     ASSERT_NOT_REACHED();
933     return FALSE;
934 }