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