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