2011-06-07 Jer Noble <jer.noble@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 "Frame.h"
36 #include "FrameView.h"
37 #include "GraphicsContext.h"
38 #include "GraphicsLayer.h"
39 #include "KURL.h"
40 #include "Logging.h"
41 #include "SoftLinking.h"
42 #include "TimeRanges.h"
43 #include <CoreMedia/CoreMedia.h>
44 #include <wtf/UnusedParam.h>
45
46 using namespace std;
47
48 namespace WebCore {
49
50 MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(MediaPlayer* player)
51     : m_player(player)
52     , m_queuedNotifications()
53     , m_queueMutex()
54     , m_networkState(MediaPlayer::Empty)
55     , m_readyState(MediaPlayer::HaveNothing)
56     , m_preload(MediaPlayer::Auto)
57     , m_scaleFactor(1, 1)
58     , m_cachedMaxTimeLoaded(0)
59     , m_cachedMaxTimeSeekable(0)
60     , m_cachedDuration(invalidTime())
61     , m_reportedDuration(invalidTime())
62     , m_seekTo(invalidTime())
63     , m_requestedRate(1)
64     , m_delayCallbacks(0)
65     , m_mainThreadCallPending(false)
66     , m_assetIsPlayable(false)
67     , m_visible(false)
68     , m_loadingMetadata(false)
69     , m_isAllowedToRender(false)
70     , m_cachedHasAudio(false)
71     , m_cachedHasVideo(false)
72     , m_cachedHasCaptions(false)
73     , m_ignoreLoadStateChanges(false)
74     , m_haveReportedFirstVideoFrame(false)
75     , m_playWhenFramesAvailable(false)
76 {
77     LOG(Media, "MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(%p)", this);
78 }
79
80 MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation()
81 {
82     LOG(Media, "MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation(%p)", this);
83     setIgnoreLoadStateChanges(true);
84     cancelCallOnMainThread(mainThreadCallback, this);
85 }
86
87 MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::currentRenderingMode() const
88 {
89 #if USE(ACCELERATED_COMPOSITING)
90     if (platformLayer())
91         return MediaRenderingToLayer;
92 #endif
93
94     if (hasContextRenderer())
95         return MediaRenderingToContext;
96
97     return MediaRenderingNone;
98 }
99
100 MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::preferredRenderingMode() const
101 {
102     if (!m_player->visible() || !m_player->frameView() || assetStatus() == MediaPlayerAVAssetStatusUnknown)
103         return MediaRenderingNone;
104
105 #if USE(ACCELERATED_COMPOSITING)
106     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
107         return MediaRenderingToLayer;
108 #endif
109
110     return MediaRenderingToContext;
111 }
112
113 void MediaPlayerPrivateAVFoundation::setUpVideoRendering()
114 {
115     if (!isReadyForVideoSetup())
116         return;
117
118     MediaRenderingMode currentMode = currentRenderingMode();
119     MediaRenderingMode preferredMode = preferredRenderingMode();
120     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
121         return;
122
123     LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - current mode = %d, preferred mode = %d", 
124         this, static_cast<int>(currentMode), static_cast<int>(preferredMode));
125
126     if (currentMode != MediaRenderingNone)  
127         tearDownVideoRendering();
128
129     switch (preferredMode) {
130     case MediaRenderingNone:
131     case MediaRenderingToContext:
132         createContextVideoRenderer();
133         break;
134         
135 #if USE(ACCELERATED_COMPOSITING)
136     case MediaRenderingToLayer:
137         createVideoLayer();
138         break;
139 #endif
140     }
141
142 #if USE(ACCELERATED_COMPOSITING)
143     // If using a movie layer, inform the client so the compositing tree is updated.
144     if (currentMode == MediaRenderingToLayer || preferredMode == MediaRenderingToLayer) {
145         LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - calling mediaPlayerRenderingModeChanged()", this);
146         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
147     }
148 #endif
149 }
150
151 void MediaPlayerPrivateAVFoundation::tearDownVideoRendering()
152 {
153     LOG(Media, "MediaPlayerPrivateAVFoundation::tearDownVideoRendering(%p)", this);
154
155     destroyContextVideoRenderer();
156
157 #if USE(ACCELERATED_COMPOSITING)
158     if (platformLayer())
159         destroyVideoLayer();
160 #endif
161 }
162
163 bool MediaPlayerPrivateAVFoundation::hasSetUpVideoRendering() const
164 {
165     return hasLayerRenderer() || hasContextRenderer();
166 }
167
168 void MediaPlayerPrivateAVFoundation::load(const String& url)
169 {
170     LOG(Media, "MediaPlayerPrivateAVFoundation::load(%p)", this);
171
172     if (m_networkState != MediaPlayer::Loading) {
173         m_networkState = MediaPlayer::Loading;
174         m_player->networkStateChanged();
175     }
176     if (m_readyState != MediaPlayer::HaveNothing) {
177         m_readyState = MediaPlayer::HaveNothing;
178         m_player->readyStateChanged();
179     }
180
181     m_assetURL = url;
182
183     // Don't do any more work if the url is empty.
184     if (!url.length())
185         return;
186
187     setPreload(m_preload);
188 }
189
190 void MediaPlayerPrivateAVFoundation::playabilityKnown()
191 {
192     LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p)", this);
193
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:setNaturalSize(%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 void MediaPlayerPrivateAVFoundation::setHasVideo(bool b)
317 {
318     if (m_cachedHasVideo != b) {
319         m_cachedHasVideo = b;
320         m_player->characteristicChanged();
321     }
322 }
323
324 void MediaPlayerPrivateAVFoundation::setHasAudio(bool b)
325 {
326     if (m_cachedHasAudio != b) {
327         m_cachedHasAudio = b;
328         m_player->characteristicChanged();
329     }
330 }
331
332 void MediaPlayerPrivateAVFoundation::setHasClosedCaptions(bool b)
333 {
334     if (m_cachedHasCaptions != b) {
335         m_cachedHasCaptions = b;
336         m_player->characteristicChanged();
337     }
338 }
339
340 PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundation::buffered() const
341 {
342     if (!m_cachedLoadedTimeRanges)
343         m_cachedLoadedTimeRanges = platformBufferedTimeRanges();
344
345     return m_cachedLoadedTimeRanges->copy();
346 }
347
348 float MediaPlayerPrivateAVFoundation::maxTimeSeekable() const
349 {
350     if (!metaDataAvailable())
351         return 0;
352
353     if (!m_cachedMaxTimeSeekable)
354         m_cachedMaxTimeSeekable = platformMaxTimeSeekable();
355
356     LOG(Media, "MediaPlayerPrivateAVFoundation::maxTimeSeekable(%p) - returning %f", this, m_cachedMaxTimeSeekable);
357     return m_cachedMaxTimeSeekable;   
358 }
359
360 float MediaPlayerPrivateAVFoundation::maxTimeLoaded() const
361 {
362     if (!metaDataAvailable())
363         return 0;
364
365     if (!m_cachedMaxTimeLoaded)
366         m_cachedMaxTimeLoaded = platformMaxTimeLoaded();
367
368     return m_cachedMaxTimeLoaded;   
369 }
370
371 unsigned MediaPlayerPrivateAVFoundation::bytesLoaded() const
372 {
373     float dur = duration();
374     if (!dur)
375         return 0;
376     unsigned loaded = totalBytes() * maxTimeLoaded() / dur;
377     LOG(Media, "MediaPlayerPrivateAVFoundation::bytesLoaded(%p) - returning %i", this, loaded);
378     return loaded;
379 }
380
381 bool MediaPlayerPrivateAVFoundation::isReadyForVideoSetup() const
382 {
383     return m_isAllowedToRender && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
384 }
385
386 void MediaPlayerPrivateAVFoundation::prepareForRendering()
387 {
388     if (m_isAllowedToRender)
389         return;
390     m_isAllowedToRender = true;
391
392     setUpVideoRendering();
393
394     if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer)
395         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
396 }
397
398 bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const
399 {
400 #if ENABLE(FULLSCREEN_API)
401     return true;
402 #else
403     // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine
404     return false;
405 #endif
406 }
407
408 void MediaPlayerPrivateAVFoundation::updateStates()
409 {
410     if (m_ignoreLoadStateChanges)
411         return;
412
413     MediaPlayer::NetworkState oldNetworkState = m_networkState;
414     MediaPlayer::ReadyState oldReadyState = m_readyState;
415
416     LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entering with networkState = %i, readyState = %i", 
417         this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
418
419     if (m_loadingMetadata)
420         m_networkState = MediaPlayer::Loading;
421     else {
422         // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state.
423         AssetStatus assetStatus = this->assetStatus();
424         ItemStatus itemStatus = playerItemStatus();
425         
426         m_assetIsPlayable = (assetStatus == MediaPlayerAVAssetStatusPlayable);
427         if (m_readyState < MediaPlayer::HaveMetadata && assetStatus > MediaPlayerAVAssetStatusLoading) {
428             if (m_assetIsPlayable) {
429                 if (assetStatus >= MediaPlayerAVAssetStatusLoaded)
430                     m_readyState = MediaPlayer::HaveMetadata;
431                 if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) {
432                     if (assetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData || isLiveStream()) {
433                         // The asset is playable but doesn't support inspection prior to playback (eg. streaming files),
434                         // or we are supposed to prepare for playback immediately, so create the player item now.
435                         m_networkState = MediaPlayer::Loading;
436                         prepareToPlay();
437                     } else
438                         m_networkState = MediaPlayer::Idle;
439                 }
440             } else {
441                 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 
442                 // and network errors.
443                 m_networkState = MediaPlayer::FormatError;
444             }
445         }
446         
447         if (assetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) {
448             switch (itemStatus) {
449             case MediaPlayerAVPlayerItemStatusDoesNotExist:
450             case MediaPlayerAVPlayerItemStatusUnknown:
451             case MediaPlayerAVPlayerItemStatusFailed:
452                 break;
453
454             case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp:
455                 m_readyState = MediaPlayer::HaveEnoughData;
456                 break;
457
458             case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
459             case MediaPlayerAVPlayerItemStatusReadyToPlay:
460                 // If the readyState is already HaveEnoughData, don't go lower because of this state change.
461                 if (m_readyState == MediaPlayer::HaveEnoughData)
462                     break;
463
464             case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty:
465                 if (maxTimeLoaded() > currentTime())
466                     m_readyState = MediaPlayer::HaveFutureData;
467                 else
468                     m_readyState = MediaPlayer::HaveCurrentData;
469                 break;
470             }
471
472             if (itemStatus == MediaPlayerAVPlayerItemStatusPlaybackBufferFull)
473                 m_networkState = MediaPlayer::Idle;
474             else if (itemStatus == MediaPlayerAVPlayerItemStatusFailed)
475                 m_networkState = MediaPlayer::DecodeError;
476             else if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
477                 m_networkState = (maxTimeLoaded() == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading;
478         }
479     }
480
481     if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode())
482         setUpVideoRendering();
483
484     if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) {
485         if (m_readyState < MediaPlayer::HaveCurrentData)
486             m_readyState = MediaPlayer::HaveCurrentData;
487         m_haveReportedFirstVideoFrame = true;
488         m_player->firstVideoFrameAvailable();
489     }
490
491     if (m_networkState != oldNetworkState)
492         m_player->networkStateChanged();
493
494     if (m_readyState != oldReadyState)
495         m_player->readyStateChanged();
496
497     if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) {
498         m_playWhenFramesAvailable = false;
499         platformPlay();
500     }
501
502     LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - exiting with networkState = %i, readyState = %i", 
503         this, static_cast<int>(m_networkState), static_cast<int>(m_readyState));
504 }
505
506 void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 
507
508 }
509
510 void MediaPlayerPrivateAVFoundation::setVisible(bool visible)
511 {
512     if (m_visible == visible)
513         return;
514
515     m_visible = visible;
516     if (visible)
517         setUpVideoRendering();
518     
519     platformSetVisible(visible);
520 }
521
522 void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
523 {
524     // Set up or change the rendering path if necessary.
525     setUpVideoRendering();
526 }
527
528 void MediaPlayerPrivateAVFoundation::metadataLoaded()
529 {
530     m_loadingMetadata = false;
531     tracksChanged();
532 }
533
534 void MediaPlayerPrivateAVFoundation::rateChanged()
535 {
536     m_player->rateChanged();
537 }
538
539 void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged()
540 {
541     m_cachedLoadedTimeRanges = 0;
542     m_cachedMaxTimeLoaded = 0;
543     invalidateCachedDuration();
544 }
545
546 void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged()
547 {
548     m_cachedMaxTimeSeekable = 0;
549 }
550
551 void MediaPlayerPrivateAVFoundation::timeChanged(double time)
552 {
553     LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time);
554 }
555
556 void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished)
557 {
558     LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, 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 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
620         Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : 0;
621         ApplicationCacheHost* cacheHost = frame ? frame->loader()->documentLoader()->applicationCacheHost() : 0;
622         ApplicationCacheResource* resource;
623         if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(m_assetURL), resource) && resource) {
624             // AVFoundation can't open arbitrary data pointers, so if this ApplicationCacheResource doesn't 
625             // have a valid local path, just open the resource's original URL.
626             if (resource->path().isEmpty())
627                 createAVAssetForURL(resource->url());
628             else
629                 createAVAssetForCacheResource(resource);
630         } else
631 #endif    
632             createAVAssetForURL(m_assetURL);
633
634         createAVPlayer();
635
636         checkPlayability();
637     }
638
639     // Don't force creation of the player item unless we already know that the asset is playable. If we aren't
640     // there yet, or if we already know it is not playable, creating it now won't help.
641     if (m_preload == MediaPlayer::Auto && m_assetIsPlayable)
642         createAVPlayerItem();
643
644     setDelayCallbacks(false);
645 }
646
647 void MediaPlayerPrivateAVFoundation::setDelayCallbacks(bool delay)
648 {
649     MutexLocker lock(m_queueMutex);
650     if (delay)
651         ++m_delayCallbacks;
652     else {
653         ASSERT(m_delayCallbacks);
654         --m_delayCallbacks;
655     }
656 }
657
658 void MediaPlayerPrivateAVFoundation::mainThreadCallback(void* context)
659 {
660     LOG(Media, "MediaPlayerPrivateAVFoundation::mainThreadCallback(%p)", context);
661     MediaPlayerPrivateAVFoundation* player = static_cast<MediaPlayerPrivateAVFoundation*>(context);
662     player->clearMainThreadPendingFlag();
663     player->dispatchNotification();
664 }
665
666 void MediaPlayerPrivateAVFoundation::clearMainThreadPendingFlag()
667 {
668     MutexLocker lock(m_queueMutex);
669     m_mainThreadCallPending = false;
670 }
671
672 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, double time)
673 {
674     scheduleMainThreadNotification(Notification(type, time));
675 }
676
677 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, bool finished)
678 {
679     scheduleMainThreadNotification(Notification(type, finished));
680 }
681
682 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification notification)
683 {
684     LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - notification %d", this, static_cast<int>(notification.type()));
685     m_queueMutex.lock();
686
687     // It is important to always process the properties in the order that we are notified, 
688     // so always go through the queue because notifications happen on different threads.
689     m_queuedNotifications.append(notification);
690
691     bool delayDispatch = m_delayCallbacks || !isMainThread();
692     if (delayDispatch && !m_mainThreadCallPending) {
693         m_mainThreadCallPending = true;
694         callOnMainThread(mainThreadCallback, this);
695     }
696
697     m_queueMutex.unlock();
698
699     if (delayDispatch) {
700         LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - early return", this);
701         return;
702     }
703
704     dispatchNotification();
705 }
706
707 void MediaPlayerPrivateAVFoundation::dispatchNotification()
708 {
709     ASSERT(isMainThread());
710
711     Notification notification = Notification();
712     {
713         MutexLocker lock(m_queueMutex);
714         
715         if (m_queuedNotifications.isEmpty())
716             return;
717         
718         if (!m_delayCallbacks) {
719             // Only dispatch one notification callback per invocation because they can cause recursion.
720             notification = m_queuedNotifications.first();
721             m_queuedNotifications.remove(0);
722         }
723         
724         if (!m_queuedNotifications.isEmpty() && !m_mainThreadCallPending)
725             callOnMainThread(mainThreadCallback, this);
726         
727         if (!notification.isValid())
728             return;
729     }
730
731     LOG(Media, "MediaPlayerPrivateAVFoundation::dispatchNotification(%p) - dispatching %d", this, static_cast<int>(notification.type()));
732
733     switch (notification.type()) {
734     case Notification::ItemDidPlayToEndTime:
735         didEnd();
736         break;
737     case Notification::ItemTracksChanged:
738         tracksChanged();
739         updateStates();
740         break;
741     case Notification::ItemStatusChanged:
742         updateStates();
743         break;
744     case Notification::ItemSeekableTimeRangesChanged:
745         seekableTimeRangesChanged();
746         updateStates();
747         break;
748     case Notification::ItemLoadedTimeRangesChanged:
749         loadedTimeRangesChanged();
750         updateStates();
751         break;
752     case Notification::ItemPresentationSizeChanged:
753         sizeChanged();
754         updateStates();
755         break;
756     case Notification::ItemIsPlaybackLikelyToKeepUpChanged:
757         updateStates();
758         break;
759     case Notification::ItemIsPlaybackBufferEmptyChanged:
760         updateStates();
761         break;
762     case Notification::ItemIsPlaybackBufferFullChanged:
763         updateStates();
764         break;
765     case Notification::PlayerRateChanged:
766         updateStates();
767         rateChanged();
768         break;
769     case Notification::PlayerTimeChanged:
770         timeChanged(notification.time());
771         break;
772     case Notification::SeekCompleted:
773         seekCompleted(notification.finished());
774         break;
775     case Notification::AssetMetadataLoaded:
776         metadataLoaded();
777         updateStates();
778         break;
779     case Notification::AssetPlayabilityKnown:
780         updateStates();
781         playabilityKnown();
782         break;
783     case Notification::DurationChanged:
784         invalidateCachedDuration();
785         break;
786
787     case Notification::None:
788         ASSERT_NOT_REACHED();
789         break;
790     }
791 }
792
793 } // namespace WebCore
794
795 #endif