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