diff options
Diffstat (limited to 'src/3rdparty/webkit/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm')
-rw-r--r-- | src/3rdparty/webkit/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm | 224 |
1 files changed, 146 insertions, 78 deletions
diff --git a/src/3rdparty/webkit/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm b/src/3rdparty/webkit/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm index af612ca3a8..fa10aee27b 100644 --- a/src/3rdparty/webkit/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm +++ b/src/3rdparty/webkit/Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationObjC.mm @@ -37,6 +37,7 @@ #import "GraphicsContext.h" #import "KURL.h" #import "Logging.h" +#import "SecurityOrigin.h" #import "SoftLinking.h" #import "TimeRanges.h" #import "WebCoreSystemInterface.h" @@ -67,6 +68,7 @@ SOFT_LINK_POINTER(AVFoundation, AVMediaTypeVideo, NSString *) SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *) SOFT_LINK_POINTER(AVFoundation, AVPlayerItemDidPlayToEndTimeNotification, NSString *) SOFT_LINK_POINTER(AVFoundation, AVAssetImageGeneratorApertureModeCleanAperture, NSString *) +SOFT_LINK_POINTER(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *) SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime) @@ -83,6 +85,7 @@ SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime) #define AVMediaTypeAudio getAVMediaTypeAudio() #define AVPlayerItemDidPlayToEndTimeNotification getAVPlayerItemDidPlayToEndTimeNotification() #define AVAssetImageGeneratorApertureModeCleanAperture getAVAssetImageGeneratorApertureModeCleanAperture() +#define AVURLAssetReferenceRestrictionsKey getAVURLAssetReferenceRestrictionsKey() #define kCMTimeZero getkCMTimeZero() @@ -138,6 +141,8 @@ MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC(MediaPlay : MediaPlayerPrivateAVFoundation(player) , m_objcObserver(AdoptNS, [[WebCoreAVFMovieObserver alloc] initWithCallback:this]) , m_timeObserver(0) + , m_videoFrameHasDrawn(false) + , m_haveCheckedPlayability(false) { } @@ -218,6 +223,9 @@ void MediaPlayerPrivateAVFoundationObjC::createVideoLayer() if (!m_videoLayer) { m_videoLayer.adoptNS([[AVPlayerLayer alloc] init]); [m_videoLayer.get() setPlayer:m_avPlayer.get()]; +#ifndef NDEBUG + [m_videoLayer.get() setName:@"Video layer"]; +#endif LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoLayer(%p) - returning %p", this, m_videoLayer.get()); } } @@ -234,77 +242,105 @@ void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer() m_videoLayer = 0; } -bool MediaPlayerPrivateAVFoundationObjC::videoLayerIsReadyToDisplay() const +bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const { - return (m_videoLayer && [m_videoLayer.get() isReadyForDisplay]); + return (m_videoFrameHasDrawn || (m_videoLayer && [m_videoLayer.get() isReadyForDisplay])); } -void MediaPlayerPrivateAVFoundationObjC::createAVPlayerForURL(const String& url) +void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const String& url) { + if (m_avAsset) + return; + + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(%p)", this); + setDelayCallbacks(true); - if (!m_avAsset) { - NSURL *cocoaURL = KURL(ParsedURLString, url); - m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:nil]); - } - - createAVPlayer(); + NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote], AVURLAssetReferenceRestrictionsKey, + nil]; + NSURL *cocoaURL = KURL(ParsedURLString, url); + m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options]); + + m_haveCheckedPlayability = false; + + setDelayCallbacks(false); } #if ENABLE(OFFLINE_WEB_APPLICATIONS) -void MediaPlayerPrivateAVFoundationObjC::createAVPlayerForCacheResource(ApplicationCacheResource* resource) +void MediaPlayerPrivateAVFoundationObjC::createAVAssetForCacheResource(ApplicationCacheResource* resource) { - // AVFoundation can't open arbitrary data pointers, so if this ApplicationCacheResource doesn't - // have a valid local path, just open the resource's original URL. - if (resource->path().isEmpty()) { - createAVPlayerForURL(resource->url()); + if (m_avAsset) return; - } + + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVAssetForCacheResource(%p)", this); + + // AVFoundation can't open arbitrary data pointers. + ASSERT(!resource->path().isEmpty()); setDelayCallbacks(true); - if (!m_avAsset) { - NSURL* localURL = [NSURL fileURLWithPath:resource->path()]; - m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:localURL options:nil]); - } + NSURL* localURL = [NSURL fileURLWithPath:resource->path()]; + m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:localURL options:nil]); + m_haveCheckedPlayability = false; - createAVPlayer(); + setDelayCallbacks(false); } #endif void MediaPlayerPrivateAVFoundationObjC::createAVPlayer() { - if (!m_avPlayer) { - m_avPlayer.adoptNS([[AVPlayer alloc] init]); - - [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayer]; - - // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but - // our observer will also be called whenever a seek happens. - const double veryLongInterval = 60*60*60*24*30; - WebCoreAVFMovieObserver *observer = m_objcObserver.get(); - m_timeObserver = [m_avPlayer.get() addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(veryLongInterval, 10) queue:nil usingBlock:^(CMTime time){ - [observer timeChanged:CMTimeGetSeconds(time)]; - }]; - } + if (m_avPlayer) + return; - if (!m_avPlayerItem) { - // Create the player item so we can media data. - m_avPlayerItem.adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]); - - [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get()selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()]; + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayer(%p)", this); - for (NSString *keyName in itemKVOProperties()) - [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem]; + setDelayCallbacks(true); - [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()]; - } + m_avPlayer.adoptNS([[AVPlayer alloc] init]); + [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayer]; + + // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but + // our observer will also be called whenever a seek happens. + const double veryLongInterval = 60 * 60 * 24 * 30; + WebCoreAVFMovieObserver *observer = m_objcObserver.get(); + m_timeObserver = [m_avPlayer.get() addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(veryLongInterval, 10) queue:nil usingBlock:^(CMTime time){ + [observer timeChanged:CMTimeGetSeconds(time)]; + }]; + + setDelayCallbacks(false); +} + +void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem() +{ + if (m_avPlayerItem) + return; + + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem(%p)", this); + + ASSERT(m_avPlayer); + + setDelayCallbacks(true); + + // Create the player item so we can load media data. + m_avPlayerItem.adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]); + + [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()]; + + for (NSString *keyName in itemKVOProperties()) + [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:nil context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem]; + + [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()]; setDelayCallbacks(false); } void MediaPlayerPrivateAVFoundationObjC::checkPlayability() { + if (m_haveCheckedPlayability) + return; + m_haveCheckedPlayability = true; + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::checkPlayability(%p)", this); [m_avAsset.get() loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"playable"] completionHandler:^{ @@ -314,7 +350,7 @@ void MediaPlayerPrivateAVFoundationObjC::checkPlayability() void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata() { - LOG(Media, "MediaPlayerPrivateAVFoundationObjC::playabilityKnown(%p) - requesting metadata loading", this); + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata(%p) - requesting metadata loading", this); [m_avAsset.get() loadValuesAsynchronouslyForKeys:[assetMetadataKeyNames() retain] completionHandler:^{ [m_objcObserver.get() metadataLoaded]; }]; @@ -323,7 +359,7 @@ void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata() MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::playerItemStatus() const { if (!m_avPlayerItem) - return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown; + return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist; AVPlayerItemStatus status = [m_avPlayerItem.get() status]; if (status == AVPlayerItemStatusUnknown) @@ -332,9 +368,9 @@ MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::p return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed; if ([m_avPlayerItem.get() isPlaybackLikelyToKeepUp]) return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp; - if (buffered()->contain(duration())) + if ([m_avPlayerItem.get() isPlaybackBufferFull]) return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull; - if (buffered()->contain(currentTime())) + if ([m_avPlayerItem.get() isPlaybackBufferEmpty]) return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty; return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay; @@ -385,21 +421,25 @@ void MediaPlayerPrivateAVFoundationObjC::platformPause() float MediaPlayerPrivateAVFoundationObjC::platformDuration() const { - if (!metaDataAvailable() || !m_avPlayerItem) - return 0; + if (!m_avAsset) + return invalidTime(); + + CMTime cmDuration; - float duration; - CMTime cmDuration = [m_avPlayerItem.get() duration]; + // Check the AVItem if we have one, some assets never report duration. + if (m_avPlayerItem) + cmDuration = [m_avPlayerItem.get() duration]; + else + cmDuration= [m_avAsset.get() duration]; + if (CMTIME_IS_NUMERIC(cmDuration)) - duration = narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration)); - else if (CMTIME_IS_INDEFINITE(cmDuration)) - duration = numeric_limits<float>::infinity(); - else { - LOG(Media, "MediaPlayerPrivateAVFoundationObjC::duration(%p) - invalid duration, returning 0", this); - return 0; - } + return narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration)); + + if (CMTIME_IS_INDEFINITE(cmDuration)) + return numeric_limits<float>::infinity(); - return duration; + LOG(Media, "MediaPlayerPrivateAVFoundationObjC::duration(%p) - invalid duration, returning %.0f", this, invalidTime()); + return invalidTime(); } float MediaPlayerPrivateAVFoundationObjC::currentTime() const @@ -535,10 +575,10 @@ void MediaPlayerPrivateAVFoundationObjC::setAsset(id asset) m_avAsset = asset; } -MediaPlayerPrivateAVFoundation::AVAssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const +MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const { if (!m_avAsset) - return MediaPlayerAVAssetStatusUnknown; + return MediaPlayerAVAssetStatusDoesNotExist; for (NSString *keyName in assetMetadataKeyNames()) { AVKeyValueStatus keyStatus = [m_avAsset.get() statusOfValueForKey:keyName error:nil]; @@ -547,6 +587,7 @@ MediaPlayerPrivateAVFoundation::AVAssetStatus MediaPlayerPrivateAVFoundationObjC if (keyStatus == AVKeyValueStatusFailed) return MediaPlayerAVAssetStatusFailed; // At least one key could not be loaded. + if (keyStatus == AVKeyValueStatusCancelled) return MediaPlayerAVAssetStatusCancelled; // Loading of at least one key was cancelled. } @@ -587,7 +628,7 @@ void MediaPlayerPrivateAVFoundationObjC::paint(GraphicsContext* context, const I END_BLOCK_OBJC_EXCEPTIONS; setDelayCallbacks(false); - MediaPlayerPrivateAVFoundation::paint(context, rect); + m_videoFrameHasDrawn = true; } static HashSet<String> mimeTypeCache() @@ -662,37 +703,54 @@ float MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(float timeValue) void MediaPlayerPrivateAVFoundationObjC::tracksChanged() { + if (!m_avAsset) + return; + // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are // asked about those fairly fequently. - bool hasVideo = false; - bool hasAudio = false; - bool hasCaptions = false; - NSArray *tracks = [m_avPlayerItem.get() tracks]; - for (AVPlayerItemTrack *track in tracks) { - if ([track isEnabled]) { - AVAssetTrack *assetTrack = [track assetTrack]; - if ([[assetTrack mediaType] isEqualToString:AVMediaTypeVideo]) - hasVideo = true; - else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeAudio]) - hasAudio = true; - else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption]) - hasCaptions = true; + if (!m_avPlayerItem) { + // We don't have a player item yet, so check with the asset because some assets support inspection + // prior to becoming ready to play. + setHasVideo([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual] count]); + setHasAudio([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicAudible] count]); + setHasClosedCaptions([[m_avAsset.get() tracksWithMediaType:AVMediaTypeClosedCaption] count]); + } else { + bool hasVideo = false; + bool hasAudio = false; + bool hasCaptions = false; + NSArray *tracks = [m_avPlayerItem.get() tracks]; + for (AVPlayerItemTrack *track in tracks) { + if ([track isEnabled]) { + AVAssetTrack *assetTrack = [track assetTrack]; + if ([[assetTrack mediaType] isEqualToString:AVMediaTypeVideo]) + hasVideo = true; + else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeAudio]) + hasAudio = true; + else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption]) + hasCaptions = true; + } } + setHasVideo(hasVideo); + setHasAudio(hasAudio); + setHasClosedCaptions(hasCaptions); } - setHasVideo(hasVideo); - setHasAudio(hasAudio); - setHasClosedCaptions(hasCaptions); + + LOG(Media, "WebCoreAVFMovieObserver:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s", + this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions())); sizeChanged(); } void MediaPlayerPrivateAVFoundationObjC::sizeChanged() { + if (!m_avAsset) + return; + NSArray *tracks = [m_avAsset.get() tracks]; // Some assets don't report track properties until they are completely ready to play, but we // want to report a size as early as possible so use presentationSize when an asset has no tracks. - if (![tracks count]) { + if (m_avPlayerItem && ![tracks count]) { setNaturalSize(IntSize([m_avPlayerItem.get() presentationSize])); return; } @@ -716,6 +774,16 @@ void MediaPlayerPrivateAVFoundationObjC::sizeChanged() setNaturalSize(IntSize(naturalSize)); } +bool MediaPlayerPrivateAVFoundationObjC::hasSingleSecurityOrigin() const +{ + if (!m_avAsset) + return false; + + RefPtr<SecurityOrigin> resolvedOrigin = SecurityOrigin::create(KURL(wkAVAssetResolvedURL(m_avAsset.get()))); + RefPtr<SecurityOrigin> requestedOrigin = SecurityOrigin::createFromString(assetURL()); + return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get()); +} + NSArray* assetMetadataKeyNames() { static NSArray* keys; |