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