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