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