2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #if PLATFORM(WIN)&& ENABLE(VIDEO)
32 #include "MediaPlayerPrivateAVFoundationCF.h"
34 #include "ApplicationCacheResource.h"
35 #include "FloatConversion.h"
36 #include "FrameView.h"
37 #include "GraphicsContext.h"
40 #include "PlatformCALayer.h"
41 #include "SoftLinking.h"
42 #include "TimeRanges.h"
44 #include <AVFoundationCF/AVCFPlayerLayer.h>
45 #include <AVFoundationCF/AVFoundationCF.h>
46 #include <CoreMedia/CoreMedia.h>
49 #include <dispatch/dispatch.h>
50 #include <wtf/HashMap.h>
51 #include <wtf/Threading.h>
52 #include <wtf/UnusedParam.h>
54 // The softlink header files must be included after the AVCF and CoreMedia header files.
55 #include "AVFoundationCFSoftLinking.h"
56 #include "CoreMediaSoftLinking.h"
58 // We don't bother softlinking against libdispatch since it's already been loaded by AAS.
59 #pragma comment(lib, "libdispatch.lib")
69 AVFWrapper(MediaPlayerPrivateAVFoundationCF*);
72 void scheduleDisconnectAndDelete();
74 void createAVCFVideoLayer();
75 void destroyVideoLayer();
76 PlatformLayer* platformLayer();
78 CACFLayerRef caVideoLayer() { return m_caVideoLayer.get(); }
79 PlatformLayer* videoLayerWrapper() { return m_videoLayerWrapper ? m_videoLayerWrapper->platformLayer() : 0; };
80 void setVideoLayerNeedsCommit();
81 void setVideoLayerHidden(bool);
83 void createImageGenerator();
84 void destroyImageGenerator();
85 RetainPtr<CGImageRef> createImageForTimeInRect(float, const IntRect&);
87 void createAssetForURL(const String& url);
88 void setAsset(AVCFURLAssetRef);
91 void createPlayerItem();
93 void checkPlayability();
94 void beginLoadingMetadata();
96 void seekToTime(float);
98 static void loadMetadataCompletionCallback(AVCFAssetRef, void*);
99 static void loadPlayableCompletionCallback(AVCFAssetRef, void*);
100 static void periodicTimeObserverCallback(AVCFPlayerRef, CMTime, void*);
101 static void seekCompletedCallback(AVCFPlayerItemRef, Boolean, void*);
102 static void notificationCallback(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef);
104 inline AVCFPlayerLayerRef videoLayer() const { return (AVCFPlayerLayerRef)m_avCFVideoLayer.get(); }
105 inline AVCFPlayerRef avPlayer() const { return (AVCFPlayerRef)m_avPlayer.get(); }
106 inline AVCFURLAssetRef avAsset() const { return (AVCFURLAssetRef)m_avAsset.get(); }
107 inline AVCFPlayerItemRef avPlayerItem() const { return (AVCFPlayerItemRef)m_avPlayerItem.get(); }
108 inline AVCFPlayerObserverRef timeObserver() const { return (AVCFPlayerObserverRef)m_timeObserver.get(); }
109 inline AVCFAssetImageGeneratorRef imageGenerator() const { return m_imageGenerator.get(); }
110 inline dispatch_queue_t dispatchQueue() const { return m_notificationQueue; }
113 inline void* callbackContext() const { return reinterpret_cast<void*>(m_objectID); }
115 static Mutex& mapLock();
116 static HashMap<uintptr_t, AVFWrapper*>& map();
117 static AVFWrapper* avfWrapperForCallbackContext(void*);
119 void removeFromMap() const;
121 static void disconnectAndDeleteAVFWrapper(void*);
123 static uintptr_t s_nextAVFWrapperObjectID;
124 uintptr_t m_objectID;
126 MediaPlayerPrivateAVFoundationCF* m_owner;
128 RetainPtr<AVCFPlayerRef> m_avPlayer;
129 RetainPtr<AVCFURLAssetRef> m_avAsset;
130 RetainPtr<AVCFPlayerItemRef> m_avPlayerItem;
131 RetainPtr<AVCFPlayerLayerRef> m_avCFVideoLayer;
132 RetainPtr<AVCFPlayerObserverRef> m_timeObserver;
133 RetainPtr<AVCFAssetImageGeneratorRef> m_imageGenerator;
134 dispatch_queue_t m_notificationQueue;
136 mutable RetainPtr<CACFLayerRef> m_caVideoLayer;
137 RefPtr<PlatformCALayer> m_videoLayerWrapper;
139 OwnPtr<LayerClient> m_layerClient;
142 uintptr_t AVFWrapper::s_nextAVFWrapperObjectID;
144 class LayerClient : public PlatformCALayerClient {
146 LayerClient(AVFWrapper* parent) : m_parent(parent) { }
147 virtual ~LayerClient() { m_parent = 0; }
150 virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*);
151 virtual bool platformCALayerRespondsToLayoutChanges() const { return true; }
153 virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { }
154 virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerContentsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp; }
155 virtual void platformCALayerPaintContents(GraphicsContext&, const IntRect& inClip) { }
156 virtual bool platformCALayerShowDebugBorders() const { return false; }
157 virtual bool platformCALayerShowRepaintCounter() const { return false; }
158 virtual int platformCALayerIncrementRepaintCount() { return 0; }
160 virtual bool platformCALayerContentsOpaque() const { return false; }
161 virtual bool platformCALayerDrawsContent() const { return false; }
162 virtual void platformCALayerLayerDidDisplay(PlatformLayer*) { }
164 AVFWrapper* m_parent;
168 static const char* boolString(bool val)
170 return val ? "true" : "false";
174 static CFArrayRef createMetadataKeyNames()
176 static const CFStringRef keyNames[] = {
177 AVCFAssetPropertyDuration,
178 AVCFAssetPropertyNaturalSize,
179 AVCFAssetPropertyPreferredTransform,
180 AVCFAssetPropertyPreferredRate,
181 AVCFAssetPropertyPlayable,
182 AVCFAssetPropertyTracks
185 return CFArrayCreate(0, (const void**)keyNames, sizeof(keyNames) / sizeof(keyNames[0]), &kCFTypeArrayCallBacks);
188 static CFArrayRef metadataKeyNames()
190 static CFArrayRef keys = createMetadataKeyNames();
194 // FIXME: It would be better if AVCFTimedMetadataGroup.h exported this key.
195 static CFStringRef CMTimeRangeStartKey()
197 DEFINE_STATIC_LOCAL(CFStringRef, key, (CFSTR("start")));
201 // FIXME: It would be better if AVCFTimedMetadataGroup.h exported this key.
202 static CFStringRef CMTimeRangeDurationKey()
204 DEFINE_STATIC_LOCAL(CFStringRef, key, (CFSTR("duration")));
208 // FIXME: It would be better if AVCF exported this notification name.
209 static CFStringRef CACFContextNeedsFlushNotification()
211 DEFINE_STATIC_LOCAL(CFStringRef, name, (CFSTR("kCACFContextNeedsFlushNotification")));
215 // Define AVCF object accessors as inline functions here instead of in MediaPlayerPrivateAVFoundationCF so we don't have
216 // to include the AVCF headers in MediaPlayerPrivateAVFoundationCF.h
217 inline AVCFPlayerLayerRef videoLayer(AVFWrapper* wrapper)
219 return wrapper ? wrapper->videoLayer() : 0;
222 inline AVCFPlayerRef avPlayer(AVFWrapper* wrapper)
224 return wrapper ? wrapper->avPlayer() : 0;
227 inline AVCFURLAssetRef avAsset(AVFWrapper* wrapper)
229 return wrapper ? wrapper->avAsset() : 0;
232 inline AVCFPlayerItemRef avPlayerItem(AVFWrapper* wrapper)
234 return wrapper ? wrapper->avPlayerItem() : 0;
237 inline AVCFAssetImageGeneratorRef imageGenerator(AVFWrapper* wrapper)
239 return wrapper ? wrapper->imageGenerator() : 0;
242 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateAVFoundationCF::create(MediaPlayer* player)
244 return adoptPtr(new MediaPlayerPrivateAVFoundationCF(player));
247 void MediaPlayerPrivateAVFoundationCF::registerMediaEngine(MediaEngineRegistrar registrar)
250 registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
253 MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF(MediaPlayer* player)
254 : MediaPlayerPrivateAVFoundation(player)
256 , m_videoFrameHasDrawn(false)
258 LOG(Media, "MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF(%p)", this);
261 MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF()
263 LOG(Media, "MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF(%p)", this);
267 void MediaPlayerPrivateAVFoundationCF::cancelLoad()
269 LOG(Media, "MediaPlayerPrivateAVFoundationCF::cancelLoad(%p)", this);
271 // Do nothing when our cancellation of pending loading calls its completion handler
272 setDelayCallbacks(true);
273 setIgnoreLoadStateChanges(true);
275 tearDownVideoRendering();
278 // The AVCF objects have to be destroyed on the same dispatch queue used for notifications, so schedule a call to
279 // disconnectAndDeleteAVFWrapper on that queue.
280 m_avfWrapper->scheduleDisconnectAndDelete();
284 setIgnoreLoadStateChanges(false);
285 setDelayCallbacks(false);
288 bool MediaPlayerPrivateAVFoundationCF::hasLayerRenderer() const
290 return videoLayer(m_avfWrapper);
293 bool MediaPlayerPrivateAVFoundationCF::hasContextRenderer() const
295 return imageGenerator(m_avfWrapper);
298 void MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer()
300 LOG(Media, "MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer(%p)", this);
302 if (imageGenerator(m_avfWrapper))
306 m_avfWrapper->createImageGenerator();
309 void MediaPlayerPrivateAVFoundationCF::destroyContextVideoRenderer()
312 m_avfWrapper->destroyImageGenerator();
315 void MediaPlayerPrivateAVFoundationCF::createVideoLayer()
317 ASSERT(supportsAcceleratedRendering());
320 m_avfWrapper->createAVCFVideoLayer();
323 void MediaPlayerPrivateAVFoundationCF::destroyVideoLayer()
325 LOG(Media, "MediaPlayerPrivateAVFoundationCF::destroyVideoLayer(%p) - destroying %p", this, videoLayer(m_avfWrapper));
327 m_avfWrapper->destroyVideoLayer();
330 bool MediaPlayerPrivateAVFoundationCF::hasAvailableVideoFrame() const
332 return (m_videoFrameHasDrawn || (videoLayer(m_avfWrapper) && AVCFPlayerLayerIsReadyForDisplay(videoLayer(m_avfWrapper))));
335 void MediaPlayerPrivateAVFoundationCF::createAVPlayer()
337 ASSERT(m_avfWrapper);
339 setDelayCallbacks(true);
340 m_avfWrapper->createPlayer();
341 setDelayCallbacks(false);
344 void MediaPlayerPrivateAVFoundationCF::createAVPlayerItem()
346 ASSERT(m_avfWrapper);
348 setDelayCallbacks(true);
349 m_avfWrapper->createPlayerItem();
350 setDelayCallbacks(false);
353 void MediaPlayerPrivateAVFoundationCF::createAVAssetForURL(const String& url)
355 ASSERT(!m_avfWrapper);
357 setDelayCallbacks(true);
358 m_avfWrapper = new AVFWrapper(this);
359 m_avfWrapper->createAssetForURL(url);
360 setDelayCallbacks(false);
363 void MediaPlayerPrivateAVFoundationCF::checkPlayability()
365 ASSERT(m_avfWrapper);
366 m_avfWrapper->checkPlayability();
369 void MediaPlayerPrivateAVFoundationCF::beginLoadingMetadata()
371 ASSERT(m_avfWrapper);
372 m_avfWrapper->beginLoadingMetadata();
375 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationCF::playerItemStatus() const
377 if (!avPlayerItem(m_avfWrapper))
378 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
380 AVCFPlayerItemStatus status = AVCFPlayerItemGetStatus(avPlayerItem(m_avfWrapper), 0);
381 if (status == AVCFPlayerItemStatusUnknown)
382 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
383 if (status == AVCFPlayerItemStatusFailed)
384 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
385 if (AVCFPlayerItemIsPlaybackLikelyToKeepUp(avPlayerItem(m_avfWrapper)))
386 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
387 if (AVCFPlayerItemIsPlaybackBufferFull(avPlayerItem(m_avfWrapper)))
388 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
389 if (AVCFPlayerItemIsPlaybackBufferEmpty(avPlayerItem(m_avfWrapper)))
390 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
391 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
394 PlatformMedia MediaPlayerPrivateAVFoundationCF::platformMedia() const
396 LOG(Media, "MediaPlayerPrivateAVFoundationCF::platformMedia(%p)", this);
398 pm.type = PlatformMedia::AVFoundationCFMediaPlayerType;
399 pm.media.avcfMediaPlayer = (AVCFPlayer*)avPlayer(m_avfWrapper);
403 PlatformLayer* MediaPlayerPrivateAVFoundationCF::platformLayer() const
408 return m_avfWrapper->platformLayer();
411 void MediaPlayerPrivateAVFoundationCF::platformSetVisible(bool isVisible)
416 // FIXME: We use a CATransaction here on the Mac, we need to figure out why this was done there and
417 // whether we're affected by the same issue.
418 setDelayCallbacks(true);
419 m_avfWrapper->setVideoLayerHidden(!isVisible);
420 setDelayCallbacks(false);
423 void MediaPlayerPrivateAVFoundationCF::platformPlay()
425 LOG(Media, "MediaPlayerPrivateAVFoundationCF::play(%p)", this);
426 if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
429 setDelayCallbacks(true);
430 AVCFPlayerSetRate(avPlayer(m_avfWrapper), requestedRate());
431 setDelayCallbacks(false);
434 void MediaPlayerPrivateAVFoundationCF::platformPause()
436 LOG(Media, "MediaPlayerPrivateAVFoundationCF::pause(%p)", this);
437 if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
440 setDelayCallbacks(true);
441 AVCFPlayerSetRate(avPlayer(m_avfWrapper), 0);
442 setDelayCallbacks(false);
445 void MediaPlayerPrivateAVFoundationCF::updateRate()
447 LOG(Media, "MediaPlayerPrivateAVFoundationCF::updateRate(%p)", this);
448 if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
451 setDelayCallbacks(true);
452 AVCFPlayerSetRate(avPlayer(m_avfWrapper), requestedRate());
453 setDelayCallbacks(false);
456 float MediaPlayerPrivateAVFoundationCF::platformDuration() const
458 if (!metaDataAvailable() || !avAsset(m_avfWrapper))
462 CMTime cmDuration = AVCFAssetGetDuration(avAsset(m_avfWrapper));
463 if (CMTIME_IS_NUMERIC(cmDuration))
464 duration = narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
465 else if (CMTIME_IS_INDEFINITE(cmDuration))
466 duration = numeric_limits<float>::infinity();
468 LOG(Media, "MediaPlayerPrivateAVFMac::duration(%p) - invalid duration, returning 0", this);
475 float MediaPlayerPrivateAVFoundationCF::currentTime() const
477 if (!metaDataAvailable() || !avPlayerItem(m_avfWrapper))
480 CMTime itemTime = AVCFPlayerItemGetCurrentTime(avPlayerItem(m_avfWrapper));
481 if (CMTIME_IS_NUMERIC(itemTime))
482 return narrowPrecisionToFloat(CMTimeGetSeconds(itemTime));
487 void MediaPlayerPrivateAVFoundationCF::seekToTime(float time)
492 // seekToTime generates several event callbacks, update afterwards.
493 setDelayCallbacks(true);
494 m_avfWrapper->seekToTime(time);
495 setDelayCallbacks(false);
498 void MediaPlayerPrivateAVFoundationCF::setVolume(float volume)
500 if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
503 AVCFPlayerSetVolume(avPlayer(m_avfWrapper), volume);
506 void MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible(bool closedCaptionsVisible)
508 if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
511 LOG(Media, "MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible(%p) - setting to %s", this, boolString(closedCaptionsVisible));
512 AVCFPlayerSetClosedCaptionDisplayEnabled(avPlayer(m_avfWrapper), closedCaptionsVisible);
515 float MediaPlayerPrivateAVFoundationCF::rate() const
517 if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
520 setDelayCallbacks(true);
521 float currentRate = AVCFPlayerGetRate(avPlayer(m_avfWrapper));
522 setDelayCallbacks(false);
527 static bool timeRangeIsValidAndNotEmpty(CMTime start, CMTime duration)
529 // Is the range valid?
530 if (!CMTIME_IS_VALID(start) || !CMTIME_IS_VALID(duration) || duration.epoch || duration.value < 0)
533 if (CMTIME_COMPARE_INLINE(duration, ==, kCMTimeZero))
539 PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundationCF::platformBufferedTimeRanges() const
541 RefPtr<TimeRanges> timeRanges = TimeRanges::create();
543 if (!avPlayerItem(m_avfWrapper))
544 return timeRanges.release();
546 RetainPtr<CFArrayRef> loadedRanges(AdoptCF, AVCFPlayerItemCopyLoadedTimeRanges(avPlayerItem(m_avfWrapper)));
548 return timeRanges.release();
550 CFIndex rangeCount = CFArrayGetCount(loadedRanges.get());
551 for (CFIndex i = 0; i < rangeCount; i++) {
552 CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loadedRanges.get(), i));
553 CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
554 CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
556 if (timeRangeIsValidAndNotEmpty(start, duration)) {
557 float rangeStart = narrowPrecisionToFloat(CMTimeGetSeconds(start));
558 float rangeEnd = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
559 timeRanges->add(rangeStart, rangeEnd);
563 return timeRanges.release();
566 float MediaPlayerPrivateAVFoundationCF::platformMaxTimeSeekable() const
568 if (!avPlayerItem(m_avfWrapper))
571 RetainPtr<CFArrayRef> seekableRanges(AdoptCF, AVCFPlayerItemCopySeekableTimeRanges(avPlayerItem(m_avfWrapper)));
575 float maxTimeSeekable = 0;
576 CFIndex rangeCount = CFArrayGetCount(seekableRanges.get());
577 for (CFIndex i = 0; i < rangeCount; i++) {
578 CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(seekableRanges.get(), i));
579 CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
580 CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
581 if (!timeRangeIsValidAndNotEmpty(start, duration))
584 float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
585 if (maxTimeSeekable < endOfRange)
586 maxTimeSeekable = endOfRange;
589 return maxTimeSeekable;
592 float MediaPlayerPrivateAVFoundationCF::platformMaxTimeLoaded() const
594 if (!avPlayerItem(m_avfWrapper))
597 RetainPtr<CFArrayRef> loadedRanges(AdoptCF, AVCFPlayerItemCopyLoadedTimeRanges(avPlayerItem(m_avfWrapper)));
601 float maxTimeLoaded = 0;
602 CFIndex rangeCount = CFArrayGetCount(loadedRanges.get());
603 for (CFIndex i = 0; i < rangeCount; i++) {
604 CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loadedRanges.get(), i));
605 CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
606 CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
607 if (!timeRangeIsValidAndNotEmpty(start, duration))
610 float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
611 if (maxTimeLoaded < endOfRange)
612 maxTimeLoaded = endOfRange;
615 return maxTimeLoaded;
618 unsigned MediaPlayerPrivateAVFoundationCF::totalBytes() const
620 if (!metaDataAvailable() || !avAsset(m_avfWrapper))
623 int64_t totalMediaSize = 0;
624 RetainPtr<CFArrayRef> tracks(AdoptCF, AVCFAssetCopyAssetTracks(avAsset(m_avfWrapper)));
625 CFIndex trackCount = CFArrayGetCount(tracks.get());
626 for (CFIndex i = 0; i < trackCount; i++) {
627 AVCFAssetTrackRef assetTrack = (AVCFAssetTrackRef)CFArrayGetValueAtIndex(tracks.get(), i);
628 totalMediaSize += AVCFAssetTrackGetTotalSampleDataLength(assetTrack);
631 // FIXME: It doesn't seem safe to cast an int64_t to unsigned.
632 return static_cast<unsigned>(totalMediaSize);
635 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationCF::assetStatus() const
637 if (!avAsset(m_avfWrapper))
638 return MediaPlayerAVAssetStatusDoesNotExist;
640 // First, make sure all metadata properties we rely on are loaded.
641 CFArrayRef keys = metadataKeyNames();
642 CFIndex keyCount = CFArrayGetCount(keys);
643 for (CFIndex i = 0; i < keyCount; i++) {
644 CFStringRef keyName = static_cast<CFStringRef>(CFArrayGetValueAtIndex(keys, i));
645 AVCFPropertyValueStatus keyStatus = AVCFAssetGetStatusOfValueForProperty(avAsset(m_avfWrapper), keyName, 0);
647 if (keyStatus < AVCFPropertyValueStatusLoaded)
648 return MediaPlayerAVAssetStatusLoading;
649 if (keyStatus == AVCFPropertyValueStatusFailed)
650 return MediaPlayerAVAssetStatusFailed;
651 if (keyStatus == AVCFPropertyValueStatusCancelled)
652 return MediaPlayerAVAssetStatusCancelled;
655 if (AVCFAssetIsPlayable(avAsset(m_avfWrapper)))
656 return MediaPlayerAVAssetStatusPlayable;
658 return MediaPlayerAVAssetStatusLoaded;
661 void MediaPlayerPrivateAVFoundationCF::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
663 if (context->paintingDisabled())
666 if (currentRenderingMode() == MediaRenderingToLayer && !imageGenerator(m_avfWrapper)) {
667 // We're being told to render into a context, but we already have the
668 // video layer, which probably means we've been called from <canvas>.
669 createContextVideoRenderer();
672 paint(context, rect);
675 void MediaPlayerPrivateAVFoundationCF::paint(GraphicsContext* context, const IntRect& rect)
677 if (context->paintingDisabled() || !imageGenerator(m_avfWrapper))
680 LOG(Media, "MediaPlayerPrivateAVFoundationCF::paint(%p)", this);
682 setDelayCallbacks(true);
683 RetainPtr<CGImageRef> image = m_avfWrapper->createImageForTimeInRect(currentTime(), rect);
686 context->translate(rect.x(), rect.y() + rect.height());
687 context->scale(FloatSize(1.0f, -1.0f));
688 context->setImageInterpolationQuality(InterpolationLow);
689 IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
690 CGContextDrawImage(context->platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
694 setDelayCallbacks(false);
696 m_videoFrameHasDrawn = true;
699 static HashSet<String> mimeTypeCache()
701 DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
702 static bool typeListInitialized = false;
704 if (typeListInitialized)
706 typeListInitialized = true;
708 RetainPtr<CFArrayRef> supportedTypes(AdoptCF, AVCFURLAssetCopyAudiovisualMIMETypes());
710 ASSERT(supportedTypes);
714 CFIndex typeCount = CFArrayGetCount(supportedTypes.get());
715 for (CFIndex i = 0; i < typeCount; i++)
716 cache.add(static_cast<CFStringRef>(CFArrayGetValueAtIndex(supportedTypes.get(), i)));
721 void MediaPlayerPrivateAVFoundationCF::getSupportedTypes(HashSet<String>& supportedTypes)
723 supportedTypes = mimeTypeCache();
726 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationCF::supportsType(const String& type, const String& codecs)
728 // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask if it supports an
729 // extended MIME type until rdar://8721715 is fixed.
730 if (mimeTypeCache().contains(type))
731 return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
733 return MediaPlayer::IsNotSupported;
737 bool MediaPlayerPrivateAVFoundationCF::isAvailable()
739 return AVFoundationCFLibrary() && CoreMediaLibrary();
742 float MediaPlayerPrivateAVFoundationCF::mediaTimeForTimeValue(float timeValue) const
744 if (!metaDataAvailable())
747 // FIXME - can not implement until rdar://8721669 is fixed.
751 void MediaPlayerPrivateAVFoundationCF::tracksChanged()
753 if (!avAsset(m_avfWrapper))
756 // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
757 // asked about those fairly frequently.
758 if (!avPlayerItem(m_avfWrapper)) {
759 // We don't have a player item yet, so check with the asset because some assets support inspection
760 // prior to becoming ready to play.
761 RetainPtr<CFArrayRef> visualTracks(AdoptCF, AVCFAssetCopyTracksWithMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicVisual));
762 setHasVideo(CFArrayGetCount(visualTracks.get()));
764 RetainPtr<CFArrayRef> audioTracks(AdoptCF, AVCFAssetCopyTracksWithMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicAudible));
765 setHasAudio(CFArrayGetCount(audioTracks.get()));
767 RetainPtr<CFArrayRef> captionTracks(AdoptCF, AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaTypeClosedCaption));
768 setHasAudio(CFArrayGetCount(captionTracks.get()));
770 bool hasVideo = false;
771 bool hasAudio = false;
772 bool hasCaptions = false;
774 RetainPtr<CFArrayRef> tracks(AdoptCF, AVCFPlayerItemCopyTracks(avPlayerItem(m_avfWrapper)));
776 CFIndex trackCount = CFArrayGetCount(tracks.get());
777 for (CFIndex i = 0; i < trackCount; i++) {
778 AVCFPlayerItemTrackRef track = (AVCFPlayerItemTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
780 if (AVCFPlayerItemTrackIsEnabled(track)) {
781 RetainPtr<AVCFAssetTrackRef> assetTrack(AdoptCF, AVCFPlayerItemTrackCopyAssetTrack(track));
782 CFStringRef mediaType = AVCFAssetTrackGetMediaType(assetTrack.get());
786 if (CFStringCompare(mediaType, AVCFMediaTypeVideo, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
788 else if (CFStringCompare(mediaType, AVCFMediaTypeAudio, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
790 else if (CFStringCompare(mediaType, AVCFMediaTypeClosedCaption, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
795 setHasVideo(hasVideo);
796 setHasAudio(hasAudio);
797 setHasClosedCaptions(hasCaptions);
800 LOG(Media, "MediaPlayerPrivateAVFoundationCF:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s",
801 this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions()));
806 void MediaPlayerPrivateAVFoundationCF::sizeChanged()
808 if (!avAsset(m_avfWrapper))
811 // AVAsset's 'naturalSize' property only considers the movie's first video track, so we need to compute
812 // the union of all visual track rects.
813 CGRect trackRectUnion = CGRectZero;
814 RetainPtr<CFArrayRef> tracks(AdoptCF, AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaCharacteristicVisual));
815 CFIndex trackCount = CFArrayGetCount(tracks.get());
816 for (CFIndex i = 0; i < trackCount; i++) {
817 AVCFAssetTrackRef assetTrack = (AVCFAssetTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
819 CGSize trackSize = AVCFAssetTrackGetNaturalSize(assetTrack);
820 CGRect trackRect = CGRectMake(0, 0, trackSize.width, trackSize.height);
821 trackRectUnion = CGRectUnion(trackRectUnion, CGRectApplyAffineTransform(trackRect, AVCFAssetTrackGetPreferredTransform(assetTrack)));
823 // The movie is always displayed at 0,0 so move the track rect to the origin before using width and height.
824 trackRectUnion = CGRectOffset(trackRectUnion, trackRectUnion.origin.x, trackRectUnion.origin.y);
825 CGSize naturalSize = trackRectUnion.size;
827 // Also look at the asset's preferred transform so we account for a movie matrix.
828 CGSize movieSize = CGSizeApplyAffineTransform(AVCFAssetGetNaturalSize(avAsset(m_avfWrapper)), AVCFAssetGetPreferredTransform(avAsset(m_avfWrapper)));
829 if (movieSize.width > naturalSize.width)
830 naturalSize.width = movieSize.width;
831 if (movieSize.height > naturalSize.height)
832 naturalSize.height = movieSize.height;
833 setNaturalSize(IntSize(naturalSize));
836 void MediaPlayerPrivateAVFoundationCF::contentsNeedsDisplay()
839 m_avfWrapper->setVideoLayerNeedsCommit();
842 AVFWrapper::AVFWrapper(MediaPlayerPrivateAVFoundationCF* owner)
844 , m_objectID(s_nextAVFWrapperObjectID++)
846 LOG(Media, "AVFWrapper::AVFWrapper(%p)", this);
848 m_notificationQueue = dispatch_queue_create("MediaPlayerPrivateAVFoundationCF.notificationQueue", 0);
852 AVFWrapper::~AVFWrapper()
854 LOG(Media, "AVFWrapper::~AVFWrapper(%p %d)", this, m_objectID);
857 destroyImageGenerator();
859 if (m_notificationQueue)
860 dispatch_release(m_notificationQueue);
863 AVCFAssetCancelLoading(avAsset());
872 Mutex& AVFWrapper::mapLock()
874 static Mutex mapLock;
878 HashMap<uintptr_t, AVFWrapper*>& AVFWrapper::map()
880 static HashMap<uintptr_t, AVFWrapper*>& map = *new HashMap<uintptr_t, AVFWrapper*>;
884 void AVFWrapper::addToMap()
886 MutexLocker locker(mapLock());
888 // HashMap doesn't like a key of 0, and also make sure we aren't
889 // using an object ID that's already in use.
890 while (!m_objectID || (map().find(m_objectID) != map().end()))
891 m_objectID = s_nextAVFWrapperObjectID++;
893 LOG(Media, "AVFWrapper::addToMap(%p %d)", this, m_objectID);
895 map().add(m_objectID, this);
898 void AVFWrapper::removeFromMap() const
900 LOG(Media, "AVFWrapper::removeFromMap(%p %d)", this, m_objectID);
902 MutexLocker locker(mapLock());
903 map().remove(m_objectID);
906 AVFWrapper* AVFWrapper::avfWrapperForCallbackContext(void* context)
908 // Assumes caller has locked mapLock().
909 HashMap<uintptr_t, AVFWrapper*>::iterator it = map().find(reinterpret_cast<uintptr_t>(context));
910 if (it == map().end())
916 void AVFWrapper::scheduleDisconnectAndDelete()
918 // Ignore any subsequent notifications we might receive in notificationCallback().
921 dispatch_async_f(dispatchQueue(), this, disconnectAndDeleteAVFWrapper);
924 void AVFWrapper::disconnectAndDeleteAVFWrapper(void* context)
926 AVFWrapper* avfWrapper = static_cast<AVFWrapper*>(context);
928 LOG(Media, "AVFWrapper::disconnectAndDeleteAVFWrapper(%p)", avfWrapper);
930 if (avfWrapper->avPlayerItem()) {
931 CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
932 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemDidPlayToEndTimeNotification, avfWrapper->avPlayerItem());
933 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemStatusChangedNotification, avfWrapper->avPlayerItem());
934 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemTracksChangedNotification, avfWrapper->avPlayerItem());
935 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemSeekableTimeRangesChangedNotification, avfWrapper->avPlayerItem());
936 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemLoadedTimeRangesChangedNotification, avfWrapper->avPlayerItem());
937 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification, avfWrapper->avPlayerItem());
938 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification, avfWrapper->avPlayerItem());
939 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemIsPlaybackBufferFullChangedNotification, avfWrapper->avPlayerItem());
940 CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), CACFContextNeedsFlushNotification(), 0);
943 if (avfWrapper->avPlayer()) {
944 if (avfWrapper->timeObserver())
945 AVCFPlayerRemoveObserver(avfWrapper->avPlayer(), avfWrapper->timeObserver());
947 CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), avfWrapper->callbackContext(), AVCFPlayerRateChangedNotification, avfWrapper->avPlayer());
953 void AVFWrapper::createAssetForURL(const String& url)
957 RetainPtr<CFURLRef> urlRef(AdoptCF, KURL(ParsedURLString, url).createCFURL());
958 AVCFURLAssetRef assetRef = AVCFURLAssetCreateWithURLAndOptions(kCFAllocatorDefault, urlRef.get(), 0, m_notificationQueue);
959 m_avAsset.adoptCF(assetRef);
962 void AVFWrapper::createPlayer()
964 ASSERT(!avPlayer() && avPlayerItem());
966 // FIXME: We need a way to create a AVPlayer without an AVPlayerItem, see <rdar://problem/9877730>.
967 AVCFPlayerRef playerRef = AVCFPlayerCreateWithPlayerItemAndOptions(kCFAllocatorDefault, avPlayerItem(), 0, m_notificationQueue);
968 m_avPlayer.adoptCF(playerRef);
970 CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
973 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerRateChangedNotification, playerRef, CFNotificationSuspensionBehaviorDeliverImmediately);
975 // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but
976 // our observer will also be called whenever a seek happens.
977 const double veryLongInterval = 60*60*60*24*30;
978 m_timeObserver.adoptCF(AVCFPlayerCreatePeriodicTimeObserverForInterval(playerRef, CMTimeMake(veryLongInterval, 10), m_notificationQueue, &periodicTimeObserverCallback, callbackContext()));
981 void AVFWrapper::createPlayerItem()
983 ASSERT(!avPlayerItem() && avAsset());
985 // Create the player item so we begin loading media data.
986 AVCFPlayerItemRef itemRef = AVCFPlayerItemCreateWithAsset(kCFAllocatorDefault, avAsset(), m_notificationQueue);
987 m_avPlayerItem.adoptCF(itemRef);
989 CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
992 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemDidPlayToEndTimeNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
993 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemStatusChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
994 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemTracksChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
995 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemSeekableTimeRangesChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
996 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemLoadedTimeRangesChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
997 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
998 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
999 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemIsPlaybackBufferFullChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1001 CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, CACFContextNeedsFlushNotification(), 0, CFNotificationSuspensionBehaviorDeliverImmediately);
1004 void AVFWrapper::periodicTimeObserverCallback(AVCFPlayerRef, CMTime cmTime, void* context)
1006 MutexLocker locker(mapLock());
1007 AVFWrapper* self = avfWrapperForCallbackContext(context);
1009 LOG(Media, "AVFWrapper::periodicTimeObserverCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1013 double time = std::max(0.0, CMTimeGetSeconds(cmTime)); // Clamp to zero, negative values are sometimes reported.
1014 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::PlayerTimeChanged, time);
1017 void AVFWrapper::notificationCallback(CFNotificationCenterRef, void* observer, CFStringRef propertyName, const void* object, CFDictionaryRef)
1019 MutexLocker locker(mapLock());
1020 AVFWrapper* self = avfWrapperForCallbackContext(observer);
1023 LOG(Media, "AVFWrapper::notificationCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(observer));
1028 char notificationName[256];
1029 CFStringGetCString(propertyName, notificationName, sizeof(notificationName), kCFStringEncodingASCII);
1030 LOG(Media, "AVFWrapper::notificationCallback(%p) %s", self, notificationName);
1033 if (CFEqual(propertyName, AVCFPlayerItemDidPlayToEndTimeNotification))
1034 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
1035 else if (CFEqual(propertyName, AVCFPlayerItemTracksChangedNotification))
1036 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemTracksChanged);
1037 else if (CFEqual(propertyName, AVCFPlayerItemStatusChangedNotification)) {
1038 AVCFURLAssetRef asset = AVCFPlayerItemGetAsset(self->avPlayerItem());
1040 self->setAsset(asset);
1041 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemStatusChanged);
1042 } else if (CFEqual(propertyName, AVCFPlayerItemSeekableTimeRangesChangedNotification))
1043 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemSeekableTimeRangesChanged);
1044 else if (CFEqual(propertyName, AVCFPlayerItemLoadedTimeRangesChangedNotification))
1045 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemLoadedTimeRangesChanged);
1046 else if (CFEqual(propertyName, AVCFPlayerItemPresentationSizeChangedNotification))
1047 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemPresentationSizeChanged);
1048 else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification))
1049 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackLikelyToKeepUpChanged);
1050 else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification))
1051 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackBufferEmptyChanged);
1052 else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackBufferFullChangedNotification))
1053 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackBufferFullChanged);
1054 else if (CFEqual(propertyName, AVCFPlayerRateChangedNotification))
1055 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::PlayerRateChanged);
1056 else if (CFEqual(propertyName, CACFContextNeedsFlushNotification()))
1057 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ContentsNeedsDisplay);
1059 ASSERT_NOT_REACHED();
1062 void AVFWrapper::loadPlayableCompletionCallback(AVCFAssetRef, void* context)
1064 MutexLocker locker(mapLock());
1065 AVFWrapper* self = avfWrapperForCallbackContext(context);
1067 LOG(Media, "AVFWrapper::loadPlayableCompletionCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1071 LOG(Media, "AVFWrapper::loadPlayableCompletionCallback(%p)", self);
1072 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
1075 void AVFWrapper::checkPlayability()
1077 LOG(Media, "AVFWrapper::checkPlayability(%p)", this);
1079 static CFArrayRef propertyKeyName;
1080 if (!propertyKeyName) {
1081 static const CFStringRef keyNames[] = {
1082 AVCFAssetPropertyPlayable
1084 propertyKeyName = CFArrayCreate(0, (const void**)keyNames, sizeof(keyNames) / sizeof(keyNames[0]), &kCFTypeArrayCallBacks);
1087 AVCFAssetLoadValuesAsynchronouslyForProperties(avAsset(), propertyKeyName, loadPlayableCompletionCallback, callbackContext());
1090 void AVFWrapper::loadMetadataCompletionCallback(AVCFAssetRef, void* context)
1092 MutexLocker locker(mapLock());
1093 AVFWrapper* self = avfWrapperForCallbackContext(context);
1095 LOG(Media, "AVFWrapper::loadMetadataCompletionCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1099 LOG(Media, "AVFWrapper::loadMetadataCompletionCallback(%p)", self);
1100 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
1103 void AVFWrapper::beginLoadingMetadata()
1106 LOG(Media, "AVFWrapper::beginLoadingMetadata(%p) - requesting metadata loading", this);
1107 AVCFAssetLoadValuesAsynchronouslyForProperties(avAsset(), metadataKeyNames(), loadMetadataCompletionCallback, callbackContext());
1110 void AVFWrapper::seekCompletedCallback(AVCFPlayerItemRef, Boolean finished, void* context)
1112 MutexLocker locker(mapLock());
1113 AVFWrapper* self = avfWrapperForCallbackContext(context);
1115 LOG(Media, "AVFWrapper::seekCompletedCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1119 LOG(Media, "AVFWrapper::seekCompletedCallback(%p)", self);
1120 self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::SeekCompleted, static_cast<bool>(finished));
1123 void AVFWrapper::seekToTime(float time)
1125 ASSERT(avPlayerItem());
1126 AVCFPlayerItemSeekToTimeWithToleranceAndCompletionCallback(avPlayerItem(), CMTimeMakeWithSeconds(time, 600),
1127 kCMTimeZero, kCMTimeZero, &seekCompletedCallback, callbackContext());
1130 void AVFWrapper::setAsset(AVCFURLAssetRef asset)
1132 if (asset == avAsset())
1135 AVCFAssetCancelLoading(avAsset());
1136 m_avAsset.adoptCF(asset);
1139 PlatformLayer* AVFWrapper::platformLayer()
1141 if (m_videoLayerWrapper)
1142 return m_videoLayerWrapper->platformLayer();
1147 // Create a PlatformCALayer so we can resize the video layer to match the element size.
1148 m_layerClient = adoptPtr(new LayerClient(this));
1152 m_videoLayerWrapper = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, m_layerClient.get());
1153 if (!m_videoLayerWrapper)
1156 CACFLayerRef layerRef = AVCFPlayerLayerCopyCACFLayer(m_avCFVideoLayer.get());
1157 m_caVideoLayer.adoptCF(layerRef);
1159 CACFLayerInsertSublayer(m_videoLayerWrapper->platformLayer(), m_caVideoLayer.get(), 0);
1160 m_videoLayerWrapper->setAnchorPoint(FloatPoint3D());
1161 m_videoLayerWrapper->setNeedsLayout();
1163 return m_videoLayerWrapper->platformLayer();
1166 void AVFWrapper::createAVCFVideoLayer()
1168 if (!avPlayer() || m_avCFVideoLayer)
1171 // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
1172 m_avCFVideoLayer.adoptCF(AVCFPlayerLayerCreateWithAVCFPlayer(kCFAllocatorDefault, avPlayer(), m_notificationQueue));
1173 LOG(Media, "AVFWrapper::createAVCFVideoLayer(%p) - returning %p", this, videoLayer());
1176 void AVFWrapper::destroyVideoLayer()
1178 LOG(Media, "AVFWrapper::destroyVideoLayer(%p)", this);
1179 m_layerClient = nullptr;
1181 m_videoLayerWrapper = 0;
1182 if (!m_avCFVideoLayer.get())
1185 AVCFPlayerLayerSetPlayer((AVCFPlayerLayerRef)m_avCFVideoLayer.get(), 0);
1186 m_avCFVideoLayer = 0;
1189 void AVFWrapper::setVideoLayerNeedsCommit()
1191 if (m_videoLayerWrapper)
1192 m_videoLayerWrapper->setNeedsCommit();
1195 void AVFWrapper::setVideoLayerHidden(bool value)
1197 if (m_videoLayerWrapper)
1198 m_videoLayerWrapper->setHidden(value);
1201 void AVFWrapper::createImageGenerator()
1203 if (!avAsset() || m_imageGenerator)
1206 m_imageGenerator.adoptCF(AVCFAssetImageGeneratorCreateWithAsset(kCFAllocatorDefault, avAsset()));
1208 AVCFAssetImageGeneratorSetApertureMode(m_imageGenerator.get(), AVCFAssetImageGeneratorApertureModeCleanAperture);
1209 AVCFAssetImageGeneratorSetRequestedTimeToleranceBefore(m_imageGenerator.get(), kCMTimeZero);
1210 AVCFAssetImageGeneratorSetRequestedTimeToleranceAfter(m_imageGenerator.get(), kCMTimeZero);
1211 AVCFAssetImageGeneratorSetAppliesPreferredTrackTransform(m_imageGenerator.get(), true);
1213 LOG(Media, "AVFWrapper::createImageGenerator(%p) - returning %p", this, m_imageGenerator.get());
1216 void AVFWrapper::destroyImageGenerator()
1218 LOG(Media, "AVFWrapper::destroyImageGenerator(%p)", this);
1219 m_imageGenerator = 0;
1222 RetainPtr<CGImageRef> AVFWrapper::createImageForTimeInRect(float time, const IntRect& rect)
1224 if (!m_imageGenerator)
1228 double start = WTF::currentTime();
1231 AVCFAssetImageGeneratorSetMaximumSize(m_imageGenerator.get(), CGSize(rect.size()));
1232 CGImageRef image = AVCFAssetImageGeneratorCopyCGImageAtTime(m_imageGenerator.get(), CMTimeMakeWithSeconds(time, 600), 0, 0);
1235 double duration = WTF::currentTime() - start;
1236 LOG(Media, "AVFWrapper::createImageForTimeInRect(%p) - creating image took %.4f", this, narrowPrecisionToFloat(duration));
1242 void LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* wrapperLayer)
1245 ASSERT(m_parent->videoLayerWrapper() == wrapperLayer->platformLayer());
1247 CGRect bounds = wrapperLayer->bounds();
1248 CGPoint anchor = CACFLayerGetAnchorPoint(m_parent->caVideoLayer());
1249 FloatPoint position(bounds.size.width * anchor.x, bounds.size.height * anchor.y);
1251 CACFLayerSetPosition(m_parent->caVideoLayer(), position);
1252 CACFLayerSetBounds(m_parent->caVideoLayer(), bounds);
1255 } // namespace WebCore
1258 // AVFoundation should always be enabled for Apple production builds.
1259 #if __PRODUCTION__ && !USE(AVFOUNDATION)
1260 #error AVFoundation is not enabled!
1261 #endif // __PRODUCTION__ && !USE(AVFOUNDATION)
1262 #endif // USE(AVFOUNDATION)
1263 #endif // PLATFORM(WIN) && ENABLE(VIDEO)