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
69using namespace std;
70
71namespace WebCore {
72
73static CGImageRef CreateCGImageFromPixelBuffer(QTPixelBuffer buffer);
74static bool requiredDllsAvailable();
75
76SOFT_LINK_LIBRARY(Wininet)
77SOFT_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
80class MediaPlayerPrivateQuickTimeVisualContext::MovieClient : public QTMovieClient {
81public:
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*);
87private:
88    MediaPlayerPrivateQuickTimeVisualContext* m_parent;
89};
90
91#if USE(ACCELERATED_COMPOSITING)
92class MediaPlayerPrivateQuickTimeVisualContext::LayerClient : public PlatformCALayerClient {
93public:
94    LayerClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
95    virtual ~LayerClient() { m_parent = 0; }
96
97private:
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
115void 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
141class MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient : public QTMovieVisualContextClient {
142public:
143    VisualContextClient(MediaPlayerPrivateQuickTimeVisualContext* parent) : m_parent(parent) {}
144    virtual ~VisualContextClient() { m_parent = 0; }
145    void imageAvailableForTime(const QTCVTimeStamp*);
146    static void retrieveCurrentImageProc(void*);
147private:
148    MediaPlayerPrivateQuickTimeVisualContext* m_parent;
149};
150
151MediaPlayerPrivateInterface* MediaPlayerPrivateQuickTimeVisualContext::create(MediaPlayer* player)
152{
153    return new MediaPlayerPrivateQuickTimeVisualContext(player);
154}
155
156void MediaPlayerPrivateQuickTimeVisualContext::registerMediaEngine(MediaEngineRegistrar registrar)
157{
158    if (isAvailable())
159        registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
160}
161
162MediaPlayerPrivateQuickTimeVisualContext::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
188MediaPlayerPrivateQuickTimeVisualContext::~MediaPlayerPrivateQuickTimeVisualContext()
189{
190    tearDownVideoRendering();
191    cancelCallOnMainThread(&VisualContextClient::retrieveCurrentImageProc, this);
192}
193
194bool 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
204PlatformMedia 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
213PlatformLayer* MediaPlayerPrivateQuickTimeVisualContext::platformLayer() const
214{
215    return m_transformLayer ? m_transformLayer->platformLayer() : 0;
216}
217#endif
218
219String 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
240static 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
257void 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
310static 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
328void MediaPlayerPrivateQuickTimeVisualContext::resumeLoad()
329{
330    m_delayingLoad = false;
331
332    if (!m_movieURL.isEmpty())
333        loadInternal(m_movieURL);
334}
335
336void 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
348void 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
388void MediaPlayerPrivateQuickTimeVisualContext::prepareToPlay()
389{
390    if (!m_movie || m_delayingLoad)
391        resumeLoad();
392}
393
394void 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
404void MediaPlayerPrivateQuickTimeVisualContext::pause()
405{
406    if (!m_movie)
407        return;
408    m_startedPlaying = false;
409
410    m_movie->pause();
411    m_visualContextTimer.stop();
412}
413
414float MediaPlayerPrivateQuickTimeVisualContext::duration() const
415{
416    if (!m_movie)
417        return 0;
418    return m_movie->duration();
419}
420
421float MediaPlayerPrivateQuickTimeVisualContext::currentTime() const
422{
423    if (!m_movie)
424        return 0;
425    return m_movie->currentTime();
426}
427
428void 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
445void 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
458void MediaPlayerPrivateQuickTimeVisualContext::cancelSeek()
459{
460    m_seekTo = -1;
461    m_seekTimer.stop();
462}
463
464void 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
485bool MediaPlayerPrivateQuickTimeVisualContext::paused() const
486{
487    if (!m_movie)
488        return true;
489    return (!m_movie->rate());
490}
491
492bool MediaPlayerPrivateQuickTimeVisualContext::seeking() const
493{
494    if (!m_movie)
495        return false;
496    return m_seekTo >= 0;
497}
498
499IntSize 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
515bool MediaPlayerPrivateQuickTimeVisualContext::hasVideo() const
516{
517    if (!m_movie)
518        return false;
519    return m_movie->hasVideo();
520}
521
522bool MediaPlayerPrivateQuickTimeVisualContext::hasAudio() const
523{
524    if (!m_movie)
525        return false;
526    return m_movie->hasAudio();
527}
528
529void MediaPlayerPrivateQuickTimeVisualContext::setVolume(float volume)
530{
531    if (!m_movie)
532        return;
533    m_movie->setVolume(volume);
534}
535
536void 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
548void MediaPlayerPrivateQuickTimeVisualContext::setPreservesPitch(bool preservesPitch)
549{
550    if (!m_movie)
551        return;
552    m_movie->setPreservesPitch(preservesPitch);
553}
554
555bool MediaPlayerPrivateQuickTimeVisualContext::hasClosedCaptions() const
556{
557    if (!m_movie)
558        return false;
559    return m_movie->hasClosedCaptions();
560}
561
562void MediaPlayerPrivateQuickTimeVisualContext::setClosedCaptionsVisible(bool visible)
563{
564    if (!m_movie)
565        return;
566    m_movie->setClosedCaptionsVisible(visible);
567}
568
569PassRefPtr<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
579float MediaPlayerPrivateQuickTimeVisualContext::maxTimeSeekable() const
580{
581    // infinite duration means live stream
582    return !isfinite(duration()) ? 0 : maxTimeLoaded();
583}
584
585float MediaPlayerPrivateQuickTimeVisualContext::maxTimeLoaded() const
586{
587    if (!m_movie)
588        return 0;
589    return m_movie->maxTimeLoaded();
590}
591
592unsigned 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
603unsigned MediaPlayerPrivateQuickTimeVisualContext::totalBytes() const
604{
605    if (!m_movie)
606        return 0;
607    return m_movie->dataSize();
608}
609
610void 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
623void 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
694bool MediaPlayerPrivateQuickTimeVisualContext::isReadyForRendering() const
695{
696    return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
697}
698
699void MediaPlayerPrivateQuickTimeVisualContext::sawUnsupportedTracks()
700{
701    m_movie->setDisabled(true);
702    m_hasUnsupportedTracks = true;
703    m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player);
704}
705
706void MediaPlayerPrivateQuickTimeVisualContext::didEnd()
707{
708    if (m_hasUnsupportedTracks)
709        return;
710
711    m_startedPlaying = false;
712
713    updateStates();
714    m_player->timeChanged();
715}
716
717void MediaPlayerPrivateQuickTimeVisualContext::setSize(const IntSize& size)
718{
719    if (m_hasUnsupportedTracks || !m_movie || m_size == size)
720        return;
721    m_size = size;
722}
723
724void 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
737void 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
780void MediaPlayerPrivateQuickTimeVisualContext::paintCompleted(GraphicsContext& context, const IntRect& rect)
781{
782    m_newFrameAvailable = false;
783}
784
785void MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient::retrieveCurrentImageProc(void* refcon)
786{
787    static_cast<MediaPlayerPrivateQuickTimeVisualContext*>(refcon)->retrieveCurrentImage();
788}
789
790void 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
799void MediaPlayerPrivateQuickTimeVisualContext::visualContextTimerFired(Timer<MediaPlayerPrivateQuickTimeVisualContext>*)
800{
801    if (m_visualContext && m_visualContext->isImageAvailableForTime(0))
802        retrieveCurrentImage();
803}
804
805static 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
814static 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
857Bail:
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
871void 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
926static 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
947static 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
978static 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
1010void MediaPlayerPrivateQuickTimeVisualContext::getSupportedTypes(HashSet<String>& types)
1011{
1012    types = mimeTypeCache();
1013}
1014
1015bool MediaPlayerPrivateQuickTimeVisualContext::isAvailable()
1016{
1017    return QTMovie::initializeQuickTime();
1018}
1019
1020MediaPlayer::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
1027void 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
1038void 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
1047void 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
1057bool 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
1064void MediaPlayerPrivateQuickTimeVisualContext::setPreload(MediaPlayer::Preload preload)
1065{
1066    m_preload = preload;
1067    if (m_delayingLoad && m_preload != MediaPlayer::None)
1068        resumeLoad();
1069}
1070
1071float 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
1081MediaPlayerPrivateQuickTimeVisualContext::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
1094MediaPlayerPrivateQuickTimeVisualContext::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
1107void 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
1135void MediaPlayerPrivateQuickTimeVisualContext::tearDownVideoRendering()
1136{
1137#if USE(ACCELERATED_COMPOSITING)
1138    if (m_qtVideoLayer)
1139        destroyLayerForMovie();
1140#endif
1141
1142    m_visualContext = 0;
1143}
1144
1145bool 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
1154void 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
1188void 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
1234void 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)
1251bool MediaPlayerPrivateQuickTimeVisualContext::supportsAcceleratedRendering() const
1252{
1253    return isReadyForRendering();
1254}
1255
1256void MediaPlayerPrivateQuickTimeVisualContext::acceleratedRenderingStateChanged()
1257{
1258    // Set up or change the rendering path if necessary.
1259    setUpVideoRendering();
1260}
1261
1262void 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