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