Home | History | Annotate | Download | only in avfoundation
      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