Clear up scale factor terminology
[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     return m_isAllowedToRender && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
382 }
383
384 void MediaPlayerPrivateAVFoundation::prepareForRendering()
385 {
386     if (m_isAllowedToRender)
387         return;
388     m_isAllowedToRender = true;
389
390     setUpVideoRendering();
391
392     if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer)
393         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
394 }
395
396 bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const
397 {
398 #if ENABLE(FULLSCREEN_API)
399     return true;
400 #else
401     // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine
402     return false;
403 #endif
404 }
405
406 void MediaPlayerPrivateAVFoundation::updateStates()
407 {
408     if (m_ignoreLoadStateChanges)
409         return;
410
411     MediaPlayer::NetworkState oldNetworkState = m_networkState;
412     MediaPlayer::ReadyState oldReadyState = m_readyState;
413
414     LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entering with networkState = %i, readyState = %i", 
415         this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
416
417     if (m_loadingMetadata)
418         m_networkState = MediaPlayer::Loading;
419     else {
420         // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state.
421         AssetStatus assetStatus = this->assetStatus();
422         ItemStatus itemStatus = playerItemStatus();
423         
424         m_assetIsPlayable = (assetStatus == MediaPlayerAVAssetStatusPlayable);
425         if (m_readyState < MediaPlayer::HaveMetadata && assetStatus > MediaPlayerAVAssetStatusLoading) {
426             if (m_assetIsPlayable) {
427                 if (assetStatus >= MediaPlayerAVAssetStatusLoaded)
428                     m_readyState = MediaPlayer::HaveMetadata;
429                 if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) {
430                     if (assetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData || isLiveStream()) {
431                         // The asset is playable but doesn't support inspection prior to playback (eg. streaming files),
432                         // or we are supposed to prepare for playback immediately, so create the player item now.
433                         m_networkState = MediaPlayer::Loading;
434                         prepareToPlay();
435                     } else
436                         m_networkState = MediaPlayer::Idle;
437                 }
438             } else {
439                 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 
440                 // and network errors.
441                 m_networkState = MediaPlayer::FormatError;
442             }
443         }
444         
445         if (assetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) {
446             switch (itemStatus) {
447             case MediaPlayerAVPlayerItemStatusDoesNotExist:
448             case MediaPlayerAVPlayerItemStatusUnknown:
449             case MediaPlayerAVPlayerItemStatusFailed:
450                 break;
451
452             case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp:
453                 m_readyState = MediaPlayer::HaveEnoughData;
454                 break;
455
456             case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
457             case MediaPlayerAVPlayerItemStatusReadyToPlay:
458                 // If the readyState is already HaveEnoughData, don't go lower because of this state change.
459                 if (m_readyState == MediaPlayer::HaveEnoughData)
460                     break;
461
462             case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty:
463                 if (maxTimeLoaded() > currentTime())
464                     m_readyState = MediaPlayer::HaveFutureData;
465                 else
466                     m_readyState = MediaPlayer::HaveCurrentData;
467                 break;
468             }
469
470             if (itemStatus == MediaPlayerAVPlayerItemStatusPlaybackBufferFull)
471                 m_networkState = MediaPlayer::Idle;
472             else if (itemStatus == MediaPlayerAVPlayerItemStatusFailed)
473                 m_networkState = MediaPlayer::DecodeError;
474             else if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
475                 m_networkState = (maxTimeLoaded() == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading;
476         }
477     }
478
479     if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode())
480         setUpVideoRendering();
481
482     if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) {
483         if (m_readyState < MediaPlayer::HaveCurrentData)
484             m_readyState = MediaPlayer::HaveCurrentData;
485         m_haveReportedFirstVideoFrame = true;
486         m_player->firstVideoFrameAvailable();
487     }
488
489     if (m_networkState != oldNetworkState)
490         m_player->networkStateChanged();
491
492     if (m_readyState != oldReadyState)
493         m_player->readyStateChanged();
494
495     if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) {
496         m_playWhenFramesAvailable = false;
497         platformPlay();
498     }
499
500     LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - exiting with networkState = %i, readyState = %i", 
501         this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
502 }
503
504 void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 
505
506 }
507
508 void MediaPlayerPrivateAVFoundation::setVisible(bool visible)
509 {
510     if (m_visible == visible)
511         return;
512
513     m_visible = visible;
514     if (visible)
515         setUpVideoRendering();
516     
517     platformSetVisible(visible);
518 }
519
520 void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
521 {
522     // Set up or change the rendering path if necessary.
523     setUpVideoRendering();
524 }
525
526 void MediaPlayerPrivateAVFoundation::metadataLoaded()
527 {
528     m_loadingMetadata = false;
529     tracksChanged();
530 }
531
532 void MediaPlayerPrivateAVFoundation::rateChanged()
533 {
534     m_player->rateChanged();
535 }
536
537 void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged()
538 {
539     m_cachedLoadedTimeRanges = 0;
540     m_cachedMaxTimeLoaded = 0;
541     invalidateCachedDuration();
542 }
543
544 void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged()
545 {
546     m_cachedMaxTimeSeekable = 0;
547 }
548
549 void MediaPlayerPrivateAVFoundation::timeChanged(double time)
550 {
551     LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time);
552     UNUSED_PARAM(time);
553 }
554
555 void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished)
556 {
557     LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, finished);
558     UNUSED_PARAM(finished);
559     
560     m_seekTo = invalidTime();
561     updateStates();
562     m_player->timeChanged();
563 }
564
565 void MediaPlayerPrivateAVFoundation::didEnd()
566 {
567     // Hang onto the current time and use it as duration from now on since we are definitely at
568     // the end of the movie. Do this because the initial duration is sometimes an estimate.
569     float now = currentTime();
570     if (now > 0)
571         m_cachedDuration = now;
572
573     updateStates();
574     m_player->timeChanged();
575 }
576
577 void MediaPlayerPrivateAVFoundation::invalidateCachedDuration()
578 {
579     LOG(Media, "MediaPlayerPrivateAVFoundation::invalidateCachedDuration(%p)", this);
580     
581     m_cachedDuration = invalidTime();
582
583     // For some media files, reported duration is estimated and updated as media is loaded
584     // so report duration changed when the estimate is upated.
585     float duration = this->duration();
586     if (duration != m_reportedDuration) {
587         if (m_reportedDuration != invalidTime())
588             m_player->durationChanged();
589         m_reportedDuration = duration;
590     }
591     
592 }
593
594 void MediaPlayerPrivateAVFoundation::repaint()
595 {
596     m_player->repaint();
597 }
598
599 MediaPlayer::MovieLoadType MediaPlayerPrivateAVFoundation::movieLoadType() const
600 {
601     if (!metaDataAvailable() || assetStatus() == MediaPlayerAVAssetStatusUnknown)
602         return MediaPlayer::Unknown;
603
604     if (isLiveStream())
605         return MediaPlayer::LiveStream;
606
607     return MediaPlayer::Download;
608 }
609
610 void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload)
611 {
612     m_preload = preload;
613     if (!m_assetURL.length())
614         return;
615
616     setDelayCallbacks(true);
617
618     if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) {
619         createAVAssetForURL(m_assetURL);
620         // FIXME: Remove this Windows-specific code when <rdar://problem/9877730> is fixed, until then
621         // we can't create an AVPlayer without an AVPlayerItem on Windows, so we always have to create
622         // the item first.
623 #if PLATFORM(WIN)
624         createAVPlayerItem();
625 #endif
626         createAVPlayer();
627         checkPlayability();
628     }
629
630     // FIXME: Enable this code on Windows when <rdar://problem/9877730> is fixed.
631 #if PLATFORM(MAC)
632     // Don't force creation of the player item unless we already know that the asset is playable. If we aren't
633     // there yet, or if we already know it is not playable, creating it now won't help.
634     if (m_preload == MediaPlayer::Auto && m_assetIsPlayable)
635         createAVPlayerItem();
636 #endif
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