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