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