Changeset 85483 in webkit


Ignore:
Timestamp:
May 2, 2011 10:21:34 AM (13 years ago)
Author:
eric.carlson@apple.com
Message:

2011-05-02 Eric Carlson <eric.carlson@apple.com>

Reviewed by Brady Eidson.

The preload attribute of the video tag is not completely implemented
https://bugs.webkit.org/show_bug.cgi?id=43673
<rdar://problem/7508322>

Tested manually with manual-tests/media-elements/video-preload.html.

  • html/HTMLMediaElement.cpp: (WebCore::HTMLMediaElement::HTMLMediaElement): Initialize m_havePreparedToPlay. (WebCore::HTMLMediaElement::prepareForLoad): Ditto. (WebCore::HTMLMediaElement::prepareToPlay): New, tell player to prepare to play. (WebCore::HTMLMediaElement::seek): Call prepareToPlay when preload is less than 'metadata'

because we need to have media data loaded to seek.

(WebCore::HTMLMediaElement::updatePlayState): Call prepareToPlay.

  • html/HTMLMediaElement.h:
  • platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp: (WebCore::MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation): Remove

m_videoFrameHasDrawn and m_delayingLoad as they are no longer used.

(WebCore::MediaPlayerPrivateAVFoundation::resumeLoad): Removed.
(WebCore::MediaPlayerPrivateAVFoundation::load): Don't initialize m_videoFrameHasDrawn.

Move all preload logic to setPreload, call it from here.

(WebCore::MediaPlayerPrivateAVFoundation::prepareToPlay): Move all preload logic to

setPreload, call it.

(WebCore::MediaPlayerPrivateAVFoundation::duration): Don't cache duration = 0, it is

unlikely to be correct and isn't worth caching.

(WebCore::MediaPlayerPrivateAVFoundation::updateStates): Update for name change AVAssetStatus

to AssetStatus. Create the AVPlayer once we know an asset is playable but preload is
'metadata'. Set networkState to 'idle' when the playback buffer is full because that is
a signal that AVFoundation won't do any more IO. Set readyState to 'HAVE_CURRENT_DATA'
when the first frame is available.

(WebCore::MediaPlayerPrivateAVFoundation::metadataLoaded): Call tracksChanged so we cache

width, height, hasVideo, etc.

(WebCore::MediaPlayerPrivateAVFoundation::repaint): Don't set m_videoFrameHasDrawn, it is done

in derived classes.

(WebCore::MediaPlayerPrivateAVFoundation::setPreload): Centralize all logic about when to create

AVAsset and AVPlayerItem here.

  • platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
  • platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.h:
  • platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm: (WebCore::MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC): Initialize

m_videoFrameHasDrawn.

(WebCore::MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame): New, renamed from

videoLayerIsReadyToDisplay. Return true if we have a layer with frames available or
if we have painted a frame to the context.

(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL): New, create the AVAsset

if necessary.

(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForCacheResource): Ditto.
(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer): Restructure logic.
(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem): New, create AVPlayerItem.
(WebCore::MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata): Correct logging.
(WebCore::MediaPlayerPrivateAVFoundationObjC::playerItemStatus): Return "buffer full" when

the buffer is full.

(WebCore::MediaPlayerPrivateAVFoundationObjC::platformDuration): Get the duration from the

AVAsset when we haven't allocated the AVPlayerItem yet so that we can return duration
when we only have metadata.

(WebCore::MediaPlayerPrivateAVFoundationObjC::assetStatus): Update for name change.
(WebCore::MediaPlayerPrivateAVFoundationObjC::paint): Set m_videoFrameHasDrawn.
(WebCore::MediaPlayerPrivateAVFoundationObjC::tracksChanged): Get attributes from AVAsset

when when we haven't allocated the AVPlayerItem yet so that we can report attributes
when we only have metadata.

(WebCore::MediaPlayerPrivateAVFoundationObjC::sizeChanged): Guard against being called before

we have allocated the AVPlayerItem.

Location:
trunk/Source/WebCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r85480 r85483  
     12011-05-02  Eric Carlson  <eric.carlson@apple.com>
     2
     3        Reviewed by Brady Eidson.
     4
     5        The preload attribute of the video tag is not completely implemented
     6        https://bugs.webkit.org/show_bug.cgi?id=43673
     7        <rdar://problem/7508322>
     8
     9        Tested manually with manual-tests/media-elements/video-preload.html.
     10
     11        * html/HTMLMediaElement.cpp:
     12        (WebCore::HTMLMediaElement::HTMLMediaElement): Initialize m_havePreparedToPlay.
     13        (WebCore::HTMLMediaElement::prepareForLoad): Ditto.
     14        (WebCore::HTMLMediaElement::prepareToPlay): New, tell player to prepare to play.
     15        (WebCore::HTMLMediaElement::seek): Call prepareToPlay when preload is less than 'metadata'
     16            because we need to have media data loaded to seek.
     17        (WebCore::HTMLMediaElement::updatePlayState): Call prepareToPlay.
     18        * html/HTMLMediaElement.h:
     19
     20        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
     21        (WebCore::MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation):  Remove
     22            m_videoFrameHasDrawn and m_delayingLoad as they are no longer used.
     23        (WebCore::MediaPlayerPrivateAVFoundation::resumeLoad): Removed.
     24        (WebCore::MediaPlayerPrivateAVFoundation::load): Don't initialize m_videoFrameHasDrawn.
     25            Move all preload logic to setPreload, call it from here.
     26        (WebCore::MediaPlayerPrivateAVFoundation::prepareToPlay): Move all preload logic to
     27            setPreload, call it.
     28        (WebCore::MediaPlayerPrivateAVFoundation::duration): Don't cache duration = 0, it is
     29            unlikely to be correct and isn't worth caching.
     30        (WebCore::MediaPlayerPrivateAVFoundation::updateStates): Update for name change AVAssetStatus
     31            to AssetStatus. Create the AVPlayer once we know an asset is playable but preload is
     32            'metadata'. Set networkState to 'idle' when the playback buffer is full because that is
     33            a signal that AVFoundation won't do any more IO. Set readyState to 'HAVE_CURRENT_DATA'
     34            when the first frame is available.
     35        (WebCore::MediaPlayerPrivateAVFoundation::metadataLoaded): Call tracksChanged so we cache
     36            width, height, hasVideo, etc.
     37        (WebCore::MediaPlayerPrivateAVFoundation::repaint): Don't set m_videoFrameHasDrawn, it is done
     38            in derived classes.
     39        (WebCore::MediaPlayerPrivateAVFoundation::setPreload): Centralize all logic about when to create
     40            AVAsset and AVPlayerItem here.
     41        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
     42
     43        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.h:
     44        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm:
     45        (WebCore::MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC): Initialize
     46            m_videoFrameHasDrawn.
     47        (WebCore::MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame): New, renamed from
     48            videoLayerIsReadyToDisplay. Return true if we have a layer with frames available or
     49            if we have painted a frame to the context.
     50        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL): New, create the AVAsset
     51            if necessary.
     52        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForCacheResource): Ditto.
     53        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayer): Restructure logic.
     54        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem): New, create AVPlayerItem.
     55        (WebCore::MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata): Correct logging.
     56        (WebCore::MediaPlayerPrivateAVFoundationObjC::playerItemStatus): Return "buffer full" when
     57            the buffer is full.
     58        (WebCore::MediaPlayerPrivateAVFoundationObjC::platformDuration): Get the duration from the
     59            AVAsset when we haven't allocated the AVPlayerItem yet so that we can return duration
     60            when we only have metadata.
     61        (WebCore::MediaPlayerPrivateAVFoundationObjC::assetStatus): Update for name change.
     62        (WebCore::MediaPlayerPrivateAVFoundationObjC::paint): Set m_videoFrameHasDrawn.
     63        (WebCore::MediaPlayerPrivateAVFoundationObjC::tracksChanged): Get attributes from AVAsset
     64            when when we haven't allocated the AVPlayerItem yet so that we can report attributes
     65            when we only have metadata.
     66        (WebCore::MediaPlayerPrivateAVFoundationObjC::sizeChanged): Guard against being called before
     67            we have allocated the AVPlayerItem.
     68
    1692011-05-02  Philippe Normand  <pnormand@igalia.com>
    270
  • trunk/Source/WebCore/html/HTMLMediaElement.cpp

    r84816 r85483  
    171171    , m_loadInitiatedByUserGesture(false)
    172172    , m_completelyLoaded(false)
     173    , m_havePreparedToPlay(false)
    173174{
    174175    LOG(Media, "HTMLMediaElement::HTMLMediaElement");
     
    517518    m_haveFiredLoadedData = false;
    518519    m_completelyLoaded = false;
     520    m_havePreparedToPlay = false;
    519521    m_displayMode = Unknown;
    520522
     
    10831085    return m_player ? m_player->supportsSave() : false;
    10841086}
    1085    
     1087
     1088void HTMLMediaElement::prepareToPlay()
     1089{
     1090    if (m_havePreparedToPlay)
     1091        return;
     1092    m_havePreparedToPlay = true;
     1093    m_player->prepareToPlay();
     1094}
     1095
    10861096void HTMLMediaElement::seek(float time, ExceptionCode& ec)
    10871097{
     
    10951105        return;
    10961106    }
     1107
     1108    // If the media engine has been told to postpone loading data, let it go ahead now.
     1109    if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA)
     1110        prepareToPlay();
    10971111
    10981112    // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set.
     
    21972211
    21982212        if (couldPlayIfEnoughData())
    2199             m_player->prepareToPlay();
     2213            prepareToPlay();
    22002214
    22012215        if (hasMediaControls())
  • trunk/Source/WebCore/html/HTMLMediaElement.h

    r84222 r85483  
    287287    void cancelPendingEventsAndCallbacks();
    288288    void waitForSourceChange();
     289    void prepareToPlay();
    289290
    290291    enum InvalidSourceAction { DoNothing, Complain };
     
    418419    bool m_loadInitiatedByUserGesture : 1;
    419420    bool m_completelyLoaded : 1;
     421    bool m_havePreparedToPlay : 1;
    420422};
    421423
  • trunk/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp

    r84988 r85483  
    3131
    3232#include "ApplicationCacheHost.h"
     33#include "ApplicationCacheResource.h"
    3334#include "DocumentLoader.h"
    3435#include "FrameView.h"
     
    4647namespace WebCore {
    4748
    48 static const float invalidTime = -1.0f;
    49 
    5049MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(MediaPlayer* player)
    5150    : m_player(player)
    5251    , m_queuedNotifications()
    5352    , m_queueMutex()
    54     , m_mainThreadCallPending(false)
    5553    , m_networkState(MediaPlayer::Empty)
    5654    , m_readyState(MediaPlayer::HaveNothing)
     
    5957    , m_cachedMaxTimeLoaded(0)
    6058    , m_cachedMaxTimeSeekable(0)
    61     , m_cachedDuration(invalidTime)
    62     , m_reportedDuration(invalidTime)
    63     , m_seekTo(invalidTime)
     59    , m_cachedDuration(invalidTime())
     60    , m_reportedDuration(invalidTime())
     61    , m_seekTo(invalidTime())
    6462    , m_requestedRate(1)
    65     , m_delayCallbacks(false)
    66     , m_havePreparedToPlay(false)
     63    , m_delayCallbacks(0)
     64    , m_mainThreadCallPending(false)
    6765    , m_assetIsPlayable(false)
    6866    , m_visible(false)
    69     , m_videoFrameHasDrawn(false)
    7067    , m_loadingMetadata(false)
    71     , m_delayingLoad(false)
    7268    , m_isAllowedToRender(false)
    7369    , m_cachedHasAudio(false)
     
    168164}
    169165
    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 
    181166void MediaPlayerPrivateAVFoundation::load(const String& url)
    182167{
     
    192177    }
    193178
    194     m_videoFrameHasDrawn = false;
    195179    m_assetURL = url;
    196180
     
    199183        return;
    200184
    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();
     185    setPreload(m_preload);
    208186}
    209187
     
    233211    LOG(Media, "MediaPlayerPrivateAVFoundation::prepareToPlay(%p)", this);
    234212
    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();
     213    setPreload(MediaPlayer::Auto);
    251214}
    252215
     
    270233}
    271234
    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 
    278235float MediaPlayerPrivateAVFoundation::duration() const
    279236{
     237    if (m_cachedDuration != invalidTime())
     238        return m_cachedDuration;
     239
    280240    if (!metaDataAvailable())
    281241        return 0;
    282242
    283     if (m_cachedDuration == invalidTime) {
    284         m_cachedDuration = platformDuration();
    285         LOG(Media, "MediaPlayerPrivateAVFMac::duration(%p) - caching %f", this, m_cachedDuration);
    286     }
    287 
     243    float duration = platformDuration();
     244    if (!duration || duration == invalidTime())
     245        return 0;
     246
     247    m_cachedDuration = duration;
     248    LOG(Media, "MediaPlayerPrivateAVFMac::duration(%p) - caching %f", this, m_cachedDuration);
    288249    return m_cachedDuration;
    289250}
     
    327288        return false;
    328289
    329     return m_seekTo != invalidTime;
     290    return m_seekTo != invalidTime();
    330291}
    331292
     
    433394    else {
    434395        // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state.
    435         AVAssetStatus avAssetStatus = assetStatus();
     396        AssetStatus avAssetStatus = assetStatus();
    436397        ItemStatus itemStatus = playerItemStatus();
    437398       
     
    439400        if (m_readyState < MediaPlayer::HaveMetadata && avAssetStatus > MediaPlayerAVAssetStatusLoading) {
    440401            if (m_assetIsPlayable) {
    441                 if (itemStatus == MediaPlayerAVPlayerItemStatusUnknown) {
     402                if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) {
    442403                    if (avAssetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData) {
    443404                        // We may have a playable asset that doesn't support inspection prior to playback; go ahead
     
    446407                        m_networkState = MediaPlayer::Loading;
    447408                        prepareToPlay();
    448                     } else
     409                    } else {
     410                        // The asset is playable, but we don't want to load media data yet so don't allocate
     411                        // the player item. Even though we don't want to play yet, allocate a player so
     412                        // we can create a layer as soon as possible.
     413                        createAVPlayer();
    449414                        m_networkState = MediaPlayer::Idle;
     415                    }
    450416                }
    451                 if (avAssetStatus == MediaPlayerAVAssetStatusLoaded)
    452                     m_readyState = MediaPlayer::HaveMetadata;
     417                m_readyState = MediaPlayer::HaveMetadata;
    453418            } else {
    454419                // FIX ME: fetch the error associated with the @"playable" key to distinguish between format
     
    464429                float maxLoaded = maxTimeLoaded();
    465430                switch (itemStatus) {
     431                case MediaPlayerAVPlayerItemStatusDoesNotExist:
    466432                case MediaPlayerAVPlayerItemStatusUnknown:
    467433                    break;
     
    472438                    m_readyState = MediaPlayer::HaveEnoughData;
    473439                    break;
     440
     441                case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
     442                    m_networkState = MediaPlayer::Idle;
     443
    474444                case MediaPlayerAVPlayerItemStatusReadyToPlay:
    475                 case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
    476445                    // If the readyState is already HaveEnoughData, don't go lower because of this state change.
    477446                    if (m_readyState == MediaPlayer::HaveEnoughData)
     
    486455                }
    487456
    488                 if (itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
     457                if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
    489458                    m_networkState = (maxLoaded == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading;
    490459            }
     
    496465
    497466    if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) {
     467        if (m_readyState < MediaPlayer::HaveCurrentData)
     468            m_readyState = MediaPlayer::HaveCurrentData;
    498469        m_haveReportedFirstVideoFrame = true;
    499470        m_player->firstVideoFrameAvailable();
     
    531502}
    532503
    533 bool MediaPlayerPrivateAVFoundation::hasAvailableVideoFrame() const
    534 {
    535     if (currentRenderingMode() == MediaRenderingToLayer)
    536         return videoLayerIsReadyToDisplay();
    537 
    538     // When using the software renderer we hope someone will signal that a frame is available so we might as well
    539     // wait until we know that a frame has been drawn.
    540     return m_videoFrameHasDrawn;
    541 }
    542 
    543504void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
    544505{
     
    550511{
    551512    m_loadingMetadata = false;
     513    tracksChanged();
    552514    updateStates();
    553515}
     
    576538    float dur = duration();
    577539    if (dur != m_reportedDuration) {
    578         if (m_reportedDuration != invalidTime)
     540        if (m_reportedDuration != invalidTime())
    579541            m_player->durationChanged();
    580542        m_reportedDuration = dur;
     
    591553    LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time);
    592554
    593     if (m_seekTo == invalidTime)
     555    if (m_seekTo == invalidTime())
    594556        return;
    595557
     
    601563    float currentRate = rate();
    602564    if ((currentRate > 0 && time >= m_seekTo) || (currentRate < 0 && time <= m_seekTo) || (abs(m_seekTo - time) <= smallSeekDelta)) {
    603         m_seekTo = invalidTime;
     565        m_seekTo = invalidTime();
    604566        updateStates();
    605567        m_player->timeChanged();
     
    612574   
    613575    if (finished)
    614         m_seekTo = invalidTime;
     576        m_seekTo = invalidTime();
    615577}
    616578
     
    629591void MediaPlayerPrivateAVFoundation::repaint()
    630592{
    631     m_videoFrameHasDrawn = true;
    632593    m_player->repaint();
    633594}
     
    647608{
    648609    m_preload = preload;
    649     if (m_delayingLoad && m_preload != MediaPlayer::None)
    650         resumeLoad();
     610    if (!m_assetURL.length())
     611        return;
     612
     613    if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) {
     614#if ENABLE(OFFLINE_WEB_APPLICATIONS)
     615        Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
     616        ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0;
     617        ApplicationCacheResource* resource;
     618        if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(m_assetURL), resource) && resource) {
     619            // AVFoundation can't open arbitrary data pointers, so if this ApplicationCacheResource doesn't
     620            // have a valid local path, just open the resource's original URL.
     621            if (resource->path().isEmpty())
     622                createAVAssetForURL(resource->url());
     623            else
     624                createAVAssetForCacheResource(resource);
     625        } else
     626#endif   
     627            createAVAssetForURL(m_assetURL);
     628
     629        checkPlayability();
     630    }
     631
     632    if (m_preload == MediaPlayer::Auto && playerItemStatus() == MediaPlayerAVPlayerItemStatusDoesNotExist)
     633        createAVPlayerItem();
    651634}
    652635
  • trunk/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h

    r84988 r85483  
    143143    virtual unsigned bytesLoaded() const;
    144144    virtual void setSize(const IntSize&);
    145     virtual void paint(GraphicsContext*, const IntRect&);
     145    virtual void paint(GraphicsContext*, const IntRect&) = 0;
    146146    virtual void paintCurrentFrameInContext(GraphicsContext*, const IntRect&) = 0;
    147147    virtual void setPreload(MediaPlayer::Preload);
    148     virtual bool hasAvailableVideoFrame() const;
    149148#if USE(ACCELERATED_COMPOSITING)
    150149    virtual PlatformLayer* platformLayer() const { return 0; }
     
    160159
    161160    // Required interfaces for concrete derived classes.
    162     virtual void createAVPlayerForURL(const String& url) = 0;
     161    virtual void createAVAssetForURL(const String&) = 0;
     162    virtual void createAVPlayer() = 0;
     163    virtual void createAVPlayerItem() = 0;
    163164#if ENABLE(OFFLINE_WEB_APPLICATIONS)
    164     virtual void createAVPlayerForCacheResource(ApplicationCacheResource*) = 0;
     165    virtual void createAVAssetForCacheResource(ApplicationCacheResource*) = 0;
    165166#endif
    166167
    167168    enum ItemStatus {
     169        MediaPlayerAVPlayerItemStatusDoesNotExist,
    168170        MediaPlayerAVPlayerItemStatusUnknown,
    169171        MediaPlayerAVPlayerItemStatusFailed,
     
    175177    virtual ItemStatus playerItemStatus() const = 0;
    176178
    177     enum AVAssetStatus {
     179    enum AssetStatus {
     180        MediaPlayerAVAssetStatusDoesNotExist,
    178181        MediaPlayerAVAssetStatusUnknown,
    179182        MediaPlayerAVAssetStatusLoading,
     
    183186        MediaPlayerAVAssetStatusPlayable,
    184187    };
    185     virtual AVAssetStatus assetStatus() const = 0;
     188    virtual AssetStatus assetStatus() const = 0;
    186189
    187190    virtual void platformSetVisible(bool) = 0;
     
    207210    virtual void createVideoLayer() = 0;
    208211    virtual void destroyVideoLayer() = 0;
    209     virtual bool videoLayerIsReadyToDisplay() const = 0;
     212
     213    virtual bool hasAvailableVideoFrame() const = 0;
    210214
    211215    virtual bool hasContextRenderer() const = 0;
     
    213217
    214218protected:
    215     void resumeLoad();
    216219    void updateStates();
    217220
     
    237240
    238241    static void mainThreadCallback(void*);
     242   
     243    float invalidTime() const { return -1.0f; }
    239244
    240245private:
    241 
    242246    MediaPlayer* m_player;
    243247
    244248    Vector<Notification> m_queuedNotifications;
    245249    Mutex m_queueMutex;
    246     bool m_mainThreadCallPending;
    247250
    248251    mutable RefPtr<TimeRanges> m_cachedLoadedTimeRanges;
     
    264267    float m_requestedRate;
    265268    int m_delayCallbacks;
    266     bool m_havePreparedToPlay;
     269    bool m_mainThreadCallPending;
    267270    bool m_assetIsPlayable;
    268271    bool m_visible;
    269     bool m_videoFrameHasDrawn;
    270272    bool m_loadingMetadata;
    271     bool m_delayingLoad;
    272273    bool m_isAllowedToRender;
    273274    bool m_cachedHasAudio;
  • trunk/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.h

    r84988 r85483  
    8787
    8888    virtual void createAVPlayer();
    89     virtual void createAVPlayerForURL(const String& url);
     89    virtual void createAVPlayerItem();
     90    virtual void createAVAssetForURL(const String& url);
    9091#if ENABLE(OFFLINE_WEB_APPLICATIONS)
    91     virtual void createAVPlayerForCacheResource(ApplicationCacheResource*);
     92    virtual void createAVAssetForCacheResource(ApplicationCacheResource*);
    9293#endif
    9394    virtual MediaPlayerPrivateAVFoundation::ItemStatus playerItemStatus() const;
    94     virtual MediaPlayerPrivateAVFoundation::AVAssetStatus assetStatus() const;
     95    virtual MediaPlayerPrivateAVFoundation::AssetStatus assetStatus() const;
    9596
    9697    virtual void checkPlayability();
     
    106107    virtual void sizeChanged();
    107108
     109    virtual bool hasAvailableVideoFrame() const;
     110
    108111    virtual void createContextVideoRenderer();
    109112    virtual void destroyContextVideoRenderer();
     
    111114    virtual void createVideoLayer();
    112115    virtual void destroyVideoLayer();
    113     virtual bool videoLayerIsReadyToDisplay() const;
    114116
    115117    virtual bool hasContextRenderer() const;
     
    126128    RetainPtr<AVAssetImageGenerator> m_imageGenerator;
    127129    id m_timeObserver;
     130    bool m_videoFrameHasDrawn;
    128131};
    129132
  • trunk/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm

    r84988 r85483  
    139139    , m_objcObserver(AdoptNS, [[WebCoreAVFMovieObserver alloc] initWithCallback:this])
    140140    , m_timeObserver(0)
     141    , m_videoFrameHasDrawn(false)
    141142{
    142143}
     
    235236}
    236237
    237 bool MediaPlayerPrivateAVFoundationObjC::videoLayerIsReadyToDisplay() const
    238 {
    239     return (m_videoLayer && [m_videoLayer.get() isReadyForDisplay]);
    240 }
    241 
    242 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerForURL(const String& url)
    243 {
     238bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
     239{
     240    return (m_videoFrameHasDrawn || (m_videoLayer && [m_videoLayer.get() isReadyForDisplay]));
     241}
     242
     243void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const String& url)
     244{
     245    if (m_avAsset)
     246        return;
     247
    244248    setDelayCallbacks(true);
    245 
    246     if (!m_avAsset) {
    247         NSURL *cocoaURL = KURL(ParsedURLString, url);
    248         m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:nil]);
    249     }
    250    
    251     createAVPlayer();
     249    NSURL *cocoaURL = KURL(ParsedURLString, url);
     250    m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:nil]);
     251    setDelayCallbacks(false);
    252252}
    253253
    254254#if ENABLE(OFFLINE_WEB_APPLICATIONS)
    255 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerForCacheResource(ApplicationCacheResource* resource)
    256 {
    257     // AVFoundation can't open arbitrary data pointers, so if this ApplicationCacheResource doesn't
    258     // have a valid local path, just open the resource's original URL.
    259     if (resource->path().isEmpty()) {
    260         createAVPlayerForURL(resource->url());
    261         return;
    262     }
     255void MediaPlayerPrivateAVFoundationObjC::createAVAssetForCacheResource(ApplicationCacheResource* resource)
     256{
     257    if (m_avAsset)
     258        return;
     259
     260    // AVFoundation can't open arbitrary data pointers.
     261    ASSERT(!resource->path().isEmpty());
    263262   
    264263    setDelayCallbacks(true);
    265264
    266     if (!m_avAsset) {
    267         NSURL* localURL = [NSURL fileURLWithPath:resource->path()];
    268         m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:localURL options:nil]);
    269     }
    270 
    271     createAVPlayer();
     265    NSURL* localURL = [NSURL fileURLWithPath:resource->path()];
     266    m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:localURL options:nil]);
     267
     268    setDelayCallbacks(false);
    272269}
    273270#endif
     
    275272void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
    276273{
    277     if (!m_avPlayer) {
    278         m_avPlayer.adoptNS([[AVPlayer alloc] init]);
    279        
    280         [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
    281        
    282         // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but
    283         // our observer will also be called whenever a seek happens.
    284         const double veryLongInterval = 60*60*60*24*30;
    285         WebCoreAVFMovieObserver *observer = m_objcObserver.get();
    286         m_timeObserver = [m_avPlayer.get() addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(veryLongInterval, 10) queue:nil usingBlock:^(CMTime time){
    287             [observer timeChanged:CMTimeGetSeconds(time)];
    288         }];
    289     }
    290 
    291     if (!m_avPlayerItem) {
    292         // Create the player item so we can media data.
    293         m_avPlayerItem.adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
    294        
    295         [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
    296 
    297         for (NSString *keyName in itemKVOProperties())
    298             [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem];
    299 
    300         [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
    301     }
     274    if (m_avPlayer)
     275        return;
     276
     277    setDelayCallbacks(true);
     278
     279    m_avPlayer.adoptNS([[AVPlayer alloc] init]);
     280    [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
     281   
     282    // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but
     283    // our observer will also be called whenever a seek happens.
     284    const double veryLongInterval = 60*60*60*24*30;
     285    WebCoreAVFMovieObserver *observer = m_objcObserver.get();
     286    m_timeObserver = [m_avPlayer.get() addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(veryLongInterval, 10) queue:nil usingBlock:^(CMTime time){
     287        [observer timeChanged:CMTimeGetSeconds(time)];
     288    }];
     289
     290    setDelayCallbacks(false);
     291}
     292
     293void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
     294{
     295    if (m_avPlayerItem)
     296        return;
     297
     298    setDelayCallbacks(true);
     299
     300    // Create the player item so we can media data.
     301    m_avPlayerItem.adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
     302   
     303    [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
     304
     305    for (NSString *keyName in itemKVOProperties())
     306        [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem];
     307
     308    [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
    302309
    303310    setDelayCallbacks(false);
     
    315322void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata()
    316323{
    317     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::playabilityKnown(%p) - requesting metadata loading", this);
     324    LOG(Media, "MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata(%p) - requesting metadata loading", this);
    318325    [m_avAsset.get() loadValuesAsynchronouslyForKeys:[assetMetadataKeyNames() retain] completionHandler:^{
    319326        [m_objcObserver.get() metadataLoaded];
     
    324331{
    325332    if (!m_avPlayerItem)
    326         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
     333        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
    327334
    328335    AVPlayerItemStatus status = [m_avPlayerItem.get() status];
     
    333340    if ([m_avPlayerItem.get() isPlaybackLikelyToKeepUp])
    334341        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
    335     if (buffered()->contain(duration()))
     342    if ([m_avPlayerItem.get() isPlaybackBufferFull])
    336343        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
    337     if (buffered()->contain(currentTime()))
     344    if ([m_avPlayerItem.get() isPlaybackBufferEmpty])
    338345        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
    339346
     
    386393float MediaPlayerPrivateAVFoundationObjC::platformDuration() const
    387394{
    388     if (!metaDataAvailable() || !m_avPlayerItem)
    389         return 0;
     395    if (!metaDataAvailable())
     396        return invalidTime();
    390397   
    391     float duration;
    392     CMTime cmDuration = [m_avPlayerItem.get() duration];
     398    CMTime cmDuration;
     399   
     400    // Check the avitem if we have one, some assets never report duration.
     401    if (m_avPlayerItem)
     402        cmDuration = [m_avPlayerItem.get() duration];
     403    else
     404        cmDuration= [m_avAsset.get() duration];
     405
    393406    if (CMTIME_IS_NUMERIC(cmDuration))
    394         duration = narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
    395     else if (CMTIME_IS_INDEFINITE(cmDuration))
    396         duration = numeric_limits<float>::infinity();
    397     else {
    398         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::duration(%p) - invalid duration, returning 0", this);
    399         return 0;
    400     }
    401 
    402     return duration;
     407        return narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
     408
     409    if (CMTIME_IS_INDEFINITE(cmDuration))
     410        return numeric_limits<float>::infinity();
     411
     412    LOG(Media, "MediaPlayerPrivateAVFoundationObjC::duration(%p) - invalid duration, returning -1", this);
     413    return invalidTime();
    403414}
    404415
     
    536547}
    537548
    538 MediaPlayerPrivateAVFoundation::AVAssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
     549MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
    539550{
    540551    if (!m_avAsset)
    541         return MediaPlayerAVAssetStatusUnknown;
     552        return MediaPlayerAVAssetStatusDoesNotExist;
    542553
    543554    for (NSString *keyName in assetMetadataKeyNames()) {
     
    588599    setDelayCallbacks(false);
    589600
    590     MediaPlayerPrivateAVFoundation::paint(context, rect);
     601    m_videoFrameHasDrawn = true;
    591602}
    592603
     
    663674void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
    664675{
     676    if (!m_avAsset)
     677        return;
     678
    665679    // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
    666680    // asked about those fairly fequently.
    667     bool hasVideo = false;
    668     bool hasAudio = false;
    669     bool hasCaptions = false;
    670     NSArray *tracks = [m_avPlayerItem.get() tracks];
    671     for (AVPlayerItemTrack *track in tracks) {
    672         if ([track isEnabled]) {
    673             AVAssetTrack *assetTrack = [track assetTrack];
    674             if ([[assetTrack mediaType] isEqualToString:AVMediaTypeVideo])
    675                 hasVideo = true;
    676             else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeAudio])
    677                 hasAudio = true;
    678             else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
    679                 hasCaptions = true;
     681    if (!m_avPlayerItem) {
     682        // We don't have a player item yet, so check with the asset because some assets support inspection
     683        // prior to becoming ready to play.
     684        setHasVideo([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual] count]);
     685        setHasAudio([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicAudible] count]);
     686        setHasClosedCaptions([[m_avAsset.get() tracksWithMediaType:AVMediaTypeClosedCaption] count]);
     687    } else {
     688        bool hasVideo = false;
     689        bool hasAudio = false;
     690        bool hasCaptions = false;
     691        NSArray *tracks = [m_avPlayerItem.get() tracks];
     692        for (AVPlayerItemTrack *track in tracks) {
     693            if ([track isEnabled]) {
     694                AVAssetTrack *assetTrack = [track assetTrack];
     695                if ([[assetTrack mediaType] isEqualToString:AVMediaTypeVideo])
     696                    hasVideo = true;
     697                else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeAudio])
     698                    hasAudio = true;
     699                else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
     700                    hasCaptions = true;
     701            }
    680702        }
    681     }
    682     setHasVideo(hasVideo);
    683     setHasAudio(hasAudio);
    684     setHasClosedCaptions(hasCaptions);
     703        setHasVideo(hasVideo);
     704        setHasAudio(hasAudio);
     705        setHasClosedCaptions(hasCaptions);
     706    }
    685707
    686708    sizeChanged();
     
    689711void MediaPlayerPrivateAVFoundationObjC::sizeChanged()
    690712{
     713    if (!m_avAsset)
     714        return;
     715
    691716    NSArray *tracks = [m_avAsset.get() tracks];
    692717
    693718    // Some assets don't report track properties until they are completely ready to play, but we
    694719    // want to report a size as early as possible so use presentationSize when an asset has no tracks.
    695     if (![tracks count]) {
     720    if (m_avPlayerItem && ![tracks count]) {
    696721        setNaturalSize(IntSize([m_avPlayerItem.get() presentationSize]));
    697722        return;
Note: See TracChangeset for help on using the changeset viewer.