Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2007, 2008, 2009, 2010 Apple, Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 
     28 #if ENABLE(VIDEO)
     29 #include "MediaPlayerPrivateQuickTimeWin.h"
     30 
     31 #include "Cookie.h"
     32 #include "CookieJar.h"
     33 #include "Frame.h"
     34 #include "FrameView.h"
     35 #include "GraphicsContext.h"
     36 #include "KURL.h"
     37 #include "MediaPlayerPrivateTaskTimer.h"
     38 #include "QTMovieTask.h"
     39 #include "ScrollView.h"
     40 #include "SoftLinking.h"
     41 #include "TimeRanges.h"
     42 #include "Timer.h"
     43 #include <CoreGraphics/CGColorSpace.h>
     44 #include <CoreGraphics/CGContext.h>
     45 #include <CoreGraphics/CGImage.h>
     46 #include <Wininet.h>
     47 #include <wtf/CurrentTime.h>
     48 #include <wtf/HashSet.h>
     49 #include <wtf/MathExtras.h>
     50 #include <wtf/StdLibExtras.h>
     51 #include <wtf/text/StringBuilder.h>
     52 #include <wtf/text/StringHash.h>
     53 
     54 #if USE(ACCELERATED_COMPOSITING)
     55 #include "GraphicsLayerCACF.h"
     56 #include "PlatformCALayer.h"
     57 #endif
     58 
     59 #if DRAW_FRAME_RATE
     60 #include "Document.h"
     61 #include "Font.h"
     62 #include "RenderObject.h"
     63 #include "RenderStyle.h"
     64 #include "Windows.h"
     65 #endif
     66 
     67 using namespace std;
     68 
     69 namespace WebCore {
     70 
     71 SOFT_LINK_LIBRARY(Wininet)
     72 SOFT_LINK(Wininet, InternetSetCookieExW, DWORD, WINAPI, (LPCWSTR lpszUrl, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData, DWORD dwFlags, DWORD_PTR dwReserved), (lpszUrl, lpszCookieName, lpszCookieData, dwFlags, dwReserved))
     73 
     74 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player)
     75 {
     76     return new MediaPlayerPrivate(player);
     77 }
     78 
     79 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar)
     80 {
     81     if (isAvailable())
     82         registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
     83 }
     84 
     85 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player)
     86     : m_player(player)
     87     , m_seekTo(-1)
     88     , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired)
     89     , m_networkState(MediaPlayer::Empty)
     90     , m_readyState(MediaPlayer::HaveNothing)
     91     , m_enabledTrackCount(0)
     92     , m_totalTrackCount(0)
     93     , m_hasUnsupportedTracks(false)
     94     , m_startedPlaying(false)
     95     , m_isStreaming(false)
     96     , m_visible(false)
     97     , m_newFrameAvailable(false)
     98 #if DRAW_FRAME_RATE
     99     , m_frameCountWhilePlaying(0)
    100     , m_timeStartedPlaying(0)
    101     , m_timeStoppedPlaying(0)
    102 #endif
    103 {
    104 }
    105 
    106 MediaPlayerPrivate::~MediaPlayerPrivate()
    107 {
    108     tearDownVideoRendering();
    109     m_qtGWorld->setMovie(0);
    110 }
    111 
    112 bool MediaPlayerPrivate::supportsFullscreen() const
    113 {
    114     return true;
    115 }
    116 
    117 PlatformMedia MediaPlayerPrivate::platformMedia() const
    118 {
    119     PlatformMedia p;
    120     p.type = PlatformMedia::QTMovieGWorldType;
    121     p.media.qtMovieGWorld = m_qtGWorld.get();
    122     return p;
    123 }
    124 
    125 #if USE(ACCELERATED_COMPOSITING)
    126 PlatformLayer* MediaPlayerPrivate::platformLayer() const
    127 {
    128     return m_qtVideoLayer ? m_qtVideoLayer->platformLayer() : 0;
    129 }
    130 #endif
    131 
    132 String MediaPlayerPrivate::rfc2616DateStringFromTime(CFAbsoluteTime time)
    133 {
    134     static const char* const dayStrings[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
    135     static const char* const monthStrings[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    136     static const CFStringRef dateFormatString = CFSTR("%s, %02d %s %04d %02d:%02d:%02d GMT");
    137     static CFTimeZoneRef gmtTimeZone;
    138     if (!gmtTimeZone)
    139         gmtTimeZone = CFTimeZoneCopyDefault();
    140 
    141     CFGregorianDate dateValue = CFAbsoluteTimeGetGregorianDate(time, gmtTimeZone);
    142     if (!CFGregorianDateIsValid(dateValue, kCFGregorianAllUnits))
    143         return String();
    144 
    145     time = CFGregorianDateGetAbsoluteTime(dateValue, gmtTimeZone);
    146     SInt32 day = CFAbsoluteTimeGetDayOfWeek(time, 0);
    147 
    148     RetainPtr<CFStringRef> dateCFString(AdoptCF, CFStringCreateWithFormat(0, 0, dateFormatString, dayStrings[day - 1], dateValue.day,
    149         monthStrings[dateValue.month - 1], dateValue.year, dateValue.hour, dateValue.minute, (int)dateValue.second));
    150     return dateCFString.get();
    151 }
    152 
    153 static void addCookieParam(StringBuilder& cookieBuilder, const String& name, const String& value)
    154 {
    155     if (name.isEmpty())
    156         return;
    157 
    158     // If this isn't the first parameter added, terminate the previous one.
    159     if (cookieBuilder.length())
    160         cookieBuilder.append("; ");
    161 
    162     // Add parameter name, and value if there is one.
    163     cookieBuilder.append(name);
    164     if (!value.isEmpty()) {
    165         cookieBuilder.append('=');
    166         cookieBuilder.append(value);
    167     }
    168 }
    169 
    170 
    171 void MediaPlayerPrivate::setUpCookiesForQuickTime(const String& url)
    172 {
    173     // WebCore loaded the page with the movie URL with CFNetwork but QuickTime will
    174     // use WinINet to download the movie, so we need to copy any cookies needed to
    175     // download the movie into WinInet before asking QuickTime to open it.
    176     Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
    177     if (!frame || !frame->page() || !frame->page()->cookieEnabled())
    178         return;
    179 
    180     KURL movieURL = KURL(KURL(), url);
    181     Vector<Cookie> documentCookies;
    182     if (!getRawCookies(frame->document(), movieURL, documentCookies))
    183         return;
    184 
    185     for (size_t ndx = 0; ndx < documentCookies.size(); ndx++) {
    186         const Cookie& cookie = documentCookies[ndx];
    187 
    188         if (cookie.name.isEmpty())
    189             continue;
    190 
    191         // Build up the cookie string with as much information as we can get so WinINet
    192         // knows what to do with it.
    193         StringBuilder cookieBuilder;
    194         addCookieParam(cookieBuilder, cookie.name, cookie.value);
    195         addCookieParam(cookieBuilder, "path", cookie.path);
    196         if (cookie.expires)
    197             addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires));
    198         if (cookie.httpOnly)
    199             addCookieParam(cookieBuilder, "httpOnly", String());
    200         cookieBuilder.append(';');
    201 
    202         String cookieURL;
    203         if (!cookie.domain.isEmpty()) {
    204             StringBuilder urlBuilder;
    205 
    206             urlBuilder.append(movieURL.protocol());
    207             urlBuilder.append("://");
    208             if (cookie.domain[0] == '.')
    209                 urlBuilder.append(cookie.domain.substring(1));
    210             else
    211                 urlBuilder.append(cookie.domain);
    212             if (cookie.path.length() > 1)
    213                 urlBuilder.append(cookie.path);
    214 
    215             cookieURL = urlBuilder.toString();
    216         } else
    217             cookieURL = movieURL;
    218 
    219         InternetSetCookieExW(cookieURL.charactersWithNullTermination(), 0, cookieBuilder.toString().charactersWithNullTermination(), 0, 0);
    220     }
    221 }
    222 
    223 void MediaPlayerPrivate::load(const String& url)
    224 {
    225     if (!QTMovie::initializeQuickTime()) {
    226         // FIXME: is this the right error to return?
    227         m_networkState = MediaPlayer::DecodeError;
    228         m_player->networkStateChanged();
    229         return;
    230     }
    231 
    232     // Initialize the task timer.
    233     MediaPlayerPrivateTaskTimer::initialize();
    234 
    235     if (m_networkState != MediaPlayer::Loading) {
    236         m_networkState = MediaPlayer::Loading;
    237         m_player->networkStateChanged();
    238     }
    239     if (m_readyState != MediaPlayer::HaveNothing) {
    240         m_readyState = MediaPlayer::HaveNothing;
    241         m_player->readyStateChanged();
    242     }
    243     cancelSeek();
    244 
    245     setUpCookiesForQuickTime(url);
    246 
    247     m_qtMovie = adoptRef(new QTMovie(this));
    248     m_qtMovie->load(url.characters(), url.length(), m_player->preservesPitch());
    249     m_qtMovie->setVolume(m_player->volume());
    250 
    251     m_qtGWorld = adoptRef(new QTMovieGWorld(this));
    252     m_qtGWorld->setMovie(m_qtMovie.get());
    253     m_qtGWorld->setVisible(m_player->visible());
    254 }
    255 
    256 void MediaPlayerPrivate::play()
    257 {
    258     if (!m_qtMovie)
    259         return;
    260     m_startedPlaying = true;
    261 #if DRAW_FRAME_RATE
    262     m_frameCountWhilePlaying = 0;
    263 #endif
    264 
    265     m_qtMovie->play();
    266 }
    267 
    268 void MediaPlayerPrivate::pause()
    269 {
    270     if (!m_qtMovie)
    271         return;
    272     m_startedPlaying = false;
    273 #if DRAW_FRAME_RATE
    274     m_timeStoppedPlaying = WTF::currentTime();
    275 #endif
    276     m_qtMovie->pause();
    277 }
    278 
    279 float MediaPlayerPrivate::duration() const
    280 {
    281     if (!m_qtMovie)
    282         return 0;
    283     return m_qtMovie->duration();
    284 }
    285 
    286 float MediaPlayerPrivate::currentTime() const
    287 {
    288     if (!m_qtMovie)
    289         return 0;
    290     return m_qtMovie->currentTime();
    291 }
    292 
    293 void MediaPlayerPrivate::seek(float time)
    294 {
    295     cancelSeek();
    296 
    297     if (!m_qtMovie)
    298         return;
    299 
    300     if (time > duration())
    301         time = duration();
    302 
    303     m_seekTo = time;
    304     if (maxTimeLoaded() >= m_seekTo)
    305         doSeek();
    306     else
    307         m_seekTimer.start(0, 0.5f);
    308 }
    309 
    310 void MediaPlayerPrivate::doSeek()
    311 {
    312     float oldRate = m_qtMovie->rate();
    313     if (oldRate)
    314         m_qtMovie->setRate(0);
    315     m_qtMovie->setCurrentTime(m_seekTo);
    316     float timeAfterSeek = currentTime();
    317     // restore playback only if not at end, othewise QTMovie will loop
    318     if (oldRate && timeAfterSeek < duration())
    319         m_qtMovie->setRate(oldRate);
    320     cancelSeek();
    321 }
    322 
    323 void MediaPlayerPrivate::cancelSeek()
    324 {
    325     m_seekTo = -1;
    326     m_seekTimer.stop();
    327 }
    328 
    329 void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*)
    330 {
    331     if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) {
    332         cancelSeek();
    333         updateStates();
    334         m_player->timeChanged();
    335         return;
    336     }
    337 
    338     if (maxTimeLoaded() >= m_seekTo)
    339         doSeek();
    340     else {
    341         MediaPlayer::NetworkState state = networkState();
    342         if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) {
    343             cancelSeek();
    344             updateStates();
    345             m_player->timeChanged();
    346         }
    347     }
    348 }
    349 
    350 bool MediaPlayerPrivate::paused() const
    351 {
    352     if (!m_qtMovie)
    353         return true;
    354     return (!m_qtMovie->rate());
    355 }
    356 
    357 bool MediaPlayerPrivate::seeking() const
    358 {
    359     if (!m_qtMovie)
    360         return false;
    361     return m_seekTo >= 0;
    362 }
    363 
    364 IntSize MediaPlayerPrivate::naturalSize() const
    365 {
    366     if (!m_qtMovie)
    367         return IntSize();
    368     int width;
    369     int height;
    370     m_qtMovie->getNaturalSize(width, height);
    371     return IntSize(width, height);
    372 }
    373 
    374 bool MediaPlayerPrivate::hasVideo() const
    375 {
    376     if (!m_qtMovie)
    377         return false;
    378     return m_qtMovie->hasVideo();
    379 }
    380 
    381 bool MediaPlayerPrivate::hasAudio() const
    382 {
    383     if (!m_qtMovie)
    384         return false;
    385     return m_qtMovie->hasAudio();
    386 }
    387 
    388 void MediaPlayerPrivate::setVolume(float volume)
    389 {
    390     if (!m_qtMovie)
    391         return;
    392     m_qtMovie->setVolume(volume);
    393 }
    394 
    395 void MediaPlayerPrivate::setRate(float rate)
    396 {
    397     if (!m_qtMovie)
    398         return;
    399     m_qtMovie->setRate(rate);
    400 }
    401 
    402 void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch)
    403 {
    404     if (!m_qtMovie)
    405         return;
    406     m_qtMovie->setPreservesPitch(preservesPitch);
    407 }
    408 
    409 bool MediaPlayerPrivate::hasClosedCaptions() const
    410 {
    411     if (!m_qtMovie)
    412         return false;
    413     return m_qtMovie->hasClosedCaptions();
    414 }
    415 
    416 void MediaPlayerPrivate::setClosedCaptionsVisible(bool visible)
    417 {
    418     if (!m_qtMovie)
    419         return;
    420     m_qtMovie->setClosedCaptionsVisible(visible);
    421 }
    422 
    423 PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const
    424 {
    425     RefPtr<TimeRanges> timeRanges = TimeRanges::create();
    426     float loaded = maxTimeLoaded();
    427     // rtsp streams are not buffered
    428     if (!m_isStreaming && loaded > 0)
    429         timeRanges->add(0, loaded);
    430     return timeRanges.release();
    431 }
    432 
    433 float MediaPlayerPrivate::maxTimeSeekable() const
    434 {
    435     // infinite duration means live stream
    436     return !isfinite(duration()) ? 0 : maxTimeLoaded();
    437 }
    438 
    439 float MediaPlayerPrivate::maxTimeLoaded() const
    440 {
    441     if (!m_qtMovie)
    442         return 0;
    443     return m_qtMovie->maxTimeLoaded();
    444 }
    445 
    446 unsigned MediaPlayerPrivate::bytesLoaded() const
    447 {
    448     if (!m_qtMovie)
    449         return 0;
    450     float dur = duration();
    451     float maxTime = maxTimeLoaded();
    452     if (!dur)
    453         return 0;
    454     return totalBytes() * maxTime / dur;
    455 }
    456 
    457 unsigned MediaPlayerPrivate::totalBytes() const
    458 {
    459     if (!m_qtMovie)
    460         return 0;
    461     return m_qtMovie->dataSize();
    462 }
    463 
    464 void MediaPlayerPrivate::cancelLoad()
    465 {
    466     if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded)
    467         return;
    468 
    469     tearDownVideoRendering();
    470 
    471     // Cancel the load by destroying the movie.
    472     m_qtMovie.clear();
    473 
    474     updateStates();
    475 }
    476 
    477 void MediaPlayerPrivate::updateStates()
    478 {
    479     MediaPlayer::NetworkState oldNetworkState = m_networkState;
    480     MediaPlayer::ReadyState oldReadyState = m_readyState;
    481 
    482     long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError;
    483 
    484     if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) {
    485         m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount);
    486         if (m_player->inMediaDocument()) {
    487             if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) {
    488                 // This is a type of media that we do not handle directly with a <video>
    489                 // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the
    490                 // MediaPlayerClient that we won't support it.
    491                 sawUnsupportedTracks();
    492                 return;
    493             }
    494         } else if (!m_enabledTrackCount)
    495             loadState = QTMovieLoadStateError;
    496     }
    497 
    498     // "Loaded" is reserved for fully buffered movies, never the case when streaming
    499     if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) {
    500         m_networkState = MediaPlayer::Loaded;
    501         m_readyState = MediaPlayer::HaveEnoughData;
    502     } else if (loadState >= QTMovieLoadStatePlaythroughOK) {
    503         m_readyState = MediaPlayer::HaveEnoughData;
    504     } else if (loadState >= QTMovieLoadStatePlayable) {
    505         // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967>
    506         m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData;
    507     } else if (loadState >= QTMovieLoadStateLoaded) {
    508         m_readyState = MediaPlayer::HaveMetadata;
    509     } else if (loadState > QTMovieLoadStateError) {
    510         m_networkState = MediaPlayer::Loading;
    511         m_readyState = MediaPlayer::HaveNothing;
    512     } else {
    513         if (m_player->inMediaDocument()) {
    514             // Something went wrong in the loading of media within a standalone file.
    515             // This can occur with chained ref movies that eventually resolve to a
    516             // file we don't support.
    517             sawUnsupportedTracks();
    518             return;
    519         }
    520 
    521         float loaded = maxTimeLoaded();
    522         if (!loaded)
    523             m_readyState = MediaPlayer::HaveNothing;
    524 
    525         if (!m_enabledTrackCount)
    526             m_networkState = MediaPlayer::FormatError;
    527         else {
    528             // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692>
    529             if (loaded > 0)
    530                 m_networkState = MediaPlayer::DecodeError;
    531             else
    532                 m_readyState = MediaPlayer::HaveNothing;
    533         }
    534     }
    535 
    536     if (isReadyForRendering() && !hasSetUpVideoRendering())
    537         setUpVideoRendering();
    538 
    539     if (seeking())
    540         m_readyState = MediaPlayer::HaveNothing;
    541 
    542     if (m_networkState != oldNetworkState)
    543         m_player->networkStateChanged();
    544     if (m_readyState != oldReadyState)
    545         m_player->readyStateChanged();
    546 }
    547 
    548 bool MediaPlayerPrivate::isReadyForRendering() const
    549 {
    550     return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
    551 }
    552 
    553 void MediaPlayerPrivate::sawUnsupportedTracks()
    554 {
    555     m_qtMovie->setDisabled(true);
    556     m_hasUnsupportedTracks = true;
    557     m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
    558 }
    559 
    560 void MediaPlayerPrivate::didEnd()
    561 {
    562     if (m_hasUnsupportedTracks)
    563         return;
    564 
    565     m_startedPlaying = false;
    566 #if DRAW_FRAME_RATE
    567     m_timeStoppedPlaying = WTF::currentTime();
    568 #endif
    569     updateStates();
    570     m_player->timeChanged();
    571 }
    572 
    573 void MediaPlayerPrivate::setSize(const IntSize& size)
    574 {
    575     if (m_hasUnsupportedTracks || !m_qtMovie || m_size == size)
    576         return;
    577     m_size = size;
    578     m_qtGWorld->setSize(size.width(), size.height());
    579 }
    580 
    581 void MediaPlayerPrivate::setVisible(bool visible)
    582 {
    583     if (m_hasUnsupportedTracks || !m_qtMovie || m_visible == visible)
    584         return;
    585 
    586     m_qtGWorld->setVisible(visible);
    587     m_visible = visible;
    588     if (m_visible) {
    589         if (isReadyForRendering())
    590             setUpVideoRendering();
    591     } else
    592         tearDownVideoRendering();
    593 }
    594 
    595 void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r)
    596 {
    597 #if USE(ACCELERATED_COMPOSITING)
    598     if (m_qtVideoLayer)
    599         return;
    600 #endif
    601     if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks)
    602         return;
    603 
    604     bool usingTempBitmap = false;
    605     OwnPtr<GraphicsContext::WindowsBitmap> bitmap;
    606     // FIXME: use LocalWindowsContext.
    607     HDC hdc = p->getWindowsContext(r);
    608     if (!hdc) {
    609         // The graphics context doesn't have an associated HDC so create a temporary
    610         // bitmap where QTMovieGWorld can draw the frame and we can copy it.
    611         usingTempBitmap = true;
    612         bitmap.set(p->createWindowsBitmap(r.size()));
    613         hdc = bitmap->hdc();
    614 
    615         // FIXME: is this necessary??
    616         XFORM xform;
    617         xform.eM11 = 1.0f;
    618         xform.eM12 = 0.0f;
    619         xform.eM21 = 0.0f;
    620         xform.eM22 = 1.0f;
    621         xform.eDx = -r.x();
    622         xform.eDy = -r.y();
    623         SetWorldTransform(hdc, &xform);
    624     }
    625 
    626     m_qtGWorld->paint(hdc, r.x(), r.y());
    627     if (usingTempBitmap)
    628         p->drawWindowsBitmap(bitmap.get(), r.location());
    629     else
    630         p->releaseWindowsContext(hdc, r);
    631 
    632     paintCompleted(*p, r);
    633 }
    634 
    635 void MediaPlayerPrivate::paintCompleted(GraphicsContext& context, const IntRect& rect)
    636 {
    637     m_newFrameAvailable = false;
    638 
    639 #if DRAW_FRAME_RATE
    640     if (m_frameCountWhilePlaying > 10) {
    641         double interval =  m_startedPlaying ? WTF::currentTime() - m_timeStartedPlaying : m_timeStoppedPlaying - m_timeStartedPlaying;
    642         double frameRate = (m_frameCountWhilePlaying - 1) / interval;
    643         CGContextRef cgContext = context.platformContext();
    644         CGRect drawRect = rect;
    645 
    646         char text[8];
    647         _snprintf(text, sizeof(text), "%1.2f", frameRate);
    648 
    649         static const int fontSize = 25;
    650         static const int fontCharWidth = 12;
    651         static const int boxHeight = 25;
    652         static const int boxBorderWidth = 4;
    653         drawRect.size.width = boxBorderWidth * 2 + fontCharWidth * strlen(text);
    654         drawRect.size.height = boxHeight;
    655 
    656         CGContextSaveGState(cgContext);
    657 #if USE(ACCELERATED_COMPOSITING)
    658         if (m_qtVideoLayer)
    659             CGContextScaleCTM(cgContext, 1, -1);
    660         CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, m_qtVideoLayer ? -rect.height() : 0);
    661 #else
    662         CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, 0);
    663 #endif
    664         static const CGFloat backgroundColor[4] = { 0.98, 0.98, 0.82, 0.8 };
    665         CGContextSetFillColor(cgContext, backgroundColor);
    666         CGContextFillRect(cgContext, drawRect);
    667 
    668         static const CGFloat textColor[4] = { 0, 0, 0, 1 };
    669         CGContextSetFillColor(cgContext, textColor);
    670         CGContextSetTextMatrix(cgContext, CGAffineTransformMakeScale(1, -1));
    671         CGContextSelectFont(cgContext, "Helvetica", fontSize, kCGEncodingMacRoman);
    672 
    673         CGContextShowTextAtPoint(cgContext, drawRect.origin.x + boxBorderWidth, drawRect.origin.y + boxHeight - boxBorderWidth, text, strlen(text));
    674 
    675         CGContextRestoreGState(cgContext);
    676     }
    677 #endif
    678 }
    679 
    680 static HashSet<String> mimeTypeCache()
    681 {
    682     DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ());
    683     static bool typeListInitialized = false;
    684 
    685     if (!typeListInitialized) {
    686         unsigned count = QTMovie::countSupportedTypes();
    687         for (unsigned n = 0; n < count; n++) {
    688             const UChar* character;
    689             unsigned len;
    690             QTMovie::getSupportedType(n, character, len);
    691             if (len)
    692                 typeCache.add(String(character, len));
    693         }
    694 
    695         typeListInitialized = true;
    696     }
    697 
    698     return typeCache;
    699 }
    700 
    701 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types)
    702 {
    703     types = mimeTypeCache();
    704 }
    705 
    706 bool MediaPlayerPrivate::isAvailable()
    707 {
    708     return QTMovie::initializeQuickTime();
    709 }
    710 
    711 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs)
    712 {
    713     // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an
    714     //  extended MIME type
    715     return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported;
    716 }
    717 
    718 void MediaPlayerPrivate::movieEnded(QTMovie* movie)
    719 {
    720     if (m_hasUnsupportedTracks)
    721         return;
    722 
    723     ASSERT(m_qtMovie.get() == movie);
    724     didEnd();
    725 }
    726 
    727 void MediaPlayerPrivate::movieLoadStateChanged(QTMovie* movie)
    728 {
    729     if (m_hasUnsupportedTracks)
    730         return;
    731 
    732     ASSERT(m_qtMovie.get() == movie);
    733     updateStates();
    734 }
    735 
    736 void MediaPlayerPrivate::movieTimeChanged(QTMovie* movie)
    737 {
    738     if (m_hasUnsupportedTracks)
    739         return;
    740 
    741     ASSERT(m_qtMovie.get() == movie);
    742     updateStates();
    743     m_player->timeChanged();
    744 }
    745 
    746 void MediaPlayerPrivate::movieNewImageAvailable(QTMovieGWorld* movie)
    747 {
    748     if (m_hasUnsupportedTracks)
    749         return;
    750 
    751     ASSERT(m_qtGWorld.get() == movie);
    752 #if DRAW_FRAME_RATE
    753     if (m_startedPlaying) {
    754         m_frameCountWhilePlaying++;
    755         // To eliminate preroll costs from our calculation, our frame rate calculation excludes
    756         // the first frame drawn after playback starts.
    757         if (m_frameCountWhilePlaying == 1)
    758             m_timeStartedPlaying = WTF::currentTime();
    759     }
    760 #endif
    761 
    762     m_newFrameAvailable = true;
    763 
    764 #if USE(ACCELERATED_COMPOSITING)
    765     if (m_qtVideoLayer)
    766         m_qtVideoLayer->setNeedsDisplay();
    767     else
    768 #endif
    769         m_player->repaint();
    770 }
    771 
    772 bool MediaPlayerPrivate::hasSingleSecurityOrigin() const
    773 {
    774     // We tell quicktime to disallow resources that come from different origins
    775     // so we all media is single origin.
    776     return true;
    777 }
    778 
    779 MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const
    780 {
    781     if (!m_qtMovie)
    782         return MediaRenderingNone;
    783 
    784 #if USE(ACCELERATED_COMPOSITING)
    785     if (m_qtVideoLayer)
    786         return MediaRenderingMovieLayer;
    787 #endif
    788 
    789     return MediaRenderingSoftwareRenderer;
    790 }
    791 
    792 MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const
    793 {
    794     if (!m_player->frameView() || !m_qtMovie)
    795         return MediaRenderingNone;
    796 
    797 #if USE(ACCELERATED_COMPOSITING)
    798     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
    799         return MediaRenderingMovieLayer;
    800 #endif
    801 
    802     return MediaRenderingSoftwareRenderer;
    803 }
    804 
    805 void MediaPlayerPrivate::setUpVideoRendering()
    806 {
    807     MediaRenderingMode currentMode = currentRenderingMode();
    808     MediaRenderingMode preferredMode = preferredRenderingMode();
    809 
    810 #if !USE(ACCELERATED_COMPOSITING)
    811     ASSERT(preferredMode != MediaRenderingMovieLayer);
    812 #endif
    813 
    814     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
    815         return;
    816 
    817     if (currentMode != MediaRenderingNone)
    818         tearDownVideoRendering();
    819 
    820     if (preferredMode == MediaRenderingMovieLayer)
    821         createLayerForMovie();
    822 
    823 #if USE(ACCELERATED_COMPOSITING)
    824     if (currentMode == MediaRenderingMovieLayer || preferredMode == MediaRenderingMovieLayer)
    825         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
    826 #endif
    827 }
    828 
    829 void MediaPlayerPrivate::tearDownVideoRendering()
    830 {
    831 #if USE(ACCELERATED_COMPOSITING)
    832     if (m_qtVideoLayer)
    833         destroyLayerForMovie();
    834 #endif
    835 }
    836 
    837 bool MediaPlayerPrivate::hasSetUpVideoRendering() const
    838 {
    839 #if USE(ACCELERATED_COMPOSITING)
    840     return m_qtVideoLayer || currentRenderingMode() != MediaRenderingMovieLayer;
    841 #else
    842     return true;
    843 #endif
    844 }
    845 
    846 #if USE(ACCELERATED_COMPOSITING)
    847 
    848 // Up-call from compositing layer drawing callback.
    849 void MediaPlayerPrivate::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect&)
    850 {
    851      if (m_hasUnsupportedTracks)
    852          return;
    853 
    854     ASSERT(supportsAcceleratedRendering());
    855 
    856     // No reason to replace the current layer image unless we have something new to show.
    857     if (!m_newFrameAvailable)
    858         return;
    859 
    860     static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    861     void* buffer;
    862     unsigned bitsPerPixel;
    863     unsigned rowBytes;
    864     unsigned width;
    865     unsigned height;
    866 
    867     m_qtGWorld->getCurrentFrameInfo(buffer, bitsPerPixel, rowBytes, width, height);
    868     if (!buffer)
    869         return;
    870 
    871     RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(0, static_cast<UInt8*>(buffer), rowBytes * height, kCFAllocatorNull));
    872     RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
    873     RetainPtr<CGImageRef> frameImage(AdoptCF, CGImageCreate(width, height, 8, bitsPerPixel, rowBytes, colorSpace,
    874         kCGBitmapByteOrder32Little | kCGImageAlphaFirst, provider.get(), 0, false, kCGRenderingIntentDefault));
    875     if (!frameImage)
    876         return;
    877 
    878     IntRect rect(0, 0, m_size.width(), m_size.height());
    879     CGContextDrawImage(context.platformContext(), rect, frameImage.get());
    880     paintCompleted(context, rect);
    881 }
    882 #endif
    883 
    884 void MediaPlayerPrivate::createLayerForMovie()
    885 {
    886 #if USE(ACCELERATED_COMPOSITING)
    887     ASSERT(supportsAcceleratedRendering());
    888 
    889     if (!m_qtMovie || m_qtVideoLayer)
    890         return;
    891 
    892     // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used
    893     // as a wrapper for a PlatformCALayer which gets inserted as the content layer of the video
    894     // renderer's GraphicsLayer.
    895     m_qtVideoLayer.set(new GraphicsLayerCACF(this));
    896     if (!m_qtVideoLayer)
    897         return;
    898 
    899     // Mark the layer as drawing itself, anchored in the top left, and bottom-up.
    900     m_qtVideoLayer->setDrawsContent(true);
    901     m_qtVideoLayer->setAnchorPoint(FloatPoint3D());
    902     m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp);
    903 #ifndef NDEBUG
    904     m_qtVideoLayer->setName("Video layer");
    905 #endif
    906     // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
    907 #endif
    908 }
    909 
    910 void MediaPlayerPrivate::destroyLayerForMovie()
    911 {
    912 #if USE(ACCELERATED_COMPOSITING)
    913     if (!m_qtVideoLayer)
    914         return;
    915     m_qtVideoLayer = 0;
    916 #endif
    917 }
    918 
    919 #if USE(ACCELERATED_COMPOSITING)
    920 bool MediaPlayerPrivate::supportsAcceleratedRendering() const
    921 {
    922     return isReadyForRendering();
    923 }
    924 
    925 void MediaPlayerPrivate::acceleratedRenderingStateChanged()
    926 {
    927     // Set up or change the rendering path if necessary.
    928     setUpVideoRendering();
    929 }
    930 
    931 #endif
    932 
    933 
    934 }
    935 
    936 #endif
    937