1/* 2 * Copyright (C) 2011, 2012 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 "DocumentLoader.h" 33#include "Frame.h" 34#include "FrameView.h" 35#include "GraphicsContext.h" 36#include "InbandTextTrackPrivateAVF.h" 37#include "InbandTextTrackPrivateClient.h" 38#include "KURL.h" 39#include "Logging.h" 40#include "PlatformLayer.h" 41#include "SoftLinking.h" 42#include "TimeRanges.h" 43#include <CoreMedia/CoreMedia.h> 44#include <wtf/MainThread.h> 45 46using namespace std; 47 48namespace WebCore { 49 50MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(MediaPlayer* player) 51 : m_player(player) 52 , m_queuedNotifications() 53 , m_queueMutex() 54 , m_networkState(MediaPlayer::Empty) 55 , m_readyState(MediaPlayer::HaveNothing) 56 , m_preload(MediaPlayer::Auto) 57 , m_cachedMaxTimeLoaded(0) 58 , m_cachedMaxTimeSeekable(0) 59 , m_cachedMinTimeSeekable(0) 60 , m_cachedDuration(MediaPlayer::invalidTime()) 61 , m_reportedDuration(MediaPlayer::invalidTime()) 62 , m_maxTimeLoadedAtLastDidLoadingProgress(MediaPlayer::invalidTime()) 63 , m_seekTo(MediaPlayer::invalidTime()) 64 , m_requestedRate(1) 65 , m_delayCallbacks(0) 66 , m_delayCharacteristicsChangedNotification(0) 67 , m_mainThreadCallPending(false) 68 , m_assetIsPlayable(false) 69 , m_visible(false) 70 , m_loadingMetadata(false) 71 , m_isAllowedToRender(false) 72 , m_cachedHasAudio(false) 73 , m_cachedHasVideo(false) 74 , m_cachedHasCaptions(false) 75 , m_ignoreLoadStateChanges(false) 76 , m_haveReportedFirstVideoFrame(false) 77 , m_playWhenFramesAvailable(false) 78 , m_inbandTrackConfigurationPending(false) 79 , m_characteristicsChanged(false) 80 , m_seekCount(0) 81{ 82 LOG(Media, "MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(%p)", this); 83} 84 85MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation() 86{ 87 LOG(Media, "MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation(%p)", this); 88 setIgnoreLoadStateChanges(true); 89 cancelCallOnMainThread(mainThreadCallback, this); 90} 91 92MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::currentRenderingMode() const 93{ 94#if USE(ACCELERATED_COMPOSITING) 95 if (platformLayer()) 96 return MediaRenderingToLayer; 97#endif 98 99 if (hasContextRenderer()) 100 return MediaRenderingToContext; 101 102 return MediaRenderingNone; 103} 104 105MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::preferredRenderingMode() const 106{ 107 if (!m_player->visible() || !m_player->frameView() || assetStatus() == MediaPlayerAVAssetStatusUnknown) 108 return MediaRenderingNone; 109 110#if USE(ACCELERATED_COMPOSITING) 111 if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player)) 112 return MediaRenderingToLayer; 113#endif 114 115 return MediaRenderingToContext; 116} 117 118void MediaPlayerPrivateAVFoundation::setUpVideoRendering() 119{ 120 if (!isReadyForVideoSetup()) 121 return; 122 123 MediaRenderingMode currentMode = currentRenderingMode(); 124 MediaRenderingMode preferredMode = preferredRenderingMode(); 125 126 if (preferredMode == MediaRenderingNone) 127 preferredMode = MediaRenderingToContext; 128 129 if (currentMode == preferredMode && currentMode != MediaRenderingNone) 130 return; 131 132 LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - current mode = %d, preferred mode = %d", 133 this, static_cast<int>(currentMode), static_cast<int>(preferredMode)); 134 135 if (currentMode != MediaRenderingNone) 136 tearDownVideoRendering(); 137 138 switch (preferredMode) { 139 case MediaRenderingNone: 140 case MediaRenderingToContext: 141 createContextVideoRenderer(); 142 break; 143 144#if USE(ACCELERATED_COMPOSITING) 145 case MediaRenderingToLayer: 146 createVideoLayer(); 147 break; 148#endif 149 } 150 151#if USE(ACCELERATED_COMPOSITING) 152 // If using a movie layer, inform the client so the compositing tree is updated. 153 if (currentMode == MediaRenderingToLayer || preferredMode == MediaRenderingToLayer) { 154 LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - calling mediaPlayerRenderingModeChanged()", this); 155 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); 156 } 157#endif 158} 159 160void MediaPlayerPrivateAVFoundation::tearDownVideoRendering() 161{ 162 LOG(Media, "MediaPlayerPrivateAVFoundation::tearDownVideoRendering(%p)", this); 163 164 destroyContextVideoRenderer(); 165 166#if USE(ACCELERATED_COMPOSITING) 167 if (platformLayer()) 168 destroyVideoLayer(); 169#endif 170} 171 172bool MediaPlayerPrivateAVFoundation::hasSetUpVideoRendering() const 173{ 174 return hasLayerRenderer() || hasContextRenderer(); 175} 176 177void MediaPlayerPrivateAVFoundation::load(const String& url) 178{ 179 LOG(Media, "MediaPlayerPrivateAVFoundation::load(%p)", this); 180 181 if (m_networkState != MediaPlayer::Loading) { 182 m_networkState = MediaPlayer::Loading; 183 m_player->networkStateChanged(); 184 } 185 if (m_readyState != MediaPlayer::HaveNothing) { 186 m_readyState = MediaPlayer::HaveNothing; 187 m_player->readyStateChanged(); 188 } 189 190 m_assetURL = url; 191 192 // Don't do any more work if the url is empty. 193 if (!url.length()) 194 return; 195 196 setPreload(m_preload); 197} 198 199void MediaPlayerPrivateAVFoundation::playabilityKnown() 200{ 201 LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p)", this); 202 203 if (m_assetIsPlayable) 204 return; 205 206 // Nothing more to do if we already have all of the item's metadata. 207 if (assetStatus() > MediaPlayerAVAssetStatusLoading) { 208 LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p) - all metadata loaded", this); 209 return; 210 } 211 212 // At this point we are supposed to load metadata. It is OK to ask the asset to load the same 213 // information multiple times, because if it has already been loaded the completion handler 214 // will just be called synchronously. 215 m_loadingMetadata = true; 216 beginLoadingMetadata(); 217} 218 219void MediaPlayerPrivateAVFoundation::prepareToPlay() 220{ 221 LOG(Media, "MediaPlayerPrivateAVFoundation::prepareToPlay(%p)", this); 222 223 setPreload(MediaPlayer::Auto); 224} 225 226void MediaPlayerPrivateAVFoundation::play() 227{ 228 LOG(Media, "MediaPlayerPrivateAVFoundation::play(%p)", this); 229 230 // If the file has video, don't request playback until the first frame of video is ready to display 231 // or the audio may start playing before we can render video. 232 if (!m_cachedHasVideo || hasAvailableVideoFrame()) 233 platformPlay(); 234 else 235 m_playWhenFramesAvailable = true; 236} 237 238void MediaPlayerPrivateAVFoundation::pause() 239{ 240 LOG(Media, "MediaPlayerPrivateAVFoundation::pause(%p)", this); 241 m_playWhenFramesAvailable = false; 242 platformPause(); 243} 244 245float MediaPlayerPrivateAVFoundation::duration() const 246{ 247 if (m_cachedDuration != MediaPlayer::invalidTime()) 248 return m_cachedDuration; 249 250 float duration = platformDuration(); 251 if (!duration || duration == MediaPlayer::invalidTime()) 252 return 0; 253 254 m_cachedDuration = duration; 255 LOG(Media, "MediaPlayerPrivateAVFoundation::duration(%p) - caching %f", this, m_cachedDuration); 256 return m_cachedDuration; 257} 258 259void MediaPlayerPrivateAVFoundation::seek(float time) 260{ 261 if (!metaDataAvailable()) 262 return; 263 264 if (time > duration()) 265 time = duration(); 266 267 if (currentTime() == time) 268 return; 269 270 if (currentTrack()) 271 currentTrack()->beginSeeking(); 272 273 LOG(Media, "MediaPlayerPrivateAVFoundation::seek(%p) - seeking to %f", this, time); 274 m_seekTo = time; 275 276 ++m_seekCount; 277 seekToTime(time); 278} 279 280void MediaPlayerPrivateAVFoundation::setRate(float rate) 281{ 282 LOG(Media, "MediaPlayerPrivateAVFoundation::setRate(%p) - seting to %f", this, rate); 283 m_requestedRate = rate; 284 285 updateRate(); 286} 287 288bool MediaPlayerPrivateAVFoundation::paused() const 289{ 290 if (!metaDataAvailable()) 291 return true; 292 293 return rate() == 0; 294} 295 296bool MediaPlayerPrivateAVFoundation::seeking() const 297{ 298 if (!metaDataAvailable()) 299 return false; 300 301 return m_seekTo != MediaPlayer::invalidTime(); 302} 303 304IntSize MediaPlayerPrivateAVFoundation::naturalSize() const 305{ 306 if (!metaDataAvailable()) 307 return IntSize(); 308 309 // In spite of the name of this method, return the natural size transformed by the 310 // initial movie scale because the spec says intrinsic size is: 311 // 312 // ... the dimensions of the resource in CSS pixels after taking into account the resource's 313 // dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the 314 // format used by the resource 315 316 return m_cachedNaturalSize; 317} 318 319void MediaPlayerPrivateAVFoundation::setNaturalSize(IntSize size) 320{ 321 LOG(Media, "MediaPlayerPrivateAVFoundation:setNaturalSize(%p) - size = %d x %d", this, size.width(), size.height()); 322 323 IntSize oldSize = m_cachedNaturalSize; 324 m_cachedNaturalSize = size; 325 if (oldSize != m_cachedNaturalSize) 326 m_player->sizeChanged(); 327} 328 329void MediaPlayerPrivateAVFoundation::setHasVideo(bool b) 330{ 331 if (m_cachedHasVideo != b) { 332 m_cachedHasVideo = b; 333 characteristicsChanged(); 334 } 335} 336 337void MediaPlayerPrivateAVFoundation::setHasAudio(bool b) 338{ 339 if (m_cachedHasAudio != b) { 340 m_cachedHasAudio = b; 341 characteristicsChanged(); 342 } 343} 344 345void MediaPlayerPrivateAVFoundation::setHasClosedCaptions(bool b) 346{ 347 if (m_cachedHasCaptions != b) { 348 m_cachedHasCaptions = b; 349 characteristicsChanged(); 350 } 351} 352 353void MediaPlayerPrivateAVFoundation::characteristicsChanged() 354{ 355 if (m_delayCharacteristicsChangedNotification) { 356 m_characteristicsChanged = true; 357 return; 358 } 359 360 m_characteristicsChanged = false; 361 m_player->characteristicChanged(); 362} 363 364void MediaPlayerPrivateAVFoundation::setDelayCharacteristicsChangedNotification(bool delay) 365{ 366 if (delay) { 367 m_delayCharacteristicsChangedNotification++; 368 return; 369 } 370 371 ASSERT(m_delayCharacteristicsChangedNotification); 372 m_delayCharacteristicsChangedNotification--; 373 if (!m_delayCharacteristicsChangedNotification && m_characteristicsChanged) 374 characteristicsChanged(); 375} 376 377PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundation::buffered() const 378{ 379 if (!m_cachedLoadedTimeRanges) 380 m_cachedLoadedTimeRanges = platformBufferedTimeRanges(); 381 382 return m_cachedLoadedTimeRanges->copy(); 383} 384 385double MediaPlayerPrivateAVFoundation::maxTimeSeekableDouble() const 386{ 387 if (!metaDataAvailable()) 388 return 0; 389 390 if (!m_cachedMaxTimeSeekable) 391 m_cachedMaxTimeSeekable = platformMaxTimeSeekable(); 392 393 LOG(Media, "MediaPlayerPrivateAVFoundation::maxTimeSeekable(%p) - returning %f", this, m_cachedMaxTimeSeekable); 394 return m_cachedMaxTimeSeekable; 395} 396 397double MediaPlayerPrivateAVFoundation::minTimeSeekable() const 398{ 399 if (!metaDataAvailable()) 400 return 0; 401 402 if (!m_cachedMinTimeSeekable) 403 m_cachedMinTimeSeekable = platformMinTimeSeekable(); 404 405 LOG(Media, "MediaPlayerPrivateAVFoundation::minTimeSeekable(%p) - returning %f", this, m_cachedMinTimeSeekable); 406 return m_cachedMinTimeSeekable; 407} 408 409float MediaPlayerPrivateAVFoundation::maxTimeLoaded() const 410{ 411 if (!metaDataAvailable()) 412 return 0; 413 414 if (!m_cachedMaxTimeLoaded) 415 m_cachedMaxTimeLoaded = platformMaxTimeLoaded(); 416 417 return m_cachedMaxTimeLoaded; 418} 419 420bool MediaPlayerPrivateAVFoundation::didLoadingProgress() const 421{ 422 if (!duration() || !totalBytes()) 423 return false; 424 float currentMaxTimeLoaded = maxTimeLoaded(); 425 bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress; 426 m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded; 427 428 return didLoadingProgress; 429} 430 431bool MediaPlayerPrivateAVFoundation::isReadyForVideoSetup() const 432{ 433 // AVFoundation will not return true for firstVideoFrameAvailable until 434 // an AVPlayerLayer has been added to the AVPlayerItem, so allow video setup 435 // here if a video track to trigger allocation of a AVPlayerLayer. 436 return (m_isAllowedToRender || m_cachedHasVideo) && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible(); 437} 438 439void MediaPlayerPrivateAVFoundation::prepareForRendering() 440{ 441 if (m_isAllowedToRender) 442 return; 443 m_isAllowedToRender = true; 444 445 setUpVideoRendering(); 446 447 if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer) 448 m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player); 449} 450 451bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const 452{ 453#if ENABLE(FULLSCREEN_API) 454 return true; 455#else 456 // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine 457 return false; 458#endif 459} 460 461void MediaPlayerPrivateAVFoundation::updateStates() 462{ 463 if (m_ignoreLoadStateChanges) 464 return; 465 466 MediaPlayer::NetworkState oldNetworkState = m_networkState; 467 MediaPlayer::ReadyState oldReadyState = m_readyState; 468 469 if (m_loadingMetadata) 470 m_networkState = MediaPlayer::Loading; 471 else { 472 // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state. 473 AssetStatus assetStatus = this->assetStatus(); 474 ItemStatus itemStatus = playerItemStatus(); 475 476 m_assetIsPlayable = (assetStatus == MediaPlayerAVAssetStatusPlayable); 477 if (m_readyState < MediaPlayer::HaveMetadata && assetStatus > MediaPlayerAVAssetStatusLoading) { 478 if (m_assetIsPlayable) { 479 if (assetStatus >= MediaPlayerAVAssetStatusLoaded) 480 m_readyState = MediaPlayer::HaveMetadata; 481 if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) { 482 if (assetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData || isLiveStream()) { 483 // The asset is playable but doesn't support inspection prior to playback (eg. streaming files), 484 // or we are supposed to prepare for playback immediately, so create the player item now. 485 m_networkState = MediaPlayer::Loading; 486 prepareToPlay(); 487 } else 488 m_networkState = MediaPlayer::Idle; 489 } 490 } else { 491 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 492 // and network errors. 493 m_networkState = MediaPlayer::FormatError; 494 } 495 } 496 497 if (assetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) { 498 switch (itemStatus) { 499 case MediaPlayerAVPlayerItemStatusDoesNotExist: 500 case MediaPlayerAVPlayerItemStatusUnknown: 501 case MediaPlayerAVPlayerItemStatusFailed: 502 break; 503 504 case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp: 505 case MediaPlayerAVPlayerItemStatusPlaybackBufferFull: 506 // If the status becomes PlaybackBufferFull, loading stops and the status will not 507 // progress to LikelyToKeepUp. Set the readyState to HAVE_ENOUGH_DATA, on the 508 // presumption that if the playback buffer is full, playback will probably not stall. 509 m_readyState = MediaPlayer::HaveEnoughData; 510 break; 511 512 case MediaPlayerAVPlayerItemStatusReadyToPlay: 513 // If the readyState is already HaveEnoughData, don't go lower because of this state change. 514 if (m_readyState == MediaPlayer::HaveEnoughData) 515 break; 516 517 case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty: 518 if (maxTimeLoaded() > currentTime()) 519 m_readyState = MediaPlayer::HaveFutureData; 520 else 521 m_readyState = MediaPlayer::HaveCurrentData; 522 break; 523 } 524 525 if (itemStatus == MediaPlayerAVPlayerItemStatusPlaybackBufferFull) 526 m_networkState = MediaPlayer::Idle; 527 else if (itemStatus == MediaPlayerAVPlayerItemStatusFailed) 528 m_networkState = MediaPlayer::DecodeError; 529 else if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay) 530 m_networkState = (maxTimeLoaded() == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading; 531 } 532 } 533 534 if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode()) 535 setUpVideoRendering(); 536 537 if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) { 538 if (m_readyState < MediaPlayer::HaveCurrentData) 539 m_readyState = MediaPlayer::HaveCurrentData; 540 m_haveReportedFirstVideoFrame = true; 541 m_player->firstVideoFrameAvailable(); 542 } 543 544 if (m_networkState != oldNetworkState) 545 m_player->networkStateChanged(); 546 547 if (m_readyState != oldReadyState) 548 m_player->readyStateChanged(); 549 550 if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) { 551 m_playWhenFramesAvailable = false; 552 platformPlay(); 553 } 554 555#if !LOG_DISABLED 556 if (m_networkState != oldNetworkState || oldReadyState != m_readyState) { 557 LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entered with networkState = %i, readyState = %i, exiting with networkState = %i, readyState = %i", 558 this, static_cast<int>(oldNetworkState), static_cast<int>(oldReadyState), static_cast<int>(m_networkState), static_cast<int>(m_readyState)); 559 } 560#endif 561} 562 563void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 564{ 565} 566 567void MediaPlayerPrivateAVFoundation::setVisible(bool visible) 568{ 569 if (m_visible == visible) 570 return; 571 572 m_visible = visible; 573 if (visible) 574 setUpVideoRendering(); 575 576 platformSetVisible(visible); 577} 578 579void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged() 580{ 581 // Set up or change the rendering path if necessary. 582 setUpVideoRendering(); 583} 584 585void MediaPlayerPrivateAVFoundation::metadataLoaded() 586{ 587 m_loadingMetadata = false; 588 tracksChanged(); 589} 590 591void MediaPlayerPrivateAVFoundation::rateChanged() 592{ 593 m_player->rateChanged(); 594} 595 596void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged() 597{ 598 m_cachedLoadedTimeRanges = 0; 599 m_cachedMaxTimeLoaded = 0; 600 invalidateCachedDuration(); 601} 602 603void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged() 604{ 605 m_cachedMaxTimeSeekable = 0; 606 m_cachedMinTimeSeekable = 0; 607} 608 609void MediaPlayerPrivateAVFoundation::timeChanged(double time) 610{ 611 LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time); 612 UNUSED_PARAM(time); 613} 614 615void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished) 616{ 617 LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, finished); 618 UNUSED_PARAM(finished); 619 620 ASSERT(m_seekCount); 621 if (--m_seekCount) 622 return; 623 624 if (currentTrack()) 625 currentTrack()->endSeeking(); 626 627 m_seekTo = MediaPlayer::invalidTime(); 628 updateStates(); 629 m_player->timeChanged(); 630} 631 632void MediaPlayerPrivateAVFoundation::didEnd() 633{ 634 // Hang onto the current time and use it as duration from now on since we are definitely at 635 // the end of the movie. Do this because the initial duration is sometimes an estimate. 636 float now = currentTime(); 637 if (now > 0) 638 m_cachedDuration = now; 639 640 updateStates(); 641 m_player->timeChanged(); 642} 643 644void MediaPlayerPrivateAVFoundation::invalidateCachedDuration() 645{ 646 LOG(Media, "MediaPlayerPrivateAVFoundation::invalidateCachedDuration(%p)", this); 647 648 m_cachedDuration = MediaPlayer::invalidTime(); 649 650 // For some media files, reported duration is estimated and updated as media is loaded 651 // so report duration changed when the estimate is upated. 652 float duration = this->duration(); 653 if (duration != m_reportedDuration) { 654 if (m_reportedDuration != MediaPlayer::invalidTime()) 655 m_player->durationChanged(); 656 m_reportedDuration = duration; 657 } 658 659} 660 661void MediaPlayerPrivateAVFoundation::repaint() 662{ 663 m_player->repaint(); 664} 665 666MediaPlayer::MovieLoadType MediaPlayerPrivateAVFoundation::movieLoadType() const 667{ 668 if (!metaDataAvailable() || assetStatus() == MediaPlayerAVAssetStatusUnknown) 669 return MediaPlayer::Unknown; 670 671 if (isLiveStream()) 672 return MediaPlayer::LiveStream; 673 674 return MediaPlayer::Download; 675} 676 677void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload) 678{ 679 m_preload = preload; 680 if (!m_assetURL.length()) 681 return; 682 683 setDelayCallbacks(true); 684 685 if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) { 686 createAVAssetForURL(m_assetURL); 687 checkPlayability(); 688 } 689 690 // Don't force creation of the player and player item unless we already know that the asset is playable. If we aren't 691 // there yet, or if we already know it is not playable, creating them now won't help. 692 if (m_preload == MediaPlayer::Auto && m_assetIsPlayable) { 693 createAVPlayerItem(); 694 createAVPlayer(); 695 } 696 697 setDelayCallbacks(false); 698} 699 700void MediaPlayerPrivateAVFoundation::setDelayCallbacks(bool delay) const 701{ 702 MutexLocker lock(m_queueMutex); 703 if (delay) 704 ++m_delayCallbacks; 705 else { 706 ASSERT(m_delayCallbacks); 707 --m_delayCallbacks; 708 } 709} 710 711void MediaPlayerPrivateAVFoundation::mainThreadCallback(void* context) 712{ 713 LOG(Media, "MediaPlayerPrivateAVFoundation::mainThreadCallback(%p)", context); 714 MediaPlayerPrivateAVFoundation* player = static_cast<MediaPlayerPrivateAVFoundation*>(context); 715 player->clearMainThreadPendingFlag(); 716 player->dispatchNotification(); 717} 718 719void MediaPlayerPrivateAVFoundation::clearMainThreadPendingFlag() 720{ 721 MutexLocker lock(m_queueMutex); 722 m_mainThreadCallPending = false; 723} 724 725void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, double time) 726{ 727 scheduleMainThreadNotification(Notification(type, time)); 728} 729 730void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, bool finished) 731{ 732 scheduleMainThreadNotification(Notification(type, finished)); 733} 734 735#if !LOG_DISABLED 736static const char* notificationName(MediaPlayerPrivateAVFoundation::Notification& notification) 737{ 738#define DEFINE_TYPE_STRING_CASE(type) case MediaPlayerPrivateAVFoundation::Notification::type: return #type; 739 switch (notification.type()) { 740 FOR_EACH_MEDIAPLAYERPRIVATEAVFOUNDATION_NOTIFICATION_TYPE(DEFINE_TYPE_STRING_CASE) 741 default: return ""; 742 } 743#undef DEFINE_TYPE_STRING_CASE 744} 745#endif // !LOG_DISABLED 746 747 748void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification notification) 749{ 750 LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - notification %s", this, notificationName(notification)); 751 m_queueMutex.lock(); 752 753 // It is important to always process the properties in the order that we are notified, 754 // so always go through the queue because notifications happen on different threads. 755 m_queuedNotifications.append(notification); 756 757 bool delayDispatch = m_delayCallbacks || !isMainThread(); 758 if (delayDispatch && !m_mainThreadCallPending) { 759 m_mainThreadCallPending = true; 760 callOnMainThread(mainThreadCallback, this); 761 } 762 763 m_queueMutex.unlock(); 764 765 if (delayDispatch) { 766 LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - early return", this); 767 return; 768 } 769 770 dispatchNotification(); 771} 772 773void MediaPlayerPrivateAVFoundation::dispatchNotification() 774{ 775 ASSERT(isMainThread()); 776 777 Notification notification = Notification(); 778 { 779 MutexLocker lock(m_queueMutex); 780 781 if (m_queuedNotifications.isEmpty()) 782 return; 783 784 if (!m_delayCallbacks) { 785 // Only dispatch one notification callback per invocation because they can cause recursion. 786 notification = m_queuedNotifications.first(); 787 m_queuedNotifications.remove(0); 788 } 789 790 if (!m_queuedNotifications.isEmpty() && !m_mainThreadCallPending) 791 callOnMainThread(mainThreadCallback, this); 792 793 if (!notification.isValid()) 794 return; 795 } 796 797 LOG(Media, "MediaPlayerPrivateAVFoundation::dispatchNotification(%p) - dispatching %s", this, notificationName(notification)); 798 799 switch (notification.type()) { 800 case Notification::ItemDidPlayToEndTime: 801 didEnd(); 802 break; 803 case Notification::ItemTracksChanged: 804 tracksChanged(); 805 updateStates(); 806 break; 807 case Notification::ItemStatusChanged: 808 updateStates(); 809 break; 810 case Notification::ItemSeekableTimeRangesChanged: 811 seekableTimeRangesChanged(); 812 updateStates(); 813 break; 814 case Notification::ItemLoadedTimeRangesChanged: 815 loadedTimeRangesChanged(); 816 updateStates(); 817 break; 818 case Notification::ItemPresentationSizeChanged: 819 sizeChanged(); 820 updateStates(); 821 break; 822 case Notification::ItemIsPlaybackLikelyToKeepUpChanged: 823 updateStates(); 824 break; 825 case Notification::ItemIsPlaybackBufferEmptyChanged: 826 updateStates(); 827 break; 828 case Notification::ItemIsPlaybackBufferFullChanged: 829 updateStates(); 830 break; 831 case Notification::PlayerRateChanged: 832 updateStates(); 833 rateChanged(); 834 break; 835 case Notification::PlayerTimeChanged: 836 timeChanged(notification.time()); 837 break; 838 case Notification::SeekCompleted: 839 seekCompleted(notification.finished()); 840 break; 841 case Notification::AssetMetadataLoaded: 842 metadataLoaded(); 843 updateStates(); 844 break; 845 case Notification::AssetPlayabilityKnown: 846 updateStates(); 847 playabilityKnown(); 848 break; 849 case Notification::DurationChanged: 850 invalidateCachedDuration(); 851 break; 852 case Notification::ContentsNeedsDisplay: 853 contentsNeedsDisplay(); 854 break; 855 case Notification::InbandTracksNeedConfiguration: 856 m_inbandTrackConfigurationPending = false; 857 configureInbandTracks(); 858 break; 859 860 case Notification::None: 861 ASSERT_NOT_REACHED(); 862 break; 863 } 864} 865 866void MediaPlayerPrivateAVFoundation::configureInbandTracks() 867{ 868 RefPtr<InbandTextTrackPrivateAVF> trackToEnable; 869 870 // AVFoundation can only emit cues for one track at a time, so enable the first track that is showing, or the first that 871 // is hidden if none are showing. Otherwise disable all tracks. 872 for (unsigned i = 0; i < m_textTracks.size(); ++i) { 873 RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i]; 874 if (track->mode() == InbandTextTrackPrivate::Showing) { 875 trackToEnable = track; 876 break; 877 } 878 if (track->mode() == InbandTextTrackPrivate::Hidden) 879 trackToEnable = track; 880 } 881 882 setCurrentTrack(trackToEnable.get()); 883} 884 885void MediaPlayerPrivateAVFoundation::trackModeChanged() 886{ 887 if (m_inbandTrackConfigurationPending) 888 return; 889 m_inbandTrackConfigurationPending = true; 890 scheduleMainThreadNotification(Notification::InbandTracksNeedConfiguration); 891} 892 893size_t MediaPlayerPrivateAVFoundation::extraMemoryCost() const 894{ 895 double duration = this->duration(); 896 if (!duration) 897 return 0; 898 899 return totalBytes() * buffered()->totalDuration() / duration; 900} 901 902void MediaPlayerPrivateAVFoundation::clearTextTracks() 903{ 904 for (unsigned i = 0; i < m_textTracks.size(); ++i) { 905 RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i]; 906 player()->removeTextTrack(track); 907 track->disconnect(); 908 } 909 m_textTracks.clear(); 910} 911 912void MediaPlayerPrivateAVFoundation::processNewAndRemovedTextTracks(const Vector<RefPtr<InbandTextTrackPrivateAVF> >& removedTextTracks) 913{ 914 if (removedTextTracks.size()) { 915 for (unsigned i = 0; i < m_textTracks.size(); ++i) { 916 if (!removedTextTracks.contains(m_textTracks[i])) 917 continue; 918 919 player()->removeTextTrack(removedTextTracks[i].get()); 920 m_textTracks.remove(i); 921 } 922 } 923 924 for (unsigned i = 0; i < m_textTracks.size(); ++i) { 925 RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i]; 926 927 track->setTextTrackIndex(i); 928 if (track->hasBeenReported()) 929 continue; 930 931 track->setHasBeenReported(true); 932 player()->addTextTrack(track.get()); 933 } 934 LOG(Media, "MediaPlayerPrivateAVFoundation::processNewAndRemovedTextTracks(%p) - found %lu text tracks", this, m_textTracks.size()); 935} 936 937} // namespace WebCore 938 939#endif 940