2 * Copyright (C) 2011-2014 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 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 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 ENABLE(VIDEO) && USE(AVFOUNDATION)
30 #import "MediaPlayerPrivateAVFoundationObjC.h"
32 #import "AVTrackPrivateAVFObjCImpl.h"
33 #import "AudioTrackPrivateAVFObjC.h"
34 #import "AuthenticationChallenge.h"
35 #import "BlockExceptions.h"
36 #import "CDMSessionAVFoundationObjC.h"
37 #import "ExceptionCodePlaceholder.h"
38 #import "FloatConversion.h"
39 #import "FloatConversion.h"
41 #import "GraphicsContext.h"
42 #import "GraphicsContextCG.h"
43 #import "InbandTextTrackPrivateAVFObjC.h"
44 #import "InbandTextTrackPrivateLegacyAVFObjC.h"
45 #import "OutOfBandTextTrackPrivateAVF.h"
48 #import "PlatformTimeRanges.h"
49 #import "SecurityOrigin.h"
50 #import "SoftLinking.h"
51 #import "TextTrackRepresentation.h"
53 #import "VideoTrackPrivateAVFObjC.h"
54 #import "WebCoreAVFResourceLoader.h"
55 #import "WebCoreSystemInterface.h"
56 #import <objc/runtime.h>
57 #import <runtime/DataView.h>
58 #import <runtime/JSCInlines.h>
59 #import <runtime/TypedArrayInlines.h>
60 #import <runtime/Uint16Array.h>
61 #import <runtime/Uint32Array.h>
62 #import <runtime/Uint8Array.h>
63 #import <wtf/CurrentTime.h>
64 #import <wtf/Functional.h>
65 #import <wtf/text/CString.h>
66 #import <wtf/text/StringBuilder.h>
68 #if ENABLE(AVF_CAPTIONS)
69 #include "TextTrack.h"
72 #import <AVFoundation/AVFoundation.h>
74 #import <CoreImage/CoreImage.h>
76 #import <QuartzCore/CoreImage.h>
78 #import <CoreMedia/CoreMedia.h>
81 #import <CoreVideo/CoreVideo.h>
82 #import <VideoToolbox/VideoToolbox.h>
85 #if ENABLE(AVF_CAPTIONS)
86 // Note: This must be defined before our SOFT_LINK macros:
87 @class AVMediaSelectionOption;
88 @interface AVMediaSelectionOption (OutOfBandExtensions)
89 @property (nonatomic, readonly) NSString* outOfBandSource;
90 @property (nonatomic, readonly) NSString* outOfBandIdentifier;
96 @interface AVPlayerItem (WebKitExtensions)
97 @property (nonatomic, copy) NSString* dataYouTubeID;
101 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
102 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreMedia)
103 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreImage)
104 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreVideo)
106 #if USE(VIDEOTOOLBOX)
107 SOFT_LINK_FRAMEWORK_OPTIONAL(VideoToolbox)
110 SOFT_LINK(CoreMedia, CMTimeCompare, int32_t, (CMTime time1, CMTime time2), (time1, time2))
111 SOFT_LINK(CoreMedia, CMTimeMakeWithSeconds, CMTime, (Float64 seconds, int32_t preferredTimeScale), (seconds, preferredTimeScale))
112 SOFT_LINK(CoreMedia, CMTimeGetSeconds, Float64, (CMTime time), (time))
113 SOFT_LINK(CoreMedia, CMTimeRangeGetEnd, CMTime, (CMTimeRange range), (range))
115 SOFT_LINK(CoreVideo, CVPixelBufferGetWidth, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
116 SOFT_LINK(CoreVideo, CVPixelBufferGetHeight, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
117 SOFT_LINK(CoreVideo, CVPixelBufferGetBaseAddress, void*, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
118 SOFT_LINK(CoreVideo, CVPixelBufferGetBytesPerRow, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
119 SOFT_LINK(CoreVideo, CVPixelBufferGetDataSize, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
120 SOFT_LINK(CoreVideo, CVPixelBufferGetPixelFormatType, OSType, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
121 SOFT_LINK(CoreVideo, CVPixelBufferLockBaseAddress, CVReturn, (CVPixelBufferRef pixelBuffer, CVOptionFlags lockFlags), (pixelBuffer, lockFlags))
122 SOFT_LINK(CoreVideo, CVPixelBufferUnlockBaseAddress, CVReturn, (CVPixelBufferRef pixelBuffer, CVOptionFlags lockFlags), (pixelBuffer, lockFlags))
124 #if USE(VIDEOTOOLBOX)
125 SOFT_LINK(VideoToolbox, VTPixelTransferSessionCreate, OSStatus, (CFAllocatorRef allocator, VTPixelTransferSessionRef *pixelTransferSessionOut), (allocator, pixelTransferSessionOut))
126 SOFT_LINK(VideoToolbox, VTPixelTransferSessionTransferImage, OSStatus, (VTPixelTransferSessionRef session, CVPixelBufferRef sourceBuffer, CVPixelBufferRef destinationBuffer), (session, sourceBuffer, destinationBuffer))
129 SOFT_LINK_CLASS(AVFoundation, AVPlayer)
130 SOFT_LINK_CLASS(AVFoundation, AVPlayerItem)
131 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemVideoOutput)
132 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
133 SOFT_LINK_CLASS(AVFoundation, AVURLAsset)
134 SOFT_LINK_CLASS(AVFoundation, AVAssetImageGenerator)
135 SOFT_LINK_CLASS(CoreImage, CIContext)
136 SOFT_LINK_CLASS(CoreImage, CIImage)
138 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicVisual, NSString *)
139 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicAudible, NSString *)
140 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeClosedCaption, NSString *)
141 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeVideo, NSString *)
142 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
143 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemDidPlayToEndTimeNotification, NSString *)
144 SOFT_LINK_POINTER(AVFoundation, AVURLAssetInheritURIQueryComponentFromReferencingURIKey, NSString *)
145 SOFT_LINK_POINTER(AVFoundation, AVAssetImageGeneratorApertureModeCleanAperture, NSString *)
146 SOFT_LINK_POINTER(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *)
147 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *)
148 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspectFill, NSString *)
149 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResize, NSString *)
150 SOFT_LINK_POINTER(CoreVideo, kCVPixelBufferPixelFormatTypeKey, NSString *)
152 SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime)
154 #define AVPlayer getAVPlayerClass()
155 #define AVPlayerItem getAVPlayerItemClass()
156 #define AVPlayerLayer getAVPlayerLayerClass()
157 #define AVURLAsset getAVURLAssetClass()
158 #define AVAssetImageGenerator getAVAssetImageGeneratorClass()
160 #define AVMediaCharacteristicVisual getAVMediaCharacteristicVisual()
161 #define AVMediaCharacteristicAudible getAVMediaCharacteristicAudible()
162 #define AVMediaTypeClosedCaption getAVMediaTypeClosedCaption()
163 #define AVMediaTypeVideo getAVMediaTypeVideo()
164 #define AVMediaTypeAudio getAVMediaTypeAudio()
165 #define AVPlayerItemDidPlayToEndTimeNotification getAVPlayerItemDidPlayToEndTimeNotification()
166 #define AVURLAssetInheritURIQueryComponentFromReferencingURIKey getAVURLAssetInheritURIQueryComponentFromReferencingURIKey()
167 #define AVAssetImageGeneratorApertureModeCleanAperture getAVAssetImageGeneratorApertureModeCleanAperture()
168 #define AVURLAssetReferenceRestrictionsKey getAVURLAssetReferenceRestrictionsKey()
169 #define AVLayerVideoGravityResizeAspect getAVLayerVideoGravityResizeAspect()
170 #define AVLayerVideoGravityResizeAspectFill getAVLayerVideoGravityResizeAspectFill()
171 #define AVLayerVideoGravityResize getAVLayerVideoGravityResize()
172 #define kCVPixelBufferPixelFormatTypeKey getkCVPixelBufferPixelFormatTypeKey()
174 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
175 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
176 typedef AVMediaSelectionOption AVMediaSelectionOptionType;
178 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemLegibleOutput)
179 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionGroup)
180 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionOption)
182 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicLegible, NSString *)
183 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeSubtitle, NSString *)
184 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicContainsOnlyForcedSubtitles, NSString *)
185 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly, NSString *)
187 #define AVPlayerItemLegibleOutput getAVPlayerItemLegibleOutputClass()
188 #define AVMediaSelectionGroup getAVMediaSelectionGroupClass()
189 #define AVMediaSelectionOption getAVMediaSelectionOptionClass()
190 #define AVMediaCharacteristicLegible getAVMediaCharacteristicLegible()
191 #define AVMediaTypeSubtitle getAVMediaTypeSubtitle()
192 #define AVMediaCharacteristicContainsOnlyForcedSubtitles getAVMediaCharacteristicContainsOnlyForcedSubtitles()
193 #define AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly getAVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly()
196 #if ENABLE(AVF_CAPTIONS)
197 SOFT_LINK_POINTER(AVFoundation, AVURLAssetOutOfBandAlternateTracksKey, NSString*)
198 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackDisplayNameKey, NSString*)
199 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackExtendedLanguageTagKey, NSString*)
200 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIsDefaultKey, NSString*)
201 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackMediaCharactersticsKey, NSString*)
202 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIdentifierKey, NSString*)
203 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackSourceKey, NSString*)
204 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, NSString*)
205 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, NSString*)
207 #define AVURLAssetOutOfBandAlternateTracksKey getAVURLAssetOutOfBandAlternateTracksKey()
208 #define AVOutOfBandAlternateTrackDisplayNameKey getAVOutOfBandAlternateTrackDisplayNameKey()
209 #define AVOutOfBandAlternateTrackExtendedLanguageTagKey getAVOutOfBandAlternateTrackExtendedLanguageTagKey()
210 #define AVOutOfBandAlternateTrackIsDefaultKey getAVOutOfBandAlternateTrackIsDefaultKey()
211 #define AVOutOfBandAlternateTrackMediaCharactersticsKey getAVOutOfBandAlternateTrackMediaCharactersticsKey()
212 #define AVOutOfBandAlternateTrackIdentifierKey getAVOutOfBandAlternateTrackIdentifierKey()
213 #define AVOutOfBandAlternateTrackSourceKey getAVOutOfBandAlternateTrackSourceKey()
214 #define AVMediaCharacteristicDescribesMusicAndSoundForAccessibility getAVMediaCharacteristicDescribesMusicAndSoundForAccessibility()
215 #define AVMediaCharacteristicTranscribesSpokenDialogForAccessibility getAVMediaCharacteristicTranscribesSpokenDialogForAccessibility()
218 #define kCMTimeZero getkCMTimeZero()
220 using namespace WebCore;
222 enum MediaPlayerAVFoundationObservationContext {
223 MediaPlayerAVFoundationObservationContextPlayerItem,
224 MediaPlayerAVFoundationObservationContextPlayer
227 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
228 @interface WebCoreAVFMovieObserver : NSObject <AVPlayerItemLegibleOutputPushDelegate>
230 @interface WebCoreAVFMovieObserver : NSObject
233 MediaPlayerPrivateAVFoundationObjC* m_callback;
234 int m_delayCallbacks;
236 -(id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
238 -(void)metadataLoaded;
239 -(void)didEnd:(NSNotification *)notification;
240 -(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context;
241 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
242 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime;
243 - (void)outputSequenceWasFlushed:(id)output;
247 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
248 @interface WebCoreAVFLoaderDelegate : NSObject<AVAssetResourceLoaderDelegate> {
249 MediaPlayerPrivateAVFoundationObjC* m_callback;
251 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
252 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
253 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
257 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
258 @interface WebCoreAVFPullDelegate : NSObject<AVPlayerItemOutputPullDelegate> {
259 MediaPlayerPrivateAVFoundationObjC *m_callback;
260 dispatch_semaphore_t m_semaphore;
262 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback;
263 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
264 - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender;
265 - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output;
271 static NSArray *assetMetadataKeyNames();
272 static NSArray *itemKVOProperties();
275 static const char *boolString(bool val)
277 return val ? "true" : "false";
281 #if ENABLE(ENCRYPTED_MEDIA_V2)
282 typedef HashMap<MediaPlayer*, MediaPlayerPrivateAVFoundationObjC*> PlayerToPrivateMapType;
283 static PlayerToPrivateMapType& playerToPrivateMap()
285 DEPRECATED_DEFINE_STATIC_LOCAL(PlayerToPrivateMapType, map, ());
290 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
291 static dispatch_queue_t globalLoaderDelegateQueue()
293 static dispatch_queue_t globalQueue;
294 static dispatch_once_t onceToken;
295 dispatch_once(&onceToken, ^{
296 globalQueue = dispatch_queue_create("WebCoreAVFLoaderDelegate queue", DISPATCH_QUEUE_SERIAL);
302 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
303 static dispatch_queue_t globalPullDelegateQueue()
305 static dispatch_queue_t globalQueue;
306 static dispatch_once_t onceToken;
307 dispatch_once(&onceToken, ^{
308 globalQueue = dispatch_queue_create("WebCoreAVFPullDelegate queue", DISPATCH_QUEUE_SERIAL);
314 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateAVFoundationObjC::create(MediaPlayer* player)
316 return adoptPtr(new MediaPlayerPrivateAVFoundationObjC(player));
319 void MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(MediaEngineRegistrar registrar)
322 registrar(create, getSupportedTypes, supportsType, 0, 0, 0, supportsKeySystem);
325 MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC(MediaPlayer* player)
326 : MediaPlayerPrivateAVFoundation(player)
327 , m_weakPtrFactory(this)
329 , m_videoFullscreenGravity(MediaPlayer::VideoGravityResizeAspect)
331 , m_objcObserver(adoptNS([[WebCoreAVFMovieObserver alloc] initWithCallback:this]))
332 , m_videoFrameHasDrawn(false)
333 , m_haveCheckedPlayability(false)
334 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
335 , m_videoOutputDelegate(adoptNS([[WebCoreAVFPullDelegate alloc] initWithCallback:this]))
336 , m_videoOutputSemaphore(nullptr)
338 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
339 , m_loaderDelegate(adoptNS([[WebCoreAVFLoaderDelegate alloc] initWithCallback:this]))
342 , m_cachedDuration(MediaPlayer::invalidTime())
344 , m_pendingStatusChanges(0)
345 , m_cachedItemStatus(MediaPlayerAVPlayerItemStatusDoesNotExist)
346 , m_cachedLikelyToKeepUp(false)
347 , m_cachedBufferEmpty(false)
348 , m_cachedBufferFull(false)
349 , m_cachedHasEnabledAudio(false)
350 #if ENABLE(IOS_AIRPLAY)
351 , m_allowsWirelessVideoPlayback(true)
354 #if ENABLE(ENCRYPTED_MEDIA_V2)
355 playerToPrivateMap().set(player, this);
359 MediaPlayerPrivateAVFoundationObjC::~MediaPlayerPrivateAVFoundationObjC()
361 #if ENABLE(ENCRYPTED_MEDIA_V2)
362 playerToPrivateMap().remove(player());
364 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
365 [m_loaderDelegate.get() setCallback:0];
366 [[m_avAsset.get() resourceLoader] setDelegate:nil queue:0];
368 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
369 [m_videoOutputDelegate setCallback:0];
370 [m_videoOutput setDelegate:nil queue:0];
371 if (m_videoOutputSemaphore)
372 dispatch_release(m_videoOutputSemaphore);
377 void MediaPlayerPrivateAVFoundationObjC::cancelLoad()
379 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::cancelLoad(%p)", this);
380 tearDownVideoRendering();
382 [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
383 [m_objcObserver.get() disconnect];
385 // Tell our observer to do nothing when our cancellation of pending loading calls its completion handler.
386 setIgnoreLoadStateChanges(true);
388 [m_avAsset.get() cancelLoading];
394 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
395 if (m_legibleOutput) {
397 [m_avPlayerItem.get() removeOutput:m_legibleOutput.get()];
398 m_legibleOutput = nil;
402 if (m_avPlayerItem) {
403 for (NSString *keyName in itemKVOProperties())
404 [m_avPlayerItem.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
406 m_avPlayerItem = nil;
410 [m_avPlayer.get() removeTimeObserver:m_timeObserver.get()];
411 m_timeObserver = nil;
412 [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"rate"];
413 #if ENABLE(IOS_AIRPLAY)
414 [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"externalPlaybackActive"];
419 // Reset cached properties
420 m_pendingStatusChanges = 0;
421 m_cachedItemStatus = MediaPlayerAVPlayerItemStatusDoesNotExist;
422 m_cachedSeekableRanges = nullptr;
423 m_cachedLoadedRanges = nullptr;
424 m_cachedTracks = nullptr;
425 m_cachedHasEnabledAudio = false;
426 m_cachedPresentationSize = FloatSize();
427 m_cachedDuration = 0;
429 setIgnoreLoadStateChanges(false);
432 bool MediaPlayerPrivateAVFoundationObjC::hasLayerRenderer() const
437 bool MediaPlayerPrivateAVFoundationObjC::hasContextRenderer() const
439 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
443 return m_imageGenerator;
446 void MediaPlayerPrivateAVFoundationObjC::createContextVideoRenderer()
448 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
451 createImageGenerator();
455 void MediaPlayerPrivateAVFoundationObjC::createImageGenerator()
457 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageGenerator(%p)", this);
459 if (!m_avAsset || m_imageGenerator)
462 m_imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:m_avAsset.get()];
464 [m_imageGenerator.get() setApertureMode:AVAssetImageGeneratorApertureModeCleanAperture];
465 [m_imageGenerator.get() setAppliesPreferredTrackTransform:YES];
466 [m_imageGenerator.get() setRequestedTimeToleranceBefore:kCMTimeZero];
467 [m_imageGenerator.get() setRequestedTimeToleranceAfter:kCMTimeZero];
469 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageGenerator(%p) - returning %p", this, m_imageGenerator.get());
472 void MediaPlayerPrivateAVFoundationObjC::destroyContextVideoRenderer()
474 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
475 destroyVideoOutput();
477 destroyImageGenerator();
480 void MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator()
482 if (!m_imageGenerator)
485 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator(%p) - destroying %p", this, m_imageGenerator.get());
487 m_imageGenerator = 0;
490 void MediaPlayerPrivateAVFoundationObjC::createVideoLayer()
492 if (!m_avPlayer || m_videoLayer)
495 auto weakThis = createWeakPtr();
496 callOnMainThread([this, weakThis] {
500 if (!m_avPlayer || m_videoLayer)
503 m_videoLayer = adoptNS([[AVPlayerLayer alloc] init]);
504 [m_videoLayer.get() setPlayer:m_avPlayer.get()];
505 [m_videoLayer.get() setBackgroundColor:cachedCGColor(Color::black, ColorSpaceDeviceRGB)];
507 [m_videoLayer.get() setName:@"Video layer"];
509 updateVideoLayerGravity();
510 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoLayer(%p) - returning %p", this, m_videoLayer.get());
513 if (m_videoFullscreenLayer) {
514 [m_videoLayer setFrame:CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height())];
515 [m_videoFullscreenLayer insertSublayer:m_videoLayer.get() atIndex:0];
518 player()->mediaPlayerClient()->mediaPlayerRenderingModeChanged(player());
522 void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer()
527 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer(%p) - destroying %p", this, m_videoLayer.get());
529 [m_videoLayer.get() setPlayer:nil];
534 bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
536 if (currentRenderingMode() == MediaRenderingToLayer)
537 return m_videoLayer && [m_videoLayer.get() isReadyForDisplay];
539 return m_videoFrameHasDrawn;
542 #if ENABLE(AVF_CAPTIONS)
543 static const NSArray* mediaDescriptionForKind(PlatformTextTrack::TrackKind kind)
545 // FIXME: Match these to correct types:
546 if (kind == PlatformTextTrack::Caption)
547 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
549 if (kind == PlatformTextTrack::Subtitle)
550 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
552 if (kind == PlatformTextTrack::Description)
553 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, nil];
555 if (kind == PlatformTextTrack::Forced)
556 return [NSArray arrayWithObjects: AVMediaCharacteristicContainsOnlyForcedSubtitles, nil];
558 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
561 void MediaPlayerPrivateAVFoundationObjC::notifyTrackModeChanged()
566 void MediaPlayerPrivateAVFoundationObjC::synchronizeTextTrackState()
568 const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
570 for (auto& textTrack : m_textTracks) {
571 if (textTrack->textTrackCategory() != InbandTextTrackPrivateAVF::OutOfBand)
574 RefPtr<OutOfBandTextTrackPrivateAVF> trackPrivate = static_cast<OutOfBandTextTrackPrivateAVF*>(textTrack.get());
575 RetainPtr<AVMediaSelectionOptionType> currentOption = trackPrivate->mediaSelectionOption();
577 for (auto& track : outOfBandTrackSources) {
578 RetainPtr<CFStringRef> uniqueID = String::number(track->uniqueId()).createCFString();
580 if (![[currentOption.get() outOfBandIdentifier] isEqual: reinterpret_cast<const NSString*>(uniqueID.get())])
583 InbandTextTrackPrivate::Mode mode = InbandTextTrackPrivate::Hidden;
584 if (track->mode() == PlatformTextTrack::Hidden)
585 mode = InbandTextTrackPrivate::Hidden;
586 else if (track->mode() == PlatformTextTrack::Disabled)
587 mode = InbandTextTrackPrivate::Disabled;
588 else if (track->mode() == PlatformTextTrack::Showing)
589 mode = InbandTextTrackPrivate::Showing;
591 textTrack->setMode(mode);
598 void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const String& url)
603 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(%p)", this);
605 setDelayCallbacks(true);
607 RetainPtr<NSMutableDictionary> options = adoptNS([[NSMutableDictionary alloc] init]);
609 [options.get() setObject:[NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote] forKey:AVURLAssetReferenceRestrictionsKey];
611 RetainPtr<NSMutableDictionary> headerFields = adoptNS([[NSMutableDictionary alloc] init]);
613 String referrer = player()->referrer();
614 if (!referrer.isEmpty())
615 [headerFields.get() setObject:referrer forKey:@"Referer"];
617 String userAgent = player()->userAgent();
618 if (!userAgent.isEmpty())
619 [headerFields.get() setObject:userAgent forKey:@"User-Agent"];
621 if ([headerFields.get() count])
622 [options.get() setObject:headerFields.get() forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
624 if (player()->doesHaveAttribute("x-itunes-inherit-uri-query-component"))
625 [options.get() setObject: [NSNumber numberWithBool: TRUE] forKey: AVURLAssetInheritURIQueryComponentFromReferencingURIKey];
627 #if ENABLE(AVF_CAPTIONS)
628 const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
629 if (!outOfBandTrackSources.isEmpty()) {
630 NSMutableArray* outOfBandTracks = [[NSMutableArray alloc] init];
631 for (auto& trackSource : outOfBandTrackSources) {
632 RetainPtr<CFStringRef> label = trackSource->label().createCFString();
633 RetainPtr<CFStringRef> language = trackSource->language().createCFString();
634 RetainPtr<CFStringRef> uniqueID = String::number(trackSource->uniqueId()).createCFString();
635 RetainPtr<CFStringRef> url = trackSource->url().createCFString();
636 [outOfBandTracks addObject:
637 [NSDictionary dictionaryWithObjectsAndKeys:
638 reinterpret_cast<const NSString*>(label.get()), AVOutOfBandAlternateTrackDisplayNameKey,
639 reinterpret_cast<const NSString*>(language.get()), AVOutOfBandAlternateTrackExtendedLanguageTagKey,
640 [NSNumber numberWithBool: (trackSource->isDefault() ? YES : NO)], AVOutOfBandAlternateTrackIsDefaultKey,
641 reinterpret_cast<const NSString*>(uniqueID.get()), AVOutOfBandAlternateTrackIdentifierKey,
642 reinterpret_cast<const NSString*>(url.get()), AVOutOfBandAlternateTrackSourceKey,
643 mediaDescriptionForKind(trackSource->kind()), AVOutOfBandAlternateTrackMediaCharactersticsKey,
647 [options.get() setObject: outOfBandTracks forKey: AVURLAssetOutOfBandAlternateTracksKey];
651 NSURL *cocoaURL = URL(ParsedURLString, url);
652 m_avAsset = adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
654 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
655 [[m_avAsset.get() resourceLoader] setDelegate:m_loaderDelegate.get() queue:globalLoaderDelegateQueue()];
658 m_haveCheckedPlayability = false;
660 setDelayCallbacks(false);
663 void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
668 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayer(%p)", this);
670 setDelayCallbacks(true);
672 m_avPlayer = adoptNS([[AVPlayer alloc] init]);
673 [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
674 #if ENABLE(IOS_AIRPLAY)
675 [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"externalPlaybackActive" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
678 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
679 [m_avPlayer.get() setAppliesMediaSelectionCriteriaAutomatically:YES];
682 #if ENABLE(IOS_AIRPLAY)
683 [m_avPlayer.get() setAllowsExternalPlayback:m_allowsWirelessVideoPlayback];
687 [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
689 setDelayCallbacks(false);
692 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
697 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem(%p)", this);
699 setDelayCallbacks(true);
701 // Create the player item so we can load media data.
702 m_avPlayerItem = adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
704 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
706 NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionPrior;
707 for (NSString *keyName in itemKVOProperties())
708 [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:options context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem];
711 [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
715 if (player()->doesHaveAttribute("data-youtube-id", &value))
716 [m_avPlayerItem.get() setDataYouTubeID: value];
719 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
720 const NSTimeInterval legibleOutputAdvanceInterval = 2;
722 m_legibleOutput = adoptNS([[AVPlayerItemLegibleOutput alloc] initWithMediaSubtypesForNativeRepresentation:[NSArray array]]);
723 [m_legibleOutput.get() setSuppressesPlayerRendering:YES];
725 [m_legibleOutput.get() setDelegate:m_objcObserver.get() queue:dispatch_get_main_queue()];
726 [m_legibleOutput.get() setAdvanceIntervalForDelegateInvocation:legibleOutputAdvanceInterval];
727 [m_legibleOutput.get() setTextStylingResolution:AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly];
728 [m_avPlayerItem.get() addOutput:m_legibleOutput.get()];
731 setDelayCallbacks(false);
734 void MediaPlayerPrivateAVFoundationObjC::checkPlayability()
736 if (m_haveCheckedPlayability)
738 m_haveCheckedPlayability = true;
740 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::checkPlayability(%p)", this);
741 auto weakThis = createWeakPtr();
743 [m_avAsset.get() loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"playable"] completionHandler:^{
744 callOnMainThread([weakThis] {
746 weakThis->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
751 void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata()
753 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata(%p) - requesting metadata loading", this);
754 [m_avAsset.get() loadValuesAsynchronouslyForKeys:[assetMetadataKeyNames() retain] completionHandler:^{
755 [m_objcObserver.get() metadataLoaded];
759 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::playerItemStatus() const
762 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
764 if (m_cachedItemStatus == AVPlayerItemStatusUnknown)
765 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
766 if (m_cachedItemStatus == AVPlayerItemStatusFailed)
767 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
768 if (m_cachedLikelyToKeepUp)
769 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
770 if (m_cachedBufferFull)
771 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
772 if (m_cachedBufferEmpty)
773 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
775 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
778 PlatformMedia MediaPlayerPrivateAVFoundationObjC::platformMedia() const
780 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformMedia(%p)", this);
782 pm.type = PlatformMedia::AVFoundationMediaPlayerType;
783 pm.media.avfMediaPlayer = m_avPlayer.get();
787 PlatformLayer* MediaPlayerPrivateAVFoundationObjC::platformLayer() const
789 return m_videoLayer.get();
793 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenLayer(PlatformLayer* videoFullscreenLayer)
795 if (m_videoFullscreenLayer == videoFullscreenLayer)
798 if (m_videoFullscreenLayer)
799 [m_videoLayer removeFromSuperlayer];
801 m_videoFullscreenLayer = videoFullscreenLayer;
803 CGRect frame = CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height());
805 if (m_videoFullscreenLayer && m_videoLayer) {
806 [m_videoLayer setFrame:frame];
807 [m_videoFullscreenLayer insertSublayer:m_videoLayer.get() atIndex:0];
810 if (m_videoFullscreenLayer && m_textTrackRepresentationLayer) {
811 CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : frame;
812 [m_textTrackRepresentationLayer setFrame:textFrame];
813 [m_videoFullscreenLayer addSublayer:m_textTrackRepresentationLayer.get()];
817 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenFrame(FloatRect frame)
819 m_videoFullscreenFrame = frame;
820 if (!m_videoFullscreenLayer)
824 [m_videoLayer setFrame:CGRectMake(0, 0, frame.width(), frame.height())];
826 if (m_textTrackRepresentationLayer) {
827 CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : static_cast<CGRect>(frame);
828 [m_textTrackRepresentationLayer setFrame:textFrame];
832 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
834 m_videoFullscreenGravity = gravity;
838 NSString *videoGravity = AVLayerVideoGravityResizeAspect;
839 if (gravity == MediaPlayer::VideoGravityResize)
840 videoGravity = AVLayerVideoGravityResize;
841 else if (gravity == MediaPlayer::VideoGravityResizeAspect)
842 videoGravity = AVLayerVideoGravityResizeAspect;
843 else if (gravity == MediaPlayer::VideoGravityResizeAspectFill)
844 videoGravity = AVLayerVideoGravityResizeAspectFill;
846 ASSERT_NOT_REACHED();
848 [m_videoLayer setVideoGravity:videoGravity];
852 void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
854 [CATransaction begin];
855 [CATransaction setDisableActions:YES];
857 [m_videoLayer.get() setHidden:!isVisible];
858 [CATransaction commit];
861 void MediaPlayerPrivateAVFoundationObjC::platformPlay()
863 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformPlay(%p)", this);
864 if (!metaDataAvailable())
867 setDelayCallbacks(true);
868 m_cachedRate = requestedRate();
869 [m_avPlayer.get() setRate:requestedRate()];
870 setDelayCallbacks(false);
873 void MediaPlayerPrivateAVFoundationObjC::platformPause()
875 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformPause(%p)", this);
876 if (!metaDataAvailable())
879 setDelayCallbacks(true);
881 [m_avPlayer.get() setRate:nil];
882 setDelayCallbacks(false);
885 float MediaPlayerPrivateAVFoundationObjC::platformDuration() const
887 // Do not ask the asset for duration before it has been loaded or it will fetch the
888 // answer synchronously.
889 if (!m_avAsset || assetStatus() < MediaPlayerAVAssetStatusLoaded)
890 return MediaPlayer::invalidTime();
894 // Check the AVItem if we have one and it has loaded duration, some assets never report duration.
895 if (m_avPlayerItem && playerItemStatus() >= MediaPlayerAVPlayerItemStatusReadyToPlay)
896 cmDuration = [m_avPlayerItem.get() duration];
898 cmDuration= [m_avAsset.get() duration];
900 if (CMTIME_IS_NUMERIC(cmDuration))
901 return narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
903 if (CMTIME_IS_INDEFINITE(cmDuration)) {
904 return std::numeric_limits<float>::infinity();
907 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformDuration(%p) - invalid duration, returning %.0f", this, MediaPlayer::invalidTime());
908 return MediaPlayer::invalidTime();
911 float MediaPlayerPrivateAVFoundationObjC::currentTime() const
913 if (!metaDataAvailable() || !m_avPlayerItem)
916 CMTime itemTime = [m_avPlayerItem.get() currentTime];
917 if (CMTIME_IS_NUMERIC(itemTime))
918 return std::max(narrowPrecisionToFloat(CMTimeGetSeconds(itemTime)), 0.0f);
923 void MediaPlayerPrivateAVFoundationObjC::seekToTime(double time, double negativeTolerance, double positiveTolerance)
925 // setCurrentTime generates several event callbacks, update afterwards.
926 setDelayCallbacks(true);
928 CMTime cmTime = CMTimeMakeWithSeconds(time, 600);
929 CMTime cmBefore = CMTimeMakeWithSeconds(negativeTolerance, 600);
930 CMTime cmAfter = CMTimeMakeWithSeconds(positiveTolerance, 600);
932 auto weakThis = createWeakPtr();
934 [m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
935 callOnMainThread([weakThis, finished] {
936 auto _this = weakThis.get();
940 _this->seekCompleted(finished);
944 setDelayCallbacks(false);
947 void MediaPlayerPrivateAVFoundationObjC::setVolume(float volume)
949 if (!metaDataAvailable())
952 [m_avPlayer.get() setVolume:volume];
955 void MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(bool closedCaptionsVisible)
957 UNUSED_PARAM(closedCaptionsVisible);
959 if (!metaDataAvailable())
962 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(%p) - set to %s", this, boolString(closedCaptionsVisible));
965 void MediaPlayerPrivateAVFoundationObjC::updateRate()
967 setDelayCallbacks(true);
968 m_cachedRate = requestedRate();
969 [m_avPlayer.get() setRate:requestedRate()];
970 setDelayCallbacks(false);
973 float MediaPlayerPrivateAVFoundationObjC::rate() const
975 if (!metaDataAvailable())
981 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateAVFoundationObjC::platformBufferedTimeRanges() const
983 auto timeRanges = PlatformTimeRanges::create();
988 for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
989 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
990 if (CMTIMERANGE_IS_VALID(timeRange) && !CMTIMERANGE_IS_EMPTY(timeRange)) {
991 float rangeStart = narrowPrecisionToFloat(CMTimeGetSeconds(timeRange.start));
992 float rangeEnd = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange)));
993 timeRanges->add(rangeStart, rangeEnd);
999 double MediaPlayerPrivateAVFoundationObjC::platformMinTimeSeekable() const
1001 if (!m_cachedSeekableRanges || ![m_cachedSeekableRanges count])
1004 double minTimeSeekable = std::numeric_limits<double>::infinity();
1005 bool hasValidRange = false;
1006 for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1007 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1008 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1011 hasValidRange = true;
1012 double startOfRange = CMTimeGetSeconds(timeRange.start);
1013 if (minTimeSeekable > startOfRange)
1014 minTimeSeekable = startOfRange;
1016 return hasValidRange ? minTimeSeekable : 0;
1019 double MediaPlayerPrivateAVFoundationObjC::platformMaxTimeSeekable() const
1021 if (!m_cachedSeekableRanges)
1022 m_cachedSeekableRanges = [m_avPlayerItem seekableTimeRanges];
1024 double maxTimeSeekable = 0;
1025 for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1026 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1027 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1030 double endOfRange = CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange));
1031 if (maxTimeSeekable < endOfRange)
1032 maxTimeSeekable = endOfRange;
1034 return maxTimeSeekable;
1037 float MediaPlayerPrivateAVFoundationObjC::platformMaxTimeLoaded() const
1039 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080
1040 // AVFoundation on Mountain Lion will occasionally not send a KVO notification
1041 // when loadedTimeRanges changes when there is no video output. In that case
1042 // update the cached value explicitly.
1043 if (!hasLayerRenderer() && !hasContextRenderer())
1044 m_cachedLoadedRanges = [m_avPlayerItem loadedTimeRanges];
1047 if (!m_cachedLoadedRanges)
1050 float maxTimeLoaded = 0;
1051 for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
1052 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1053 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1056 float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange)));
1057 if (maxTimeLoaded < endOfRange)
1058 maxTimeLoaded = endOfRange;
1061 return maxTimeLoaded;
1064 unsigned long long MediaPlayerPrivateAVFoundationObjC::totalBytes() const
1066 if (!metaDataAvailable())
1069 long long totalMediaSize = 0;
1070 for (AVPlayerItemTrack *thisTrack in m_cachedTracks.get())
1071 totalMediaSize += [[thisTrack assetTrack] totalSampleDataLength];
1073 return totalMediaSize;
1076 void MediaPlayerPrivateAVFoundationObjC::setAsset(id asset)
1081 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
1084 return MediaPlayerAVAssetStatusDoesNotExist;
1086 for (NSString *keyName in assetMetadataKeyNames()) {
1087 AVKeyValueStatus keyStatus = [m_avAsset.get() statusOfValueForKey:keyName error:nil];
1089 if (keyStatus < AVKeyValueStatusLoaded)
1090 return MediaPlayerAVAssetStatusLoading;// At least one key is not loaded yet.
1092 if (keyStatus == AVKeyValueStatusFailed)
1093 return MediaPlayerAVAssetStatusFailed; // At least one key could not be loaded.
1095 if (keyStatus == AVKeyValueStatusCancelled)
1096 return MediaPlayerAVAssetStatusCancelled; // Loading of at least one key was cancelled.
1099 if ([[m_avAsset.get() valueForKey:@"playable"] boolValue])
1100 return MediaPlayerAVAssetStatusPlayable;
1102 return MediaPlayerAVAssetStatusLoaded;
1105 void MediaPlayerPrivateAVFoundationObjC::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
1107 if (!metaDataAvailable() || context->paintingDisabled())
1110 setDelayCallbacks(true);
1111 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1113 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1114 if (videoOutputHasAvailableFrame())
1115 paintWithVideoOutput(context, rect);
1118 paintWithImageGenerator(context, rect);
1120 END_BLOCK_OBJC_EXCEPTIONS;
1121 setDelayCallbacks(false);
1123 m_videoFrameHasDrawn = true;
1126 void MediaPlayerPrivateAVFoundationObjC::paint(GraphicsContext* context, const IntRect& rect)
1128 if (!metaDataAvailable() || context->paintingDisabled())
1131 // We can ignore the request if we are already rendering to a layer.
1132 if (currentRenderingMode() == MediaRenderingToLayer)
1135 paintCurrentFrameInContext(context, rect);
1138 void MediaPlayerPrivateAVFoundationObjC::paintWithImageGenerator(GraphicsContext* context, const IntRect& rect)
1140 RetainPtr<CGImageRef> image = createImageForTimeInRect(currentTime(), rect);
1142 GraphicsContextStateSaver stateSaver(*context);
1143 context->translate(rect.x(), rect.y() + rect.height());
1144 context->scale(FloatSize(1.0f, -1.0f));
1145 context->setImageInterpolationQuality(InterpolationLow);
1146 IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
1147 CGContextDrawImage(context->platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
1152 static HashSet<String> mimeTypeCache()
1154 DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1155 static bool typeListInitialized = false;
1157 if (typeListInitialized)
1159 typeListInitialized = true;
1161 NSArray *types = [AVURLAsset audiovisualMIMETypes];
1162 for (NSString *mimeType in types)
1163 cache.add(mimeType);
1168 RetainPtr<CGImageRef> MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(float time, const IntRect& rect)
1170 if (!m_imageGenerator)
1171 createImageGenerator();
1172 ASSERT(m_imageGenerator);
1175 double start = monotonicallyIncreasingTime();
1178 [m_imageGenerator.get() setMaximumSize:CGSize(rect.size())];
1179 RetainPtr<CGImageRef> rawImage = adoptCF([m_imageGenerator.get() copyCGImageAtTime:CMTimeMakeWithSeconds(time, 600) actualTime:nil error:nil]);
1180 RetainPtr<CGImageRef> image = adoptCF(CGImageCreateCopyWithColorSpace(rawImage.get(), deviceRGBColorSpaceRef()));
1183 double duration = monotonicallyIncreasingTime() - start;
1184 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(%p) - creating image took %.4f", this, narrowPrecisionToFloat(duration));
1190 void MediaPlayerPrivateAVFoundationObjC::getSupportedTypes(HashSet<String>& supportedTypes)
1192 supportedTypes = mimeTypeCache();
1195 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1196 static bool keySystemIsSupported(const String& keySystem)
1198 if (equalIgnoringCase(keySystem, "com.apple.fps") || equalIgnoringCase(keySystem, "com.apple.fps.1_0"))
1204 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationObjC::supportsType(const MediaEngineSupportParameters& parameters)
1206 #if ENABLE(ENCRYPTED_MEDIA)
1207 // From: <http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-canplaytype>
1208 // In addition to the steps in the current specification, this method must run the following steps:
1210 // 1. Check whether the Key System is supported with the specified container and codec type(s) by following the steps for the first matching condition from the following list:
1211 // If keySystem is null, continue to the next step.
1212 if (!parameters.keySystem.isNull() && !parameters.keySystem.isEmpty()) {
1213 // If keySystem contains an unrecognized or unsupported Key System, return the empty string
1214 if (!keySystemIsSupported(parameters.keySystem))
1215 return MediaPlayer::IsNotSupported;
1217 // If the Key System specified by keySystem does not support decrypting the container and/or codec specified in the rest of the type string.
1218 // (AVFoundation does not provide an API which would allow us to determine this, so this is a no-op)
1221 // 2. Return "maybe" or "probably" as appropriate per the existing specification of canPlayType().
1224 #if ENABLE(MEDIA_SOURCE)
1225 if (parameters.isMediaSource)
1226 return MediaPlayer::IsNotSupported;
1229 if (!mimeTypeCache().contains(parameters.type))
1230 return MediaPlayer::IsNotSupported;
1233 // "Implementors are encouraged to return "maybe" unless the type can be confidently established as being supported or not."
1234 if (parameters.codecs.isEmpty())
1235 return MediaPlayer::MayBeSupported;
1237 NSString *typeString = [NSString stringWithFormat:@"%@; codecs=\"%@\"", (NSString *)parameters.type, (NSString *)parameters.codecs];
1238 return [AVURLAsset isPlayableExtendedMIMEType:typeString] ? MediaPlayer::IsSupported : MediaPlayer::MayBeSupported;;
1241 bool MediaPlayerPrivateAVFoundationObjC::supportsKeySystem(const String& keySystem, const String& mimeType)
1243 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1244 if (!keySystem.isEmpty()) {
1245 if (!keySystemIsSupported(keySystem))
1248 if (!mimeType.isEmpty() && !mimeTypeCache().contains(mimeType))
1254 UNUSED_PARAM(keySystem);
1255 UNUSED_PARAM(mimeType);
1260 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1261 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest* avRequest)
1263 String scheme = [[[avRequest request] URL] scheme];
1264 String keyURI = [[[avRequest request] URL] absoluteString];
1266 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1267 if (scheme == "skd") {
1268 // Create an initData with the following layout:
1269 // [4 bytes: keyURI size], [keyURI size bytes: keyURI]
1270 unsigned keyURISize = keyURI.length() * sizeof(UChar);
1271 RefPtr<ArrayBuffer> initDataBuffer = ArrayBuffer::create(4 + keyURISize, 1);
1272 RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
1273 initDataView->set<uint32_t>(0, keyURISize, true);
1275 RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, 4, keyURI.length());
1276 keyURIArray->setRange(StringView(keyURI).upconvertedCharacters(), keyURI.length() / sizeof(unsigned char), 0);
1278 #if ENABLE(ENCRYPTED_MEDIA)
1279 if (!player()->keyNeeded("com.apple.lskd", emptyString(), static_cast<const unsigned char*>(initDataBuffer->data()), initDataBuffer->byteLength()))
1280 #elif ENABLE(ENCRYPTED_MEDIA_V2)
1281 RefPtr<Uint8Array> initData = Uint8Array::create(initDataBuffer, 0, initDataBuffer->byteLength());
1282 if (!player()->keyNeeded(initData.get()))
1286 m_keyURIToRequestMap.set(keyURI, avRequest);
1291 RefPtr<WebCoreAVFResourceLoader> resourceLoader = WebCoreAVFResourceLoader::create(this, avRequest);
1292 m_resourceLoaderMap.add(avRequest, resourceLoader);
1293 resourceLoader->startLoading();
1297 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForResponseToAuthenticationChallenge(NSURLAuthenticationChallenge* nsChallenge)
1300 UNUSED_PARAM(nsChallenge);
1301 // FIXME: <rdar://problem/15799844>
1304 AuthenticationChallenge challenge(nsChallenge);
1306 return player()->shouldWaitForResponseToAuthenticationChallenge(challenge);
1310 void MediaPlayerPrivateAVFoundationObjC::didCancelLoadingRequest(AVAssetResourceLoadingRequest* avRequest)
1312 String scheme = [[[avRequest request] URL] scheme];
1314 WebCoreAVFResourceLoader* resourceLoader = m_resourceLoaderMap.get(avRequest);
1317 resourceLoader->stopLoading();
1320 void MediaPlayerPrivateAVFoundationObjC::didStopLoadingRequest(AVAssetResourceLoadingRequest *avRequest)
1322 m_resourceLoaderMap.remove(avRequest);
1326 bool MediaPlayerPrivateAVFoundationObjC::isAvailable()
1328 return AVFoundationLibrary() && CoreMediaLibrary();
1331 float MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(float timeValue) const
1333 if (!metaDataAvailable())
1336 // FIXME - impossible to implement until rdar://8721510 is fixed.
1340 void MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity()
1345 [CATransaction begin];
1346 [CATransaction setDisableActions:YES];
1347 NSString* gravity = shouldMaintainAspectRatio() ? AVLayerVideoGravityResizeAspect : AVLayerVideoGravityResize;
1348 [m_videoLayer.get() setVideoGravity:gravity];
1349 [CATransaction commit];
1352 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
1354 String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
1355 m_languageOfPrimaryAudioTrack = String();
1360 setDelayCharacteristicsChangedNotification(true);
1362 bool haveCCTrack = false;
1363 bool hasCaptions = false;
1365 // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
1366 // asked about those fairly fequently.
1367 if (!m_avPlayerItem) {
1368 // We don't have a player item yet, so check with the asset because some assets support inspection
1369 // prior to becoming ready to play.
1370 setHasVideo([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual] count]);
1371 setHasAudio([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicAudible] count]);
1372 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1373 hasCaptions = [[m_avAsset.get() tracksWithMediaType:AVMediaTypeClosedCaption] count];
1376 bool hasVideo = false;
1377 bool hasAudio = false;
1378 for (AVPlayerItemTrack *track in m_cachedTracks.get()) {
1379 if ([track isEnabled]) {
1380 AVAssetTrack *assetTrack = [track assetTrack];
1381 if ([[assetTrack mediaType] isEqualToString:AVMediaTypeVideo])
1383 else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeAudio])
1385 else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption]) {
1386 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1393 setHasVideo(hasVideo);
1394 setHasAudio(hasAudio);
1397 #if ENABLE(VIDEO_TRACK)
1398 updateAudioTracks();
1399 updateVideoTracks();
1403 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1404 if (AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia()) {
1405 hasCaptions = [[AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]] count];
1407 processMediaSelectionOptions();
1411 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT) && HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1412 if (!hasCaptions && haveCCTrack)
1413 processLegacyClosedCaptionsTracks();
1414 #elif !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1416 processLegacyClosedCaptionsTracks();
1419 setHasClosedCaptions(hasCaptions);
1421 LOG(Media, "WebCoreAVFMovieObserver:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s",
1422 this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions()));
1426 if (primaryAudioTrackLanguage != languageOfPrimaryAudioTrack())
1427 characteristicsChanged();
1429 setDelayCharacteristicsChangedNotification(false);
1432 #if ENABLE(VIDEO_TRACK)
1433 template <typename RefT, typename PassRefT>
1434 void determineChangedTracksFromNewTracksAndOldItems(NSArray* tracks, NSString* trackType, Vector<RefT>& oldItems, RefT (*itemFactory)(AVPlayerItemTrack*), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
1436 RetainPtr<NSSet> newTracks = adoptNS([[NSSet alloc] initWithArray:[tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id track, NSUInteger, BOOL*){
1437 return [[[track assetTrack] mediaType] isEqualToString:trackType];
1439 RetainPtr<NSMutableSet> oldTracks = adoptNS([[NSMutableSet alloc] initWithCapacity:oldItems.size()]);
1441 typedef Vector<RefT> ItemVector;
1442 for (auto i = oldItems.begin(); i != oldItems.end(); ++i)
1443 [oldTracks addObject:(*i)->playerItemTrack()];
1445 RetainPtr<NSMutableSet> removedTracks = adoptNS([oldTracks mutableCopy]);
1446 [removedTracks minusSet:newTracks.get()];
1448 RetainPtr<NSMutableSet> addedTracks = adoptNS([newTracks mutableCopy]);
1449 [addedTracks minusSet:oldTracks.get()];
1451 ItemVector replacementItems;
1452 ItemVector addedItems;
1453 ItemVector removedItems;
1454 for (auto i = oldItems.begin(); i != oldItems.end(); ++i) {
1455 if ([removedTracks containsObject:(*i)->playerItemTrack()])
1456 removedItems.append(*i);
1458 replacementItems.append(*i);
1461 for (AVPlayerItemTrack* track in addedTracks.get())
1462 addedItems.append(itemFactory(track));
1464 replacementItems.appendVector(addedItems);
1465 oldItems.swap(replacementItems);
1467 for (auto i = removedItems.begin(); i != removedItems.end(); ++i)
1468 (player->*removedFunction)(*i);
1470 for (auto i = addedItems.begin(); i != addedItems.end(); ++i)
1471 (player->*addedFunction)(*i);
1474 void MediaPlayerPrivateAVFoundationObjC::updateAudioTracks()
1476 determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeAudio, m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
1479 void MediaPlayerPrivateAVFoundationObjC::updateVideoTracks()
1481 determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeVideo, m_videoTracks, &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
1484 bool MediaPlayerPrivateAVFoundationObjC::requiresTextTrackRepresentation() const
1487 if (m_videoFullscreenLayer)
1493 void MediaPlayerPrivateAVFoundationObjC::setTextTrackRepresentation(TextTrackRepresentation* representation)
1496 PlatformLayer* representationLayer = representation ? representation->platformLayer() : nil;
1497 if (representationLayer == m_textTrackRepresentationLayer)
1500 if (m_textTrackRepresentationLayer)
1501 [m_textTrackRepresentationLayer removeFromSuperlayer];
1503 m_textTrackRepresentationLayer = representationLayer;
1505 if (m_videoFullscreenLayer && m_textTrackRepresentationLayer) {
1506 CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height());
1508 [m_textTrackRepresentationLayer setFrame:textFrame];
1509 [m_videoFullscreenLayer addSublayer:m_textTrackRepresentationLayer.get()];
1513 UNUSED_PARAM(representation);
1516 #endif // ENABLE(VIDEO_TRACK)
1518 void MediaPlayerPrivateAVFoundationObjC::sizeChanged()
1523 setNaturalSize(roundedIntSize(m_cachedPresentationSize));
1527 // FIXME: Implement for iOS in WebKit System Interface.
1528 static inline NSURL *wkAVAssetResolvedURL(AVAsset*)
1534 bool MediaPlayerPrivateAVFoundationObjC::hasSingleSecurityOrigin() const
1539 RefPtr<SecurityOrigin> resolvedOrigin = SecurityOrigin::create(URL(wkAVAssetResolvedURL(m_avAsset.get())));
1540 RefPtr<SecurityOrigin> requestedOrigin = SecurityOrigin::createFromString(assetURL());
1541 return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
1544 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1545 void MediaPlayerPrivateAVFoundationObjC::createVideoOutput()
1547 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoOutput(%p)", this);
1549 if (!m_avPlayerItem || m_videoOutput)
1552 #if USE(VIDEOTOOLBOX)
1553 NSDictionary* attributes = @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_422YpCbCr8) };
1555 NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
1558 m_videoOutput = adoptNS([[getAVPlayerItemVideoOutputClass() alloc] initWithPixelBufferAttributes:attributes]);
1559 ASSERT(m_videoOutput);
1561 [m_videoOutput setDelegate:m_videoOutputDelegate.get() queue:globalPullDelegateQueue()];
1563 [m_avPlayerItem.get() addOutput:m_videoOutput.get()];
1565 waitForVideoOutputMediaDataWillChange();
1567 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoOutput(%p) - returning %p", this, m_videoOutput.get());
1570 void MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput()
1576 [m_avPlayerItem.get() removeOutput:m_videoOutput.get()];
1577 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput(%p) - destroying %p", this, m_videoOutput.get());
1582 RetainPtr<CVPixelBufferRef> MediaPlayerPrivateAVFoundationObjC::createPixelBuffer()
1585 createVideoOutput();
1586 ASSERT(m_videoOutput);
1589 double start = monotonicallyIncreasingTime();
1592 CMTime currentTime = [m_avPlayerItem.get() currentTime];
1594 if (![m_videoOutput.get() hasNewPixelBufferForItemTime:currentTime])
1597 RetainPtr<CVPixelBufferRef> buffer = adoptCF([m_videoOutput.get() copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
1601 #if USE(VIDEOTOOLBOX)
1602 // Create a VTPixelTransferSession, if necessary, as we cannot guarantee timely delivery of ARGB pixels.
1603 if (!m_pixelTransferSession) {
1604 VTPixelTransferSessionRef session = 0;
1605 VTPixelTransferSessionCreate(kCFAllocatorDefault, &session);
1606 m_pixelTransferSession = adoptCF(session);
1609 CVPixelBufferRef outputBuffer;
1610 CVPixelBufferCreate(kCFAllocatorDefault, CVPixelBufferGetWidth(buffer.get()), CVPixelBufferGetHeight(buffer.get()), kCVPixelFormatType_32BGRA, 0, &outputBuffer);
1611 VTPixelTransferSessionTransferImage(m_pixelTransferSession.get(), buffer.get(), outputBuffer);
1612 buffer = adoptCF(outputBuffer);
1616 double duration = monotonicallyIncreasingTime() - start;
1617 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createPixelBuffer(%p) - creating buffer took %.4f", this, narrowPrecisionToFloat(duration));
1623 bool MediaPlayerPrivateAVFoundationObjC::videoOutputHasAvailableFrame()
1625 if (!m_avPlayerItem)
1632 createVideoOutput();
1634 return [m_videoOutput hasNewPixelBufferForItemTime:[m_avPlayerItem currentTime]];
1637 static const void* CVPixelBufferGetBytePointerCallback(void* info)
1639 CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1640 CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
1641 return CVPixelBufferGetBaseAddress(pixelBuffer);
1644 static void CVPixelBufferReleaseBytePointerCallback(void* info, const void*)
1646 CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1647 CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
1650 static void CVPixelBufferReleaseInfoCallback(void* info)
1652 CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1653 CFRelease(pixelBuffer);
1656 static RetainPtr<CGImageRef> createImageFromPixelBuffer(CVPixelBufferRef pixelBuffer)
1658 // pixelBuffer will be of type kCVPixelFormatType_32BGRA.
1659 ASSERT(CVPixelBufferGetPixelFormatType(pixelBuffer) == kCVPixelFormatType_32BGRA);
1661 size_t width = CVPixelBufferGetWidth(pixelBuffer);
1662 size_t height = CVPixelBufferGetHeight(pixelBuffer);
1663 size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
1664 size_t byteLength = CVPixelBufferGetDataSize(pixelBuffer);
1665 CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
1667 CFRetain(pixelBuffer); // Balanced by CVPixelBufferReleaseInfoCallback in providerCallbacks.
1668 CGDataProviderDirectCallbacks providerCallbacks = { 0, CVPixelBufferGetBytePointerCallback, CVPixelBufferReleaseBytePointerCallback, 0, CVPixelBufferReleaseInfoCallback };
1669 RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateDirect(pixelBuffer, byteLength, &providerCallbacks));
1671 return adoptCF(CGImageCreate(width, height, 8, 32, bytesPerRow, deviceRGBColorSpaceRef(), bitmapInfo, provider.get(), NULL, false, kCGRenderingIntentDefault));
1674 void MediaPlayerPrivateAVFoundationObjC::updateLastImage()
1676 RetainPtr<CVPixelBufferRef> pixelBuffer = createPixelBuffer();
1678 // Calls to copyPixelBufferForItemTime:itemTimeForDisplay: may return nil if the pixel buffer
1679 // for the requested time has already been retrieved. In this case, the last valid image (if any)
1680 // should be displayed.
1682 m_lastImage = createImageFromPixelBuffer(pixelBuffer.get());
1685 void MediaPlayerPrivateAVFoundationObjC::paintWithVideoOutput(GraphicsContext* context, const IntRect& outputRect)
1690 GraphicsContextStateSaver stateSaver(*context);
1692 IntRect imageRect(0, 0, CGImageGetWidth(m_lastImage.get()), CGImageGetHeight(m_lastImage.get()));
1694 context->drawNativeImage(m_lastImage.get(), imageRect.size(), ColorSpaceDeviceRGB, outputRect, imageRect);
1696 // If we have created an AVAssetImageGenerator in the past due to m_videoOutput not having an available
1697 // video frame, destroy it now that it is no longer needed.
1698 if (m_imageGenerator)
1699 destroyImageGenerator();
1703 PassNativeImagePtr MediaPlayerPrivateAVFoundationObjC::nativeImageForCurrentTime()
1706 return m_lastImage.get();
1709 void MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange()
1711 if (!m_videoOutputSemaphore)
1712 m_videoOutputSemaphore = dispatch_semaphore_create(0);
1714 [m_videoOutput requestNotificationOfMediaDataChangeWithAdvanceInterval:0];
1716 // Wait for 1 second.
1717 long result = dispatch_semaphore_wait(m_videoOutputSemaphore, dispatch_time(0, 1 * NSEC_PER_SEC));
1720 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange(%p) timed out", this);
1723 void MediaPlayerPrivateAVFoundationObjC::outputMediaDataWillChange(AVPlayerItemVideoOutput*)
1725 dispatch_semaphore_signal(m_videoOutputSemaphore);
1729 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1730 bool MediaPlayerPrivateAVFoundationObjC::extractKeyURIKeyIDAndCertificateFromInitData(Uint8Array* initData, String& keyURI, String& keyID, RefPtr<Uint8Array>& certificate)
1732 // initData should have the following layout:
1733 // [4 bytes: keyURI length][N bytes: keyURI][4 bytes: contentID length], [N bytes: contentID], [4 bytes: certificate length][N bytes: certificate]
1734 if (initData->byteLength() < 4)
1737 RefPtr<ArrayBuffer> initDataBuffer = initData->buffer();
1739 // Use a DataView to read uint32 values from the buffer, as Uint32Array requires the reads be aligned on 4-byte boundaries.
1740 RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
1741 uint32_t offset = 0;
1744 uint32_t keyURILength = initDataView->get<uint32_t>(offset, true, &status);
1746 if (!status || offset + keyURILength > initData->length())
1749 RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, offset, keyURILength);
1753 keyURI = String(keyURIArray->data(), keyURILength / sizeof(unsigned short));
1754 offset += keyURILength;
1756 uint32_t keyIDLength = initDataView->get<uint32_t>(offset, true, &status);
1758 if (!status || offset + keyIDLength > initData->length())
1761 RefPtr<Uint16Array> keyIDArray = Uint16Array::create(initDataBuffer, offset, keyIDLength);
1765 keyID = String(keyIDArray->data(), keyIDLength / sizeof(unsigned short));
1766 offset += keyIDLength;
1768 uint32_t certificateLength = initDataView->get<uint32_t>(offset, true, &status);
1770 if (!status || offset + certificateLength > initData->length())
1773 certificate = Uint8Array::create(initDataBuffer, offset, certificateLength);
1781 #if ENABLE(ENCRYPTED_MEDIA)
1782 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::generateKeyRequest(const String& keySystem, const unsigned char* initDataPtr, unsigned initDataLength)
1784 if (!keySystemIsSupported(keySystem))
1785 return MediaPlayer::KeySystemNotSupported;
1787 RefPtr<Uint8Array> initData = Uint8Array::create(initDataPtr, initDataLength);
1790 RefPtr<Uint8Array> certificate;
1791 if (!extractKeyURIKeyIDAndCertificateFromInitData(initData.get(), keyURI, keyID, certificate))
1792 return MediaPlayer::InvalidPlayerState;
1794 if (!m_keyURIToRequestMap.contains(keyURI))
1795 return MediaPlayer::InvalidPlayerState;
1797 String sessionID = createCanonicalUUIDString();
1799 RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_keyURIToRequestMap.get(keyURI);
1801 RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:certificate->baseAddress() length:certificate->byteLength()]);
1802 NSString* assetStr = keyID;
1803 RetainPtr<NSData> assetID = [NSData dataWithBytes: [assetStr cStringUsingEncoding:NSUTF8StringEncoding] length:[assetStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
1805 RetainPtr<NSData> keyRequest = [avRequest.get() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:assetID.get() options:nil error:&error];
1808 NSError* underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
1809 player()->keyError(keySystem, sessionID, MediaPlayerClient::DomainError, [underlyingError code]);
1810 return MediaPlayer::NoError;
1813 RefPtr<ArrayBuffer> keyRequestBuffer = ArrayBuffer::create([keyRequest.get() bytes], [keyRequest.get() length]);
1814 RefPtr<Uint8Array> keyRequestArray = Uint8Array::create(keyRequestBuffer, 0, keyRequestBuffer->byteLength());
1815 player()->keyMessage(keySystem, sessionID, keyRequestArray->data(), keyRequestArray->byteLength(), URL());
1817 // Move ownership of the AVAssetResourceLoadingRequestfrom the keyIDToRequestMap to the sessionIDToRequestMap:
1818 m_sessionIDToRequestMap.set(sessionID, avRequest);
1819 m_keyURIToRequestMap.remove(keyURI);
1821 return MediaPlayer::NoError;
1824 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::addKey(const String& keySystem, const unsigned char* keyPtr, unsigned keyLength, const unsigned char* initDataPtr, unsigned initDataLength, const String& sessionID)
1826 if (!keySystemIsSupported(keySystem))
1827 return MediaPlayer::KeySystemNotSupported;
1829 if (!m_sessionIDToRequestMap.contains(sessionID))
1830 return MediaPlayer::InvalidPlayerState;
1832 RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_sessionIDToRequestMap.get(sessionID);
1833 RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:keyPtr length:keyLength]);
1834 [[avRequest.get() dataRequest] respondWithData:keyData.get()];
1835 [avRequest.get() finishLoading];
1836 m_sessionIDToRequestMap.remove(sessionID);
1838 player()->keyAdded(keySystem, sessionID);
1840 UNUSED_PARAM(initDataPtr);
1841 UNUSED_PARAM(initDataLength);
1842 return MediaPlayer::NoError;
1845 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::cancelKeyRequest(const String& keySystem, const String& sessionID)
1847 if (!keySystemIsSupported(keySystem))
1848 return MediaPlayer::KeySystemNotSupported;
1850 if (!m_sessionIDToRequestMap.contains(sessionID))
1851 return MediaPlayer::InvalidPlayerState;
1853 m_sessionIDToRequestMap.remove(sessionID);
1854 return MediaPlayer::NoError;
1858 #if ENABLE(ENCRYPTED_MEDIA_V2)
1859 RetainPtr<AVAssetResourceLoadingRequest> MediaPlayerPrivateAVFoundationObjC::takeRequestForKeyURI(const String& keyURI)
1861 return m_keyURIToRequestMap.take(keyURI);
1864 std::unique_ptr<CDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem)
1866 if (!keySystemIsSupported(keySystem))
1869 return std::make_unique<CDMSessionAVFoundationObjC>(this);
1873 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1874 void MediaPlayerPrivateAVFoundationObjC::processLegacyClosedCaptionsTracks()
1876 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1877 [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
1880 Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
1881 for (AVPlayerItemTrack *playerItemTrack in m_cachedTracks.get()) {
1883 AVAssetTrack *assetTrack = [playerItemTrack assetTrack];
1884 if (![[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
1887 bool newCCTrack = true;
1888 for (unsigned i = removedTextTracks.size(); i > 0; --i) {
1889 if (removedTextTracks[i - 1]->textTrackCategory() != InbandTextTrackPrivateAVF::LegacyClosedCaption)
1892 RefPtr<InbandTextTrackPrivateLegacyAVFObjC> track = static_cast<InbandTextTrackPrivateLegacyAVFObjC*>(m_textTracks[i - 1].get());
1893 if (track->avPlayerItemTrack() == playerItemTrack) {
1894 removedTextTracks.remove(i - 1);
1903 m_textTracks.append(InbandTextTrackPrivateLegacyAVFObjC::create(this, playerItemTrack));
1906 processNewAndRemovedTextTracks(removedTextTracks);
1910 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1911 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForLegibleMedia()
1916 if ([m_avAsset.get() statusOfValueForKey:@"availableMediaCharacteristicsWithMediaSelectionOptions" error:NULL] != AVKeyValueStatusLoaded)
1919 return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1922 void MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions()
1924 AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
1925 if (!legibleGroup) {
1926 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions(%p) - nil mediaSelectionGroup", this);
1930 // We enabled automatic media selection because we want alternate audio tracks to be enabled/disabled automatically,
1931 // but set the selected legible track to nil so text tracks will not be automatically configured.
1932 if (!m_textTracks.size())
1933 [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
1935 Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
1936 NSArray *legibleOptions = [AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]];
1937 for (AVMediaSelectionOptionType *option in legibleOptions) {
1938 bool newTrack = true;
1939 for (unsigned i = removedTextTracks.size(); i > 0; --i) {
1940 if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
1943 RetainPtr<AVMediaSelectionOptionType> currentOption;
1944 #if ENABLE(AVF_CAPTIONS)
1945 if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand) {
1946 RefPtr<OutOfBandTextTrackPrivateAVF> track = static_cast<OutOfBandTextTrackPrivateAVF*>(removedTextTracks[i - 1].get());
1947 currentOption = track->mediaSelectionOption();
1951 RefPtr<InbandTextTrackPrivateAVFObjC> track = static_cast<InbandTextTrackPrivateAVFObjC*>(removedTextTracks[i - 1].get());
1952 currentOption = track->mediaSelectionOption();
1955 if ([currentOption.get() isEqual:option]) {
1956 removedTextTracks.remove(i - 1);
1964 #if ENABLE(AVF_CAPTIONS)
1965 if ([option outOfBandSource]) {
1966 m_textTracks.append(OutOfBandTextTrackPrivateAVF::create(this, option));
1967 m_textTracks.last()->setHasBeenReported(true); // Ignore out-of-band tracks that we passed to AVFoundation so we do not double-count them
1972 m_textTracks.append(InbandTextTrackPrivateAVFObjC::create(this, option));
1975 processNewAndRemovedTextTracks(removedTextTracks);
1978 void MediaPlayerPrivateAVFoundationObjC::processCue(NSArray *attributedStrings, double time)
1980 if (!m_currentTrack)
1983 m_currentTrack->processCue(reinterpret_cast<CFArrayRef>(attributedStrings), time);
1986 void MediaPlayerPrivateAVFoundationObjC::flushCues()
1988 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::flushCues(%p)", this);
1990 if (!m_currentTrack)
1993 m_currentTrack->resetCueValues();
1995 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1997 void MediaPlayerPrivateAVFoundationObjC::setCurrentTrack(InbandTextTrackPrivateAVF *track)
1999 if (m_currentTrack == track)
2002 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setCurrentTrack(%p) - selecting track %p, language = %s", this, track, track ? track->language().string().utf8().data() : "");
2004 m_currentTrack = track;
2007 if (track->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
2008 [m_avPlayer.get() setClosedCaptionDisplayEnabled:YES];
2009 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2010 #if ENABLE(AVF_CAPTIONS)
2011 else if (track->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand)
2012 [m_avPlayerItem.get() selectMediaOption:static_cast<OutOfBandTextTrackPrivateAVF*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2015 [m_avPlayerItem.get() selectMediaOption:static_cast<InbandTextTrackPrivateAVFObjC*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2018 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2019 [m_avPlayerItem.get() selectMediaOption:0 inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2021 [m_avPlayer.get() setClosedCaptionDisplayEnabled:NO];
2026 String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
2028 if (!m_languageOfPrimaryAudioTrack.isNull())
2029 return m_languageOfPrimaryAudioTrack;
2031 if (!m_avPlayerItem.get())
2032 return emptyString();
2034 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2035 // If AVFoundation has an audible group, return the language of the currently selected audible option.
2036 AVMediaSelectionGroupType *audibleGroup = [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
2037 AVMediaSelectionOptionType *currentlySelectedAudibleOption = [m_avPlayerItem.get() selectedMediaOptionInMediaSelectionGroup:audibleGroup];
2038 if (currentlySelectedAudibleOption) {
2039 m_languageOfPrimaryAudioTrack = [[currentlySelectedAudibleOption locale] localeIdentifier];
2040 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of selected audible option: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
2042 return m_languageOfPrimaryAudioTrack;
2044 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2046 // AVFoundation synthesizes an audible group when there is only one ungrouped audio track if there is also a legible group (one or
2047 // more in-band text tracks). It doesn't know about out-of-band tracks, so if there is a single audio track return its language.
2048 NSArray *tracks = [m_avAsset.get() tracksWithMediaType:AVMediaTypeAudio];
2049 if (!tracks || [tracks count] != 1) {
2050 m_languageOfPrimaryAudioTrack = emptyString();
2051 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - %lu audio tracks, returning emptyString()", this, static_cast<unsigned long>(tracks ? [tracks count] : 0));
2052 return m_languageOfPrimaryAudioTrack;
2055 AVAssetTrack *track = [tracks objectAtIndex:0];
2056 m_languageOfPrimaryAudioTrack = AVTrackPrivateAVFObjCImpl::languageForAVAssetTrack(track);
2059 if (m_languageOfPrimaryAudioTrack == emptyString())
2060 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - single audio track has no language, returning emptyString()", this);
2062 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of single audio track: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
2065 return m_languageOfPrimaryAudioTrack;
2068 #if ENABLE(IOS_AIRPLAY)
2069 bool MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless() const
2074 bool wirelessTarget = [m_avPlayer.get() isExternalPlaybackActive];
2075 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless(%p) - returning %s", this, boolString(wirelessTarget));
2076 return wirelessTarget;
2079 MediaPlayer::WirelessPlaybackTargetType MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetType() const
2082 return MediaPlayer::TargetTypeNone;
2084 switch (wkExernalDeviceTypeForPlayer(m_avPlayer.get())) {
2085 case wkExternalPlaybackTypeNone:
2086 return MediaPlayer::TargetTypeNone;
2087 case wkExternalPlaybackTypeAirPlay:
2088 return MediaPlayer::TargetTypeAirPlay;
2089 case wkExternalPlaybackTypeTVOut:
2090 return MediaPlayer::TargetTypeTVOut;
2093 ASSERT_NOT_REACHED();
2094 return MediaPlayer::TargetTypeNone;
2097 String MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName() const
2100 return emptyString();
2102 String wirelessTargetName = wkExernalDeviceDisplayNameForPlayer(m_avPlayer.get());
2103 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName(%p) - returning %s", this, wirelessTargetName.utf8().data());
2105 return wirelessTargetName;
2108 bool MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled() const
2111 return !m_allowsWirelessVideoPlayback;
2113 m_allowsWirelessVideoPlayback = [m_avPlayer.get() allowsExternalPlayback];
2114 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled(%p) - returning %s", this, boolString(!m_allowsWirelessVideoPlayback));
2116 return !m_allowsWirelessVideoPlayback;
2119 void MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(bool disabled)
2121 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(%p) - %s", this, boolString(disabled));
2122 m_allowsWirelessVideoPlayback = !disabled;
2126 [m_avPlayer.get() setAllowsExternalPlayback:!disabled];
2130 void MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange(int status)
2132 m_cachedItemStatus = status;
2137 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange()
2139 m_pendingStatusChanges++;
2142 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange(bool likelyToKeepUp)
2144 m_cachedLikelyToKeepUp = likelyToKeepUp;
2146 ASSERT(m_pendingStatusChanges);
2147 if (!--m_pendingStatusChanges)
2151 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange()
2153 m_pendingStatusChanges++;
2156 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange(bool bufferEmpty)
2158 m_cachedBufferEmpty = bufferEmpty;
2160 ASSERT(m_pendingStatusChanges);
2161 if (!--m_pendingStatusChanges)
2165 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange()
2167 m_pendingStatusChanges++;
2170 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange(bool bufferFull)
2172 m_cachedBufferFull = bufferFull;
2174 ASSERT(m_pendingStatusChanges);
2175 if (!--m_pendingStatusChanges)
2179 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray> seekableRanges)
2181 m_cachedSeekableRanges = seekableRanges;
2183 seekableTimeRangesChanged();
2187 void MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange(RetainPtr<NSArray> loadedRanges)
2189 m_cachedLoadedRanges = loadedRanges;
2191 loadedTimeRangesChanged();
2195 void MediaPlayerPrivateAVFoundationObjC::tracksDidChange(RetainPtr<NSArray> tracks)
2197 m_cachedTracks = tracks;
2203 void MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange(bool hasEnabledAudio)
2205 m_cachedHasEnabledAudio = hasEnabledAudio;
2211 void MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange(FloatSize size)
2213 m_cachedPresentationSize = size;
2219 void MediaPlayerPrivateAVFoundationObjC::durationDidChange(double duration)
2221 m_cachedDuration = duration;
2223 invalidateCachedDuration();
2226 void MediaPlayerPrivateAVFoundationObjC::rateDidChange(double rate)
2228 m_cachedRate = rate;
2234 #if ENABLE(IOS_AIRPLAY)
2235 void MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange()
2237 playbackTargetIsWirelessChanged();
2241 NSArray* assetMetadataKeyNames()
2243 static NSArray* keys;
2245 keys = [[NSArray alloc] initWithObjects:@"duration",
2247 @"preferredTransform",
2252 @"availableMediaCharacteristicsWithMediaSelectionOptions",
2258 NSArray* itemKVOProperties()
2260 static NSArray* keys;
2262 keys = [[NSArray alloc] initWithObjects:@"presentationSize",
2266 @"seekableTimeRanges",
2267 @"loadedTimeRanges",
2268 @"playbackLikelyToKeepUp",
2269 @"playbackBufferFull",
2270 @"playbackBufferEmpty",
2278 } // namespace WebCore
2280 @implementation WebCoreAVFMovieObserver
2282 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2284 self = [super init];
2287 m_callback = callback;
2293 [NSObject cancelPreviousPerformRequestsWithTarget:self];
2297 - (void)metadataLoaded
2302 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
2305 - (void)didEnd:(NSNotification *)unusedNotification
2307 UNUSED_PARAM(unusedNotification);
2310 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
2313 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context
2315 UNUSED_PARAM(object);
2316 id newValue = [change valueForKey:NSKeyValueChangeNewKey];
2318 LOG(Media, "WebCoreAVFMovieObserver::observeValueForKeyPath(%p) - keyPath = %s", self, [keyPath UTF8String]);
2323 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
2325 WTF::Function<void ()> function;
2327 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) {
2328 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
2329 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange, m_callback);
2330 else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
2331 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange, m_callback);
2332 else if ([keyPath isEqualToString:@"playbackBufferFull"])
2333 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange, m_callback);
2336 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) {
2337 // A value changed for an AVPlayerItem
2338 if ([keyPath isEqualToString:@"status"])
2339 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange, m_callback, [newValue intValue]);
2340 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
2341 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange, m_callback, [newValue boolValue]);
2342 else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
2343 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange, m_callback, [newValue boolValue]);
2344 else if ([keyPath isEqualToString:@"playbackBufferFull"])
2345 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange, m_callback, [newValue boolValue]);
2346 else if ([keyPath isEqualToString:@"asset"])
2347 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::setAsset, m_callback, RetainPtr<NSArray>(newValue));
2348 else if ([keyPath isEqualToString:@"loadedTimeRanges"])
2349 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
2350 else if ([keyPath isEqualToString:@"seekableTimeRanges"])
2351 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
2352 else if ([keyPath isEqualToString:@"tracks"])
2353 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::tracksDidChange, m_callback, RetainPtr<NSArray>(newValue));
2354 else if ([keyPath isEqualToString:@"hasEnabledAudio"])
2355 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange, m_callback, [newValue boolValue]);
2356 else if ([keyPath isEqualToString:@"presentationSize"])
2357 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange, m_callback, FloatSize([newValue sizeValue]));
2358 else if ([keyPath isEqualToString:@"duration"])
2359 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::durationDidChange, m_callback, CMTimeGetSeconds([newValue CMTimeValue]));
2362 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) {
2363 // A value changed for an AVPlayer.
2364 if ([keyPath isEqualToString:@"rate"])
2365 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::rateDidChange, m_callback, [newValue doubleValue]);
2366 #if ENABLE(IOS_AIRPLAY)
2367 else if ([keyPath isEqualToString:@"externalPlaybackActive"])
2368 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange, m_callback);
2372 if (function.isNull())
2375 auto weakThis = m_callback->createWeakPtr();
2376 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification([weakThis, function]{
2377 // weakThis and function both refer to the same MediaPlayerPrivateAVFoundationObjC instance. If the WeakPtr has
2378 // been cleared, the underlying object has been destroyed, and it is unsafe to call function().
2385 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2386 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime
2388 UNUSED_PARAM(output);
2389 UNUSED_PARAM(nativeSamples);
2394 RetainPtr<WebCoreAVFMovieObserver> strongSelf = self;
2395 RetainPtr<NSArray> strongStrings = strings;
2396 callOnMainThread([strongSelf, strongStrings, itemTime] {
2397 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2400 callback->processCue(strongStrings.get(), CMTimeGetSeconds(itemTime));
2404 - (void)outputSequenceWasFlushed:(id)output
2406 UNUSED_PARAM(output);
2411 RetainPtr<WebCoreAVFMovieObserver> strongSelf = self;
2412 callOnMainThread([strongSelf] {
2413 if (MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback)
2414 callback->flushCues();
2421 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
2422 @implementation WebCoreAVFLoaderDelegate
2424 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2426 self = [super init];
2429 m_callback = callback;
2433 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
2435 UNUSED_PARAM(resourceLoader);
2439 RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2440 RetainPtr<AVAssetResourceLoadingRequest> strongRequest = loadingRequest;
2441 callOnMainThread([strongSelf, strongRequest] {
2442 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2444 [strongRequest finishLoadingWithError:nil];
2448 if (!callback->shouldWaitForLoadingOfResource(strongRequest.get()))
2449 [strongRequest finishLoadingWithError:nil];
2455 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForResponseToAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
2457 UNUSED_PARAM(resourceLoader);
2461 if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
2464 RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2465 RetainPtr<NSURLAuthenticationChallenge> strongChallenge = challenge;
2466 callOnMainThread([strongSelf, strongChallenge] {
2467 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2469 [[strongChallenge sender] cancelAuthenticationChallenge:strongChallenge.get()];
2473 if (!callback->shouldWaitForResponseToAuthenticationChallenge(strongChallenge.get()))
2474 [[strongChallenge sender] cancelAuthenticationChallenge:strongChallenge.get()];
2480 - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
2482 UNUSED_PARAM(resourceLoader);
2486 RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2487 RetainPtr<AVAssetResourceLoadingRequest> strongRequest = loadingRequest;
2488 callOnMainThread([strongSelf, strongRequest] {
2489 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2491 callback->didCancelLoadingRequest(strongRequest.get());
2495 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2497 m_callback = callback;
2502 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
2503 @implementation WebCoreAVFPullDelegate
2504 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback
2506 self = [super init];
2508 m_callback = callback;
2512 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC *)callback
2514 m_callback = callback;
2517 - (void)outputMediaDataWillChange:(AVPlayerItemVideoOutput *)output
2520 m_callback->outputMediaDataWillChange(output);
2523 - (void)outputSequenceWasFlushed:(AVPlayerItemVideoOutput *)output
2525 UNUSED_PARAM(output);