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)
29 #import "MediaPlayerPrivateAVFoundationObjC.h"
31 #import "AVTrackPrivateAVFObjCImpl.h"
32 #import "AudioTrackPrivateAVFObjC.h"
33 #import "AuthenticationChallenge.h"
34 #import "BlockExceptions.h"
35 #import "CDMSessionAVFoundationObjC.h"
36 #import "ExceptionCodePlaceholder.h"
37 #import "FloatConversion.h"
38 #import "FloatConversion.h"
40 #import "GraphicsContext.h"
41 #import "GraphicsContextCG.h"
42 #import "InbandMetadataTextTrackPrivateAVF.h"
43 #import "InbandTextTrackPrivateAVFObjC.h"
44 #import "InbandTextTrackPrivateLegacyAVFObjC.h"
45 #import "OutOfBandTextTrackPrivateAVF.h"
48 #import "PlatformTimeRanges.h"
49 #import "SecurityOrigin.h"
50 #import "SerializedPlatformRepresentationMac.h"
51 #import "SoftLinking.h"
52 #import "TextTrackRepresentation.h"
54 #import "VideoTrackPrivateAVFObjC.h"
55 #import "WebCoreAVFResourceLoader.h"
56 #import "WebCoreSystemInterface.h"
57 #import <objc/runtime.h>
58 #import <runtime/DataView.h>
59 #import <runtime/JSCInlines.h>
60 #import <runtime/TypedArrayInlines.h>
61 #import <runtime/Uint16Array.h>
62 #import <runtime/Uint32Array.h>
63 #import <runtime/Uint8Array.h>
64 #import <wtf/CurrentTime.h>
65 #import <wtf/Functional.h>
66 #import <wtf/NeverDestroyed.h>
67 #import <wtf/text/CString.h>
68 #import <wtf/text/StringBuilder.h>
70 #if ENABLE(AVF_CAPTIONS)
71 #include "TextTrack.h"
74 #import <Foundation/NSGeometry.h>
75 #import <AVFoundation/AVFoundation.h>
77 #import <CoreImage/CoreImage.h>
79 #import <QuartzCore/CoreImage.h>
81 #import <CoreMedia/CoreMedia.h>
84 #import <CoreVideo/CoreVideo.h>
85 #import <VideoToolbox/VideoToolbox.h>
88 #if ENABLE(AVF_CAPTIONS)
89 // Note: This must be defined before our SOFT_LINK macros:
90 @class AVMediaSelectionOption;
91 @interface AVMediaSelectionOption (OutOfBandExtensions)
92 @property (nonatomic, readonly) NSString* outOfBandSource;
93 @property (nonatomic, readonly) NSString* outOfBandIdentifier;
99 @interface AVPlayerItem (WebKitExtensions)
100 @property (nonatomic, copy) NSString* dataYouTubeID;
104 typedef AVPlayerItem AVPlayerItemType;
105 typedef AVMetadataItem AVMetadataItemType;
107 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
108 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreMedia)
109 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreImage)
110 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreVideo)
112 #if USE(VIDEOTOOLBOX)
113 SOFT_LINK_FRAMEWORK_OPTIONAL(VideoToolbox)
116 SOFT_LINK(CoreMedia, CMTimeCompare, int32_t, (CMTime time1, CMTime time2), (time1, time2))
117 SOFT_LINK(CoreMedia, CMTimeMakeWithSeconds, CMTime, (Float64 seconds, int32_t preferredTimeScale), (seconds, preferredTimeScale))
118 SOFT_LINK(CoreMedia, CMTimeGetSeconds, Float64, (CMTime time), (time))
119 SOFT_LINK(CoreMedia, CMTimeRangeGetEnd, CMTime, (CMTimeRange range), (range))
121 SOFT_LINK(CoreVideo, CVPixelBufferGetWidth, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
122 SOFT_LINK(CoreVideo, CVPixelBufferGetHeight, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
123 SOFT_LINK(CoreVideo, CVPixelBufferGetBaseAddress, void*, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
124 SOFT_LINK(CoreVideo, CVPixelBufferGetBytesPerRow, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
125 SOFT_LINK(CoreVideo, CVPixelBufferGetDataSize, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
126 SOFT_LINK(CoreVideo, CVPixelBufferGetPixelFormatType, OSType, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
127 SOFT_LINK(CoreVideo, CVPixelBufferLockBaseAddress, CVReturn, (CVPixelBufferRef pixelBuffer, CVOptionFlags lockFlags), (pixelBuffer, lockFlags))
128 SOFT_LINK(CoreVideo, CVPixelBufferUnlockBaseAddress, CVReturn, (CVPixelBufferRef pixelBuffer, CVOptionFlags lockFlags), (pixelBuffer, lockFlags))
130 #if USE(VIDEOTOOLBOX)
131 SOFT_LINK(VideoToolbox, VTPixelTransferSessionCreate, OSStatus, (CFAllocatorRef allocator, VTPixelTransferSessionRef *pixelTransferSessionOut), (allocator, pixelTransferSessionOut))
132 SOFT_LINK(VideoToolbox, VTPixelTransferSessionTransferImage, OSStatus, (VTPixelTransferSessionRef session, CVPixelBufferRef sourceBuffer, CVPixelBufferRef destinationBuffer), (session, sourceBuffer, destinationBuffer))
135 SOFT_LINK_CLASS(AVFoundation, AVPlayer)
136 SOFT_LINK_CLASS(AVFoundation, AVPlayerItem)
137 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemVideoOutput)
138 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
139 SOFT_LINK_CLASS(AVFoundation, AVURLAsset)
140 SOFT_LINK_CLASS(AVFoundation, AVAssetImageGenerator)
141 SOFT_LINK_CLASS(AVFoundation, AVMetadataItem)
143 SOFT_LINK_CLASS(CoreImage, CIContext)
144 SOFT_LINK_CLASS(CoreImage, CIImage)
146 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicVisual, NSString *)
147 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicAudible, NSString *)
148 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeClosedCaption, NSString *)
149 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeVideo, NSString *)
150 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
151 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeMetadata, NSString *)
152 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemDidPlayToEndTimeNotification, NSString *)
153 SOFT_LINK_POINTER(AVFoundation, AVURLAssetInheritURIQueryComponentFromReferencingURIKey, NSString *)
154 SOFT_LINK_POINTER(AVFoundation, AVAssetImageGeneratorApertureModeCleanAperture, NSString *)
155 SOFT_LINK_POINTER(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *)
156 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *)
157 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspectFill, NSString *)
158 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResize, NSString *)
159 SOFT_LINK_POINTER(CoreVideo, kCVPixelBufferPixelFormatTypeKey, NSString *)
161 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetClientBundleIdentifierKey, NSString *)
163 SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime)
165 #define AVPlayer getAVPlayerClass()
166 #define AVPlayerItem getAVPlayerItemClass()
167 #define AVPlayerLayer getAVPlayerLayerClass()
168 #define AVURLAsset getAVURLAssetClass()
169 #define AVAssetImageGenerator getAVAssetImageGeneratorClass()
170 #define AVMetadataItem getAVMetadataItemClass()
172 #define AVMediaCharacteristicVisual getAVMediaCharacteristicVisual()
173 #define AVMediaCharacteristicAudible getAVMediaCharacteristicAudible()
174 #define AVMediaTypeClosedCaption getAVMediaTypeClosedCaption()
175 #define AVMediaTypeVideo getAVMediaTypeVideo()
176 #define AVMediaTypeAudio getAVMediaTypeAudio()
177 #define AVMediaTypeMetadata getAVMediaTypeMetadata()
178 #define AVPlayerItemDidPlayToEndTimeNotification getAVPlayerItemDidPlayToEndTimeNotification()
179 #define AVURLAssetInheritURIQueryComponentFromReferencingURIKey getAVURLAssetInheritURIQueryComponentFromReferencingURIKey()
180 #define AVURLAssetClientBundleIdentifierKey getAVURLAssetClientBundleIdentifierKey()
181 #define AVAssetImageGeneratorApertureModeCleanAperture getAVAssetImageGeneratorApertureModeCleanAperture()
182 #define AVURLAssetReferenceRestrictionsKey getAVURLAssetReferenceRestrictionsKey()
183 #define AVLayerVideoGravityResizeAspect getAVLayerVideoGravityResizeAspect()
184 #define AVLayerVideoGravityResizeAspectFill getAVLayerVideoGravityResizeAspectFill()
185 #define AVLayerVideoGravityResize getAVLayerVideoGravityResize()
186 #define kCVPixelBufferPixelFormatTypeKey getkCVPixelBufferPixelFormatTypeKey()
188 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
189 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
190 typedef AVMediaSelectionOption AVMediaSelectionOptionType;
192 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemLegibleOutput)
193 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionGroup)
194 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionOption)
196 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicLegible, NSString *)
197 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeSubtitle, NSString *)
198 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicContainsOnlyForcedSubtitles, NSString *)
199 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly, NSString *)
201 #define AVPlayerItemLegibleOutput getAVPlayerItemLegibleOutputClass()
202 #define AVMediaSelectionGroup getAVMediaSelectionGroupClass()
203 #define AVMediaSelectionOption getAVMediaSelectionOptionClass()
204 #define AVMediaCharacteristicLegible getAVMediaCharacteristicLegible()
205 #define AVMediaTypeSubtitle getAVMediaTypeSubtitle()
206 #define AVMediaCharacteristicContainsOnlyForcedSubtitles getAVMediaCharacteristicContainsOnlyForcedSubtitles()
207 #define AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly getAVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly()
210 #if ENABLE(AVF_CAPTIONS)
211 SOFT_LINK_POINTER(AVFoundation, AVURLAssetOutOfBandAlternateTracksKey, NSString*)
212 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackDisplayNameKey, NSString*)
213 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackExtendedLanguageTagKey, NSString*)
214 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIsDefaultKey, NSString*)
215 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackMediaCharactersticsKey, NSString*)
216 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIdentifierKey, NSString*)
217 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackSourceKey, NSString*)
218 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, NSString*)
219 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, NSString*)
221 #define AVURLAssetOutOfBandAlternateTracksKey getAVURLAssetOutOfBandAlternateTracksKey()
222 #define AVOutOfBandAlternateTrackDisplayNameKey getAVOutOfBandAlternateTrackDisplayNameKey()
223 #define AVOutOfBandAlternateTrackExtendedLanguageTagKey getAVOutOfBandAlternateTrackExtendedLanguageTagKey()
224 #define AVOutOfBandAlternateTrackIsDefaultKey getAVOutOfBandAlternateTrackIsDefaultKey()
225 #define AVOutOfBandAlternateTrackMediaCharactersticsKey getAVOutOfBandAlternateTrackMediaCharactersticsKey()
226 #define AVOutOfBandAlternateTrackIdentifierKey getAVOutOfBandAlternateTrackIdentifierKey()
227 #define AVOutOfBandAlternateTrackSourceKey getAVOutOfBandAlternateTrackSourceKey()
228 #define AVMediaCharacteristicDescribesMusicAndSoundForAccessibility getAVMediaCharacteristicDescribesMusicAndSoundForAccessibility()
229 #define AVMediaCharacteristicTranscribesSpokenDialogForAccessibility getAVMediaCharacteristicTranscribesSpokenDialogForAccessibility()
232 #if ENABLE(DATACUE_VALUE)
233 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceQuickTimeUserData, NSString*)
234 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceISOUserData, NSString*)
235 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceQuickTimeMetadata, NSString*)
236 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceiTunes, NSString*)
237 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceID3, NSString*)
239 #define AVMetadataKeySpaceQuickTimeUserData getAVMetadataKeySpaceQuickTimeUserData()
240 #define AVMetadataKeySpaceISOUserData getAVMetadataKeySpaceISOUserData()
241 #define AVMetadataKeySpaceQuickTimeMetadata getAVMetadataKeySpaceQuickTimeMetadata()
242 #define AVMetadataKeySpaceiTunes getAVMetadataKeySpaceiTunes()
243 #define AVMetadataKeySpaceID3 getAVMetadataKeySpaceID3()
246 #define kCMTimeZero getkCMTimeZero()
248 using namespace WebCore;
250 enum MediaPlayerAVFoundationObservationContext {
251 MediaPlayerAVFoundationObservationContextPlayerItem,
252 MediaPlayerAVFoundationObservationContextPlayerItemTrack,
253 MediaPlayerAVFoundationObservationContextPlayer,
254 MediaPlayerAVFoundationObservationContextAVPlayerLayer,
257 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
258 @interface WebCoreAVFMovieObserver : NSObject <AVPlayerItemLegibleOutputPushDelegate>
260 @interface WebCoreAVFMovieObserver : NSObject
263 MediaPlayerPrivateAVFoundationObjC* m_callback;
264 int m_delayCallbacks;
266 -(id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
268 -(void)metadataLoaded;
269 -(void)didEnd:(NSNotification *)notification;
270 -(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context;
271 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
272 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime;
273 - (void)outputSequenceWasFlushed:(id)output;
277 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
278 @interface WebCoreAVFLoaderDelegate : NSObject<AVAssetResourceLoaderDelegate> {
279 MediaPlayerPrivateAVFoundationObjC* m_callback;
281 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
282 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
283 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
287 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
288 @interface WebCoreAVFPullDelegate : NSObject<AVPlayerItemOutputPullDelegate> {
289 MediaPlayerPrivateAVFoundationObjC *m_callback;
290 dispatch_semaphore_t m_semaphore;
292 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback;
293 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
294 - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender;
295 - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output;
301 static NSArray *assetMetadataKeyNames();
302 static NSArray *itemKVOProperties();
303 static NSArray* assetTrackMetadataKeyNames();
306 static const char *boolString(bool val)
308 return val ? "true" : "false";
312 #if ENABLE(ENCRYPTED_MEDIA_V2)
313 typedef HashMap<MediaPlayer*, MediaPlayerPrivateAVFoundationObjC*> PlayerToPrivateMapType;
314 static PlayerToPrivateMapType& playerToPrivateMap()
316 DEPRECATED_DEFINE_STATIC_LOCAL(PlayerToPrivateMapType, map, ());
321 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
322 static dispatch_queue_t globalLoaderDelegateQueue()
324 static dispatch_queue_t globalQueue;
325 static dispatch_once_t onceToken;
326 dispatch_once(&onceToken, ^{
327 globalQueue = dispatch_queue_create("WebCoreAVFLoaderDelegate queue", DISPATCH_QUEUE_SERIAL);
333 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
334 static dispatch_queue_t globalPullDelegateQueue()
336 static dispatch_queue_t globalQueue;
337 static dispatch_once_t onceToken;
338 dispatch_once(&onceToken, ^{
339 globalQueue = dispatch_queue_create("WebCoreAVFPullDelegate queue", DISPATCH_QUEUE_SERIAL);
345 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateAVFoundationObjC::create(MediaPlayer* player)
347 return adoptPtr(new MediaPlayerPrivateAVFoundationObjC(player));
350 void MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(MediaEngineRegistrar registrar)
353 registrar(create, getSupportedTypes, supportsType, 0, 0, 0, supportsKeySystem);
356 MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC(MediaPlayer* player)
357 : MediaPlayerPrivateAVFoundation(player)
358 , m_weakPtrFactory(this)
360 , m_videoFullscreenGravity(MediaPlayer::VideoGravityResizeAspect)
362 , m_objcObserver(adoptNS([[WebCoreAVFMovieObserver alloc] initWithCallback:this]))
363 , m_videoFrameHasDrawn(false)
364 , m_haveCheckedPlayability(false)
365 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
366 , m_videoOutputDelegate(adoptNS([[WebCoreAVFPullDelegate alloc] initWithCallback:this]))
367 , m_videoOutputSemaphore(nullptr)
369 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
370 , m_loaderDelegate(adoptNS([[WebCoreAVFLoaderDelegate alloc] initWithCallback:this]))
372 , m_currentTextTrack(0)
373 , m_cachedDuration(MediaPlayer::invalidTime())
375 , m_cachedTotalBytes(0)
376 , m_pendingStatusChanges(0)
377 , m_cachedItemStatus(MediaPlayerAVPlayerItemStatusDoesNotExist)
378 , m_cachedLikelyToKeepUp(false)
379 , m_cachedBufferEmpty(false)
380 , m_cachedBufferFull(false)
381 , m_cachedHasEnabledAudio(false)
382 , m_shouldBufferData(true)
383 , m_cachedIsReadyForDisplay(false)
384 #if ENABLE(IOS_AIRPLAY)
385 , m_allowsWirelessVideoPlayback(true)
388 #if ENABLE(ENCRYPTED_MEDIA_V2)
389 playerToPrivateMap().set(player, this);
393 MediaPlayerPrivateAVFoundationObjC::~MediaPlayerPrivateAVFoundationObjC()
395 #if ENABLE(ENCRYPTED_MEDIA_V2)
396 playerToPrivateMap().remove(player());
398 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
399 [m_loaderDelegate.get() setCallback:0];
400 [[m_avAsset.get() resourceLoader] setDelegate:nil queue:0];
402 for (auto& pair : m_resourceLoaderMap)
403 pair.value->invalidate();
405 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
406 [m_videoOutputDelegate setCallback:0];
407 [m_videoOutput setDelegate:nil queue:0];
408 if (m_videoOutputSemaphore)
409 dispatch_release(m_videoOutputSemaphore);
414 void MediaPlayerPrivateAVFoundationObjC::cancelLoad()
416 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::cancelLoad(%p)", this);
417 tearDownVideoRendering();
419 [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
420 [m_objcObserver.get() disconnect];
422 // Tell our observer to do nothing when our cancellation of pending loading calls its completion handler.
423 setIgnoreLoadStateChanges(true);
425 [m_avAsset.get() cancelLoading];
431 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
432 if (m_legibleOutput) {
434 [m_avPlayerItem.get() removeOutput:m_legibleOutput.get()];
435 m_legibleOutput = nil;
439 if (m_avPlayerItem) {
440 for (NSString *keyName in itemKVOProperties())
441 [m_avPlayerItem.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
443 m_avPlayerItem = nil;
447 [m_avPlayer.get() removeTimeObserver:m_timeObserver.get()];
448 m_timeObserver = nil;
449 [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"rate"];
450 #if ENABLE(IOS_AIRPLAY)
451 [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"externalPlaybackActive"];
456 // Reset cached properties
457 m_pendingStatusChanges = 0;
458 m_cachedItemStatus = MediaPlayerAVPlayerItemStatusDoesNotExist;
459 m_cachedSeekableRanges = nullptr;
460 m_cachedLoadedRanges = nullptr;
461 m_cachedHasEnabledAudio = false;
462 m_cachedPresentationSize = FloatSize();
463 m_cachedDuration = 0;
465 for (AVPlayerItemTrack *track in m_cachedTracks.get())
466 [track removeObserver:m_objcObserver.get() forKeyPath:@"enabled"];
467 m_cachedTracks = nullptr;
469 setIgnoreLoadStateChanges(false);
472 bool MediaPlayerPrivateAVFoundationObjC::hasLayerRenderer() const
477 bool MediaPlayerPrivateAVFoundationObjC::hasContextRenderer() const
479 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
483 return m_imageGenerator;
486 void MediaPlayerPrivateAVFoundationObjC::createContextVideoRenderer()
488 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
491 createImageGenerator();
495 void MediaPlayerPrivateAVFoundationObjC::createImageGenerator()
497 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageGenerator(%p)", this);
499 if (!m_avAsset || m_imageGenerator)
502 m_imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:m_avAsset.get()];
504 [m_imageGenerator.get() setApertureMode:AVAssetImageGeneratorApertureModeCleanAperture];
505 [m_imageGenerator.get() setAppliesPreferredTrackTransform:YES];
506 [m_imageGenerator.get() setRequestedTimeToleranceBefore:kCMTimeZero];
507 [m_imageGenerator.get() setRequestedTimeToleranceAfter:kCMTimeZero];
509 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageGenerator(%p) - returning %p", this, m_imageGenerator.get());
512 void MediaPlayerPrivateAVFoundationObjC::destroyContextVideoRenderer()
514 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
515 destroyVideoOutput();
517 destroyImageGenerator();
520 void MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator()
522 if (!m_imageGenerator)
525 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator(%p) - destroying %p", this, m_imageGenerator.get());
527 m_imageGenerator = 0;
530 void MediaPlayerPrivateAVFoundationObjC::createVideoLayer()
532 if (!m_avPlayer || m_videoLayer)
535 auto weakThis = createWeakPtr();
536 callOnMainThread([this, weakThis] {
540 if (!m_avPlayer || m_videoLayer)
543 m_videoLayer = adoptNS([[AVPlayerLayer alloc] init]);
544 [m_videoLayer.get() setPlayer:m_avPlayer.get()];
545 [m_videoLayer.get() setBackgroundColor:cachedCGColor(Color::black, ColorSpaceDeviceRGB)];
547 [m_videoLayer.get() setName:@"MediaPlayerPrivate AVPlayerLayer"];
549 [m_videoLayer.get() addObserver:m_objcObserver.get() forKeyPath:@"readyForDisplay" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextAVPlayerLayer];
550 updateVideoLayerGravity();
551 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoLayer(%p) - returning %p", this, m_videoLayer.get());
554 if (m_videoFullscreenLayer) {
555 [m_videoLayer setFrame:CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height())];
556 [m_videoFullscreenLayer insertSublayer:m_videoLayer.get() atIndex:0];
559 player()->mediaPlayerClient()->mediaPlayerRenderingModeChanged(player());
563 void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer()
568 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer(%p) - destroying %p", this, m_videoLayer.get());
570 [m_videoLayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"readyForDisplay"];
571 [m_videoLayer.get() setPlayer:nil];
574 if (m_videoFullscreenLayer)
575 [m_videoLayer removeFromSuperlayer];
581 bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
583 if (currentRenderingMode() == MediaRenderingToLayer)
584 return m_cachedIsReadyForDisplay;
586 return m_videoFrameHasDrawn;
589 #if ENABLE(AVF_CAPTIONS)
590 static const NSArray* mediaDescriptionForKind(PlatformTextTrack::TrackKind kind)
592 // FIXME: Match these to correct types:
593 if (kind == PlatformTextTrack::Caption)
594 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
596 if (kind == PlatformTextTrack::Subtitle)
597 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
599 if (kind == PlatformTextTrack::Description)
600 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, nil];
602 if (kind == PlatformTextTrack::Forced)
603 return [NSArray arrayWithObjects: AVMediaCharacteristicContainsOnlyForcedSubtitles, nil];
605 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
608 void MediaPlayerPrivateAVFoundationObjC::notifyTrackModeChanged()
613 void MediaPlayerPrivateAVFoundationObjC::synchronizeTextTrackState()
615 const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
617 for (auto& textTrack : m_textTracks) {
618 if (textTrack->textTrackCategory() != InbandTextTrackPrivateAVF::OutOfBand)
621 RefPtr<OutOfBandTextTrackPrivateAVF> trackPrivate = static_cast<OutOfBandTextTrackPrivateAVF*>(textTrack.get());
622 RetainPtr<AVMediaSelectionOptionType> currentOption = trackPrivate->mediaSelectionOption();
624 for (auto& track : outOfBandTrackSources) {
625 RetainPtr<CFStringRef> uniqueID = String::number(track->uniqueId()).createCFString();
627 if (![[currentOption.get() outOfBandIdentifier] isEqual: reinterpret_cast<const NSString*>(uniqueID.get())])
630 InbandTextTrackPrivate::Mode mode = InbandTextTrackPrivate::Hidden;
631 if (track->mode() == PlatformTextTrack::Hidden)
632 mode = InbandTextTrackPrivate::Hidden;
633 else if (track->mode() == PlatformTextTrack::Disabled)
634 mode = InbandTextTrackPrivate::Disabled;
635 else if (track->mode() == PlatformTextTrack::Showing)
636 mode = InbandTextTrackPrivate::Showing;
638 textTrack->setMode(mode);
645 void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const String& url)
650 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(%p)", this);
652 setDelayCallbacks(true);
654 RetainPtr<NSMutableDictionary> options = adoptNS([[NSMutableDictionary alloc] init]);
656 [options.get() setObject:[NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote] forKey:AVURLAssetReferenceRestrictionsKey];
658 RetainPtr<NSMutableDictionary> headerFields = adoptNS([[NSMutableDictionary alloc] init]);
660 String referrer = player()->referrer();
661 if (!referrer.isEmpty())
662 [headerFields.get() setObject:referrer forKey:@"Referer"];
664 String userAgent = player()->userAgent();
665 if (!userAgent.isEmpty())
666 [headerFields.get() setObject:userAgent forKey:@"User-Agent"];
668 if ([headerFields.get() count])
669 [options.get() setObject:headerFields.get() forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
671 if (player()->doesHaveAttribute("x-itunes-inherit-uri-query-component"))
672 [options.get() setObject: [NSNumber numberWithBool: TRUE] forKey: AVURLAssetInheritURIQueryComponentFromReferencingURIKey];
674 String identifier = player()->sourceApplicationIdentifier();
675 if (!identifier.isEmpty() && AVURLAssetClientBundleIdentifierKey)
676 [options setObject:identifier forKey:AVURLAssetClientBundleIdentifierKey];
678 #if ENABLE(AVF_CAPTIONS)
679 const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
680 if (!outOfBandTrackSources.isEmpty()) {
681 NSMutableArray* outOfBandTracks = [[NSMutableArray alloc] init];
682 for (auto& trackSource : outOfBandTrackSources) {
683 RetainPtr<CFStringRef> label = trackSource->label().createCFString();
684 RetainPtr<CFStringRef> language = trackSource->language().createCFString();
685 RetainPtr<CFStringRef> uniqueID = String::number(trackSource->uniqueId()).createCFString();
686 RetainPtr<CFStringRef> url = trackSource->url().createCFString();
687 [outOfBandTracks addObject:
688 [NSDictionary dictionaryWithObjectsAndKeys:
689 reinterpret_cast<const NSString*>(label.get()), AVOutOfBandAlternateTrackDisplayNameKey,
690 reinterpret_cast<const NSString*>(language.get()), AVOutOfBandAlternateTrackExtendedLanguageTagKey,
691 [NSNumber numberWithBool: (trackSource->isDefault() ? YES : NO)], AVOutOfBandAlternateTrackIsDefaultKey,
692 reinterpret_cast<const NSString*>(uniqueID.get()), AVOutOfBandAlternateTrackIdentifierKey,
693 reinterpret_cast<const NSString*>(url.get()), AVOutOfBandAlternateTrackSourceKey,
694 mediaDescriptionForKind(trackSource->kind()), AVOutOfBandAlternateTrackMediaCharactersticsKey,
698 [options.get() setObject: outOfBandTracks forKey: AVURLAssetOutOfBandAlternateTracksKey];
702 NSURL *cocoaURL = URL(ParsedURLString, url);
703 m_avAsset = adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
705 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
706 [[m_avAsset.get() resourceLoader] setDelegate:m_loaderDelegate.get() queue:globalLoaderDelegateQueue()];
709 m_haveCheckedPlayability = false;
711 setDelayCallbacks(false);
714 void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
719 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayer(%p)", this);
721 setDelayCallbacks(true);
723 m_avPlayer = adoptNS([[AVPlayer alloc] init]);
724 [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
725 #if ENABLE(IOS_AIRPLAY)
726 [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"externalPlaybackActive" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
727 [m_avPlayer.get() setUsesExternalPlaybackWhileExternalScreenIsActive:YES];
730 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
731 [m_avPlayer.get() setAppliesMediaSelectionCriteriaAutomatically:YES];
734 #if ENABLE(IOS_AIRPLAY)
735 [m_avPlayer.get() setAllowsExternalPlayback:m_allowsWirelessVideoPlayback];
739 [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
741 setDelayCallbacks(false);
744 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
749 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem(%p)", this);
751 setDelayCallbacks(true);
753 // Create the player item so we can load media data.
754 m_avPlayerItem = adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
756 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
758 NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionPrior;
759 for (NSString *keyName in itemKVOProperties())
760 [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:options context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem];
763 [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
767 if (player()->doesHaveAttribute("data-youtube-id", &value))
768 [m_avPlayerItem.get() setDataYouTubeID: value];
771 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
772 const NSTimeInterval legibleOutputAdvanceInterval = 2;
774 m_legibleOutput = adoptNS([[AVPlayerItemLegibleOutput alloc] initWithMediaSubtypesForNativeRepresentation:[NSArray array]]);
775 [m_legibleOutput.get() setSuppressesPlayerRendering:YES];
777 [m_legibleOutput.get() setDelegate:m_objcObserver.get() queue:dispatch_get_main_queue()];
778 [m_legibleOutput.get() setAdvanceIntervalForDelegateInvocation:legibleOutputAdvanceInterval];
779 [m_legibleOutput.get() setTextStylingResolution:AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly];
780 [m_avPlayerItem.get() addOutput:m_legibleOutput.get()];
783 setDelayCallbacks(false);
786 void MediaPlayerPrivateAVFoundationObjC::checkPlayability()
788 if (m_haveCheckedPlayability)
790 m_haveCheckedPlayability = true;
792 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::checkPlayability(%p)", this);
793 auto weakThis = createWeakPtr();
795 [m_avAsset.get() loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"playable"] completionHandler:^{
796 callOnMainThread([weakThis] {
798 weakThis->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
803 void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata()
805 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata(%p) - requesting metadata loading", this);
807 dispatch_group_t metadataLoadingGroup = dispatch_group_create();
808 dispatch_group_enter(metadataLoadingGroup);
809 auto weakThis = createWeakPtr();
810 [m_avAsset.get() loadValuesAsynchronouslyForKeys:assetMetadataKeyNames() completionHandler:^{
812 callOnMainThread([weakThis, metadataLoadingGroup] {
813 if (weakThis && [weakThis->m_avAsset.get() statusOfValueForKey:@"tracks" error:nil] == AVKeyValueStatusLoaded) {
814 for (AVAssetTrack *track in [weakThis->m_avAsset.get() tracks]) {
815 dispatch_group_enter(metadataLoadingGroup);
816 [track loadValuesAsynchronouslyForKeys:assetTrackMetadataKeyNames() completionHandler:^{
817 dispatch_group_leave(metadataLoadingGroup);
821 dispatch_group_leave(metadataLoadingGroup);
825 dispatch_group_notify(metadataLoadingGroup, dispatch_get_main_queue(), ^{
826 callOnMainThread([weakThis] {
828 [weakThis->m_objcObserver.get() metadataLoaded];
831 dispatch_release(metadataLoadingGroup);
835 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::playerItemStatus() const
838 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
840 if (m_cachedItemStatus == AVPlayerItemStatusUnknown)
841 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
842 if (m_cachedItemStatus == AVPlayerItemStatusFailed)
843 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
844 if (m_cachedLikelyToKeepUp)
845 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
846 if (m_cachedBufferFull)
847 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
848 if (m_cachedBufferEmpty)
849 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
851 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
854 PlatformMedia MediaPlayerPrivateAVFoundationObjC::platformMedia() const
856 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformMedia(%p)", this);
858 pm.type = PlatformMedia::AVFoundationMediaPlayerType;
859 pm.media.avfMediaPlayer = m_avPlayer.get();
863 PlatformLayer* MediaPlayerPrivateAVFoundationObjC::platformLayer() const
865 return m_videoLayer.get();
869 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenLayer(PlatformLayer* videoFullscreenLayer)
871 if (m_videoFullscreenLayer == videoFullscreenLayer)
874 if (m_videoFullscreenLayer)
875 [m_videoLayer removeFromSuperlayer];
877 m_videoFullscreenLayer = videoFullscreenLayer;
879 if (m_videoFullscreenLayer && m_videoLayer) {
880 CGRect frame = CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height());
881 [m_videoLayer setFrame:frame];
882 [m_videoFullscreenLayer insertSublayer:m_videoLayer.get() atIndex:0];
885 if (m_videoFullscreenLayer && m_textTrackRepresentationLayer) {
886 syncTextTrackBounds();
887 [m_videoFullscreenLayer addSublayer:m_textTrackRepresentationLayer.get()];
891 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenFrame(FloatRect frame)
893 m_videoFullscreenFrame = frame;
894 if (!m_videoFullscreenLayer)
898 [m_videoLayer setFrame:CGRectMake(0, 0, frame.width(), frame.height())];
900 syncTextTrackBounds();
903 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
905 m_videoFullscreenGravity = gravity;
909 NSString *videoGravity = AVLayerVideoGravityResizeAspect;
910 if (gravity == MediaPlayer::VideoGravityResize)
911 videoGravity = AVLayerVideoGravityResize;
912 else if (gravity == MediaPlayer::VideoGravityResizeAspect)
913 videoGravity = AVLayerVideoGravityResizeAspect;
914 else if (gravity == MediaPlayer::VideoGravityResizeAspectFill)
915 videoGravity = AVLayerVideoGravityResizeAspectFill;
917 ASSERT_NOT_REACHED();
919 [m_videoLayer setVideoGravity:videoGravity];
922 NSArray *MediaPlayerPrivateAVFoundationObjC::timedMetadata() const
924 if (m_currentMetaData)
925 return m_currentMetaData.get();
929 String MediaPlayerPrivateAVFoundationObjC::accessLog() const
932 return emptyString();
934 AVPlayerItemAccessLog *log = [m_avPlayerItem.get() accessLog];
935 RetainPtr<NSString> logString = adoptNS([[NSString alloc] initWithData:[log extendedLogData] encoding:[log extendedLogDataStringEncoding]]);
937 return logString.get();
940 String MediaPlayerPrivateAVFoundationObjC::errorLog() const
943 return emptyString();
945 AVPlayerItemErrorLog *log = [m_avPlayerItem.get() errorLog];
946 RetainPtr<NSString> logString = adoptNS([[NSString alloc] initWithData:[log extendedLogData] encoding:[log extendedLogDataStringEncoding]]);
948 return logString.get();
952 void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
954 [CATransaction begin];
955 [CATransaction setDisableActions:YES];
957 [m_videoLayer.get() setHidden:!isVisible];
958 [CATransaction commit];
961 void MediaPlayerPrivateAVFoundationObjC::platformPlay()
963 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformPlay(%p)", this);
964 if (!metaDataAvailable())
967 setDelayCallbacks(true);
968 m_cachedRate = requestedRate();
969 [m_avPlayer.get() setRate:requestedRate()];
970 setDelayCallbacks(false);
973 void MediaPlayerPrivateAVFoundationObjC::platformPause()
975 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformPause(%p)", this);
976 if (!metaDataAvailable())
979 setDelayCallbacks(true);
981 [m_avPlayer.get() setRate:0];
982 setDelayCallbacks(false);
985 float MediaPlayerPrivateAVFoundationObjC::platformDuration() const
987 // Do not ask the asset for duration before it has been loaded or it will fetch the
988 // answer synchronously.
989 if (!m_avAsset || assetStatus() < MediaPlayerAVAssetStatusLoaded)
990 return MediaPlayer::invalidTime();
994 // Check the AVItem if we have one and it has loaded duration, some assets never report duration.
995 if (m_avPlayerItem && playerItemStatus() >= MediaPlayerAVPlayerItemStatusReadyToPlay)
996 cmDuration = [m_avPlayerItem.get() duration];
998 cmDuration= [m_avAsset.get() duration];
1000 if (CMTIME_IS_NUMERIC(cmDuration))
1001 return narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
1003 if (CMTIME_IS_INDEFINITE(cmDuration)) {
1004 return std::numeric_limits<float>::infinity();
1007 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformDuration(%p) - invalid duration, returning %.0f", this, MediaPlayer::invalidTime());
1008 return MediaPlayer::invalidTime();
1011 float MediaPlayerPrivateAVFoundationObjC::currentTime() const
1013 if (!metaDataAvailable() || !m_avPlayerItem)
1016 CMTime itemTime = [m_avPlayerItem.get() currentTime];
1017 if (CMTIME_IS_NUMERIC(itemTime))
1018 return std::max(narrowPrecisionToFloat(CMTimeGetSeconds(itemTime)), 0.0f);
1023 void MediaPlayerPrivateAVFoundationObjC::seekToTime(double time, double negativeTolerance, double positiveTolerance)
1025 // setCurrentTime generates several event callbacks, update afterwards.
1026 setDelayCallbacks(true);
1028 if (m_metadataTrack)
1029 m_metadataTrack->flushPartialCues();
1031 CMTime cmTime = CMTimeMakeWithSeconds(time, 600);
1032 CMTime cmBefore = CMTimeMakeWithSeconds(negativeTolerance, 600);
1033 CMTime cmAfter = CMTimeMakeWithSeconds(positiveTolerance, 600);
1035 auto weakThis = createWeakPtr();
1037 [m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
1038 callOnMainThread([weakThis, finished] {
1039 auto _this = weakThis.get();
1043 _this->seekCompleted(finished);
1047 setDelayCallbacks(false);
1050 void MediaPlayerPrivateAVFoundationObjC::setVolume(float volume)
1052 if (!metaDataAvailable())
1055 [m_avPlayer.get() setVolume:volume];
1058 void MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(bool closedCaptionsVisible)
1060 UNUSED_PARAM(closedCaptionsVisible);
1062 if (!metaDataAvailable())
1065 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(%p) - set to %s", this, boolString(closedCaptionsVisible));
1068 void MediaPlayerPrivateAVFoundationObjC::updateRate()
1070 setDelayCallbacks(true);
1071 m_cachedRate = requestedRate();
1072 [m_avPlayer.get() setRate:requestedRate()];
1073 setDelayCallbacks(false);
1076 float MediaPlayerPrivateAVFoundationObjC::rate() const
1078 if (!metaDataAvailable())
1081 return m_cachedRate;
1084 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateAVFoundationObjC::platformBufferedTimeRanges() const
1086 auto timeRanges = PlatformTimeRanges::create();
1088 if (!m_avPlayerItem)
1091 for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
1092 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1093 if (CMTIMERANGE_IS_VALID(timeRange) && !CMTIMERANGE_IS_EMPTY(timeRange)) {
1094 float rangeStart = narrowPrecisionToFloat(CMTimeGetSeconds(timeRange.start));
1095 float rangeEnd = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange)));
1096 timeRanges->add(rangeStart, rangeEnd);
1102 double MediaPlayerPrivateAVFoundationObjC::platformMinTimeSeekable() const
1104 if (!m_cachedSeekableRanges || ![m_cachedSeekableRanges count])
1107 double minTimeSeekable = std::numeric_limits<double>::infinity();
1108 bool hasValidRange = false;
1109 for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1110 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1111 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1114 hasValidRange = true;
1115 double startOfRange = CMTimeGetSeconds(timeRange.start);
1116 if (minTimeSeekable > startOfRange)
1117 minTimeSeekable = startOfRange;
1119 return hasValidRange ? minTimeSeekable : 0;
1122 double MediaPlayerPrivateAVFoundationObjC::platformMaxTimeSeekable() const
1124 if (!m_cachedSeekableRanges)
1125 m_cachedSeekableRanges = [m_avPlayerItem seekableTimeRanges];
1127 double maxTimeSeekable = 0;
1128 for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1129 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1130 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1133 double endOfRange = CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange));
1134 if (maxTimeSeekable < endOfRange)
1135 maxTimeSeekable = endOfRange;
1137 return maxTimeSeekable;
1140 float MediaPlayerPrivateAVFoundationObjC::platformMaxTimeLoaded() const
1142 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080
1143 // AVFoundation on Mountain Lion will occasionally not send a KVO notification
1144 // when loadedTimeRanges changes when there is no video output. In that case
1145 // update the cached value explicitly.
1146 if (!hasLayerRenderer() && !hasContextRenderer())
1147 m_cachedLoadedRanges = [m_avPlayerItem loadedTimeRanges];
1150 if (!m_cachedLoadedRanges)
1153 float maxTimeLoaded = 0;
1154 for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
1155 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1156 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1159 float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange)));
1160 if (maxTimeLoaded < endOfRange)
1161 maxTimeLoaded = endOfRange;
1164 return maxTimeLoaded;
1167 unsigned long long MediaPlayerPrivateAVFoundationObjC::totalBytes() const
1169 if (!metaDataAvailable())
1172 if (m_cachedTotalBytes)
1173 return m_cachedTotalBytes;
1175 for (AVPlayerItemTrack *thisTrack in m_cachedTracks.get())
1176 m_cachedTotalBytes += [[thisTrack assetTrack] totalSampleDataLength];
1178 return m_cachedTotalBytes;
1181 void MediaPlayerPrivateAVFoundationObjC::setAsset(id asset)
1186 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
1189 return MediaPlayerAVAssetStatusDoesNotExist;
1191 for (NSString *keyName in assetMetadataKeyNames()) {
1192 NSError *error = nil;
1193 AVKeyValueStatus keyStatus = [m_avAsset.get() statusOfValueForKey:keyName error:&error];
1196 LOG(Media, "MediaPlayerPrivateAVFoundation::assetStatus - statusOfValueForKey failed for %s, error = %s", [keyName UTF8String], [[error localizedDescription] UTF8String]);
1199 if (keyStatus < AVKeyValueStatusLoaded)
1200 return MediaPlayerAVAssetStatusLoading;// At least one key is not loaded yet.
1202 if (keyStatus == AVKeyValueStatusFailed)
1203 return MediaPlayerAVAssetStatusFailed; // At least one key could not be loaded.
1205 if (keyStatus == AVKeyValueStatusCancelled)
1206 return MediaPlayerAVAssetStatusCancelled; // Loading of at least one key was cancelled.
1209 if ([[m_avAsset.get() valueForKey:@"playable"] boolValue])
1210 return MediaPlayerAVAssetStatusPlayable;
1212 return MediaPlayerAVAssetStatusLoaded;
1215 void MediaPlayerPrivateAVFoundationObjC::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
1217 if (!metaDataAvailable() || context->paintingDisabled())
1220 setDelayCallbacks(true);
1221 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1223 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1224 if (videoOutputHasAvailableFrame())
1225 paintWithVideoOutput(context, rect);
1228 paintWithImageGenerator(context, rect);
1230 END_BLOCK_OBJC_EXCEPTIONS;
1231 setDelayCallbacks(false);
1233 m_videoFrameHasDrawn = true;
1236 void MediaPlayerPrivateAVFoundationObjC::paint(GraphicsContext* context, const IntRect& rect)
1238 if (!metaDataAvailable() || context->paintingDisabled())
1241 // We can ignore the request if we are already rendering to a layer.
1242 if (currentRenderingMode() == MediaRenderingToLayer)
1245 paintCurrentFrameInContext(context, rect);
1248 void MediaPlayerPrivateAVFoundationObjC::paintWithImageGenerator(GraphicsContext* context, const IntRect& rect)
1250 RetainPtr<CGImageRef> image = createImageForTimeInRect(currentTime(), rect);
1252 GraphicsContextStateSaver stateSaver(*context);
1253 context->translate(rect.x(), rect.y() + rect.height());
1254 context->scale(FloatSize(1.0f, -1.0f));
1255 context->setImageInterpolationQuality(InterpolationLow);
1256 IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
1257 CGContextDrawImage(context->platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
1262 static HashSet<String> mimeTypeCache()
1264 DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1265 static bool typeListInitialized = false;
1267 if (typeListInitialized)
1269 typeListInitialized = true;
1271 NSArray *types = [AVURLAsset audiovisualMIMETypes];
1272 for (NSString *mimeType in types)
1273 cache.add(mimeType);
1278 RetainPtr<CGImageRef> MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(float time, const IntRect& rect)
1280 if (!m_imageGenerator)
1281 createImageGenerator();
1282 ASSERT(m_imageGenerator);
1285 double start = monotonicallyIncreasingTime();
1288 [m_imageGenerator.get() setMaximumSize:CGSize(rect.size())];
1289 RetainPtr<CGImageRef> rawImage = adoptCF([m_imageGenerator.get() copyCGImageAtTime:CMTimeMakeWithSeconds(time, 600) actualTime:nil error:nil]);
1290 RetainPtr<CGImageRef> image = adoptCF(CGImageCreateCopyWithColorSpace(rawImage.get(), deviceRGBColorSpaceRef()));
1293 double duration = monotonicallyIncreasingTime() - start;
1294 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(%p) - creating image took %.4f", this, narrowPrecisionToFloat(duration));
1300 void MediaPlayerPrivateAVFoundationObjC::getSupportedTypes(HashSet<String>& supportedTypes)
1302 supportedTypes = mimeTypeCache();
1305 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1306 static bool keySystemIsSupported(const String& keySystem)
1308 if (equalIgnoringCase(keySystem, "com.apple.fps") || equalIgnoringCase(keySystem, "com.apple.fps.1_0"))
1314 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationObjC::supportsType(const MediaEngineSupportParameters& parameters)
1316 #if ENABLE(ENCRYPTED_MEDIA)
1317 // From: <http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-canplaytype>
1318 // In addition to the steps in the current specification, this method must run the following steps:
1320 // 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:
1321 // If keySystem is null, continue to the next step.
1322 if (!parameters.keySystem.isNull() && !parameters.keySystem.isEmpty()) {
1323 // If keySystem contains an unrecognized or unsupported Key System, return the empty string
1324 if (!keySystemIsSupported(parameters.keySystem))
1325 return MediaPlayer::IsNotSupported;
1327 // If the Key System specified by keySystem does not support decrypting the container and/or codec specified in the rest of the type string.
1328 // (AVFoundation does not provide an API which would allow us to determine this, so this is a no-op)
1331 // 2. Return "maybe" or "probably" as appropriate per the existing specification of canPlayType().
1334 #if ENABLE(MEDIA_SOURCE)
1335 if (parameters.isMediaSource)
1336 return MediaPlayer::IsNotSupported;
1339 if (!mimeTypeCache().contains(parameters.type))
1340 return MediaPlayer::IsNotSupported;
1343 // "Implementors are encouraged to return "maybe" unless the type can be confidently established as being supported or not."
1344 if (parameters.codecs.isEmpty())
1345 return MediaPlayer::MayBeSupported;
1347 NSString *typeString = [NSString stringWithFormat:@"%@; codecs=\"%@\"", (NSString *)parameters.type, (NSString *)parameters.codecs];
1348 return [AVURLAsset isPlayableExtendedMIMEType:typeString] ? MediaPlayer::IsSupported : MediaPlayer::MayBeSupported;;
1351 bool MediaPlayerPrivateAVFoundationObjC::supportsKeySystem(const String& keySystem, const String& mimeType)
1353 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1354 if (!keySystem.isEmpty()) {
1355 if (!keySystemIsSupported(keySystem))
1358 if (!mimeType.isEmpty() && !mimeTypeCache().contains(mimeType))
1364 UNUSED_PARAM(keySystem);
1365 UNUSED_PARAM(mimeType);
1370 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1371 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest* avRequest)
1373 String scheme = [[[avRequest request] URL] scheme];
1374 String keyURI = [[[avRequest request] URL] absoluteString];
1376 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1377 if (scheme == "skd") {
1378 // Create an initData with the following layout:
1379 // [4 bytes: keyURI size], [keyURI size bytes: keyURI]
1380 unsigned keyURISize = keyURI.length() * sizeof(UChar);
1381 RefPtr<ArrayBuffer> initDataBuffer = ArrayBuffer::create(4 + keyURISize, 1);
1382 RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
1383 initDataView->set<uint32_t>(0, keyURISize, true);
1385 RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, 4, keyURI.length());
1386 keyURIArray->setRange(StringView(keyURI).upconvertedCharacters(), keyURI.length() / sizeof(unsigned char), 0);
1388 #if ENABLE(ENCRYPTED_MEDIA)
1389 if (!player()->keyNeeded("com.apple.lskd", emptyString(), static_cast<const unsigned char*>(initDataBuffer->data()), initDataBuffer->byteLength()))
1390 #elif ENABLE(ENCRYPTED_MEDIA_V2)
1391 RefPtr<Uint8Array> initData = Uint8Array::create(initDataBuffer, 0, initDataBuffer->byteLength());
1392 if (!player()->keyNeeded(initData.get()))
1396 m_keyURIToRequestMap.set(keyURI, avRequest);
1401 RefPtr<WebCoreAVFResourceLoader> resourceLoader = WebCoreAVFResourceLoader::create(this, avRequest);
1402 m_resourceLoaderMap.add(avRequest, resourceLoader);
1403 resourceLoader->startLoading();
1407 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForResponseToAuthenticationChallenge(NSURLAuthenticationChallenge* nsChallenge)
1410 UNUSED_PARAM(nsChallenge);
1411 // FIXME: <rdar://problem/15799844>
1414 AuthenticationChallenge challenge(nsChallenge);
1416 return player()->shouldWaitForResponseToAuthenticationChallenge(challenge);
1420 void MediaPlayerPrivateAVFoundationObjC::didCancelLoadingRequest(AVAssetResourceLoadingRequest* avRequest)
1422 String scheme = [[[avRequest request] URL] scheme];
1424 WebCoreAVFResourceLoader* resourceLoader = m_resourceLoaderMap.get(avRequest);
1427 resourceLoader->stopLoading();
1430 void MediaPlayerPrivateAVFoundationObjC::didStopLoadingRequest(AVAssetResourceLoadingRequest *avRequest)
1432 m_resourceLoaderMap.remove(avRequest);
1436 bool MediaPlayerPrivateAVFoundationObjC::isAvailable()
1438 return AVFoundationLibrary() && CoreMediaLibrary();
1441 float MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(float timeValue) const
1443 if (!metaDataAvailable())
1446 // FIXME - impossible to implement until rdar://8721510 is fixed.
1450 void MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity()
1455 [CATransaction begin];
1456 [CATransaction setDisableActions:YES];
1457 NSString* gravity = shouldMaintainAspectRatio() ? AVLayerVideoGravityResizeAspect : AVLayerVideoGravityResize;
1458 [m_videoLayer.get() setVideoGravity:gravity];
1459 [CATransaction commit];
1462 static AVAssetTrack* firstEnabledTrack(NSArray* tracks)
1464 NSUInteger index = [tracks indexOfObjectPassingTest:^(id obj, NSUInteger, BOOL *) {
1465 return [static_cast<AVAssetTrack*>(obj) isEnabled];
1467 if (index == NSNotFound)
1469 return [tracks objectAtIndex:index];
1472 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
1474 String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
1475 m_languageOfPrimaryAudioTrack = String();
1480 setDelayCharacteristicsChangedNotification(true);
1482 bool haveCCTrack = false;
1483 bool hasCaptions = false;
1485 // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
1486 // asked about those fairly fequently.
1487 if (!m_avPlayerItem) {
1488 // We don't have a player item yet, so check with the asset because some assets support inspection
1489 // prior to becoming ready to play.
1490 AVAssetTrack* firstEnabledVideoTrack = firstEnabledTrack([m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual]);
1491 setHasVideo(firstEnabledVideoTrack);
1492 setHasAudio(firstEnabledTrack([m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicAudible]));
1493 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1494 hasCaptions = [[m_avAsset.get() tracksWithMediaType:AVMediaTypeClosedCaption] count];
1497 presentationSizeDidChange(firstEnabledVideoTrack ? IntSize(CGSizeApplyAffineTransform([firstEnabledVideoTrack naturalSize], [firstEnabledVideoTrack preferredTransform])) : IntSize());
1499 bool hasVideo = false;
1500 bool hasAudio = false;
1501 bool hasMetaData = false;
1502 for (AVPlayerItemTrack *track in m_cachedTracks.get()) {
1503 if ([track isEnabled]) {
1504 AVAssetTrack *assetTrack = [track assetTrack];
1505 NSString *mediaType = [assetTrack mediaType];
1506 if ([mediaType isEqualToString:AVMediaTypeVideo])
1508 else if ([mediaType isEqualToString:AVMediaTypeAudio])
1510 else if ([mediaType isEqualToString:AVMediaTypeClosedCaption]) {
1511 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1515 } else if ([mediaType isEqualToString:AVMediaTypeMetadata]) {
1521 // Always says we have video if the AVPlayerLayer is ready for diaplay to work around
1522 // an AVFoundation bug which causes it to sometimes claim a track is disabled even
1524 setHasVideo(hasVideo || m_cachedIsReadyForDisplay);
1526 setHasAudio(hasAudio);
1527 #if ENABLE(DATACUE_VALUE)
1529 processMetadataTrack();
1532 #if ENABLE(VIDEO_TRACK)
1533 updateAudioTracks();
1534 updateVideoTracks();
1538 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1539 if (AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia()) {
1540 hasCaptions = [[AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]] count];
1542 processMediaSelectionOptions();
1546 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT) && HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1547 if (!hasCaptions && haveCCTrack)
1548 processLegacyClosedCaptionsTracks();
1549 #elif !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1551 processLegacyClosedCaptionsTracks();
1554 setHasClosedCaptions(hasCaptions);
1556 LOG(Media, "MediaPlayerPrivateAVFoundation:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s",
1557 this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions()));
1561 if (primaryAudioTrackLanguage != languageOfPrimaryAudioTrack())
1562 characteristicsChanged();
1564 setDelayCharacteristicsChangedNotification(false);
1567 #if ENABLE(VIDEO_TRACK)
1568 template <typename RefT, typename PassRefT>
1569 void determineChangedTracksFromNewTracksAndOldItems(NSArray* tracks, NSString* trackType, Vector<RefT>& oldItems, RefT (*itemFactory)(AVPlayerItemTrack*), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
1571 RetainPtr<NSSet> newTracks = adoptNS([[NSSet alloc] initWithArray:[tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id track, NSUInteger, BOOL*){
1572 return [[[track assetTrack] mediaType] isEqualToString:trackType];
1574 RetainPtr<NSMutableSet> oldTracks = adoptNS([[NSMutableSet alloc] initWithCapacity:oldItems.size()]);
1576 typedef Vector<RefT> ItemVector;
1577 for (auto i = oldItems.begin(); i != oldItems.end(); ++i)
1578 [oldTracks addObject:(*i)->playerItemTrack()];
1580 RetainPtr<NSMutableSet> removedTracks = adoptNS([oldTracks mutableCopy]);
1581 [removedTracks minusSet:newTracks.get()];
1583 RetainPtr<NSMutableSet> addedTracks = adoptNS([newTracks mutableCopy]);
1584 [addedTracks minusSet:oldTracks.get()];
1586 ItemVector replacementItems;
1587 ItemVector addedItems;
1588 ItemVector removedItems;
1589 for (auto i = oldItems.begin(); i != oldItems.end(); ++i) {
1590 if ([removedTracks containsObject:(*i)->playerItemTrack()])
1591 removedItems.append(*i);
1593 replacementItems.append(*i);
1596 for (AVPlayerItemTrack* track in addedTracks.get())
1597 addedItems.append(itemFactory(track));
1599 replacementItems.appendVector(addedItems);
1600 oldItems.swap(replacementItems);
1602 for (auto i = removedItems.begin(); i != removedItems.end(); ++i)
1603 (player->*removedFunction)(*i);
1605 for (auto i = addedItems.begin(); i != addedItems.end(); ++i)
1606 (player->*addedFunction)(*i);
1609 void MediaPlayerPrivateAVFoundationObjC::updateAudioTracks()
1612 size_t count = m_audioTracks.size();
1615 determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeAudio, m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
1618 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::updateAudioTracks(%p) - audio track count was %lu, is %lu", this, count, m_audioTracks.size());
1622 void MediaPlayerPrivateAVFoundationObjC::updateVideoTracks()
1625 size_t count = m_videoTracks.size();
1628 determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeVideo, m_videoTracks, &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
1631 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::updateVideoTracks(%p) - video track count was %lu, is %lu", this, count, m_videoTracks.size());
1635 bool MediaPlayerPrivateAVFoundationObjC::requiresTextTrackRepresentation() const
1638 if (m_videoFullscreenLayer)
1644 void MediaPlayerPrivateAVFoundationObjC::syncTextTrackBounds()
1647 if (!m_videoFullscreenLayer || !m_textTrackRepresentationLayer)
1650 CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height());
1651 [m_textTrackRepresentationLayer setFrame:textFrame];
1655 void MediaPlayerPrivateAVFoundationObjC::setTextTrackRepresentation(TextTrackRepresentation* representation)
1658 PlatformLayer* representationLayer = representation ? representation->platformLayer() : nil;
1659 if (representationLayer == m_textTrackRepresentationLayer) {
1660 syncTextTrackBounds();
1664 if (m_textTrackRepresentationLayer)
1665 [m_textTrackRepresentationLayer removeFromSuperlayer];
1667 m_textTrackRepresentationLayer = representationLayer;
1669 if (m_videoFullscreenLayer && m_textTrackRepresentationLayer) {
1670 syncTextTrackBounds();
1671 [m_videoFullscreenLayer addSublayer:m_textTrackRepresentationLayer.get()];
1675 UNUSED_PARAM(representation);
1678 #endif // ENABLE(VIDEO_TRACK)
1680 void MediaPlayerPrivateAVFoundationObjC::sizeChanged()
1685 setNaturalSize(roundedIntSize(m_cachedPresentationSize));
1689 // FIXME: Implement for iOS in WebKit System Interface.
1690 static inline NSURL *wkAVAssetResolvedURL(AVAsset*)
1696 bool MediaPlayerPrivateAVFoundationObjC::hasSingleSecurityOrigin() const
1701 RefPtr<SecurityOrigin> resolvedOrigin = SecurityOrigin::create(URL(wkAVAssetResolvedURL(m_avAsset.get())));
1702 RefPtr<SecurityOrigin> requestedOrigin = SecurityOrigin::createFromString(assetURL());
1703 return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
1706 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1707 void MediaPlayerPrivateAVFoundationObjC::createVideoOutput()
1709 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoOutput(%p)", this);
1711 if (!m_avPlayerItem || m_videoOutput)
1714 #if USE(VIDEOTOOLBOX)
1715 NSDictionary* attributes = @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_422YpCbCr8) };
1717 NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
1720 m_videoOutput = adoptNS([[getAVPlayerItemVideoOutputClass() alloc] initWithPixelBufferAttributes:attributes]);
1721 ASSERT(m_videoOutput);
1723 [m_videoOutput setDelegate:m_videoOutputDelegate.get() queue:globalPullDelegateQueue()];
1725 [m_avPlayerItem.get() addOutput:m_videoOutput.get()];
1727 waitForVideoOutputMediaDataWillChange();
1729 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoOutput(%p) - returning %p", this, m_videoOutput.get());
1732 void MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput()
1738 [m_avPlayerItem.get() removeOutput:m_videoOutput.get()];
1739 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput(%p) - destroying %p", this, m_videoOutput.get());
1744 RetainPtr<CVPixelBufferRef> MediaPlayerPrivateAVFoundationObjC::createPixelBuffer()
1747 createVideoOutput();
1748 ASSERT(m_videoOutput);
1751 double start = monotonicallyIncreasingTime();
1754 CMTime currentTime = [m_avPlayerItem.get() currentTime];
1756 if (![m_videoOutput.get() hasNewPixelBufferForItemTime:currentTime])
1759 RetainPtr<CVPixelBufferRef> buffer = adoptCF([m_videoOutput.get() copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
1763 #if USE(VIDEOTOOLBOX)
1764 // Create a VTPixelTransferSession, if necessary, as we cannot guarantee timely delivery of ARGB pixels.
1765 if (!m_pixelTransferSession) {
1766 VTPixelTransferSessionRef session = 0;
1767 VTPixelTransferSessionCreate(kCFAllocatorDefault, &session);
1768 m_pixelTransferSession = adoptCF(session);
1771 CVPixelBufferRef outputBuffer;
1772 CVPixelBufferCreate(kCFAllocatorDefault, CVPixelBufferGetWidth(buffer.get()), CVPixelBufferGetHeight(buffer.get()), kCVPixelFormatType_32BGRA, 0, &outputBuffer);
1773 VTPixelTransferSessionTransferImage(m_pixelTransferSession.get(), buffer.get(), outputBuffer);
1774 buffer = adoptCF(outputBuffer);
1778 double duration = monotonicallyIncreasingTime() - start;
1779 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createPixelBuffer(%p) - creating buffer took %.4f", this, narrowPrecisionToFloat(duration));
1785 bool MediaPlayerPrivateAVFoundationObjC::videoOutputHasAvailableFrame()
1787 if (!m_avPlayerItem)
1794 createVideoOutput();
1796 return [m_videoOutput hasNewPixelBufferForItemTime:[m_avPlayerItem currentTime]];
1799 static const void* CVPixelBufferGetBytePointerCallback(void* info)
1801 CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1802 CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
1803 return CVPixelBufferGetBaseAddress(pixelBuffer);
1806 static void CVPixelBufferReleaseBytePointerCallback(void* info, const void*)
1808 CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1809 CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
1812 static void CVPixelBufferReleaseInfoCallback(void* info)
1814 CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1815 CFRelease(pixelBuffer);
1818 static RetainPtr<CGImageRef> createImageFromPixelBuffer(CVPixelBufferRef pixelBuffer)
1820 // pixelBuffer will be of type kCVPixelFormatType_32BGRA.
1821 ASSERT(CVPixelBufferGetPixelFormatType(pixelBuffer) == kCVPixelFormatType_32BGRA);
1823 size_t width = CVPixelBufferGetWidth(pixelBuffer);
1824 size_t height = CVPixelBufferGetHeight(pixelBuffer);
1825 size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
1826 size_t byteLength = CVPixelBufferGetDataSize(pixelBuffer);
1827 CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
1829 CFRetain(pixelBuffer); // Balanced by CVPixelBufferReleaseInfoCallback in providerCallbacks.
1830 CGDataProviderDirectCallbacks providerCallbacks = { 0, CVPixelBufferGetBytePointerCallback, CVPixelBufferReleaseBytePointerCallback, 0, CVPixelBufferReleaseInfoCallback };
1831 RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateDirect(pixelBuffer, byteLength, &providerCallbacks));
1833 return adoptCF(CGImageCreate(width, height, 8, 32, bytesPerRow, deviceRGBColorSpaceRef(), bitmapInfo, provider.get(), NULL, false, kCGRenderingIntentDefault));
1836 void MediaPlayerPrivateAVFoundationObjC::updateLastImage()
1838 RetainPtr<CVPixelBufferRef> pixelBuffer = createPixelBuffer();
1840 // Calls to copyPixelBufferForItemTime:itemTimeForDisplay: may return nil if the pixel buffer
1841 // for the requested time has already been retrieved. In this case, the last valid image (if any)
1842 // should be displayed.
1844 m_lastImage = createImageFromPixelBuffer(pixelBuffer.get());
1847 void MediaPlayerPrivateAVFoundationObjC::paintWithVideoOutput(GraphicsContext* context, const IntRect& outputRect)
1852 GraphicsContextStateSaver stateSaver(*context);
1854 IntRect imageRect(0, 0, CGImageGetWidth(m_lastImage.get()), CGImageGetHeight(m_lastImage.get()));
1856 context->drawNativeImage(m_lastImage.get(), imageRect.size(), ColorSpaceDeviceRGB, outputRect, imageRect);
1858 // If we have created an AVAssetImageGenerator in the past due to m_videoOutput not having an available
1859 // video frame, destroy it now that it is no longer needed.
1860 if (m_imageGenerator)
1861 destroyImageGenerator();
1865 PassNativeImagePtr MediaPlayerPrivateAVFoundationObjC::nativeImageForCurrentTime()
1868 return m_lastImage.get();
1871 void MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange()
1873 if (!m_videoOutputSemaphore)
1874 m_videoOutputSemaphore = dispatch_semaphore_create(0);
1876 [m_videoOutput requestNotificationOfMediaDataChangeWithAdvanceInterval:0];
1878 // Wait for 1 second.
1879 long result = dispatch_semaphore_wait(m_videoOutputSemaphore, dispatch_time(0, 1 * NSEC_PER_SEC));
1882 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange(%p) timed out", this);
1885 void MediaPlayerPrivateAVFoundationObjC::outputMediaDataWillChange(AVPlayerItemVideoOutput*)
1887 dispatch_semaphore_signal(m_videoOutputSemaphore);
1891 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1892 bool MediaPlayerPrivateAVFoundationObjC::extractKeyURIKeyIDAndCertificateFromInitData(Uint8Array* initData, String& keyURI, String& keyID, RefPtr<Uint8Array>& certificate)
1894 // initData should have the following layout:
1895 // [4 bytes: keyURI length][N bytes: keyURI][4 bytes: contentID length], [N bytes: contentID], [4 bytes: certificate length][N bytes: certificate]
1896 if (initData->byteLength() < 4)
1899 RefPtr<ArrayBuffer> initDataBuffer = initData->buffer();
1901 // Use a DataView to read uint32 values from the buffer, as Uint32Array requires the reads be aligned on 4-byte boundaries.
1902 RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
1903 uint32_t offset = 0;
1906 uint32_t keyURILength = initDataView->get<uint32_t>(offset, true, &status);
1908 if (!status || offset + keyURILength > initData->length())
1911 RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, offset, keyURILength);
1915 keyURI = String(keyURIArray->data(), keyURILength / sizeof(unsigned short));
1916 offset += keyURILength;
1918 uint32_t keyIDLength = initDataView->get<uint32_t>(offset, true, &status);
1920 if (!status || offset + keyIDLength > initData->length())
1923 RefPtr<Uint16Array> keyIDArray = Uint16Array::create(initDataBuffer, offset, keyIDLength);
1927 keyID = String(keyIDArray->data(), keyIDLength / sizeof(unsigned short));
1928 offset += keyIDLength;
1930 uint32_t certificateLength = initDataView->get<uint32_t>(offset, true, &status);
1932 if (!status || offset + certificateLength > initData->length())
1935 certificate = Uint8Array::create(initDataBuffer, offset, certificateLength);
1943 #if ENABLE(ENCRYPTED_MEDIA)
1944 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::generateKeyRequest(const String& keySystem, const unsigned char* initDataPtr, unsigned initDataLength)
1946 if (!keySystemIsSupported(keySystem))
1947 return MediaPlayer::KeySystemNotSupported;
1949 RefPtr<Uint8Array> initData = Uint8Array::create(initDataPtr, initDataLength);
1952 RefPtr<Uint8Array> certificate;
1953 if (!extractKeyURIKeyIDAndCertificateFromInitData(initData.get(), keyURI, keyID, certificate))
1954 return MediaPlayer::InvalidPlayerState;
1956 if (!m_keyURIToRequestMap.contains(keyURI))
1957 return MediaPlayer::InvalidPlayerState;
1959 String sessionID = createCanonicalUUIDString();
1961 RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_keyURIToRequestMap.get(keyURI);
1963 RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:certificate->baseAddress() length:certificate->byteLength()]);
1964 NSString* assetStr = keyID;
1965 RetainPtr<NSData> assetID = [NSData dataWithBytes: [assetStr cStringUsingEncoding:NSUTF8StringEncoding] length:[assetStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
1967 RetainPtr<NSData> keyRequest = [avRequest.get() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:assetID.get() options:nil error:&error];
1970 NSError* underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
1971 player()->keyError(keySystem, sessionID, MediaPlayerClient::DomainError, [underlyingError code]);
1972 return MediaPlayer::NoError;
1975 RefPtr<ArrayBuffer> keyRequestBuffer = ArrayBuffer::create([keyRequest.get() bytes], [keyRequest.get() length]);
1976 RefPtr<Uint8Array> keyRequestArray = Uint8Array::create(keyRequestBuffer, 0, keyRequestBuffer->byteLength());
1977 player()->keyMessage(keySystem, sessionID, keyRequestArray->data(), keyRequestArray->byteLength(), URL());
1979 // Move ownership of the AVAssetResourceLoadingRequestfrom the keyIDToRequestMap to the sessionIDToRequestMap:
1980 m_sessionIDToRequestMap.set(sessionID, avRequest);
1981 m_keyURIToRequestMap.remove(keyURI);
1983 return MediaPlayer::NoError;
1986 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::addKey(const String& keySystem, const unsigned char* keyPtr, unsigned keyLength, const unsigned char* initDataPtr, unsigned initDataLength, const String& sessionID)
1988 if (!keySystemIsSupported(keySystem))
1989 return MediaPlayer::KeySystemNotSupported;
1991 if (!m_sessionIDToRequestMap.contains(sessionID))
1992 return MediaPlayer::InvalidPlayerState;
1994 RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_sessionIDToRequestMap.get(sessionID);
1995 RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:keyPtr length:keyLength]);
1996 [[avRequest.get() dataRequest] respondWithData:keyData.get()];
1997 [avRequest.get() finishLoading];
1998 m_sessionIDToRequestMap.remove(sessionID);
2000 player()->keyAdded(keySystem, sessionID);
2002 UNUSED_PARAM(initDataPtr);
2003 UNUSED_PARAM(initDataLength);
2004 return MediaPlayer::NoError;
2007 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::cancelKeyRequest(const String& keySystem, const String& sessionID)
2009 if (!keySystemIsSupported(keySystem))
2010 return MediaPlayer::KeySystemNotSupported;
2012 if (!m_sessionIDToRequestMap.contains(sessionID))
2013 return MediaPlayer::InvalidPlayerState;
2015 m_sessionIDToRequestMap.remove(sessionID);
2016 return MediaPlayer::NoError;
2020 #if ENABLE(ENCRYPTED_MEDIA_V2)
2021 RetainPtr<AVAssetResourceLoadingRequest> MediaPlayerPrivateAVFoundationObjC::takeRequestForKeyURI(const String& keyURI)
2023 return m_keyURIToRequestMap.take(keyURI);
2026 std::unique_ptr<CDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem)
2028 if (!keySystemIsSupported(keySystem))
2031 return std::make_unique<CDMSessionAVFoundationObjC>(this);
2035 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
2036 void MediaPlayerPrivateAVFoundationObjC::processLegacyClosedCaptionsTracks()
2038 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2039 [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2042 Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
2043 for (AVPlayerItemTrack *playerItemTrack in m_cachedTracks.get()) {
2045 AVAssetTrack *assetTrack = [playerItemTrack assetTrack];
2046 if (![[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
2049 bool newCCTrack = true;
2050 for (unsigned i = removedTextTracks.size(); i > 0; --i) {
2051 if (removedTextTracks[i - 1]->textTrackCategory() != InbandTextTrackPrivateAVF::LegacyClosedCaption)
2054 RefPtr<InbandTextTrackPrivateLegacyAVFObjC> track = static_cast<InbandTextTrackPrivateLegacyAVFObjC*>(m_textTracks[i - 1].get());
2055 if (track->avPlayerItemTrack() == playerItemTrack) {
2056 removedTextTracks.remove(i - 1);
2065 m_textTracks.append(InbandTextTrackPrivateLegacyAVFObjC::create(this, playerItemTrack));
2068 processNewAndRemovedTextTracks(removedTextTracks);
2072 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2073 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForLegibleMedia()
2078 if ([m_avAsset.get() statusOfValueForKey:@"availableMediaCharacteristicsWithMediaSelectionOptions" error:NULL] != AVKeyValueStatusLoaded)
2081 return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
2084 void MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions()
2086 AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
2087 if (!legibleGroup) {
2088 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions(%p) - nil mediaSelectionGroup", this);
2092 // We enabled automatic media selection because we want alternate audio tracks to be enabled/disabled automatically,
2093 // but set the selected legible track to nil so text tracks will not be automatically configured.
2094 if (!m_textTracks.size())
2095 [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2097 Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
2098 NSArray *legibleOptions = [AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]];
2099 for (AVMediaSelectionOptionType *option in legibleOptions) {
2100 bool newTrack = true;
2101 for (unsigned i = removedTextTracks.size(); i > 0; --i) {
2102 if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
2105 RetainPtr<AVMediaSelectionOptionType> currentOption;
2106 #if ENABLE(AVF_CAPTIONS)
2107 if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand) {
2108 RefPtr<OutOfBandTextTrackPrivateAVF> track = static_cast<OutOfBandTextTrackPrivateAVF*>(removedTextTracks[i - 1].get());
2109 currentOption = track->mediaSelectionOption();
2113 RefPtr<InbandTextTrackPrivateAVFObjC> track = static_cast<InbandTextTrackPrivateAVFObjC*>(removedTextTracks[i - 1].get());
2114 currentOption = track->mediaSelectionOption();
2117 if ([currentOption.get() isEqual:option]) {
2118 removedTextTracks.remove(i - 1);
2126 #if ENABLE(AVF_CAPTIONS)
2127 if ([option outOfBandSource]) {
2128 m_textTracks.append(OutOfBandTextTrackPrivateAVF::create(this, option));
2129 m_textTracks.last()->setHasBeenReported(true); // Ignore out-of-band tracks that we passed to AVFoundation so we do not double-count them
2134 m_textTracks.append(InbandTextTrackPrivateAVFObjC::create(this, option));
2137 processNewAndRemovedTextTracks(removedTextTracks);
2140 void MediaPlayerPrivateAVFoundationObjC::processMetadataTrack()
2142 if (m_metadataTrack)
2145 m_metadataTrack = InbandMetadataTextTrackPrivateAVF::create(InbandTextTrackPrivate::Metadata, InbandTextTrackPrivate::Data);
2146 m_metadataTrack->setInBandMetadataTrackDispatchType("com.apple.streaming");
2147 player()->addTextTrack(m_metadataTrack);
2150 void MediaPlayerPrivateAVFoundationObjC::processCue(NSArray *attributedStrings, double time)
2152 if (!m_currentTextTrack)
2155 m_currentTextTrack->processCue(reinterpret_cast<CFArrayRef>(attributedStrings), time);
2158 void MediaPlayerPrivateAVFoundationObjC::flushCues()
2160 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::flushCues(%p)", this);
2162 if (!m_currentTextTrack)
2165 m_currentTextTrack->resetCueValues();
2167 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2169 void MediaPlayerPrivateAVFoundationObjC::setCurrentTextTrack(InbandTextTrackPrivateAVF *track)
2171 if (m_currentTextTrack == track)
2174 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setCurrentTextTrack(%p) - selecting track %p, language = %s", this, track, track ? track->language().string().utf8().data() : "");
2176 m_currentTextTrack = track;
2179 if (track->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
2180 [m_avPlayer.get() setClosedCaptionDisplayEnabled:YES];
2181 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2182 #if ENABLE(AVF_CAPTIONS)
2183 else if (track->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand)
2184 [m_avPlayerItem.get() selectMediaOption:static_cast<OutOfBandTextTrackPrivateAVF*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2187 [m_avPlayerItem.get() selectMediaOption:static_cast<InbandTextTrackPrivateAVFObjC*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2190 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2191 [m_avPlayerItem.get() selectMediaOption:0 inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2193 [m_avPlayer.get() setClosedCaptionDisplayEnabled:NO];
2198 String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
2200 if (!m_languageOfPrimaryAudioTrack.isNull())
2201 return m_languageOfPrimaryAudioTrack;
2203 if (!m_avPlayerItem.get())
2204 return emptyString();
2206 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2207 // If AVFoundation has an audible group, return the language of the currently selected audible option.
2208 AVMediaSelectionGroupType *audibleGroup = [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
2209 AVMediaSelectionOptionType *currentlySelectedAudibleOption = [m_avPlayerItem.get() selectedMediaOptionInMediaSelectionGroup:audibleGroup];
2210 if (currentlySelectedAudibleOption) {
2211 m_languageOfPrimaryAudioTrack = [[currentlySelectedAudibleOption locale] localeIdentifier];
2212 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of selected audible option: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
2214 return m_languageOfPrimaryAudioTrack;
2216 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2218 // AVFoundation synthesizes an audible group when there is only one ungrouped audio track if there is also a legible group (one or
2219 // 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.
2220 NSArray *tracks = [m_avAsset.get() tracksWithMediaType:AVMediaTypeAudio];
2221 if (!tracks || [tracks count] != 1) {
2222 m_languageOfPrimaryAudioTrack = emptyString();
2223 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - %lu audio tracks, returning emptyString()", this, static_cast<unsigned long>(tracks ? [tracks count] : 0));
2224 return m_languageOfPrimaryAudioTrack;
2227 AVAssetTrack *track = [tracks objectAtIndex:0];
2228 m_languageOfPrimaryAudioTrack = AVTrackPrivateAVFObjCImpl::languageForAVAssetTrack(track);
2231 if (m_languageOfPrimaryAudioTrack == emptyString())
2232 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - single audio track has no language, returning emptyString()", this);
2234 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of single audio track: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
2237 return m_languageOfPrimaryAudioTrack;
2240 #if ENABLE(IOS_AIRPLAY)
2241 bool MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless() const
2246 bool wirelessTarget = [m_avPlayer.get() isExternalPlaybackActive];
2247 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless(%p) - returning %s", this, boolString(wirelessTarget));
2248 return wirelessTarget;
2251 MediaPlayer::WirelessPlaybackTargetType MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetType() const
2254 return MediaPlayer::TargetTypeNone;
2256 switch (wkExernalDeviceTypeForPlayer(m_avPlayer.get())) {
2257 case wkExternalPlaybackTypeNone:
2258 return MediaPlayer::TargetTypeNone;
2259 case wkExternalPlaybackTypeAirPlay:
2260 return MediaPlayer::TargetTypeAirPlay;
2261 case wkExternalPlaybackTypeTVOut:
2262 return MediaPlayer::TargetTypeTVOut;
2265 ASSERT_NOT_REACHED();
2266 return MediaPlayer::TargetTypeNone;
2269 String MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName() const
2272 return emptyString();
2274 String wirelessTargetName = wkExernalDeviceDisplayNameForPlayer(m_avPlayer.get());
2275 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName(%p) - returning %s", this, wirelessTargetName.utf8().data());
2277 return wirelessTargetName;
2280 bool MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled() const
2283 return !m_allowsWirelessVideoPlayback;
2285 m_allowsWirelessVideoPlayback = [m_avPlayer.get() allowsExternalPlayback];
2286 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled(%p) - returning %s", this, boolString(!m_allowsWirelessVideoPlayback));
2288 return !m_allowsWirelessVideoPlayback;
2291 void MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(bool disabled)
2293 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(%p) - %s", this, boolString(disabled));
2294 m_allowsWirelessVideoPlayback = !disabled;
2298 [m_avPlayer.get() setAllowsExternalPlayback:!disabled];
2302 void MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange(int status)
2304 m_cachedItemStatus = status;
2309 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange()
2311 m_pendingStatusChanges++;
2314 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange(bool likelyToKeepUp)
2316 m_cachedLikelyToKeepUp = likelyToKeepUp;
2318 ASSERT(m_pendingStatusChanges);
2319 if (!--m_pendingStatusChanges)
2323 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange()
2325 m_pendingStatusChanges++;
2328 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange(bool bufferEmpty)
2330 m_cachedBufferEmpty = bufferEmpty;
2332 ASSERT(m_pendingStatusChanges);
2333 if (!--m_pendingStatusChanges)
2337 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange()
2339 m_pendingStatusChanges++;
2342 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange(bool bufferFull)
2344 m_cachedBufferFull = bufferFull;
2346 ASSERT(m_pendingStatusChanges);
2347 if (!--m_pendingStatusChanges)
2351 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray> seekableRanges)
2353 m_cachedSeekableRanges = seekableRanges;
2355 seekableTimeRangesChanged();
2359 void MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange(RetainPtr<NSArray> loadedRanges)
2361 m_cachedLoadedRanges = loadedRanges;
2363 loadedTimeRangesChanged();
2367 void MediaPlayerPrivateAVFoundationObjC::firstFrameAvailableDidChange(bool isReady)
2369 m_cachedIsReadyForDisplay = isReady;
2370 if (!hasVideo() && isReady)
2375 void MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange(bool)
2381 void MediaPlayerPrivateAVFoundationObjC::setShouldBufferData(bool shouldBuffer)
2383 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::shouldBufferData(%p) - %s", this, boolString(shouldBuffer));
2384 if (m_shouldBufferData == shouldBuffer)
2387 m_shouldBufferData = shouldBuffer;
2392 if (m_shouldBufferData)
2393 [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
2395 [m_avPlayer.get() replaceCurrentItemWithPlayerItem:nil];
2398 #if ENABLE(DATACUE_VALUE)
2399 static const AtomicString& metadataType(NSString *avMetadataKeySpace)
2401 static NeverDestroyed<const AtomicString> quickTimeUserData("com.apple.quicktime.udta", AtomicString::ConstructFromLiteral);
2402 static NeverDestroyed<const AtomicString> isoUserData("org.mp4ra", AtomicString::ConstructFromLiteral);
2403 static NeverDestroyed<const AtomicString> quickTimeMetadata("com.apple.quicktime.mdta", AtomicString::ConstructFromLiteral);
2404 static NeverDestroyed<const AtomicString> iTunesMetadata("com.apple.itunes", AtomicString::ConstructFromLiteral);
2405 static NeverDestroyed<const AtomicString> id3Metadata("org.id3", AtomicString::ConstructFromLiteral);
2407 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData])
2408 return quickTimeUserData;
2409 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceISOUserData])
2411 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata])
2412 return quickTimeMetadata;
2413 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceiTunes])
2414 return iTunesMetadata;
2415 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceID3])
2422 void MediaPlayerPrivateAVFoundationObjC::metadataDidArrive(RetainPtr<NSArray> metadata, double mediaTime)
2424 m_currentMetaData = metadata && ![metadata isKindOfClass:[NSNull class]] ? metadata : nil;
2426 LOG(Media, "MediaPlayerPrivateAVFoundationObjC::metadataDidArrive(%p) - adding %i cues at time %.2f", this, m_currentMetaData ? static_cast<int>([m_currentMetaData.get() count]) : 0, mediaTime);
2428 #if ENABLE(DATACUE_VALUE)
2432 if (!metadata || [metadata isKindOfClass:[NSNull class]]) {
2433 m_metadataTrack->updatePendingCueEndTimes(mediaTime);
2437 if (!m_metadataTrack)
2438 processMetadataTrack();
2440 // Set the duration of all incomplete cues before adding new ones.
2441 double earliesStartTime = std::numeric_limits<double>::infinity();
2442 for (AVMetadataItemType *item in m_currentMetaData.get()) {
2443 double start = CMTimeGetSeconds(item.time);
2444 if (start < earliesStartTime)
2445 earliesStartTime = start;
2447 m_metadataTrack->updatePendingCueEndTimes(earliesStartTime);
2449 for (AVMetadataItemType *item in m_currentMetaData.get()) {
2450 double start = CMTimeGetSeconds(item.time);
2451 double end = std::numeric_limits<double>::infinity();
2452 if (CMTIME_IS_VALID(item.duration))
2453 end = start + CMTimeGetSeconds(item.duration);
2455 AtomicString type = nullAtom;
2457 type = metadataType(item.keySpace);
2459 m_metadataTrack->addDataCue(start, end, SerializedPlatformRepresentationMac::create(item), type);
2464 void MediaPlayerPrivateAVFoundationObjC::tracksDidChange(RetainPtr<NSArray> tracks)
2466 for (AVPlayerItemTrack *track in m_cachedTracks.get())
2467 [track removeObserver:m_objcObserver.get() forKeyPath:@"enabled"];
2469 m_cachedTracks = tracks;
2470 for (AVPlayerItemTrack *track in m_cachedTracks.get())
2471 [track addObserver:m_objcObserver.get() forKeyPath:@"enabled" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayerItemTrack];
2473 m_cachedTotalBytes = 0;
2479 void MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange(bool hasEnabledAudio)
2481 m_cachedHasEnabledAudio = hasEnabledAudio;
2487 void MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange(FloatSize size)
2489 m_cachedPresentationSize = size;
2495 void MediaPlayerPrivateAVFoundationObjC::durationDidChange(double duration)
2497 m_cachedDuration = duration;
2499 invalidateCachedDuration();
2502 void MediaPlayerPrivateAVFoundationObjC::rateDidChange(double rate)
2504 m_cachedRate = rate;
2510 #if ENABLE(IOS_AIRPLAY)
2511 void MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange()
2513 playbackTargetIsWirelessChanged();
2517 NSArray* assetMetadataKeyNames()
2519 static NSArray* keys;
2521 keys = [[NSArray alloc] initWithObjects:@"duration",
2523 @"preferredTransform",
2528 @"availableMediaCharacteristicsWithMediaSelectionOptions",
2534 NSArray* itemKVOProperties()
2536 static NSArray* keys;
2538 keys = [[NSArray alloc] initWithObjects:@"presentationSize",
2542 @"seekableTimeRanges",
2543 @"loadedTimeRanges",
2544 @"playbackLikelyToKeepUp",
2545 @"playbackBufferFull",
2546 @"playbackBufferEmpty",
2555 NSArray* assetTrackMetadataKeyNames()
2557 static NSArray* keys = [[NSArray alloc] initWithObjects:@"totalSampleDataLength", @"mediaType", @"enabled", nil];
2562 } // namespace WebCore
2564 @implementation WebCoreAVFMovieObserver
2566 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2568 self = [super init];
2571 m_callback = callback;
2577 [NSObject cancelPreviousPerformRequestsWithTarget:self];
2581 - (void)metadataLoaded
2586 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
2589 - (void)didEnd:(NSNotification *)unusedNotification
2591 UNUSED_PARAM(unusedNotification);
2594 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
2597 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context
2599 UNUSED_PARAM(object);
2600 id newValue = [change valueForKey:NSKeyValueChangeNewKey];
2602 LOG(Media, "WebCoreAVFMovieObserver::observeValueForKeyPath(%p) - keyPath = %s", self, [keyPath UTF8String]);
2607 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
2609 WTF::Function<void ()> function;
2611 if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) {
2612 if ([keyPath isEqualToString:@"readyForDisplay"])
2613 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::firstFrameAvailableDidChange, m_callback, [newValue boolValue]);
2616 if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) {
2617 if ([keyPath isEqualToString:@"enabled"])
2618 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange, m_callback, [newValue boolValue]);
2621 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) {
2622 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
2623 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange, m_callback);
2624 else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
2625 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange, m_callback);
2626 else if ([keyPath isEqualToString:@"playbackBufferFull"])
2627 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange, m_callback);
2630 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) {
2631 // A value changed for an AVPlayerItem
2632 if ([keyPath isEqualToString:@"status"])
2633 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange, m_callback, [newValue intValue]);
2634 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
2635 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange, m_callback, [newValue boolValue]);
2636 else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
2637 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange, m_callback, [newValue boolValue]);
2638 else if ([keyPath isEqualToString:@"playbackBufferFull"])
2639 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange, m_callback, [newValue boolValue]);
2640 else if ([keyPath isEqualToString:@"asset"])
2641 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::setAsset, m_callback, RetainPtr<NSArray>(newValue));
2642 else if ([keyPath isEqualToString:@"loadedTimeRanges"])
2643 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
2644 else if ([keyPath isEqualToString:@"seekableTimeRanges"])
2645 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
2646 else if ([keyPath isEqualToString:@"tracks"])
2647 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::tracksDidChange, m_callback, RetainPtr<NSArray>(newValue));
2648 else if ([keyPath isEqualToString:@"hasEnabledAudio"])
2649 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange, m_callback, [newValue boolValue]);
2650 else if ([keyPath isEqualToString:@"presentationSize"])
2651 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange, m_callback, FloatSize([newValue sizeValue]));
2652 else if ([keyPath isEqualToString:@"duration"])
2653 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::durationDidChange, m_callback, CMTimeGetSeconds([newValue CMTimeValue]));
2654 else if ([keyPath isEqualToString:@"timedMetadata"] && newValue) {
2656 CMTime itemTime = [(AVPlayerItemType *)object currentTime];
2657 if (CMTIME_IS_NUMERIC(itemTime))
2658 now = std::max(narrowPrecisionToFloat(CMTimeGetSeconds(itemTime)), 0.0f);
2659 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::metadataDidArrive, m_callback, RetainPtr<NSArray>(newValue), now);
2663 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) {
2664 // A value changed for an AVPlayer.
2665 if ([keyPath isEqualToString:@"rate"])
2666 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::rateDidChange, m_callback, [newValue doubleValue]);
2667 #if ENABLE(IOS_AIRPLAY)
2668 else if ([keyPath isEqualToString:@"externalPlaybackActive"])
2669 function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange, m_callback);
2673 if (function.isNull())
2676 auto weakThis = m_callback->createWeakPtr();
2677 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification([weakThis, function]{
2678 // weakThis and function both refer to the same MediaPlayerPrivateAVFoundationObjC instance. If the WeakPtr has
2679 // been cleared, the underlying object has been destroyed, and it is unsafe to call function().
2686 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2687 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime
2689 UNUSED_PARAM(output);
2690 UNUSED_PARAM(nativeSamples);
2695 RetainPtr<WebCoreAVFMovieObserver> strongSelf = self;
2696 RetainPtr<NSArray> strongStrings = strings;
2697 callOnMainThread([strongSelf, strongStrings, itemTime] {
2698 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2701 callback->processCue(strongStrings.get(), CMTimeGetSeconds(itemTime));
2705 - (void)outputSequenceWasFlushed:(id)output
2707 UNUSED_PARAM(output);
2712 RetainPtr<WebCoreAVFMovieObserver> strongSelf = self;
2713 callOnMainThread([strongSelf] {
2714 if (MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback)
2715 callback->flushCues();
2722 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
2723 @implementation WebCoreAVFLoaderDelegate
2725 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2727 self = [super init];
2730 m_callback = callback;
2734 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
2736 UNUSED_PARAM(resourceLoader);
2740 RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2741 RetainPtr<AVAssetResourceLoadingRequest> strongRequest = loadingRequest;
2742 callOnMainThread([strongSelf, strongRequest] {
2743 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2745 [strongRequest finishLoadingWithError:nil];
2749 if (!callback->shouldWaitForLoadingOfResource(strongRequest.get()))
2750 [strongRequest finishLoadingWithError:nil];
2756 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForResponseToAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
2758 UNUSED_PARAM(resourceLoader);
2762 if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
2765 RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2766 RetainPtr<NSURLAuthenticationChallenge> strongChallenge = challenge;
2767 callOnMainThread([strongSelf, strongChallenge] {
2768 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2770 [[strongChallenge sender] cancelAuthenticationChallenge:strongChallenge.get()];
2774 if (!callback->shouldWaitForResponseToAuthenticationChallenge(strongChallenge.get()))
2775 [[strongChallenge sender] cancelAuthenticationChallenge:strongChallenge.get()];
2781 - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
2783 UNUSED_PARAM(resourceLoader);
2787 RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2788 RetainPtr<AVAssetResourceLoadingRequest> strongRequest = loadingRequest;
2789 callOnMainThread([strongSelf, strongRequest] {
2790 MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2792 callback->didCancelLoadingRequest(strongRequest.get());
2796 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2798 m_callback = callback;
2803 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
2804 @implementation WebCoreAVFPullDelegate
2805 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback
2807 self = [super init];
2809 m_callback = callback;
2813 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC *)callback
2815 m_callback = callback;
2818 - (void)outputMediaDataWillChange:(AVPlayerItemVideoOutput *)output
2821 m_callback->outputMediaDataWillChange(output);
2824 - (void)outputSequenceWasFlushed:(AVPlayerItemVideoOutput *)output
2826 UNUSED_PARAM(output);