bc2d7c1571fd1b912b00a72d2af79137a0065415
[WebKit-https.git] / Source / WebCore / platform / graphics / avfoundation / objc / MediaPlayerPrivateAVFoundationObjC.mm
1 /*
2  * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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. 
24  */
25
26 #import "config.h"
27 #import "MediaPlayerPrivateAVFoundationObjC.h"
28
29 #if ENABLE(VIDEO) && USE(AVFOUNDATION)
30
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"
38 #import "Cookie.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"
48 #import "Logging.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"
60 #import "URL.h"
61 #import "VideoTextureCopierCV.h"
62 #import "VideoTrackPrivateAVFObjC.h"
63 #import "WebCoreAVFResourceLoader.h"
64 #import "WebCoreCALayerExtras.h"
65 #import "WebCoreNSURLSession.h"
66 #import <functional>
67 #import <map>
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>
84
85 #if ENABLE(AVF_CAPTIONS)
86 #include "TextTrack.h"
87 #endif
88
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>
99
100 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
101 #import "VideoFullscreenLayerManager.h"
102 #endif
103
104 #if PLATFORM(IOS)
105 #import "WAKAppKitStubs.h"
106 #import <CoreImage/CoreImage.h>
107 #import <UIKit/UIDevice.h>
108 #import <mach/mach_port.h>
109 #else
110 #import <Foundation/NSGeometry.h>
111 #import <QuartzCore/CoreImage.h>
112 #endif
113
114 #if USE(VIDEOTOOLBOX)
115 #import <CoreVideo/CoreVideo.h>
116 #import <VideoToolbox/VideoToolbox.h>
117 #endif
118
119 #if USE(CFURLCONNECTION)
120 #include <pal/spi/cocoa/CFNSURLConnectionSPI.h>
121 #endif
122
123 #import "CoreVideoSoftLink.h"
124 #import "MediaRemoteSoftLink.h"
125
126 namespace std {
127 template <> struct iterator_traits<HashSet<RefPtr<WebCore::MediaSelectionOptionAVFObjC>>::iterator> {
128     typedef RefPtr<WebCore::MediaSelectionOptionAVFObjC> value_type;
129 };
130 }
131
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;
138 @end
139 #endif
140
141 @interface AVURLAsset (WebKitExtensions)
142 @property (nonatomic, readonly) NSURL *resolvedURL;
143 @end
144
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;
153
154 #pragma mark - Soft Linking
155
156 // Soft-linking headers must be included last since they #define functions, constants, etc.
157 #import <pal/cf/CoreMediaSoftLink.h>
158
159 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
160
161 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreImage)
162
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)
171
172 SOFT_LINK_CLASS(CoreImage, CIContext)
173 SOFT_LINK_CLASS(CoreImage, CIImage)
174
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 *)
190
191 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetClientBundleIdentifierKey, NSString *)
192 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetRequiresCustomURLLoadingKey, NSString *)
193 SOFT_LINK_POINTER_OPTIONAL(AVFoundation, AVURLAssetOutOfBandMIMETypeKey, NSString *)
194
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()
203
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()
222
223 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
224 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
225 typedef AVMediaSelectionOption AVMediaSelectionOptionType;
226
227 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemLegibleOutput)
228 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionGroup)
229 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionOption)
230
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 *)
235
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()
243 #endif
244
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*)
259
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()
273 #endif
274
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*)
281
282 #define AVMetadataKeySpaceQuickTimeUserData getAVMetadataKeySpaceQuickTimeUserData()
283 #define AVMetadataKeySpaceISOUserData getAVMetadataKeySpaceISOUserData()
284 #define AVMetadataKeySpaceQuickTimeMetadata getAVMetadataKeySpaceQuickTimeMetadata()
285 #define AVMetadataKeySpaceiTunes getAVMetadataKeySpaceiTunes()
286 #define AVMetadataKeySpaceID3 getAVMetadataKeySpaceID3()
287 #endif
288
289 #if PLATFORM(IOS)
290 SOFT_LINK_POINTER(AVFoundation, AVURLAssetBoundNetworkInterfaceName, NSString *)
291 #define AVURLAssetBoundNetworkInterfaceName getAVURLAssetBoundNetworkInterfaceName()
292 #endif
293
294 SOFT_LINK_FRAMEWORK(MediaToolbox)
295 SOFT_LINK_OPTIONAL(MediaToolbox, MTEnableCaption2015Behavior, Boolean, (), ())
296
297 #if PLATFORM(IOS)
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()
305
306 SOFT_LINK_FRAMEWORK(UIKit)
307 SOFT_LINK_CLASS(UIKit, UIDevice)
308 #define UIDevice getUIDeviceClass()
309 #endif
310
311 using namespace WebCore;
312
313 enum MediaPlayerAVFoundationObservationContext {
314     MediaPlayerAVFoundationObservationContextPlayerItem,
315     MediaPlayerAVFoundationObservationContextPlayerItemTrack,
316     MediaPlayerAVFoundationObservationContextPlayer,
317     MediaPlayerAVFoundationObservationContextAVPlayerLayer,
318 };
319
320 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
321 @interface WebCoreAVFMovieObserver : NSObject <AVPlayerItemLegibleOutputPushDelegate>
322 #else
323 @interface WebCoreAVFMovieObserver : NSObject
324 #endif
325 {
326     MediaPlayerPrivateAVFoundationObjC* m_callback;
327     int m_delayCallbacks;
328 }
329 -(id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
330 -(void)disconnect;
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;
337 #endif
338 @end
339
340 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
341 @interface WebCoreAVFLoaderDelegate : NSObject<AVAssetResourceLoaderDelegate> {
342     MediaPlayerPrivateAVFoundationObjC* m_callback;
343 }
344 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
345 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
346 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
347 @end
348 #endif
349
350 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
351 @interface WebCoreAVFPullDelegate : NSObject<AVPlayerItemOutputPullDelegate> {
352     MediaPlayerPrivateAVFoundationObjC *m_callback;
353     dispatch_semaphore_t m_semaphore;
354 }
355 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback;
356 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
357 - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender;
358 - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output;
359 @end
360 #endif
361
362 namespace WebCore {
363 using namespace PAL;
364
365 static NSArray *assetMetadataKeyNames();
366 static NSArray *itemKVOProperties();
367 static NSArray *assetTrackMetadataKeyNames();
368 static NSArray *playerKVOProperties();
369 static AVAssetTrack* firstEnabledTrack(NSArray* tracks);
370
371 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
372 static dispatch_queue_t globalLoaderDelegateQueue()
373 {
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);
378     });
379     return globalQueue;
380 }
381 #endif
382
383 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
384 static dispatch_queue_t globalPullDelegateQueue()
385 {
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);
390     });
391     return globalQueue;
392 }
393 #endif
394
395 #if USE(CFURLCONNECTION)
396 class WebCoreNSURLAuthenticationChallengeClient : public RefCounted<WebCoreNSURLAuthenticationChallengeClient>, public AuthenticationClient {
397 public:
398     static RefPtr<WebCoreNSURLAuthenticationChallengeClient> create(NSURLAuthenticationChallenge *challenge)
399     {
400         return adoptRef(new WebCoreNSURLAuthenticationChallengeClient(challenge));
401     }
402
403     using RefCounted<WebCoreNSURLAuthenticationChallengeClient>::ref;
404     using RefCounted<WebCoreNSURLAuthenticationChallengeClient>::deref;
405
406 private:
407     WebCoreNSURLAuthenticationChallengeClient(NSURLAuthenticationChallenge *challenge)
408         : m_challenge(challenge)
409     {
410         ASSERT(m_challenge);
411     }
412
413     void refAuthenticationClient() override { ref(); }
414     void derefAuthenticationClient() override { deref(); }
415
416     void receivedCredential(const AuthenticationChallenge&, const Credential& credential) override
417     {
418         [[m_challenge sender] useCredential:credential.nsCredential() forAuthenticationChallenge:m_challenge.get()];
419     }
420
421     void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) override
422     {
423         [[m_challenge sender] continueWithoutCredentialForAuthenticationChallenge:m_challenge.get()];
424     }
425
426     void receivedCancellation(const AuthenticationChallenge&) override
427     {
428         [[m_challenge sender] cancelAuthenticationChallenge:m_challenge.get()];
429     }
430
431     void receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) override
432     {
433         if ([[m_challenge sender] respondsToSelector:@selector(performDefaultHandlingForAuthenticationChallenge:)])
434             [[m_challenge sender] performDefaultHandlingForAuthenticationChallenge:m_challenge.get()];
435     }
436
437     void receivedChallengeRejection(const AuthenticationChallenge&) override
438     {
439         if ([[m_challenge sender] respondsToSelector:@selector(rejectProtectionSpaceAndContinueWithChallenge:)])
440             [[m_challenge sender] rejectProtectionSpaceAndContinueWithChallenge:m_challenge.get()];
441     }
442
443     RetainPtr<NSURLAuthenticationChallenge> m_challenge;
444 };
445 #endif
446
447 void MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(MediaEngineRegistrar registrar)
448 {
449     if (!isAvailable())
450         return;
451
452     registrar([](MediaPlayer* player) { return std::make_unique<MediaPlayerPrivateAVFoundationObjC>(player); },
453             getSupportedTypes, supportsType, originsInMediaCache, clearMediaCache, clearMediaCacheForOrigins, supportsKeySystem);
454     AVFoundationMIMETypeCache::singleton().loadTypes();
455 }
456
457 static AVAssetCacheType *assetCacheForPath(const String& path)
458 {
459     NSURL *assetCacheURL;
460     
461     if (path.isEmpty())
462         assetCacheURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"MediaCache" isDirectory:YES];
463     else
464         assetCacheURL = [NSURL fileURLWithPath:path isDirectory:YES];
465
466     return [initAVAssetCache() assetCacheWithURL:assetCacheURL];
467 }
468
469 HashSet<RefPtr<SecurityOrigin>> MediaPlayerPrivateAVFoundationObjC::originsInMediaCache(const String& path)
470 {
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));
476     }
477     return origins;
478 }
479
480 static std::chrono::system_clock::time_point toSystemClockTime(NSDate *date)
481 {
482     ASSERT(date);
483     using namespace std::chrono;
484
485     return system_clock::time_point(duration_cast<system_clock::duration>(duration<double>(date.timeIntervalSince1970)));
486 }
487
488 void MediaPlayerPrivateAVFoundationObjC::clearMediaCache(const String& path, std::chrono::system_clock::time_point modifiedSince)
489 {
490     AVAssetCacheType* assetCache = assetCacheForPath(path);
491     
492     for (NSString *key in [assetCache allKeys]) {
493         if (toSystemClockTime([assetCache lastModifiedDateOfEntryForKey:key]) > modifiedSince)
494             [assetCache removeEntryForKey:key];
495     }
496
497     NSFileManager *fileManager = [NSFileManager defaultManager];
498     NSURL *baseURL = [assetCache URL];
499
500     if (modifiedSince <= std::chrono::system_clock::time_point { }) {
501         [fileManager removeItemAtURL:baseURL error:nil];
502         return;
503     }
504     
505     NSArray *propertyKeys = @[NSURLNameKey, NSURLContentModificationDateKey, NSURLIsRegularFileKey];
506     NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:baseURL includingPropertiesForKeys:
507         propertyKeys options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
508         errorHandler:nil];
509     
510     RetainPtr<NSMutableArray> urlsToDelete = adoptNS([[NSMutableArray alloc] init]);
511     for (NSURL *fileURL : enumerator) {
512         NSDictionary *fileAttributes = [fileURL resourceValuesForKeys:propertyKeys error:nil];
513     
514         if (![fileAttributes[NSURLNameKey] hasPrefix:@"CachedMedia-"])
515             continue;
516         
517         if (![fileAttributes[NSURLIsRegularFileKey] boolValue])
518             continue;
519         
520         if (toSystemClockTime(fileAttributes[NSURLContentModificationDateKey]) <= modifiedSince)
521             continue;
522         
523         [urlsToDelete addObject:fileURL];
524     }
525     
526     for (NSURL *fileURL in urlsToDelete.get())
527         [fileManager removeItemAtURL:fileURL error:nil];
528 }
529
530 void MediaPlayerPrivateAVFoundationObjC::clearMediaCacheForOrigins(const String& path, const HashSet<RefPtr<SecurityOrigin>>& origins)
531 {
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];
538         }
539     }
540 }
541
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)
547 #endif
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)
554 #endif
555 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
556     , m_loaderDelegate(adoptNS([[WebCoreAVFLoaderDelegate alloc] initWithCallback:this]))
557 #endif
558     , m_currentTextTrack(0)
559     , m_cachedRate(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)
572 #endif
573 {
574 }
575
576 MediaPlayerPrivateAVFoundationObjC::~MediaPlayerPrivateAVFoundationObjC()
577 {
578 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
579     [m_loaderDelegate.get() setCallback:0];
580     [[m_avAsset.get() resourceLoader] setDelegate:nil queue:0];
581
582     for (auto& pair : m_resourceLoaderMap)
583         pair.value->invalidate();
584 #endif
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);
590 #endif
591
592     if (m_videoLayer)
593         destroyVideoLayer();
594
595     cancelLoad();
596 }
597
598 void MediaPlayerPrivateAVFoundationObjC::cancelLoad()
599 {
600     INFO_LOG(LOGIDENTIFIER);
601     tearDownVideoRendering();
602
603     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
604     [m_objcObserver.get() disconnect];
605
606     // Tell our observer to do nothing when our cancellation of pending loading calls its completion handler.
607     setIgnoreLoadStateChanges(true);
608     if (m_avAsset) {
609         [m_avAsset.get() cancelLoading];
610         m_avAsset = nil;
611     }
612
613     clearTextTracks();
614
615 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
616     if (m_legibleOutput) {
617         if (m_avPlayerItem)
618             [m_avPlayerItem.get() removeOutput:m_legibleOutput.get()];
619         m_legibleOutput = nil;
620     }
621 #endif
622
623     if (m_avPlayerItem) {
624         for (NSString *keyName in itemKVOProperties())
625             [m_avPlayerItem.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
626         
627         m_avPlayerItem = nil;
628     }
629     if (m_avPlayer) {
630         if (m_timeObserver)
631             [m_avPlayer.get() removeTimeObserver:m_timeObserver.get()];
632         m_timeObserver = nil;
633
634         for (NSString *keyName in playerKVOProperties())
635             [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
636
637         [m_avPlayer replaceCurrentItemWithPlayerItem:nil];
638         m_avPlayer = nil;
639     }
640
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();
649
650     for (AVPlayerItemTrack *track in m_cachedTracks.get())
651         [track removeObserver:m_objcObserver.get() forKeyPath:@"enabled"];
652     m_cachedTracks = nullptr;
653
654 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
655     if (m_provider) {
656         m_provider->setPlayerItem(nullptr);
657         m_provider->setAudioTrack(nullptr);
658     }
659 #endif
660
661     setIgnoreLoadStateChanges(false);
662 }
663
664 bool MediaPlayerPrivateAVFoundationObjC::hasLayerRenderer() const
665 {
666     return m_haveBeenAskedToCreateLayer;
667 }
668
669 bool MediaPlayerPrivateAVFoundationObjC::hasContextRenderer() const
670 {
671 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
672     if (m_videoOutput)
673         return true;
674 #endif
675     return m_imageGenerator;
676 }
677
678 void MediaPlayerPrivateAVFoundationObjC::createContextVideoRenderer()
679 {
680 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
681     createVideoOutput();
682 #else
683     createImageGenerator();
684 #endif
685 }
686
687 void MediaPlayerPrivateAVFoundationObjC::createImageGenerator()
688 {
689     using namespace PAL;
690     INFO_LOG(LOGIDENTIFIER);
691
692     if (!m_avAsset || m_imageGenerator)
693         return;
694
695     m_imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:m_avAsset.get()];
696
697     [m_imageGenerator.get() setApertureMode:AVAssetImageGeneratorApertureModeCleanAperture];
698     [m_imageGenerator.get() setAppliesPreferredTrackTransform:YES];
699     [m_imageGenerator.get() setRequestedTimeToleranceBefore:kCMTimeZero];
700     [m_imageGenerator.get() setRequestedTimeToleranceAfter:kCMTimeZero];
701 }
702
703 void MediaPlayerPrivateAVFoundationObjC::destroyContextVideoRenderer()
704 {
705 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
706     destroyVideoOutput();
707     destroyOpenGLVideoOutput();
708 #endif
709     destroyImageGenerator();
710 }
711
712 void MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator()
713 {
714     if (!m_imageGenerator)
715         return;
716
717     INFO_LOG(LOGIDENTIFIER);
718
719     m_imageGenerator = 0;
720 }
721
722 void MediaPlayerPrivateAVFoundationObjC::createVideoLayer()
723 {
724     if (!m_avPlayer || m_haveBeenAskedToCreateLayer)
725         return;
726
727     callOnMainThread([this, weakThis = createWeakPtr()] {
728         if (!weakThis)
729             return;
730
731         if (!m_avPlayer || m_haveBeenAskedToCreateLayer)
732             return;
733         m_haveBeenAskedToCreateLayer = true;
734
735         if (!m_videoLayer)
736             createAVPlayerLayer();
737
738 #if USE(VIDEOTOOLBOX)
739         if (!m_videoOutput)
740             createVideoOutput();
741 #endif
742
743         player()->client().mediaPlayerRenderingModeChanged(player());
744     });
745 }
746
747 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerLayer()
748 {
749     if (!m_avPlayer)
750         return;
751
752     m_videoLayer = adoptNS([[AVPlayerLayer alloc] init]);
753     [m_videoLayer setPlayer:m_avPlayer.get()];
754
755 #ifndef NDEBUG
756     [m_videoLayer setName:@"MediaPlayerPrivate AVPlayerLayer"];
757 #endif
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);
763
764 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
765     m_videoFullscreenLayerManager->setVideoLayer(m_videoLayer.get(), defaultSize);
766
767 #if PLATFORM(IOS)
768     [m_videoLayer setPIPModeEnabled:(player()->fullscreenMode() & MediaPlayer::VideoFullscreenModePictureInPicture)];
769 #endif
770 #else
771     [m_videoLayer setFrame:CGRectMake(0, 0, defaultSize.width(), defaultSize.height())];
772 #endif
773 }
774
775 void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer()
776 {
777     if (!m_videoLayer)
778         return;
779
780     INFO_LOG(LOGIDENTIFIER);
781
782     [m_videoLayer removeObserver:m_objcObserver.get() forKeyPath:@"readyForDisplay"];
783     [m_videoLayer setPlayer:nil];
784
785 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
786     m_videoFullscreenLayerManager->didDestroyVideoLayer();
787 #endif
788
789     m_videoLayer = nil;
790 }
791
792 MediaTime MediaPlayerPrivateAVFoundationObjC::getStartDate() const
793 {
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;
796
797     // No live streams were made during the epoch (1970). AVFoundation returns 0 if the media file doesn't have a start date
798     if (!date)
799         return MediaTime::invalidTime();
800
801     double currentTime = CMTimeGetSeconds([m_avPlayerItem currentTime]) * 1000;
802
803     // Rounding due to second offset error when subtracting.
804     return MediaTime::createWithDouble(round(date - currentTime));
805 }
806
807 bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
808 {
809     if (currentRenderingMode() == MediaRenderingToLayer)
810         return m_cachedIsReadyForDisplay;
811
812     return m_videoFrameHasDrawn;
813 }
814
815 #if ENABLE(AVF_CAPTIONS)
816 static const NSArray* mediaDescriptionForKind(PlatformTextTrack::TrackKind kind)
817 {
818     static bool manualSelectionMode = MTEnableCaption2015BehaviorPtr() && MTEnableCaption2015BehaviorPtr()();
819     if (manualSelectionMode)
820         return @[ AVMediaCharacteristicIsAuxiliaryContent ];
821
822     // FIXME: Match these to correct types:
823     if (kind == PlatformTextTrack::Caption)
824         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
825
826     if (kind == PlatformTextTrack::Subtitle)
827         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
828
829     if (kind == PlatformTextTrack::Description)
830         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, nil];
831
832     if (kind == PlatformTextTrack::Forced)
833         return [NSArray arrayWithObjects: AVMediaCharacteristicContainsOnlyForcedSubtitles, nil];
834
835     return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
836 }
837     
838 void MediaPlayerPrivateAVFoundationObjC::notifyTrackModeChanged()
839 {
840     trackModeChanged();
841 }
842     
843 void MediaPlayerPrivateAVFoundationObjC::synchronizeTextTrackState()
844 {
845     const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
846     
847     for (auto& textTrack : m_textTracks) {
848         if (textTrack->textTrackCategory() != InbandTextTrackPrivateAVF::OutOfBand)
849             continue;
850         
851         RefPtr<OutOfBandTextTrackPrivateAVF> trackPrivate = static_cast<OutOfBandTextTrackPrivateAVF*>(textTrack.get());
852         RetainPtr<AVMediaSelectionOptionType> currentOption = trackPrivate->mediaSelectionOption();
853         
854         for (auto& track : outOfBandTrackSources) {
855             RetainPtr<CFStringRef> uniqueID = String::number(track->uniqueId()).createCFString();
856             
857             if (![[currentOption.get() outOfBandIdentifier] isEqual: reinterpret_cast<const NSString*>(uniqueID.get())])
858                 continue;
859             
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;
867             
868             textTrack->setMode(mode);
869             break;
870         }
871     }
872 }
873 #endif
874
875
876 static NSURL *canonicalURL(const URL& url)
877 {
878     NSURL *cocoaURL = url;
879     if (url.isEmpty())
880         return cocoaURL;
881
882     RetainPtr<NSURLRequest> request = adoptNS([[NSURLRequest alloc] initWithURL:cocoaURL]);
883     if (!request)
884         return cocoaURL;
885
886     NSURLRequest *canonicalRequest = [NSURLProtocol canonicalRequestForRequest:request.get()];
887     if (!canonicalRequest)
888         return cocoaURL;
889
890     return [canonicalRequest URL];
891 }
892
893 #if PLATFORM(IOS)
894 static NSHTTPCookie* toNSHTTPCookie(const Cookie& cookie)
895 {
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)],
903     }];
904     if (cookie.secure)
905         [properties setObject:@YES forKey:NSHTTPCookieSecure];
906     if (cookie.session)
907         [properties setObject:@YES forKey:NSHTTPCookieDiscard];
908
909     return [NSHTTPCookie cookieWithProperties:properties.get()];
910 }
911 #endif
912
913 void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const URL& url)
914 {
915     if (m_avAsset)
916         return;
917
918     INFO_LOG(LOGIDENTIFIER);
919
920     setDelayCallbacks(true);
921
922     RetainPtr<NSMutableDictionary> options = adoptNS([[NSMutableDictionary alloc] init]);    
923
924     [options.get() setObject:[NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote] forKey:AVURLAssetReferenceRestrictionsKey];
925
926     RetainPtr<NSMutableDictionary> headerFields = adoptNS([[NSMutableDictionary alloc] init]);
927
928     String referrer = player()->referrer();
929     if (!referrer.isEmpty())
930         [headerFields.get() setObject:referrer forKey:@"Referer"];
931
932     String userAgent = player()->userAgent();
933     if (!userAgent.isEmpty())
934         [headerFields.get() setObject:userAgent forKey:@"User-Agent"];
935
936     if ([headerFields.get() count])
937         [options.get() setObject:headerFields.get() forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
938
939     if (player()->doesHaveAttribute("x-itunes-inherit-uri-query-component"))
940         [options.get() setObject:@YES forKey: AVURLAssetInheritURIQueryComponentFromReferencingURIKey];
941
942 #if PLATFORM(IOS)
943     // FIXME: rdar://problem/20354688
944     String identifier = player()->sourceApplicationIdentifier();
945     if (!identifier.isEmpty() && AVURLAssetClientBundleIdentifierKey)
946         [options setObject:identifier forKey:AVURLAssetClientBundleIdentifierKey];
947
948     if (AVURLAssetRequiresCustomURLLoadingKey)
949         [options setObject:@YES forKey:AVURLAssetRequiresCustomURLLoadingKey];
950 #endif
951
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];
958         } else
959             [options setObject:(NSString *)type forKey:AVURLAssetOutOfBandMIMETypeKey];
960     }
961
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()),
978             }];
979         }
980
981         [options.get() setObject:outOfBandTracks.get() forKey:AVURLAssetOutOfBandAlternateTracksKey];
982     }
983 #endif
984
985 #if PLATFORM(IOS)
986     String networkInterfaceName = player()->mediaPlayerNetworkInterfaceName();
987     if (!networkInterfaceName.isEmpty())
988         [options setObject:networkInterfaceName forKey:AVURLAssetBoundNetworkInterfaceName];
989 #endif
990
991 #if PLATFORM(IOS)
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)];
997
998         [options setObject:nsCookies.get() forKey:AVURLAssetHTTPCookiesKey];
999     }
1000 #endif
1001
1002     bool usePersistentCache = player()->client().mediaPlayerShouldUsePersistentCache();
1003     [options setObject:@(!usePersistentCache) forKey:AVURLAssetUsesNoPersistentCacheKey];
1004     
1005     if (usePersistentCache)
1006         [options setObject:assetCacheForPath(player()->client().mediaPlayerMediaCacheDirectory()) forKey:AVURLAssetCacheKey];
1007
1008     NSURL *cocoaURL = canonicalURL(url);
1009     m_avAsset = adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
1010
1011 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1012     AVAssetResourceLoader *resourceLoader = m_avAsset.get().resourceLoader;
1013     [resourceLoader setDelegate:m_loaderDelegate.get() queue:globalLoaderDelegateQueue()];
1014
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];
1023     }
1024 #endif
1025
1026 #endif
1027
1028     m_haveCheckedPlayability = false;
1029
1030     setDelayCallbacks(false);
1031 }
1032
1033 void MediaPlayerPrivateAVFoundationObjC::setAVPlayerItem(AVPlayerItemType *item)
1034 {
1035     if (!m_avPlayer)
1036         return;
1037
1038     if (pthread_main_np()) {
1039         [m_avPlayer replaceCurrentItemWithPlayerItem:item];
1040         return;
1041     }
1042
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()];
1047     });
1048 }
1049
1050 void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
1051 {
1052     if (m_avPlayer)
1053         return;
1054
1055     INFO_LOG(LOGIDENTIFIER);
1056
1057     setDelayCallbacks(true);
1058
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];
1062
1063 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1064     [m_avPlayer.get() setAppliesMediaSelectionCriteriaAutomatically:NO];
1065 #endif
1066
1067 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
1068     updateDisableExternalPlayback();
1069     [m_avPlayer.get() setAllowsExternalPlayback:m_allowsWirelessVideoPlayback];
1070 #endif
1071
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);
1077     }
1078 #endif
1079
1080 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
1081     setShouldDisableSleep(player()->shouldDisableSleep());
1082 #endif
1083
1084     if (m_muted) {
1085         // Clear m_muted so setMuted doesn't return without doing anything.
1086         m_muted = false;
1087         [m_avPlayer.get() setMuted:m_muted];
1088     }
1089
1090     if (player()->client().mediaPlayerIsVideo())
1091         createAVPlayerLayer();
1092
1093     if (m_avPlayerItem)
1094         setAVPlayerItem(m_avPlayerItem.get());
1095
1096     setDelayCallbacks(false);
1097 }
1098
1099 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
1100 {
1101     if (m_avPlayerItem)
1102         return;
1103
1104     INFO_LOG(LOGIDENTIFIER);
1105
1106     setDelayCallbacks(true);
1107
1108     // Create the player item so we can load media data. 
1109     m_avPlayerItem = adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
1110
1111     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
1112
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];
1116
1117     [m_avPlayerItem setAudioTimePitchAlgorithm:(player()->preservesPitch() ? AVAudioTimePitchAlgorithmSpectral : AVAudioTimePitchAlgorithmVarispeed)];
1118
1119     if (m_avPlayer)
1120         setAVPlayerItem(m_avPlayerItem.get());
1121
1122 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1123     const NSTimeInterval legibleOutputAdvanceInterval = 2;
1124
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];
1128
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()];
1133 #endif
1134
1135 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
1136     if (m_provider) {
1137         m_provider->setPlayerItem(m_avPlayerItem.get());
1138         m_provider->setAudioTrack(firstEnabledTrack(safeAVAssetTracksForAudibleMedia()));
1139     }
1140 #endif
1141
1142     setDelayCallbacks(false);
1143 }
1144
1145 void MediaPlayerPrivateAVFoundationObjC::checkPlayability()
1146 {
1147     if (m_haveCheckedPlayability)
1148         return;
1149     m_haveCheckedPlayability = true;
1150
1151     INFO_LOG(LOGIDENTIFIER);
1152     auto weakThis = createWeakPtr();
1153
1154     [m_avAsset.get() loadValuesAsynchronouslyForKeys:[NSArray arrayWithObjects:@"playable", @"tracks", nil] completionHandler:^{
1155         callOnMainThread([weakThis] {
1156             if (weakThis)
1157                 weakThis->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
1158         });
1159     }];
1160 }
1161
1162 void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata()
1163 {
1164     INFO_LOG(LOGIDENTIFIER);
1165
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:^{
1170
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());
1177                     }];
1178                 }
1179             }
1180             dispatch_group_leave(metadataLoadingGroup.get());
1181         });
1182     }];
1183
1184     dispatch_group_notify(metadataLoadingGroup.get(), dispatch_get_main_queue(), ^{
1185         callOnMainThread([weakThis] {
1186             if (weakThis)
1187                 [weakThis->m_objcObserver.get() metadataLoaded];
1188         });
1189     });
1190 }
1191
1192 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::playerItemStatus() const
1193 {
1194     if (!m_avPlayerItem)
1195         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
1196
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;
1207
1208     return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
1209 }
1210
1211 PlatformMedia MediaPlayerPrivateAVFoundationObjC::platformMedia() const
1212 {
1213     INFO_LOG(LOGIDENTIFIER);
1214     PlatformMedia pm;
1215     pm.type = PlatformMedia::AVFoundationMediaPlayerType;
1216     pm.media.avfMediaPlayer = m_avPlayer.get();
1217     return pm;
1218 }
1219
1220 PlatformLayer* MediaPlayerPrivateAVFoundationObjC::platformLayer() const
1221 {
1222 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1223     return m_haveBeenAskedToCreateLayer ? m_videoFullscreenLayerManager->videoInlineLayer() : nullptr;
1224 #else
1225     return m_haveBeenAskedToCreateLayer ? m_videoLayer.get() : nullptr;
1226 #endif
1227 }
1228
1229 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1230 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenLayer(PlatformLayer* videoFullscreenLayer, WTF::Function<void()>&& completionHandler)
1231 {
1232     if (m_videoFullscreenLayerManager->videoFullscreenLayer() == videoFullscreenLayer) {
1233         completionHandler();
1234         return;
1235     }
1236
1237     [CATransaction begin];
1238     [CATransaction setDisableActions:YES];
1239
1240     m_videoFullscreenLayerManager->setVideoFullscreenLayer(videoFullscreenLayer, WTFMove(completionHandler));
1241
1242     if (m_videoFullscreenLayerManager->videoFullscreenLayer() && m_textTrackRepresentationLayer) {
1243         syncTextTrackBounds();
1244         [m_videoFullscreenLayerManager->videoFullscreenLayer() addSublayer:m_textTrackRepresentationLayer.get()];
1245     }
1246
1247     [CATransaction commit];
1248
1249     updateDisableExternalPlayback();
1250 }
1251
1252 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenFrame(FloatRect frame)
1253 {
1254     m_videoFullscreenLayerManager->setVideoFullscreenFrame(frame);
1255     syncTextTrackBounds();
1256 }
1257
1258 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
1259 {
1260     m_videoFullscreenGravity = gravity;
1261
1262     if (!m_videoLayer)
1263         return;
1264
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;
1272     else
1273         ASSERT_NOT_REACHED();
1274     
1275     if ([m_videoLayer videoGravity] == videoGravity)
1276         return;
1277
1278     [m_videoLayer setVideoGravity:videoGravity];
1279     syncTextTrackBounds();
1280 }
1281
1282 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenMode(MediaPlayer::VideoFullscreenMode mode)
1283 {
1284 #if PLATFORM(IOS)
1285     [m_videoLayer setPIPModeEnabled:(mode & MediaPlayer::VideoFullscreenModePictureInPicture)];
1286     updateDisableExternalPlayback();
1287 #else
1288     UNUSED_PARAM(mode);
1289 #endif
1290 }
1291
1292 #endif // PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
1293
1294 #if PLATFORM(IOS)
1295 NSArray *MediaPlayerPrivateAVFoundationObjC::timedMetadata() const
1296 {
1297     if (m_currentMetaData)
1298         return m_currentMetaData.get();
1299     return nil;
1300 }
1301
1302 String MediaPlayerPrivateAVFoundationObjC::accessLog() const
1303 {
1304     if (!m_avPlayerItem)
1305         return emptyString();
1306     
1307     AVPlayerItemAccessLog *log = [m_avPlayerItem.get() accessLog];
1308     RetainPtr<NSString> logString = adoptNS([[NSString alloc] initWithData:[log extendedLogData] encoding:[log extendedLogDataStringEncoding]]);
1309
1310     return logString.get();
1311 }
1312
1313 String MediaPlayerPrivateAVFoundationObjC::errorLog() const
1314 {
1315     if (!m_avPlayerItem)
1316         return emptyString();
1317
1318     AVPlayerItemErrorLog *log = [m_avPlayerItem.get() errorLog];
1319     RetainPtr<NSString> logString = adoptNS([[NSString alloc] initWithData:[log extendedLogData] encoding:[log extendedLogDataStringEncoding]]);
1320
1321     return logString.get();
1322 }
1323 #endif
1324
1325 void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
1326 {
1327     [CATransaction begin];
1328     [CATransaction setDisableActions:YES];    
1329     if (m_videoLayer)
1330         [m_videoLayer.get() setHidden:!isVisible];
1331     [CATransaction commit];
1332 }
1333     
1334 void MediaPlayerPrivateAVFoundationObjC::platformPlay()
1335 {
1336     INFO_LOG(LOGIDENTIFIER);
1337     if (!metaDataAvailable())
1338         return;
1339
1340     setDelayCallbacks(true);
1341     m_cachedRate = requestedRate();
1342     [m_avPlayer.get() setRate:requestedRate()];
1343     setDelayCallbacks(false);
1344 }
1345
1346 void MediaPlayerPrivateAVFoundationObjC::platformPause()
1347 {
1348     INFO_LOG(LOGIDENTIFIER);
1349     if (!metaDataAvailable())
1350         return;
1351
1352     setDelayCallbacks(true);
1353     m_cachedRate = 0;
1354     [m_avPlayer.get() setRate:0];
1355     setDelayCallbacks(false);
1356 }
1357
1358 MediaTime MediaPlayerPrivateAVFoundationObjC::platformDuration() const
1359 {
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();
1364     
1365     CMTime cmDuration;
1366     
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];
1370     else
1371         cmDuration = [m_avAsset.get() duration];
1372
1373     if (CMTIME_IS_NUMERIC(cmDuration))
1374         return PAL::toMediaTime(cmDuration);
1375
1376     if (CMTIME_IS_INDEFINITE(cmDuration))
1377         return MediaTime::positiveInfiniteTime();
1378
1379     INFO_LOG(LOGIDENTIFIER, "returning invalid time");
1380     return MediaTime::invalidTime();
1381 }
1382
1383 MediaTime MediaPlayerPrivateAVFoundationObjC::currentMediaTime() const
1384 {
1385     if (!metaDataAvailable() || !m_avPlayerItem)
1386         return MediaTime::zeroTime();
1387
1388     CMTime itemTime = [m_avPlayerItem.get() currentTime];
1389     if (CMTIME_IS_NUMERIC(itemTime))
1390         return std::max(PAL::toMediaTime(itemTime), MediaTime::zeroTime());
1391
1392     return MediaTime::zeroTime();
1393 }
1394
1395 void MediaPlayerPrivateAVFoundationObjC::seekToTime(const MediaTime& time, const MediaTime& negativeTolerance, const MediaTime& positiveTolerance)
1396 {
1397     // setCurrentTime generates several event callbacks, update afterwards.
1398     setDelayCallbacks(true);
1399
1400     if (m_metadataTrack)
1401         m_metadataTrack->flushPartialCues();
1402
1403     CMTime cmTime = PAL::toCMTime(time);
1404     CMTime cmBefore = PAL::toCMTime(negativeTolerance);
1405     CMTime cmAfter = PAL::toCMTime(positiveTolerance);
1406
1407     // [AVPlayerItem seekToTime] will throw an exception if toleranceBefore is negative.
1408     if (CMTimeCompare(cmBefore, kCMTimeZero) < 0)
1409         cmBefore = kCMTimeZero;
1410     
1411     auto weakThis = createWeakPtr();
1412
1413     [m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
1414         callOnMainThread([weakThis, finished] {
1415             auto _this = weakThis.get();
1416             if (!_this)
1417                 return;
1418
1419             _this->seekCompleted(finished);
1420         });
1421     }];
1422
1423     setDelayCallbacks(false);
1424 }
1425
1426 void MediaPlayerPrivateAVFoundationObjC::setVolume(float volume)
1427 {
1428 #if PLATFORM(IOS)
1429     UNUSED_PARAM(volume);
1430     return;
1431 #else
1432     if (!m_avPlayer)
1433         return;
1434
1435     [m_avPlayer.get() setVolume:volume];
1436 #endif
1437 }
1438
1439 void MediaPlayerPrivateAVFoundationObjC::setMuted(bool muted)
1440 {
1441     if (m_muted == muted)
1442         return;
1443
1444     INFO_LOG(LOGIDENTIFIER, "- ", muted);
1445
1446     m_muted = muted;
1447
1448     if (!m_avPlayer)
1449         return;
1450
1451     [m_avPlayer.get() setMuted:m_muted];
1452 }
1453
1454 void MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(bool closedCaptionsVisible)
1455 {
1456     UNUSED_PARAM(closedCaptionsVisible);
1457
1458     if (!metaDataAvailable())
1459         return;
1460
1461     INFO_LOG(LOGIDENTIFIER, "- ", closedCaptionsVisible);
1462 }
1463
1464 void MediaPlayerPrivateAVFoundationObjC::setRateDouble(double rate)
1465 {
1466     setDelayCallbacks(true);
1467     m_cachedRate = rate;
1468     [m_avPlayer.get() setRate:rate];
1469     setDelayCallbacks(false);
1470 }
1471
1472 double MediaPlayerPrivateAVFoundationObjC::rate() const
1473 {
1474     if (!metaDataAvailable())
1475         return 0;
1476
1477     return m_cachedRate;
1478 }
1479
1480 double MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesLastModifiedTime() const
1481 {
1482 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
1483     return [m_avPlayerItem seekableTimeRangesLastModifiedTime];
1484 #else
1485     return 0;
1486 #endif
1487 }
1488
1489 double MediaPlayerPrivateAVFoundationObjC::liveUpdateInterval() const
1490 {
1491 #if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
1492     return [m_avPlayerItem liveUpdateInterval];
1493 #else
1494     return 0;
1495 #endif
1496 }
1497
1498 void MediaPlayerPrivateAVFoundationObjC::setPreservesPitch(bool preservesPitch)
1499 {
1500     if (m_avPlayerItem)
1501         [m_avPlayerItem setAudioTimePitchAlgorithm:(preservesPitch ? AVAudioTimePitchAlgorithmSpectral : AVAudioTimePitchAlgorithmVarispeed)];
1502 }
1503
1504 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateAVFoundationObjC::platformBufferedTimeRanges() const
1505 {
1506     auto timeRanges = std::make_unique<PlatformTimeRanges>();
1507
1508     if (!m_avPlayerItem)
1509         return timeRanges;
1510
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)));
1515     }
1516     return timeRanges;
1517 }
1518
1519 MediaTime MediaPlayerPrivateAVFoundationObjC::platformMinTimeSeekable() const
1520 {
1521     if (!m_cachedSeekableRanges || ![m_cachedSeekableRanges count])
1522         return MediaTime::zeroTime();
1523
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))
1529             continue;
1530
1531         hasValidRange = true;
1532         MediaTime startOfRange = PAL::toMediaTime(timeRange.start);
1533         if (minTimeSeekable > startOfRange)
1534             minTimeSeekable = startOfRange;
1535     }
1536     return hasValidRange ? minTimeSeekable : MediaTime::zeroTime();
1537 }
1538
1539 MediaTime MediaPlayerPrivateAVFoundationObjC::platformMaxTimeSeekable() const
1540 {
1541     if (!m_cachedSeekableRanges)
1542         m_cachedSeekableRanges = [m_avPlayerItem seekableTimeRanges];
1543
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))
1548             continue;
1549         
1550         MediaTime endOfRange = PAL::toMediaTime(CMTimeRangeGetEnd(timeRange));
1551         if (maxTimeSeekable < endOfRange)
1552             maxTimeSeekable = endOfRange;
1553     }
1554     return maxTimeSeekable;
1555 }
1556
1557 MediaTime MediaPlayerPrivateAVFoundationObjC::platformMaxTimeLoaded() const
1558 {
1559     if (!m_cachedLoadedRanges)
1560         return MediaTime::zeroTime();
1561
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))
1566             continue;
1567         
1568         MediaTime endOfRange = PAL::toMediaTime(CMTimeRangeGetEnd(timeRange));
1569         if (maxTimeLoaded < endOfRange)
1570             maxTimeLoaded = endOfRange;
1571     }
1572
1573     return maxTimeLoaded;   
1574 }
1575
1576 unsigned long long MediaPlayerPrivateAVFoundationObjC::totalBytes() const
1577 {
1578     if (!metaDataAvailable())
1579         return 0;
1580
1581     if (m_cachedTotalBytes)
1582         return m_cachedTotalBytes;
1583
1584     for (AVPlayerItemTrack *thisTrack in m_cachedTracks.get())
1585         m_cachedTotalBytes += [[thisTrack assetTrack] totalSampleDataLength];
1586
1587     return m_cachedTotalBytes;
1588 }
1589
1590 void MediaPlayerPrivateAVFoundationObjC::setAsset(RetainPtr<id> asset)
1591 {
1592     m_avAsset = asset;
1593 }
1594
1595 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
1596 {
1597     if (!m_avAsset)
1598         return MediaPlayerAVAssetStatusDoesNotExist;
1599
1600     for (NSString *keyName in assetMetadataKeyNames()) {
1601         NSError *error = nil;
1602         AVKeyValueStatus keyStatus = [m_avAsset.get() statusOfValueForKey:keyName error:&error];
1603
1604         if (error)
1605             ERROR_LOG(LOGIDENTIFIER, "failed for ", [keyName UTF8String], ", error = ", [[error localizedDescription] UTF8String]);
1606
1607         if (keyStatus < AVKeyValueStatusLoaded)
1608             return MediaPlayerAVAssetStatusLoading;// At least one key is not loaded yet.
1609         
1610         if (keyStatus == AVKeyValueStatusFailed)
1611             return MediaPlayerAVAssetStatusFailed; // At least one key could not be loaded.
1612
1613         if (keyStatus == AVKeyValueStatusCancelled)
1614             return MediaPlayerAVAssetStatusCancelled; // Loading of at least one key was cancelled.
1615     }
1616
1617     if (!player()->shouldCheckHardwareSupport())
1618         m_tracksArePlayable = true;
1619
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;
1625                 break;
1626             }
1627         }
1628     }
1629
1630     if ([[m_avAsset.get() valueForKey:@"playable"] boolValue] && m_tracksArePlayable.value())
1631         return MediaPlayerAVAssetStatusPlayable;
1632
1633     return MediaPlayerAVAssetStatusLoaded;
1634 }
1635
1636 long MediaPlayerPrivateAVFoundationObjC::assetErrorCode() const
1637 {
1638     if (!m_avAsset)
1639         return 0;
1640
1641     NSError *error = nil;
1642     [m_avAsset statusOfValueForKey:@"playable" error:&error];
1643     return [error code];
1644 }
1645
1646 void MediaPlayerPrivateAVFoundationObjC::paintCurrentFrameInContext(GraphicsContext& context, const FloatRect& rect)
1647 {
1648     if (!metaDataAvailable() || context.paintingDisabled())
1649         return;
1650
1651     setDelayCallbacks(true);
1652     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1653
1654 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1655     if (videoOutputHasAvailableFrame())
1656         paintWithVideoOutput(context, rect);
1657     else
1658 #endif
1659         paintWithImageGenerator(context, rect);
1660
1661     END_BLOCK_OBJC_EXCEPTIONS;
1662     setDelayCallbacks(false);
1663
1664     m_videoFrameHasDrawn = true;
1665 }
1666
1667 void MediaPlayerPrivateAVFoundationObjC::paint(GraphicsContext& context, const FloatRect& rect)
1668 {
1669     if (!metaDataAvailable() || context.paintingDisabled())
1670         return;
1671
1672     // We can ignore the request if we are already rendering to a layer.
1673     if (currentRenderingMode() == MediaRenderingToLayer)
1674         return;
1675
1676     // paint() is best effort, so only paint if we already have an image generator or video output available.
1677     if (!hasContextRenderer())
1678         return;
1679
1680     paintCurrentFrameInContext(context, rect);
1681 }
1682
1683 void MediaPlayerPrivateAVFoundationObjC::paintWithImageGenerator(GraphicsContext& context, const FloatRect& rect)
1684 {
1685     INFO_LOG(LOGIDENTIFIER);
1686
1687     RetainPtr<CGImageRef> image = createImageForTimeInRect(currentTime(), rect);
1688     if (image) {
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());
1695     }
1696 }
1697
1698 RetainPtr<CGImageRef> MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(float time, const FloatRect& rect)
1699 {
1700     if (!m_imageGenerator)
1701         createImageGenerator();
1702     ASSERT(m_imageGenerator);
1703
1704 #if !RELEASE_LOG_DISABLED
1705     double start = monotonicallyIncreasingTime();
1706 #endif
1707
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()));
1711
1712 #if !RELEASE_LOG_DISABLED
1713     DEBUG_LOG(LOGIDENTIFIER, "creating image took ", monotonicallyIncreasingTime() - start);
1714 #endif
1715
1716     return image;
1717 }
1718
1719 void MediaPlayerPrivateAVFoundationObjC::getSupportedTypes(HashSet<String, ASCIICaseInsensitiveHash>& supportedTypes)
1720 {
1721     supportedTypes = AVFoundationMIMETypeCache::singleton().types();
1722
1723
1724 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1725 static bool keySystemIsSupported(const String& keySystem)
1726 {
1727     if (equalIgnoringASCIICase(keySystem, "com.apple.fps") || equalIgnoringASCIICase(keySystem, "com.apple.fps.1_0") || equalIgnoringASCIICase(keySystem, "org.w3c.clearkey"))
1728         return true;
1729     return false;
1730 }
1731 #endif
1732
1733 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationObjC::supportsType(const MediaEngineSupportParameters& parameters)
1734 {
1735 #if ENABLE(MEDIA_SOURCE)
1736     if (parameters.isMediaSource)
1737         return MediaPlayer::IsNotSupported;
1738 #endif
1739 #if ENABLE(MEDIA_STREAM)
1740     if (parameters.isMediaStream)
1741         return MediaPlayer::IsNotSupported;
1742 #endif
1743
1744     auto containerType = parameters.type.containerType();
1745     if (isUnsupportedMIMEType(containerType))
1746         return MediaPlayer::IsNotSupported;
1747
1748     if (!staticMIMETypeList().contains(containerType) && !AVFoundationMIMETypeCache::singleton().types().contains(containerType))
1749         return MediaPlayer::IsNotSupported;
1750
1751     // The spec says:
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;
1755
1756     if (!contentTypeMeetsHardwareDecodeRequirements(parameters.type, parameters.contentTypesRequiringHardwareSupport))
1757         return MediaPlayer::IsNotSupported;
1758
1759     NSString *typeString = [NSString stringWithFormat:@"%@; codecs=\"%@\"", (NSString *)containerType, (NSString *)parameters.type.parameter(ContentType::codecsParameter())];
1760     return [AVURLAsset isPlayableExtendedMIMEType:typeString] ? MediaPlayer::IsSupported : MediaPlayer::MayBeSupported;
1761 }
1762
1763 bool MediaPlayerPrivateAVFoundationObjC::supportsKeySystem(const String& keySystem, const String& mimeType)
1764 {
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;
1770
1771         if (!keySystemIsSupported(keySystem))
1772             return false;
1773
1774         if (!mimeType.isEmpty() && isUnsupportedMIMEType(mimeType))
1775             return false;
1776
1777         if (!mimeType.isEmpty() && !staticMIMETypeList().contains(mimeType) && !AVFoundationMIMETypeCache::singleton().types().contains(mimeType))
1778             return false;
1779
1780         return true;
1781     }
1782 #else
1783     UNUSED_PARAM(keySystem);
1784     UNUSED_PARAM(mimeType);
1785 #endif
1786     return false;
1787 }
1788
1789 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1790 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
1791 static void fulfillRequestWithKeyData(AVAssetResourceLoadingRequest *request, ArrayBuffer* keyData)
1792 {
1793     if (AVAssetResourceLoadingContentInformationRequest *infoRequest = [request contentInformationRequest]) {
1794         [infoRequest setContentLength:keyData->byteLength()];
1795         [infoRequest setByteRangeAccessSupported:YES];
1796     }
1797
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]);
1801
1802         if (start < 0 || end < 0 || start >= static_cast<long long>(keyData->byteLength())) {
1803             [request finishLoadingWithError:nil];
1804             return;
1805         }
1806
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()];
1812     }
1813
1814     [request finishLoading];
1815 }
1816 #endif
1817
1818 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest* avRequest)
1819 {
1820     String scheme = [[[avRequest request] URL] scheme];
1821     String keyURI = [[[avRequest request] URL] absoluteString];
1822
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);
1832
1833         RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer.copyRef(), 4, keyURI.length());
1834         keyURIArray->setRange(StringView(keyURI).upconvertedCharacters(), keyURI.length() / sizeof(unsigned char), 0);
1835
1836         RefPtr<Uint8Array> initData = Uint8Array::create(WTFMove(initDataBuffer), 0, byteLength);
1837         if (!player()->keyNeeded(initData.get()))
1838             return false;
1839
1840         m_keyURIToRequestMap.set(keyURI, avRequest);
1841         return true;
1842     }
1843
1844     if (scheme == "clearkey") {
1845         String keyID = [[[avRequest request] URL] resourceSpecifier];
1846         StringView keyIDView(keyID);
1847         CString utf8EncodedKeyId = UTF8Encoding().encode(keyIDView, URLEncodedEntitiesForUnencodables);
1848
1849         RefPtr<Uint8Array> initData = Uint8Array::create(utf8EncodedKeyId.length());
1850         initData->setRange(reinterpret_cast<const JSC::Uint8Adaptor::Type*>(utf8EncodedKeyId.data()), utf8EncodedKeyId.length(), 0);
1851
1852         auto keyData = player()->cachedKeyForKeyId(keyID);
1853         if (keyData) {
1854             fulfillRequestWithKeyData(avRequest, keyData.get());
1855             return false;
1856         }
1857
1858         if (!player()->keyNeeded(initData.get()))
1859             return false;
1860
1861         m_keyURIToRequestMap.set(keyID, avRequest);
1862         return true;
1863     }
1864 #endif
1865
1866     RefPtr<WebCoreAVFResourceLoader> resourceLoader = WebCoreAVFResourceLoader::create(this, avRequest);
1867     m_resourceLoaderMap.add(avRequest, resourceLoader);
1868     resourceLoader->startLoading();
1869     return true;
1870 }
1871
1872 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForResponseToAuthenticationChallenge(NSURLAuthenticationChallenge* nsChallenge)
1873 {
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());
1878 #else
1879     AuthenticationChallenge challenge(nsChallenge);
1880 #endif
1881
1882     return player()->shouldWaitForResponseToAuthenticationChallenge(challenge);
1883 }
1884
1885 void MediaPlayerPrivateAVFoundationObjC::didCancelLoadingRequest(AVAssetResourceLoadingRequest* avRequest)
1886 {
1887     String scheme = [[[avRequest request] URL] scheme];
1888
1889     WebCoreAVFResourceLoader* resourceLoader = m_resourceLoaderMap.get(avRequest);
1890
1891     if (resourceLoader)
1892         resourceLoader->stopLoading();
1893 }
1894
1895 void MediaPlayerPrivateAVFoundationObjC::didStopLoadingRequest(AVAssetResourceLoadingRequest *avRequest)
1896 {
1897     m_resourceLoaderMap.remove(avRequest);
1898 }
1899 #endif
1900
1901 bool MediaPlayerPrivateAVFoundationObjC::isAvailable()
1902 {
1903     return AVFoundationLibrary() && isCoreMediaFrameworkAvailable();
1904 }
1905
1906 MediaTime MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(const MediaTime& timeValue) const
1907 {
1908     if (!metaDataAvailable())
1909         return timeValue;
1910
1911     // FIXME - impossible to implement until rdar://8721510 is fixed.
1912     return timeValue;
1913 }
1914
1915 double MediaPlayerPrivateAVFoundationObjC::maximumDurationToCacheMediaTime() const
1916 {
1917 #if PLATFORM(IOS) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1010
1918     return 0;
1919 #else
1920     return 5;
1921 #endif
1922 }
1923
1924 void MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity()
1925 {
1926     if (!m_videoLayer)
1927         return;
1928
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())
1933         return;
1934 #endif
1935
1936     [CATransaction begin];
1937     [CATransaction setDisableActions:YES];    
1938     NSString* gravity = shouldMaintainAspectRatio() ? AVLayerVideoGravityResizeAspect : AVLayerVideoGravityResize;
1939     [m_videoLayer.get() setVideoGravity:gravity];
1940     [CATransaction commit];
1941 }
1942
1943 static AVAssetTrack* firstEnabledTrack(NSArray* tracks)
1944 {
1945     NSUInteger index = [tracks indexOfObjectPassingTest:^(id obj, NSUInteger, BOOL *) {
1946         return [static_cast<AVAssetTrack*>(obj) isEnabled];
1947     }];
1948     if (index == NSNotFound)
1949         return nil;
1950     return [tracks objectAtIndex:index];
1951 }
1952
1953 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
1954 {
1955     String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
1956     m_languageOfPrimaryAudioTrack = String();
1957
1958     if (!m_avAsset)
1959         return;
1960
1961     setDelayCharacteristicsChangedNotification(true);
1962
1963     bool haveCCTrack = false;
1964     bool hasCaptions = false;
1965
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];
1976 #endif
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);
1985     } else {
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])
1994                     hasVideo = true;
1995                 else if ([mediaType isEqualToString:AVMediaTypeAudio])
1996                     hasAudio = true;
1997                 else if ([mediaType isEqualToString:AVMediaTypeClosedCaption]) {
1998 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1999                     hasCaptions = true;
2000 #endif
2001                     haveCCTrack = true;
2002                 } else if ([mediaType isEqualToString:AVMediaTypeMetadata]) {
2003                     hasMetaData = true;
2004                 }
2005             }
2006         }
2007
2008 #if ENABLE(VIDEO_TRACK)
2009         updateAudioTracks();
2010         updateVideoTracks();
2011
2012 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2013         hasAudio |= (m_audibleGroup && m_audibleGroup->selectedOption());
2014         hasVideo |= (m_visualGroup && m_visualGroup->selectedOption());
2015 #endif
2016 #endif
2017
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
2020         // when it is not.
2021         setHasVideo(hasVideo || m_cachedIsReadyForDisplay);
2022
2023         setHasAudio(hasAudio);
2024 #if ENABLE(DATACUE_VALUE)
2025         if (hasMetaData)
2026             processMetadataTrack();
2027 #endif
2028     }
2029
2030 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2031     AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
2032     if (legibleGroup && m_cachedTracks) {
2033         hasCaptions = [[AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]] count];
2034         if (hasCaptions)
2035             processMediaSelectionOptions();
2036     }
2037 #endif
2038
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)
2043     if (haveCCTrack)
2044         processLegacyClosedCaptionsTracks();
2045 #endif
2046
2047     setHasClosedCaptions(hasCaptions);
2048
2049     INFO_LOG(LOGIDENTIFIER, "has video = ", hasVideo(), ", has audio = ", hasAudio(), ", has captions = ", hasClosedCaptions());
2050
2051     sizeChanged();
2052
2053     if (primaryAudioTrackLanguage != languageOfPrimaryAudioTrack())
2054         characteristicsChanged();
2055
2056 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
2057     if (m_provider)
2058         m_provider->setAudioTrack(firstEnabledTrack(safeAVAssetTracksForAudibleMedia()));
2059 #endif
2060
2061     setDelayCharacteristicsChangedNotification(false);
2062 }
2063
2064 #if ENABLE(VIDEO_TRACK)
2065
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))
2068 {
2069     RetainPtr<NSSet> newTracks = adoptNS([[NSSet alloc] initWithArray:[tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id track, NSUInteger, BOOL*){
2070         return [[[track assetTrack] mediaType] isEqualToString:trackType];
2071     }]]]);
2072     RetainPtr<NSMutableSet> oldTracks = adoptNS([[NSMutableSet alloc] initWithCapacity:oldItems.size()]);
2073
2074     for (auto& oldItem : oldItems) {
2075         if (oldItem->playerItemTrack())
2076             [oldTracks addObject:oldItem->playerItemTrack()];
2077     }
2078
2079     // Find the added & removed AVPlayerItemTracks:
2080     RetainPtr<NSMutableSet> removedTracks = adoptNS([oldTracks mutableCopy]);
2081     [removedTracks minusSet:newTracks.get()];
2082
2083     RetainPtr<NSMutableSet> addedTracks = adoptNS([newTracks mutableCopy]);
2084     [addedTracks minusSet:oldTracks.get()];
2085
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);
2093         else
2094             replacementItems.append(oldItem);
2095     }
2096
2097     for (AVPlayerItemTrack* track in addedTracks.get())
2098         addedItems.append(itemFactory(track));
2099
2100     replacementItems.appendVector(addedItems);
2101     oldItems.swap(replacementItems);
2102
2103     for (auto& removedItem : removedItems)
2104         (player->*removedFunction)(*removedItem);
2105
2106     for (auto& addedItem : addedItems)
2107         (player->*addedFunction)(*addedItem);
2108 }
2109
2110 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2111
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))
2114 {
2115     group->updateOptions(characteristics);
2116
2117     ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> newSelectionOptions;
2118     for (auto& option : group->options()) {
2119         if (!option)
2120             continue;
2121         AVMediaSelectionOptionType* avOption = option->avMediaSelectionOption();
2122         if (!avOption)
2123             continue;
2124         newSelectionOptions.add(option);
2125     }
2126
2127     ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> oldSelectionOptions;
2128     for (auto& oldItem : oldItems) {
2129         if (MediaSelectionOptionAVFObjC *option = oldItem->mediaSelectionOption())
2130             oldSelectionOptions.add(option);
2131     }
2132
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);
2138     }
2139
2140     ListHashSet<RefPtr<MediaSelectionOptionAVFObjC>> addedSelectionOptions;
2141     for (auto& newOption : newSelectionOptions) {
2142         if (!oldSelectionOptions.contains(newOption))
2143             addedSelectionOptions.add(newOption);
2144     }
2145
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);
2155         else
2156             replacementItems.append(oldItem);
2157     }
2158
2159     for (auto& option : addedSelectionOptions)
2160         addedItems.append(itemFactory(*option.get()));
2161
2162     replacementItems.appendVector(addedItems);
2163     oldItems.swap(replacementItems);
2164     
2165     for (auto& removedItem : removedItems)
2166         (player->*removedFunction)(*removedItem);
2167
2168     for (auto& addedItem : addedItems)
2169         (player->*addedFunction)(*addedItem);
2170 }
2171
2172 #endif
2173
2174 void MediaPlayerPrivateAVFoundationObjC::updateAudioTracks()
2175 {
2176 #if !RELEASE_LOG_DISABLED
2177     size_t count = m_audioTracks.size();
2178 #endif
2179
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);
2185     }
2186
2187     if (m_audibleGroup)
2188         determineChangedTracksFromNewTracksAndOldItems(m_audibleGroup.get(), m_audioTracks, characteristics, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
2189     else
2190 #endif
2191         determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeAudio, m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
2192
2193     for (auto& track : m_audioTracks)
2194         track->resetPropertiesFromTrack();
2195
2196 #if !RELEASE_LOG_DISABLED
2197     INFO_LOG(LOGIDENTIFIER, "track count was ", count, ", is ", m_audioTracks.size());
2198 #endif
2199 }
2200
2201 void MediaPlayerPrivateAVFoundationObjC::updateVideoTracks()
2202 {
2203 #if !RELEASE_LOG_DISABLED
2204     size_t count = m_videoTracks.size();
2205 #endif
2206
2207     determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeVideo, m_videoTracks, &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
2208
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>());
2213     }
2214
2215     if (m_visualGroup)
2216         determineChangedTracksFromNewTracksAndOldItems(m_visualGroup.get(), m_videoTracks, Vector<String>(), &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
2217 #endif
2218
2219     for (auto& track : m_audioTracks)
2220         track->resetPropertiesFromTrack();
2221
2222 #if !RELEASE_LOG_DISABLED
2223     INFO_LOG(LOGIDENTIFIER, "track count was ", count, ", is ", m_videoTracks.size());
2224 #endif
2225 }
2226
2227 bool MediaPlayerPrivateAVFoundationObjC::requiresTextTrackRepresentation() const
2228 {
2229 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
2230     if (m_videoFullscreenLayerManager->videoFullscreenLayer())
2231         return true;
2232 #endif
2233     return false;
2234 }
2235
2236 void MediaPlayerPrivateAVFoundationObjC::syncTextTrackBounds()
2237 {
2238 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
2239     if (!m_videoFullscreenLayerManager->videoFullscreenLayer() || !m_textTrackRepresentationLayer)
2240         return;
2241
2242     [CATransaction begin];
2243     [CATransaction setDisableActions:YES];
2244
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];
2248
2249     [CATransaction commit];
2250 #endif
2251 }
2252
2253 void MediaPlayerPrivateAVFoundationObjC::setTextTrackRepresentation(TextTrackRepresentation* representation)
2254 {
2255 #if PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
2256     PlatformLayer* representationLayer = representation ? representation->platformLayer() : nil;
2257     if (representationLayer == m_textTrackRepresentationLayer) {
2258         syncTextTrackBounds();
2259         return;
2260     }
2261
2262     [CATransaction begin];
2263     [CATransaction setDisableActions:YES];
2264
2265     if (m_textTrackRepresentationLayer)
2266         [m_textTrackRepresentationLayer removeFromSuperlayer];
2267
2268     m_textTrackRepresentationLayer = representationLayer;
2269
2270     if (m_videoFullscreenLayerManager->videoFullscreenLayer() && m_textTrackRepresentationLayer) {
2271         syncTextTrackBounds();
2272         [m_videoFullscreenLayerManager->videoFullscreenLayer() addSublayer:m_textTrackRepresentationLayer.get()];
2273     }
2274
2275     [CATransaction commit];
2276
2277 #else
2278     UNUSED_PARAM(representation);
2279 #endif
2280 }
2281
2282 #endif // ENABLE(VIDEO_TRACK)
2283
2284 #if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
2285
2286 AudioSourceProvider* MediaPlayerPrivateAVFoundationObjC::audioSourceProvider()
2287 {
2288     if (!m_provider) {
2289         m_provider = AudioSourceProviderAVFObjC::create(m_avPlayerItem.get());
2290         m_provider->setAudioTrack(firstEnabledTrack(safeAVAssetTracksForAudibleMedia()));
2291     }
2292     return m_provider.get();
2293 }
2294
2295 #endif
2296
2297 void MediaPlayerPrivateAVFoundationObjC::sizeChanged()
2298 {
2299     if (!m_avAsset)
2300         return;
2301
2302     setNaturalSize(m_cachedPresentationSize);
2303 }
2304
2305 void MediaPlayerPrivateAVFoundationObjC::resolvedURLChanged()
2306 {
2307     setResolvedURL(m_avAsset ? URL([m_avAsset resolvedURL]) : URL());
2308 }
2309
2310 bool MediaPlayerPrivateAVFoundationObjC::didPassCORSAccessCheck() const
2311 {
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)])
2316         return false;
2317
2318     WebCoreNSURLSession *session = (WebCoreNSURLSession *)resourceLoader.URLSession;
2319     if ([session isKindOfClass:[WebCoreNSURLSession class]])
2320         return session.didPassCORSAccessChecks;
2321 #endif
2322     return false;
2323 }
2324
2325 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
2326
2327 void MediaPlayerPrivateAVFoundationObjC::createVideoOutput()
2328 {
2329     INFO_LOG(LOGIDENTIFIER);
2330
2331     if (!m_avPlayerItem || m_videoOutput)
2332         return;
2333
2334 #if USE(VIDEOTOOLBOX)
2335     NSDictionary* attributes = nil;
2336 #else
2337     NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil];
2338 #endif
2339     m_videoOutput = adoptNS([[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:attributes]);
2340     ASSERT(m_videoOutput);
2341
2342     [m_videoOutput setDelegate:m_videoOutputDelegate.get() queue:globalPullDelegateQueue()];
2343
2344     [m_avPlayerItem.get() addOutput:m_videoOutput.get()];
2345 }
2346
2347 void MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput()
2348 {
2349     if (!m_videoOutput)
2350         return;
2351
2352     if (m_avPlayerItem)
2353         [m_avPlayerItem.get() removeOutput:m_videoOutput.get()];
2354
2355     INFO_LOG(LOGIDENTIFIER);
2356
2357     m_videoOutput = 0;
2358 }
2359
2360 RetainPtr<CVPixelBufferRef> MediaPlayerPrivateAVFoundationObjC::createPixelBuffer()
2361 {
2362     if (!m_videoOutput)
2363         createVideoOutput();
2364     ASSERT(m_videoOutput);
2365
2366     CMTime currentTime = [m_avPlayerItem.get() currentTime];
2367
2368     if (![m_videoOutput.get() hasNewPixelBufferForItemTime:currentTime])
2369         return 0;
2370
2371     return adoptCF([m_videoOutput.get() copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
2372 }
2373
2374 bool MediaPlayerPrivateAVFoundationObjC::videoOutputHasAvailableFrame()
2375 {
2376     if (!m_avPlayerItem)
2377         return false;
2378
2379     if (m_lastImage)
2380         return true;
2381
2382     if (!m_videoOutput)
2383         createVideoOutput();
2384
2385     return [m_videoOutput hasNewPixelBufferForItemTime:[m_avPlayerItem currentTime]];
2386 }
2387
2388 void MediaPlayerPrivateAVFoundationObjC::updateLastImage()
2389 {
2390     RetainPtr<CVPixelBufferRef> pixelBuffer = createPixelBuffer();
2391
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.
2395     if (!pixelBuffer)
2396         return;
2397
2398     if (!m_pixelBufferConformer) {
2399 #if USE(VIDEOTOOLBOX)
2400         NSDictionary *attributes = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA) };
2401 #else
2402         NSDictionary *attributes = nil;
2403 #endif
2404         m_pixelBufferConformer = std::make_unique<PixelBufferConformerCV>((CFDictionaryRef)attributes);
2405     }
2406
2407 #if !RELEASE_LOG_DISABLED
2408     double start = monotonicallyIncreasingTime();
2409 #endif
2410
2411     m_lastImage = m_pixelBufferConformer->createImageFromPixelBuffer(pixelBuffer.get());
2412
2413 #if !RELEASE_LOG_DISABLED
2414     DEBUG_LOG(LOGIDENTIFIER, "creating buffer took ", monotonicallyIncreasingTime() - start);
2415 #endif
2416 }
2417
2418 void MediaPlayerPrivateAVFoundationObjC::paintWithVideoOutput(GraphicsContext& context, const FloatRect& outputRect)
2419 {
2420     if (m_videoOutput && !m_lastImage && !videoOutputHasAvailableFrame())
2421         waitForVideoOutputMediaDataWillChange();
2422
2423     updateLastImage();
2424
2425     if (!m_lastImage)
2426         return;
2427
2428     AVAssetTrack* firstEnabledVideoTrack = firstEnabledTrack([m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual]);
2429     if (!firstEnabledVideoTrack)
2430         return;
2431
2432     INFO_LOG(LOGIDENTIFIER);
2433
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);
2438
2439     context.concatCTM(videoTransform);
2440     context.drawNativeImage(m_lastImage.get(), imageRect.size(), transformedOutputRect, imageRect);
2441
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();
2446
2447 }
2448
2449 void MediaPlayerPrivateAVFoundationObjC::createOpenGLVideoOutput()
2450 {
2451     INFO_LOG(LOGIDENTIFIER);
2452
2453     if (!m_avPlayerItem || m_openGLVideoOutput)
2454         return;
2455
2456 #if PLATFORM(IOS)
2457     NSDictionary* attributes = @{(NSString *)kCVPixelBufferIOSurfaceOpenGLESFBOCompatibilityKey: @YES};
2458 #else
2459     NSDictionary* attributes = @{(NSString *)kCVPixelBufferIOSurfaceOpenGLFBOCompatibilityKey: @YES};
2460 #endif
2461     m_openGLVideoOutput = adoptNS([[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:attributes]);
2462     ASSERT(m_openGLVideoOutput);
2463
2464     [m_avPlayerItem.get() addOutput:m_openGLVideoOutput.get()];
2465 }
2466
2467 void MediaPlayerPrivateAVFoundationObjC::destroyOpenGLVideoOutput()
2468 {
2469     if (!m_openGLVideoOutput)
2470         return;
2471
2472     INFO_LOG(LOGIDENTIFIER);
2473
2474     if (m_avPlayerItem)
2475         [m_avPlayerItem.get() removeOutput:m_openGLVideoOutput.get()];
2476
2477     m_openGLVideoOutput = 0;
2478 }
2479
2480 void MediaPlayerPrivateAVFoundationObjC::updateLastOpenGLImage()
2481 {
2482     if (!m_openGLVideoOutput)
2483         return;
2484
2485     CMTime currentTime = [m_openGLVideoOutput itemTimeForHostTime:CACurrentMediaTime()];
2486     if (![m_openGLVideoOutput hasNewPixelBufferForItemTime:currentTime])
2487         return;
2488
2489     m_lastOpenGLImage = adoptCF([m_openGLVideoOutput copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
2490 }
2491
2492 bool MediaPlayerPrivateAVFoundationObjC::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject outputTexture, GC3Denum outputTarget, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY)
2493 {
2494     ASSERT(context);
2495
2496     if (!m_openGLVideoOutput)
2497         createOpenGLVideoOutput();
2498
2499     updateLastOpenGLImage();
2500
2501     if (!m_lastOpenGLImage)
2502         return false;
2503
2504     size_t width = CVPixelBufferGetWidth(m_lastOpenGLImage.get());
2505     size_t height = CVPixelBufferGetHeight(m_lastOpenGLImage.get());
2506
2507     if (!m_textureCache) {
2508         m_textureCache = TextureCacheCV::create(*context);
2509         if (!m_textureCache)
2510             return false;
2511     }
2512
2513     RetainPtr<CVOpenGLTextureRef> videoTexture = m_textureCache->textureFromImage(m_lastOpenGLImage.get(), outputTarget, level, internalFormat, format, type);
2514
2515     if (!m_videoTextureCopier)
2516         m_videoTextureCopier = std::make_unique<VideoTextureCopierCV>(*context);
2517
2518     return m_videoTextureCopier->copyVideoTextureToPlatformTexture(videoTexture.get(), width, height, outputTexture, outputTarget, level, internalFormat, format, type, premultiplyAlpha, flipY);
2519 }
2520
2521 NativeImagePtr MediaPlayerPrivateAVFoundationObjC::nativeImageForCurrentTime()
2522 {
2523     updateLastImage();
2524     return m_lastImage;
2525 }
2526
2527 void MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange()
2528 {
2529     if (!m_videoOutputSemaphore)
2530         m_videoOutputSemaphore = dispatch_semaphore_create(0);
2531
2532     [m_videoOutput requestNotificationOfMediaDataChangeWithAdvanceInterval:0];
2533
2534     // Wait for 1 second.
2535     long result = dispatch_semaphore_wait(m_videoOutputSemaphore, dispatch_time(0, 1 * NSEC_PER_SEC));
2536
2537     if (result)
2538         ERROR_LOG(LOGIDENTIFIER, "timed out");
2539 }
2540
2541 void MediaPlayerPrivateAVFoundationObjC::outputMediaDataWillChange(AVPlayerItemVideoOutputType *)
2542 {
2543     dispatch_semaphore_signal(m_videoOutputSemaphore);
2544 }
2545
2546 #endif
2547
2548 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
2549
2550 RetainPtr<AVAssetResourceLoadingRequest> MediaPlayerPrivateAVFoundationObjC::takeRequestForKeyURI(const String& keyURI)
2551 {
2552     return m_keyURIToRequestMap.take(keyURI);
2553 }
2554
2555 void MediaPlayerPrivateAVFoundationObjC::keyAdded()
2556 {
2557     Vector<String> fulfilledKeyIds;
2558
2559     for (auto& pair : m_keyURIToRequestMap) {
2560         const String& keyId = pair.key;
2561         const RetainPtr<AVAssetResourceLoadingRequest>& request = pair.value;
2562
2563         auto keyData = player()->cachedKeyForKeyId(keyId);
2564         if (!keyData)
2565             continue;
2566
2567         fulfillRequestWithKeyData(request.get(), keyData.get());
2568         fulfilledKeyIds.append(keyId);
2569     }
2570
2571     for (auto& keyId : fulfilledKeyIds)
2572         m_keyURIToRequestMap.remove(keyId);
2573 }
2574
2575 void MediaPlayerPrivateAVFoundationObjC::removeSession(LegacyCDMSession& session)
2576 {
2577     ASSERT_UNUSED(session, &session == m_session);
2578     m_session = nullptr;
2579 }
2580
2581 std::unique_ptr<LegacyCDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem, LegacyCDMSessionClient* client)
2582 {
2583     if (!keySystemIsSupported(keySystem))
2584         return nullptr;
2585     auto session = std::make_unique<CDMSessionAVFoundationObjC>(this, client);
2586     m_session = session->createWeakPtr();
2587     return WTFMove(session);
2588 }
2589
2590 void MediaPlayerPrivateAVFoundationObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool newValue)
2591 {
2592     if (m_session && newValue)
2593         m_session->playerDidReceiveError([NSError errorWithDomain:@"com.apple.WebKit" code:'HDCP' userInfo:nil]);
2594 }
2595
2596 #endif
2597
2598 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
2599
2600 void MediaPlayerPrivateAVFoundationObjC::processLegacyClosedCaptionsTracks()
2601 {
2602 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2603     [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2604 #endif
2605
2606     Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
2607     for (AVPlayerItemTrack *playerItemTrack in m_cachedTracks.get()) {
2608
2609         AVAssetTrack *assetTrack = [playerItemTrack assetTrack];
2610         if (![[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
2611             continue;
2612
2613         bool newCCTrack = true;
2614         for (unsigned i = removedTextTracks.size(); i > 0; --i) {
2615             if (removedTextTracks[i - 1]->textTrackCategory() != InbandTextTrackPrivateAVF::LegacyClosedCaption)
2616                 continue;
2617
2618             RefPtr<InbandTextTrackPrivateLegacyAVFObjC> track = static_cast<InbandTextTrackPrivateLegacyAVFObjC*>(m_textTracks[i - 1].get());
2619             if (track->avPlayerItemTrack() == playerItemTrack) {
2620                 removedTextTracks.remove(i - 1);
2621                 newCCTrack = false;
2622                 break;
2623             }
2624         }
2625
2626         if (!newCCTrack)
2627             continue;
2628         
2629         m_textTracks.append(InbandTextTrackPrivateLegacyAVFObjC::create(this, playerItemTrack));
2630     }
2631
2632     processNewAndRemovedTextTracks(removedTextTracks);
2633 }
2634
2635 #endif
2636
2637 NSArray* MediaPlayerPrivateAVFoundationObjC::safeAVAssetTracksForAudibleMedia()
2638 {
2639     if (!m_avAsset)
2640         return nil;
2641
2642     if ([m_avAsset.get() statusOfValueForKey:@"tracks" error:NULL] != AVKeyValueStatusLoaded)
2643         return nil;
2644
2645     return [m_avAsset tracksWithMediaCharacteristic:AVMediaCharacteristicAudible];
2646 }
2647
2648 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2649
2650 bool MediaPlayerPrivateAVFoundationObjC::hasLoadedMediaSelectionGroups()
2651 {
2652     if (!m_avAsset)
2653         return false;
2654
2655     if ([m_avAsset.get() statusOfValueForKey:@"availableMediaCharacteristicsWithMediaSelectionOptions" error:NULL] != AVKeyValueStatusLoaded)
2656         return false;
2657
2658     return true;
2659 }
2660
2661 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForLegibleMedia()
2662 {
2663     if (!hasLoadedMediaSelectionGroups())
2664         return nil;
2665
2666     return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
2667 }
2668
2669 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForAudibleMedia()
2670 {
2671     if (!hasLoadedMediaSelectionGroups())
2672         return nil;
2673
2674     return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
2675 }
2676
2677 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForVisualMedia()
2678 {
2679     if (!hasLoadedMediaSelectionGroups())
2680         return nil;
2681
2682     return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicVisual];
2683 }
2684
2685 void MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions()
2686 {
2687     AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
2688     if (!legibleGroup) {
2689         INFO_LOG(LOGIDENTIFIER, "no mediaSelectionGroup");
2690         return;
2691     }
2692
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()];
2697
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)
2704                 continue;
2705             
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();
2711             } else
2712 #endif
2713             {
2714                 RefPtr<InbandTextTrackPrivateAVFObjC> track = static_cast<InbandTextTrackPrivateAVFObjC*>(removedTextTracks[i - 1].get());
2715                 currentOption = track->mediaSelectionOption();
2716             }
2717             
2718             if ([currentOption.get() isEqual:option]) {
2719                 removedTextTracks.remove(i - 1);
2720                 newTrack = false;
2721                 break;
2722             }
2723         }
2724         if (!newTrack)
2725             continue;
2726
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
2731             continue;
2732         }
2733 #endif
2734
2735         m_textTracks.append(InbandTextTrackPrivateAVFObjC::create(this, option, InbandTextTrackPrivate::Generic));
2736     }
2737
2738     processNewAndRemovedTextTracks(removedTextTracks);
2739 }
2740
2741 void MediaPlayerPrivateAVFoundationObjC::processMetadataTrack()
2742 {
2743     if (m_metadataTrack)
2744         return;
2745
2746     m_metadataTrack = InbandMetadataTextTrackPrivateAVF::create(InbandTextTrackPrivate::Metadata, InbandTextTrackPrivate::Data);
2747     m_metadataTrack->setInBandMetadataTrackDispatchType("com.apple.streaming");
2748     player()->addTextTrack(*m_metadataTrack);
2749 }
2750
2751 void MediaPlayerPrivateAVFoundationObjC::processCue(NSArray *attributedStrings, NSArray *nativeSamples, const MediaTime& time)
2752 {
2753     ASSERT(time >= MediaTime::zeroTime());
2754
2755     if (!m_currentTextTrack)
2756         return;
2757
2758     m_currentTextTrack->processCue(reinterpret_cast<CFArrayRef>(attributedStrings), reinterpret_cast<CFArrayRef>(nativeSamples), time);
2759 }
2760
2761 void MediaPlayerPrivateAVFoundationObjC::flushCues()
2762 {
2763     INFO_LOG(LOGIDENTIFIER);
2764
2765     if (!m_currentTextTrack)
2766         return;
2767     
2768     m_currentTextTrack->resetCueValues();
2769 }
2770
2771 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2772
2773 void MediaPlayerPrivateAVFoundationObjC::setCurrentTextTrack(InbandTextTrackPrivateAVF *track)
2774 {
2775     if (m_currentTextTrack == track)
2776         return;
2777
2778     INFO_LOG(LOGIDENTIFIER, "selecting track with language ", track ? track->language() : "");
2779
2780     m_currentTextTrack = track;
2781
2782     if (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()];
2792 #endif
2793         else
2794             [m_avPlayerItem.get() selectMediaOption:static_cast<InbandTextTrackPrivateAVFObjC*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2795 #endif
2796     } else {
2797 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2798         [m_avPlayerItem.get() selectMediaOption:0 inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2799 #endif
2800 #pragma clang diagnostic push
2801 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
2802         [m_avPlayer.get() setClosedCaptionDisplayEnabled:NO];
2803 #pragma clang diagnostic pop
2804     }
2805
2806 }
2807
2808 String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
2809 {
2810     if (!m_languageOfPrimaryAudioTrack.isNull())
2811         return m_languageOfPrimaryAudioTrack;
2812
2813     if (!m_avPlayerItem.get())
2814         return emptyString();
2815
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);
2826
2827         return m_languageOfPrimaryAudioTrack;
2828     }
2829 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2830
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;
2838     }
2839
2840     AVAssetTrack *track = [tracks objectAtIndex:0];
2841     m_languageOfPrimaryAudioTrack = AVTrackPrivateAVFObjCImpl::languageForAVAssetTrack(track);
2842
2843     INFO_LOG(LOGIDENTIFIER, "single audio track has language \"", m_languageOfPrimaryAudioTrack, "\"");
2844
2845     return m_languageOfPrimaryAudioTrack;
2846 }
2847
2848 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
2849 bool MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless() const
2850 {
2851     bool wirelessTarget = false;
2852
2853 #if !PLATFORM(IOS)
2854     if (m_playbackTarget) {
2855         if (m_playbackTarget->targetType() == MediaPlaybackTarget::AVFoundation)
2856             wirelessTarget = m_avPlayer && m_avPlayer.get().externalPlaybackActive;
2857         else
2858             wirelessTarget = m_shouldPlayToPlaybackTarget && m_playbackTarget->hasActiveRoute();
2859     }
2860 #else
2861     wirelessTarget = m_avPlayer && m_avPlayer.get().externalPlaybackActive;
2862 #endif
2863
2864     INFO_LOG(LOGIDENTIFIER, "- ", wirelessTarget);
2865
2866     return wirelessTarget;
2867 }
2868
2869 MediaPlayer::WirelessPlaybackTargetType MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetType() const
2870 {
2871     if (!m_avPlayer)
2872         return MediaPlayer::TargetTypeNone;
2873
2874 #if PLATFORM(IOS)
2875     if (!AVFoundationLibrary())
2876         return MediaPlayer::TargetTypeNone;
2877
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;
2885     }
2886
2887     ASSERT_NOT_REACHED();
2888     return MediaPlayer::TargetTypeNone;
2889
2890 #else
2891     return MediaPlayer::TargetTypeAirPlay;
2892 #endif
2893 }
2894     
2895 #if PLATFORM(IOS)
2896 static NSString *exernalDeviceDisplayNameForPlayer(AVPlayerType *player)
2897 {
2898     NSString *displayName = nil;
2899
2900     if (!AVFoundationLibrary())
2901         return nil;
2902
2903     if (player.externalPlaybackType != AVPlayerExternalPlaybackTypeAirPlay)
2904         return nil;
2905
2906     NSArray *pickableRoutes = CFBridgingRelease(MRMediaRemoteCopyPickableRoutes());
2907     if (!pickableRoutes.count)
2908         return nil;
2909
2910     for (NSDictionary *pickableRoute in pickableRoutes) {
2911         if (![pickableRoute[AVController_RouteDescriptionKey_RouteCurrentlyPicked] boolValue])
2912             continue;
2913
2914         displayName = pickableRoute[AVController_RouteDescriptionKey_RouteName];
2915
2916         NSString *routeName = pickableRoute[AVController_RouteDescriptionKey_AVAudioRouteName];
2917         if (![routeName isEqualToString:@"Speaker"] && ![routeName isEqualToString:@"HDMIOutput"])
2918             break;
2919
2920         // The route is a speaker or HDMI out, override the name to be the localized device model.
2921         NSString *localizedDeviceModel = [[UIDevice currentDevice] localizedModel];
2922
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)
2927                 continue;
2928
2929             if ([otherRoute[AVController_RouteDescriptionKey_RouteName] rangeOfString:displayName].location != NSNotFound) {
2930                 includeLocalizedDeviceModelName = YES;
2931                 break;
2932             }
2933         }
2934
2935         if (includeLocalizedDeviceModelName)
2936             displayName =  [NSString stringWithFormat:@"%@ %@", localizedDeviceModel, displayName];
2937         else
2938             displayName = localizedDeviceModel;
2939
2940         break;
2941     }
2942
2943     return displayName;
2944 }
2945 #endif
2946
2947 String MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName() const
2948 {
2949     if (!m_avPlayer)
2950         return emptyString();
2951
2952     String wirelessTargetName;
2953 #if !PLATFORM(IOS)
2954     if (m_playbackTarget)
2955         wirelessTargetName = m_playbackTarget->deviceName();
2956 #else
2957     wirelessTargetName = exernalDeviceDisplayNameForPlayer(m_avPlayer.get());
2958 #endif
2959
2960     return wirelessTargetName;
2961 }
2962
2963 bool MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled() const
2964 {
2965     if (!m_avPlayer)
2966         return !m_allowsWirelessVideoPlayback;
2967
2968     m_allowsWirelessVideoPlayback = [m_avPlayer.get() allowsExternalPlayback];
2969     INFO_LOG(LOGIDENTIFIER, "- ", !m_allowsWirelessVideoPlayback);
2970
2971     return !m_allowsWirelessVideoPlayback;
2972 }
2973
2974 void MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(bool disabled)
2975 {
2976     INFO_LOG(LOGIDENTIFIER, "- ", disabled);
2977     m_allowsWirelessVideoPlayback = !disabled;
2978     if (!m_avPlayer)
2979         return;
2980
2981     setDelayCallbacks(true);
2982     [m_avPlayer.get() setAllowsExternalPlayback:!disabled];
2983     setDelayCallbacks(false);
2984 }
2985
2986 #if !PLATFORM(IOS)
2987
2988 void MediaPlayerPrivateAVFoundationObjC::setWirelessPlaybackTarget(Ref<MediaPlaybackTarget>&& target)
2989 {
2990     m_playbackTarget = WTFMove(target);
2991
2992     m_outputContext = m_playbackTarget->targetType() == MediaPlaybackTarget::AVFoundation ? toMediaPlaybackTargetMac(m_playbackTarget.get())->outputContext() : nullptr;
2993
2994     INFO_LOG(LOGIDENTIFIER);
2995
2996     if (!m_playbackTarget->hasActiveRoute())
2997         setShouldPlayToPlaybackTarget(false);
2998 }
2999
3000 void MediaPlayerPrivateAVFoundationObjC::setShouldPlayToPlaybackTarget(bool shouldPlay)
3001 {
3002     if (m_shouldPlayToPlaybackTarget == shouldPlay)
3003         return;
3004
3005     m_shouldPlayToPlaybackTarget = shouldPlay;
3006
3007     if (!m_playbackTarget)
3008         return;
3009
3010     INFO_LOG(LOGIDENTIFIER, "- ", shouldPlay);
3011
3012     if (m_playbackTarget->targetType() == MediaPlaybackTarget::AVFoundation) {
3013         AVOutputContext *newContext = shouldPlay ? m_outputContext.get() : nil;
3014
3015         if (!m_avPlayer)
3016             return;
3017
3018         RetainPtr<AVOutputContext> currentContext = m_avPlayer.get().outputContext;
3019         if ((!newContext && !currentContext.get()) || [currentContext.get() isEqual:newContext])
3020             return;
3021
3022         setDelayCallbacks(true);
3023         m_avPlayer.get().outputContext = newContext;
3024         setDelayCallbacks(false);
3025
3026         return;
3027     }
3028
3029     ASSERT(m_playbackTarget->targetType() == MediaPlaybackTarget::Mock);
3030
3031     setDelayCallbacks(true);
3032     auto weakThis = createWeakPtr();
3033     scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification([weakThis] {
3034         if (!weakThis)
3035             return;
3036         weakThis->playbackTargetIsWirelessDidChange();
3037     }));
3038     setDelayCallbacks(false);
3039 }
3040
3041 #endif // !PLATFORM(IOS)
3042
3043 void MediaPlayerPrivateAVFoundationObjC::updateDisableExternalPlayback()
3044 {
3045 #if PLATFORM(IOS)
3046     if (!m_avPlayer)
3047         return;
3048
3049     [m_avPlayer setUsesExternalPlaybackWhileExternalScreenIsActive:player()->fullscreenMode() & MediaPlayer::VideoFullscreenModeStandard];
3050 #endif
3051 }
3052
3053 #endif
3054
3055 void MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange(int status)
3056 {
3057     m_cachedItemStatus = status;
3058
3059     updateStates();
3060 }
3061
3062 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange()
3063 {
3064     m_pendingStatusChanges++;
3065 }
3066
3067 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange(bool likelyToKeepUp)
3068 {
3069     m_cachedLikelyToKeepUp = likelyToKeepUp;
3070
3071     ASSERT(m_pendingStatusChanges);
3072     if (!--m_pendingStatusChanges)
3073         updateStates();
3074 }
3075
3076 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange()
3077 {
3078     m_pendingStatusChanges++;
3079 }
3080
3081 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange(bool bufferEmpty)
3082 {
3083     m_cachedBufferEmpty = bufferEmpty;
3084
3085     ASSERT(m_pendingStatusChanges);
3086     if (!--m_pendingStatusChanges)
3087         updateStates();
3088 }
3089
3090 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange()
3091 {
3092     m_pendingStatusChanges++;
3093 }
3094
3095 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange(bool bufferFull)
3096 {
3097     m_cachedBufferFull = bufferFull;
3098
3099     ASSERT(m_pendingStatusChanges);
3100     if (!--m_pendingStatusChanges)
3101         updateStates();
3102 }
3103
3104 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray> seekableRanges)
3105 {
3106     m_cachedSeekableRanges = seekableRanges;
3107
3108     seekableTimeRangesChanged();
3109     updateStates();
3110 }
3111
3112 void MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange(RetainPtr<NSArray> loadedRanges)
3113 {
3114     m_cachedLoadedRanges = loadedRanges;
3115
3116     loadedTimeRangesChanged();
3117     updateStates();
3118 }
3119
3120 void MediaPlayerPrivateAVFoundationObjC::firstFrameAvailableDidChange(bool isReady)
3121 {
3122     m_cachedIsReadyForDisplay = isReady;
3123     if (!hasVideo() && isReady)
3124         tracksChanged();
3125     updateStates();
3126 }
3127
3128 void MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange(bool)
3129 {
3130     tracksChanged();
3131     updateStates();
3132 }
3133
3134 void MediaPlayerPrivateAVFoundationObjC::setShouldBufferData(bool shouldBuffer)
3135 {
3136     INFO_LOG(LOGIDENTIFIER, "- ", shouldBuffer);
3137
3138     if (m_shouldBufferData == shouldBuffer)
3139         return;
3140
3141     m_shouldBufferData = shouldBuffer;
3142     
3143     if (!m_avPlayer)
3144         return;
3145
3146     setAVPlayerItem(shouldBuffer ? m_avPlayerItem.get() : nil);
3147 }
3148
3149 #if ENABLE(DATACUE_VALUE)
3150
3151 static const AtomicString& metadataType(NSString *avMetadataKeySpace)
3152 {
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);
3158
3159     if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceQuickTimeUserData])
3160         return quickTimeUserData;
3161     if (AVMetadataKeySpaceISOUserData && [avMetadataKeySpace isEqualToString:AVMetadataKeySpaceISOUserData])
3162         return isoUserData;
3163     if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceQuickTimeMetadata])
3164         return quickTimeMetadata;
3165     if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceiTunes])
3166         return iTunesMetadata;
3167     if ([avMetadataKeySpace isEqualToString:AVMetadataKeySpaceID3])
3168         return id3Metadata;
3169
3170     return emptyAtom();
3171 }
3172
3173 #endif
3174
3175 void MediaPlayerPrivateAVFoundationObjC::metadataDidArrive(RetainPtr<NSArray> metadata, const MediaTime& mediaTime)
3176 {
3177     m_currentMetaData = metadata && ![metadata isKindOfClass:[NSNull class]] ? metadata : nil;
3178
3179     DEBUG_LOG(LOGIDENTIFIER, "adding ", m_currentMetaData ? [m_currentMetaData.get() count] : 0, " at time ", mediaTime);
3180
3181 #if ENABLE(DATACUE_VALUE)
3182     if (seeking())
3183         return;
3184
3185     if (!m_metadataTrack)
3186         processMetadataTrack();
3187
3188     if (!metadata || [metadata isKindOfClass:[NSNull class]]) {
3189         m_metadataTrack->updatePendingCueEndTimes(mediaTime);
3190         return;
3191     }
3192
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;
3199     }
3200     m_metadataTrack->updatePendingCueEndTimes(earliestStartTime);
3201
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);
3207
3208         AtomicString type = nullAtom();
3209         if (item.keySpace)
3210             type = metadataType(item.keySpace);
3211
3212         m_metadataTrack->addDataCue(start, end, SerializedPlatformRepresentationMac::create(item), type);
3213     }
3214 #endif
3215 }
3216
3217 void MediaPlayerPrivateAVFoundationObjC::tracksDidChange(RetainPtr<NSArray> tracks)
3218 {
3219     for (AVPlayerItemTrack *track in m_cachedTracks.get())
3220         [track removeObserver:m_objcObserver.get() forKeyPath:@"enabled"];
3221
3222     NSArray *assetTracks = [m_avAsset tracks];
3223
3224     m_cachedTracks = [tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id obj, NSUInteger, BOOL*) {
3225         AVAssetTrack* assetTrack = [obj assetTrack];
3226
3227         if ([assetTracks containsObject:assetTrack])
3228             return YES;
3229
3230         // Track is a streaming track. Omit if it belongs to a valid AVMediaSelectionGroup.
3231         if (!hasLoadedMediaSelectionGroups())
3232             return NO;
3233
3234         if ([assetTrack hasMediaCharacteristic:AVMediaCharacteristicAudible] && safeMediaSelectionGroupForAudibleMedia())
3235             return NO;
3236
3237         if ([assetTrack hasMediaCharacteristic:AVMediaCharacteristicVisual] && safeMediaSelectionGroupForVisualMedia())
3238             return NO;
3239
3240         if ([assetTrack hasMediaCharacteristic:AVMediaCharacteristicLegible] && safeMediaSelectionGroupForLegibleMedia())
3241             return NO;
3242
3243         return YES;
3244     }]];
3245
3246     for (AVPlayerItemTrack *track in m_cachedTracks.get())
3247         [track addObserver:m_objcObserver.get() forKeyPath:@"enabled" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayerItemTrack];
3248
3249     m_cachedTotalBytes = 0;
3250
3251     tracksChanged();
3252     updateStates();
3253 }
3254
3255 void MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange(bool hasEnabledAudio)
3256 {
3257     m_cachedHasEnabledAudio = hasEnabledAudio;
3258
3259     tracksChanged();
3260     updateStates();
3261 }
3262
3263 void MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange(FloatSize size)
3264 {
3265     m_cachedPresentationSize = size;
3266
3267     sizeChanged();
3268     updateStates();
3269 }
3270
3271 void MediaPlayerPrivateAVFoundationObjC::durationDidChange(const MediaTime& duration)
3272 {
3273     m_cachedDuration = duration;
3274
3275     invalidateCachedDuration();
3276 }
3277
3278 void MediaPlayerPrivateAVFoundationObjC::rateDidChange(double rate)
3279 {
3280     m_cachedRate = rate;
3281
3282     updateStates();
3283     rateChanged();
3284 }
3285
3286 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3287
3288 void MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange()
3289 {
3290     playbackTargetIsWirelessChanged();
3291 }
3292
3293 #endif
3294
3295 void MediaPlayerPrivateAVFoundationObjC::canPlayFastForwardDidChange(bool newValue)
3296 {
3297     m_cachedCanPlayFastForward = newValue;
3298 }
3299
3300 void MediaPlayerPrivateAVFoundationObjC::canPlayFastReverseDidChange(bool newValue)
3301 {
3302     m_cachedCanPlayFastReverse = newValue;
3303 }
3304
3305 void MediaPlayerPrivateAVFoundationObjC::setShouldDisableSleep(bool flag)
3306 {
3307 #if PLATFORM(IOS) && !PLATFORM(IOS_SIMULATOR)
3308     [m_avPlayer _setPreventsSleepDuringVideoPlayback:flag];
3309 #else
3310     UNUSED_PARAM(flag);
3311 #endif
3312 }
3313
3314 NSArray* assetMetadataKeyNames()
3315 {
3316     static NSArray* keys = [[NSArray alloc] initWithObjects:
3317         @"duration",
3318         @"naturalSize",
3319         @"preferredTransform",
3320         @"preferredVolume",
3321         @"preferredRate",
3322         @"playable",
3323         @"resolvedURL",
3324         @"tracks",
3325         @"availableMediaCharacteristicsWithMediaSelectionOptions",
3326     nil];
3327     return keys;
3328 }
3329
3330 NSArray* itemKVOProperties()
3331 {
3332     static NSArray* keys = [[NSArray alloc] initWithObjects:
3333         @"presentationSize",
3334         @"status",
3335         @"asset",
3336         @"tracks",
3337         @"seekableTimeRanges",
3338         @"loadedTimeRanges",
3339         @"playbackLikelyToKeepUp",
3340         @"playbackBufferFull",
3341         @"playbackBufferEmpty",
3342         @"duration",
3343         @"hasEnabledAudio",
3344         @"timedMetadata",
3345         @"canPlayFastForward",
3346         @"canPlayFastReverse",
3347     nil];
3348     return keys;
3349 }
3350
3351 NSArray* assetTrackMetadataKeyNames()
3352 {
3353     static NSArray* keys = [[NSArray alloc] initWithObjects:@"totalSampleDataLength", @"mediaType", @"enabled", @"preferredTransform", @"naturalSize", nil];
3354     return keys;
3355 }
3356
3357 NSArray* playerKVOProperties()
3358 {
3359     static NSArray* keys = [[NSArray alloc] initWithObjects:
3360         @"rate",
3361 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
3362         @"externalPlaybackActive",
3363         @"allowsExternalPlayback",
3364 #endif
3365 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
3366         @"outputObscuredDueToInsufficientExternalProtection",
3367 #endif
3368     nil];
3369     return keys;
3370 }
3371 } // namespace WebCore
3372
3373 @implementation WebCoreAVFMovieObserver
3374
3375 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
3376 {
3377     self = [super init];
3378     if (!self)
3379         return nil;
3380     m_callback = callback;
3381     return self;
3382 }
3383
3384 - (void)disconnect
3385 {
3386     [NSObject cancelPreviousPerformRequestsWithTarget:self];
3387     m_callback = nil;
3388 }
3389
3390 - (void)metadataLoaded
3391 {
3392     if (!m_callback)
3393         return;
3394     m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
3395 }
3396
3397 - (void)didEnd:(NSNotification *)unusedNotification
3398 {
3399     UNUSED_PARAM(unusedNotification);
3400     if (!m_callback)
3401         return;
3402     m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
3403 }
3404
3405 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context
3406 {
3407     UNUSED_PARAM(object);
3408     id newValue = [change valueForKey:NSKeyValueChangeNewKey];
3409
3410     if (!m_callback)
3411         return;
3412
3413     bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
3414     bool shouldLogValue = !willChange;
3415     WTF::Function<void ()> function;
3416
3417     if (context == MediaPlayerAVFoundationObservationContextAVPlayerLayer) {
3418         if ([keyPath isEqualToString:@"readyForDisplay"])
3419             function = std::bind(&MediaPlayerPrivateAVFoundationObjC::firstFrameAvailableDidChange, m_callback, [newValue boolValue]);
3420     }
3421
3422     if (context == MediaPlayerAVFoundationObservationContextPlayerItemTrack) {
3423         if ([keyPath isEqualToString:@"enabled"])
3424             function = std::bind(&MediaPlayerPrivateAVFoundationObjC::trackEnabledDidChange, m_callback, [newValue boolValue]);
3425     }
3426
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);
3434     }
3435
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) {
3463             MediaTime now;
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]);
3473     }
3474
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);
3482 #endif
3483 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
3484         else if ([keyPath isEqualToString:@"outputObscuredDueToInsufficientExternalProtection"])
3485             function = std::bind(&MediaPlayerPrivateAVFoundationObjC::outputObscuredDueToInsufficientExternalProtectionChanged, m_callback, [newValue boolValue]);
3486 #endif
3487     }
3488
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());
3492
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]));
3496             else {
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]);
3499             }
3500         } else
3501             m_callback->logger().debug(m_callback->logChannel(), identifier, willChange ? "will" : "did", " change '", [keyPath UTF8String], "'");
3502     }
3503 #endif
3504
3505     if (!function)
3506         return;
3507
3508     auto weakThis = m_callback->createWeakPtr();