1 /* 2 * Copyright (C) 2007, 2008, 2009, 2010 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 "MediaPlayerPrivateQuickTimeWin.h" 30 31 #include "GraphicsContext.h" 32 #include "KURL.h" 33 #include "QTMovieWin.h" 34 #include "ScrollView.h" 35 #include "StringHash.h" 36 #include "TimeRanges.h" 37 #include "Timer.h" 38 #include <wtf/CurrentTime.h> 39 #include <wtf/HashSet.h> 40 #include <wtf/MathExtras.h> 41 #include <wtf/StdLibExtras.h> 42 43 #if USE(ACCELERATED_COMPOSITING) 44 #include "GraphicsLayerCACF.h" 45 #include "WKCACFLayer.h" 46 #endif 47 48 #if DRAW_FRAME_RATE 49 #include "Font.h" 50 #include "FrameView.h" 51 #include "Frame.h" 52 #include "Document.h" 53 #include "RenderObject.h" 54 #include "RenderStyle.h" 55 #include "Windows.h" 56 #endif 57 58 using namespace std; 59 60 namespace WebCore { 61 62 MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) 63 { 64 return new MediaPlayerPrivate(player); 65 } 66 67 void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) 68 { 69 if (isAvailable()) 70 registrar(create, getSupportedTypes, supportsType); 71 } 72 73 MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) 74 : m_player(player) 75 , m_seekTo(-1) 76 , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) 77 , m_networkState(MediaPlayer::Empty) 78 , m_readyState(MediaPlayer::HaveNothing) 79 , m_enabledTrackCount(0) 80 , m_totalTrackCount(0) 81 , m_hasUnsupportedTracks(false) 82 , m_startedPlaying(false) 83 , m_isStreaming(false) 84 , m_visible(false) 85 , m_newFrameAvailable(false) 86 #if DRAW_FRAME_RATE 87 , m_frameCountWhilePlaying(0) 88 , m_timeStartedPlaying(0) 89 , m_timeStoppedPlaying(0) 90 #endif 91 { 92 } 93 94 MediaPlayerPrivate::~MediaPlayerPrivate() 95 { 96 tearDownVideoRendering(); 97 } 98 99 bool MediaPlayerPrivate::supportsFullscreen() const 100 { 101 return true; 102 } 103 104 PlatformMedia MediaPlayerPrivate::platformMedia() const 105 { 106 PlatformMedia p; 107 p.qtMovie = reinterpret_cast<QTMovie*>(m_qtMovie.get()); 108 return p; 109 } 110 111 class TaskTimer : TimerBase { 112 public: 113 static void initialize(); 114 115 private: 116 static void setTaskTimerDelay(double); 117 static void stopTaskTimer(); 118 119 void fired(); 120 121 static TaskTimer* s_timer; 122 }; 123 124 TaskTimer* TaskTimer::s_timer = 0; 125 126 void TaskTimer::initialize() 127 { 128 if (s_timer) 129 return; 130 131 s_timer = new TaskTimer; 132 133 QTMovieWin::setTaskTimerFuncs(setTaskTimerDelay, stopTaskTimer); 134 } 135 136 void TaskTimer::setTaskTimerDelay(double delayInSeconds) 137 { 138 ASSERT(s_timer); 139 140 s_timer->startOneShot(delayInSeconds); 141 } 142 143 void TaskTimer::stopTaskTimer() 144 { 145 ASSERT(s_timer); 146 147 s_timer->stop(); 148 } 149 150 void TaskTimer::fired() 151 { 152 QTMovieWin::taskTimerFired(); 153 } 154 155 void MediaPlayerPrivate::load(const String& url) 156 { 157 if (!QTMovieWin::initializeQuickTime()) { 158 // FIXME: is this the right error to return? 159 m_networkState = MediaPlayer::DecodeError; 160 m_player->networkStateChanged(); 161 return; 162 } 163 164 // Initialize the task timer. 165 TaskTimer::initialize(); 166 167 if (m_networkState != MediaPlayer::Loading) { 168 m_networkState = MediaPlayer::Loading; 169 m_player->networkStateChanged(); 170 } 171 if (m_readyState != MediaPlayer::HaveNothing) { 172 m_readyState = MediaPlayer::HaveNothing; 173 m_player->readyStateChanged(); 174 } 175 cancelSeek(); 176 177 m_qtMovie.set(new QTMovieWin(this)); 178 m_qtMovie->load(url.characters(), url.length(), m_player->preservesPitch()); 179 m_qtMovie->setVolume(m_player->volume()); 180 m_qtMovie->setVisible(m_player->visible()); 181 } 182 183 void MediaPlayerPrivate::play() 184 { 185 if (!m_qtMovie) 186 return; 187 m_startedPlaying = true; 188 #if DRAW_FRAME_RATE 189 m_frameCountWhilePlaying = 0; 190 #endif 191 192 m_qtMovie->play(); 193 } 194 195 void MediaPlayerPrivate::pause() 196 { 197 if (!m_qtMovie) 198 return; 199 m_startedPlaying = false; 200 #if DRAW_FRAME_RATE 201 m_timeStoppedPlaying = WTF::currentTime(); 202 #endif 203 m_qtMovie->pause(); 204 } 205 206 float MediaPlayerPrivate::duration() const 207 { 208 if (!m_qtMovie) 209 return 0; 210 return m_qtMovie->duration(); 211 } 212 213 float MediaPlayerPrivate::currentTime() const 214 { 215 if (!m_qtMovie) 216 return 0; 217 return m_qtMovie->currentTime(); 218 } 219 220 void MediaPlayerPrivate::seek(float time) 221 { 222 cancelSeek(); 223 224 if (!m_qtMovie) 225 return; 226 227 if (time > duration()) 228 time = duration(); 229 230 m_seekTo = time; 231 if (maxTimeLoaded() >= m_seekTo) 232 doSeek(); 233 else 234 m_seekTimer.start(0, 0.5f); 235 } 236 237 void MediaPlayerPrivate::doSeek() 238 { 239 float oldRate = m_qtMovie->rate(); 240 if (oldRate) 241 m_qtMovie->setRate(0); 242 m_qtMovie->setCurrentTime(m_seekTo); 243 float timeAfterSeek = currentTime(); 244 // restore playback only if not at end, othewise QTMovie will loop 245 if (oldRate && timeAfterSeek < duration()) 246 m_qtMovie->setRate(oldRate); 247 cancelSeek(); 248 } 249 250 void MediaPlayerPrivate::cancelSeek() 251 { 252 m_seekTo = -1; 253 m_seekTimer.stop(); 254 } 255 256 void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) 257 { 258 if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) { 259 cancelSeek(); 260 updateStates(); 261 m_player->timeChanged(); 262 return; 263 } 264 265 if (maxTimeLoaded() >= m_seekTo) 266 doSeek(); 267 else { 268 MediaPlayer::NetworkState state = networkState(); 269 if (state == MediaPlayer::Empty || state == MediaPlayer::Loaded) { 270 cancelSeek(); 271 updateStates(); 272 m_player->timeChanged(); 273 } 274 } 275 } 276 277 bool MediaPlayerPrivate::paused() const 278 { 279 if (!m_qtMovie) 280 return true; 281 return m_qtMovie->rate() == 0.0f; 282 } 283 284 bool MediaPlayerPrivate::seeking() const 285 { 286 if (!m_qtMovie) 287 return false; 288 return m_seekTo >= 0; 289 } 290 291 IntSize MediaPlayerPrivate::naturalSize() const 292 { 293 if (!m_qtMovie) 294 return IntSize(); 295 int width; 296 int height; 297 m_qtMovie->getNaturalSize(width, height); 298 return IntSize(width, height); 299 } 300 301 bool MediaPlayerPrivate::hasVideo() const 302 { 303 if (!m_qtMovie) 304 return false; 305 return m_qtMovie->hasVideo(); 306 } 307 308 bool MediaPlayerPrivate::hasAudio() const 309 { 310 if (!m_qtMovie) 311 return false; 312 return m_qtMovie->hasAudio(); 313 } 314 315 void MediaPlayerPrivate::setVolume(float volume) 316 { 317 if (!m_qtMovie) 318 return; 319 m_qtMovie->setVolume(volume); 320 } 321 322 void MediaPlayerPrivate::setRate(float rate) 323 { 324 if (!m_qtMovie) 325 return; 326 m_qtMovie->setRate(rate); 327 } 328 329 void MediaPlayerPrivate::setPreservesPitch(bool preservesPitch) 330 { 331 if (!m_qtMovie) 332 return; 333 m_qtMovie->setPreservesPitch(preservesPitch); 334 } 335 336 bool MediaPlayerPrivate::hasClosedCaptions() const 337 { 338 if (!m_qtMovie) 339 return false; 340 return m_qtMovie->hasClosedCaptions(); 341 } 342 343 void MediaPlayerPrivate::setClosedCaptionsVisible(bool visible) 344 { 345 if (!m_qtMovie) 346 return; 347 m_qtMovie->setClosedCaptionsVisible(visible); 348 } 349 350 PassRefPtr<TimeRanges> MediaPlayerPrivate::buffered() const 351 { 352 RefPtr<TimeRanges> timeRanges = TimeRanges::create(); 353 float loaded = maxTimeLoaded(); 354 // rtsp streams are not buffered 355 if (!m_isStreaming && loaded > 0) 356 timeRanges->add(0, loaded); 357 return timeRanges.release(); 358 } 359 360 float MediaPlayerPrivate::maxTimeSeekable() const 361 { 362 // infinite duration means live stream 363 return !isfinite(duration()) ? 0 : maxTimeLoaded(); 364 } 365 366 float MediaPlayerPrivate::maxTimeLoaded() const 367 { 368 if (!m_qtMovie) 369 return 0; 370 return m_qtMovie->maxTimeLoaded(); 371 } 372 373 unsigned MediaPlayerPrivate::bytesLoaded() const 374 { 375 if (!m_qtMovie) 376 return 0; 377 float dur = duration(); 378 float maxTime = maxTimeLoaded(); 379 if (!dur) 380 return 0; 381 return totalBytes() * maxTime / dur; 382 } 383 384 unsigned MediaPlayerPrivate::totalBytes() const 385 { 386 if (!m_qtMovie) 387 return 0; 388 return m_qtMovie->dataSize(); 389 } 390 391 void MediaPlayerPrivate::cancelLoad() 392 { 393 if (m_networkState < MediaPlayer::Loading || m_networkState == MediaPlayer::Loaded) 394 return; 395 396 tearDownVideoRendering(); 397 398 // Cancel the load by destroying the movie. 399 m_qtMovie.clear(); 400 401 updateStates(); 402 } 403 404 void MediaPlayerPrivate::updateStates() 405 { 406 MediaPlayer::NetworkState oldNetworkState = m_networkState; 407 MediaPlayer::ReadyState oldReadyState = m_readyState; 408 409 long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError; 410 411 if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata) { 412 m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount, m_totalTrackCount); 413 if (m_player->inMediaDocument()) { 414 if (!m_enabledTrackCount || m_enabledTrackCount != m_totalTrackCount) { 415 // This is a type of media that we do not handle directly with a <video> 416 // element, eg. QuickTime VR, a movie with a sprite track, etc. Tell the 417 // MediaPlayerClient that we won't support it. 418 sawUnsupportedTracks(); 419 return; 420 } 421 } else if (!m_enabledTrackCount) 422 loadState = QTMovieLoadStateError; 423 } 424 425 // "Loaded" is reserved for fully buffered movies, never the case when streaming 426 if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { 427 m_networkState = MediaPlayer::Loaded; 428 m_readyState = MediaPlayer::HaveEnoughData; 429 } else if (loadState >= QTMovieLoadStatePlaythroughOK) { 430 m_readyState = MediaPlayer::HaveEnoughData; 431 } else if (loadState >= QTMovieLoadStatePlayable) { 432 // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> 433 m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; 434 } else if (loadState >= QTMovieLoadStateLoaded) { 435 m_readyState = MediaPlayer::HaveMetadata; 436 } else if (loadState > QTMovieLoadStateError) { 437 m_networkState = MediaPlayer::Loading; 438 m_readyState = MediaPlayer::HaveNothing; 439 } else { 440 if (m_player->inMediaDocument()) { 441 // Something went wrong in the loading of media within a standalone file. 442 // This can occur with chained ref movies that eventually resolve to a 443 // file we don't support. 444 sawUnsupportedTracks(); 445 return; 446 } 447 448 float loaded = maxTimeLoaded(); 449 if (!loaded) 450 m_readyState = MediaPlayer::HaveNothing; 451 452 if (!m_enabledTrackCount) 453 m_networkState = MediaPlayer::FormatError; 454 else { 455 // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> 456 if (loaded > 0) 457 m_networkState = MediaPlayer::DecodeError; 458 else 459 m_readyState = MediaPlayer::HaveNothing; 460 } 461 } 462 463 if (isReadyForRendering() && !hasSetUpVideoRendering()) 464 setUpVideoRendering(); 465 466 if (seeking()) 467 m_readyState = MediaPlayer::HaveNothing; 468 469 if (m_networkState != oldNetworkState) 470 m_player->networkStateChanged(); 471 if (m_readyState != oldReadyState) 472 m_player->readyStateChanged(); 473 } 474 475 bool MediaPlayerPrivate::isReadyForRendering() const 476 { 477 return m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); 478 } 479 480 void MediaPlayerPrivate::sawUnsupportedTracks() 481 { 482 m_qtMovie->setDisabled(true); 483 m_hasUnsupportedTracks = true; 484 m_player->mediaPlayerClient()->mediaPlayerSawUnsupportedTracks(m_player); 485 } 486 487 void MediaPlayerPrivate::didEnd() 488 { 489 if (m_hasUnsupportedTracks) 490 return; 491 492 m_startedPlaying = false; 493 #if DRAW_FRAME_RATE 494 m_timeStoppedPlaying = WTF::currentTime(); 495 #endif 496 updateStates(); 497 m_player->timeChanged(); 498 } 499 500 void MediaPlayerPrivate::setSize(const IntSize& size) 501 { 502 if (m_hasUnsupportedTracks || !m_qtMovie || m_size == size) 503 return; 504 m_size = size; 505 m_qtMovie->setSize(size.width(), size.height()); 506 } 507 508 void MediaPlayerPrivate::setVisible(bool visible) 509 { 510 if (m_hasUnsupportedTracks || !m_qtMovie || m_visible == visible) 511 return; 512 513 m_qtMovie->setVisible(visible); 514 m_visible = visible; 515 if (m_visible) { 516 if (isReadyForRendering()) 517 setUpVideoRendering(); 518 } else 519 tearDownVideoRendering(); 520 } 521 522 void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) 523 { 524 #if USE(ACCELERATED_COMPOSITING) 525 if (m_qtVideoLayer) 526 return; 527 #endif 528 if (p->paintingDisabled() || !m_qtMovie || m_hasUnsupportedTracks) 529 return; 530 531 bool usingTempBitmap = false; 532 OwnPtr<GraphicsContext::WindowsBitmap> bitmap; 533 HDC hdc = p->getWindowsContext(r); 534 if (!hdc) { 535 // The graphics context doesn't have an associated HDC so create a temporary 536 // bitmap where QTMovieWin can draw the frame and we can copy it. 537 usingTempBitmap = true; 538 bitmap.set(p->createWindowsBitmap(r.size())); 539 hdc = bitmap->hdc(); 540 541 // FIXME: is this necessary?? 542 XFORM xform; 543 xform.eM11 = 1.0f; 544 xform.eM12 = 0.0f; 545 xform.eM21 = 0.0f; 546 xform.eM22 = 1.0f; 547 xform.eDx = -r.x(); 548 xform.eDy = -r.y(); 549 SetWorldTransform(hdc, &xform); 550 } 551 552 m_qtMovie->paint(hdc, r.x(), r.y()); 553 if (usingTempBitmap) 554 p->drawWindowsBitmap(bitmap.get(), r.topLeft()); 555 else 556 p->releaseWindowsContext(hdc, r); 557 558 paintCompleted(*p, r); 559 } 560 561 void MediaPlayerPrivate::paintCompleted(GraphicsContext& context, const IntRect& rect) 562 { 563 m_newFrameAvailable = false; 564 565 #if DRAW_FRAME_RATE 566 if (m_frameCountWhilePlaying > 10) { 567 double interval = m_startedPlaying ? WTF::currentTime() - m_timeStartedPlaying : m_timeStoppedPlaying - m_timeStartedPlaying; 568 double frameRate = (m_frameCountWhilePlaying - 1) / interval; 569 CGContextRef cgContext = context.platformContext(); 570 CGRect drawRect = rect; 571 572 char text[8]; 573 _snprintf(text, sizeof(text), "%1.2f", frameRate); 574 575 static const int fontSize = 25; 576 static const int fontCharWidth = 12; 577 static const int boxHeight = 25; 578 static const int boxBorderWidth = 4; 579 drawRect.size.width = boxBorderWidth * 2 + fontCharWidth * strlen(text); 580 drawRect.size.height = boxHeight; 581 582 CGContextSaveGState(cgContext); 583 #if USE(ACCELERATED_COMPOSITING) 584 if (m_qtVideoLayer) 585 CGContextScaleCTM(cgContext, 1, -1); 586 CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, m_qtVideoLayer ? -rect.height() : 0); 587 #else 588 CGContextTranslateCTM(cgContext, rect.width() - drawRect.size.width, 0); 589 #endif 590 static const CGFloat backgroundColor[4] = { 0.98, 0.98, 0.82, 0.8 }; 591 CGContextSetFillColor(cgContext, backgroundColor); 592 CGContextFillRect(cgContext, drawRect); 593 594 static const CGFloat textColor[4] = { 0, 0, 0, 1 }; 595 CGContextSetFillColor(cgContext, textColor); 596 CGContextSetTextMatrix(cgContext, CGAffineTransformMakeScale(1, -1)); 597 CGContextSelectFont(cgContext, "Helvetica", fontSize, kCGEncodingMacRoman); 598 599 CGContextShowTextAtPoint(cgContext, drawRect.origin.x + boxBorderWidth, drawRect.origin.y + boxHeight - boxBorderWidth, text, strlen(text)); 600 601 CGContextRestoreGState(cgContext); 602 } 603 #endif 604 } 605 606 static HashSet<String> mimeTypeCache() 607 { 608 DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ()); 609 static bool typeListInitialized = false; 610 611 if (!typeListInitialized) { 612 unsigned count = QTMovieWin::countSupportedTypes(); 613 for (unsigned n = 0; n < count; n++) { 614 const UChar* character; 615 unsigned len; 616 QTMovieWin::getSupportedType(n, character, len); 617 if (len) 618 typeCache.add(String(character, len)); 619 } 620 621 typeListInitialized = true; 622 } 623 624 return typeCache; 625 } 626 627 void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) 628 { 629 types = mimeTypeCache(); 630 } 631 632 bool MediaPlayerPrivate::isAvailable() 633 { 634 return QTMovieWin::initializeQuickTime(); 635 } 636 637 MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) 638 { 639 // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an 640 // extended MIME type 641 return mimeTypeCache().contains(type) ? (codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; 642 } 643 644 void MediaPlayerPrivate::movieEnded(QTMovieWin* movie) 645 { 646 if (m_hasUnsupportedTracks) 647 return; 648 649 ASSERT(m_qtMovie.get() == movie); 650 didEnd(); 651 } 652 653 void MediaPlayerPrivate::movieLoadStateChanged(QTMovieWin* movie) 654 { 655 if (m_hasUnsupportedTracks) 656 return; 657 658 ASSERT(m_qtMovie.get() == movie); 659 updateStates(); 660 } 661 662 void MediaPlayerPrivate::movieTimeChanged(QTMovieWin* movie) 663 { 664 if (m_hasUnsupportedTracks) 665 return; 666 667 ASSERT(m_qtMovie.get() == movie); 668 updateStates(); 669 m_player->timeChanged(); 670 } 671 672 void MediaPlayerPrivate::movieNewImageAvailable(QTMovieWin* movie) 673 { 674 if (m_hasUnsupportedTracks) 675 return; 676 677 ASSERT(m_qtMovie.get() == movie); 678 #if DRAW_FRAME_RATE 679 if (m_startedPlaying) { 680 m_frameCountWhilePlaying++; 681 // To eliminate preroll costs from our calculation, our frame rate calculation excludes 682 // the first frame drawn after playback starts. 683 if (m_frameCountWhilePlaying == 1) 684 m_timeStartedPlaying = WTF::currentTime(); 685 } 686 #endif 687 688 m_newFrameAvailable = true; 689 690 #if USE(ACCELERATED_COMPOSITING) 691 if (m_qtVideoLayer) 692 m_qtVideoLayer->platformLayer()->setNeedsDisplay(); 693 else 694 #endif 695 m_player->repaint(); 696 } 697 698 bool MediaPlayerPrivate::hasSingleSecurityOrigin() const 699 { 700 // We tell quicktime to disallow resources that come from different origins 701 // so we all media is single origin. 702 return true; 703 } 704 705 MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::currentRenderingMode() const 706 { 707 if (!m_qtMovie) 708 return MediaRenderingNone; 709 710 #if USE(ACCELERATED_COMPOSITING) 711 if (m_qtVideoLayer) 712 return MediaRenderingMovieLayer; 713 #endif 714 715 return MediaRenderingSoftwareRenderer; 716 } 717 718 MediaPlayerPrivate::MediaRenderingMode MediaPlayerPrivate::preferredRenderingMode() const 719 { 720 if (!m_player->frameView() || !m_qtMovie) 721 return MediaRenderingNone; 722 723 #if USE(ACCELERATED_COMPOSITING) 724 if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) 725 return MediaRenderingMovieLayer; 726 #endif 727 728 return MediaRenderingSoftwareRenderer; 729 } 730 731 void MediaPlayerPrivate::setUpVideoRendering() 732 { 733 MediaRenderingMode currentMode = currentRenderingMode(); 734 MediaRenderingMode preferredMode = preferredRenderingMode(); 735 736 #if !USE(ACCELERATED_COMPOSITING) 737 ASSERT(preferredMode != MediaRenderingMovieLayer); 738 #endif 739 740 if (currentMode == preferredMode && currentMode != MediaRenderingNone) 741 return; 742 743 if (currentMode != MediaRenderingNone) 744 tearDownVideoRendering(); 745 746 if (preferredMode == MediaRenderingMovieLayer) 747 createLayerForMovie(); 748 } 749 750 void MediaPlayerPrivate::tearDownVideoRendering() 751 { 752 #if USE(ACCELERATED_COMPOSITING) 753 if (m_qtVideoLayer) 754 destroyLayerForMovie(); 755 #endif 756 } 757 758 bool MediaPlayerPrivate::hasSetUpVideoRendering() const 759 { 760 #if USE(ACCELERATED_COMPOSITING) 761 return m_qtVideoLayer || currentRenderingMode() != MediaRenderingMovieLayer; 762 #else 763 return true; 764 #endif 765 } 766 767 #if USE(ACCELERATED_COMPOSITING) 768 769 // Up-call from compositing layer drawing callback. 770 void MediaPlayerPrivate::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase, const IntRect&) 771 { 772 if (m_hasUnsupportedTracks) 773 return; 774 775 ASSERT(supportsAcceleratedRendering()); 776 777 // No reason to replace the current layer image unless we have something new to show. 778 if (!m_newFrameAvailable) 779 return; 780 781 static CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 782 void* buffer; 783 unsigned bitsPerPixel; 784 unsigned rowBytes; 785 unsigned width; 786 unsigned height; 787 788 m_qtMovie->getCurrentFrameInfo(buffer, bitsPerPixel, rowBytes, width, height); 789 if (!buffer) 790 return ; 791 792 RetainPtr<CFDataRef> data(AdoptCF, CFDataCreateWithBytesNoCopy(0, static_cast<UInt8*>(buffer), rowBytes * height, kCFAllocatorNull)); 793 RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithCFData(data.get())); 794 RetainPtr<CGImageRef> frameImage(AdoptCF, CGImageCreate(width, height, 8, bitsPerPixel, rowBytes, colorSpace, 795 kCGBitmapByteOrder32Little | kCGImageAlphaFirst, provider.get(), 0, false, kCGRenderingIntentDefault)); 796 if (!frameImage) 797 return; 798 799 IntRect rect(0, 0, m_size.width(), m_size.height()); 800 CGContextDrawImage(context.platformContext(), rect, frameImage.get()); 801 paintCompleted(context, rect); 802 } 803 #endif 804 805 void MediaPlayerPrivate::createLayerForMovie() 806 { 807 #if USE(ACCELERATED_COMPOSITING) 808 ASSERT(supportsAcceleratedRendering()); 809 810 if (!m_qtMovie || m_qtVideoLayer) 811 return; 812 813 // Do nothing if the parent layer hasn't been set up yet. 814 GraphicsLayer* videoGraphicsLayer = m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player); 815 if (!videoGraphicsLayer) 816 return; 817 818 // Create a GraphicsLayer that won't be inserted directly into the render tree, but will used 819 // as a wrapper for a WKCACFLayer which gets inserted as the content layer of the video 820 // renderer's GraphicsLayer. 821 m_qtVideoLayer.set(new GraphicsLayerCACF(this)); 822 if (!m_qtVideoLayer) 823 return; 824 825 // Mark the layer as drawing itself, anchored in the top left, and bottom-up. 826 m_qtVideoLayer->setDrawsContent(true); 827 m_qtVideoLayer->setAnchorPoint(FloatPoint3D()); 828 m_qtVideoLayer->setContentsOrientation(GraphicsLayer::CompositingCoordinatesBottomUp); 829 #ifndef NDEBUG 830 m_qtVideoLayer->setName("Video layer"); 831 #endif 832 833 // Hang the video layer from the render layer. 834 videoGraphicsLayer->setContentsToMedia(m_qtVideoLayer->platformLayer()); 835 #endif 836 } 837 838 void MediaPlayerPrivate::destroyLayerForMovie() 839 { 840 #if USE(ACCELERATED_COMPOSITING) 841 if (!m_qtVideoLayer) 842 return; 843 m_qtVideoLayer = 0; 844 #endif 845 } 846 847 #if USE(ACCELERATED_COMPOSITING) 848 bool MediaPlayerPrivate::supportsAcceleratedRendering() const 849 { 850 return isReadyForRendering(); 851 } 852 853 void MediaPlayerPrivate::acceleratedRenderingStateChanged() 854 { 855 // Set up or change the rendering path if necessary. 856 setUpVideoRendering(); 857 } 858 859 void MediaPlayerPrivate::notifySyncRequired(const GraphicsLayer*) 860 { 861 GraphicsLayerCACF* videoGraphicsLayer = static_cast<GraphicsLayerCACF*>(m_player->mediaPlayerClient()->mediaPlayerGraphicsLayer(m_player)); 862 if (videoGraphicsLayer) 863 videoGraphicsLayer->notifySyncRequired(); 864 } 865 866 867 #endif 868 869 870 } 871 872 #endif 873