2008-04-17 Eric Carlson <eric.carlson@apple.com>
[WebKit-https.git] / WebCore / platform / graphics / win / QTMovieWin.cpp
1 /*
2  * Copyright (C) 2007 Apple Computer, 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 <windows.h>
28
29 #include "QTMovieWin.h"
30
31 // Put Movies.h first so build failures here point clearly to QuickTime
32 #include <Movies.h>
33 #include <GXMath.h>
34 #include <QTML.h>
35
36 #include "QTMovieWinTimer.h"
37
38 #include <wtf/Assertions.h>
39 #include <wtf/HashSet.h>
40 #include <wtf/Noncopyable.h>
41 #include <wtf/Vector.h>
42
43 using namespace std;
44
45 static const long minimumQuickTimeVersion = 0x07300000; // 7.3
46
47 // Resizing GWorlds is slow, give them a minimum size so size of small 
48 // videos can be animated smoothly
49 static const int cGWorldMinWidth = 640;
50 static const int cGWorldMinHeight = 360;
51
52 static const float cNonContinuousTimeChange = 0.2f;
53
54 union UppParam {
55     long longValue;
56     void* ptr;
57 };
58
59 static MovieDrawingCompleteUPP gMovieDrawingCompleteUPP = 0;
60 static HashSet<QTMovieWinPrivate*>* gTaskList;
61 static Vector<CFStringRef>* gSupportedTypes = 0;
62
63 static void updateTaskTimer(int maxInterval = 1000)
64 {
65     if (!gTaskList->size()) {
66         stopSharedTimer();
67         return;    
68     }
69     
70     long intervalInMS;
71     QTGetTimeUntilNextTask(&intervalInMS, 1000);
72     if (intervalInMS > maxInterval)
73         intervalInMS = maxInterval;
74     setSharedTimerFireDelay(static_cast<float>(intervalInMS) / 1000);
75 }
76
77 class QTMovieWinPrivate : Noncopyable {
78 public:
79     QTMovieWinPrivate();
80     ~QTMovieWinPrivate();
81     void task();
82     void startTask();
83     void endTask();
84
85     void registerDrawingCallback();
86     void drawingComplete();
87     void updateGWorld();
88     void createGWorld();
89     void deleteGWorld();
90
91     void setSize(int, int);
92
93     QTMovieWin* m_movieWin;
94     Movie m_movie;
95     bool m_tasking;
96     QTMovieWinClient* m_client;
97     long m_loadState;
98     bool m_ended;
99     bool m_seeking;
100     float m_lastMediaTime;
101     double m_lastLoadStateCheckTime;
102     int m_width;
103     int m_height;
104     bool m_visible;
105     GWorldPtr m_gWorld;
106     int m_gWorldWidth;
107     int m_gWorldHeight;
108     GWorldPtr m_savedGWorld;
109     long m_loadError;
110 };
111
112 QTMovieWinPrivate::QTMovieWinPrivate()
113     : m_movieWin(0)
114     , m_movie(0)
115     , m_tasking(false)
116     , m_client(0)
117     , m_loadState(0)
118     , m_ended(false)
119     , m_seeking(false)
120     , m_lastMediaTime(0)
121     , m_lastLoadStateCheckTime(0)
122     , m_width(0)
123     , m_height(0)
124     , m_visible(false)
125     , m_gWorld(0)
126     , m_gWorldWidth(0)
127     , m_gWorldHeight(0)
128     , m_savedGWorld(0)
129     , m_loadError(0)
130 {
131 }
132
133 QTMovieWinPrivate::~QTMovieWinPrivate()
134 {
135     endTask();
136     if (m_gWorld)
137         deleteGWorld();
138     if (m_movie)
139         DisposeMovie(m_movie);
140 }
141
142 static void taskTimerFired()
143 {
144     // The hash content might change during task()
145     Vector<QTMovieWinPrivate*> tasks;
146     copyToVector(*gTaskList, tasks);
147     size_t count = tasks.size();
148     for (unsigned n = 0; n < count; ++n)
149         tasks[n]->task();
150
151     updateTaskTimer();
152 }
153
154 void QTMovieWinPrivate::startTask() 
155 {
156     if (m_tasking)
157         return;
158     if (!gTaskList)
159         gTaskList = new HashSet<QTMovieWinPrivate*>;
160     gTaskList->add(this);
161     m_tasking = true;
162     updateTaskTimer();
163 }
164
165 void QTMovieWinPrivate::endTask() 
166 {
167     if (!m_tasking)
168         return;
169     gTaskList->remove(this);
170     m_tasking = false;
171     updateTaskTimer();
172 }
173
174 void QTMovieWinPrivate::task() 
175 {
176     ASSERT(m_tasking);
177
178     if (!m_loadError)
179         MoviesTask(m_movie, 0);
180
181     // GetMovieLoadState documentation says that you should not call it more often than every quarter of a second.
182     if (systemTime() >= m_lastLoadStateCheckTime + 0.25 || m_loadError) { 
183         // If load fails QT's load state is kMovieLoadStateComplete.
184         // This is different from QTKit API and seems strange.
185         long loadState = m_loadError ? kMovieLoadStateError : GetMovieLoadState(m_movie);
186         if (loadState != m_loadState) {
187             m_loadState = loadState;
188             m_client->movieLoadStateChanged(m_movieWin);
189         }
190         m_lastLoadStateCheckTime = systemTime();
191     }
192
193     bool ended = !!IsMovieDone(m_movie);
194     if (ended != m_ended) {
195         m_ended = ended;
196         if (m_client && ended)
197             m_client->movieEnded(m_movieWin);
198     }
199
200     float time = m_movieWin->currentTime();
201     if (time < m_lastMediaTime || time >= m_lastMediaTime + cNonContinuousTimeChange || m_seeking) {
202         m_seeking = false;
203         if (m_client)
204             m_client->movieTimeChanged(m_movieWin);
205     }
206     m_lastMediaTime = time;
207
208     if (m_loadError)
209         endTask();
210 }
211
212 void QTMovieWinPrivate::registerDrawingCallback()
213 {
214     UppParam param;
215     param.ptr = this;
216     SetMovieDrawingCompleteProc(m_movie, movieDrawingCallWhenChanged, gMovieDrawingCompleteUPP, param.longValue);
217 }
218
219 void QTMovieWinPrivate::drawingComplete()
220 {
221     if (!m_gWorld)
222         return;
223     m_client->movieNewImageAvailable(m_movieWin);
224 }
225
226 void QTMovieWinPrivate::updateGWorld()
227 {
228     bool shouldBeVisible = m_visible;
229     if (!m_height || !m_width)
230         shouldBeVisible = false;
231
232     if (shouldBeVisible && !m_gWorld)
233         createGWorld();
234     else if (!shouldBeVisible && m_gWorld)
235         deleteGWorld();
236     else if (m_gWorld && (m_width > m_gWorldWidth || m_height > m_gWorldHeight)) {
237         // need a bigger, better gWorld
238         deleteGWorld();
239         createGWorld();
240     }
241 }
242
243 void QTMovieWinPrivate::createGWorld()
244 {
245     ASSERT(!m_gWorld);
246     if (!m_movie)
247         return;
248
249     m_gWorldWidth = max(cGWorldMinWidth, m_width);
250     m_gWorldHeight = max(cGWorldMinHeight, m_height);
251     Rect bounds; 
252     bounds.top = 0;
253     bounds.left = 0; 
254     bounds.right = m_gWorldWidth;
255     bounds.bottom = m_gWorldHeight;
256     OSErr err = QTNewGWorld(&m_gWorld, k32BGRAPixelFormat, &bounds, NULL, NULL, NULL); 
257     if (err) 
258         return;
259     GetMovieGWorld(m_movie, &m_savedGWorld, 0);
260     SetMovieGWorld(m_movie, m_gWorld, 0);
261     bounds.right = m_width;
262     bounds.bottom = m_height;
263     SetMovieBox(m_movie, &bounds);
264 }
265
266 void QTMovieWinPrivate::setSize(int width, int height)
267 {
268     if (m_width == width && m_height == height)
269         return;
270     m_width = width;
271     m_height = height;
272     if (!m_movie)
273         return;
274     Rect bounds; 
275     bounds.top = 0;
276     bounds.left = 0; 
277     bounds.right = width;
278     bounds.bottom = height;
279     SetMovieBox(m_movie, &bounds);
280     updateGWorld();
281 }
282
283 void QTMovieWinPrivate::deleteGWorld()
284 {
285     ASSERT(m_gWorld);
286     if (m_movie)
287         SetMovieGWorld(m_movie, m_savedGWorld, 0);
288     m_savedGWorld = 0;
289     DisposeGWorld(m_gWorld); 
290     m_gWorld = 0;
291     m_gWorldWidth = 0;
292     m_gWorldHeight = 0;
293 }
294
295
296 QTMovieWin::QTMovieWin(QTMovieWinClient* client)
297     : m_private(new QTMovieWinPrivate())
298 {
299     m_private->m_movieWin = this;
300     m_private->m_client = client;
301     initializeQuickTime();
302 }
303
304 QTMovieWin::~QTMovieWin()
305 {
306     delete m_private;
307 }
308
309 void QTMovieWin::play()
310 {
311     StartMovie(m_private->m_movie);
312     m_private->startTask();
313 }
314
315 void QTMovieWin::pause()
316 {
317     StopMovie(m_private->m_movie);
318     updateTaskTimer();
319 }
320
321 float QTMovieWin::rate() const
322 {
323     if (!m_private->m_movie)
324         return 0;
325     return FixedToFloat(GetMovieRate(m_private->m_movie));
326 }
327
328 void QTMovieWin::setRate(float rate)
329 {
330     if (!m_private->m_movie)
331         return;
332     SetMovieRate(m_private->m_movie, FloatToFixed(rate));
333     updateTaskTimer();
334 }
335
336 float QTMovieWin::duration() const
337 {
338     if (!m_private->m_movie)
339         return 0;
340     TimeValue val = GetMovieDuration(m_private->m_movie);
341     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
342     return static_cast<float>(val) / scale;
343 }
344
345 float QTMovieWin::currentTime() const
346 {
347     if (!m_private->m_movie)
348         return 0;
349     TimeValue val = GetMovieTime(m_private->m_movie, 0);
350     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
351     return static_cast<float>(val) / scale;
352 }
353
354 void QTMovieWin::setCurrentTime(float time) const
355 {
356     if (!m_private->m_movie)
357         return;
358     m_private->m_seeking = true;
359     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
360     SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale));
361     updateTaskTimer();
362 }
363
364 void QTMovieWin::setVolume(float volume)
365 {
366     if (!m_private->m_movie)
367         return;
368     SetMovieVolume(m_private->m_movie, static_cast<short>(volume * 256));
369 }
370
371 unsigned QTMovieWin::dataSize() const
372 {
373     // FIXME: How to get this?
374     return 1000;
375 }
376
377 float QTMovieWin::maxTimeLoaded() const
378 {
379     if (!m_private->m_movie)
380         return 0;
381     TimeValue val;
382     GetMaxLoadedTimeInMovie(m_private->m_movie, &val);
383     TimeScale scale = GetMovieTimeScale(m_private->m_movie);
384     return static_cast<float>(val) / scale;
385 }
386
387 long QTMovieWin::loadState() const
388 {
389     return m_private->m_loadState;
390 }
391
392 void QTMovieWin::getNaturalSize(int& width, int& height)
393 {
394     Rect rect = { 0, };
395
396     if (m_private->m_movie)
397         GetMovieNaturalBoundsRect(m_private->m_movie, &rect);
398     width = rect.right;
399     height = rect.bottom;
400 }
401
402 void QTMovieWin::setSize(int width, int height)
403 {
404     m_private->setSize(width, height);
405     updateTaskTimer(0);
406 }
407
408 void QTMovieWin::setVisible(bool b)
409 {
410     m_private->m_visible = b;
411     m_private->updateGWorld();
412 }
413
414 void QTMovieWin::paint(HDC hdc, int x, int y)
415 {
416     if (!m_private->m_gWorld)
417         return;
418
419     HDC hdcSrc = static_cast<HDC>(GetPortHDC(reinterpret_cast<GrafPtr>(m_private->m_gWorld))); 
420     if (!hdcSrc)
421         return;
422
423     // FIXME: If we could determine the movie has no alpha, we could use BitBlt for those cases, which might be faster.
424     BLENDFUNCTION blendFunction; 
425     blendFunction.BlendOp = AC_SRC_OVER;
426     blendFunction.BlendFlags = 0;
427     blendFunction.SourceConstantAlpha = 255;
428     blendFunction.AlphaFormat = AC_SRC_ALPHA;
429     AlphaBlend(hdc, x, y, m_private->m_width, m_private->m_height, hdcSrc, 
430          0, 0, m_private->m_width, m_private->m_height, blendFunction);
431 }
432
433 void QTMovieWin::load(const UChar* url, int len)
434 {
435     if (m_private->m_movie) {
436         m_private->endTask();
437         if (m_private->m_gWorld)
438             m_private->deleteGWorld();
439         DisposeMovie(m_private->m_movie);
440         m_private->m_movie = 0;
441     }  
442
443     // Define a property array for NewMovieFromProperties. 8 should be enough for our needs. 
444     QTNewMoviePropertyElement movieProps[8]; 
445     ItemCount moviePropCount = 0; 
446
447     bool boolTrue = true;
448
449     // Create a URL data reference of type CFURL 
450     CFStringRef urlStringRef = CFStringCreateWithCharacters(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(url), len);
451     
452     // Disable streaming support for now. 
453     if (CFStringHasPrefix(urlStringRef, CFSTR("rtsp:"))) {
454         m_private->m_loadError = noMovieFound;
455         goto end;
456     }
457
458     CFURLRef urlRef = CFURLCreateWithString(kCFAllocatorDefault, urlStringRef, 0); 
459
460     // Add the movie data location to the property array 
461     movieProps[moviePropCount].propClass = kQTPropertyClass_DataLocation; 
462     movieProps[moviePropCount].propID = kQTDataLocationPropertyID_CFURL; 
463     movieProps[moviePropCount].propValueSize = sizeof(urlRef); 
464     movieProps[moviePropCount].propValueAddress = &urlRef; 
465     movieProps[moviePropCount].propStatus = 0; 
466     moviePropCount++; 
467
468     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; 
469     movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_DontAskUnresolvedDataRefs; 
470     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
471     movieProps[moviePropCount].propValueAddress = &boolTrue; 
472     movieProps[moviePropCount].propStatus = 0; 
473     moviePropCount++; 
474
475     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; 
476     movieProps[moviePropCount].propID = kQTMovieInstantiationPropertyID_AsyncOK; 
477     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
478     movieProps[moviePropCount].propValueAddress = &boolTrue; 
479     movieProps[moviePropCount].propStatus = 0; 
480     moviePropCount++; 
481
482     movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; 
483     movieProps[moviePropCount].propID = kQTNewMoviePropertyID_Active; 
484     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
485     movieProps[moviePropCount].propValueAddress = &boolTrue; 
486     movieProps[moviePropCount].propStatus = 0; 
487     moviePropCount++; 
488
489     movieProps[moviePropCount].propClass = kQTPropertyClass_NewMovieProperty; 
490     movieProps[moviePropCount].propID = kQTNewMoviePropertyID_DontInteractWithUser; 
491     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
492     movieProps[moviePropCount].propValueAddress = &boolTrue; 
493     movieProps[moviePropCount].propStatus = 0; 
494     moviePropCount++; 
495
496     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation;
497     movieProps[moviePropCount].propID = '!url';
498     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
499     movieProps[moviePropCount].propValueAddress = &boolTrue; 
500     movieProps[moviePropCount].propStatus = 0; 
501     moviePropCount++; 
502
503     movieProps[moviePropCount].propClass = kQTPropertyClass_MovieInstantiation; 
504     movieProps[moviePropCount].propID = 'site';
505     movieProps[moviePropCount].propValueSize = sizeof(boolTrue); 
506     movieProps[moviePropCount].propValueAddress = &boolTrue; 
507     movieProps[moviePropCount].propStatus = 0; 
508     moviePropCount++; 
509
510     m_private->m_loadError = NewMovieFromProperties(moviePropCount, movieProps, 0, NULL, &m_private->m_movie);
511
512     CFRelease(urlRef);
513 end:
514     m_private->startTask();
515     // get the load fail callback quickly 
516     if (m_private->m_loadError)
517         updateTaskTimer(0);
518     else
519         m_private->registerDrawingCallback();
520
521     CFRelease(urlStringRef);
522 }
523
524 void QTMovieWin::disableUnsupportedTracks(unsigned& enabledTrackCount)
525 {
526     if (!m_private->m_movie) {
527         enabledTrackCount = 0;
528         return;
529     }
530
531     static HashSet<OSType>* allowedTrackTypes = 0;
532     if (!allowedTrackTypes) {
533         allowedTrackTypes = new HashSet<OSType>;
534         allowedTrackTypes->add(VideoMediaType);
535         allowedTrackTypes->add(SoundMediaType);
536         allowedTrackTypes->add(TextMediaType);
537         allowedTrackTypes->add(BaseMediaType);
538         allowedTrackTypes->add('clcp'); // Closed caption
539         allowedTrackTypes->add('sbtl'); // Subtitle
540     }
541
542     long trackCount = GetMovieTrackCount(m_private->m_movie);
543     enabledTrackCount = trackCount;
544     
545     // Track indexes are 1-based. yuck. These things must descend from old-
546     // school mac resources or something.
547     for (long trackIndex = 1; trackIndex <= trackCount; trackIndex++) {
548         // Grab the track at the current index. If there isn't one there, then
549         // we can move onto the next one.
550         Track currentTrack = GetMovieIndTrack(m_private->m_movie, trackIndex);
551         if (!currentTrack)
552             continue;
553         
554         // Check to see if the track is disabled already, we should move along.
555         // We don't need to re-disable it.
556         if (!GetTrackEnabled(currentTrack))
557             continue;
558
559         // Grab the track's media. We're going to check to see if we need to
560         // disable the tracks. They could be unsupported.
561         Media trackMedia = GetTrackMedia(currentTrack);
562         if (!trackMedia)
563             continue;
564         
565         // Grab the media type for this track. Make sure that we don't
566         // get an error in doing so. If we do, then something really funky is
567         // wrong.
568         OSType mediaType;
569         GetMediaHandlerDescription(trackMedia, &mediaType, nil, nil);
570         OSErr mediaErr = GetMoviesError();    
571         if (mediaErr != noErr)
572             continue;
573         
574         if (!allowedTrackTypes->contains(mediaType)) {
575             SetTrackEnabled(currentTrack, false);
576             --enabledTrackCount;
577         }
578         
579         // Grab the track reference count for chapters. This will tell us if it
580         // has chapter tracks in it. If there aren't any references, then we
581         // can move on the next track.
582         long referenceCount = GetTrackReferenceCount(currentTrack, kTrackReferenceChapterList);
583         if (referenceCount <= 0)
584             continue;
585         
586         long referenceIndex = 0;        
587         while (1) {
588             // If we get nothing here, we've overstepped our bounds and can stop
589             // looking. Chapter indices here are 1-based as well - hence, the
590             // pre-increment.
591             referenceIndex++;
592             Track chapterTrack = GetTrackReference(currentTrack, kTrackReferenceChapterList, referenceIndex);
593             if (!chapterTrack)
594                 break;
595             
596             // Try to grab the media for the track.
597             Media chapterMedia = GetTrackMedia(chapterTrack);
598             if (!chapterMedia)
599                 continue;
600         
601             // Grab the media type for this track. Make sure that we don't
602             // get an error in doing so. If we do, then something really
603             // funky is wrong.
604             OSType mediaType;
605             GetMediaHandlerDescription(chapterMedia, &mediaType, nil, nil);
606             OSErr mediaErr = GetMoviesError();
607             if (mediaErr != noErr)
608                 continue;
609             
610             // Check to see if the track is a video track. We don't care about
611             // other non-video tracks.
612             if (mediaType != VideoMediaType)
613                 continue;
614             
615             // Check to see if the track is already disabled. If it is, we
616             // should move along.
617             if (!GetTrackEnabled(chapterTrack))
618                 continue;
619             
620             // Disabled the evil, evil track.
621             SetTrackEnabled(chapterTrack, false);
622             --enabledTrackCount;
623         }
624     }
625 }
626
627 pascal OSErr movieDrawingCompleteProc(Movie movie, long data)
628 {
629     UppParam param;
630     param.longValue = data;
631     QTMovieWinPrivate* mp = static_cast<QTMovieWinPrivate*>(param.ptr);
632     if (mp)
633         mp->drawingComplete();
634     return 0;
635 }
636
637 static void initializeSupportedTypes() 
638 {
639     if (gSupportedTypes)
640         return;
641     // FIXME: This list might not be complete. 
642     // There must be some way to get it out from QuickTime.
643     gSupportedTypes = new Vector<CFStringRef>;
644     gSupportedTypes->append(CFSTR("video/3gpp"));
645     gSupportedTypes->append(CFSTR("video/3gpp2"));
646     gSupportedTypes->append(CFSTR("video/mp4"));
647     gSupportedTypes->append(CFSTR("video/mpeg"));
648     gSupportedTypes->append(CFSTR("video/quicktime"));
649     gSupportedTypes->append(CFSTR("audio/ac3"));
650     gSupportedTypes->append(CFSTR("audio/aiff"));
651     gSupportedTypes->append(CFSTR("audio/basic"));
652     gSupportedTypes->append(CFSTR("audio/mpeg"));
653 }
654
655 unsigned QTMovieWin::countSupportedTypes()
656 {
657     initializeSupportedTypes();
658     return static_cast<unsigned>(gSupportedTypes->size());
659 }
660
661 void QTMovieWin::getSupportedType(unsigned index, const UChar*& str, unsigned& len)
662 {
663     initializeSupportedTypes();
664     ASSERT(index < gSupportedTypes->size());
665
666     // Allocate sufficient buffer to hold any MIME type
667     static UniChar* staticBuffer = 0;
668     if (!staticBuffer)
669         staticBuffer = new UniChar[32];
670
671     CFStringRef cfstr = gSupportedTypes->at(index);
672     len = CFStringGetLength(cfstr);
673     CFRange range = { 0, len };
674     CFStringGetCharacters(cfstr, range, staticBuffer);
675     str = reinterpret_cast<const UChar*>(staticBuffer);
676     
677 }
678
679 bool QTMovieWin::initializeQuickTime() 
680 {
681     static bool initialized = false;
682     static bool initializationSucceeded = false;
683     if (!initialized) {
684         initialized = true;
685         // Initialize and check QuickTime version
686         OSErr result = InitializeQTML(0);
687         SInt32 version = 0;
688         if (result == noErr)
689             result = Gestalt(gestaltQuickTime, &version);
690         if (result != noErr) {
691             LOG_ERROR("No QuickTime available. Disabling <video> and <audio> support.");
692             return false;
693         }
694         if (version < minimumQuickTimeVersion) {
695             LOG_ERROR("QuickTime version %x detected, at least %x required. Disabling <video> and <audio> support.", version, minimumQuickTimeVersion);
696             return false;
697         }
698         EnterMovies();
699         setSharedTimerFiredFunction(taskTimerFired);
700         gMovieDrawingCompleteUPP = NewMovieDrawingCompleteUPP(movieDrawingCompleteProc);
701         initializationSucceeded = true;
702     }
703     return initializationSucceeded;
704 }
705
706 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
707 {
708     switch (fdwReason) {
709         case DLL_PROCESS_ATTACH:
710             setSharedTimerInstanceHandle(hinstDLL);
711             return TRUE;
712         case DLL_PROCESS_DETACH:
713         case DLL_THREAD_ATTACH:
714         case DLL_THREAD_DETACH:
715             return FALSE;
716     }
717     ASSERT_NOT_REACHED();
718     return FALSE;
719 }