Unreviewed, rolling out r152135.
[WebKit-https.git] / Source / WebCore / platform / graphics / avfoundation / MediaPlayerPrivateAVFoundation.cpp
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
46 using namespace std;
47
48 namespace WebCore {
49
50 MediaPlayerPrivateAVFoundation::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_mainThreadCallPending(false)
67     , m_assetIsPlayable(false)
68     , m_visible(false)
69     , m_loadingMetadata(false)
70     , m_isAllowedToRender(false)
71     , m_cachedHasAudio(false)
72     , m_cachedHasVideo(false)
73     , m_cachedHasCaptions(false)
74     , m_ignoreLoadStateChanges(false)
75     , m_haveReportedFirstVideoFrame(false)
76     , m_playWhenFramesAvailable(false)
77 #if !PLATFORM(WIN)
78     , m_inbandTrackConfigurationPending(false)
79 #endif
80     , m_seekCount(0)
81 {
82     LOG(Media, "MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(%p)", this);
83 }
84
85 MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation()
86 {
87     LOG(Media, "MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation(%p)", this);
88     setIgnoreLoadStateChanges(true);
89     cancelCallOnMainThread(mainThreadCallback, this);
90 }
91
92 MediaPlayerPrivateAVFoundation::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
105 MediaPlayerPrivateAVFoundation::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
118 void 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
160 void 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
172 bool MediaPlayerPrivateAVFoundation::hasSetUpVideoRendering() const
173 {
174     return hasLayerRenderer() || hasContextRenderer();
175 }
176
177 void 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
199 void 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
219 void MediaPlayerPrivateAVFoundation::prepareToPlay()
220 {
221     LOG(Media, "MediaPlayerPrivateAVFoundation::prepareToPlay(%p)", this);
222
223     setPreload(MediaPlayer::Auto);
224 }
225
226 void 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
238 void MediaPlayerPrivateAVFoundation::pause()
239 {
240     LOG(Media, "MediaPlayerPrivateAVFoundation::pause(%p)", this);
241     m_playWhenFramesAvailable = false;
242     platformPause();
243 }
244
245 float 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
259 void 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 !PLATFORM(WIN)
271     if (currentTrack())
272         currentTrack()->beginSeeking();
273 #endif
274     
275     LOG(Media, "MediaPlayerPrivateAVFoundation::seek(%p) - seeking to %f", this, time);
276     m_seekTo = time;
277
278     ++m_seekCount;
279     seekToTime(time);
280 }
281
282 void MediaPlayerPrivateAVFoundation::setRate(float rate)
283 {
284     LOG(Media, "MediaPlayerPrivateAVFoundation::setRate(%p) - seting to %f", this, rate);
285     m_requestedRate = rate;
286
287     updateRate();
288 }
289
290 bool MediaPlayerPrivateAVFoundation::paused() const
291 {
292     if (!metaDataAvailable())
293         return true;
294
295     return rate() == 0;
296 }
297
298 bool MediaPlayerPrivateAVFoundation::seeking() const
299 {
300     if (!metaDataAvailable())
301         return false;
302
303     return m_seekTo != MediaPlayer::invalidTime();
304 }
305
306 IntSize MediaPlayerPrivateAVFoundation::naturalSize() const
307 {
308     if (!metaDataAvailable())
309         return IntSize();
310
311     // In spite of the name of this method, return the natural size transformed by the 
312     // initial movie scale because the spec says intrinsic size is:
313     //
314     //    ... the dimensions of the resource in CSS pixels after taking into account the resource's 
315     //    dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the 
316     //    format used by the resource
317
318     return m_cachedNaturalSize;
319 }
320
321 void MediaPlayerPrivateAVFoundation::setNaturalSize(IntSize size)
322 {
323     LOG(Media, "MediaPlayerPrivateAVFoundation:setNaturalSize(%p) - size = %d x %d", this, size.width(), size.height());
324
325     IntSize oldSize = m_cachedNaturalSize;
326     m_cachedNaturalSize = size;
327     if (oldSize != m_cachedNaturalSize)
328         m_player->sizeChanged();
329 }
330
331 void MediaPlayerPrivateAVFoundation::setHasVideo(bool b)
332 {
333     if (m_cachedHasVideo != b) {
334         m_cachedHasVideo = b;
335         m_player->characteristicChanged();
336     }
337 }
338
339 void MediaPlayerPrivateAVFoundation::setHasAudio(bool b)
340 {
341     if (m_cachedHasAudio != b) {
342         m_cachedHasAudio = b;
343         m_player->characteristicChanged();
344     }
345 }
346
347 void MediaPlayerPrivateAVFoundation::setHasClosedCaptions(bool b)
348 {
349     if (m_cachedHasCaptions != b) {
350         m_cachedHasCaptions = b;
351         m_player->characteristicChanged();
352     }
353 }
354
355 PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundation::buffered() const
356 {
357     if (!m_cachedLoadedTimeRanges)
358         m_cachedLoadedTimeRanges = platformBufferedTimeRanges();
359
360     return m_cachedLoadedTimeRanges->copy();
361 }
362
363 double MediaPlayerPrivateAVFoundation::maxTimeSeekableDouble() const
364 {
365     if (!metaDataAvailable())
366         return 0;
367
368     if (!m_cachedMaxTimeSeekable)
369         m_cachedMaxTimeSeekable = platformMaxTimeSeekable();
370
371     LOG(Media, "MediaPlayerPrivateAVFoundation::maxTimeSeekable(%p) - returning %f", this, m_cachedMaxTimeSeekable);
372     return m_cachedMaxTimeSeekable;   
373 }
374
375 double MediaPlayerPrivateAVFoundation::minTimeSeekable() const
376 {
377     if (!metaDataAvailable())
378         return 0;
379
380     if (!m_cachedMinTimeSeekable)
381         m_cachedMinTimeSeekable = platformMinTimeSeekable();
382
383     LOG(Media, "MediaPlayerPrivateAVFoundation::minTimeSeekable(%p) - returning %f", this, m_cachedMinTimeSeekable);
384     return m_cachedMinTimeSeekable;
385 }
386
387 float MediaPlayerPrivateAVFoundation::maxTimeLoaded() const
388 {
389     if (!metaDataAvailable())
390         return 0;
391
392     if (!m_cachedMaxTimeLoaded)
393         m_cachedMaxTimeLoaded = platformMaxTimeLoaded();
394
395     return m_cachedMaxTimeLoaded;   
396 }
397
398 bool MediaPlayerPrivateAVFoundation::didLoadingProgress() const
399 {
400     if (!duration() || !totalBytes())
401         return false;
402     float currentMaxTimeLoaded = maxTimeLoaded();
403     bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
404     m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
405
406     return didLoadingProgress;
407 }
408
409 bool MediaPlayerPrivateAVFoundation::isReadyForVideoSetup() const
410 {
411     // AVFoundation will not return true for firstVideoFrameAvailable until
412     // an AVPlayerLayer has been added to the AVPlayerItem, so allow video setup
413     // here if a video track to trigger allocation of a AVPlayerLayer.
414     return (m_isAllowedToRender || m_cachedHasVideo) && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
415 }
416
417 void MediaPlayerPrivateAVFoundation::prepareForRendering()
418 {
419     if (m_isAllowedToRender)
420         return;
421     m_isAllowedToRender = true;
422
423     setUpVideoRendering();
424
425     if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer)
426         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
427 }
428
429 bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const
430 {
431 #if ENABLE(FULLSCREEN_API)
432     return true;
433 #else
434     // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine
435     return false;
436 #endif
437 }
438
439 void MediaPlayerPrivateAVFoundation::updateStates()
440 {
441     if (m_ignoreLoadStateChanges)
442         return;
443
444     MediaPlayer::NetworkState oldNetworkState = m_networkState;
445     MediaPlayer::ReadyState oldReadyState = m_readyState;
446
447     if (m_loadingMetadata)
448         m_networkState = MediaPlayer::Loading;
449     else {
450         // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state.
451         AssetStatus assetStatus = this->assetStatus();
452         ItemStatus itemStatus = playerItemStatus();
453         
454         m_assetIsPlayable = (assetStatus == MediaPlayerAVAssetStatusPlayable);
455         if (m_readyState < MediaPlayer::HaveMetadata && assetStatus > MediaPlayerAVAssetStatusLoading) {
456             if (m_assetIsPlayable) {
457                 if (assetStatus >= MediaPlayerAVAssetStatusLoaded)
458                     m_readyState = MediaPlayer::HaveMetadata;
459                 if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) {
460                     if (assetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData || isLiveStream()) {
461                         // The asset is playable but doesn't support inspection prior to playback (eg. streaming files),
462                         // or we are supposed to prepare for playback immediately, so create the player item now.
463                         m_networkState = MediaPlayer::Loading;
464                         prepareToPlay();
465                     } else
466                         m_networkState = MediaPlayer::Idle;
467                 }
468             } else {
469                 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 
470                 // and network errors.
471                 m_networkState = MediaPlayer::FormatError;
472             }
473         }
474         
475         if (assetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) {
476             switch (itemStatus) {
477             case MediaPlayerAVPlayerItemStatusDoesNotExist:
478             case MediaPlayerAVPlayerItemStatusUnknown:
479             case MediaPlayerAVPlayerItemStatusFailed:
480                 break;
481
482             case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp:
483             case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
484                 // If the status becomes PlaybackBufferFull, loading stops and the status will not
485                 // progress to LikelyToKeepUp. Set the readyState to  HAVE_ENOUGH_DATA, on the
486                 // presumption that if the playback buffer is full, playback will probably not stall.
487                 m_readyState = MediaPlayer::HaveEnoughData;
488                 break;
489
490             case MediaPlayerAVPlayerItemStatusReadyToPlay:
491                 // If the readyState is already HaveEnoughData, don't go lower because of this state change.
492                 if (m_readyState == MediaPlayer::HaveEnoughData)
493                     break;
494
495             case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty:
496                 if (maxTimeLoaded() > currentTime())
497                     m_readyState = MediaPlayer::HaveFutureData;
498                 else
499                     m_readyState = MediaPlayer::HaveCurrentData;
500                 break;
501             }
502
503             if (itemStatus == MediaPlayerAVPlayerItemStatusPlaybackBufferFull)
504                 m_networkState = MediaPlayer::Idle;
505             else if (itemStatus == MediaPlayerAVPlayerItemStatusFailed)
506                 m_networkState = MediaPlayer::DecodeError;
507             else if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
508                 m_networkState = (maxTimeLoaded() == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading;
509         }
510     }
511
512     if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode())
513         setUpVideoRendering();
514
515     if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) {
516         if (m_readyState < MediaPlayer::HaveCurrentData)
517             m_readyState = MediaPlayer::HaveCurrentData;
518         m_haveReportedFirstVideoFrame = true;
519         m_player->firstVideoFrameAvailable();
520     }
521
522     if (m_networkState != oldNetworkState)
523         m_player->networkStateChanged();
524
525     if (m_readyState != oldReadyState)
526         m_player->readyStateChanged();
527
528     if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) {
529         m_playWhenFramesAvailable = false;
530         platformPlay();
531     }
532
533 #if !LOG_DISABLED
534     if (m_networkState != oldNetworkState || oldReadyState != m_readyState) {
535         LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entered with networkState = %i, readyState = %i,  exiting with networkState = %i, readyState = %i",
536             this, static_cast<int>(oldNetworkState), static_cast<int>(oldReadyState), static_cast<int>(m_networkState), static_cast<int>(m_readyState));
537     }
538 #endif
539 }
540
541 void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 
542
543 }
544
545 void MediaPlayerPrivateAVFoundation::setVisible(bool visible)
546 {
547     if (m_visible == visible)
548         return;
549
550     m_visible = visible;
551     if (visible)
552         setUpVideoRendering();
553     
554     platformSetVisible(visible);
555 }
556
557 void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
558 {
559     // Set up or change the rendering path if necessary.
560     setUpVideoRendering();
561 }
562
563 void MediaPlayerPrivateAVFoundation::metadataLoaded()
564 {
565     m_loadingMetadata = false;
566     tracksChanged();
567 }
568
569 void MediaPlayerPrivateAVFoundation::rateChanged()
570 {
571     m_player->rateChanged();
572 }
573
574 void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged()
575 {
576     m_cachedLoadedTimeRanges = 0;
577     m_cachedMaxTimeLoaded = 0;
578     invalidateCachedDuration();
579 }
580
581 void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged()
582 {
583     m_cachedMaxTimeSeekable = 0;
584     m_cachedMinTimeSeekable = 0;
585 }
586
587 void MediaPlayerPrivateAVFoundation::timeChanged(double time)
588 {
589     LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time);
590     UNUSED_PARAM(time);
591 }
592
593 void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished)
594 {
595     LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, finished);
596     UNUSED_PARAM(finished);
597
598     ASSERT(m_seekCount);
599     if (--m_seekCount)
600         return;
601
602 #if !PLATFORM(WIN)
603     if (currentTrack())
604         currentTrack()->endSeeking();
605 #endif
606
607     m_seekTo = MediaPlayer::invalidTime();
608     updateStates();
609     m_player->timeChanged();
610 }
611
612 void MediaPlayerPrivateAVFoundation::didEnd()
613 {
614     // Hang onto the current time and use it as duration from now on since we are definitely at
615     // the end of the movie. Do this because the initial duration is sometimes an estimate.
616     float now = currentTime();
617     if (now > 0)
618         m_cachedDuration = now;
619
620     updateStates();
621     m_player->timeChanged();
622 }
623
624 void MediaPlayerPrivateAVFoundation::invalidateCachedDuration()
625 {
626     LOG(Media, "MediaPlayerPrivateAVFoundation::invalidateCachedDuration(%p)", this);
627     
628     m_cachedDuration = MediaPlayer::invalidTime();
629
630     // For some media files, reported duration is estimated and updated as media is loaded
631     // so report duration changed when the estimate is upated.
632     float duration = this->duration();
633     if (duration != m_reportedDuration) {
634         if (m_reportedDuration != MediaPlayer::invalidTime())
635             m_player->durationChanged();
636         m_reportedDuration = duration;
637     }
638     
639 }
640
641 void MediaPlayerPrivateAVFoundation::repaint()
642 {
643     m_player->repaint();
644 }
645
646 MediaPlayer::MovieLoadType MediaPlayerPrivateAVFoundation::movieLoadType() const
647 {
648     if (!metaDataAvailable() || assetStatus() == MediaPlayerAVAssetStatusUnknown)
649         return MediaPlayer::Unknown;
650
651     if (isLiveStream())
652         return MediaPlayer::LiveStream;
653
654     return MediaPlayer::Download;
655 }
656
657 void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload)
658 {
659     m_preload = preload;
660     if (!m_assetURL.length())
661         return;
662
663     setDelayCallbacks(true);
664
665     if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) {
666         createAVAssetForURL(m_assetURL);
667         checkPlayability();
668     }
669
670     // Don't force creation of the player and player item unless we already know that the asset is playable. If we aren't
671     // there yet, or if we already know it is not playable, creating them now won't help.
672     if (m_preload == MediaPlayer::Auto && m_assetIsPlayable) {
673         createAVPlayerItem();
674         createAVPlayer();
675     }
676
677     setDelayCallbacks(false);
678 }
679
680 void MediaPlayerPrivateAVFoundation::setDelayCallbacks(bool delay) const
681 {
682     MutexLocker lock(m_queueMutex);
683     if (delay)
684         ++m_delayCallbacks;
685     else {
686         ASSERT(m_delayCallbacks);
687         --m_delayCallbacks;
688     }
689 }
690
691 void MediaPlayerPrivateAVFoundation::mainThreadCallback(void* context)
692 {
693     LOG(Media, "MediaPlayerPrivateAVFoundation::mainThreadCallback(%p)", context);
694     MediaPlayerPrivateAVFoundation* player = static_cast<MediaPlayerPrivateAVFoundation*>(context);
695     player->clearMainThreadPendingFlag();
696     player->dispatchNotification();
697 }
698
699 void MediaPlayerPrivateAVFoundation::clearMainThreadPendingFlag()
700 {
701     MutexLocker lock(m_queueMutex);
702     m_mainThreadCallPending = false;
703 }
704
705 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, double time)
706 {
707     scheduleMainThreadNotification(Notification(type, time));
708 }
709
710 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, bool finished)
711 {
712     scheduleMainThreadNotification(Notification(type, finished));
713 }
714
715 #if !LOG_DISABLED
716 static const char* notificationName(MediaPlayerPrivateAVFoundation::Notification& notification)
717 {
718 #define DEFINE_TYPE_STRING_CASE(type) case MediaPlayerPrivateAVFoundation::Notification::type: return #type;
719     switch (notification.type()) {
720         FOR_EACH_MEDIAPLAYERPRIVATEAVFOUNDATION_NOTIFICATION_TYPE(DEFINE_TYPE_STRING_CASE)
721         default: return "";
722     }
723 #undef DEFINE_TYPE_STRING_CASE
724 }
725 #endif // !LOG_DISABLED
726     
727
728 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification notification)
729 {
730     LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - notification %s", this, notificationName(notification));
731     m_queueMutex.lock();
732
733     // It is important to always process the properties in the order that we are notified, 
734     // so always go through the queue because notifications happen on different threads.
735     m_queuedNotifications.append(notification);
736
737     bool delayDispatch = m_delayCallbacks || !isMainThread();
738     if (delayDispatch && !m_mainThreadCallPending) {
739         m_mainThreadCallPending = true;
740         callOnMainThread(mainThreadCallback, this);
741     }
742
743     m_queueMutex.unlock();
744
745     if (delayDispatch) {
746         LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - early return", this);
747         return;
748     }
749
750     dispatchNotification();
751 }
752
753 void MediaPlayerPrivateAVFoundation::dispatchNotification()
754 {
755     ASSERT(isMainThread());
756
757     Notification notification = Notification();
758     {
759         MutexLocker lock(m_queueMutex);
760         
761         if (m_queuedNotifications.isEmpty())
762             return;
763         
764         if (!m_delayCallbacks) {
765             // Only dispatch one notification callback per invocation because they can cause recursion.
766             notification = m_queuedNotifications.first();
767             m_queuedNotifications.remove(0);
768         }
769         
770         if (!m_queuedNotifications.isEmpty() && !m_mainThreadCallPending)
771             callOnMainThread(mainThreadCallback, this);
772         
773         if (!notification.isValid())
774             return;
775     }
776
777     LOG(Media, "MediaPlayerPrivateAVFoundation::dispatchNotification(%p) - dispatching %s", this, notificationName(notification));
778
779     switch (notification.type()) {
780     case Notification::ItemDidPlayToEndTime:
781         didEnd();
782         break;
783     case Notification::ItemTracksChanged:
784         tracksChanged();
785         updateStates();
786         break;
787     case Notification::ItemStatusChanged:
788         updateStates();
789         break;
790     case Notification::ItemSeekableTimeRangesChanged:
791         seekableTimeRangesChanged();
792         updateStates();
793         break;
794     case Notification::ItemLoadedTimeRangesChanged:
795         loadedTimeRangesChanged();
796         updateStates();
797         break;
798     case Notification::ItemPresentationSizeChanged:
799         sizeChanged();
800         updateStates();
801         break;
802     case Notification::ItemIsPlaybackLikelyToKeepUpChanged:
803         updateStates();
804         break;
805     case Notification::ItemIsPlaybackBufferEmptyChanged:
806         updateStates();
807         break;
808     case Notification::ItemIsPlaybackBufferFullChanged:
809         updateStates();
810         break;
811     case Notification::PlayerRateChanged:
812         updateStates();
813         rateChanged();
814         break;
815     case Notification::PlayerTimeChanged:
816         timeChanged(notification.time());
817         break;
818     case Notification::SeekCompleted:
819         seekCompleted(notification.finished());
820         break;
821     case Notification::AssetMetadataLoaded:
822         metadataLoaded();
823         updateStates();
824         break;
825     case Notification::AssetPlayabilityKnown:
826         updateStates();
827         playabilityKnown();
828         break;
829     case Notification::DurationChanged:
830         invalidateCachedDuration();
831         break;
832     case Notification::ContentsNeedsDisplay:
833         contentsNeedsDisplay();
834         break;
835     case Notification::InbandTracksNeedConfiguration:
836 #if !PLATFORM(WIN)
837         m_inbandTrackConfigurationPending = false;
838         configureInbandTracks();
839 #endif
840         break;
841
842     case Notification::None:
843         ASSERT_NOT_REACHED();
844         break;
845     }
846 }
847
848 #if !PLATFORM(WIN)
849 void MediaPlayerPrivateAVFoundation::configureInbandTracks()
850 {
851     RefPtr<InbandTextTrackPrivateAVF> trackToEnable;
852
853     // AVFoundation can only emit cues for one track at a time, so enable the first track that is showing, or the first that
854     // is hidden if none are showing. Otherwise disable all tracks.
855     for (unsigned i = 0; i < m_textTracks.size(); ++i) {
856         RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i];
857         if (track->mode() == InbandTextTrackPrivate::Showing) {
858             trackToEnable = track;
859             break;
860         }
861         if (track->mode() == InbandTextTrackPrivate::Hidden)
862             trackToEnable = track;
863     }
864
865     setCurrentTrack(trackToEnable.get());
866 }
867
868 void MediaPlayerPrivateAVFoundation::trackModeChanged()
869 {
870     if (m_inbandTrackConfigurationPending)
871         return;
872     m_inbandTrackConfigurationPending = true;
873     scheduleMainThreadNotification(Notification::InbandTracksNeedConfiguration);
874 }
875 #endif
876
877 size_t MediaPlayerPrivateAVFoundation::extraMemoryCost() const
878 {
879     double duration = this->duration();
880     if (!duration)
881         return 0;
882
883     return totalBytes() * buffered()->totalDuration() / duration;
884 }
885
886 } // namespace WebCore
887
888 #endif