2011-05-05 Eric Carlson <eric.carlson@apple.com>
[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 "ApplicationCacheHost.h"
33 #include "ApplicationCacheResource.h"
34 #include "DocumentLoader.h"
35 #include "FrameView.h"
36 #include "GraphicsContext.h"
37 #include "GraphicsLayer.h"
38 #include "KURL.h"
39 #include "Logging.h"
40 #include "SoftLinking.h"
41 #include "TimeRanges.h"
42 #include <CoreMedia/CoreMedia.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_scaleFactor(1, 1)
57     , m_cachedMaxTimeLoaded(0)
58     , m_cachedMaxTimeSeekable(0)
59     , m_cachedDuration(invalidTime())
60     , m_reportedDuration(invalidTime())
61     , m_seekTo(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     updateStates();
194     if (m_assetIsPlayable)
195         return;
196
197     // Nothing more to do if we already have all of the item's metadata.
198     if (assetStatus() > MediaPlayerAVAssetStatusLoading) {
199         LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p) - all metadata loaded", this);
200         return;
201     }
202
203     // At this point we are supposed to load metadata. It is OK to ask the asset to load the same 
204     // information multiple times, because if it has already been loaded the completion handler 
205     // will just be called synchronously.
206     m_loadingMetadata = true;
207     beginLoadingMetadata();
208 }
209
210 void MediaPlayerPrivateAVFoundation::prepareToPlay()
211 {
212     LOG(Media, "MediaPlayerPrivateAVFoundation::prepareToPlay(%p)", this);
213
214     setPreload(MediaPlayer::Auto);
215 }
216
217 void MediaPlayerPrivateAVFoundation::play()
218 {
219     LOG(Media, "MediaPlayerPrivateAVFoundation::play(%p)", this);
220
221     // If the file has video, don't request playback until the first frame of video is ready to display
222     // or the audio may start playing before we can render video.
223     if (!m_cachedHasVideo || hasAvailableVideoFrame())
224         platformPlay();
225     else
226         m_playWhenFramesAvailable = true;
227 }
228
229 void MediaPlayerPrivateAVFoundation::pause()
230 {
231     LOG(Media, "MediaPlayerPrivateAVFoundation::pause(%p)", this);
232     m_playWhenFramesAvailable = false;
233     platformPause();
234 }
235
236 float MediaPlayerPrivateAVFoundation::duration() const
237 {
238     if (m_cachedDuration != invalidTime())
239         return m_cachedDuration;
240
241     float duration = platformDuration();
242     if (!duration || duration == invalidTime())
243         return 0;
244
245     m_cachedDuration = duration;
246     LOG(Media, "MediaPlayerPrivateAVFoundation::duration(%p) - caching %f", this, m_cachedDuration);
247     return m_cachedDuration;
248 }
249
250 void MediaPlayerPrivateAVFoundation::seek(float time)
251 {
252     if (!metaDataAvailable())
253         return;
254
255     if (time > duration())
256         time = duration();
257
258     if (currentTime() == time)
259         return;
260
261     LOG(Media, "MediaPlayerPrivateAVFoundation::seek(%p) - seeking to %f", this, time);
262     m_seekTo = time;
263
264     seekToTime(time);
265 }
266
267 void MediaPlayerPrivateAVFoundation::setRate(float rate)
268 {
269     LOG(Media, "MediaPlayerPrivateAVFoundation::setRate(%p) - seting to %f", this, rate);
270     m_requestedRate = rate;
271
272     updateRate();
273 }
274
275 bool MediaPlayerPrivateAVFoundation::paused() const
276 {
277     if (!metaDataAvailable())
278         return true;
279
280     return rate() == 0;
281 }
282
283 bool MediaPlayerPrivateAVFoundation::seeking() const
284 {
285     if (!metaDataAvailable())
286         return false;
287
288     return m_seekTo != invalidTime();
289 }
290
291 IntSize MediaPlayerPrivateAVFoundation::naturalSize() const
292 {
293     if (!metaDataAvailable())
294         return IntSize();
295
296     // In spite of the name of this method, return the natural size transformed by the 
297     // initial movie scale because the spec says intrinsic size is:
298     //
299     //    ... the dimensions of the resource in CSS pixels after taking into account the resource's 
300     //    dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the 
301     //    format used by the resource
302
303     return m_cachedNaturalSize;
304 }
305
306 void MediaPlayerPrivateAVFoundation::setNaturalSize(IntSize size)
307 {
308     LOG(Media, "MediaPlayerPrivateAVFoundation:sizeChanged(%p) - size = %d x %d", this, size.width(), size.height());
309
310     IntSize oldSize = m_cachedNaturalSize;
311     m_cachedNaturalSize = size;
312     if (oldSize != m_cachedNaturalSize)
313         m_player->sizeChanged();
314 }
315
316 PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundation::buffered() const
317 {
318     if (!m_cachedLoadedTimeRanges)
319         m_cachedLoadedTimeRanges = platformBufferedTimeRanges();
320
321     return m_cachedLoadedTimeRanges->copy();
322 }
323
324 float MediaPlayerPrivateAVFoundation::maxTimeSeekable() const
325 {
326     if (!metaDataAvailable())
327         return 0;
328
329     if (!m_cachedMaxTimeSeekable)
330         m_cachedMaxTimeSeekable = platformMaxTimeSeekable();
331
332     LOG(Media, "MediaPlayerPrivateAVFoundation::maxTimeSeekable(%p) - returning %f", this, m_cachedMaxTimeSeekable);
333     return m_cachedMaxTimeSeekable;   
334 }
335
336 float MediaPlayerPrivateAVFoundation::maxTimeLoaded() const
337 {
338     if (!metaDataAvailable())
339         return 0;
340
341     if (!m_cachedMaxTimeLoaded)
342         m_cachedMaxTimeLoaded = platformMaxTimeLoaded();
343
344     return m_cachedMaxTimeLoaded;   
345 }
346
347 unsigned MediaPlayerPrivateAVFoundation::bytesLoaded() const
348 {
349     float dur = duration();
350     if (!dur)
351         return 0;
352     unsigned loaded = totalBytes() * maxTimeLoaded() / dur;
353     LOG(Media, "MediaPlayerPrivateAVFoundation::bytesLoaded(%p) - returning %i", this, loaded);
354     return loaded;
355 }
356
357 bool MediaPlayerPrivateAVFoundation::isReadyForVideoSetup() const
358 {
359     return m_isAllowedToRender && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
360 }
361
362 void MediaPlayerPrivateAVFoundation::prepareForRendering()
363 {
364     if (m_isAllowedToRender)
365         return;
366     m_isAllowedToRender = true;
367
368     setUpVideoRendering();
369
370     if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer)
371         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
372 }
373
374 bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const
375 {
376 #if ENABLE(FULLSCREEN_API)
377     return true;
378 #else
379     // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine
380     return false;
381 #endif
382 }
383
384 void MediaPlayerPrivateAVFoundation::updateStates()
385 {
386     if (m_ignoreLoadStateChanges)
387         return;
388
389     MediaPlayer::NetworkState oldNetworkState = m_networkState;
390     MediaPlayer::ReadyState oldReadyState = m_readyState;
391
392     LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entering with networkState = %i, readyState = %i", 
393         this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
394
395     if (m_loadingMetadata)
396         m_networkState = MediaPlayer::Loading;
397     else {
398         // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state.
399         AssetStatus assetStatus = this->assetStatus();
400         ItemStatus itemStatus = playerItemStatus();
401         
402         m_assetIsPlayable = (assetStatus == MediaPlayerAVAssetStatusPlayable);
403         if (m_readyState < MediaPlayer::HaveMetadata && assetStatus > MediaPlayerAVAssetStatusLoading) {
404             if (m_assetIsPlayable) {
405                 if (assetStatus >= MediaPlayerAVAssetStatusLoaded)
406                     m_readyState = MediaPlayer::HaveMetadata;
407                 if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) {
408                     if (assetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData || isLiveStream()) {
409                         // The asset is playable but doesn't support inspection prior to playback (eg. streaming files),
410                         // or we are supposed to prepare for playback immediately, so create the player item now.
411                         m_networkState = MediaPlayer::Loading;
412                         prepareToPlay();
413                     } else
414                         m_networkState = MediaPlayer::Idle;
415                 }
416             } else {
417                 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 
418                 // and network errors.
419                 m_networkState = MediaPlayer::FormatError;
420             }
421         }
422         
423         if (assetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) {
424             switch (itemStatus) {
425             case MediaPlayerAVPlayerItemStatusDoesNotExist:
426             case MediaPlayerAVPlayerItemStatusUnknown:
427             case MediaPlayerAVPlayerItemStatusFailed:
428                 break;
429
430             case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp:
431                 m_readyState = MediaPlayer::HaveEnoughData;
432                 break;
433
434             case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
435             case MediaPlayerAVPlayerItemStatusReadyToPlay:
436                 // If the readyState is already HaveEnoughData, don't go lower because of this state change.
437                 if (m_readyState == MediaPlayer::HaveEnoughData)
438                     break;
439
440             case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty:
441                 if (maxTimeLoaded() > currentTime())
442                     m_readyState = MediaPlayer::HaveFutureData;
443                 else
444                     m_readyState = MediaPlayer::HaveCurrentData;
445                 break;
446             }
447
448             if (itemStatus == MediaPlayerAVPlayerItemStatusPlaybackBufferFull)
449                 m_networkState = MediaPlayer::Idle;
450             else if (itemStatus == MediaPlayerAVPlayerItemStatusFailed)
451                 m_networkState = MediaPlayer::DecodeError;
452             else if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
453                 m_networkState = (maxTimeLoaded() == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading;
454         }
455     }
456
457     if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode())
458         setUpVideoRendering();
459
460     if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) {
461         if (m_readyState < MediaPlayer::HaveCurrentData)
462             m_readyState = MediaPlayer::HaveCurrentData;
463         m_haveReportedFirstVideoFrame = true;
464         m_player->firstVideoFrameAvailable();
465     }
466
467     if (m_networkState != oldNetworkState)
468         m_player->networkStateChanged();
469
470     if (m_readyState != oldReadyState)
471         m_player->readyStateChanged();
472
473     if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) {
474         m_playWhenFramesAvailable = false;
475         platformPlay();
476     }
477
478     LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - exiting with networkState = %i, readyState = %i", 
479         this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
480 }
481
482 void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 
483
484 }
485
486 void MediaPlayerPrivateAVFoundation::setVisible(bool visible)
487 {
488     if (m_visible == visible)
489         return;
490
491     m_visible = visible;
492     if (visible)
493         setUpVideoRendering();
494     
495     platformSetVisible(visible);
496 }
497
498 void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
499 {
500     // Set up or change the rendering path if necessary.
501     setUpVideoRendering();
502 }
503
504 void MediaPlayerPrivateAVFoundation::metadataLoaded()
505 {
506     m_loadingMetadata = false;
507     tracksChanged();
508     updateStates();
509 }
510
511 void MediaPlayerPrivateAVFoundation::rateChanged()
512 {
513     updateStates();
514     m_player->rateChanged();
515 }
516
517 void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged()
518 {
519     m_cachedLoadedTimeRanges = 0;
520     m_cachedMaxTimeLoaded = 0;
521     updateStates();
522
523     // For some media files, reported duration is estimated and updated as media is loaded
524     // so report duration changed when the estimate is upated.
525     float dur = duration();
526     if (dur != m_reportedDuration) {
527         if (m_reportedDuration != invalidTime())
528             m_player->durationChanged();
529         m_reportedDuration = dur;
530     }
531 }
532
533 void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged()
534 {
535     m_cachedMaxTimeSeekable = 0;
536 }
537
538 void MediaPlayerPrivateAVFoundation::timeChanged(double time)
539 {
540     LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time);
541
542     if (m_seekTo == invalidTime())
543         return;
544
545     // AVFoundation may call our observer more than once during a seek, and we can't currently tell
546     // if we will be able to seek to an exact time, so assume that we are done seeking if we are
547     // "close enough" to the seek time.
548     const double smallSeekDelta = 1.0 / 100;
549
550     float currentRate = rate();
551     if ((currentRate > 0 && time >= m_seekTo) || (currentRate < 0 && time <= m_seekTo) || (abs(m_seekTo - time) <= smallSeekDelta)) {
552         m_seekTo = invalidTime();
553         updateStates();
554         m_player->timeChanged();
555     }
556 }
557
558 void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished)
559 {
560     LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, finished);
561     
562     if (finished)
563         m_seekTo = invalidTime();
564 }
565
566 void MediaPlayerPrivateAVFoundation::didEnd()
567 {
568     // Hang onto the current time and use it as duration from now on since we are definitely at
569     // the end of the movie. Do this because the initial duration is sometimes an estimate.
570     float now = currentTime();
571     if (now > 0)
572         m_cachedDuration = now;
573
574     updateStates();
575     m_player->timeChanged();
576 }
577
578 void MediaPlayerPrivateAVFoundation::repaint()
579 {
580     m_player->repaint();
581 }
582
583 MediaPlayer::MovieLoadType MediaPlayerPrivateAVFoundation::movieLoadType() const
584 {
585     if (!metaDataAvailable() || assetStatus() == MediaPlayerAVAssetStatusUnknown)
586         return MediaPlayer::Unknown;
587
588     if (isLiveStream())
589         return MediaPlayer::LiveStream;
590
591     return MediaPlayer::Download;
592 }
593
594 void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload)
595 {
596     m_preload = preload;
597     if (!m_assetURL.length())
598         return;
599
600     setDelayCallbacks(true);
601
602     if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) {
603 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
604         Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
605         ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0;
606         ApplicationCacheResource* resource;
607         if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(m_assetURL), resource) && resource) {
608             // AVFoundation can't open arbitrary data pointers, so if this ApplicationCacheResource doesn't 
609             // have a valid local path, just open the resource's original URL.
610             if (resource->path().isEmpty())
611                 createAVAssetForURL(resource->url());
612             else
613                 createAVAssetForCacheResource(resource);
614         } else
615 #endif    
616             createAVAssetForURL(m_assetURL);
617
618         createAVPlayer();
619
620         checkPlayability();
621     }
622
623     // Don't force creation of the player item unless we already know that the asset is playable. If we aren't
624     // there yet, or if we already know it is not playable, creating it now won't help.
625     if (m_preload == MediaPlayer::Auto && m_assetIsPlayable)
626         createAVPlayerItem();
627
628     setDelayCallbacks(false);
629 }
630
631 void MediaPlayerPrivateAVFoundation::setDelayCallbacks(bool delay)
632 {
633     MutexLocker lock(m_queueMutex);
634     if (delay)
635         ++m_delayCallbacks;
636     else {
637         ASSERT(m_delayCallbacks);
638         --m_delayCallbacks;
639     }
640 }
641
642 void MediaPlayerPrivateAVFoundation::mainThreadCallback(void* context)
643 {
644     LOG(Media, "MediaPlayerPrivateAVFoundation::mainThreadCallback(%p)", context);
645     MediaPlayerPrivateAVFoundation* player = static_cast<MediaPlayerPrivateAVFoundation*>(context);
646     player->clearMainThreadPendingFlag();
647     player->dispatchNotification();
648 }
649
650 void MediaPlayerPrivateAVFoundation::clearMainThreadPendingFlag()
651 {
652     MutexLocker lock(m_queueMutex);
653     m_mainThreadCallPending = false;
654 }
655
656 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, double time)
657 {
658     scheduleMainThreadNotification(Notification(type, time));
659 }
660
661 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, bool finished)
662 {
663     scheduleMainThreadNotification(Notification(type, finished));
664 }
665
666 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification notification)
667 {
668     LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - notification %d", this, static_cast<int>(notification.type()));
669     m_queueMutex.lock();
670
671     // It is important to always process the properties in the order that we are notified, 
672     // so always go through the queue because notifications happen on different threads.
673     m_queuedNotifications.append(notification);
674
675     bool delayDispatch = m_delayCallbacks || !isMainThread();
676     if (delayDispatch && !m_mainThreadCallPending) {
677         m_mainThreadCallPending = true;
678         callOnMainThread(mainThreadCallback, this);
679     }
680
681     m_queueMutex.unlock();
682
683     if (delayDispatch) {
684         LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - early return", this);
685         return;
686     }
687
688     dispatchNotification();
689 }
690
691 void MediaPlayerPrivateAVFoundation::dispatchNotification()
692 {
693     ASSERT(isMainThread());
694
695     Notification notification = Notification();
696     {
697         MutexLocker lock(m_queueMutex);
698         
699         if (m_queuedNotifications.isEmpty())
700             return;
701         
702         if (!m_delayCallbacks) {
703             // Only dispatch one notification callback per invocation because they can cause recursion.
704             notification = m_queuedNotifications.first();
705             m_queuedNotifications.remove(0);
706         }
707         
708         if (!m_queuedNotifications.isEmpty() && !m_mainThreadCallPending)
709             callOnMainThread(mainThreadCallback, this);
710         
711         if (!notification.isValid())
712             return;
713     }
714
715     LOG(Media, "MediaPlayerPrivateAVFoundation::dispatchNotification(%p) - dispatching %d", this, static_cast<int>(notification.type()));
716
717     switch (notification.type()) {
718     case Notification::ItemDidPlayToEndTime:
719         didEnd();
720         break;
721     case Notification::ItemTracksChanged:
722         tracksChanged();
723         updateStates();
724         break;
725     case Notification::ItemStatusChanged:
726         updateStates();
727         break;
728     case Notification::ItemSeekableTimeRangesChanged:
729         seekableTimeRangesChanged();
730         break;
731     case Notification::ItemLoadedTimeRangesChanged:
732         loadedTimeRangesChanged();
733         break;
734     case Notification::ItemPresentationSizeChanged:
735         sizeChanged();
736         updateStates();
737         break;
738     case Notification::ItemIsPlaybackLikelyToKeepUpChanged:
739         updateStates();
740         break;
741     case Notification::ItemIsPlaybackBufferEmptyChanged:
742         updateStates();
743         break;
744     case Notification::ItemIsPlaybackBufferFullChanged:
745         updateStates();
746         break;
747     case Notification::PlayerRateChanged:
748         rateChanged();
749         break;
750     case Notification::PlayerTimeChanged:
751         timeChanged(notification.time());
752         break;
753     case Notification::SeekCompleted:
754         seekCompleted(notification.finished());
755         break;
756     case Notification::AssetMetadataLoaded:
757         metadataLoaded();
758         break;
759     case Notification::AssetPlayabilityKnown:
760         playabilityKnown();
761         break;
762     case Notification::None:
763         ASSERT_NOT_REACHED();
764         break;
765     }
766 }
767
768 } // namespace WebCore
769
770 #endif