2 * Copyright (C) 2011-2017 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.
27 #import "MediaPlayerPrivateAVFoundationObjC.h"
29 #if ENABLE(VIDEO) && USE(AVFOUNDATION)
31 #import "AVAssetTrackUtilities.h"
32 #import "AVFoundationMIMETypeCache.h"
33 #import "AVTrackPrivateAVFObjCImpl.h"
34 #import "AudioSourceProviderAVFObjC.h"
35 #import "AudioTrackPrivateAVFObjC.h"
36 #import "AuthenticationChallenge.h"
37 #import "CDMSessionAVFoundationObjC.h"
39 #import "DeprecatedGlobalSettings.h"
40 #import "Extensions3D.h"
41 #import "FloatConversion.h"
42 #import "GraphicsContext.h"
43 #import "GraphicsContext3D.h"
44 #import "GraphicsContextCG.h"
45 #import "InbandMetadataTextTrackPrivateAVF.h"
46 #import "InbandTextTrackPrivateAVFObjC.h"
47 #import "InbandTextTrackPrivateLegacyAVFObjC.h"
49 #import "MediaPlaybackTargetMac.h"
50 #import "MediaPlaybackTargetMock.h"
51 #import "MediaSelectionGroupAVFObjC.h"
52 #import "OutOfBandTextTrackPrivateAVF.h"
53 #import "PixelBufferConformerCV.h"
54 #import "PlatformTimeRanges.h"
55 #import "SecurityOrigin.h"
56 #import "SerializedPlatformRepresentationMac.h"
57 #import "TextEncoding.h"
58 #import "TextTrackRepresentation.h"
59 #import "TextureCacheCV.h"
61 #import "VideoTextureCopierCV.h"
62 #import "VideoTrackPrivateAVFObjC.h"
63 #import "WebCoreAVFResourceLoader.h"
64 #import "WebCoreCALayerExtras.h"
65 #import "WebCoreNSURLSession.h"
68 #import <objc/runtime.h>
69 #import <pal/avfoundation/MediaTimeAVFoundation.h>
70 #import <pal/spi/cocoa/QuartzCoreSPI.h>
71 #import <pal/spi/mac/AVFoundationSPI.h>
72 #import <runtime/DataView.h>
73 #import <runtime/JSCInlines.h>
74 #import <runtime/TypedArrayInlines.h>
75 #import <runtime/Uint16Array.h>
76 #import <runtime/Uint32Array.h>
77 #import <runtime/Uint8Array.h>
78 #import <wtf/BlockObjCExceptions.h>
79 #import <wtf/CurrentTime.h>
80 #import <wtf/ListHashSet.h>
81 #import <wtf/NeverDestroyed.h>
82 #import <wtf/OSObjectPtr.h>
83 #import <wtf/text/CString.h>
85 #if ENABLE(AVF_CAPTIONS)
86 #include "TextTrack.h"
89 #import <AVFoundation/AVAssetImageGenerator.h>
90 #import <AVFoundation/AVAssetTrack.h>
91 #import <AVFoundation/AVMediaSelectionGroup.h>
92 #import <AVFoundation/AVMetadataItem.h>
93 #import <AVFoundation/AVPlayer.h>
94 #import <AVFoundation/AVPlayerItem.h>
95 #import <AVFoundation/AVPlayerItemOutput.h>
96 #import <AVFoundation/AVPlayerItemTrack.h>
97 #import <AVFoundation/AVPlayerLayer.h>
98 #import <AVFoundation/AVTime.h>
100 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
101 #import "VideoFullscreenLayerManager.h"
105 #import "WAKAppKitStubs.h"
106 #import <CoreImage/CoreImage.h>
107 #import <UIKit/UIDevice.h>
108 #import <mach/mach_port.h>
110 #import <Foundation/NSGeometry.h>
111 #import <QuartzCore/CoreImage.h>
114 #if USE(VIDEOTOOLBOX)
115 #import <CoreVideo/CoreVideo.h>
116 #import <VideoToolbox/VideoToolbox.h>
119 #if USE(CFURLCONNECTION)
120 #include <pal/spi/cocoa/CFNSURLConnectionSPI.h>
123 #import "CoreVideoSoftLink.h"
124 #import "MediaRemoteSoftLink.h"
127 template <> struct iterator_traits<HashSet<RefPtr<WebCore::MediaSelectionOptionAVFObjC>>::iterator> {
128 typedef RefPtr<WebCore::MediaSelectionOptionAVFObjC> value_type;
132 #if ENABLE(AVF_CAPTIONS)
133 // Note: This must be defined before our SOFT_LINK macros:
134 @class AVMediaSelectionOption;
135 @interface AVMediaSelectionOption (OutOfBandExtensions)
136 @property (nonatomic, readonly) NSString* outOfBandSource;
137 @property (nonatomic, readonly) NSString* outOfBandIdentifier;
141 @interface AVURLAsset (WebKitExtensions)
142 @property (nonatomic, readonly) NSURL *resolvedURL;
145 typedef AVPlayer AVPlayerType;
146 typedef AVPlayerItem AVPlayerItemType;
147 typedef AVPlayerItemLegibleOutput AVPlayerItemLegibleOutputType;
148 typedef AVPlayerItemVideoOutput AVPlayerItemVideoOutputType;
149 typedef AVMetadataItem AVMetadataItemType;
150 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
151 typedef AVMediaSelectionOption AVMediaSelectionOptionType;
152 typedef AVAssetCache AVAssetCacheType;
154 #pragma mark - Soft Linking
156 // Soft-linking headers must be included last since they #define functions, constants, etc.
157 #import <pal/cf/CoreMediaSoftLink.h>
159 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
161 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreImage)
163 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVPlayer)
164 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVPlayerItem)
165 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVPlayerItemVideoOutput)
166 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVPlayerLayer)
167 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVURLAsset)
168 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVAssetImageGenerator)
169 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVMetadataItem)
170 SOFT_LINK_CLASS_FOR_SOURCE(WebCore, AVFoundation, AVAssetCache)
172 SOFT_LINK_CLASS(CoreImage, CIContext)
173 SOFT_LINK_CLASS(CoreImage, CIImage)
175 SOFT_LINK_POINTER(AVFoundation, AVAudioTimePitchAlgorithmSpectral, NSString*)
176 SOFT_LINK_POINTER(AVFoundation, AVAudioTimePitchAlgorithmVarispeed, NSString*)
177 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicVisual, NSString *)
178 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicAudible, NSString *)
179 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeClosedCaption, NSString *)
180 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeVideo, NSString *)
181 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
182 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeMetadata, NSString *)
183 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemDidPlayToEndTimeNotification, NSString *)
184 SOFT_LINK_POINTER(AVFoundation, AVURLAssetInheritURIQueryComponentFromReferencingURIKey, NSString *)
185 SOFT_LINK_POINTER(AVFoundation, AVAssetImageGeneratorApertureModeCleanAperture, NSString *)
186 SOFT_LINK_POINTER(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *)
187 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *)
188 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspectFill, NSString *)
189 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResize, NSString *)
191 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetClientBundleIdentifierKey, NSString *)
192 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetRequiresCustomURLLoadingKey, NSString *)
193 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetOutOfBandMIMETypeKey, NSString *)
195 #define AVPlayer initAVPlayer()
196 #define AVPlayerItem initAVPlayerItem()
197 #define AVPlayerLayer initAVPlayerLayer()
198 #define AVURLAsset initAVURLAsset()
199 #define AVAssetImageGenerator initAVAssetImageGenerator()
200 #define AVPlayerItemVideoOutput initAVPlayerItemVideoOutput()
201 #define AVMetadataItem initAVMetadataItem()
202 #define AVAssetCache initAVAssetCache()
204 #define AVAudioTimePitchAlgorithmSpectral getAVAudioTimePitchAlgorithmSpectral()
205 #define AVAudioTimePitchAlgorithmVarispeed getAVAudioTimePitchAlgorithmVarispeed()
206 #define AVMediaCharacteristicVisual getAVMediaCharacteristicVisual()
207 #define AVMediaCharacteristicAudible getAVMediaCharacteristicAudible()
208 #define AVMediaTypeClosedCaption getAVMediaTypeClosedCaption()
209 #define AVMediaTypeVideo getAVMediaTypeVideo()
210 #define AVMediaTypeAudio getAVMediaTypeAudio()
211 #define AVMediaTypeMetadata getAVMediaTypeMetadata()
212 #define AVPlayerItemDidPlayToEndTimeNotification getAVPlayerItemDidPlayToEndTimeNotification()
213 #define AVURLAssetInheritURIQueryComponentFromReferencingURIKey getAVURLAssetInheritURIQueryComponentFromReferencingURIKey()
214 #define AVURLAssetClientBundleIdentifierKey getAVURLAssetClientBundleIdentifierKey()
215 #define AVURLAssetRequiresCustomURLLoadingKey getAVURLAssetRequiresCustomURLLoadingKey()
216 #define AVURLAssetOutOfBandMIMETypeKey getAVURLAssetOutOfBandMIMETypeKey()
217 #define AVAssetImageGeneratorApertureModeCleanAperture getAVAssetImageGeneratorApertureModeCleanAperture()
218 #define AVURLAssetReferenceRestrictionsKey getAVURLAssetReferenceRestrictionsKey()
219 #define AVLayerVideoGravityResizeAspect getAVLayerVideoGravityResizeAspect()
220 #define AVLayerVideoGravityResizeAspectFill getAVLayerVideoGravityResizeAspectFill()
221 #define AVLayerVideoGravityResize getAVLayerVideoGravityResize()
223 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
224 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
225 typedef AVMediaSelectionOption AVMediaSelectionOptionType;
227 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemLegibleOutput)
228 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionGroup)
229 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionOption)
231 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicLegible, NSString *)
232 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeSubtitle, NSString *)
233 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicContainsOnlyForcedSubtitles, NSString *)
234 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly, NSString *)
236 #define AVPlayerItemLegibleOutput getAVPlayerItemLegibleOutputClass()
237 #define AVMediaSelectionGroup getAVMediaSelectionGroupClass()
238 #define AVMediaSelectionOption getAVMediaSelectionOptionClass()
239 #define AVMediaCharacteristicLegible getAVMediaCharacteristicLegible()
240 #define AVMediaTypeSubtitle getAVMediaTypeSubtitle()
241 #define AVMediaCharacteristicContainsOnlyForcedSubtitles getAVMediaCharacteristicContainsOnlyForcedSubtitles()
242 #define AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly getAVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly()
245 #if ENABLE(AVF_CAPTIONS)
246 SOFT_LINK_POINTER(AVFoundation, AVURLAssetCacheKey, NSString*)
247 SOFT_LINK_POINTER(AVFoundation, AVURLAssetHTTPCookiesKey, NSString*)
248 SOFT_LINK_POINTER(AVFoundation, AVURLAssetOutOfBandAlternateTracksKey, NSString*)
249 SOFT_LINK_POINTER(AVFoundation, AVURLAssetUsesNoPersistentCacheKey, NSString*)
250 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackDisplayNameKey, NSString*)
251 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackExtendedLanguageTagKey, NSString*)
252 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIsDefaultKey, NSString*)
253 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackMediaCharactersticsKey, NSString*)
254 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIdentifierKey, NSString*)
255 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackSourceKey, NSString*)
256 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, NSString*)
257 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, NSString*)
258 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicIsAuxiliaryContent, NSString*)
260 #define AVURLAssetHTTPCookiesKey getAVURLAssetHTTPCookiesKey()
261 #define AVURLAssetOutOfBandAlternateTracksKey getAVURLAssetOutOfBandAlternateTracksKey()
262 #define AVURLAssetCacheKey getAVURLAssetCacheKey()
263 #define AVURLAssetUsesNoPersistentCacheKey getAVURLAssetUsesNoPersistentCacheKey()
264 #define AVOutOfBandAlternateTrackDisplayNameKey getAVOutOfBandAlternateTrackDisplayNameKey()
265 #define AVOutOfBandAlternateTrackExtendedLanguageTagKey getAVOutOfBandAlternateTrackExtendedLanguageTagKey()
266 #define AVOutOfBandAlternateTrackIsDefaultKey getAVOutOfBandAlternateTrackIsDefaultKey()
267 #define AVOutOfBandAlternateTrackMediaCharactersticsKey getAVOutOfBandAlternateTrackMediaCharactersticsKey()
268 #define AVOutOfBandAlternateTrackIdentifierKey getAVOutOfBandAlternateTrackIdentifierKey()
269 #define AVOutOfBandAlternateTrackSourceKey getAVOutOfBandAlternateTrackSourceKey()
270 #define AVMediaCharacteristicDescribesMusicAndSoundForAccessibility getAVMediaCharacteristicDescribesMusicAndSoundForAccessibility()
271 #define AVMediaCharacteristicTranscribesSpokenDialogForAccessibility getAVMediaCharacteristicTranscribesSpokenDialogForAccessibility()
272 #define AVMediaCharacteristicIsAuxiliaryContent getAVMediaCharacteristicIsAuxiliaryContent()
275 #if ENABLE(DATACUE_VALUE)
276 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceQuickTimeUserData, NSString*)
277 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVMetadataKeySpaceISOUserData, NSString*)
278 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceQuickTimeMetadata, NSString*)
279 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceiTunes, NSString*)
280 SOFT_LINK_POINTER(AVFoundation, AVMetadataKeySpaceID3, NSString*)
282 #define AVMetadataKeySpaceQuickTimeUserData getAVMetadataKeySpaceQuickTimeUserData()
283 #define AVMetadataKeySpaceISOUserData getAVMetadataKeySpaceISOUserData()
284 #define AVMetadataKeySpaceQuickTimeMetadata getAVMetadataKeySpaceQuickTimeMetadata()
285 #define AVMetadataKeySpaceiTunes getAVMetadataKeySpaceiTunes()
286 #define AVMetadataKeySpaceID3 getAVMetadataKeySpaceID3()
290 SOFT_LINK_POINTER(AVFoundation, AVURLAssetBoundNetworkInterfaceName, NSString *)
291 #define AVURLAssetBoundNetworkInterfaceName getAVURLAssetBoundNetworkInterfaceName()
294 SOFT_LINK_FRAMEWORK(MediaToolbox)
295 SOFT_LINK_OPTIONAL(MediaToolbox, MTEnableCaption2015Behavior, Boolean, (), ())
298 SOFT_LINK_PRIVATE_FRAMEWORK(Celestial)
299 SOFT_LINK_POINTER(Celestial, AVController_RouteDescriptionKey_RouteCurrentlyPicked, NSString *)
300 SOFT_LINK_POINTER(Celestial, AVController_RouteDescriptionKey_RouteName, NSString *)
301 SOFT_LINK_POINTER(Celestial, AVController_RouteDescriptionKey_AVAudioRouteName, NSString *)
302 #define AVController_RouteDescriptionKey_RouteCurrentlyPicked getAVController_RouteDescriptionKey_RouteCurrentlyPicked()
303 #define AVController_RouteDescriptionKey_RouteName getAVController_RouteDescriptionKey_RouteName()
304 #define AVController_RouteDescriptionKey_AVAudioRouteName getAVController_RouteDescriptionKey_AVAudioRouteName()
306 SOFT_LINK_FRAMEWORK(UIKit)
307 SOFT_LINK_CLASS(UIKit, UIDevice)
308 #define UIDevice getUIDeviceClass()
311 using namespace WebCore;
313 enum MediaPlayerAVFoundationObservationContext {
314 MediaPlayerAVFoundationObservationContextPlayerItem,
315 MediaPlayerAVFoundationObservationContextPlayerItemTrack,
316 MediaPlayerAVFoundationObservationContextPlayer,
317 MediaPlayerAVFoundationObservationContextAVPlayerLayer,
320 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
321 @interface WebCoreAVFMovieObserver : NSObject <AVPlayerItemLegibleOutputPushDelegate>
323 @interface WebCoreAVFMovieObserver : NSObject
326 MediaPlayerPrivateAVFoundationObjC* m_callback;
327 int m_delayCallbacks;
329 -(id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
331 -(void)metadataLoaded;
332 -(void)didEnd:(NSNotification *)notification;
333 -(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context;
334 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
335 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime;
336 - (void)outputSequenceWasFlushed:(id)output;
340 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
341 @interface WebCoreAVFLoaderDelegate : NSObject<AVAssetResourceLoaderDelegate> {
342 MediaPlayerPrivateAVFoundationObjC* m_callback;
344 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
345 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
346 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
350 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
351 @interface WebCoreAVFPullDelegate : NSObject<AVPlayerItemOutputPullDelegate> {
352 MediaPlayerPrivateAVFoundationObjC *m_callback;
353 dispatch_semaphore_t m_semaphore;
355 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback;
356 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
357 - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender;
358 - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output;
365 static NSArray *assetMetadataKeyNames();
366 static NSArray *itemKVOProperties();
367 static NSArray *assetTrackMetadataKeyNames();
368 static NSArray *playerKVOProperties();
369 static AVAssetTrack* firstEnabledTrack(NSArray* tracks);
371 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
372 static dispatch_queue_t globalLoaderDelegateQueue()
374 static dispatch_queue_t globalQueue;
375 static dispatch_once_t onceToken;
376 dispatch_once(&onceToken, ^{
377 globalQueue = dispatch_queue_create("WebCoreAVFLoaderDelegate queue", DISPATCH_QUEUE_SERIAL);
383 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
384 static dispatch_queue_t globalPullDelegateQueue()
386 static dispatch_queue_t globalQueue;
387 static dispatch_once_t onceToken;
388 dispatch_once(&onceToken, ^{
389 globalQueue = dispatch_queue_create("WebCoreAVFPullDelegate queue", DISPATCH_QUEUE_SERIAL);
395 #if USE(CFURLCONNECTION)
396 class WebCoreNSURLAuthenticationChallengeClient : public RefCounted<WebCoreNSURLAuthenticationChallengeClient>, public AuthenticationClient {
398 static RefPtr<WebCoreNSURLAuthenticationChallengeClient> create(NSURLAuthenticationChallenge *challenge)
400 return adoptRef(new WebCoreNSURLAuthenticationChallengeClient(challenge));
403 using RefCounted<WebCoreNSURLAuthenticationChallengeClient>::ref;
404 using RefCounted<WebCoreNSURLAuthenticationChallengeClient>::deref;
407 WebCoreNSURLAuthenticationChallengeClient(NSURLAuthenticationChallenge *challenge)
408 : m_challenge(challenge)
413 void refAuthenticationClient() override { ref(); }
414 void derefAuthenticationClient() override { deref(); }
416 void receivedCredential(const AuthenticationChallenge&, const Credential& credential) override
418 [[m_challenge sender] useCredential:credential.nsCredential() forAuthenticationChallenge:m_challenge.get()];
421 void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) override
423 [[m_challenge sender] continueWithoutCredentialForAuthenticationChallenge:m_challenge.get()];
426 void receivedCancellation(const AuthenticationChallenge&) override
428 [[m_challenge sender] cancelAuthenticationChallenge:m_challenge.get()];
431 void receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) override
433 if ([[m_challenge sender] respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)])
434 [[m_challenge sender] performDefaultHandlingForAuthenticationChallenge:m_challenge.get()];
437 void receivedChallengeRejection(const AuthenticationChallenge&) override
439 if ([[m_challenge sender] respondsToSelector:@selector(rejectProtectionSpaceAndContinueWithChallenge:)])
440 [[m_challenge sender] rejectProtectionSpaceAndContinueWithChallenge:m_challenge.get()];
443 RetainPtr<NSURLAuthenticationChallenge> m_challenge;
447 void MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(MediaEngineRegistrar registrar)
452 registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateAVFoundationObjC>(player); },
453 getSupportedTypes, supportsType, originsInMediaCache, clearMediaCache, clearMediaCacheForOrigins, supportsKeySystem);
454 AVFoundationMIMETypeCache::singleton().loadTypes();
457 static AVAssetCacheType *assetCacheForPath(const String& path)
459 NSURL *assetCacheURL;
462 assetCacheURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"MediaCache" isDirectory:YES];
464 assetCacheURL = [NSURL fileURLWithPath:path isDirectory:YES];
466 return [initAVAssetCache() assetCacheWithURL:assetCacheURL];
469 HashSet<RefPtr<SecurityOrigin>> MediaPlayerPrivateAVFoundationObjC::originsInMediaCache(const String& path)
471 HashSet<RefPtr<SecurityOrigin>> origins;
472 for (NSString *key in [assetCacheForPath(path) allKeys]) {
473 URL keyAsURL = URL(URL(), key);
474 if (keyAsURL.isValid())
475 origins.add(SecurityOrigin::create(keyAsURL));
480 static std::chrono::system_clock::time_point toSystemClockTime(NSDate *date)
483 using namespace std::chrono;
485 return system_clock::time_point(duration_cast<system_clock::duration>(duration<double>(date.timeIntervalSince1970)));
488 void MediaPlayerPrivateAVFoundationObjC::clearMediaCache(const String& path, std::chrono::system_clock::time_point modifiedSince)
490 AVAssetCacheType* assetCache = assetCacheForPath(path);
492 for (NSString *key in [assetCache allKeys]) {
493 if (toSystemClockTime([assetCache lastModifiedDateOfEntryForKey:key]) > modifiedSince)
494 [assetCache removeEntryForKey:key];
497 NSFileManager *fileManager = [NSFileManager defaultManager];
498 NSURL *baseURL = [assetCache URL];
500 if (modifiedSince <= std::chrono::system_clock::time_point { }) {
501 [fileManager removeItemAtURL:baseURL error:nil];
505 NSArray *propertyKeys = @[NSURLNameKey, NSURLContentModificationDateKey, NSURLIsRegularFileKey];
506 NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:baseURL includingPropertiesForKeys:
507 propertyKeys options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
510 RetainPtr<NSMutableArray> urlsToDelete = adoptNS([[NSMutableArray alloc] init]);
511 for (NSURL *fileURL : enumerator) {
512 NSDictionary *fileAttributes = [fileURL resourceValuesForKeys:propertyKeys error:nil];
514 if (![fileAttributes[NSURLNameKey] hasPrefix:@"CachedMedia-"])
517 if (![fileAttributes[NSURLIsRegularFileKey] boolValue])
520 if (toSystemClockTime(fileAttributes[NSURLContentModificationDateKey]) <= modifiedSince)
523 [urlsToDelete addObject:fileURL];
526 for (NSURL *fileURL in urlsToDelete.get())
527 [fileManager removeItemAtURL:fileURL error:nil];
530 void MediaPlayerPrivateAVFoundationObjC::clearMediaCacheForOrigins(const String& path, const HashSet<RefPtr<SecurityOrigin>>& origins)
532 AVAssetCacheType* assetCache = assetCacheForPath(path);
533 for (NSString *key in [assetCache allKeys]) {
534 URL keyAsURL = URL(URL(), key);
535 if (keyAsURL.isValid()) {
536 if (origins.contains(SecurityOrigin::create(keyAsURL)))
537 [assetCache removeEntryForKey:key];
542 MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC(MediaPlayer* player)
543 : MediaPlayerPrivateAVFoundation(player)
544 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
545 , m_videoFullscreenLayerManager(VideoFullscreenLayerManager::create())
546 , m_videoFullscreenGravity(MediaPlayer::VideoGravityResizeAspect)
548 , m_objcObserver(adoptNS([[WebCoreAVFMovieObserver alloc] initWithCallback:this]))
549 , m_videoFrameHasDrawn(false)
550 , m_haveCheckedPlayability(false)
551 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
552 , m_videoOutputDelegate(adoptNS([[WebCoreAVFPullDelegate alloc] initWithCallback:this]))
553 , m_videoOutputSemaphore(nullptr)
555 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
556 , m_loaderDelegate(adoptNS([[WebCoreAVFLoaderDelegate alloc] initWithCallback:this]))
558 , m_currentTextTrack(0)
560 , m_cachedTotalBytes(0)
561 , m_pendingStatusChanges(0)
562 , m_cachedItemStatus(MediaPlayerAVPlayerItemStatusDoesNotExist)
563 , m_cachedLikelyToKeepUp(false)
564 , m_cachedBufferEmpty(false)
565 , m_cachedBufferFull(false)
566 , m_cachedHasEnabledAudio(false)
567 , m_shouldBufferData(true)
568 , m_cachedIsReadyForDisplay(false)
569 , m_haveBeenAskedToCreateLayer(false)
570 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
571 , m_allowsWirelessVideoPlayback(true)
576 MediaPlayerPrivateAVFoundationObjC::~MediaPlayerPrivateAVFoundationObjC()
578 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
579 [m_loaderDelegate.get() setCallback:0];
580 [[m_avAsset.get() resourceLoader] setDelegate:nil queue:0];
582 for (auto& pair : m_resourceLoaderMap)
583 pair.value->invalidate();
585 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
586 [m_videoOutputDelegate setCallback:0];
587 [m_videoOutput setDelegate:nil queue:0];
588 if (m_videoOutputSemaphore)
589 dispatch_release(m_videoOutputSemaphore);
598 void MediaPlayerPrivateAVFoundationObjC::cancelLoad()
600 INFO_LOG(LOGIDENTIFIER);
601 tearDownVideoRendering();
603 [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
604 [m_objcObserver.get() disconnect];
606 // Tell our observer to do nothing when our cancellation of pending loading calls its completion handler.
607 setIgnoreLoadStateChanges(true);
609 [m_avAsset.get() cancelLoading];
615 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
616 if (m_legibleOutput) {
618 [m_avPlayerItem.get() removeOutput:m_legibleOutput.get()];
619 m_legibleOutput = nil;
623 if (m_avPlayerItem) {
624 for (NSString *keyName in itemKVOProperties())
625 [m_avPlayerItem.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
627 m_avPlayerItem = nil;
631 [m_avPlayer.get() removeTimeObserver:m_timeObserver.get()];
632 m_timeObserver = nil;
634 for (NSString *keyName in playerKVOProperties())
635 [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
637 [m_avPlayer replaceCurrentItemWithPlayerItem:nil];
641 // Reset cached properties
642 m_pendingStatusChanges = 0;
643 m_cachedItemStatus = MediaPlayerAVPlayerItemStatusDoesNotExist;
644 m_cachedSeekableRanges = nullptr;
645 m_cachedLoadedRanges = nullptr;
646 m_cachedHasEnabledAudio = false;
647 m_cachedPresentationSize = FloatSize();
648 m_cachedDuration = MediaTime::zeroTime();
650 for (AVPlayerItemTrack *track in m_cachedTracks.get())
651 [track removeObserver:m_objcObserver.get() forKeyPath:@"enabled"];
652 m_cachedTracks = nullptr;
654 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
656 m_provider->setPlayerItem(nullptr);
657 m_provider->setAudioTrack(nullptr);
661 setIgnoreLoadStateChanges(false);
664 bool MediaPlayerPrivateAVFoundationObjC::hasLayerRenderer() const
666 return m_haveBeenAskedToCreateLayer;
669 bool MediaPlayerPrivateAVFoundationObjC::hasContextRenderer() const
671 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
675 return m_imageGenerator;
678 void MediaPlayerPrivateAVFoundationObjC::createContextVideoRenderer()
680 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
683 createImageGenerator();
687 void MediaPlayerPrivateAVFoundationObjC::createImageGenerator()
690 INFO_LOG(LOGIDENTIFIER);
692 if (!m_avAsset || m_imageGenerator)
695 m_imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:m_avAsset.get()];
697 [m_imageGenerator.get() setApertureMode:AVAssetImageGeneratorApertureModeCleanAperture];
698 [m_imageGenerator.get() setAppliesPreferredTrackTransform:YES];
699 [m_imageGenerator.get() setRequestedTimeToleranceBefore:kCMTimeZero];
700 [m_imageGenerator.get() setRequestedTimeToleranceAfter:kCMTimeZero];
703 void MediaPlayerPrivateAVFoundationObjC::destroyContextVideoRenderer()
705 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
706 destroyVideoOutput();
707 destroyOpenGLVideoOutput();
709 destroyImageGenerator();
712 void MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator()
714 if (!m_imageGenerator)
717 INFO_LOG(LOGIDENTIFIER);
719 m_imageGenerator = 0;
722 void MediaPlayerPrivateAVFoundationObjC::createVideoLayer()
724 if (!m_avPlayer || m_haveBeenAskedToCreateLayer)
727 callOnMainThread([this, weakThis = createWeakPtr()] {
731 if (!m_avPlayer || m_haveBeenAskedToCreateLayer)
733 m_haveBeenAskedToCreateLayer = true;
736 createAVPlayerLayer();
738 #if USE(VIDEOTOOLBOX)
743 player()->client().mediaPlayerRenderingModeChanged(player());
747 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerLayer()
752 m_videoLayer = adoptNS([[AVPlayerLayer alloc] init]);
753 [m_videoLayer setPlayer:m_avPlayer.get()];
756 [m_videoLayer setName:@"MediaPlayerPrivate AVPlayerLayer"];
758 [m_videoLayer addObserver:m_objcObserver.get() forKeyPath:@"readyForDisplay" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextAVPlayerLayer];
759 updateVideoLayerGravity();
760 [m_videoLayer setContentsScale:player()->client().mediaPlayerContentsScale()];
761 IntSize defaultSize = snappedIntRect(player()->client().mediaPlayerContentBoxRect()).size();
762 INFO_LOG(LOGIDENTIFIER);
764 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
765 m_videoFullscreenLayerManager->setVideoLayer(m_videoLayer.get(), defaultSize);
768 [m_videoLayer setPIPModeEnabled:(player()->fullscreenMode() & MediaPlayer::VideoFullscreenModePictureInPicture)];
771 [m_videoLayer setFrame:CGRectMake(0, 0, defaultSize.width(), defaultSize.height())];
775 void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer()
780 INFO_LOG(LOGIDENTIFIER);
782 [m_videoLayer removeObserver:m_objcObserver.get() forKeyPath:@"readyForDisplay"];
783 [m_videoLayer setPlayer:nil];
785 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
786 m_videoFullscreenLayerManager->didDestroyVideoLayer();
792 MediaTime MediaPlayerPrivateAVFoundationObjC::getStartDate() const
794 // Date changes as the track's playback position changes. Must subtract currentTime (offset in seconds) from date offset to get date beginning
795 double date = [[m_avPlayerItem currentDate] timeIntervalSince1970] * 1000;
797 // No live streams were made during the epoch (1970). AVFoundation returns 0 if the media file doesn't have a start date
799 return MediaTime::invalidTime();
801 double currentTime = CMTimeGetSeconds([m_avPlayerItem currentTime]) * 1000;
803 // Rounding due to second offset error when subtracting.
804 return MediaTime::createWithDouble(round(date - currentTime));
807 bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
809 if (currentRenderingMode() == MediaRenderingToLayer)
810 return m_cachedIsReadyForDisplay;
812 return m_videoFrameHasDrawn;
815 #if ENABLE(AVF_CAPTIONS)
816 static const NSArray* mediaDescriptionForKind(PlatformTextTrack::TrackKind kind)
818 static bool manualSelectionMode = MTEnableCaption2015BehaviorPtr() && MTEnableCaption2015BehaviorPtr()();
819 if (manualSelectionMode)
820 return @[ AVMediaCharacteristicIsAuxiliaryContent ];
822 // FIXME: Match these to correct types:
823 if (kind == PlatformTextTrack::Caption)
824 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
826 if (kind == PlatformTextTrack::Subtitle)
827 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
829 if (kind == PlatformTextTrack::Description)
830 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, nil];
832 if (kind == PlatformTextTrack::Forced)
833 return [NSArray arrayWithObjects: AVMediaCharacteristicContainsOnlyForcedSubtitles, nil];
835 return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
838 void MediaPlayerPrivateAVFoundationObjC::notifyTrackModeChanged()
843 void MediaPlayerPrivateAVFoundationObjC::synchronizeTextTrackState()
845 const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
847 for (auto& textTrack : m_textTracks) {
848 if (textTrack->textTrackCategory() != InbandTextTrackPrivateAVF::OutOfBand)
851 RefPtr<OutOfBandTextTrackPrivateAVF> trackPrivate = static_cast<OutOfBandTextTrackPrivateAVF*>(textTrack.get());
852 RetainPtr<AVMediaSelectionOptionType> currentOption = trackPrivate->mediaSelectionOption();
854 for (auto& track : outOfBandTrackSources) {
855 RetainPtr<CFStringRef> uniqueID = String::number(track->uniqueId()).createCFString();
857 if (![[currentOption.get() outOfBandIdentifier] isEqual: reinterpret_cast<const NSString*>(uniqueID.get())])
860 InbandTextTrackPrivate::Mode mode = InbandTextTrackPrivate::Hidden;
861 if (track->mode() == PlatformTextTrack::Hidden)
862 mode = InbandTextTrackPrivate::Hidden;
863 else if (track->mode() == PlatformTextTrack::Disabled)
864 mode = InbandTextTrackPrivate::Disabled;
865 else if (track->mode() == PlatformTextTrack::Showing)
866 mode = InbandTextTrackPrivate::Showing;
868 textTrack->setMode(mode);
876 static NSURL *canonicalURL(const URL& url)
878 NSURL *cocoaURL = url;
882 RetainPtr<NSURLRequest> request = adoptNS([[NSURLRequest alloc] initWithURL:cocoaURL]);
886 NSURLRequest *canonicalRequest = [NSURLProtocol canonicalRequestForRequest:request.get()];
887 if (!canonicalRequest)
890 return [canonicalRequest URL];
894 static NSHTTPCookie* toNSHTTPCookie(const Cookie& cookie)
896 RetainPtr<NSMutableDictionary> properties = adoptNS([[NSMutableDictionary alloc] init]);
897 [properties setDictionary:@{
898 NSHTTPCookieName: cookie.name,
899 NSHTTPCookieValue: cookie.value,
900 NSHTTPCookieDomain: cookie.domain,
901 NSHTTPCookiePath: cookie.path,
902 NSHTTPCookieExpires: [NSDate dateWithTimeIntervalSince1970:(cookie.expires / 1000)],
905 [properties setObject:@YES forKey:NSHTTPCookieSecure];
907 [properties setObject:@YES forKey:NSHTTPCookieDiscard];
909 return [NSHTTPCookie cookieWithProperties:properties.get()];
913 void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const URL& url)
918 INFO_LOG(LOGIDENTIFIER);
920 setDelayCallbacks(true);
922 RetainPtr<NSMutableDictionary> options = adoptNS([[NSMutableDictionary alloc] init]);
924 [options.get() setObject:[NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote] forKey:AVURLAssetReferenceRestrictionsKey];
926 RetainPtr<NSMutableDictionary> headerFields = adoptNS([[NSMutableDictionary alloc] init]);
928 String referrer = player()->referrer();
929 if (!referrer.isEmpty())
930 [headerFields.get() setObject:referrer forKey:@"Referer"];
932 String userAgent = player()->userAgent();
933 if (!userAgent.isEmpty())
934 [headerFields.get() setObject:userAgent forKey:@"User-Agent"];
936 if ([headerFields.get() count])
937 [options.get() setObject:headerFields.get() forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
939 if (player()->doesHaveAttribute("x-itunes-inherit-uri-query-component"))
940 [options.get() setObject:@YES forKey: AVURLAssetInheritURIQueryComponentFromReferencingURIKey];
943 // FIXME: rdar://problem/20354688
944 String identifier = player()->sourceApplicationIdentifier();
945 if (!identifier.isEmpty() && AVURLAssetClientBundleIdentifierKey)
946 [options setObject:identifier forKey:AVURLAssetClientBundleIdentifierKey];
948 if (AVURLAssetRequiresCustomURLLoadingKey)
949 [options setObject:@YES forKey:AVURLAssetRequiresCustomURLLoadingKey];
952 auto type = player()->contentMIMEType();
953 if (AVURLAssetOutOfBandMIMETypeKey && !type.isEmpty() && !player()->contentMIMETypeWasInferredFromExtension()) {
954 auto codecs = player()->contentTypeCodecs();
955 if (!codecs.isEmpty()) {
956 NSString *typeString = [NSString stringWithFormat:@"%@; codecs=\"%@\"", (NSString *)type, (NSString *)codecs];
957 [options setObject:typeString forKey:AVURLAssetOutOfBandMIMETypeKey];
959 [options setObject:(NSString *)type forKey:AVURLAssetOutOfBandMIMETypeKey];
962 #if ENABLE(AVF_CAPTIONS)
963 const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
964 if (!outOfBandTrackSources.isEmpty()) {
965 RetainPtr<NSMutableArray> outOfBandTracks = adoptNS([[NSMutableArray alloc] init]);
966 for (auto& trackSource : outOfBandTrackSources) {
967 RetainPtr<CFStringRef> label = trackSource->label().createCFString();
968 RetainPtr<CFStringRef> language = trackSource->language().createCFString();
969 RetainPtr<CFStringRef> uniqueID = String::number(trackSource->uniqueId()).createCFString();
970 RetainPtr<CFStringRef> url = trackSource->url().createCFString();
971 [outOfBandTracks.get() addObject:@{
972 AVOutOfBandAlternateTrackDisplayNameKey: reinterpret_cast<const NSString*>(label.get()),
973 AVOutOfBandAlternateTrackExtendedLanguageTagKey: reinterpret_cast<const NSString*>(language.get()),
974 AVOutOfBandAlternateTrackIsDefaultKey: trackSource->isDefault() ? @YES : @NO,
975 AVOutOfBandAlternateTrackIdentifierKey: reinterpret_cast<const NSString*>(uniqueID.get()),
976 AVOutOfBandAlternateTrackSourceKey: reinterpret_cast<const NSString*>(url.get()),
977 AVOutOfBandAlternateTrackMediaCharactersticsKey: mediaDescriptionForKind(trackSource->kind()),
981 [options.get() setObject:outOfBandTracks.get() forKey:AVURLAssetOutOfBandAlternateTracksKey];
986 String networkInterfaceName = player()->mediaPlayerNetworkInterfaceName();
987 if (!networkInterfaceName.isEmpty())
988 [options setObject:networkInterfaceName forKey:AVURLAssetBoundNetworkInterfaceName];
992 Vector<Cookie> cookies;
993 if (player()->getRawCookies(url, cookies)) {
994 RetainPtr<NSMutableArray> nsCookies = adoptNS([[NSMutableArray alloc] initWithCapacity:cookies.size()]);
995 for (auto& cookie : cookies)
996 [nsCookies addObject:toNSHTTPCookie(cookie)];
998 [options setObject:nsCookies.get() forKey:AVURLAssetHTTPCookiesKey];
1002 bool usePersistentCache = player()->client().mediaPlayerShouldUsePersistentCache();
1003 [options setObject:@(!usePersistentCache) forKey:AVURLAssetUsesNoPersistentCacheKey];
1005 if (usePersistentCache)
1006 [options setObject:assetCacheForPath(player()->client().mediaPlayerMediaCacheDirectory()) forKey:AVURLAssetCacheKey];
1008 NSURL *cocoaURL = canonicalURL(url);
1009 m_avAsset = adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
1011 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1012 AVAssetResourceLoader *resourceLoader = m_avAsset.get().resourceLoader;
1013 [resourceLoader setDelegate:m_loaderDelegate.get() queue:globalLoaderDelegateQueue()];
1015 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED > 101100
1016 if (DeprecatedGlobalSettings::isAVFoundationNSURLSessionEnabled()
1017 && [resourceLoader respondsToSelector:@selector(setURLSession:)]
1018 && [resourceLoader respondsToSelector:@selector(URLSessionDataDelegate)]
1019 && [resourceLoader respondsToSelector:@selector(URLSessionDataDelegateQueue)]) {
1020 RefPtr<PlatformMediaResourceLoader> mediaResourceLoader = player()->createResourceLoader();
1021 if (mediaResourceLoader)
1022 resourceLoader.URLSession = (NSURLSession *)[[[WebCoreNSURLSession alloc] initWithResourceLoader:*mediaResourceLoader delegate:resourceLoader.URLSessionDataDelegate delegateQueue:resourceLoader.URLSessionDataDelegateQueue] autorelease];
1028 m_haveCheckedPlayability = false;
1030 setDelayCallbacks(false);
1033 void MediaPlayerPrivateAVFoundationObjC::setAVPlayerItem(AVPlayerItemType *item)
1038 if (pthread_main_np()) {
1039 [m_avPlayer replaceCurrentItemWithPlayerItem:item];
1043 RetainPtr<AVPlayerType> strongPlayer = m_avPlayer.get();
1044 RetainPtr<AVPlayerItemType> strongItem = item;
1045 dispatch_async(dispatch_get_main_queue(), [strongPlayer, strongItem] {
1046 [strongPlayer replaceCurrentItemWithPlayerItem:strongItem.get()];
1050 void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
1055 INFO_LOG(LOGIDENTIFIER);
1057 setDelayCallbacks(true);
1059 m_avPlayer = adoptNS([[AVPlayer alloc] init]);
1060 for (NSString *keyName in playerKVOProperties())
1061 [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
1063 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1064 [m_avPlayer.get() setAppliesMediaSelectionCriteriaAutomatically:NO];
1067 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1068 updateDisableExternalPlayback();
1069 [m_avPlayer.get() setAllowsExternalPlayback:m_allowsWirelessVideoPlayback];
1072 #if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
1073 if (m_shouldPlayToPlaybackTarget) {
1074 // Clear m_shouldPlayToPlaybackTarget so doesn't return without doing anything.
1075 m_shouldPlayToPlaybackTarget = false;
1076 setShouldPlayToPlaybackTarget(true);
1080 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
1081 setShouldDisableSleep(player()->shouldDisableSleep());
1085 // Clear m_muted so setMuted doesn't return without doing anything.
1087 [m_avPlayer.get() setMuted:m_muted];
1090 if (player()->client().mediaPlayerIsVideo())
1091 createAVPlayerLayer();
1094 setAVPlayerItem(m_avPlayerItem.get());
1096 setDelayCallbacks(false);
1099 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
1104 INFO_LOG(LOGIDENTIFIER);
1106 setDelayCallbacks(true);
1108 // Create the player item so we can load media data.
1109 m_avPlayerItem = adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
1111 [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
1113 NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionPrior;
1114 for (NSString *keyName in itemKVOProperties())
1115 [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:options context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem];
1117 [m_avPlayerItem setAudioTimePitchAlgorithm:(player()->preservesPitch() ? AVAudioTimePitchAlgorithmSpectral : AVAudioTimePitchAlgorithmVarispeed)];
1120 setAVPlayerItem(m_avPlayerItem.get());
1122 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1123 const NSTimeInterval legibleOutputAdvanceInterval = 2;
1125 RetainPtr<NSArray> subtypes = adoptNS([[NSArray alloc] initWithObjects:[NSNumber numberWithUnsignedInt:kCMSubtitleFormatType_WebVTT], nil]);
1126 m_legibleOutput = adoptNS([[AVPlayerItemLegibleOutput alloc] initWithMediaSubtypesForNativeRepresentation:subtypes.get()]);
1127 [m_legibleOutput.get() setSuppressesPlayerRendering:YES];
1129 [m_legibleOutput.get() setDelegate:m_objcObserver.get() queue:dispatch_get_main_queue()];
1130 [m_legibleOutput.get() setAdvanceIntervalForDelegateInvocation:legibleOutputAdvanceInterval];
1131 [m_legibleOutput.get() setTextStylingResolution:AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly];
1132 [m_avPlayerItem.get() addOutput:m_legibleOutput.get()];
1135 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
1137 m_provider->setPlayerItem(m_avPlayerItem.get());
1138 m_provider->setAudioTrack(firstEnabledTrack(safeAVAssetTracksForAudibleMedia()));
1142 setDelayCallbacks(false);
1145 void MediaPlayerPrivateAVFoundationObjC::checkPlayability()
1147 if (m_haveCheckedPlayability)
1149 m_haveCheckedPlayability = true;
1151 INFO_LOG(LOGIDENTIFIER);
1152 auto weakThis = createWeakPtr();
1154 [m_avAsset.get() loadValuesAsynchronouslyForKeys:[NSArray arrayWithObjects:@"playable", @"tracks", nil] completionHandler:^{
1155 callOnMainThread([weakThis] {
1157 weakThis->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
1162 void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata()
1164 INFO_LOG(LOGIDENTIFIER);
1166 OSObjectPtr<dispatch_group_t> metadataLoadingGroup = adoptOSObject(dispatch_group_create());
1167 dispatch_group_enter(metadataLoadingGroup.get());
1168 auto weakThis = createWeakPtr();
1169 [m_avAsset.get() loadValuesAsynchronouslyForKeys:assetMetadataKeyNames() completionHandler:^{
1171 callOnMainThread([weakThis, metadataLoadingGroup] {
1172 if (weakThis && [weakThis->m_avAsset.get() statusOfValueForKey:@"tracks" error:nil] == AVKeyValueStatusLoaded) {
1173 for (AVAssetTrack *track in [weakThis->m_avAsset.get() tracks]) {
1174 dispatch_group_enter(metadataLoadingGroup.get());
1175 [track loadValuesAsynchronouslyForKeys:assetTrackMetadataKeyNames() completionHandler:^{
1176 dispatch_group_leave(metadataLoadingGroup.get());
1180 dispatch_group_leave(metadataLoadingGroup.get());
1184 dispatch_group_notify(metadataLoadingGroup.get(), dispatch_get_main_queue(), ^{
1185 callOnMainThread([weakThis] {
1187 [weakThis->m_objcObserver.get() metadataLoaded];
1192 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::playerItemStatus() const
1194 if (!m_avPlayerItem)
1195 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
1197 if (m_cachedItemStatus == AVPlayerItemStatusUnknown)
1198 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
1199 if (m_cachedItemStatus == AVPlayerItemStatusFailed)
1200 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
1201 if (m_cachedLikelyToKeepUp)
1202 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
1203 if (m_cachedBufferFull)
1204 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
1205 if (m_cachedBufferEmpty)
1206 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
1208 return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
1211 PlatformMedia MediaPlayerPrivateAVFoundationObjC::platformMedia() const
1213 INFO_LOG(LOGIDENTIFIER);
1215 pm.type = PlatformMedia::AVFoundationMediaPlayerType;
1216 pm.media.avfMediaPlayer = m_avPlayer.get();
1220 PlatformLayer* MediaPlayerPrivateAVFoundationObjC::platformLayer() const
1222 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1223 return m_haveBeenAskedToCreateLayer ? m_videoFullscreenLayerManager->videoInlineLayer() : nullptr;
1225 return m_haveBeenAskedToCreateLayer ? m_videoLayer.get() : nullptr;
1229 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1230 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenLayer(PlatformLayer* videoFullscreenLayer, WTF::Function<void()>&& completionHandler)
1232 if (m_videoFullscreenLayerManager->videoFullscreenLayer() == videoFullscreenLayer) {
1233 completionHandler();
1237 [CATransaction begin];
1238 [CATransaction setDisableActions:YES];
1240 m_videoFullscreenLayerManager->setVideoFullscreenLayer(videoFullscreenLayer, WTFMove(completionHandler));
1242 if (m_videoFullscreenLayerManager->videoFullscreenLayer() && m_textTrackRepresentationLayer) {
1243 syncTextTrackBounds();
1244 [m_videoFullscreenLayerManager->videoFullscreenLayer() addSublayer:m_textTrackRepresentationLayer.get()];
1247 [CATransaction commit];
1249 updateDisableExternalPlayback();
1252 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenFrame(FloatRect frame)
1254 m_videoFullscreenLayerManager->setVideoFullscreenFrame(frame);
1255 syncTextTrackBounds();
1258 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
1260 m_videoFullscreenGravity = gravity;
1265 NSString *videoGravity = AVLayerVideoGravityResizeAspect;
1266 if (gravity == MediaPlayer::VideoGravityResize)
1267 videoGravity = AVLayerVideoGravityResize;
1268 else if (gravity == MediaPlayer::VideoGravityResizeAspect)
1269 videoGravity = AVLayerVideoGravityResizeAspect;
1270 else if (gravity == MediaPlayer::VideoGravityResizeAspectFill)
1271 videoGravity = AVLayerVideoGravityResizeAspectFill;
1273 ASSERT_NOT_REACHED();
1275 if ([m_videoLayer videoGravity] == videoGravity)
1278 [m_videoLayer setVideoGravity:videoGravity];
1279 syncTextTrackBounds();
1282 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode mode)
1285 [m_videoLayer setPIPModeEnabled:(mode & MediaPlayer::VideoFullscreenModePictureInPicture)];
1286 updateDisableExternalPlayback();
1292 #endif // PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1295 NSArray *MediaPlayerPrivateAVFoundationObjC::timedMetadata() const
1297 if (m_currentMetaData)
1298 return m_currentMetaData.get();
1302 String MediaPlayerPrivateAVFoundationObjC::accessLog() const
1304 if (!m_avPlayerItem)
1305 return emptyString();
1307 AVPlayerItemAccessLog *log = [m_avPlayerItem.get() accessLog];
1308 RetainPtr<NSString> logString = adoptNS([[NSString alloc] initWithData:[log extendedLogData] encoding:[log extendedLogDataStringEncoding]]);
1310 return logString.get();
1313 String MediaPlayerPrivateAVFoundationObjC::errorLog() const
1315 if (!m_avPlayerItem)
1316 return emptyString();
1318 AVPlayerItemErrorLog *log = [m_avPlayerItem.get() errorLog];
1319 RetainPtr<NSString> logString = adoptNS([[NSString alloc] initWithData:[log extendedLogData] encoding:[log extendedLogDataStringEncoding]]);
1321 return logString.get();
1325 void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
1327 [CATransaction begin];
1328 [CATransaction setDisableActions:YES];
1330 [m_videoLayer.get() setHidden:!isVisible];
1331 [CATransaction commit];
1334 void MediaPlayerPrivateAVFoundationObjC::platformPlay()
1336 INFO_LOG(LOGIDENTIFIER);
1337 if (!metaDataAvailable())
1340 setDelayCallbacks(true);
1341 m_cachedRate = requestedRate();
1342 [m_avPlayer.get() setRate:requestedRate()];
1343 setDelayCallbacks(false);
1346 void MediaPlayerPrivateAVFoundationObjC::platformPause()
1348 INFO_LOG(LOGIDENTIFIER);
1349 if (!metaDataAvailable())
1352 setDelayCallbacks(true);
1354 [m_avPlayer.get() setRate:0];
1355 setDelayCallbacks(false);
1358 MediaTime MediaPlayerPrivateAVFoundationObjC::platformDuration() const
1360 // Do not ask the asset for duration before it has been loaded or it will fetch the
1361 // answer synchronously.
1362 if (!m_avAsset || assetStatus() < MediaPlayerAVAssetStatusLoaded)
1363 return MediaTime::invalidTime();
1367 // Check the AVItem if we have one and it has loaded duration, some assets never report duration.
1368 if (m_avPlayerItem && playerItemStatus() >= MediaPlayerAVPlayerItemStatusReadyToPlay)
1369 cmDuration = [m_avPlayerItem.get() duration];
1371 cmDuration = [m_avAsset.get() duration];
1373 if (CMTIME_IS_NUMERIC(cmDuration))
1374 return PAL::toMediaTime(cmDuration);
1376 if (CMTIME_IS_INDEFINITE(cmDuration))
1377 return MediaTime::positiveInfiniteTime();
1379 INFO_LOG(LOGIDENTIFIER, "returning invalid time");
1380 return MediaTime::invalidTime();
1383 MediaTime MediaPlayerPrivateAVFoundationObjC::currentMediaTime() const
1385 if (!metaDataAvailable() || !m_avPlayerItem)
1386 return MediaTime::zeroTime();
1388 CMTime itemTime = [m_avPlayerItem.get() currentTime];
1389 if (CMTIME_IS_NUMERIC(itemTime))
1390 return std::max(PAL::toMediaTime(itemTime), MediaTime::zeroTime());
1392 return MediaTime::zeroTime();
1395 void MediaPlayerPrivateAVFoundationObjC::seekToTime(const MediaTime& time, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance)
1397 // setCurrentTime generates several event callbacks, update afterwards.
1398 setDelayCallbacks(true);
1400 if (m_metadataTrack)
1401 m_metadataTrack->flushPartialCues();
1403 CMTime cmTime = PAL::toCMTime(time);
1404 CMTime cmBefore = PAL::toCMTime(negativeTolerance);
1405 CMTime cmAfter = PAL::toCMTime(positiveTolerance);
1407 // [AVPlayerItem seekToTime] will throw an exception if toleranceBefore is negative.
1408 if (CMTimeCompare(cmBefore, kCMTimeZero) < 0)
1409 cmBefore = kCMTimeZero;
1411 auto weakThis = createWeakPtr();
1413 [m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
1414 callOnMainThread([weakThis, finished] {
1415 auto _this = weakThis.get();
1419 _this->seekCompleted(finished);
1423 setDelayCallbacks(false);
1426 void MediaPlayerPrivateAVFoundationObjC::setVolume(float volume)
1429 UNUSED_PARAM(volume);
1435 [m_avPlayer.get() setVolume:volume];
1439 void MediaPlayerPrivateAVFoundationObjC::setMuted(bool muted)
1441 if (m_muted == muted)
1444 INFO_LOG(LOGIDENTIFIER, "- ", muted);
1451 [m_avPlayer.get() setMuted:m_muted];
1454 void MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(bool closedCaptionsVisible)
1456 UNUSED_PARAM(closedCaptionsVisible);
1458 if (!metaDataAvailable())
1461 INFO_LOG(LOGIDENTIFIER, "- ", closedCaptionsVisible);
1464 void MediaPlayerPrivateAVFoundationObjC::setRateDouble(double rate)
1466 setDelayCallbacks(true);
1467 m_cachedRate = rate;
1468 [m_avPlayer.get() setRate:rate];
1469 setDelayCallbacks(false);
1472 double MediaPlayerPrivateAVFoundationObjC::rate() const
1474 if (!metaDataAvailable())
1477 return m_cachedRate;
1480 double MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesLastModifiedTime() const
1482 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
1483 return [m_avPlayerItem seekableTimeRangesLastModifiedTime];
1489 double MediaPlayerPrivateAVFoundationObjC::liveUpdateInterval() const
1491 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
1492 return [m_avPlayerItem liveUpdateInterval];
1498 void MediaPlayerPrivateAVFoundationObjC::setPreservesPitch(bool preservesPitch)
1501 [m_avPlayerItem setAudioTimePitchAlgorithm:(preservesPitch ? AVAudioTimePitchAlgorithmSpectral : AVAudioTimePitchAlgorithmVarispeed)];
1504 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateAVFoundationObjC::platformBufferedTimeRanges() const
1506 auto timeRanges = std::make_unique<PlatformTimeRanges>();
1508 if (!m_avPlayerItem)
1511 for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
1512 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1513 if (CMTIMERANGE_IS_VALID(timeRange) && !CMTIMERANGE_IS_EMPTY(timeRange))
1514 timeRanges->add(PAL::toMediaTime(timeRange.start), PAL::toMediaTime(CMTimeRangeGetEnd(timeRange)));
1519 MediaTime MediaPlayerPrivateAVFoundationObjC::platformMinTimeSeekable() const
1521 if (!m_cachedSeekableRanges || ![m_cachedSeekableRanges count])
1522 return MediaTime::zeroTime();
1524 MediaTime minTimeSeekable = MediaTime::positiveInfiniteTime();
1525 bool hasValidRange = false;
1526 for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1527 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1528 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1531 hasValidRange = true;
1532 MediaTime startOfRange = PAL::toMediaTime(timeRange.start);
1533 if (minTimeSeekable > startOfRange)
1534 minTimeSeekable = startOfRange;
1536 return hasValidRange ? minTimeSeekable : MediaTime::zeroTime();
1539 MediaTime MediaPlayerPrivateAVFoundationObjC::platformMaxTimeSeekable() const
1541 if (!m_cachedSeekableRanges)
1542 m_cachedSeekableRanges = [m_avPlayerItem seekableTimeRanges];
1544 MediaTime maxTimeSeekable;
1545 for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1546 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1547 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1550 MediaTime endOfRange = PAL::toMediaTime(CMTimeRangeGetEnd(timeRange));
1551 if (maxTimeSeekable < endOfRange)
1552 maxTimeSeekable = endOfRange;
1554 return maxTimeSeekable;
1557 MediaTime MediaPlayerPrivateAVFoundationObjC::platformMaxTimeLoaded() const
1559 if (!m_cachedLoadedRanges)
1560 return MediaTime::zeroTime();
1562 MediaTime maxTimeLoaded;
1563 for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
1564 CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1565 if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1568 MediaTime endOfRange = PAL::toMediaTime(CMTimeRangeGetEnd(timeRange));
1569 if (maxTimeLoaded < endOfRange)
1570 maxTimeLoaded = endOfRange;
1573 return maxTimeLoaded;
1576 unsigned long long MediaPlayerPrivateAVFoundationObjC::totalBytes() const
1578 if (!metaDataAvailable())
1581 if (m_cachedTotalBytes)
1582 return m_cachedTotalBytes;
1584 for (AVPlayerItemTrack *thisTrack in m_cachedTracks.get())
1585 m_cachedTotalBytes += [[thisTrack assetTrack] totalSampleDataLength];
1587 return m_cachedTotalBytes;
1590 void MediaPlayerPrivateAVFoundationObjC::setAsset(RetainPtr<id> asset)
1595 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
1598 return MediaPlayerAVAssetStatusDoesNotExist;
1600 for (NSString *keyName in assetMetadataKeyNames()) {
1601 NSError *error = nil;
1602 AVKeyValueStatus keyStatus = [m_avAsset.get() statusOfValueForKey:keyName error:&error];
1605 ERROR_LOG(LOGIDENTIFIER, "failed for ", [keyName UTF8String], ", error = ", [[error localizedDescription] UTF8String]);
1607 if (keyStatus < AVKeyValueStatusLoaded)
1608 return MediaPlayerAVAssetStatusLoading;// At least one key is not loaded yet.
1610 if (keyStatus == AVKeyValueStatusFailed)
1611 return MediaPlayerAVAssetStatusFailed; // At least one key could not be loaded.
1613 if (keyStatus == AVKeyValueStatusCancelled)
1614 return MediaPlayerAVAssetStatusCancelled; // Loading of at least one key was cancelled.
1617 if (!player()->shouldCheckHardwareSupport())
1618 m_tracksArePlayable = true;
1620 if (!m_tracksArePlayable) {
1621 m_tracksArePlayable = true;
1622 for (AVAssetTrack *track in [m_avAsset tracks]) {
1623 if (!assetTrackMeetsHardwareDecodeRequirements(track, player()->mediaContentTypesRequiringHardwareSupport())) {
1624 m_tracksArePlayable = false;
1630 if ([[m_avAsset.get() valueForKey:@"playable"] boolValue] && m_tracksArePlayable.value())
1631 return MediaPlayerAVAssetStatusPlayable;
1633 return MediaPlayerAVAssetStatusLoaded;
1636 long MediaPlayerPrivateAVFoundationObjC::assetErrorCode() const
1641 NSError *error = nil;
1642 [m_avAsset statusOfValueForKey:@"playable" error:&error];
1643 return [error code];
1646 void MediaPlayerPrivateAVFoundationObjC::paintCurrentFrameInContext(GraphicsContext& context, const FloatRect& rect)
1648 if (!metaDataAvailable() || context.paintingDisabled())
1651 setDelayCallbacks(true);
1652 BEGIN_BLOCK_OBJC_EXCEPTIONS;
1654 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1655 if (videoOutputHasAvailableFrame())
1656 paintWithVideoOutput(context, rect);
1659 paintWithImageGenerator(context, rect);
1661 END_BLOCK_OBJC_EXCEPTIONS;
1662 setDelayCallbacks(false);
1664 m_videoFrameHasDrawn = true;
1667 void MediaPlayerPrivateAVFoundationObjC::paint(GraphicsContext& context, const FloatRect& rect)
1669 if (!metaDataAvailable() || context.paintingDisabled())
1672 // We can ignore the request if we are already rendering to a layer.
1673 if (currentRenderingMode() == MediaRenderingToLayer)
1676 // paint() is best effort, so only paint if we already have an image generator or video output available.
1677 if (!hasContextRenderer())
1680 paintCurrentFrameInContext(context, rect);
1683 void MediaPlayerPrivateAVFoundationObjC::paintWithImageGenerator(GraphicsContext& context, const FloatRect& rect)
1685 INFO_LOG(LOGIDENTIFIER);
1687 RetainPtr<CGImageRef> image = createImageForTimeInRect(currentTime(), rect);
1689 GraphicsContextStateSaver stateSaver(context);
1690 context.translate(rect.x(), rect.y() + rect.height());
1691 context.scale(FloatSize(1.0f, -1.0f));
1692 context.setImageInterpolationQuality(InterpolationLow);
1693 IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
1694 CGContextDrawImage(context.platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
1698 RetainPtr<CGImageRef> MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(float time, const FloatRect& rect)
1700 if (!m_imageGenerator)
1701 createImageGenerator();
1702 ASSERT(m_imageGenerator);
1704 #if !RELEASE_LOG_DISABLED
1705 double start = monotonicallyIncreasingTime();
1708 [m_imageGenerator.get() setMaximumSize:CGSize(rect.size())];
1709 RetainPtr<CGImageRef> rawImage = adoptCF([m_imageGenerator.get() copyCGImageAtTime:CMTimeMakeWithSeconds(time, 600) actualTime:nil error:nil]);
1710 RetainPtr<CGImageRef> image = adoptCF(CGImageCreateCopyWithColorSpace(rawImage.get(), sRGBColorSpaceRef()));
1712 #if !RELEASE_LOG_DISABLED
1713 DEBUG_LOG(LOGIDENTIFIER, "creating image took ", monotonicallyIncreasingTime() - start);
1719 void MediaPlayerPrivateAVFoundationObjC::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& supportedTypes)
1721 supportedTypes = AVFoundationMIMETypeCache::singleton().types();
1724 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1725 static bool keySystemIsSupported(const String& keySystem)
1727 if (equalIgnoringASCIICase(keySystem, "com.apple.fps") || equalIgnoringASCIICase(keySystem, "com.apple.fps.1_0") || equalIgnoringASCIICase(keySystem, "org.w3c.clearkey"))
1733 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationObjC::supportsType(const MediaEngineSupportParameters& parameters)
1735 #if ENABLE(MEDIA_SOURCE)
1736 if (parameters.isMediaSource)
1737 return MediaPlayer::IsNotSupported;
1739 #if ENABLE(MEDIA_STREAM)
1740 if (parameters.isMediaStream)
1741 return MediaPlayer::IsNotSupported;
1744 auto containerType = parameters.type.containerType();
1745 if (isUnsupportedMIMEType(containerType))
1746 return MediaPlayer::IsNotSupported;
1748 if (!staticMIMETypeList().contains(containerType) && !AVFoundationMIMETypeCache::singleton().types().contains(containerType))
1749 return MediaPlayer::IsNotSupported;
1752 // "Implementors are encouraged to return "maybe" unless the type can be confidently established as being supported or not."
1753 if (parameters.type.codecs().isEmpty())
1754 return MediaPlayer::MayBeSupported;
1756 if (!contentTypeMeetsHardwareDecodeRequirements(parameters.type, parameters.contentTypesRequiringHardwareSupport))
1757 return MediaPlayer::IsNotSupported;
1759 NSString *typeString = [NSString stringWithFormat:@"%@; codecs=\"%@\"", (NSString *)containerType, (NSString *)parameters.type.parameter(ContentType::codecsParameter())];
1760 return [AVURLAsset isPlayableExtendedMIMEType:typeString] ? MediaPlayer::IsSupported : MediaPlayer::MayBeSupported;
1763 bool MediaPlayerPrivateAVFoundationObjC::supportsKeySystem(const String& keySystem, const String& mimeType)
1765 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1766 if (!keySystem.isEmpty()) {
1767 // "Clear Key" is only supported with HLS:
1768 if (equalIgnoringASCIICase(keySystem, "org.w3c.clearkey") && !mimeType.isEmpty() && !equalIgnoringASCIICase(mimeType, "application/x-mpegurl"))
1769 return MediaPlayer::IsNotSupported;
1771 if (!keySystemIsSupported(keySystem))
1774 if (!mimeType.isEmpty() && isUnsupportedMIMEType(mimeType))
1777 if (!mimeType.isEmpty() && !staticMIMETypeList().contains(mimeType) && !AVFoundationMIMETypeCache::singleton().types().contains(mimeType))
1783 UNUSED_PARAM(keySystem);
1784 UNUSED_PARAM(mimeType);
1789 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1790 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1791 static void fulfillRequestWithKeyData(AVAssetResourceLoadingRequest *request, ArrayBuffer* keyData)
1793 if (AVAssetResourceLoadingContentInformationRequest *infoRequest = [request contentInformationRequest]) {
1794 [infoRequest setContentLength:keyData->byteLength()];
1795 [infoRequest setByteRangeAccessSupported:YES];
1798 if (AVAssetResourceLoadingDataRequest *dataRequest = [request dataRequest]) {
1799 long long start = [dataRequest currentOffset];
1800 long long end = std::min<long long>(keyData->byteLength(), [dataRequest currentOffset] + [dataRequest requestedLength]);
1802 if (start < 0 || end < 0 || start >= static_cast<long long>(keyData->byteLength())) {
1803 [request finishLoadingWithError:nil];
1807 ASSERT(start <= std::numeric_limits<int>::max());
1808 ASSERT(end <= std::numeric_limits<int>::max());
1809 RefPtr<ArrayBuffer> requestedKeyData = keyData->slice(static_cast<int>(start), static_cast<int>(end));
1810 RetainPtr<NSData> nsData = adoptNS([[NSData alloc] initWithBytes:requestedKeyData->data() length:requestedKeyData->byteLength()]);
1811 [dataRequest respondWithData:nsData.get()];
1814 [request finishLoading];
1818 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest* avRequest)
1820 String scheme = [[[avRequest request] URL] scheme];
1821 String keyURI = [[[avRequest request] URL] absoluteString];
1823 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1824 if (scheme == "skd") {
1825 // Create an initData with the following layout:
1826 // [4 bytes: keyURI size], [keyURI size bytes: keyURI]
1827 unsigned keyURISize = keyURI.length() * sizeof(UChar);
1828 RefPtr<ArrayBuffer> initDataBuffer = ArrayBuffer::create(4 + keyURISize, 1);
1829 unsigned byteLength = initDataBuffer->byteLength();
1830 RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer.copyRef(), 0, byteLength);
1831 initDataView->set<uint32_t>(0, keyURISize, true);
1833 RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer.copyRef(), 4, keyURI.length());
1834 keyURIArray->setRange(StringView(keyURI).upconvertedCharacters(), keyURI.length() / sizeof(unsigned char), 0);
1836 RefPtr<Uint8Array> initData = Uint8Array::create(WTFMove(initDataBuffer), 0, byteLength);
1837 if (!player()->keyNeeded(initData.get()))
1840 m_keyURIToRequestMap.set(keyURI, avRequest);
1844 if (scheme == "clearkey") {
1845 String keyID = [[[avRequest request] URL] resourceSpecifier];
1846 StringView keyIDView(keyID);
1847 CString utf8EncodedKeyId = UTF8Encoding().encode(keyIDView, URLEncodedEntitiesForUnencodables);
1849 RefPtr<Uint8Array> initData = Uint8Array::create(utf8EncodedKeyId.length());
1850 initData->setRange(reinterpret_cast<const JSC::Uint8Adaptor::Type*>(utf8EncodedKeyId.data()), utf8EncodedKeyId.length(), 0);
1852 auto keyData = player()->cachedKeyForKeyId(keyID);
1854 fulfillRequestWithKeyData(avRequest, keyData.get());
1858 if (!player()->keyNeeded(initData.get()))
1861 m_keyURIToRequestMap.set(keyID, avRequest);
1866 RefPtr<WebCoreAVFResourceLoader> resourceLoader = WebCoreAVFResourceLoader::create(this, avRequest);
1867 m_resourceLoaderMap.add(avRequest, resourceLoader);
1868 resourceLoader->startLoading();
1872 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForResponseToAuthenticationChallenge(NSURLAuthenticationChallenge* nsChallenge)
1874 #if USE(CFURLCONNECTION)
1875 RefPtr<WebCoreNSURLAuthenticationChallengeClient> client = WebCoreNSURLAuthenticationChallengeClient::create(nsChallenge);
1876 RetainPtr<CFURLAuthChallengeRef> cfChallenge = adoptCF([nsChallenge _createCFAuthChallenge]);
1877 AuthenticationChallenge challenge(cfChallenge.get(), client.get());
1879 AuthenticationChallenge challenge(nsChallenge);
1882 return player()->shouldWaitForResponseToAuthenticationChallenge(challenge);
1885 void MediaPlayerPrivateAVFoundationObjC::didCancelLoadingRequest(AVAssetResourceLoadingRequest* avRequest)
1887 String scheme = [[[avRequest request] URL] scheme];
1889 WebCoreAVFResourceLoader* resourceLoader = m_resourceLoaderMap.get(avRequest);
1892 resourceLoader->stopLoading();
1895 void MediaPlayerPrivateAVFoundationObjC::didStopLoadingRequest(AVAssetResourceLoadingRequest *avRequest)
1897 m_resourceLoaderMap.remove(avRequest);
1901 bool MediaPlayerPrivateAVFoundationObjC::isAvailable()
1903 return AVFoundationLibrary() && isCoreMediaFrameworkAvailable();
1906 MediaTime MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(const MediaTime& timeValue) const
1908 if (!metaDataAvailable())
1911 // FIXME - impossible to implement until rdar://8721510 is fixed.
1915 double MediaPlayerPrivateAVFoundationObjC::maximumDurationToCacheMediaTime() const
1917 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1010
1924 void MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity()
1929 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1930 // Do not attempt to change the video gravity while in full screen mode.
1931 // See setVideoFullscreenGravity().
1932 if (m_videoFullscreenLayerManager->videoFullscreenLayer())
1936 [CATransaction begin];
1937 [CATransaction setDisableActions:YES];
1938 NSString* gravity = shouldMaintainAspectRatio() ? AVLayerVideoGravityResizeAspect : AVLayerVideoGravityResize;
1939 [m_videoLayer.get() setVideoGravity:gravity];
1940 [CATransaction commit];
1943 static AVAssetTrack* firstEnabledTrack(NSArray* tracks)
1945 NSUInteger index = [tracks indexOfObjectPassingTest:^(id obj, NSUInteger, BOOL *) {
1946 return [static_cast<AVAssetTrack*>(obj) isEnabled];
1948 if (index == NSNotFound)
1950 return [tracks objectAtIndex:index];
1953 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
1955 String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
1956 m_languageOfPrimaryAudioTrack = String();
1961 setDelayCharacteristicsChangedNotification(true);
1963 bool haveCCTrack = false;
1964 bool hasCaptions = false;
1966 // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
1967 // asked about those fairly fequently.
1968 if (!m_avPlayerItem) {
1969 // We don't have a player item yet, so check with the asset because some assets support inspection
1970 // prior to becoming ready to play.
1971 AVAssetTrack* firstEnabledVideoTrack = firstEnabledTrack([m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual]);
1972 setHasVideo(firstEnabledVideoTrack);
1973 setHasAudio(firstEnabledTrack([m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicAudible]));
1974 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1975 hasCaptions = [[m_avAsset.get() tracksWithMediaType:AVMediaTypeClosedCaption] count];
1977 auto size = firstEnabledVideoTrack ? FloatSize(CGSizeApplyAffineTransform([firstEnabledVideoTrack naturalSize], [firstEnabledVideoTrack preferredTransform])) : FloatSize();
1978 // For videos with rotation tag set, the transformation above might return a CGSize instance with negative width or height.
1979 // See https://bugs.webkit.org/show_bug.cgi?id=172648.
1980 if (size.width() < 0)
1981 size.setWidth(-size.width());
1982 if (size.height() < 0)
1983 size.setHeight(-size.height());
1984 presentationSizeDidChange(size);
1986 bool hasVideo = false;
1987 bool hasAudio = false;
1988 bool hasMetaData = false;
1989 for (AVPlayerItemTrack *track in m_cachedTracks.get()) {
1990 if ([track isEnabled]) {
1991 AVAssetTrack *assetTrack = [track assetTrack];
1992 NSString *mediaType = [assetTrack mediaType];
1993 if ([mediaType isEqualToString:AVMediaTypeVideo])
1995 else if ([mediaType isEqualToString:AVMediaTypeAudio])
1997 else if ([mediaType isEqualToString:AVMediaTypeClosedCaption]) {
1998 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2002 } else if ([mediaType isEqualToString:AVMediaTypeMetadata]) {
2008 #if ENABLE(VIDEO_TRACK)
2009 updateAudioTracks();
2010 updateVideoTracks();
2012 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2013 hasAudio |= (m_audibleGroup && m_audibleGroup->selectedOption());
2014 hasVideo |= (m_visualGroup && m_visualGroup->selectedOption());
2018 // Always says we have video if the AVPlayerLayer is ready for diaplay to work around
2019 // an AVFoundation bug which causes it to sometimes claim a track is disabled even
2021 setHasVideo(hasVideo || m_cachedIsReadyForDisplay);
2023 setHasAudio(hasAudio);
2024 #if ENABLE(DATACUE_VALUE)
2026 processMetadataTrack();
2030 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2031 AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
2032 if (legibleGroup && m_cachedTracks) {
2033 hasCaptions = [[AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]] count];
2035 processMediaSelectionOptions();
2039 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT) && HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2040 if (!hasCaptions && haveCCTrack)
2041 processLegacyClosedCaptionsTracks();
2042 #elif !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
2044 processLegacyClosedCaptionsTracks();
2047 setHasClosedCaptions(hasCaptions);
2049 INFO_LOG(LOGIDENTIFIER, "has video = ", hasVideo(), ", has audio = ", hasAudio(), ", has captions = ", hasClosedCaptions());
2053 if (primaryAudioTrackLanguage != languageOfPrimaryAudioTrack())
2054 characteristicsChanged();
2056 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
2058 m_provider->setAudioTrack(firstEnabledTrack(safeAVAssetTracksForAudibleMedia()));
2061 setDelayCharacteristicsChangedNotification(false);
2064 #if ENABLE(VIDEO_TRACK)
2066 template <typename RefT, typename PassRefT>
2067 void determineChangedTracksFromNewTracksAndOldItems(NSArray* tracks, NSString* trackType, Vector<RefT>& oldItems, RefT (*itemFactory)(AVPlayerItemTrack*), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
2069 RetainPtr<NSSet> newTracks = adoptNS([[NSSet alloc] initWithArray:[tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id track, NSUInteger, BOOL*){
2070 return [[[track assetTrack] mediaType] isEqualToString:trackType];
2072 RetainPtr<NSMutableSet> oldTracks = adoptNS([[NSMutableSet alloc] initWithCapacity:oldItems.size()]);
2074 for (auto& oldItem : oldItems) {
2075 if (oldItem->playerItemTrack())
2076 [oldTracks addObject:oldItem->playerItemTrack()];
2079 // Find the added & removed AVPlayerItemTracks:
2080 RetainPtr<NSMutableSet> removedTracks = adoptNS([oldTracks mutableCopy]);
2081 [removedTracks minusSet:newTracks.get()];
2083 RetainPtr<NSMutableSet> addedTracks = adoptNS([newTracks mutableCopy]);
2084 [addedTracks minusSet:oldTracks.get()];
2086 typedef Vector<RefT> ItemVector;
2087 ItemVector replacementItems;
2088 ItemVector addedItems;
2089 ItemVector removedItems;
2090 for (auto& oldItem : oldItems) {
2091 if (oldItem->playerItemTrack() && [removedTracks containsObject:oldItem->playerItemTrack()])
2092 removedItems.append(oldItem);
2094 replacementItems.append(oldItem);
2097 for (AVPlayerItemTrack* track in addedTracks.get())
2098 addedItems.append(itemFactory(track));
2100 replacementItems.appendVector(addedItems);
2101 oldItems.swap(replacementItems);
2103 for (auto& removedItem : removedItems)
2104 (player->*removedFunction)(*removedItem);
2106 for (auto& addedItem : addedItems)
2107 (player->*addedFunction)(*addedItem);
2110 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2112 template <typename RefT, typename PassRefT>
2113 void determineChangedTracksFromNewTracksAndOldItems(MediaSelectionGroupAVFObjC* group, Vector<RefT>& oldItems, const Vector<String>& characteristics, RefT (*itemFactory)(MediaSelectionOptionAVFObjC&), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
2115 group->updateOptions(characteristics);
2117 ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> newSelectionOptions;
2118 for (auto& option : group->options()) {
2121 AVMediaSelectionOptionType* avOption = option->avMediaSelectionOption();
2124 newSelectionOptions.add(option);
2127 ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> oldSelectionOptions;
2128 for (auto& oldItem : oldItems) {
2129 if (MediaSelectionOptionAVFObjC *option = oldItem->mediaSelectionOption())
2130 oldSelectionOptions.add(option);
2133 // Find the added & removed AVMediaSelectionOptions:
2134 ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> removedSelectionOptions;
2135 for (auto& oldOption : oldSelectionOptions) {
2136 if (!newSelectionOptions.contains(oldOption))
2137 removedSelectionOptions.add(oldOption);
2140 ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> addedSelectionOptions;
2141 for (auto& newOption : newSelectionOptions) {
2142 if (!oldSelectionOptions.contains(newOption))
2143 addedSelectionOptions.add(newOption);
2146 typedef Vector<RefT> ItemVector;
2147 ItemVector replacementItems;
2148 ItemVector addedItems;
2149 ItemVector removedItems;
2150 for (auto& oldItem : oldItems) {
2151 if (!oldItem->mediaSelectionOption())
2152 removedItems.append(oldItem);
2153 else if (removedSelectionOptions.contains(oldItem->mediaSelectionOption()))
2154 removedItems.append(oldItem);
2156 replacementItems.append(oldItem);
2159 for (auto& option : addedSelectionOptions)
2160 addedItems.append(itemFactory(*option.get()));
2162 replacementItems.appendVector(addedItems);
2163 oldItems.swap(replacementItems);
2165 for (auto& removedItem : removedItems)
2166 (player->*removedFunction)(*removedItem);
2168 for (auto& addedItem : addedItems)
2169 (player->*addedFunction)(*addedItem);
2174 void MediaPlayerPrivateAVFoundationObjC::updateAudioTracks()
2176 #if !RELEASE_LOG_DISABLED
2177 size_t count = m_audioTracks.size();
2180 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2181 Vector<String> characteristics = player()->preferredAudioCharacteristics();
2182 if (!m_audibleGroup) {
2183 if (AVMediaSelectionGroupType *group = safeMediaSelectionGroupForAudibleMedia())
2184 m_audibleGroup = MediaSelectionGroupAVFObjC::create(m_avPlayerItem.get(), group, characteristics);
2188 determineChangedTracksFromNewTracksAndOldItems(m_audibleGroup.get(), m_audioTracks, characteristics, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
2191 determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeAudio, m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
2193 for (auto& track : m_audioTracks)
2194 track->resetPropertiesFromTrack();
2196 #if !RELEASE_LOG_DISABLED
2197 INFO_LOG(LOGIDENTIFIER, "track count was ", count, ", is ", m_audioTracks.size());
2201 void MediaPlayerPrivateAVFoundationObjC::updateVideoTracks()
2203 #if !RELEASE_LOG_DISABLED
2204 size_t count = m_videoTracks.size();
2207 determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeVideo, m_videoTracks, &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
2209 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2210 if (!m_visualGroup) {
2211 if (AVMediaSelectionGroupType *group = safeMediaSelectionGroupForVisualMedia())
2212 m_visualGroup = MediaSelectionGroupAVFObjC::create(m_avPlayerItem.get(), group, Vector<String>());
2216 determineChangedTracksFromNewTracksAndOldItems(m_visualGroup.get(), m_videoTracks, Vector<String>(), &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
2219 for (auto& track : m_audioTracks)
2220 track->resetPropertiesFromTrack();
2222 #if !RELEASE_LOG_DISABLED
2223 INFO_LOG(LOGIDENTIFIER, "track count was ", count, ", is ", m_videoTracks.size());
2227 bool MediaPlayerPrivateAVFoundationObjC::requiresTextTrackRepresentation() const
2229 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
2230 if (m_videoFullscreenLayerManager->videoFullscreenLayer())
2236 void MediaPlayerPrivateAVFoundationObjC::syncTextTrackBounds()
2238 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
2239 if (!m_videoFullscreenLayerManager->videoFullscreenLayer() || !m_textTrackRepresentationLayer)
2242 [CATransaction begin];
2243 [CATransaction setDisableActions:YES];
2245 FloatRect videoFullscreenFrame = m_videoFullscreenLayerManager->videoFullscreenFrame();
2246 CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : CGRectMake(0, 0, videoFullscreenFrame.width(), videoFullscreenFrame.height());
2247 [m_textTrackRepresentationLayer setFrame:textFrame];
2249 [CATransaction commit];
2253 void MediaPlayerPrivateAVFoundationObjC::setTextTrackRepresentation(TextTrackRepresentation* representation)
2255 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
2256 PlatformLayer* representationLayer = representation ? representation->platformLayer() : nil;
2257 if (representationLayer == m_textTrackRepresentationLayer) {
2258 syncTextTrackBounds();
2262 [CATransaction begin];
2263 [CATransaction setDisableActions:YES];
2265 if (m_textTrackRepresentationLayer)
2266 [m_textTrackRepresentationLayer removeFromSuperlayer];
2268 m_textTrackRepresentationLayer = representationLayer;
2270 if (m_videoFullscreenLayerManager->videoFullscreenLayer() && m_textTrackRepresentationLayer) {
2271 syncTextTrackBounds();
2272 [m_videoFullscreenLayerManager->videoFullscreenLayer() addSublayer:m_textTrackRepresentationLayer.get()];
2275 [CATransaction commit];
2278 UNUSED_PARAM(representation);
2282 #endif // ENABLE(VIDEO_TRACK)
2284 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
2286 AudioSourceProvider* MediaPlayerPrivateAVFoundationObjC::audioSourceProvider()
2289 m_provider = AudioSourceProviderAVFObjC::create(m_avPlayerItem.get());
2290 m_provider->setAudioTrack(firstEnabledTrack(safeAVAssetTracksForAudibleMedia()));
2292 return m_provider.get();
2297 void MediaPlayerPrivateAVFoundationObjC::sizeChanged()
2302 setNaturalSize(m_cachedPresentationSize);
2305 void MediaPlayerPrivateAVFoundationObjC::resolvedURLChanged()
2307 setResolvedURL(m_avAsset ? URL([m_avAsset resolvedURL]) : URL());
2310 bool MediaPlayerPrivateAVFoundationObjC::didPassCORSAccessCheck() const
2312 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED > 101100
2313 AVAssetResourceLoader *resourceLoader = m_avAsset.get().resourceLoader;
2314 if (!DeprecatedGlobalSettings::isAVFoundationNSURLSessionEnabled()
2315 || ![resourceLoader respondsToSelector:@selector(URLSession)])
2318 WebCoreNSURLSession *session = (WebCoreNSURLSession *)resourceLoader.URLSession;
2319 if ([session isKindOfClass:[WebCoreNSURLSession class]])
2320 return session.didPassCORSAccessChecks;
2325 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
2327 void MediaPlayerPrivateAVFoundationObjC::createVideoOutput()
2329 INFO_LOG(LOGIDENTIFIER);
2331 if (!m_avPlayerItem || m_videoOutput)
2334 #if USE(VIDEOTOOLBOX)
2335 NSDictionary* attributes = nil;
2337 NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil];
2339 m_videoOutput = adoptNS([[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:attributes]);
2340 ASSERT(m_videoOutput);
2342 [m_videoOutput setDelegate:m_videoOutputDelegate.get() queue:globalPullDelegateQueue()];
2344 [m_avPlayerItem.get() addOutput:m_videoOutput.get()];
2347 void MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput()
2353 [m_avPlayerItem.get() removeOutput:m_videoOutput.get()];
2355 INFO_LOG(LOGIDENTIFIER);
2360 RetainPtr<CVPixelBufferRef> MediaPlayerPrivateAVFoundationObjC::createPixelBuffer()
2363 createVideoOutput();
2364 ASSERT(m_videoOutput);
2366 CMTime currentTime = [m_avPlayerItem.get() currentTime];
2368 if (![m_videoOutput.get() hasNewPixelBufferForItemTime:currentTime])
2371 return adoptCF([m_videoOutput.get() copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
2374 bool MediaPlayerPrivateAVFoundationObjC::videoOutputHasAvailableFrame()
2376 if (!m_avPlayerItem)
2383 createVideoOutput();
2385 return [m_videoOutput hasNewPixelBufferForItemTime:[m_avPlayerItem currentTime]];
2388 void MediaPlayerPrivateAVFoundationObjC::updateLastImage()
2390 RetainPtr<CVPixelBufferRef> pixelBuffer = createPixelBuffer();
2392 // Calls to copyPixelBufferForItemTime:itemTimeForDisplay: may return nil if the pixel buffer
2393 // for the requested time has already been retrieved. In this case, the last valid image (if any)
2394 // should be displayed.
2398 if (!m_pixelBufferConformer) {
2399 #if USE(VIDEOTOOLBOX)
2400 NSDictionary *attributes = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA) };
2402 NSDictionary *attributes = nil;
2404 m_pixelBufferConformer = std::make_unique<PixelBufferConformerCV>((CFDictionaryRef)attributes);
2407 #if !RELEASE_LOG_DISABLED
2408 double start = monotonicallyIncreasingTime();
2411 m_lastImage = m_pixelBufferConformer->createImageFromPixelBuffer(pixelBuffer.get());
2413 #if !RELEASE_LOG_DISABLED
2414 DEBUG_LOG(LOGIDENTIFIER, "creating buffer took ", monotonicallyIncreasingTime() - start);
2418 void MediaPlayerPrivateAVFoundationObjC::paintWithVideoOutput(GraphicsContext& context, const FloatRect& outputRect)
2420 if (m_videoOutput && !m_lastImage && !videoOutputHasAvailableFrame())
2421 waitForVideoOutputMediaDataWillChange();
2428 AVAssetTrack* firstEnabledVideoTrack = firstEnabledTrack([m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual]);
2429 if (!firstEnabledVideoTrack)
2432 INFO_LOG(LOGIDENTIFIER);
2434 GraphicsContextStateSaver stateSaver(context);
2435 FloatRect imageRect(0, 0, CGImageGetWidth(m_lastImage.get()), CGImageGetHeight(m_lastImage.get()));
2436 AffineTransform videoTransform = [firstEnabledVideoTrack preferredTransform];
2437 FloatRect transformedOutputRect = videoTransform.inverse().value_or(AffineTransform()).mapRect(outputRect);
2439 context.concatCTM(videoTransform);
2440 context.drawNativeImage(m_lastImage.get(), imageRect.size(), transformedOutputRect, imageRect);
2442 // If we have created an AVAssetImageGenerator in the past due to m_videoOutput not having an available
2443 // video frame, destroy it now that it is no longer needed.
2444 if (m_imageGenerator)
2445 destroyImageGenerator();
2449 void MediaPlayerPrivateAVFoundationObjC::createOpenGLVideoOutput()
2451 INFO_LOG(LOGIDENTIFIER);
2453 if (!m_avPlayerItem || m_openGLVideoOutput)
2457 NSDictionary* attributes = @{(NSString *)kCVPixelBufferIOSurfaceOpenGLESFBOCompatibilityKey: @YES};
2459 NSDictionary* attributes = @{(NSString *)kCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey: @YES};
2461 m_openGLVideoOutput = adoptNS([[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:attributes]);
2462 ASSERT(m_openGLVideoOutput);
2464 [m_avPlayerItem.get() addOutput:m_openGLVideoOutput.get()];
2467 void MediaPlayerPrivateAVFoundationObjC::destroyOpenGLVideoOutput()
2469 if (!m_openGLVideoOutput)
2472 INFO_LOG(LOGIDENTIFIER);
2475 [m_avPlayerItem.get() removeOutput:m_openGLVideoOutput.get()];
2477 m_openGLVideoOutput = 0;
2480 void MediaPlayerPrivateAVFoundationObjC::updateLastOpenGLImage()
2482 if (!m_openGLVideoOutput)
2485 CMTime currentTime = [m_openGLVideoOutput itemTimeForHostTime:CACurrentMediaTime()];
2486 if (![m_openGLVideoOutput hasNewPixelBufferForItemTime:currentTime])
2489 m_lastOpenGLImage = adoptCF([m_openGLVideoOutput copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
2492 bool MediaPlayerPrivateAVFoundationObjC::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY)
2496 if (!m_openGLVideoOutput)
2497 createOpenGLVideoOutput();
2499 updateLastOpenGLImage();
2501 if (!m_lastOpenGLImage)
2504 size_t width = CVPixelBufferGetWidth(m_lastOpenGLImage.get());
2505 size_t height = CVPixelBufferGetHeight(m_lastOpenGLImage.get());
2507 if (!m_textureCache) {
2508 m_textureCache = TextureCacheCV::create(*context);
2509 if (!m_textureCache)
2513 RetainPtr<CVOpenGLTextureRef> videoTexture = m_textureCache->textureFromImage(m_lastOpenGLImage.get(), outputTarget, level, internalFormat, format, type);
2515 if (!m_videoTextureCopier)
2516 m_videoTextureCopier = std::make_unique<VideoTextureCopierCV>(*context);
2518 return m_videoTextureCopier->copyVideoTextureToPlatformTexture(videoTexture.get(), width, height, outputTexture, outputTarget, level, internalFormat, format, type, premultiplyAlpha, flipY);
2521 NativeImagePtr MediaPlayerPrivateAVFoundationObjC::nativeImageForCurrentTime()
2527 void MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange()
2529 if (!m_videoOutputSemaphore)
2530 m_videoOutputSemaphore = dispatch_semaphore_create(0);
2532 [m_videoOutput requestNotificationOfMediaDataChangeWithAdvanceInterval:0];
2534 // Wait for 1 second.
2535 long result = dispatch_semaphore_wait(m_videoOutputSemaphore, dispatch_time(0, 1 * NSEC_PER_SEC));
2538 ERROR_LOG(LOGIDENTIFIER, "timed out");
2541 void MediaPlayerPrivateAVFoundationObjC::outputMediaDataWillChange(AVPlayerItemVideoOutputType *)
2543 dispatch_semaphore_signal(m_videoOutputSemaphore);
2548 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
2550 RetainPtr<AVAssetResourceLoadingRequest> MediaPlayerPrivateAVFoundationObjC::takeRequestForKeyURI(const String& keyURI)
2552 return m_keyURIToRequestMap.take(keyURI);
2555 void MediaPlayerPrivateAVFoundationObjC::keyAdded()
2557 Vector<String> fulfilledKeyIds;
2559 for (auto& pair : m_keyURIToRequestMap) {
2560 const String& keyId = pair.key;
2561 const RetainPtr<AVAssetResourceLoadingRequest>& request = pair.value;
2563 auto keyData = player()->cachedKeyForKeyId(keyId);
2567 fulfillRequestWithKeyData(request.get(), keyData.get());
2568 fulfilledKeyIds.append(keyId);
2571 for (auto& keyId : fulfilledKeyIds)
2572 m_keyURIToRequestMap.remove(keyId);
2575 void MediaPlayerPrivateAVFoundationObjC::removeSession(LegacyCDMSession& session)
2577 ASSERT_UNUSED(session, &session == m_session);
2578 m_session = nullptr;
2581 std::unique_ptr<LegacyCDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem, LegacyCDMSessionClient* client)
2583 if (!keySystemIsSupported(keySystem))
2585 auto session = std::make_unique<CDMSessionAVFoundationObjC>(this, client);
2586 m_session = session->createWeakPtr();
2587 return WTFMove(session);
2590 void MediaPlayerPrivateAVFoundationObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool newValue)
2592 if (m_session && newValue)
2593 m_session->playerDidReceiveError([NSError errorWithDomain:@"com.apple.WebKit" code:'HDCP' userInfo:nil]);
2598 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
2600 void MediaPlayerPrivateAVFoundationObjC::processLegacyClosedCaptionsTracks()
2602 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2603 [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2606 Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
2607 for (AVPlayerItemTrack *playerItemTrack in m_cachedTracks.get()) {
2609 AVAssetTrack *assetTrack = [playerItemTrack assetTrack];
2610 if (![[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
2613 bool newCCTrack = true;
2614 for (unsigned i = removedTextTracks.size(); i > 0; --i) {
2615 if (removedTextTracks[i - 1]->textTrackCategory() != InbandTextTrackPrivateAVF::LegacyClosedCaption)
2618 RefPtr<InbandTextTrackPrivateLegacyAVFObjC> track = static_cast<InbandTextTrackPrivateLegacyAVFObjC*>(m_textTracks[i - 1].get());
2619 if (track->avPlayerItemTrack() == playerItemTrack) {
2620 removedTextTracks.remove(i - 1);
2629 m_textTracks.append(InbandTextTrackPrivateLegacyAVFObjC::create(this, playerItemTrack));
2632 processNewAndRemovedTextTracks(removedTextTracks);
2637 NSArray* MediaPlayerPrivateAVFoundationObjC::safeAVAssetTracksForAudibleMedia()
2642 if ([m_avAsset.get() statusOfValueForKey:@"tracks" error:NULL] != AVKeyValueStatusLoaded)
2645 return [m_avAsset tracksWithMediaCharacteristic:AVMediaCharacteristicAudible];
2648 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2650 bool MediaPlayerPrivateAVFoundationObjC::hasLoadedMediaSelectionGroups()
2655 if ([m_avAsset.get() statusOfValueForKey:@"availableMediaCharacteristicsWithMediaSelectionOptions" error:NULL] != AVKeyValueStatusLoaded)
2661 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForLegibleMedia()
2663 if (!hasLoadedMediaSelectionGroups())
2666 return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
2669 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForAudibleMedia()
2671 if (!hasLoadedMediaSelectionGroups())
2674 return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
2677 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForVisualMedia()
2679 if (!hasLoadedMediaSelectionGroups())
2682 return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicVisual];
2685 void MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions()
2687 AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
2688 if (!legibleGroup) {
2689 INFO_LOG(LOGIDENTIFIER, "no mediaSelectionGroup");
2693 // We enabled automatic media selection because we want alternate audio tracks to be enabled/disabled automatically,
2694 // but set the selected legible track to nil so text tracks will not be automatically configured.
2695 if (!m_textTracks.size())
2696 [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2698 Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
2699 NSArray *legibleOptions = [AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]];
2700 for (AVMediaSelectionOptionType *option in legibleOptions) {
2701 bool newTrack = true;
2702 for (unsigned i = removedTextTracks.size(); i > 0; --i) {
2703 if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
2706 RetainPtr<AVMediaSelectionOptionType> currentOption;
2707 #if ENABLE(AVF_CAPTIONS)
2708 if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand) {
2709 RefPtr<OutOfBandTextTrackPrivateAVF> track = static_cast<OutOfBandTextTrackPrivateAVF*>(removedTextTracks[i - 1].get());
2710 currentOption = track->mediaSelectionOption();
2714 RefPtr<InbandTextTrackPrivateAVFObjC> track = static_cast<InbandTextTrackPrivateAVFObjC*>(removedTextTracks[i - 1].get());
2715 currentOption = track->mediaSelectionOption();
2718 if ([currentOption.get() isEqual:option]) {
2719 removedTextTracks.remove(i - 1);
2727 #if ENABLE(AVF_CAPTIONS)
2728 if ([option outOfBandSource]) {
2729 m_textTracks.append(OutOfBandTextTrackPrivateAVF::create(this, option));
2730 m_textTracks.last()->setHasBeenReported(true); // Ignore out-of-band tracks that we passed to AVFoundation so we do not double-count them
2735 m_textTracks.append(InbandTextTrackPrivateAVFObjC::create(this, option, InbandTextTrackPrivate::Generic));
2738 processNewAndRemovedTextTracks(removedTextTracks);
2741 void MediaPlayerPrivateAVFoundationObjC::processMetadataTrack()
2743 if (m_metadataTrack)
2746 m_metadataTrack = InbandMetadataTextTrackPrivateAVF::create(InbandTextTrackPrivate::Metadata, InbandTextTrackPrivate::Data);
2747 m_metadataTrack->setInBandMetadataTrackDispatchType("com.apple.streaming");
2748 player()->addTextTrack(*m_metadataTrack);
2751 void MediaPlayerPrivateAVFoundationObjC::processCue(NSArray *attributedStrings, NSArray *nativeSamples, const MediaTime& time)
2753 ASSERT(time >= MediaTime::zeroTime());
2755 if (!m_currentTextTrack)
2758 m_currentTextTrack->processCue(reinterpret_cast<CFArrayRef>(attributedStrings), reinterpret_cast<CFArrayRef>(nativeSamples), time);
2761 void MediaPlayerPrivateAVFoundationObjC::flushCues()
2763 INFO_LOG(LOGIDENTIFIER);
2765 if (!m_currentTextTrack)
2768 m_currentTextTrack->resetCueValues();
2771 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2773 void MediaPlayerPrivateAVFoundationObjC::setCurrentTextTrack(InbandTextTrackPrivateAVF *track)
2775 if (m_currentTextTrack == track)
2778 INFO_LOG(LOGIDENTIFIER, "selecting track with language ", track ? track->language() : "");
2780 m_currentTextTrack = track;
2783 if (track->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
2784 #pragma clang diagnostic push
2785 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2786 [m_avPlayer.get() setClosedCaptionDisplayEnabled:YES];
2787 #pragma clang diagnostic pop
2788 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2789 #if ENABLE(AVF_CAPTIONS)
2790 else if (track->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand)
2791 [m_avPlayerItem.get() selectMediaOption:static_cast<OutOfBandTextTrackPrivateAVF*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2794 [m_avPlayerItem.get() selectMediaOption:static_cast<InbandTextTrackPrivateAVFObjC*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2797 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2798 [m_avPlayerItem.get() selectMediaOption:0 inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2800 #pragma clang diagnostic push
2801 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2802 [m_avPlayer.get() setClosedCaptionDisplayEnabled:NO];
2803 #pragma clang diagnostic pop
2808 String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
2810 if (!m_languageOfPrimaryAudioTrack.isNull())
2811 return m_languageOfPrimaryAudioTrack;
2813 if (!m_avPlayerItem.get())
2814 return emptyString();
2816 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2817 // If AVFoundation has an audible group, return the language of the currently selected audible option.
2818 AVMediaSelectionGroupType *audibleGroup = [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
2819 #pragma clang diagnostic push
2820 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2821 AVMediaSelectionOptionType *currentlySelectedAudibleOption = [m_avPlayerItem.get() selectedMediaOptionInMediaSelectionGroup:audibleGroup];
2822 #pragma clang diagnostic pop
2823 if (currentlySelectedAudibleOption) {
2824 m_languageOfPrimaryAudioTrack = [[currentlySelectedAudibleOption locale] localeIdentifier];
2825 INFO_LOG(LOGIDENTIFIER, "language of selected audible option ", m_languageOfPrimaryAudioTrack);
2827 return m_languageOfPrimaryAudioTrack;
2829 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2831 // AVFoundation synthesizes an audible group when there is only one ungrouped audio track if there is also a legible group (one or
2832 // 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.
2833 NSArray *tracks = [m_avAsset.get() tracksWithMediaType:AVMediaTypeAudio];
2834 if (!tracks || [tracks count] != 1) {
2835 m_languageOfPrimaryAudioTrack = emptyString();
2836 INFO_LOG(LOGIDENTIFIER, tracks ? [tracks count] : 0, " audio tracks, returning empty");
2837 return m_languageOfPrimaryAudioTrack;
2840 AVAssetTrack *track = [tracks objectAtIndex:0];
2841 m_languageOfPrimaryAudioTrack = AVTrackPrivateAVFObjCImpl::languageForAVAssetTrack(track);
2843 INFO_LOG(LOGIDENTIFIER, "single audio track has language \"", m_languageOfPrimaryAudioTrack, "\"");
2845 return m_languageOfPrimaryAudioTrack;
2848 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2849 bool MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless() const
2851 bool wirelessTarget = false;
2854 if (m_playbackTarget) {
2855 if (m_playbackTarget->targetType() == MediaPlaybackTarget::AVFoundation)
2856 wirelessTarget = m_avPlayer && m_avPlayer.get().externalPlaybackActive;
2858 wirelessTarget = m_shouldPlayToPlaybackTarget && m_playbackTarget->hasActiveRoute();
2861 wirelessTarget = m_avPlayer && m_avPlayer.get().externalPlaybackActive;
2864 INFO_LOG(LOGIDENTIFIER, "- ", wirelessTarget);
2866 return wirelessTarget;
2869 MediaPlayer::WirelessPlaybackTargetType MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetType() const
2872 return MediaPlayer::TargetTypeNone;
2875 if (!AVFoundationLibrary())
2876 return MediaPlayer::TargetTypeNone;
2878 switch ([m_avPlayer externalPlaybackType]) {
2879 case AVPlayerExternalPlaybackTypeNone:
2880 return MediaPlayer::TargetTypeNone;
2881 case AVPlayerExternalPlaybackTypeAirPlay:
2882 return MediaPlayer::TargetTypeAirPlay;
2883 case AVPlayerExternalPlaybackTypeTVOut:
2884 return MediaPlayer::TargetTypeTVOut;
2887 ASSERT_NOT_REACHED();
2888 return MediaPlayer::TargetTypeNone;
2891 return MediaPlayer::TargetTypeAirPlay;
2896 static NSString *exernalDeviceDisplayNameForPlayer(AVPlayerType *player)
2898 NSString *displayName = nil;
2900 if (!AVFoundationLibrary())
2903 if (player.externalPlaybackType != AVPlayerExternalPlaybackTypeAirPlay)
2906 NSArray *pickableRoutes = CFBridgingRelease(MRMediaRemoteCopyPickableRoutes());
2907 if (!pickableRoutes.count)
2910 for (NSDictionary *pickableRoute in pickableRoutes) {
2911 if (![pickableRoute[AVController_RouteDescriptionKey_RouteCurrentlyPicked] boolValue])
2914 displayName = pickableRoute[AVController_RouteDescriptionKey_RouteName];
2916 NSString *routeName = pickableRoute[AVController_RouteDescriptionKey_AVAudioRouteName];
2917 if (![routeName isEqualToString:@"Speaker"] && ![routeName isEqualToString:@"HDMIOutput"])
2920 // The route is a speaker or HDMI out, override the name to be the localized device model.
2921 NSString *localizedDeviceModel = [[UIDevice currentDevice] localizedModel];
2923 // In cases where a route with that name already exists, prefix the name with the model.
2924 BOOL includeLocalizedDeviceModelName = NO;
2925 for (NSDictionary *otherRoute in pickableRoutes) {
2926 if (otherRoute == pickableRoute)
2929 if ([otherRoute[AVController_RouteDescriptionKey_RouteName] rangeOfString:displayName].location != NSNotFound) {
2930 includeLocalizedDeviceModelName = YES;
2935 if (includeLocalizedDeviceModelName)
2936 displayName = [NSString stringWithFormat:@"%@ %@", localizedDeviceModel, displayName];
2938 displayName = localizedDeviceModel;
2947 String MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName() const
2950 return emptyString();
2952 String wirelessTargetName;
2954 if (m_playbackTarget)
2955 wirelessTargetName = m_playbackTarget->deviceName();
2957 wirelessTargetName = exernalDeviceDisplayNameForPlayer(m_avPlayer.get());
2960 return wirelessTargetName;
2963 bool MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled() const
2966 return !m_allowsWirelessVideoPlayback;
2968 m_allowsWirelessVideoPlayback = [m_avPlayer.get() allowsExternalPlayback];
2969 INFO_LOG(LOGIDENTIFIER, "- ", !m_allowsWirelessVideoPlayback);
2971 return !m_allowsWirelessVideoPlayback;
2974 void MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(bool disabled)
2976 INFO_LOG(LOGIDENTIFIER, "- ", disabled);
2977 m_allowsWirelessVideoPlayback = !disabled;
2981 setDelayCallbacks(true);
2982 [m_avPlayer.get() setAllowsExternalPlayback:!disabled];
2983 setDelayCallbacks(false);
2988 void MediaPlayerPrivateAVFoundationObjC::setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&& target)
2990 m_playbackTarget = WTFMove(target);
2992 m_outputContext = m_playbackTarget->targetType() == MediaPlaybackTarget::AVFoundation ? toMediaPlaybackTargetMac(m_playbackTarget.get())->outputContext() : nullptr;
2994 INFO_LOG(LOGIDENTIFIER);
2996 if (!m_playbackTarget->hasActiveRoute())
2997 setShouldPlayToPlaybackTarget(false);
3000 void MediaPlayerPrivateAVFoundationObjC::setShouldPlayToPlaybackTarget(bool shouldPlay)
3002 if (m_shouldPlayToPlaybackTarget == shouldPlay)
3005 m_shouldPlayToPlaybackTarget = shouldPlay;
3007 if (!m_playbackTarget)
3010 INFO_LOG(LOGIDENTIFIER, "- ", shouldPlay);
3012 if (m_playbackTarget->targetType() == MediaPlaybackTarget::AVFoundation) {
3013 AVOutputContext *newContext = shouldPlay ? m_outputContext.get() : nil;
3018 RetainPtr<AVOutputContext> currentContext = m_avPlayer.get().outputContext;
3019 if ((!newContext && !currentContext.get()) || [currentContext.get() isEqual:newContext])
3022 setDelayCallbacks(true);
3023 m_avPlayer.get().outputContext = newContext;
3024 setDelayCallbacks(false);
3029 ASSERT(m_playbackTarget->targetType() == MediaPlaybackTarget::Mock);
3031 setDelayCallbacks(true);
3032 auto weakThis = createWeakPtr();
3033 scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification([weakThis] {
3036 weakThis->playbackTargetIsWirelessDidChange();
3038 setDelayCallbacks(false);
3041 #endif // !PLATFORM(IOS)
3043 void MediaPlayerPrivateAVFoundationObjC::updateDisableExternalPlayback()
3049 [m_avPlayer setUsesExternalPlaybackWhileExternalScreenIsActive:player()->fullscreenMode() & MediaPlayer::VideoFullscreenModeStandard];
3055 void MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange(int status)
3057 m_cachedItemStatus = status;
3062 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange()
3064 m_pendingStatusChanges++;
3067 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange(bool likelyToKeepUp)
3069 m_cachedLikelyToKeepUp = likelyToKeepUp;
3071 ASSERT(m_pendingStatusChanges);
3072 if (!--m_pendingStatusChanges)
3076 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange()
3078 m_pendingStatusChanges++;
3081 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange(bool bufferEmpty)
3083 m_cachedBufferEmpty = bufferEmpty;
3085 ASSERT(m_pendingStatusChanges);
3086 if (!--m_pendingStatusChanges)
3090 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange()
3092 m_pendingStatusChanges++;
3095 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange(bool bufferFull)
3097 m_cachedBufferFull = bufferFull;
3099 ASSERT(m_pendingStatusChanges);
3100 if (!--m_pendingStatusChanges)
3104 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray> seekableRanges)
3106 m_cachedSeekableRanges = seekableRanges;
3108 seekableTimeRangesChanged();
3112 void MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange(RetainPtr<NSArray> loadedRanges)
3114 m_cachedLoadedRanges = loadedRanges;
3116 loadedTimeRangesChanged();
3120 void MediaPlayerPrivateAVFoundationObjC::firstFrameAvailableDidChange(bool isReady)
3122 m_cachedIsReadyForDisplay = isReady;
3123 if (!hasVideo() && isReady)
3128 void MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange(bool)
3134 void MediaPlayerPrivateAVFoundationObjC::setShouldBufferData(bool shouldBuffer)
3136 INFO_LOG(LOGIDENTIFIER, "- ", shouldBuffer);
3138 if (m_shouldBufferData == shouldBuffer)
3141 m_shouldBufferData = shouldBuffer;
3146 setAVPlayerItem(shouldBuffer ? m_avPlayerItem.get() : nil);
3149 #if ENABLE(DATACUE_VALUE)
3151 static const AtomicString& metadataType(NSString *avMetadataKeySpace)
3153 static NeverDestroyed<const AtomicString> quickTimeUserData("com.apple.quicktime.udta", AtomicString::ConstructFromLiteral);
3154 static NeverDestroyed<const AtomicString> isoUserData("org.mp4ra", AtomicString::ConstructFromLiteral);
3155 static NeverDestroyed<const AtomicString> quickTimeMetadata("com.apple.quicktime.mdta", AtomicString::ConstructFromLiteral);
3156 static NeverDestroyed<const AtomicString> iTunesMetadata("com.apple.itunes", AtomicString::ConstructFromLiteral);
3157 static NeverDestroyed<const AtomicString> id3Metadata("org.id3", AtomicString::ConstructFromLiteral);
3159 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData])
3160 return quickTimeUserData;
3161 if (AVMetadataKeySpaceISOUserData && [avMetadataKeySpace isEqualToString:AVMetadataKeySpaceISOUserData])
3163 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata])
3164 return quickTimeMetadata;
3165 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceiTunes])
3166 return iTunesMetadata;
3167 if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceID3])
3175 void MediaPlayerPrivateAVFoundationObjC::metadataDidArrive(RetainPtr<NSArray> metadata, const MediaTime& mediaTime)
3177 m_currentMetaData = metadata && ![metadata isKindOfClass:[NSNull class]] ? metadata : nil;
3179 DEBUG_LOG(LOGIDENTIFIER, "adding ", m_currentMetaData ? [m_currentMetaData.get() count] : 0, " at time ", mediaTime);
3181 #if ENABLE(DATACUE_VALUE)
3185 if (!m_metadataTrack)
3186 processMetadataTrack();
3188 if (!metadata || [metadata isKindOfClass:[NSNull class]]) {
3189 m_metadataTrack->updatePendingCueEndTimes(mediaTime);
3193 // Set the duration of all incomplete cues before adding new ones.
3194 MediaTime earliestStartTime = MediaTime::positiveInfiniteTime();
3195 for (AVMetadataItemType *item in m_currentMetaData.get()) {
3196 MediaTime start = std::max(PAL::toMediaTime(item.time), MediaTime::zeroTime());
3197 if (start < earliestStartTime)
3198 earliestStartTime = start;
3200 m_metadataTrack->updatePendingCueEndTimes(earliestStartTime);
3202 for (AVMetadataItemType *item in m_currentMetaData.get()) {
3203 MediaTime start = std::max(PAL::toMediaTime(item.time), MediaTime::zeroTime());
3204 MediaTime end = MediaTime::positiveInfiniteTime();
3205 if (CMTIME_IS_VALID(item.duration))
3206 end = start + PAL::toMediaTime(item.duration);
3208 AtomicString type = nullAtom();
3210 type = metadataType(item.keySpace);
3212 m_metadataTrack->addDataCue(start, end, SerializedPlatformRepresentationMac::create(item), type);
3217 void MediaPlayerPrivateAVFoundationObjC::tracksDidChange(RetainPtr<NSArray> tracks)
3219 for (AVPlayerItemTrack *track in m_cachedTracks.get())
3220 [track removeObserver:m_objcObserver.get() forKeyPath:@"enabled"];
3222 NSArray *assetTracks = [m_avAsset tracks];
3224 m_cachedTracks = [tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id obj, NSUInteger, BOOL*) {
3225 AVAssetTrack* assetTrack = [obj assetTrack];
3227 if ([assetTracks containsObject:assetTrack])
3230 // Track is a streaming track. Omit if it belongs to a valid AVMediaSelectionGroup.
3231 if (!hasLoadedMediaSelectionGroups())
3234 if ([assetTrack hasMediaCharacteristic:AVMediaCharacteristicAudible] && safeMediaSelectionGroupForAudibleMedia())
3237 if ([assetTrack hasMediaCharacteristic:AVMediaCharacteristicVisual] && safeMediaSelectionGroupForVisualMedia())
3240 if ([assetTrack hasMediaCharacteristic:AVMediaCharacteristicLegible] && safeMediaSelectionGroupForLegibleMedia())
3246 for (AVPlayerItemTrack *track in m_cachedTracks.get())
3247 [track addObserver:m_objcObserver.get() forKeyPath:@"enabled" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayerItemTrack];
3249 m_cachedTotalBytes = 0;
3255 void MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange(bool hasEnabledAudio)
3257 m_cachedHasEnabledAudio = hasEnabledAudio;
3263 void MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange(FloatSize size)
3265 m_cachedPresentationSize = size;
3271 void MediaPlayerPrivateAVFoundationObjC::durationDidChange(const MediaTime& duration)
3273 m_cachedDuration = duration;
3275 invalidateCachedDuration();
3278 void MediaPlayerPrivateAVFoundationObjC::rateDidChange(double rate)
3280 m_cachedRate = rate;
3286 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3288 void MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange()
3290 playbackTargetIsWirelessChanged();
3295 void MediaPlayerPrivateAVFoundationObjC::canPlayFastForwardDidChange(bool newValue)
3297 m_cachedCanPlayFastForward = newValue;
3300 void MediaPlayerPrivateAVFoundationObjC::canPlayFastReverseDidChange(bool newValue)
3302 m_cachedCanPlayFastReverse = newValue;
3305 void MediaPlayerPrivateAVFoundationObjC::setShouldDisableSleep(bool flag)
3307 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
3308 [m_avPlayer _setPreventsSleepDuringVideoPlayback:flag];
3314 NSArray* assetMetadataKeyNames()
3316 static NSArray* keys = [[NSArray alloc] initWithObjects:
3319 @"preferredTransform",
3325 @"availableMediaCharacteristicsWithMediaSelectionOptions",
3330 NSArray* itemKVOProperties()
3332 static NSArray* keys = [[NSArray alloc] initWithObjects:
3333 @"presentationSize",
3337 @"seekableTimeRanges",
3338 @"loadedTimeRanges",
3339 @"playbackLikelyToKeepUp",
3340 @"playbackBufferFull",
3341 @"playbackBufferEmpty",
3345 @"canPlayFastForward",
3346 @"canPlayFastReverse",
3351 NSArray* assetTrackMetadataKeyNames()
3353 static NSArray* keys = [[NSArray alloc] initWithObjects:@"totalSampleDataLength", @"mediaType", @"enabled", @"preferredTransform", @"naturalSize", nil];
3357 NSArray* playerKVOProperties()
3359 static NSArray* keys = [[NSArray alloc] initWithObjects:
3361 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3362 @"externalPlaybackActive",
3363 @"allowsExternalPlayback",
3365 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
3366 @"outputObscuredDueToInsufficientExternalProtection",
3371 } // namespace WebCore
3373 @implementation WebCoreAVFMovieObserver
3375 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
3377 self = [super init];
3380 m_callback = callback;
3386 [NSObject cancelPreviousPerformRequestsWithTarget:self];
3390 - (void)metadataLoaded
3394 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
3397 - (void)didEnd:(NSNotification *)unusedNotification
3399 UNUSED_PARAM(unusedNotification);
3402 m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
3405 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context
3407 UNUSED_PARAM(object);
3408 id newValue = [change valueForKey:NSKeyValueChangeNewKey];
3413 bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
3414 bool shouldLogValue = !willChange;
3415 WTF::Function<void ()> function;
3417 if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) {
3418 if ([keyPath isEqualToString:@"readyForDisplay"])
3419 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::firstFrameAvailableDidChange, m_callback, [newValue boolValue]);
3422 if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) {
3423 if ([keyPath isEqualToString:@"enabled"])
3424 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange, m_callback, [newValue boolValue]);
3427 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) {
3428 if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
3429 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange, m_callback);
3430 else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
3431 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange, m_callback);
3432 else if ([keyPath isEqualToString:@"playbackBufferFull"])
3433 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange, m_callback);
3436 if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) {
3437 // A value changed for an AVPlayerItem
3438 if ([keyPath isEqualToString:@"status"])
3439 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange, m_callback, [newValue intValue]);
3440 else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
3441 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange, m_callback, [newValue boolValue]);
3442 else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
3443 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange, m_callback, [newValue boolValue]);
3444 else if ([keyPath isEqualToString:@"playbackBufferFull"])
3445 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange, m_callback, [newValue boolValue]);
3446 else if ([keyPath isEqualToString:@"asset"]) {
3447 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::setAsset, m_callback, RetainPtr<id>(newValue));
3448 shouldLogValue = false;
3449 } else if ([keyPath isEqualToString:@"loadedTimeRanges"])
3450 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
3451 else if ([keyPath isEqualToString:@"seekableTimeRanges"])
3452 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
3453 else if ([keyPath isEqualToString:@"tracks"]) {
3454 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::tracksDidChange, m_callback, RetainPtr<NSArray>(newValue));
3455 shouldLogValue = false;
3456 } else if ([keyPath isEqualToString:@"hasEnabledAudio"])
3457 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange, m_callback, [newValue boolValue]);
3458 else if ([keyPath isEqualToString:@"presentationSize"])
3459 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange, m_callback, FloatSize([newValue sizeValue]));
3460 else if ([keyPath isEqualToString:@"duration"])
3461 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::durationDidChange, m_callback, PAL::toMediaTime([newValue CMTimeValue]));
3462 else if ([keyPath isEqualToString:@"timedMetadata"] && newValue) {
3464 CMTime itemTime = [(AVPlayerItemType *)object currentTime];
3465 if (CMTIME_IS_NUMERIC(itemTime))
3466 now = std::max(PAL::toMediaTime(itemTime), MediaTime::zeroTime());
3467 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::metadataDidArrive, m_callback, RetainPtr<NSArray>(newValue), now);
3468 shouldLogValue = false;
3469 } else if ([keyPath isEqualToString:@"canPlayFastReverse"])
3470 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::canPlayFastReverseDidChange, m_callback, [newValue boolValue]);
3471 else if ([keyPath isEqualToString:@"canPlayFastForward"])
3472 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::canPlayFastForwardDidChange, m_callback, [newValue boolValue]);
3475 if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) {
3476 // A value changed for an AVPlayer.
3477 if ([keyPath isEqualToString:@"rate"])
3478 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::rateDidChange, m_callback, [newValue doubleValue]);
3479 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3480 else if ([keyPath isEqualToString:@"externalPlaybackActive"] || [keyPath isEqualToString:@"allowsExternalPlayback"])
3481 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange, m_callback);
3483 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
3484 else if ([keyPath isEqualToString:@"outputObscuredDueToInsufficientExternalProtection"])
3485 function = std::bind(&MediaPlayerPrivateAVFoundationObjC::outputObscuredDueToInsufficientExternalProtectionChanged, m_callback, [newValue boolValue]);
3489 #if !RELEASE_LOG_DISABLED
3490 if (m_callback->logger().willLog(m_callback->logChannel(), WTFLogLevelDebug) && !([keyPath isEqualToString:@"loadedTimeRanges"] || [keyPath isEqualToString:@"seekableTimeRanges"])) {
3491 auto identifier = PAL::Logger::LogSiteIdentifier("MediaPlayerPrivateAVFoundation", "observeValueForKeyPath", m_callback->logIdentifier());
3493 if (shouldLogValue) {
3494 if ([keyPath isEqualToString:@"duration"])
3495 m_callback->logger().debug(m_callback->logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", PAL::toMediaTime([newValue CMTimeValue]));
3497 RetainPtr<NSString> valueString = adoptNS([[NSString alloc] initWithFormat:@"%@", newValue]);
3498 m_callback->logger().debug(m_callback->logChannel(), identifier, "did change '", [keyPath UTF8String], "' to ", [valueString.get() UTF8String]);
3501 m_callback->logger().debug(m_callback->logChannel(), identifier, willChange ? "will" : "did", " change '", [keyPath UTF8String], "'");
3508 auto weakThis = m_callback->createWeakPtr();