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