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 
     26 #include "config.h"
     27 
     28 #if ENABLE(VIDEO)
     29 #include "MediaPlayerPrivateQuickTimeVisualContext.h"
     30 
     31 #include "ApplicationCacheHost.h"
     32 #include "ApplicationCacheResource.h"
     33 #include "Cookie.h"
     34 #include "CookieJar.h"
     35 #include "DocumentLoader.h"
     36 #include "Frame.h"
     37 #include "FrameView.h"
     38 #include "GraphicsContext.h"
     39 #include "KURL.h"
     40 #include "MediaPlayerPrivateTaskTimer.h"
     41 #include "QTCFDictionary.h"
     42 #include "QTDecompressionSession.h"
     43 #include "QTMovie.h"
     44 #include "QTMovieTask.h"
     45 #include "QTMovieVisualContext.h"
     46 #include "ScrollView.h"
     47 #include "Settings.h"
     48 #include "SoftLinking.h"
     49 #include "TimeRanges.h"
     50 #include "Timer.h"
     51 #include <AssertMacros.h>
     52 #include <CoreGraphics/CGAffineTransform.h>
     53 #include <CoreGraphics/CGContext.h>
     54 #include <QuartzCore/CATransform3D.h>
     55 #include <Wininet.h>
     56 #include <wtf/CurrentTime.h>
     57 #include <wtf/HashSet.h>
     58 #include <wtf/MainThread.h>
     59 #include <wtf/MathExtras.h>
     60 #include <wtf/StdLibExtras.h>
     61 #include <wtf/text/StringBuilder.h>
     62 #include <wtf/text/StringHash.h>
     63 
     64 #if USE(ACCELERATED_COMPOSITING)
     65 #include "PlatformCALayer.h"
     66 #include "WKCAImageQueue.h"
     67 #endif
     68 
     69 using namespace std;
     70 
     71 namespace WebCore {
     72 
     73 static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer);
     74 static bool requiredDllsAvailable();
     75 
     76 SOFT_LINK_LIBRARY(Wininet)
     77 SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved))
     78 
     79 // Interface declaration for MediaPlayerPrivateQuickTimeVisualContext's QTMovieClient aggregate
     80 class MediaPlayerPrivateQuickTimeVisualContext::MovieClient : public QTMovieClient {
     81 public:
     82     MovieClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
     83     virtual ~MovieClient() { m_parent = 0; }
     84     virtual void movieEnded(QTMovie*);
     85     virtual void movieLoadStateChanged(QTMovie*);
     86     virtual void movieTimeChanged(QTMovie*);
     87 private:
     88     MediaPlayerPrivateQuickTimeVisualContext* m_parent;
     89 };
     90 
     91 #if USE(ACCELERATED_COMPOSITING)
     92 class MediaPlayerPrivateQuickTimeVisualContext::LayerClient : public PlatformCALayerClient {
     93 public:
     94     LayerClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
     95     virtual ~LayerClient() { m_parent = 0; }
     96 
     97 private:
     98     virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*);
     99     virtual bool platformCALayerRespondsToLayoutChanges() const { return true; }
    100 
    101     virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { }
    102     virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerContentsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp; }
    103     virtual void platformCALayerPaintContents(GraphicsContext&, const IntRect& inClip) { }
    104     virtual bool platformCALayerShowDebugBorders() const { return false; }
    105     virtual bool platformCALayerShowRepaintCounter() const { return false; }
    106     virtual int platformCALayerIncrementRepaintCount() { return 0; }
    107 
    108     virtual bool platformCALayerContentsOpaque() const { return false; }
    109     virtual bool platformCALayerDrawsContent() const { return false; }
    110     virtual void platformCALayerLayerDidDisplay(PlatformLayer*) { }
    111 
    112     MediaPlayerPrivateQuickTimeVisualContext* m_parent;
    113 };
    114 
    115 void MediaPlayerPrivateQuickTimeVisualContext::LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* layer)
    116 {
    117     ASSERT(m_parent);
    118     ASSERT(m_parent->m_transformLayer == layer);
    119 
    120     FloatSize parentSize = layer->bounds().size();
    121     FloatSize naturalSize = m_parent->naturalSize();
    122 
    123     // Calculate the ratio of these two sizes and use that ratio to scale the qtVideoLayer:
    124     FloatSize ratio(parentSize.width() / naturalSize.width(), parentSize.height() / naturalSize.height());
    125 
    126     int videoWidth = 0;
    127     int videoHeight = 0;
    128     m_parent->m_movie->getNaturalSize(videoWidth, videoHeight);
    129     FloatRect videoBounds(0, 0, videoWidth * ratio.width(), videoHeight * ratio.height());
    130     FloatPoint3D videoAnchor = m_parent->m_qtVideoLayer->anchorPoint();
    131 
    132     // Calculate the new position based on the parent's size:
    133     FloatPoint position(parentSize.width() * 0.5 - videoBounds.width() * (0.5 - videoAnchor.x()),
    134         parentSize.height() * 0.5 - videoBounds.height() * (0.5 - videoAnchor.y()));
    135 
    136     m_parent->m_qtVideoLayer->setBounds(videoBounds);
    137     m_parent->m_qtVideoLayer->setPosition(position);
    138 }
    139 #endif
    140 
    141 class MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient : public QTMovieVisualContextClient {
    142 public:
    143     VisualContextClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
    144     virtual ~VisualContextClient() { m_parent = 0; }
    145     void imageAvailableForTime(const QTCVTimeStamp*);
    146     static void retrieveCurrentImageProc(void*);
    147 private:
    148     MediaPlayerPrivateQuickTimeVisualContext* m_parent;
    149 };
    150 
    151 MediaPlayerPrivateInterface* MediaPlayerPrivateQuickTimeVisualContext::create(MediaPlayer* player)
    152 {
    153     return new MediaPlayerPrivateQuickTimeVisualContext(player);
    154 }
    155 
    156 void MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(MediaEngineRegistrar registrar)
    157 {
    158     if (isAvailable())
    159         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
    160 }
    161 
    162 MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualContext(MediaPlayer* player)
    163     : m_player(player)
    164     , m_seekTo(-1)
    165     , m_seekTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired)
    166     , m_visualContextTimer(this, &MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired)
    167     , m_networkState(MediaPlayer::Empty)
    168     , m_readyState(MediaPlayer::HaveNothing)
    169     , m_enabledTrackCount(0)
    170     , m_totalTrackCount(0)
    171     , m_hasUnsupportedTracks(false)
    172     , m_startedPlaying(false)
    173     , m_isStreaming(false)
    174     , m_visible(false)
    175     , m_newFrameAvailable(false)
    176     , m_movieClient(new MediaPlayerPrivateQuickTimeVisualContext::MovieClient(this))
    177 #if USE(ACCELERATED_COMPOSITING)
    178     , m_layerClient(new MediaPlayerPrivateQuickTimeVisualContext::LayerClient(this))
    179     , m_movieTransform(CGAffineTransformIdentity)
    180 #endif
    181     , m_visualContextClient(new MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient(this))
    182     , m_delayingLoad(false)
    183     , m_privateBrowsing(false)
    184     , m_preload(MediaPlayer::Auto)
    185 {
    186 }
    187 
    188 MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualContext()
    189 {
    190     tearDownVideoRendering();
    191     cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this);
    192 }
    193 
    194 bool MediaPlayerPrivateQuickTimeVisualContext::supportsFullscreen() const
    195 {
    196 #if USE(ACCELERATED_COMPOSITING)
    197     Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
    198     if (document && document->settings())
    199         return document->settings()->acceleratedCompositingEnabled();
    200 #endif
    201     return false;
    202 }
    203 
    204 PlatformMedia MediaPlayerPrivateQuickTimeVisualContext::platformMedia() const
    205 {
    206     PlatformMedia p;
    207     p.type = PlatformMedia::QTMovieVisualContextType;
    208     p.media.qtMovieVisualContext = m_visualContext.get();
    209     return p;
    210 }
    211 #if USE(ACCELERATED_COMPOSITING)
    212 
    213 PlatformLayer* MediaPlayerPrivateQuickTimeVisualContext::platformLayer() const
    214 {
    215     return m_transformLayer ? m_transformLayer->platformLayer() : 0;
    216 }
    217 #endif
    218 
    219 String MediaPlayerPrivateQuickTimeVisualContext::rfc2616DateStringFromTime(CFAbsoluteTime time)
    220 {
    221     static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
    222     static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    223     static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT");
    224     static CFTimeZoneRef gmtTimeZone;
    225     if (!gmtTimeZone)
    226         gmtTimeZone = CFTimeZoneCopyDefault();
    227 
    228     CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone);
    229     if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits))
    230         return String();
    231 
    232     time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone);
    233     SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0);
    234 
    235     RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day,
    236         monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second));
    237     return dateCFString.get();
    238 }
    239 
    240 static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value)
    241 {
    242     if (name.isEmpty())
    243         return;
    244 
    245     // If this isn't the first parameter added, terminate the previous one.
    246     if (cookieBuilder.length())
    247         cookieBuilder.append("; ");
    248 
    249     // Add parameter name, and value if there is one.
    250     cookieBuilder.append(name);
    251     if (!value.isEmpty()) {
    252         cookieBuilder.append('=');
    253         cookieBuilder.append(value);
    254     }
    255 }
    256 
    257 void MediaPlayerPrivateQuickTimeVisualContext::setUpCookiesForQuickTime(const String& url)
    258 {
    259     // WebCore loaded the page with the movie URL with CFNetwork but QuickTime will
    260     // use WinINet to download the movie, so we need to copy any cookies needed to
    261     // download the movie into WinInet before asking QuickTime to open it.
    262     Document* document = m_player->mediaPlayerClient()->mediaPlayerOwningDocument();
    263     Frame* frame = document ? document->frame() : 0;
    264     if (!frame || !frame->page() || !frame->page()->cookieEnabled())
    265         return;
    266 
    267     KURL movieURL = KURL(KURL(), url);
    268     Vector<Cookie> documentCookies;
    269     if (!getRawCookies(frame->document(), movieURL, documentCookies))
    270         return;
    271 
    272     for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) {
    273         const Cookie& cookie = documentCookies[ndx];
    274 
    275         if (cookie.name.isEmpty())
    276             continue;
    277 
    278         // Build up the cookie string with as much information as we can get so WinINet
    279         // knows what to do with it.
    280         StringBuilder cookieBuilder;
    281         addCookieParam(cookieBuilder, cookie.name, cookie.value);
    282         addCookieParam(cookieBuilder, "path", cookie.path);
    283         if (cookie.expires)
    284             addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires));
    285         if (cookie.httpOnly)
    286             addCookieParam(cookieBuilder, "httpOnly", String());
    287         cookieBuilder.append(';');
    288 
    289         String cookieURL;
    290         if (!cookie.domain.isEmpty()) {
    291             StringBuilder urlBuilder;
    292 
    293             urlBuilder.append(movieURL.protocol());
    294             urlBuilder.append("://");
    295             if (cookie.domain[0] == '.')
    296                 urlBuilder.append(cookie.domain.substring(1));
    297             else
    298                 urlBuilder.append(cookie.domain);
    299             if (cookie.path.length() > 1)
    300                 urlBuilder.append(cookie.path);
    301 
    302             cookieURL = urlBuilder.toString();
    303         } else
    304             cookieURL = movieURL;
    305 
    306         InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0);
    307     }
    308 }
    309 
    310 static void disableComponentsOnce()
    311 {
    312     static bool sComponentsDisabled = false;
    313     if (sComponentsDisabled)
    314         return;
    315     sComponentsDisabled = true;
    316 
    317     uint32_t componentsToDisable[][5] = {
    318         {'eat ', 'TEXT', 'text', 0, 0},
    319         {'eat ', 'TXT ', 'text', 0, 0},
    320         {'eat ', 'utxt', 'text', 0, 0},
    321         {'eat ', 'TEXT', 'tx3g', 0, 0},
    322     };
    323 
    324     for (size_t i = 0; i < WTF_ARRAY_LENGTH(componentsToDisable); ++i)
    325         QTMovie::disableComponent(componentsToDisable[i]);
    326 }
    327 
    328 void MediaPlayerPrivateQuickTimeVisualContext::resumeLoad()
    329 {
    330     m_delayingLoad = false;
    331 
    332     if (!m_movieURL.isEmpty())
    333         loadInternal(m_movieURL);
    334 }
    335 
    336 void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url)
    337 {
    338     m_movieURL = url;
    339 
    340     if (m_preload == MediaPlayer::None) {
    341         m_delayingLoad = true;
    342         return;
    343     }
    344 
    345     loadInternal(url);
    346 }
    347 
    348 void MediaPlayerPrivateQuickTimeVisualContext::loadInternal(const String& url)
    349 {
    350     if (!QTMovie::initializeQuickTime()) {
    351         // FIXME: is this the right error to return?
    352         m_networkState = MediaPlayer::DecodeError;
    353         m_player->networkStateChanged();
    354         return;
    355     }
    356 
    357     disableComponentsOnce();
    358 
    359     // Initialize the task timer.
    360     MediaPlayerPrivateTaskTimer::initialize();
    361 
    362     if (m_networkState != MediaPlayer::Loading) {
    363         m_networkState = MediaPlayer::Loading;
    364         m_player->networkStateChanged();
    365     }
    366     if (m_readyState != MediaPlayer::HaveNothing) {
    367         m_readyState = MediaPlayer::HaveNothing;
    368         m_player->readyStateChanged();
    369     }
    370     cancelSeek();
    371 
    372     setUpCookiesForQuickTime(url);
    373 
    374     m_movie = adoptRef(new QTMovie(m_movieClient.get()));
    375 
    376 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
    377     Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
    378     ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0;
    379     ApplicationCacheResource* resource = 0;
    380     if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource) && resource && !resource->path().isEmpty())
    381         m_movie->load(resource->path().characters(), resource->path().length(), m_player->preservesPitch());
    382     else
    383 #endif
    384         m_movie->load(url.characters(), url.length(), m_player->preservesPitch());
    385     m_movie->setVolume(m_player->volume());
    386 }
    387 
    388 void MediaPlayerPrivateQuickTimeVisualContext::prepareToPlay()
    389 {
    390     if (!m_movie || m_delayingLoad)
    391         resumeLoad();
    392 }
    393 
    394 void MediaPlayerPrivateQuickTimeVisualContext::play()
    395 {
    396     if (!m_movie)
    397         return;
    398     m_startedPlaying = true;
    399 
    400     m_movie->play();
    401     m_visualContextTimer.startRepeating(1.0 / 30);
    402 }
    403 
    404 void MediaPlayerPrivateQuickTimeVisualContext::pause()
    405 {
    406     if (!m_movie)
    407         return;
    408     m_startedPlaying = false;
    409 
    410     m_movie->pause();
    411     m_visualContextTimer.stop();
    412 }
    413 
    414 float MediaPlayerPrivateQuickTimeVisualContext::duration() const
    415 {
    416     if (!m_movie)
    417         return 0;
    418     return m_movie->duration();
    419 }
    420 
    421 float MediaPlayerPrivateQuickTimeVisualContext::currentTime() const
    422 {
    423     if (!m_movie)
    424         return 0;
    425     return m_movie->currentTime();
    426 }
    427 
    428 void MediaPlayerPrivateQuickTimeVisualContext::seek(float time)
    429 {
    430     cancelSeek();
    431 
    432     if (!m_movie)
    433         return;
    434 
    435     if (time > duration())
    436         time = duration();
    437 
    438     m_seekTo = time;
    439     if (maxTimeLoaded() >= m_seekTo)
    440         doSeek();
    441     else
    442         m_seekTimer.start(0, 0.5f);
    443 }
    444 
    445 void MediaPlayerPrivateQuickTimeVisualContext::doSeek()
    446 {
    447     float oldRate = m_movie->rate();
    448     if (oldRate)
    449         m_movie->setRate(0);
    450     m_movie->setCurrentTime(m_seekTo);
    451     float timeAfterSeek = currentTime();
    452     // restore playback only if not at end, othewise QTMovie will loop
    453     if (oldRate && timeAfterSeek < duration())
    454         m_movie->setRate(oldRate);
    455     cancelSeek();
    456 }
    457 
    458 void MediaPlayerPrivateQuickTimeVisualContext::cancelSeek()
    459 {
    460     m_seekTo = -1;
    461     m_seekTimer.stop();
    462 }
    463 
    464 void MediaPlayerPrivateQuickTimeVisualContext::seekTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
    465 {
    466     if (!m_movie || !seeking() || currentTime() == m_seekTo) {
    467         cancelSeek();
    468         updateStates();
    469         m_player->timeChanged();
    470         return;
    471     }
    472 
    473     if (maxTimeLoaded() >= m_seekTo)
    474         doSeek();
    475     else {
    476         MediaPlayer::NetworkState state = networkState();
    477         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
    478             cancelSeek();
    479             updateStates();
    480             m_player->timeChanged();
    481         }
    482     }
    483 }
    484 
    485 bool MediaPlayerPrivateQuickTimeVisualContext::paused() const
    486 {
    487     if (!m_movie)
    488         return true;
    489     return (!m_movie->rate());
    490 }
    491 
    492 bool MediaPlayerPrivateQuickTimeVisualContext::seeking() const
    493 {
    494     if (!m_movie)
    495         return false;
    496     return m_seekTo >= 0;
    497 }
    498 
    499 IntSize MediaPlayerPrivateQuickTimeVisualContext::naturalSize() const
    500 {
    501     if (!m_movie)
    502         return IntSize();
    503     int width;
    504     int height;
    505     m_movie->getNaturalSize(width, height);
    506 #if USE(ACCELERATED_COMPOSITING)
    507     CGSize originalSize = {width, height};
    508     CGSize transformedSize = CGSizeApplyAffineTransform(originalSize, m_movieTransform);
    509     return IntSize(abs(transformedSize.width), abs(transformedSize.height));
    510 #else
    511     return IntSize(width, height);
    512 #endif
    513 }
    514 
    515 bool MediaPlayerPrivateQuickTimeVisualContext::hasVideo() const
    516 {
    517     if (!m_movie)
    518         return false;
    519     return m_movie->hasVideo();
    520 }
    521 
    522 bool MediaPlayerPrivateQuickTimeVisualContext::hasAudio() const
    523 {
    524     if (!m_movie)
    525         return false;
    526     return m_movie->hasAudio();
    527 }
    528 
    529 void MediaPlayerPrivateQuickTimeVisualContext::setVolume(float volume)
    530 {
    531     if (!m_movie)
    532         return;
    533     m_movie->setVolume(volume);
    534 }
    535 
    536 void MediaPlayerPrivateQuickTimeVisualContext::setRate(float rate)
    537 {
    538     if (!m_movie)
    539         return;
    540 
    541     // Do not call setRate(...) unless we have started playing; otherwise
    542     // QuickTime's VisualContext can get wedged waiting for a rate change
    543     // call which will never come.
    544     if (m_startedPlaying)
    545         m_movie->setRate(rate);
    546 }
    547 
    548 void MediaPlayerPrivateQuickTimeVisualContext::setPreservesPitch(bool preservesPitch)
    549 {
    550     if (!m_movie)
    551         return;
    552     m_movie->setPreservesPitch(preservesPitch);
    553 }
    554 
    555 bool MediaPlayerPrivateQuickTimeVisualContext::hasClosedCaptions() const
    556 {
    557     if (!m_movie)
    558         return false;
    559     return m_movie->hasClosedCaptions();
    560 }
    561 
    562 void MediaPlayerPrivateQuickTimeVisualContext::setClosedCaptionsVisible(bool visible)
    563 {
    564     if (!m_movie)
    565         return;
    566     m_movie->setClosedCaptionsVisible(visible);
    567 }
    568 
    569 PassRefPtr<TimeRanges> MediaPlayerPrivateQuickTimeVisualContext::buffered() const
    570 {
    571     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
    572     float loaded = maxTimeLoaded();
    573     // rtsp streams are not buffered
    574     if (!m_isStreaming && loaded > 0)
    575         timeRanges->add(0, loaded);
    576     return timeRanges.release();
    577 }
    578 
    579 float MediaPlayerPrivateQuickTimeVisualContext::maxTimeSeekable() const
    580 {
    581     // infinite duration means live stream
    582     return !isfinite(duration()) ? 0 : maxTimeLoaded();
    583 }
    584 
    585 float MediaPlayerPrivateQuickTimeVisualContext::maxTimeLoaded() const
    586 {
    587     if (!m_movie)
    588         return 0;
    589     return m_movie->maxTimeLoaded();
    590 }
    591 
    592 unsigned MediaPlayerPrivateQuickTimeVisualContext::bytesLoaded() const
    593 {
    594     if (!m_movie)
    595         return 0;
    596     float dur = duration();
    597     float maxTime = maxTimeLoaded();
    598     if (!dur)
    599         return 0;
    600     return totalBytes() * maxTime / dur;
    601 }
    602 
    603 unsigned MediaPlayerPrivateQuickTimeVisualContext::totalBytes() const
    604 {
    605     if (!m_movie)
    606         return 0;
    607     return m_movie->dataSize();
    608 }
    609 
    610 void MediaPlayerPrivateQuickTimeVisualContext::cancelLoad()
    611 {
    612     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
    613         return;
    614 
    615     tearDownVideoRendering();
    616 
    617     // Cancel the load by destroying the movie.
    618     m_movie.clear();
    619 
    620     updateStates();
    621 }
    622 
    623 void MediaPlayerPrivateQuickTimeVisualContext::updateStates()
    624 {
    625     MediaPlayer::NetworkState oldNetworkState = m_networkState;
    626     MediaPlayer::ReadyState oldReadyState = m_readyState;
    627 
    628     long loadState = m_movie ? m_movie->loadState() : QTMovieLoadStateError;
    629 
    630     if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
    631         m_movie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount);
    632         if (m_player->inMediaDocument()) {
    633             if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) {
    634                 // This is a type of media that we do not handle directly with a <video>
    635                 // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the
    636                 // MediaPlayerClient that we won't support it.
    637                 sawUnsupportedTracks();
    638                 return;
    639             }
    640         } else if (!m_enabledTrackCount)
    641             loadState = QTMovieLoadStateError;
    642     }
    643 
    644     // "Loaded" is reserved for fully buffered movies, never the case when streaming
    645     if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
    646         m_networkState = MediaPlayer::Loaded;
    647         m_readyState = MediaPlayer::HaveEnoughData;
    648     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
    649         m_readyState = MediaPlayer::HaveEnoughData;
    650     } else if (loadState >= QTMovieLoadStatePlayable) {
    651         // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
    652         m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
    653     } else if (loadState >= QTMovieLoadStateLoaded) {
    654         m_readyState = MediaPlayer::HaveMetadata;
    655     } else if (loadState > QTMovieLoadStateError) {
    656         m_networkState = MediaPlayer::Loading;
    657         m_readyState = MediaPlayer::HaveNothing;
    658     } else {
    659         if (m_player->inMediaDocument()) {
    660             // Something went wrong in the loading of media within a standalone file.
    661             // This can occur with chained ref movies that eventually resolve to a
    662             // file we don't support.
    663             sawUnsupportedTracks();
    664             return;
    665         }
    666 
    667         float loaded = maxTimeLoaded();
    668         if (!loaded)
    669             m_readyState = MediaPlayer::HaveNothing;
    670 
    671         if (!m_enabledTrackCount)
    672             m_networkState = MediaPlayer::FormatError;
    673         else {
    674             // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
    675             if (loaded > 0)
    676                 m_networkState = MediaPlayer::DecodeError;
    677             else
    678                 m_readyState = MediaPlayer::HaveNothing;
    679         }
    680     }
    681 
    682     if (isReadyForRendering() && !hasSetUpVideoRendering())
    683         setUpVideoRendering();
    684 
    685     if (seeking())
    686         m_readyState = MediaPlayer::HaveNothing;
    687 
    688     if (m_networkState != oldNetworkState)
    689         m_player->networkStateChanged();
    690     if (m_readyState != oldReadyState)
    691         m_player->readyStateChanged();
    692 }
    693 
    694 bool MediaPlayerPrivateQuickTimeVisualContext::isReadyForRendering() const
    695 {
    696     return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
    697 }
    698 
    699 void MediaPlayerPrivateQuickTimeVisualContext::sawUnsupportedTracks()
    700 {
    701     m_movie->setDisabled(true);
    702     m_hasUnsupportedTracks = true;
    703     m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
    704 }
    705 
    706 void MediaPlayerPrivateQuickTimeVisualContext::didEnd()
    707 {
    708     if (m_hasUnsupportedTracks)
    709         return;
    710 
    711     m_startedPlaying = false;
    712 
    713     updateStates();
    714     m_player->timeChanged();
    715 }
    716 
    717 void MediaPlayerPrivateQuickTimeVisualContext::setSize(const IntSize& size)
    718 {
    719     if (m_hasUnsupportedTracks || !m_movie || m_size == size)
    720         return;
    721     m_size = size;
    722 }
    723 
    724 void MediaPlayerPrivateQuickTimeVisualContext::setVisible(bool visible)
    725 {
    726     if (m_hasUnsupportedTracks || !m_movie || m_visible == visible)
    727         return;
    728 
    729     m_visible = visible;
    730     if (m_visible) {
    731         if (isReadyForRendering())
    732             setUpVideoRendering();
    733     } else
    734         tearDownVideoRendering();
    735 }
    736 
    737 void MediaPlayerPrivateQuickTimeVisualContext::paint(GraphicsContext* p, const IntRect& r)
    738 {
    739     MediaRenderingMode currentMode = currentRenderingMode();
    740 
    741     if (currentMode == MediaRenderingNone)
    742         return;
    743 
    744     if (currentMode == MediaRenderingSoftwareRenderer && !m_visualContext)
    745         return;
    746 
    747     QTPixelBuffer buffer = m_visualContext->imageForTime(0);
    748     if (buffer.pixelBufferRef()) {
    749 #if USE(ACCELERATED_COMPOSITING)
    750         if (m_qtVideoLayer) {
    751             // We are probably being asked to render the video into a canvas, but
    752             // there's a good chance the QTPixelBuffer is not ARGB and thus can't be
    753             // drawn using CG.  If so, fire up an ICMDecompressionSession and convert
    754             // the current frame into something which can be rendered by CG.
    755             if (!buffer.pixelFormatIs32ARGB() && !buffer.pixelFormatIs32BGRA()) {
    756                 // The decompression session will only decompress a specific pixelFormat
    757                 // at a specific width and height; if these differ, the session must be
    758                 // recreated with the new parameters.
    759                 if (!m_decompressionSession || !m_decompressionSession->canDecompress(buffer))
    760                     m_decompressionSession = QTDecompressionSession::create(buffer.pixelFormatType(), buffer.width(), buffer.height());
    761                 buffer = m_decompressionSession->decompress(buffer);
    762             }
    763         }
    764 #endif
    765         CGImageRef image = CreateCGImageFromPixelBuffer(buffer);
    766 
    767         CGContextRef context = p->platformContext();
    768         CGContextSaveGState(context);
    769         CGContextTranslateCTM(context, r.x(), r.y());
    770         CGContextTranslateCTM(context, 0, r.height());
    771         CGContextScaleCTM(context, 1, -1);
    772         CGContextDrawImage(context, CGRectMake(0, 0, r.width(), r.height()), image);
    773         CGContextRestoreGState(context);
    774 
    775         CGImageRelease(image);
    776     }
    777     paintCompleted(*p, r);
    778 }
    779 
    780 void MediaPlayerPrivateQuickTimeVisualContext::paintCompleted(GraphicsContext& context, const IntRect& rect)
    781 {
    782     m_newFrameAvailable = false;
    783 }
    784 
    785 void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::retrieveCurrentImageProc(void* refcon)
    786 {
    787     static_cast<MediaPlayerPrivateQuickTimeVisualContext*>(refcon)->retrieveCurrentImage();
    788 }
    789 
    790 void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::imageAvailableForTime(const QTCVTimeStamp* timeStamp)
    791 {
    792     // This call may come in on another thread, so marshall to the main thread first:
    793     callOnMainThread(&retrieveCurrentImageProc, m_parent);
    794 
    795     // callOnMainThread must be paired with cancelCallOnMainThread in the destructor,
    796     // in case this object is deleted before the main thread request is handled.
    797 }
    798 
    799 void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
    800 {
    801     if (m_visualContext && m_visualContext->isImageAvailableForTime(0))
    802         retrieveCurrentImage();
    803 }
    804 
    805 static CFDictionaryRef QTCFDictionaryCreateWithDataCallback(CFAllocatorRef allocator, const UInt8* bytes, CFIndex length)
    806 {
    807     RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(allocator, bytes, length, kCFAllocatorNull));
    808     if (!data)
    809         return 0;
    810 
    811     return reinterpret_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(allocator, data.get(), kCFPropertyListImmutable, 0));
    812 }
    813 
    814 static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer)
    815 {
    816 #if USE(ACCELERATED_COMPOSITING)
    817     CGDataProviderRef provider = 0;
    818     CGColorSpaceRef colorSpace = 0;
    819     CGImageRef image = 0;
    820 
    821     size_t bitsPerComponent = 0;
    822     size_t bitsPerPixel = 0;
    823     CGImageAlphaInfo alphaInfo = kCGImageAlphaNone;
    824 
    825     if (buffer.pixelFormatIs32BGRA()) {
    826         bitsPerComponent = 8;
    827         bitsPerPixel = 32;
    828         alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little);
    829     } else if (buffer.pixelFormatIs32ARGB()) {
    830         bitsPerComponent = 8;
    831         bitsPerPixel = 32;
    832         alphaInfo = (CGImageAlphaInfo)(kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big);
    833     } else {
    834         // All other pixel formats are currently unsupported:
    835         ASSERT_NOT_REACHED();
    836     }
    837 
    838     CGDataProviderDirectAccessCallbacks callbacks = {
    839         &QTPixelBuffer::dataProviderGetBytePointerCallback,
    840         &QTPixelBuffer::dataProviderReleaseBytePointerCallback,
    841         &QTPixelBuffer::dataProviderGetBytesAtPositionCallback,
    842         &QTPixelBuffer::dataProviderReleaseInfoCallback,
    843     };
    844 
    845     // Colorspace should be device, so that Quartz does not have to do an extra render.
    846     colorSpace = CGColorSpaceCreateDeviceRGB();
    847     require(colorSpace, Bail);
    848 
    849     provider = CGDataProviderCreateDirectAccess(buffer.pixelBufferRef(), buffer.dataSize(), &callbacks);
    850     require(provider, Bail);
    851 
    852     // CGDataProvider does not retain the buffer, but it will release it later, so do an extra retain here:
    853     QTPixelBuffer::retainCallback(buffer.pixelBufferRef());
    854 
    855     image = CGImageCreate(buffer.width(), buffer.height(), bitsPerComponent, bitsPerPixel, buffer.bytesPerRow(), colorSpace, alphaInfo, provider, 0, false, kCGRenderingIntentDefault);
    856 
    857 Bail:
    858     // Once the image is created we can release our reference to the provider and the colorspace, they are retained by the image
    859     if (provider)
    860         CGDataProviderRelease(provider);
    861     if (colorSpace)
    862         CGColorSpaceRelease(colorSpace);
    863 
    864     return image;
    865 #else
    866     return 0;
    867 #endif
    868 }
    869 
    870 
    871 void MediaPlayerPrivateQuickTimeVisualContext::retrieveCurrentImage()
    872 {
    873     if (!m_visualContext)
    874         return;
    875 
    876 #if USE(ACCELERATED_COMPOSITING)
    877     if (m_qtVideoLayer) {
    878 
    879         QTPixelBuffer buffer = m_visualContext->imageForTime(0);
    880         if (!buffer.pixelBufferRef())
    881             return;
    882 
    883         PlatformCALayer* layer = m_qtVideoLayer.get();
    884 
    885         if (!buffer.lockBaseAddress()) {
    886             if (requiredDllsAvailable()) {
    887                 if (!m_imageQueue) {
    888                     m_imageQueue = new WKCAImageQueue(buffer.width(), buffer.height(), 30);
    889                     m_imageQueue->setFlags(WKCAImageQueue::Fill, WKCAImageQueue::Fill);
    890                     layer->setContents(m_imageQueue->get());
    891                 }
    892 
    893                 // Debug QuickTime links against a non-Debug version of CoreFoundation, so the
    894                 // CFDictionary attached to the CVPixelBuffer cannot be directly passed on into the
    895                 // CAImageQueue without being converted to a non-Debug CFDictionary.  Additionally,
    896                 // old versions of QuickTime used a non-AAS CoreFoundation, so the types are not
    897                 // interchangable even in the release case.
    898                 RetainPtr<CFDictionaryRef> attachments(AdoptCF, QTCFDictionaryCreateCopyWithDataCallback(kCFAllocatorDefault, buffer.attachments(), &QTCFDictionaryCreateWithDataCallback));
    899                 CFTimeInterval imageTime = QTMovieVisualContext::currentHostTime();
    900 
    901                 m_imageQueue->collect();
    902 
    903                 uint64_t imageId = m_imageQueue->registerPixelBuffer(buffer.baseAddress(), buffer.dataSize(), buffer.bytesPerRow(), buffer.width(), buffer.height(), buffer.pixelFormatType(), attachments.get(), 0);
    904 
    905                 if (m_imageQueue->insertImage(imageTime, WKCAImageQueue::Buffer, imageId, WKCAImageQueue::Opaque | WKCAImageQueue::Flush, &QTPixelBuffer::imageQueueReleaseCallback, buffer.pixelBufferRef())) {
    906                     // Retain the buffer one extra time so it doesn't dissappear before CAImageQueue decides to release it:
    907                     QTPixelBuffer::retainCallback(buffer.pixelBufferRef());
    908                 }
    909 
    910             } else {
    911                 CGImageRef image = CreateCGImageFromPixelBuffer(buffer);
    912                 layer->setContents(image);
    913                 CGImageRelease(image);
    914             }
    915 
    916             buffer.unlockBaseAddress();
    917             layer->setNeedsCommit();
    918         }
    919     } else
    920 #endif
    921         m_player->repaint();
    922 
    923     m_visualContext->task();
    924 }
    925 
    926 static HashSet<String> mimeTypeCache()
    927 {
    928     DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ());
    929     static bool typeListInitialized = false;
    930 
    931     if (!typeListInitialized) {
    932         unsigned count = QTMovie::countSupportedTypes();
    933         for (unsigned n = 0; n < count; n++) {
    934             const UChar* character;
    935             unsigned len;
    936             QTMovie::getSupportedType(n, character, len);
    937             if (len)
    938                 typeCache.add(String(character, len));
    939         }
    940 
    941         typeListInitialized = true;
    942     }
    943 
    944     return typeCache;
    945 }
    946 
    947 static CFStringRef createVersionStringFromModuleName(LPCWSTR moduleName)
    948 {
    949     HMODULE module = GetModuleHandleW(moduleName);
    950     if (!module)
    951         return 0;
    952 
    953     wchar_t filePath[MAX_PATH] = {0};
    954     if (!GetModuleFileNameW(module, filePath, MAX_PATH))
    955         return 0;
    956 
    957     DWORD versionInfoSize = GetFileVersionInfoSizeW(filePath, 0);
    958     if (!versionInfoSize)
    959         return 0;
    960 
    961     CFStringRef versionString = 0;
    962     void* versionInfo = calloc(versionInfoSize, sizeof(char));
    963     if (GetFileVersionInfo(filePath, 0, versionInfoSize, versionInfo)) {
    964         VS_FIXEDFILEINFO* fileInfo = 0;
    965         UINT fileInfoLength = 0;
    966 
    967         if (VerQueryValueW(versionInfo, L"\\", reinterpret_cast<LPVOID*>(&fileInfo), &fileInfoLength)) {
    968             versionString = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%d.%d.%d.%d"),
    969                 HIWORD(fileInfo->dwFileVersionMS), LOWORD(fileInfo->dwFileVersionMS),
    970                 HIWORD(fileInfo->dwFileVersionLS), LOWORD(fileInfo->dwFileVersionLS));
    971         }
    972     }
    973     free(versionInfo);
    974 
    975     return versionString;
    976 }
    977 
    978 static bool requiredDllsAvailable()
    979 {
    980     static bool s_prerequisitesChecked = false;
    981     static bool s_prerequisitesSatisfied;
    982     static const CFStringRef kMinQuartzCoreVersion = CFSTR("1.0.42.0");
    983     static const CFStringRef kMinCoreVideoVersion = CFSTR("1.0.1.0");
    984 
    985     if (s_prerequisitesChecked)
    986         return s_prerequisitesSatisfied;
    987     s_prerequisitesChecked = true;
    988     s_prerequisitesSatisfied = false;
    989 
    990     CFStringRef quartzCoreString = createVersionStringFromModuleName(L"QuartzCore");
    991     if (!quartzCoreString)
    992         quartzCoreString = createVersionStringFromModuleName(L"QuartzCore_debug");
    993 
    994     CFStringRef coreVideoString = createVersionStringFromModuleName(L"CoreVideo");
    995     if (!coreVideoString)
    996         coreVideoString = createVersionStringFromModuleName(L"CoreVideo_debug");
    997 
    998     s_prerequisitesSatisfied = (quartzCoreString && coreVideoString
    999         && CFStringCompare(quartzCoreString, kMinQuartzCoreVersion, kCFCompareNumerically) != kCFCompareLessThan
   1000         && CFStringCompare(coreVideoString, kMinCoreVideoVersion, kCFCompareNumerically) != kCFCompareLessThan);
   1001 
   1002     if (quartzCoreString)
   1003         CFRelease(quartzCoreString);
   1004     if (coreVideoString)
   1005         CFRelease(coreVideoString);
   1006 
   1007     return s_prerequisitesSatisfied;
   1008 }
   1009 
   1010 void MediaPlayerPrivateQuickTimeVisualContext::getSupportedTypes(HashSet<String>& types)
   1011 {
   1012     types = mimeTypeCache();
   1013 }
   1014 
   1015 bool MediaPlayerPrivateQuickTimeVisualContext::isAvailable()
   1016 {
   1017     return QTMovie::initializeQuickTime();
   1018 }
   1019 
   1020 MediaPlayer::SupportsType MediaPlayerPrivateQuickTimeVisualContext::supportsType(const String& type, const String& codecs)
   1021 {
   1022     // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
   1023     //  extended MIME type
   1024     return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported;
   1025 }
   1026 
   1027 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieEnded(QTMovie* movie)
   1028 {
   1029     if (m_parent->m_hasUnsupportedTracks)
   1030         return;
   1031 
   1032     m_parent->m_visualContextTimer.stop();
   1033 
   1034     ASSERT(m_parent->m_movie.get() == movie);
   1035     m_parent->didEnd();
   1036 }
   1037 
   1038 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieLoadStateChanged(QTMovie* movie)
   1039 {
   1040     if (m_parent->m_hasUnsupportedTracks)
   1041         return;
   1042 
   1043     ASSERT(m_parent->m_movie.get() == movie);
   1044     m_parent->updateStates();
   1045 }
   1046 
   1047 void MediaPlayerPrivateQuickTimeVisualContext::MovieClient::movieTimeChanged(QTMovie* movie)
   1048 {
   1049     if (m_parent->m_hasUnsupportedTracks)
   1050         return;
   1051 
   1052     ASSERT(m_parent->m_movie.get() == movie);
   1053     m_parent->updateStates();
   1054     m_parent->m_player->timeChanged();
   1055 }
   1056 
   1057 bool MediaPlayerPrivateQuickTimeVisualContext::hasSingleSecurityOrigin() const
   1058 {
   1059     // We tell quicktime to disallow resources that come from different origins
   1060     // so we all media is single origin.
   1061     return true;
   1062 }
   1063 
   1064 void MediaPlayerPrivateQuickTimeVisualContext::setPreload(MediaPlayer::Preload preload)
   1065 {
   1066     m_preload = preload;
   1067     if (m_delayingLoad && m_preload != MediaPlayer::None)
   1068         resumeLoad();
   1069 }
   1070 
   1071 float MediaPlayerPrivateQuickTimeVisualContext::mediaTimeForTimeValue(float timeValue) const
   1072 {
   1073     long timeScale;
   1074     if (m_readyState < MediaPlayer::HaveMetadata || !(timeScale = m_movie->timeScale()))
   1075         return timeValue;
   1076 
   1077     long mediaTimeValue = lroundf(timeValue * timeScale);
   1078     return static_cast<float>(mediaTimeValue) / timeScale;
   1079 }
   1080 
   1081 MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::currentRenderingMode() const
   1082 {
   1083     if (!m_movie)
   1084         return MediaRenderingNone;
   1085 
   1086 #if USE(ACCELERATED_COMPOSITING)
   1087     if (m_qtVideoLayer)
   1088         return MediaRenderingMovieLayer;
   1089 #endif
   1090 
   1091     return m_visualContext ? MediaRenderingSoftwareRenderer : MediaRenderingNone;
   1092 }
   1093 
   1094 MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::preferredRenderingMode() const
   1095 {
   1096     if (!m_player->frameView() || !m_movie)
   1097         return MediaRenderingNone;
   1098 
   1099 #if USE(ACCELERATED_COMPOSITING)
   1100     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
   1101         return MediaRenderingMovieLayer;
   1102 #endif
   1103 
   1104     return MediaRenderingSoftwareRenderer;
   1105 }
   1106 
   1107 void MediaPlayerPrivateQuickTimeVisualContext::setUpVideoRendering()
   1108 {
   1109     MediaRenderingMode currentMode = currentRenderingMode();
   1110     MediaRenderingMode preferredMode = preferredRenderingMode();
   1111 
   1112 #if !USE(ACCELERATED_COMPOSITING)
   1113     ASSERT(preferredMode != MediaRenderingMovieLayer);
   1114 #endif
   1115 
   1116     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
   1117         return;
   1118 
   1119     if (currentMode != MediaRenderingNone)
   1120         tearDownVideoRendering();
   1121 
   1122     if (preferredMode == MediaRenderingMovieLayer)
   1123         createLayerForMovie();
   1124 
   1125 #if USE(ACCELERATED_COMPOSITING)
   1126     if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
   1127         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
   1128 #endif
   1129 
   1130     QTPixelBuffer::Type contextType = requiredDllsAvailable() && preferredMode == MediaRenderingMovieLayer ? QTPixelBuffer::ConfigureForCAImageQueue : QTPixelBuffer::ConfigureForCGImage;
   1131     m_visualContext = QTMovieVisualContext::create(m_visualContextClient.get(), contextType);
   1132     m_visualContext->setMovie(m_movie.get());
   1133 }
   1134 
   1135 void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering()
   1136 {
   1137 #if USE(ACCELERATED_COMPOSITING)
   1138     if (m_qtVideoLayer)
   1139         destroyLayerForMovie();
   1140 #endif
   1141 
   1142     m_visualContext = 0;
   1143 }
   1144 
   1145 bool MediaPlayerPrivateQuickTimeVisualContext::hasSetUpVideoRendering() const
   1146 {
   1147 #if USE(ACCELERATED_COMPOSITING)
   1148     return m_qtVideoLayer || (currentRenderingMode() != MediaRenderingMovieLayer && m_visualContext);
   1149 #else
   1150     return true;
   1151 #endif
   1152 }
   1153 
   1154 void MediaPlayerPrivateQuickTimeVisualContext::retrieveAndResetMovieTransform()
   1155 {
   1156 #if USE(ACCELERATED_COMPOSITING)
   1157     // First things first, reset the total movie transform so that
   1158     // we can bail out early:
   1159     m_movieTransform = CGAffineTransformIdentity;
   1160 
   1161     if (!m_movie || !m_movie->hasVideo())
   1162         return;
   1163 
   1164     // This trick will only work on movies with a single video track,
   1165     // so bail out early if the video contains more than one (or zero)
   1166     // video tracks.
   1167     QTTrackArray videoTracks = m_movie->videoTracks();
   1168     if (videoTracks.size() != 1)
   1169         return;
   1170 
   1171     QTTrack* track = videoTracks[0].get();
   1172     ASSERT(track);
   1173 
   1174     CGAffineTransform movieTransform = m_movie->getTransform();
   1175     if (!CGAffineTransformEqualToTransform(movieTransform, CGAffineTransformIdentity))
   1176         m_movie->resetTransform();
   1177 
   1178     CGAffineTransform trackTransform = track->getTransform();
   1179     if (!CGAffineTransformEqualToTransform(trackTransform, CGAffineTransformIdentity))
   1180         track->resetTransform();
   1181 
   1182     // Multiply the two transforms together, taking care to
   1183     // do so in the correct order, track * movie = final:
   1184     m_movieTransform = CGAffineTransformConcat(trackTransform, movieTransform);
   1185 #endif
   1186 }
   1187 
   1188 void MediaPlayerPrivateQuickTimeVisualContext::createLayerForMovie()
   1189 {
   1190 #if USE(ACCELERATED_COMPOSITING)
   1191     ASSERT(supportsAcceleratedRendering());
   1192 
   1193     if (!m_movie || m_qtVideoLayer)
   1194         return;
   1195 
   1196     // Create a PlatformCALayer which will transform the contents of the video layer
   1197     // which is in m_qtVideoLayer.
   1198     m_transformLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, m_layerClient.get());
   1199     if (!m_transformLayer)
   1200         return;
   1201 
   1202     // Mark the layer as anchored in the top left.
   1203     m_transformLayer->setAnchorPoint(FloatPoint3D());
   1204 
   1205     m_qtVideoLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, 0);
   1206     if (!m_qtVideoLayer)
   1207         return;
   1208 
   1209     if (CGAffineTransformEqualToTransform(m_movieTransform, CGAffineTransformIdentity))
   1210         retrieveAndResetMovieTransform();
   1211     CGAffineTransform t = m_movieTransform;
   1212 
   1213     // Remove the translation portion of the transform, since we will always rotate about
   1214     // the layer's center point.  In our limited use-case (a single video track), this is
   1215     // safe:
   1216     t.tx = t.ty = 0;
   1217     m_qtVideoLayer->setTransform(CATransform3DMakeAffineTransform(t));
   1218 
   1219 #ifndef NDEBUG
   1220     m_qtVideoLayer->setName("Video layer");
   1221 #endif
   1222     m_transformLayer->appendSublayer(m_qtVideoLayer.get());
   1223     m_transformLayer->setNeedsLayout();
   1224     // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
   1225 #endif
   1226 
   1227     // Fill the newly created layer with image data, so we're not looking at
   1228     // an empty layer until the next time a new image is available, which could
   1229     // be a long time if we're paused.
   1230     if (m_visualContext)
   1231         retrieveCurrentImage();
   1232 }
   1233 
   1234 void MediaPlayerPrivateQuickTimeVisualContext::destroyLayerForMovie()
   1235 {
   1236 #if USE(ACCELERATED_COMPOSITING)
   1237     if (m_qtVideoLayer) {
   1238         m_qtVideoLayer->removeFromSuperlayer();
   1239         m_qtVideoLayer = 0;
   1240     }
   1241 
   1242     if (m_transformLayer)
   1243         m_transformLayer = 0;
   1244 
   1245     if (m_imageQueue)
   1246         m_imageQueue = 0;
   1247 #endif
   1248 }
   1249 
   1250 #if USE(ACCELERATED_COMPOSITING)
   1251 bool MediaPlayerPrivateQuickTimeVisualContext::supportsAcceleratedRendering() const
   1252 {
   1253     return isReadyForRendering();
   1254 }
   1255 
   1256 void MediaPlayerPrivateQuickTimeVisualContext::acceleratedRenderingStateChanged()
   1257 {
   1258     // Set up or change the rendering path if necessary.
   1259     setUpVideoRendering();
   1260 }
   1261 
   1262 void MediaPlayerPrivateQuickTimeVisualContext::setPrivateBrowsingMode(bool privateBrowsing)
   1263 {
   1264     m_privateBrowsing = privateBrowsing;
   1265     if (m_movie)
   1266         m_movie->setPrivateBrowsingMode(m_privateBrowsing);
   1267 }
   1268 
   1269 #endif
   1270 
   1271 
   1272 }
   1273 
   1274 #endif
   1275