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