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