[MSE][Mac] Adopt new AVStreamDataParser delegate API
[WebKit-https.git] / Source / WebCore / platform / graphics / avfoundation / MediaPlayerPrivateAVFoundation.cpp
1 /*
2  * Copyright (C) 2011, 2012 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 "InbandTextTrackPrivateAVF.h"
37 #include "InbandTextTrackPrivateClient.h"
38 #include "URL.h"
39 #include "Logging.h"
40 #include "PlatformLayer.h"
41 #include "Settings.h"
42 #include "SoftLinking.h"
43 #include "TimeRanges.h"
44 #include <CoreMedia/CoreMedia.h>
45 #include <wtf/MainThread.h>
46
47 namespace WebCore {
48
49 MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(MediaPlayer* player)
50     : m_player(player)
51     , m_weakPtrFactory(this)
52     , m_queuedNotifications()
53     , m_queueMutex()
54     , m_networkState(MediaPlayer::Empty)
55     , m_readyState(MediaPlayer::HaveNothing)
56     , m_preload(MediaPlayer::Auto)
57     , m_cachedMaxTimeLoaded(0)
58     , m_cachedMaxTimeSeekable(0)
59     , m_cachedMinTimeSeekable(0)
60     , m_cachedDuration(MediaPlayer::invalidTime())
61     , m_reportedDuration(MediaPlayer::invalidTime())
62     , m_maxTimeLoadedAtLastDidLoadingProgress(MediaPlayer::invalidTime())
63     , m_requestedRate(1)
64     , m_delayCallbacks(0)
65     , m_delayCharacteristicsChangedNotification(0)
66     , m_mainThreadCallPending(false)
67     , m_assetIsPlayable(false)
68     , m_visible(false)
69     , m_loadingMetadata(false)
70     , m_isAllowedToRender(false)
71     , m_cachedHasAudio(false)
72     , m_cachedHasVideo(false)
73     , m_cachedHasCaptions(false)
74     , m_ignoreLoadStateChanges(false)
75     , m_haveReportedFirstVideoFrame(false)
76     , m_playWhenFramesAvailable(false)
77     , m_inbandTrackConfigurationPending(false)
78     , m_characteristicsChanged(false)
79     , m_shouldMaintainAspectRatio(true)
80     , m_seeking(false)
81 {
82     LOG(Media, "MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(%p)", this);
83 }
84
85 MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation()
86 {
87     LOG(Media, "MediaPlayerPrivateAVFoundation::~MediaPlayerPrivateAVFoundation(%p)", this);
88     setIgnoreLoadStateChanges(true);
89     cancelCallOnMainThread(mainThreadCallback, this);
90 }
91
92 MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::currentRenderingMode() const
93 {
94 #if USE(ACCELERATED_COMPOSITING)
95     if (platformLayer())
96         return MediaRenderingToLayer;
97 #endif
98
99     if (hasContextRenderer())
100         return MediaRenderingToContext;
101
102     return MediaRenderingNone;
103 }
104
105 MediaPlayerPrivateAVFoundation::MediaRenderingMode MediaPlayerPrivateAVFoundation::preferredRenderingMode() const
106 {
107     if (!m_player->visible() || !m_player->frameView() || assetStatus() == MediaPlayerAVAssetStatusUnknown)
108         return MediaRenderingNone;
109
110 #if USE(ACCELERATED_COMPOSITING)
111     if (supportsAcceleratedRendering() && m_player->mediaPlayerClient()->mediaPlayerRenderingCanBeAccelerated(m_player))
112         return MediaRenderingToLayer;
113 #endif
114
115     return MediaRenderingToContext;
116 }
117
118 void MediaPlayerPrivateAVFoundation::setUpVideoRendering()
119 {
120     if (!isReadyForVideoSetup())
121         return;
122
123     MediaRenderingMode currentMode = currentRenderingMode();
124     MediaRenderingMode preferredMode = preferredRenderingMode();
125
126     if (preferredMode == MediaRenderingNone)
127         preferredMode = MediaRenderingToContext;
128
129     if (currentMode == preferredMode && currentMode != MediaRenderingNone)
130         return;
131
132     LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - current mode = %d, preferred mode = %d", 
133         this, static_cast<int>(currentMode), static_cast<int>(preferredMode));
134
135     if (currentMode != MediaRenderingNone)  
136         tearDownVideoRendering();
137
138     switch (preferredMode) {
139     case MediaRenderingNone:
140     case MediaRenderingToContext:
141         createContextVideoRenderer();
142         break;
143         
144 #if USE(ACCELERATED_COMPOSITING)
145     case MediaRenderingToLayer:
146         createVideoLayer();
147         break;
148 #endif
149     }
150
151 #if USE(ACCELERATED_COMPOSITING)
152     // If using a movie layer, inform the client so the compositing tree is updated.
153     if (currentMode == MediaRenderingToLayer || preferredMode == MediaRenderingToLayer) {
154         LOG(Media, "MediaPlayerPrivateAVFoundation::setUpVideoRendering(%p) - calling mediaPlayerRenderingModeChanged()", this);
155         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
156     }
157 #endif
158 }
159
160 void MediaPlayerPrivateAVFoundation::tearDownVideoRendering()
161 {
162     LOG(Media, "MediaPlayerPrivateAVFoundation::tearDownVideoRendering(%p)", this);
163
164     destroyContextVideoRenderer();
165
166 #if USE(ACCELERATED_COMPOSITING)
167     if (platformLayer())
168         destroyVideoLayer();
169 #endif
170 }
171
172 bool MediaPlayerPrivateAVFoundation::hasSetUpVideoRendering() const
173 {
174     return hasLayerRenderer() || hasContextRenderer();
175 }
176
177 void MediaPlayerPrivateAVFoundation::load(const String& url)
178 {
179     LOG(Media, "MediaPlayerPrivateAVFoundation::load(%p)", this);
180
181     if (m_networkState != MediaPlayer::Loading) {
182         m_networkState = MediaPlayer::Loading;
183         m_player->networkStateChanged();
184     }
185     if (m_readyState != MediaPlayer::HaveNothing) {
186         m_readyState = MediaPlayer::HaveNothing;
187         m_player->readyStateChanged();
188     }
189
190     m_assetURL = url;
191
192     // Don't do any more work if the url is empty.
193     if (!url.length())
194         return;
195
196     setPreload(m_preload);
197 }
198
199 #if ENABLE(MEDIA_SOURCE)
200 void MediaPlayerPrivateAVFoundation::load(const String&, PassRefPtr<HTMLMediaSource>)
201 {
202     m_networkState = MediaPlayer::FormatError;
203     m_player->networkStateChanged();
204 }
205 #endif
206
207
208 void MediaPlayerPrivateAVFoundation::playabilityKnown()
209 {
210     LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p)", this);
211
212     if (m_assetIsPlayable)
213         return;
214
215     // Nothing more to do if we already have all of the item's metadata.
216     if (assetStatus() > MediaPlayerAVAssetStatusLoading) {
217         LOG(Media, "MediaPlayerPrivateAVFoundation::playabilityKnown(%p) - all metadata loaded", this);
218         return;
219     }
220
221     // At this point we are supposed to load metadata. It is OK to ask the asset to load the same 
222     // information multiple times, because if it has already been loaded the completion handler 
223     // will just be called synchronously.
224     m_loadingMetadata = true;
225     beginLoadingMetadata();
226 }
227
228 void MediaPlayerPrivateAVFoundation::prepareToPlay()
229 {
230     LOG(Media, "MediaPlayerPrivateAVFoundation::prepareToPlay(%p)", this);
231
232     setPreload(MediaPlayer::Auto);
233 }
234
235 void MediaPlayerPrivateAVFoundation::play()
236 {
237     LOG(Media, "MediaPlayerPrivateAVFoundation::play(%p)", this);
238
239     // If the file has video, don't request playback until the first frame of video is ready to display
240     // or the audio may start playing before we can render video.
241     if (!m_cachedHasVideo || hasAvailableVideoFrame())
242         platformPlay();
243     else
244         m_playWhenFramesAvailable = true;
245 }
246
247 void MediaPlayerPrivateAVFoundation::pause()
248 {
249     LOG(Media, "MediaPlayerPrivateAVFoundation::pause(%p)", this);
250     m_playWhenFramesAvailable = false;
251     platformPause();
252 }
253
254 float MediaPlayerPrivateAVFoundation::duration() const
255 {
256     if (m_cachedDuration != MediaPlayer::invalidTime())
257         return m_cachedDuration;
258
259     float duration = platformDuration();
260     if (!duration || duration == MediaPlayer::invalidTime())
261         return 0;
262
263     m_cachedDuration = duration;
264     LOG(Media, "MediaPlayerPrivateAVFoundation::duration(%p) - caching %f", this, m_cachedDuration);
265     return m_cachedDuration;
266 }
267
268 void MediaPlayerPrivateAVFoundation::seek(float time)
269 {
270     seekWithTolerance(time, 0, 0);
271 }
272
273 void MediaPlayerPrivateAVFoundation::seekWithTolerance(double time, double negativeTolerance, double positiveTolerance)
274 {
275     if (m_seeking) {
276         LOG(Media, "MediaPlayerPrivateAVFoundation::seekWithTolerance(%p) - save pending seek", this);
277         m_pendingSeek = [this, time, negativeTolerance, positiveTolerance]() {
278             seekWithTolerance(time, negativeTolerance, positiveTolerance);
279         };
280         return;
281     }
282     m_seeking = true;
283
284     if (!metaDataAvailable())
285         return;
286
287     if (time > duration())
288         time = duration();
289
290     if (currentTime() == time)
291         return;
292
293     if (currentTrack())
294         currentTrack()->beginSeeking();
295     
296     LOG(Media, "MediaPlayerPrivateAVFoundation::seek(%p) - seeking to %f", this, time);
297
298     seekToTime(time, negativeTolerance, positiveTolerance);
299 }
300
301 void MediaPlayerPrivateAVFoundation::setRate(float rate)
302 {
303     LOG(Media, "MediaPlayerPrivateAVFoundation::setRate(%p) - seting to %f", this, rate);
304     m_requestedRate = rate;
305
306     updateRate();
307 }
308
309 bool MediaPlayerPrivateAVFoundation::paused() const
310 {
311     if (!metaDataAvailable())
312         return true;
313
314     return rate() == 0;
315 }
316
317 bool MediaPlayerPrivateAVFoundation::seeking() const
318 {
319     if (!metaDataAvailable())
320         return false;
321
322     return m_seeking;
323 }
324
325 IntSize MediaPlayerPrivateAVFoundation::naturalSize() const
326 {
327     if (!metaDataAvailable())
328         return IntSize();
329
330     // In spite of the name of this method, return the natural size transformed by the 
331     // initial movie scale because the spec says intrinsic size is:
332     //
333     //    ... the dimensions of the resource in CSS pixels after taking into account the resource's 
334     //    dimensions, aspect ratio, clean aperture, resolution, and so forth, as defined for the 
335     //    format used by the resource
336
337     return m_cachedNaturalSize;
338 }
339
340 void MediaPlayerPrivateAVFoundation::setNaturalSize(IntSize size)
341 {
342     LOG(Media, "MediaPlayerPrivateAVFoundation:setNaturalSize(%p) - size = %d x %d", this, size.width(), size.height());
343
344     IntSize oldSize = m_cachedNaturalSize;
345     m_cachedNaturalSize = size;
346     if (oldSize != m_cachedNaturalSize)
347         m_player->sizeChanged();
348 }
349
350 void MediaPlayerPrivateAVFoundation::setHasVideo(bool b)
351 {
352     if (m_cachedHasVideo != b) {
353         m_cachedHasVideo = b;
354         characteristicsChanged();
355     }
356 }
357
358 void MediaPlayerPrivateAVFoundation::setHasAudio(bool b)
359 {
360     if (m_cachedHasAudio != b) {
361         m_cachedHasAudio = b;
362         characteristicsChanged();
363     }
364 }
365
366 void MediaPlayerPrivateAVFoundation::setHasClosedCaptions(bool b)
367 {
368     if (m_cachedHasCaptions != b) {
369         m_cachedHasCaptions = b;
370         characteristicsChanged();
371     }
372 }
373
374 void MediaPlayerPrivateAVFoundation::characteristicsChanged()
375 {
376     if (m_delayCharacteristicsChangedNotification) {
377         m_characteristicsChanged = true;
378         return;
379     }
380
381     m_characteristicsChanged = false;
382     m_player->characteristicChanged();
383 }
384
385 void MediaPlayerPrivateAVFoundation::setDelayCharacteristicsChangedNotification(bool delay)
386 {
387     if (delay) {
388         m_delayCharacteristicsChangedNotification++;
389         return;
390     }
391     
392     ASSERT(m_delayCharacteristicsChangedNotification);
393     m_delayCharacteristicsChangedNotification--;
394     if (!m_delayCharacteristicsChangedNotification && m_characteristicsChanged)
395         characteristicsChanged();
396 }
397
398 PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundation::buffered() const
399 {
400     if (!m_cachedLoadedTimeRanges)
401         m_cachedLoadedTimeRanges = platformBufferedTimeRanges();
402
403     return m_cachedLoadedTimeRanges->copy();
404 }
405
406 double MediaPlayerPrivateAVFoundation::maxTimeSeekableDouble() const
407 {
408     if (!metaDataAvailable())
409         return 0;
410
411     if (!m_cachedMaxTimeSeekable)
412         m_cachedMaxTimeSeekable = platformMaxTimeSeekable();
413
414     LOG(Media, "MediaPlayerPrivateAVFoundation::maxTimeSeekable(%p) - returning %f", this, m_cachedMaxTimeSeekable);
415     return m_cachedMaxTimeSeekable;   
416 }
417
418 double MediaPlayerPrivateAVFoundation::minTimeSeekable() const
419 {
420     if (!metaDataAvailable())
421         return 0;
422
423     if (!m_cachedMinTimeSeekable)
424         m_cachedMinTimeSeekable = platformMinTimeSeekable();
425
426     LOG(Media, "MediaPlayerPrivateAVFoundation::minTimeSeekable(%p) - returning %f", this, m_cachedMinTimeSeekable);
427     return m_cachedMinTimeSeekable;
428 }
429
430 float MediaPlayerPrivateAVFoundation::maxTimeLoaded() const
431 {
432     if (!metaDataAvailable())
433         return 0;
434
435     if (!m_cachedMaxTimeLoaded)
436         m_cachedMaxTimeLoaded = platformMaxTimeLoaded();
437
438     return m_cachedMaxTimeLoaded;   
439 }
440
441 bool MediaPlayerPrivateAVFoundation::didLoadingProgress() const
442 {
443     if (!duration() || !totalBytes())
444         return false;
445     float currentMaxTimeLoaded = maxTimeLoaded();
446     bool didLoadingProgress = currentMaxTimeLoaded != m_maxTimeLoadedAtLastDidLoadingProgress;
447     m_maxTimeLoadedAtLastDidLoadingProgress = currentMaxTimeLoaded;
448
449     return didLoadingProgress;
450 }
451
452 bool MediaPlayerPrivateAVFoundation::isReadyForVideoSetup() const
453 {
454     // AVFoundation will not return true for firstVideoFrameAvailable until
455     // an AVPlayerLayer has been added to the AVPlayerItem, so allow video setup
456     // here if a video track to trigger allocation of a AVPlayerLayer.
457     return (m_isAllowedToRender || m_cachedHasVideo) && m_readyState >= MediaPlayer::HaveMetadata && m_player->visible();
458 }
459
460 void MediaPlayerPrivateAVFoundation::prepareForRendering()
461 {
462     if (m_isAllowedToRender)
463         return;
464     m_isAllowedToRender = true;
465
466     setUpVideoRendering();
467
468     if (currentRenderingMode() == MediaRenderingToLayer || preferredRenderingMode() == MediaRenderingToLayer)
469         m_player->mediaPlayerClient()->mediaPlayerRenderingModeChanged(m_player);
470 }
471
472 bool MediaPlayerPrivateAVFoundation::supportsFullscreen() const
473 {
474 #if ENABLE(FULLSCREEN_API)
475     return true;
476 #else
477     // FIXME: WebVideoFullscreenController assumes a QTKit/QuickTime media engine
478 #if PLATFORM(IOS)
479     if (Settings::avKitEnabled())
480         return true;
481 #endif
482     return false;
483 #endif
484 }
485
486 void MediaPlayerPrivateAVFoundation::updateStates()
487 {
488     if (m_ignoreLoadStateChanges)
489         return;
490
491     MediaPlayer::NetworkState oldNetworkState = m_networkState;
492     MediaPlayer::ReadyState oldReadyState = m_readyState;
493
494     if (m_loadingMetadata)
495         m_networkState = MediaPlayer::Loading;
496     else {
497         // -loadValuesAsynchronouslyForKeys:completionHandler: has invoked its handler; test status of keys and determine state.
498         AssetStatus assetStatus = this->assetStatus();
499         ItemStatus itemStatus = playerItemStatus();
500         
501         m_assetIsPlayable = (assetStatus == MediaPlayerAVAssetStatusPlayable);
502         if (m_readyState < MediaPlayer::HaveMetadata && assetStatus > MediaPlayerAVAssetStatusLoading) {
503             if (m_assetIsPlayable) {
504                 if (assetStatus >= MediaPlayerAVAssetStatusLoaded)
505                     m_readyState = MediaPlayer::HaveMetadata;
506                 if (itemStatus <= MediaPlayerAVPlayerItemStatusUnknown) {
507                     if (assetStatus == MediaPlayerAVAssetStatusFailed || m_preload > MediaPlayer::MetaData || isLiveStream()) {
508                         // The asset is playable but doesn't support inspection prior to playback (eg. streaming files),
509                         // or we are supposed to prepare for playback immediately, so create the player item now.
510                         m_networkState = MediaPlayer::Loading;
511                         prepareToPlay();
512                     } else
513                         m_networkState = MediaPlayer::Idle;
514                 }
515             } else {
516                 // FIX ME: fetch the error associated with the @"playable" key to distinguish between format 
517                 // and network errors.
518                 m_networkState = MediaPlayer::FormatError;
519             }
520         }
521         
522         if (assetStatus >= MediaPlayerAVAssetStatusLoaded && itemStatus > MediaPlayerAVPlayerItemStatusUnknown) {
523             switch (itemStatus) {
524             case MediaPlayerAVPlayerItemStatusDoesNotExist:
525             case MediaPlayerAVPlayerItemStatusUnknown:
526             case MediaPlayerAVPlayerItemStatusFailed:
527                 break;
528
529             case MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp:
530             case MediaPlayerAVPlayerItemStatusPlaybackBufferFull:
531                 // If the status becomes PlaybackBufferFull, loading stops and the status will not
532                 // progress to LikelyToKeepUp. Set the readyState to  HAVE_ENOUGH_DATA, on the
533                 // presumption that if the playback buffer is full, playback will probably not stall.
534                 m_readyState = MediaPlayer::HaveEnoughData;
535                 break;
536
537             case MediaPlayerAVPlayerItemStatusReadyToPlay:
538                 // If the readyState is already HaveEnoughData, don't go lower because of this state change.
539                 if (m_readyState == MediaPlayer::HaveEnoughData)
540                     break;
541
542             case MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty:
543                 if (maxTimeLoaded() > currentTime())
544                     m_readyState = MediaPlayer::HaveFutureData;
545                 else
546                     m_readyState = MediaPlayer::HaveCurrentData;
547                 break;
548             }
549
550             if (itemStatus == MediaPlayerAVPlayerItemStatusPlaybackBufferFull)
551                 m_networkState = MediaPlayer::Idle;
552             else if (itemStatus == MediaPlayerAVPlayerItemStatusFailed)
553                 m_networkState = MediaPlayer::DecodeError;
554             else if (itemStatus != MediaPlayerAVPlayerItemStatusPlaybackBufferFull && itemStatus >= MediaPlayerAVPlayerItemStatusReadyToPlay)
555                 m_networkState = (maxTimeLoaded() == duration()) ? MediaPlayer::Loaded : MediaPlayer::Loading;
556         }
557     }
558
559     if (isReadyForVideoSetup() && currentRenderingMode() != preferredRenderingMode())
560         setUpVideoRendering();
561
562     if (!m_haveReportedFirstVideoFrame && m_cachedHasVideo && hasAvailableVideoFrame()) {
563         if (m_readyState < MediaPlayer::HaveCurrentData)
564             m_readyState = MediaPlayer::HaveCurrentData;
565         m_haveReportedFirstVideoFrame = true;
566         m_player->firstVideoFrameAvailable();
567     }
568
569     if (m_networkState != oldNetworkState)
570         m_player->networkStateChanged();
571
572     if (m_readyState != oldReadyState)
573         m_player->readyStateChanged();
574
575     if (m_playWhenFramesAvailable && hasAvailableVideoFrame()) {
576         m_playWhenFramesAvailable = false;
577         platformPlay();
578     }
579
580 #if !LOG_DISABLED
581     if (m_networkState != oldNetworkState || oldReadyState != m_readyState) {
582         LOG(Media, "MediaPlayerPrivateAVFoundation::updateStates(%p) - entered with networkState = %i, readyState = %i,  exiting with networkState = %i, readyState = %i",
583             this, static_cast<int>(oldNetworkState), static_cast<int>(oldReadyState), static_cast<int>(m_networkState), static_cast<int>(m_readyState));
584     }
585 #endif
586 }
587
588 void MediaPlayerPrivateAVFoundation::setSize(const IntSize&) 
589
590 }
591
592 void MediaPlayerPrivateAVFoundation::setVisible(bool visible)
593 {
594     if (m_visible == visible)
595         return;
596
597     m_visible = visible;
598     if (visible)
599         setUpVideoRendering();
600     
601     platformSetVisible(visible);
602 }
603
604 void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
605 {
606     // Set up or change the rendering path if necessary.
607     setUpVideoRendering();
608 }
609
610 void MediaPlayerPrivateAVFoundation::setShouldMaintainAspectRatio(bool maintainAspectRatio)
611 {
612     if (maintainAspectRatio == m_shouldMaintainAspectRatio)
613         return;
614
615     m_shouldMaintainAspectRatio = maintainAspectRatio;
616     updateVideoLayerGravity();
617 }
618
619 void MediaPlayerPrivateAVFoundation::metadataLoaded()
620 {
621     m_loadingMetadata = false;
622     tracksChanged();
623 }
624
625 void MediaPlayerPrivateAVFoundation::rateChanged()
626 {
627     m_player->rateChanged();
628 }
629
630 void MediaPlayerPrivateAVFoundation::loadedTimeRangesChanged()
631 {
632     m_cachedLoadedTimeRanges = 0;
633     m_cachedMaxTimeLoaded = 0;
634     invalidateCachedDuration();
635 }
636
637 void MediaPlayerPrivateAVFoundation::seekableTimeRangesChanged()
638 {
639     m_cachedMaxTimeSeekable = 0;
640     m_cachedMinTimeSeekable = 0;
641 }
642
643 void MediaPlayerPrivateAVFoundation::timeChanged(double time)
644 {
645     LOG(Media, "MediaPlayerPrivateAVFoundation::timeChanged(%p) - time = %f", this, time);
646     UNUSED_PARAM(time);
647 }
648
649 void MediaPlayerPrivateAVFoundation::seekCompleted(bool finished)
650 {
651     LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - finished = %d", this, finished);
652     UNUSED_PARAM(finished);
653
654     m_seeking = false;
655
656     std::function<void()> pendingSeek;
657     std::swap(pendingSeek, m_pendingSeek);
658
659     if (pendingSeek) {
660         LOG(Media, "MediaPlayerPrivateAVFoundation::seekCompleted(%p) - issuing pending seek", this);
661         pendingSeek();
662         return;
663     }
664
665     if (currentTrack())
666         currentTrack()->endSeeking();
667
668     updateStates();
669     m_player->timeChanged();
670 }
671
672 void MediaPlayerPrivateAVFoundation::didEnd()
673 {
674     // Hang onto the current time and use it as duration from now on since we are definitely at
675     // the end of the movie. Do this because the initial duration is sometimes an estimate.
676     float now = currentTime();
677     if (now > 0)
678         m_cachedDuration = now;
679
680     updateStates();
681     m_player->timeChanged();
682 }
683
684 void MediaPlayerPrivateAVFoundation::invalidateCachedDuration()
685 {
686     LOG(Media, "MediaPlayerPrivateAVFoundation::invalidateCachedDuration(%p)", this);
687     
688     m_cachedDuration = MediaPlayer::invalidTime();
689
690     // For some media files, reported duration is estimated and updated as media is loaded
691     // so report duration changed when the estimate is upated.
692     float duration = this->duration();
693     if (duration != m_reportedDuration) {
694         if (m_reportedDuration != MediaPlayer::invalidTime())
695             m_player->durationChanged();
696         m_reportedDuration = duration;
697     }
698     
699 }
700
701 void MediaPlayerPrivateAVFoundation::repaint()
702 {
703     m_player->repaint();
704 }
705
706 MediaPlayer::MovieLoadType MediaPlayerPrivateAVFoundation::movieLoadType() const
707 {
708     if (!metaDataAvailable() || assetStatus() == MediaPlayerAVAssetStatusUnknown)
709         return MediaPlayer::Unknown;
710
711     if (isLiveStream())
712         return MediaPlayer::LiveStream;
713
714     return MediaPlayer::Download;
715 }
716
717 void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload)
718 {
719     m_preload = preload;
720     if (!m_assetURL.length())
721         return;
722
723     setDelayCallbacks(true);
724
725     if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) {
726         createAVAssetForURL(m_assetURL);
727         checkPlayability();
728     }
729
730     // Don't force creation of the player and player item unless we already know that the asset is playable. If we aren't
731     // there yet, or if we already know it is not playable, creating them now won't help.
732     if (m_preload == MediaPlayer::Auto && m_assetIsPlayable) {
733         createAVPlayerItem();
734         createAVPlayer();
735     }
736
737     setDelayCallbacks(false);
738 }
739
740 void MediaPlayerPrivateAVFoundation::setDelayCallbacks(bool delay) const
741 {
742     MutexLocker lock(m_queueMutex);
743     if (delay)
744         ++m_delayCallbacks;
745     else {
746         ASSERT(m_delayCallbacks);
747         --m_delayCallbacks;
748     }
749 }
750
751 void MediaPlayerPrivateAVFoundation::mainThreadCallback(void* context)
752 {
753     LOG(Media, "MediaPlayerPrivateAVFoundation::mainThreadCallback(%p)", context);
754     MediaPlayerPrivateAVFoundation* player = static_cast<MediaPlayerPrivateAVFoundation*>(context);
755     player->clearMainThreadPendingFlag();
756     player->dispatchNotification();
757 }
758
759 void MediaPlayerPrivateAVFoundation::clearMainThreadPendingFlag()
760 {
761     MutexLocker lock(m_queueMutex);
762     m_mainThreadCallPending = false;
763 }
764
765 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, double time)
766 {
767     scheduleMainThreadNotification(Notification(type, time));
768 }
769
770 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification::Type type, bool finished)
771 {
772     scheduleMainThreadNotification(Notification(type, finished));
773 }
774
775 #if !LOG_DISABLED
776 static const char* notificationName(MediaPlayerPrivateAVFoundation::Notification& notification)
777 {
778 #define DEFINE_TYPE_STRING_CASE(type) case MediaPlayerPrivateAVFoundation::Notification::type: return #type;
779     switch (notification.type()) {
780         FOR_EACH_MEDIAPLAYERPRIVATEAVFOUNDATION_NOTIFICATION_TYPE(DEFINE_TYPE_STRING_CASE)
781         default: return "";
782     }
783 #undef DEFINE_TYPE_STRING_CASE
784 }
785 #endif // !LOG_DISABLED
786     
787
788 void MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(Notification notification)
789 {
790     LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - notification %s", this, notificationName(notification));
791     m_queueMutex.lock();
792
793     // It is important to always process the properties in the order that we are notified, 
794     // so always go through the queue because notifications happen on different threads.
795     m_queuedNotifications.append(notification);
796
797     bool delayDispatch = m_delayCallbacks || !isMainThread();
798     if (delayDispatch && !m_mainThreadCallPending) {
799         m_mainThreadCallPending = true;
800         callOnMainThread(mainThreadCallback, this);
801     }
802
803     m_queueMutex.unlock();
804
805     if (delayDispatch) {
806         LOG(Media, "MediaPlayerPrivateAVFoundation::scheduleMainThreadNotification(%p) - early return", this);
807         return;
808     }
809
810     dispatchNotification();
811 }
812
813 void MediaPlayerPrivateAVFoundation::dispatchNotification()
814 {
815     ASSERT(isMainThread());
816
817     Notification notification = Notification();
818     {
819         MutexLocker lock(m_queueMutex);
820         
821         if (m_queuedNotifications.isEmpty())
822             return;
823         
824         if (!m_delayCallbacks) {
825             // Only dispatch one notification callback per invocation because they can cause recursion.
826             notification = m_queuedNotifications.first();
827             m_queuedNotifications.remove(0);
828         }
829         
830         if (!m_queuedNotifications.isEmpty() && !m_mainThreadCallPending)
831             callOnMainThread(mainThreadCallback, this);
832
833         if (!notification.isValid())
834             return;
835     }
836
837     LOG(Media, "MediaPlayerPrivateAVFoundation::dispatchNotification(%p) - dispatching %s", this, notificationName(notification));
838
839     switch (notification.type()) {
840     case Notification::ItemDidPlayToEndTime:
841         didEnd();
842         break;
843     case Notification::ItemTracksChanged:
844         tracksChanged();
845         updateStates();
846         break;
847     case Notification::ItemStatusChanged:
848         updateStates();
849         break;
850     case Notification::ItemSeekableTimeRangesChanged:
851         seekableTimeRangesChanged();
852         updateStates();
853         break;
854     case Notification::ItemLoadedTimeRangesChanged:
855         loadedTimeRangesChanged();
856         updateStates();
857         break;
858     case Notification::ItemPresentationSizeChanged:
859         sizeChanged();
860         updateStates();
861         break;
862     case Notification::ItemIsPlaybackLikelyToKeepUpChanged:
863         updateStates();
864         break;
865     case Notification::ItemIsPlaybackBufferEmptyChanged:
866         updateStates();
867         break;
868     case Notification::ItemIsPlaybackBufferFullChanged:
869         updateStates();
870         break;
871     case Notification::PlayerRateChanged:
872         updateStates();
873         rateChanged();
874         break;
875     case Notification::PlayerTimeChanged:
876         timeChanged(notification.time());
877         break;
878     case Notification::SeekCompleted:
879         seekCompleted(notification.finished());
880         break;
881     case Notification::AssetMetadataLoaded:
882         metadataLoaded();
883         updateStates();
884         break;
885     case Notification::AssetPlayabilityKnown:
886         updateStates();
887         playabilityKnown();
888         break;
889     case Notification::DurationChanged:
890         invalidateCachedDuration();
891         break;
892     case Notification::ContentsNeedsDisplay:
893         contentsNeedsDisplay();
894         break;
895     case Notification::InbandTracksNeedConfiguration:
896         m_inbandTrackConfigurationPending = false;
897         configureInbandTracks();
898         break;
899     case Notification::FunctionType:
900         notification.function()();
901         break;
902
903     case Notification::None:
904         ASSERT_NOT_REACHED();
905         break;
906     }
907 }
908
909 void MediaPlayerPrivateAVFoundation::configureInbandTracks()
910 {
911     RefPtr<InbandTextTrackPrivateAVF> trackToEnable;
912
913     // AVFoundation can only emit cues for one track at a time, so enable the first track that is showing, or the first that
914     // is hidden if none are showing. Otherwise disable all tracks.
915     for (unsigned i = 0; i < m_textTracks.size(); ++i) {
916         RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i];
917         if (track->mode() == InbandTextTrackPrivate::Showing) {
918             trackToEnable = track;
919             break;
920         }
921         if (track->mode() == InbandTextTrackPrivate::Hidden)
922             trackToEnable = track;
923     }
924
925     setCurrentTrack(trackToEnable.get());
926 }
927
928 void MediaPlayerPrivateAVFoundation::trackModeChanged()
929 {
930     if (m_inbandTrackConfigurationPending)
931         return;
932     m_inbandTrackConfigurationPending = true;
933     scheduleMainThreadNotification(Notification::InbandTracksNeedConfiguration);
934 }
935
936 size_t MediaPlayerPrivateAVFoundation::extraMemoryCost() const
937 {
938     double duration = this->duration();
939     if (!duration)
940         return 0;
941
942     unsigned long long extra = totalBytes() * buffered()->totalDuration() / duration;
943     return static_cast<unsigned>(extra);
944 }
945
946 void MediaPlayerPrivateAVFoundation::clearTextTracks()
947 {
948     for (unsigned i = 0; i < m_textTracks.size(); ++i) {
949         RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i];
950         player()->removeTextTrack(track);
951         track->disconnect();
952     }
953     m_textTracks.clear();
954 }
955
956 void MediaPlayerPrivateAVFoundation::processNewAndRemovedTextTracks(const Vector<RefPtr<InbandTextTrackPrivateAVF>>& removedTextTracks)
957 {
958     if (removedTextTracks.size()) {
959         for (unsigned i = 0; i < m_textTracks.size(); ++i) {
960             if (!removedTextTracks.contains(m_textTracks[i]))
961                 continue;
962             
963             player()->removeTextTrack(removedTextTracks[i].get());
964             m_textTracks.remove(i);
965         }
966     }
967     
968     for (unsigned i = 0; i < m_textTracks.size(); ++i) {
969         RefPtr<InbandTextTrackPrivateAVF> track = m_textTracks[i];
970         
971         track->setTextTrackIndex(i);
972         if (track->hasBeenReported())
973             continue;
974         
975         track->setHasBeenReported(true);
976         player()->addTextTrack(track.get());
977     }
978     LOG(Media, "MediaPlayerPrivateAVFoundation::processNewAndRemovedTextTracks(%p) - found %lu text tracks", this, m_textTracks.size());
979 }
980
981 } // namespace WebCore
982
983 #endif