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