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