1 /* 2 * Copyright (C) 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) && USE(AVFOUNDATION) 29 30 #include "MediaPlayerPrivateAVFoundation.h" 31 32 #include "ApplicationCacheHost.h" 33 #include "DocumentLoader.h" 34 #include "FrameView.h" 35 #include "GraphicsContext.h" 36 #include "GraphicsLayer.h" 37 #include "KURL.h" 38 #include "Logging.h" 39 #include "SoftLinking.h" 40 #include "TimeRanges.h" 41 #include <CoreMedia/CoreMedia.h> 42 #include <wtf/UnusedParam.h> 43 44 using namespace std; 45 46 namespace WebCore { 47 48 static const float invalidTime = -1.0f; 49 50 MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(MediaPlayer* player) 51 : m_player(player) 52 , m_queuedNotifications() 53 , m_queueMutex() 54 , m_mainThreadCallPending(false) 55 , m_networkState(MediaPlayer::Empty) 56 , m_readyState(MediaPlayer::HaveNothing) 57 , m_preload(MediaPlayer::Auto) 58 , m_scaleFactor(1, 1) 59 , m_cachedMaxTimeLoaded(0) 60 , m_cachedMaxTimeSeekable(0) 61 , m_cachedDuration(invalidTime) 62 , m_reportedDuration(invalidTime) 63 , m_seekTo(invalidTime) 64 , m_requestedRate(1) 65 , m_delayCallbacks(false) 66 , m_havePreparedToPlay(false) 67 , m_assetIsPlayable(false) 68 , m_visible(false) 69 , m_videoFrameHasDrawn(false) 70 , m_loadingMetadata(false) 71 , m_delayingLoad(false) 72 , m_isAllowedToRender(false) 73 , m_cachedHasAudio(false) 74 , m_cachedHasVideo(false) 75 , m_cachedHasCaptions(false) 76 , m_ignoreLoadStateChanges(false) 77 , m_haveReportedFirstVideoFrame(false) 78 , m_playWhenFramesAvailable(false) 79 { 80 LOG(Media, "MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(%p)", this); 81 } 82 83 MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation() 84 { 85 LOG(Media, "MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation(%p)", this); 86 cancelCallOnMainThread(mainThreadCallback, this); 87 } 88 89 MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::currentRenderingMode() const 90 { 91 #if USE(ACCELERATED_COMPOSITING) 92 if (platformLayer()) 93 return MediaRenderingToLayer; 94 #endif 95 96 if (hasContextRenderer()) 97 return MediaRenderingToContext; 98 99 return MediaRenderingNone; 100 } 101 102 MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::preferredRenderingMode() const 103 { 104 if (!m_player->visible() || !m_player->frameView() || assetStatus() == MediaPlayerAVAssetStatusUnknown) 105 return MediaRenderingNone; 106 107 #if USE(ACCELERATED_COMPOSITING) 108 if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) 109 return MediaRenderingToLayer; 110 #endif 111 112 return MediaRenderingToContext; 113 } 114 115 void MediaPlayerPrivateAVFoundation::setUpVideoRendering() 116 { 117 if (!isReadyForVideoSetup()) 118 return; 119 120 MediaRenderingMode currentMode = currentRenderingMode(); 121 MediaRenderingMode preferredMode = preferredRenderingMode(); 122 if (currentMode == preferredMode && currentMode != MediaRenderingNone) 123 return; 124 125 LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - current mode = %d, preferred mode = %d", 126 this, static_cast<int>(currentMode), static_cast<int>(preferredMode)); 127 128 if (currentMode != MediaRenderingNone) 129 tearDownVideoRendering(); 130 131 switch (preferredMode) { 132 case MediaRenderingNone: 133 case MediaRenderingToContext: 134 createContextVideoRenderer(); 135 break; 136 137 #if USE(ACCELERATED_COMPOSITING) 138 case MediaRenderingToLayer: 139 createVideoLayer(); 140 break; 141 #endif 142 } 143 144 #if USE(ACCELERATED_COMPOSITING) 145 // If using a movie layer, inform the client so the compositing tree is updated. 146 if (currentMode == MediaRenderingToLayer || preferredMode == MediaRenderingToLayer) { 147 LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - calling mediaPlayerRenderingModeChanged()", this); 148 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); 149 } 150 #endif 151 } 152 153 void MediaPlayerPrivateAVFoundation::tearDownVideoRendering() 154 { 155 LOG(Media, "MediaPlayerPrivateAVFoundation::tearDownVideoRendering(%p)", this); 156 157 destroyContextVideoRenderer(); 158 159 #if USE(ACCELERATED_COMPOSITING) 160 if (platformLayer()) 161 destroyVideoLayer(); 162 #endif 163 } 164 165 bool MediaPlayerPrivateAVFoundation::hasSetUpVideoRendering() const 166 { 167 return hasLayerRenderer() || hasContextRenderer(); 168 } 169 170 void MediaPlayerPrivateAVFoundation::resumeLoad() 171 { 172 LOG(Media, "MediaPlayerPrivateAVFoundation::resumeLoad(%p)", this); 173 174 ASSERT(m_delayingLoad); 175 m_delayingLoad = false; 176 177 if (m_assetURL.length()) 178 prepareToPlay(); 179 } 180 181 void MediaPlayerPrivateAVFoundation::load(const String& url) 182 { 183 LOG(Media, "MediaPlayerPrivateAVFoundation::load(%p)", this); 184 185 if (m_networkState != MediaPlayer::Loading) { 186 m_networkState = MediaPlayer::Loading; 187 m_player->networkStateChanged(); 188 } 189 if (m_readyState != MediaPlayer::HaveNothing) { 190 m_readyState = MediaPlayer::HaveNothing; 191 m_player->readyStateChanged(); 192 } 193 194 m_videoFrameHasDrawn = false; 195 m_assetURL = url; 196 197 // Don't do any more work if the url is empty. 198 if (!url.length()) 199 return; 200 201 if (m_preload == MediaPlayer::None) { 202 LOG(Media, "MediaPlayerPrivateAVFoundation::load(%p) - preload==none so returning", this); 203 m_delayingLoad = true; 204 return; 205 } 206 207 prepareToPlay(); 208 } 209 210 void MediaPlayerPrivateAVFoundation::playabilityKnown() 211 { 212 LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p)", this); 213 214 updateStates(); 215 if (m_assetIsPlayable) 216 return; 217 218 // Nothing more to do if we already have all of the item's metadata. 219 if (assetStatus() > MediaPlayerAVAssetStatusLoading) { 220 LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p) - all metadata loaded", this); 221 return; 222 } 223 224 // At this point we are supposed to load metadata. It is OK to ask the asset to load the same 225 // information multiple times, because if it has already been loaded the completion handler 226 // will just be called synchronously. 227 m_loadingMetadata = true; 228 beginLoadingMetadata(); 229 } 230 231 void MediaPlayerPrivateAVFoundation::prepareToPlay() 232 { 233 LOG(Media, "MediaPlayerPrivateAVFoundation::prepareToPlay(%p)", this); 234 235 m_preload = MediaPlayer::Auto; 236 if (m_havePreparedToPlay) 237 return; 238 m_havePreparedToPlay = true; 239 240 m_delayingLoad = false; 241 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 242 Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0; 243 ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0; 244 ApplicationCacheResource* resource = 0; 245 if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(m_assetURL), resource) && resource) 246 createAVPlayerForCacheResource(resource); 247 else 248 #endif 249 createAVPlayerForURL(m_assetURL); 250 checkPlayability(); 251 } 252 253 void MediaPlayerPrivateAVFoundation::play() 254 { 255 LOG(Media, "MediaPlayerPrivateAVFoundation::play(%p)", this); 256 257 // If the file has video, don't request playback until the first frame of video is ready to display 258 // or the audio may start playing before we can render video. 259 if (!m_cachedHasVideo || hasAvailableVideoFrame()) 260 platformPlay(); 261 else 262 m_playWhenFramesAvailable = true; 263 } 264 265 void MediaPlayerPrivateAVFoundation::pause() 266 { 267 LOG(Media, "MediaPlayerPrivateAVFoundation::pause(%p)", this); 268 m_playWhenFramesAvailable = false; 269 platformPause(); 270 } 271 272 void MediaPlayerPrivateAVFoundation::paint(GraphicsContext*, const IntRect&) 273 { 274 // This is the base class, only need to remember that a frame has been drawn. 275 m_videoFrameHasDrawn = true; 276 } 277 278 float MediaPlayerPrivateAVFoundation::duration() const 279 { 280 if (!metaDataAvailable()) 281 return 0; 282 283 if (m_cachedDuration == invalidTime) { 284 m_cachedDuration = platformDuration(); 285 LOG(Media, "MediaPlayerPrivateAVFMac::duration(%p) - caching %f", this, m_cachedDuration); 286 } 287 288 return m_cachedDuration; 289 } 290 291 void MediaPlayerPrivateAVFoundation::seek(float time) 292 { 293 LOG(Media, "MediaPlayerPrivateAVFoundation::seek(%p) - seeking to %f", this, time); 294 if (!metaDataAvailable()) 295 return; 296 297 if (time > duration()) 298 time = duration(); 299 300 m_seekTo = time; 301 302 seekToTime(time); 303 } 304 305 void MediaPlayerPrivateAVFoundation::setRate(float rate) 306 { 307 LOG(Media, "MediaPlayerPrivateAVFoundation::setRate(%p) - seting to %f", this, rate); 308 m_requestedRate = rate; 309 310 updateRate(); 311 } 312 313 bool MediaPlayerPrivateAVFoundation::paused() const 314 { 315 if (!metaDataAvailable()) 316 return true; 317 318 return rate() == 0; 319 } 320 321 bool MediaPlayerPrivateAVFoundation::seeking() const 322 { 323 if (!metaDataAvailable()) 324 return false; 325 326 return m_seekTo != invalidTime; 327 } 328 329 IntSize MediaPlayerPrivateAVFoundation::naturalSize() const 330 { 331 if (!metaDataAvailable()) 332 return IntSize(); 333 334 // In spite of the name of this method, return the natural size transformed by the 335 // initial movie scale because the spec says intrinsic size is: 336 // 337 // ... the dimensions of the resource in CSS pixels after taking into account the resource's 338 // dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the 339 // format used by the resource 340 341 return m_cachedNaturalSize; 342 } 343 344 void MediaPlayerPrivateAVFoundation::setNaturalSize(IntSize size) 345 { 346 IntSize oldSize = m_cachedNaturalSize; 347 m_cachedNaturalSize = size; 348 if (oldSize != m_cachedNaturalSize) 349 m_player->sizeChanged(); 350 } 351 352 PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundation::buffered() const 353 { 354 if (!m_cachedLoadedTimeRanges) 355 m_cachedLoadedTimeRanges = platformBufferedTimeRanges(); 356 357 return m_cachedLoadedTimeRanges->copy(); 358 } 359 360 float MediaPlayerPrivateAVFoundation::maxTimeSeekable() const 361 { 362 if (!metaDataAvailable()) 363 return 0; 364 365 if (!m_cachedMaxTimeSeekable) 366 m_cachedMaxTimeSeekable = platformMaxTimeSeekable(); 367 368 LOG(Media, "MediaPlayerPrivateAVFoundation::maxTimeSeekable(%p) - returning %f", this, m_cachedMaxTimeSeekable); 369 return m_cachedMaxTimeSeekable; 370 } 371 372 float MediaPlayerPrivateAVFoundation::maxTimeLoaded() const 373 { 374 if (!metaDataAvailable()) 375 return 0; 376 377 if (!m_cachedMaxTimeLoaded) 378 m_cachedMaxTimeLoaded = platformMaxTimeLoaded(); 379 380 return m_cachedMaxTimeLoaded; 381 } 382 383 unsigned MediaPlayerPrivateAVFoundation::bytesLoaded() const 384 { 385 float dur = duration(); 386 if (!dur) 387 return 0; 388 unsigned loaded = totalBytes() * maxTimeLoaded() / dur; 389 LOG(Media, "MediaPlayerPrivateAVFoundation::bytesLoaded(%p) - returning %i", this, loaded); 390 return loaded; 391 } 392 393 bool MediaPlayerPrivateAVFoundation::isReadyForVideoSetup() const 394 { 395 return m_isAllowedToRender && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); 396 } 397 398 void MediaPlayerPrivateAVFoundation::prepareForRendering() 399 { 400 if (m_isAllowedToRender) 401 return; 402 m_isAllowedToRender = true; 403 404 setUpVideoRendering(); 405 406 if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer) 407 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); 408 } 409 410 bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const 411 { 412 #if ENABLE(FULLSCREEN_API) 413 return true; 414 #else 415 // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine 416 return false; 417 #endif 418 } 419 420 void MediaPlayerPrivateAVFoundation::updateStates() 421 { 422 MediaPlayer::NetworkState oldNetworkState = m_networkState; 423 MediaPlayer::ReadyState oldReadyState = m_readyState; 424 425 LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entering with networkState = %i, readyState = %i", 426 this, static_cast<int>(m_networkState), static_cast<int>(m_readyState)); 427 428 if (m_loadingMetadata) 429 m_networkState = MediaPlayer::Loading; 430 else { 431 // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state. 432 AVAssetStatus avAssetStatus = assetStatus(); 433 ItemStatus itemStatus = playerItemStatus(); 434 435 m_assetIsPlayable = (avAssetStatus == MediaPlayerAVAssetStatusPlayable); 436 if (m_readyState < MediaPlayer::HaveMetadata && avAssetStatus > MediaPlayerAVAssetStatusLoading) { 437 if (m_assetIsPlayable) { 438 if (itemStatus == MediaPlayerAVPlayerItemStatusUnknown) { 439 if (avAssetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData) { 440 // We may have a playable asset that doesn't support inspection prior to playback; go ahead 441 // and create the AVPlayerItem now. When the AVPlayerItem becomes ready to play, we will 442 // have access to its metadata. Or we may have been asked to become ready to play immediately. 443 m_networkState = MediaPlayer::Loading; 444 prepareToPlay(); 445 } else 446 m_networkState = MediaPlayer::Idle; 447 } 448 if (avAssetStatus == MediaPlayerAVAssetStatusLoaded) 449 m_readyState = MediaPlayer::HaveMetadata; 450 } else { 451 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 452 // and network errors. 453 m_networkState = MediaPlayer::FormatError; 454 } 455 } 456 457 if (avAssetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) { 458 if (seeking()) 459 m_readyState = m_readyState >= MediaPlayer::HaveMetadata ? MediaPlayer::HaveMetadata : MediaPlayer::HaveNothing; 460 else { 461 float maxLoaded = maxTimeLoaded(); 462 switch (itemStatus) { 463 case MediaPlayerAVPlayerItemStatusUnknown: 464 break; 465 case MediaPlayerAVPlayerItemStatusFailed: 466 m_networkState = MediaPlayer::DecodeError; 467 break; 468 case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp: 469 m_readyState = MediaPlayer::HaveEnoughData; 470 break; 471 case MediaPlayerAVPlayerItemStatusReadyToPlay: 472 case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty: 473 case MediaPlayerAVPlayerItemStatusPlaybackBufferFull: 474 if (maxLoaded > currentTime()) 475 m_readyState = MediaPlayer::HaveFutureData; 476 else 477 m_readyState = MediaPlayer::HaveCurrentData; 478 break; 479 } 480 481 if (itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay) 482 m_networkState = (maxLoaded == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading; 483 } 484 } 485 } 486 487 if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode()) 488 setUpVideoRendering(); 489 490 if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) { 491 m_haveReportedFirstVideoFrame = true; 492 m_player->firstVideoFrameAvailable(); 493 } 494 495 if (m_networkState != oldNetworkState) 496 m_player->networkStateChanged(); 497 498 if (m_readyState != oldReadyState) 499 m_player->readyStateChanged(); 500 501 if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) { 502 m_playWhenFramesAvailable = false; 503 platformPlay(); 504 } 505 506 LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - exiting with networkState = %i, readyState = %i", 507 this, static_cast<int>(m_networkState), static_cast<int>(m_readyState)); 508 } 509 510 void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 511 { 512 } 513 514 void MediaPlayerPrivateAVFoundation::setVisible(bool visible) 515 { 516 if (m_visible == visible) 517 return; 518 519 m_visible = visible; 520 if (visible) 521 setUpVideoRendering(); 522 else 523 tearDownVideoRendering(); 524 } 525 526 bool MediaPlayerPrivateAVFoundation::hasAvailableVideoFrame() const 527 { 528 if (currentRenderingMode() == MediaRenderingToLayer) 529 return videoLayerIsReadyToDisplay(); 530 531 // When using the software renderer we hope someone will signal that a frame is available so we might as well 532 // wait until we know that a frame has been drawn. 533 return m_videoFrameHasDrawn; 534 } 535 536 void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged() 537 { 538 // Set up or change the rendering path if necessary. 539 setUpVideoRendering(); 540 } 541 542 void MediaPlayerPrivateAVFoundation::metadataLoaded() 543 { 544 m_loadingMetadata = false; 545 updateStates(); 546 } 547 548 void MediaPlayerPrivateAVFoundation::loadStateChanged() 549 { 550 if (m_ignoreLoadStateChanges) 551 return; 552 updateStates(); 553 } 554 555 void MediaPlayerPrivateAVFoundation::rateChanged() 556 { 557 updateStates(); 558 m_player->rateChanged(); 559 } 560 561 void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged() 562 { 563 m_cachedLoadedTimeRanges = 0; 564 m_cachedMaxTimeLoaded = 0; 565 updateStates(); 566 567 // For some media files, reported duration is estimated and updated as media is loaded 568 // so report duration changed when the estimate is upated. 569 float dur = duration(); 570 if (dur != m_reportedDuration) { 571 if (m_reportedDuration != invalidTime) 572 m_player->durationChanged(); 573 m_reportedDuration = dur; 574 } 575 } 576 577 void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged() 578 { 579 m_cachedMaxTimeSeekable = 0; 580 } 581 582 void MediaPlayerPrivateAVFoundation::timeChanged(double time) 583 { 584 LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time); 585 586 if (m_seekTo == invalidTime) 587 return; 588 589 // AVFoundation may call our observer more than once during a seek, and we can't currently tell 590 // if we will be able to seek to an exact time, so assume that we are done seeking if we are 591 // "close enough" to the seek time. 592 const double smallSeekDelta = 1.0 / 100; 593 594 float currentRate = rate(); 595 if ((currentRate > 0 && time >= m_seekTo) || (currentRate < 0 && time <= m_seekTo) || (abs(m_seekTo - time) <= smallSeekDelta)) { 596 m_seekTo = invalidTime; 597 updateStates(); 598 m_player->timeChanged(); 599 } 600 } 601 602 void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished) 603 { 604 LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, finished); 605 606 if (finished) 607 m_seekTo = invalidTime; 608 } 609 610 void MediaPlayerPrivateAVFoundation::didEnd() 611 { 612 // Hang onto the current time and use it as duration from now on since we are definitely at 613 // the end of the movie. Do this because the initial duration is sometimes an estimate. 614 float now = currentTime(); 615 if (now > 0) 616 m_cachedDuration = now; 617 618 updateStates(); 619 m_player->timeChanged(); 620 } 621 622 void MediaPlayerPrivateAVFoundation::repaint() 623 { 624 m_videoFrameHasDrawn = true; 625 m_player->repaint(); 626 } 627 628 MediaPlayer::MovieLoadType MediaPlayerPrivateAVFoundation::movieLoadType() const 629 { 630 if (!metaDataAvailable() || assetStatus() == MediaPlayerAVAssetStatusUnknown) 631 return MediaPlayer::Unknown; 632 633 if (isinf(duration())) 634 return MediaPlayer::LiveStream; 635 636 return MediaPlayer::Download; 637 } 638 639 void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload) 640 { 641 m_preload = preload; 642 if (m_delayingLoad && m_preload != MediaPlayer::None) 643 resumeLoad(); 644 } 645 646 void MediaPlayerPrivateAVFoundation::setDelayCallbacks(bool delay) 647 { 648 MutexLocker lock(m_queueMutex); 649 if (delay) 650 ++m_delayCallbacks; 651 else { 652 ASSERT(m_delayCallbacks); 653 --m_delayCallbacks; 654 } 655 } 656 657 void MediaPlayerPrivateAVFoundation::mainThreadCallback(void* context) 658 { 659 LOG(Media, "MediaPlayerPrivateAVFoundation::mainThreadCallback(%p)", context); 660 MediaPlayerPrivateAVFoundation* player = static_cast<MediaPlayerPrivateAVFoundation*>(context); 661 player->clearMainThreadPendingFlag(); 662 player->dispatchNotification(); 663 } 664 665 void MediaPlayerPrivateAVFoundation::clearMainThreadPendingFlag() 666 { 667 MutexLocker lock(m_queueMutex); 668 m_mainThreadCallPending = false; 669 } 670 671 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, double time) 672 { 673 scheduleMainThreadNotification(Notification(type, time)); 674 } 675 676 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, bool finished) 677 { 678 scheduleMainThreadNotification(Notification(type, finished)); 679 } 680 681 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification notification) 682 { 683 LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - notification %d", this, static_cast<int>(notification.type())); 684 m_queueMutex.lock(); 685 686 // It is important to always process the properties in the order that we are notified, 687 // so always go through the queue because notifications happen on different threads. 688 m_queuedNotifications.append(notification); 689 690 bool delayDispatch = m_delayCallbacks || !isMainThread(); 691 if (delayDispatch && !m_mainThreadCallPending) { 692 m_mainThreadCallPending = true; 693 callOnMainThread(mainThreadCallback, this); 694 } 695 696 m_queueMutex.unlock(); 697 698 if (delayDispatch) { 699 LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - early return", this); 700 return; 701 } 702 703 dispatchNotification(); 704 } 705 706 void MediaPlayerPrivateAVFoundation::dispatchNotification() 707 { 708 ASSERT(isMainThread()); 709 710 Notification notification = Notification(); 711 { 712 MutexLocker lock(m_queueMutex); 713 714 if (m_queuedNotifications.isEmpty()) 715 return; 716 717 if (!m_delayCallbacks) { 718 // Only dispatch one notification callback per invocation because they can cause recursion. 719 notification = m_queuedNotifications.first(); 720 m_queuedNotifications.remove(0); 721 } 722 723 if (!m_queuedNotifications.isEmpty() && !m_mainThreadCallPending) 724 callOnMainThread(mainThreadCallback, this); 725 726 if (!notification.isValid()) 727 return; 728 } 729 730 LOG(Media, "MediaPlayerPrivateAVFoundation::dispatchNotification(%p) - dispatching %d", this, static_cast<int>(notification.type())); 731 732 switch (notification.type()) { 733 case Notification::ItemDidPlayToEndTime: 734 didEnd(); 735 break; 736 case Notification::ItemTracksChanged: 737 tracksChanged(); 738 break; 739 case Notification::ItemStatusChanged: 740 loadStateChanged(); 741 break; 742 case Notification::ItemSeekableTimeRangesChanged: 743 seekableTimeRangesChanged(); 744 loadStateChanged(); 745 break; 746 case Notification::ItemLoadedTimeRangesChanged: 747 loadedTimeRangesChanged(); 748 loadStateChanged(); 749 break; 750 case Notification::ItemPresentationSizeChanged: 751 sizeChanged(); 752 break; 753 case Notification::ItemIsPlaybackLikelyToKeepUpChanged: 754 loadStateChanged(); 755 break; 756 case Notification::ItemIsPlaybackBufferEmptyChanged: 757 loadStateChanged(); 758 break; 759 case Notification::ItemIsPlaybackBufferFullChanged: 760 loadStateChanged(); 761 break; 762 case Notification::PlayerRateChanged: 763 rateChanged(); 764 break; 765 case Notification::PlayerTimeChanged: 766 timeChanged(notification.time()); 767 break; 768 case Notification::SeekCompleted: 769 seekCompleted(notification.finished()); 770 break; 771 case Notification::AssetMetadataLoaded: 772 metadataLoaded(); 773 break; 774 case Notification::AssetPlayabilityKnown: 775 playabilityKnown(); 776 break; 777 case Notification::None: 778 ASSERT_NOT_REACHED(); 779 break; 780 } 781 } 782 783 } // namespace WebCore 784 785 #endif 786