eb6534d3d0539fdd0827bb42d466d41c716d2909
[WebKit-https.git] / Source / WebCore / platform / graphics / avfoundation / objc / MediaPlayerPrivateAVFoundationObjC.mm
1 /*
2  * Copyright (C) 2011-2014 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
28 #if ENABLE(VIDEO) && USE(AVFOUNDATION)
29
30 #import "MediaPlayerPrivateAVFoundationObjC.h"
31
32 #import "AVTrackPrivateAVFObjCImpl.h"
33 #import "AudioTrackPrivateAVFObjC.h"
34 #import "AuthenticationChallenge.h"
35 #import "BlockExceptions.h"
36 #import "CDMSessionAVFoundationObjC.h"
37 #import "ExceptionCodePlaceholder.h"
38 #import "FloatConversion.h"
39 #import "FloatConversion.h"
40 #import "FrameView.h"
41 #import "GraphicsContext.h"
42 #import "GraphicsContextCG.h"
43 #import "InbandTextTrackPrivateAVFObjC.h"
44 #import "InbandTextTrackPrivateLegacyAVFObjC.h"
45 #import "OutOfBandTextTrackPrivateAVF.h"
46 #import "URL.h"
47 #import "Logging.h"
48 #import "PlatformTimeRanges.h"
49 #import "SecurityOrigin.h"
50 #import "SoftLinking.h"
51 #import "TextTrackRepresentation.h"
52 #import "UUID.h"
53 #import "VideoTrackPrivateAVFObjC.h"
54 #import "WebCoreAVFResourceLoader.h"
55 #import "WebCoreSystemInterface.h"
56 #import <objc/runtime.h>
57 #import <runtime/DataView.h>
58 #import <runtime/JSCInlines.h>
59 #import <runtime/TypedArrayInlines.h>
60 #import <runtime/Uint16Array.h>
61 #import <runtime/Uint32Array.h>
62 #import <runtime/Uint8Array.h>
63 #import <wtf/CurrentTime.h>
64 #import <wtf/Functional.h>
65 #import <wtf/text/CString.h>
66 #import <wtf/text/StringBuilder.h>
67
68 #if ENABLE(AVF_CAPTIONS)
69 #include "TextTrack.h"
70 #endif
71
72 #import <AVFoundation/AVFoundation.h>
73 #if PLATFORM(IOS)
74 #import <CoreImage/CoreImage.h>
75 #else
76 #import <QuartzCore/CoreImage.h>
77 #endif
78 #import <CoreMedia/CoreMedia.h>
79
80 #if USE(VIDEOTOOLBOX)
81 #import <CoreVideo/CoreVideo.h>
82 #import <VideoToolbox/VideoToolbox.h>
83 #endif
84
85 #if ENABLE(AVF_CAPTIONS)
86 // Note: This must be defined before our SOFT_LINK macros:
87 @class AVMediaSelectionOption;
88 @interface AVMediaSelectionOption (OutOfBandExtensions)
89 @property (nonatomic, readonly) NSString* outOfBandSource;
90 @property (nonatomic, readonly) NSString* outOfBandIdentifier;
91 @end
92 #endif
93
94 #if PLATFORM(IOS)
95 @class AVPlayerItem;
96 @interface AVPlayerItem (WebKitExtensions)
97 @property (nonatomic, copy) NSString* dataYouTubeID;
98 @end
99 #endif
100
101 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
102 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreMedia)
103 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreImage)
104 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreVideo)
105
106 #if USE(VIDEOTOOLBOX)
107 SOFT_LINK_FRAMEWORK_OPTIONAL(VideoToolbox)
108 #endif
109
110 SOFT_LINK(CoreMedia, CMTimeCompare, int32_t, (CMTime time1, CMTime time2), (time1, time2))
111 SOFT_LINK(CoreMedia, CMTimeMakeWithSeconds, CMTime, (Float64 seconds, int32_t preferredTimeScale), (seconds, preferredTimeScale))
112 SOFT_LINK(CoreMedia, CMTimeGetSeconds, Float64, (CMTime time), (time))
113 SOFT_LINK(CoreMedia, CMTimeRangeGetEnd, CMTime, (CMTimeRange range), (range))
114
115 SOFT_LINK(CoreVideo, CVPixelBufferGetWidth, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
116 SOFT_LINK(CoreVideo, CVPixelBufferGetHeight, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
117 SOFT_LINK(CoreVideo, CVPixelBufferGetBaseAddress, void*, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
118 SOFT_LINK(CoreVideo, CVPixelBufferGetBytesPerRow, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
119 SOFT_LINK(CoreVideo, CVPixelBufferGetDataSize, size_t, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
120 SOFT_LINK(CoreVideo, CVPixelBufferGetPixelFormatType, OSType, (CVPixelBufferRef pixelBuffer), (pixelBuffer))
121 SOFT_LINK(CoreVideo, CVPixelBufferLockBaseAddress, CVReturn, (CVPixelBufferRef pixelBuffer, CVOptionFlags lockFlags), (pixelBuffer, lockFlags))
122 SOFT_LINK(CoreVideo, CVPixelBufferUnlockBaseAddress, CVReturn, (CVPixelBufferRef pixelBuffer, CVOptionFlags lockFlags), (pixelBuffer, lockFlags))
123
124 #if USE(VIDEOTOOLBOX)
125 SOFT_LINK(VideoToolbox, VTPixelTransferSessionCreate, OSStatus, (CFAllocatorRef allocator, VTPixelTransferSessionRef *pixelTransferSessionOut), (allocator, pixelTransferSessionOut))
126 SOFT_LINK(VideoToolbox, VTPixelTransferSessionTransferImage, OSStatus, (VTPixelTransferSessionRef session, CVPixelBufferRef sourceBuffer, CVPixelBufferRef destinationBuffer), (session, sourceBuffer, destinationBuffer))
127 #endif
128
129 SOFT_LINK_CLASS(AVFoundation, AVPlayer)
130 SOFT_LINK_CLASS(AVFoundation, AVPlayerItem)
131 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemVideoOutput)
132 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
133 SOFT_LINK_CLASS(AVFoundation, AVURLAsset)
134 SOFT_LINK_CLASS(AVFoundation, AVAssetImageGenerator)
135 SOFT_LINK_CLASS(CoreImage, CIContext)
136 SOFT_LINK_CLASS(CoreImage, CIImage)
137
138 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicVisual, NSString *)
139 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicAudible, NSString *)
140 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeClosedCaption, NSString *)
141 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeVideo, NSString *)
142 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
143 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemDidPlayToEndTimeNotification, NSString *)
144 SOFT_LINK_POINTER(AVFoundation, AVURLAssetInheritURIQueryComponentFromReferencingURIKey, NSString *)
145 SOFT_LINK_POINTER(AVFoundation, AVAssetImageGeneratorApertureModeCleanAperture, NSString *)
146 SOFT_LINK_POINTER(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *)
147 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *)
148 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspectFill, NSString *)
149 SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResize, NSString *)
150 SOFT_LINK_POINTER(CoreVideo, kCVPixelBufferPixelFormatTypeKey, NSString *)
151
152 SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime)
153
154 #define AVPlayer getAVPlayerClass()
155 #define AVPlayerItem getAVPlayerItemClass()
156 #define AVPlayerLayer getAVPlayerLayerClass()
157 #define AVURLAsset getAVURLAssetClass()
158 #define AVAssetImageGenerator getAVAssetImageGeneratorClass()
159
160 #define AVMediaCharacteristicVisual getAVMediaCharacteristicVisual()
161 #define AVMediaCharacteristicAudible getAVMediaCharacteristicAudible()
162 #define AVMediaTypeClosedCaption getAVMediaTypeClosedCaption()
163 #define AVMediaTypeVideo getAVMediaTypeVideo()
164 #define AVMediaTypeAudio getAVMediaTypeAudio()
165 #define AVPlayerItemDidPlayToEndTimeNotification getAVPlayerItemDidPlayToEndTimeNotification()
166 #define AVURLAssetInheritURIQueryComponentFromReferencingURIKey getAVURLAssetInheritURIQueryComponentFromReferencingURIKey()
167 #define AVAssetImageGeneratorApertureModeCleanAperture getAVAssetImageGeneratorApertureModeCleanAperture()
168 #define AVURLAssetReferenceRestrictionsKey getAVURLAssetReferenceRestrictionsKey()
169 #define AVLayerVideoGravityResizeAspect getAVLayerVideoGravityResizeAspect()
170 #define AVLayerVideoGravityResizeAspectFill getAVLayerVideoGravityResizeAspectFill()
171 #define AVLayerVideoGravityResize getAVLayerVideoGravityResize()
172 #define kCVPixelBufferPixelFormatTypeKey getkCVPixelBufferPixelFormatTypeKey()
173
174 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
175 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
176 typedef AVMediaSelectionOption AVMediaSelectionOptionType;
177
178 SOFT_LINK_CLASS(AVFoundation, AVPlayerItemLegibleOutput)
179 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionGroup)
180 SOFT_LINK_CLASS(AVFoundation, AVMediaSelectionOption)
181
182 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicLegible, NSString *)
183 SOFT_LINK_POINTER(AVFoundation, AVMediaTypeSubtitle, NSString *)
184 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicContainsOnlyForcedSubtitles, NSString *)
185 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly, NSString *)
186
187 #define AVPlayerItemLegibleOutput getAVPlayerItemLegibleOutputClass()
188 #define AVMediaSelectionGroup getAVMediaSelectionGroupClass()
189 #define AVMediaSelectionOption getAVMediaSelectionOptionClass()
190 #define AVMediaCharacteristicLegible getAVMediaCharacteristicLegible()
191 #define AVMediaTypeSubtitle getAVMediaTypeSubtitle()
192 #define AVMediaCharacteristicContainsOnlyForcedSubtitles getAVMediaCharacteristicContainsOnlyForcedSubtitles()
193 #define AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly getAVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly()
194 #endif
195
196 #if ENABLE(AVF_CAPTIONS)
197 SOFT_LINK_POINTER(AVFoundation, AVURLAssetOutOfBandAlternateTracksKey, NSString*)
198 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackDisplayNameKey, NSString*)
199 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackExtendedLanguageTagKey, NSString*)
200 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIsDefaultKey, NSString*)
201 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackMediaCharactersticsKey, NSString*)
202 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackIdentifierKey, NSString*)
203 SOFT_LINK_POINTER(AVFoundation, AVOutOfBandAlternateTrackSourceKey, NSString*)
204 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, NSString*)
205 SOFT_LINK_POINTER(AVFoundation, AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, NSString*)
206
207 #define AVURLAssetOutOfBandAlternateTracksKey getAVURLAssetOutOfBandAlternateTracksKey()
208 #define AVOutOfBandAlternateTrackDisplayNameKey getAVOutOfBandAlternateTrackDisplayNameKey()
209 #define AVOutOfBandAlternateTrackExtendedLanguageTagKey getAVOutOfBandAlternateTrackExtendedLanguageTagKey()
210 #define AVOutOfBandAlternateTrackIsDefaultKey getAVOutOfBandAlternateTrackIsDefaultKey()
211 #define AVOutOfBandAlternateTrackMediaCharactersticsKey getAVOutOfBandAlternateTrackMediaCharactersticsKey()
212 #define AVOutOfBandAlternateTrackIdentifierKey getAVOutOfBandAlternateTrackIdentifierKey()
213 #define AVOutOfBandAlternateTrackSourceKey getAVOutOfBandAlternateTrackSourceKey()
214 #define AVMediaCharacteristicDescribesMusicAndSoundForAccessibility getAVMediaCharacteristicDescribesMusicAndSoundForAccessibility()
215 #define AVMediaCharacteristicTranscribesSpokenDialogForAccessibility getAVMediaCharacteristicTranscribesSpokenDialogForAccessibility()
216 #endif
217
218 #define kCMTimeZero getkCMTimeZero()
219
220 using namespace WebCore;
221
222 enum MediaPlayerAVFoundationObservationContext {
223     MediaPlayerAVFoundationObservationContextPlayerItem,
224     MediaPlayerAVFoundationObservationContextPlayer
225 };
226
227 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
228 @interface WebCoreAVFMovieObserver : NSObject <AVPlayerItemLegibleOutputPushDelegate>
229 #else
230 @interface WebCoreAVFMovieObserver : NSObject
231 #endif
232 {
233     MediaPlayerPrivateAVFoundationObjC* m_callback;
234     int m_delayCallbacks;
235 }
236 -(id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
237 -(void)disconnect;
238 -(void)metadataLoaded;
239 -(void)didEnd:(NSNotification *)notification;
240 -(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context;
241 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
242 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime;
243 - (void)outputSequenceWasFlushed:(id)output;
244 #endif
245 @end
246
247 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
248 @interface WebCoreAVFLoaderDelegate : NSObject<AVAssetResourceLoaderDelegate> {
249     MediaPlayerPrivateAVFoundationObjC* m_callback;
250 }
251 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
252 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
253 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
254 @end
255 #endif
256
257 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
258 @interface WebCoreAVFPullDelegate : NSObject<AVPlayerItemOutputPullDelegate> {
259     MediaPlayerPrivateAVFoundationObjC *m_callback;
260     dispatch_semaphore_t m_semaphore;
261 }
262 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback;
263 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
264 - (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender;
265 - (void)outputSequenceWasFlushed:(AVPlayerItemOutput *)output;
266 @end
267 #endif
268
269 namespace WebCore {
270
271 static NSArray *assetMetadataKeyNames();
272 static NSArray *itemKVOProperties();
273
274 #if !LOG_DISABLED
275 static const char *boolString(bool val)
276 {
277     return val ? "true" : "false";
278 }
279 #endif
280
281 #if ENABLE(ENCRYPTED_MEDIA_V2)
282 typedef HashMap<MediaPlayer*, MediaPlayerPrivateAVFoundationObjC*> PlayerToPrivateMapType;
283 static PlayerToPrivateMapType& playerToPrivateMap()
284 {
285     DEPRECATED_DEFINE_STATIC_LOCAL(PlayerToPrivateMapType, map, ());
286     return map;
287 };
288 #endif
289
290 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
291 static dispatch_queue_t globalLoaderDelegateQueue()
292 {
293     static dispatch_queue_t globalQueue;
294     static dispatch_once_t onceToken;
295     dispatch_once(&onceToken, ^{
296         globalQueue = dispatch_queue_create("WebCoreAVFLoaderDelegate queue", DISPATCH_QUEUE_SERIAL);
297     });
298     return globalQueue;
299 }
300 #endif
301
302 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
303 static dispatch_queue_t globalPullDelegateQueue()
304 {
305     static dispatch_queue_t globalQueue;
306     static dispatch_once_t onceToken;
307     dispatch_once(&onceToken, ^{
308         globalQueue = dispatch_queue_create("WebCoreAVFPullDelegate queue", DISPATCH_QUEUE_SERIAL);
309     });
310     return globalQueue;
311 }
312 #endif
313
314 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateAVFoundationObjC::create(MediaPlayer* player)
315
316     return adoptPtr(new MediaPlayerPrivateAVFoundationObjC(player));
317 }
318
319 void MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(MediaEngineRegistrar registrar)
320 {
321     if (isAvailable())
322         registrar(create, getSupportedTypes, supportsType, 0, 0, 0, supportsKeySystem);
323 }
324
325 MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC(MediaPlayer* player)
326     : MediaPlayerPrivateAVFoundation(player)
327     , m_weakPtrFactory(this)
328 #if PLATFORM(IOS)
329     , m_videoFullscreenGravity(MediaPlayer::VideoGravityResizeAspect)
330 #endif
331     , m_objcObserver(adoptNS([[WebCoreAVFMovieObserver alloc] initWithCallback:this]))
332     , m_videoFrameHasDrawn(false)
333     , m_haveCheckedPlayability(false)
334 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
335     , m_videoOutputDelegate(adoptNS([[WebCoreAVFPullDelegate alloc] initWithCallback:this]))
336     , m_videoOutputSemaphore(nullptr)
337 #endif
338 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
339     , m_loaderDelegate(adoptNS([[WebCoreAVFLoaderDelegate alloc] initWithCallback:this]))
340 #endif
341     , m_currentTrack(0)
342     , m_cachedDuration(MediaPlayer::invalidTime())
343     , m_cachedRate(0)
344     , m_pendingStatusChanges(0)
345     , m_cachedItemStatus(MediaPlayerAVPlayerItemStatusDoesNotExist)
346     , m_cachedLikelyToKeepUp(false)
347     , m_cachedBufferEmpty(false)
348     , m_cachedBufferFull(false)
349     , m_cachedHasEnabledAudio(false)
350 #if ENABLE(IOS_AIRPLAY)
351     , m_allowsWirelessVideoPlayback(true)
352 #endif
353 {
354 #if ENABLE(ENCRYPTED_MEDIA_V2)
355     playerToPrivateMap().set(player, this);
356 #endif
357 }
358
359 MediaPlayerPrivateAVFoundationObjC::~MediaPlayerPrivateAVFoundationObjC()
360 {
361 #if ENABLE(ENCRYPTED_MEDIA_V2)
362     playerToPrivateMap().remove(player());
363 #endif
364 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
365     [m_loaderDelegate.get() setCallback:0];
366     [[m_avAsset.get() resourceLoader] setDelegate:nil queue:0];
367 #endif
368 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
369     [m_videoOutputDelegate setCallback:0];
370     [m_videoOutput setDelegate:nil queue:0];
371     if (m_videoOutputSemaphore)
372         dispatch_release(m_videoOutputSemaphore);
373 #endif
374     cancelLoad();
375 }
376
377 void MediaPlayerPrivateAVFoundationObjC::cancelLoad()
378 {
379     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::cancelLoad(%p)", this);
380     tearDownVideoRendering();
381
382     [[NSNotificationCenter defaultCenter] removeObserver:m_objcObserver.get()];
383     [m_objcObserver.get() disconnect];
384
385     // Tell our observer to do nothing when our cancellation of pending loading calls its completion handler.
386     setIgnoreLoadStateChanges(true);
387     if (m_avAsset) {
388         [m_avAsset.get() cancelLoading];
389         m_avAsset = nil;
390     }
391
392     clearTextTracks();
393
394 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
395     if (m_legibleOutput) {
396         if (m_avPlayerItem)
397             [m_avPlayerItem.get() removeOutput:m_legibleOutput.get()];
398         m_legibleOutput = nil;
399     }
400 #endif
401
402     if (m_avPlayerItem) {
403         for (NSString *keyName in itemKVOProperties())
404             [m_avPlayerItem.get() removeObserver:m_objcObserver.get() forKeyPath:keyName];
405         
406         m_avPlayerItem = nil;
407     }
408     if (m_avPlayer) {
409         if (m_timeObserver)
410             [m_avPlayer.get() removeTimeObserver:m_timeObserver.get()];
411         m_timeObserver = nil;
412         [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"rate"];
413 #if ENABLE(IOS_AIRPLAY)
414         [m_avPlayer.get() removeObserver:m_objcObserver.get() forKeyPath:@"externalPlaybackActive"];
415 #endif
416         m_avPlayer = nil;
417     }
418
419     // Reset cached properties
420     m_pendingStatusChanges = 0;
421     m_cachedItemStatus = MediaPlayerAVPlayerItemStatusDoesNotExist;
422     m_cachedSeekableRanges = nullptr;
423     m_cachedLoadedRanges = nullptr;
424     m_cachedTracks = nullptr;
425     m_cachedHasEnabledAudio = false;
426     m_cachedPresentationSize = FloatSize();
427     m_cachedDuration = 0;
428
429     setIgnoreLoadStateChanges(false);
430 }
431
432 bool MediaPlayerPrivateAVFoundationObjC::hasLayerRenderer() const
433 {
434     return m_videoLayer;
435 }
436
437 bool MediaPlayerPrivateAVFoundationObjC::hasContextRenderer() const
438 {
439 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
440     if (m_videoOutput)
441         return true;
442 #endif
443     return m_imageGenerator;
444 }
445
446 void MediaPlayerPrivateAVFoundationObjC::createContextVideoRenderer()
447 {
448 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
449     createVideoOutput();
450 #else
451     createImageGenerator();
452 #endif
453 }
454
455 void MediaPlayerPrivateAVFoundationObjC::createImageGenerator()
456 {
457     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageGenerator(%p)", this);
458
459     if (!m_avAsset || m_imageGenerator)
460         return;
461
462     m_imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:m_avAsset.get()];
463
464     [m_imageGenerator.get() setApertureMode:AVAssetImageGeneratorApertureModeCleanAperture];
465     [m_imageGenerator.get() setAppliesPreferredTrackTransform:YES];
466     [m_imageGenerator.get() setRequestedTimeToleranceBefore:kCMTimeZero];
467     [m_imageGenerator.get() setRequestedTimeToleranceAfter:kCMTimeZero];
468
469     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageGenerator(%p) - returning %p", this, m_imageGenerator.get());
470 }
471
472 void MediaPlayerPrivateAVFoundationObjC::destroyContextVideoRenderer()
473 {
474 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
475     destroyVideoOutput();
476 #endif
477     destroyImageGenerator();
478 }
479
480 void MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator()
481 {
482     if (!m_imageGenerator)
483         return;
484
485     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyImageGenerator(%p) - destroying  %p", this, m_imageGenerator.get());
486
487     m_imageGenerator = 0;
488 }
489
490 void MediaPlayerPrivateAVFoundationObjC::createVideoLayer()
491 {
492     if (!m_avPlayer || m_videoLayer)
493         return;
494
495     auto weakThis = createWeakPtr();
496     callOnMainThread([this, weakThis] {
497         if (!weakThis)
498             return;
499
500         if (!m_avPlayer || m_videoLayer)
501             return;
502
503         m_videoLayer = adoptNS([[AVPlayerLayer alloc] init]);
504         [m_videoLayer.get() setPlayer:m_avPlayer.get()];
505         [m_videoLayer.get() setBackgroundColor:cachedCGColor(Color::black, ColorSpaceDeviceRGB)];
506 #ifndef NDEBUG
507         [m_videoLayer.get() setName:@"Video layer"];
508 #endif
509         updateVideoLayerGravity();
510         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoLayer(%p) - returning %p", this, m_videoLayer.get());
511
512 #if PLATFORM(IOS)
513         if (m_videoFullscreenLayer) {
514             [m_videoLayer setFrame:CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height())];
515             [m_videoFullscreenLayer insertSublayer:m_videoLayer.get() atIndex:0];
516         }
517 #endif
518         player()->mediaPlayerClient()->mediaPlayerRenderingModeChanged(player());
519     });
520 }
521
522 void MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer()
523 {
524     if (!m_videoLayer)
525         return;
526
527     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyVideoLayer(%p) - destroying %p", this, m_videoLayer.get());
528
529     [m_videoLayer.get() setPlayer:nil];
530
531     m_videoLayer = 0;
532 }
533
534 bool MediaPlayerPrivateAVFoundationObjC::hasAvailableVideoFrame() const
535 {
536     if (currentRenderingMode() == MediaRenderingToLayer)
537         return m_videoLayer && [m_videoLayer.get() isReadyForDisplay];
538
539     return m_videoFrameHasDrawn;
540 }
541
542 #if ENABLE(AVF_CAPTIONS)
543 static const NSArray* mediaDescriptionForKind(PlatformTextTrack::TrackKind kind)
544 {
545     // FIXME: Match these to correct types:
546     if (kind == PlatformTextTrack::Caption)
547         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
548
549     if (kind == PlatformTextTrack::Subtitle)
550         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
551
552     if (kind == PlatformTextTrack::Description)
553         return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, AVMediaCharacteristicDescribesMusicAndSoundForAccessibility, nil];
554
555     if (kind == PlatformTextTrack::Forced)
556         return [NSArray arrayWithObjects: AVMediaCharacteristicContainsOnlyForcedSubtitles, nil];
557
558     return [NSArray arrayWithObjects: AVMediaCharacteristicTranscribesSpokenDialogForAccessibility, nil];
559 }
560     
561 void MediaPlayerPrivateAVFoundationObjC::notifyTrackModeChanged()
562 {
563     trackModeChanged();
564 }
565     
566 void MediaPlayerPrivateAVFoundationObjC::synchronizeTextTrackState()
567 {
568     const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
569     
570     for (auto& textTrack : m_textTracks) {
571         if (textTrack->textTrackCategory() != InbandTextTrackPrivateAVF::OutOfBand)
572             continue;
573         
574         RefPtr<OutOfBandTextTrackPrivateAVF> trackPrivate = static_cast<OutOfBandTextTrackPrivateAVF*>(textTrack.get());
575         RetainPtr<AVMediaSelectionOptionType> currentOption = trackPrivate->mediaSelectionOption();
576         
577         for (auto& track : outOfBandTrackSources) {
578             RetainPtr<CFStringRef> uniqueID = String::number(track->uniqueId()).createCFString();
579             
580             if (![[currentOption.get() outOfBandIdentifier] isEqual: reinterpret_cast<const NSString*>(uniqueID.get())])
581                 continue;
582             
583             InbandTextTrackPrivate::Mode mode = InbandTextTrackPrivate::Hidden;
584             if (track->mode() == PlatformTextTrack::Hidden)
585                 mode = InbandTextTrackPrivate::Hidden;
586             else if (track->mode() == PlatformTextTrack::Disabled)
587                 mode = InbandTextTrackPrivate::Disabled;
588             else if (track->mode() == PlatformTextTrack::Showing)
589                 mode = InbandTextTrackPrivate::Showing;
590             
591             textTrack->setMode(mode);
592             break;
593         }
594     }
595 }
596 #endif
597
598 void MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(const String& url)
599 {
600     if (m_avAsset)
601         return;
602
603     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL(%p)", this);
604
605     setDelayCallbacks(true);
606
607     RetainPtr<NSMutableDictionary> options = adoptNS([[NSMutableDictionary alloc] init]);    
608
609     [options.get() setObject:[NSNumber numberWithInt:AVAssetReferenceRestrictionForbidRemoteReferenceToLocal | AVAssetReferenceRestrictionForbidLocalReferenceToRemote] forKey:AVURLAssetReferenceRestrictionsKey];
610
611     RetainPtr<NSMutableDictionary> headerFields = adoptNS([[NSMutableDictionary alloc] init]);
612
613     String referrer = player()->referrer();
614     if (!referrer.isEmpty())
615         [headerFields.get() setObject:referrer forKey:@"Referer"];
616
617     String userAgent = player()->userAgent();
618     if (!userAgent.isEmpty())
619         [headerFields.get() setObject:userAgent forKey:@"User-Agent"];
620
621     if ([headerFields.get() count])
622         [options.get() setObject:headerFields.get() forKey:@"AVURLAssetHTTPHeaderFieldsKey"];
623
624     if (player()->doesHaveAttribute("x-itunes-inherit-uri-query-component"))
625         [options.get() setObject: [NSNumber numberWithBool: TRUE] forKey: AVURLAssetInheritURIQueryComponentFromReferencingURIKey];
626
627 #if ENABLE(AVF_CAPTIONS)
628     const Vector<RefPtr<PlatformTextTrack>>& outOfBandTrackSources = player()->outOfBandTrackSources();
629     if (!outOfBandTrackSources.isEmpty()) {
630         NSMutableArray* outOfBandTracks = [[NSMutableArray alloc] init];
631         for (auto& trackSource : outOfBandTrackSources) {
632             RetainPtr<CFStringRef> label = trackSource->label().createCFString();
633             RetainPtr<CFStringRef> language = trackSource->language().createCFString();
634             RetainPtr<CFStringRef> uniqueID = String::number(trackSource->uniqueId()).createCFString();
635             RetainPtr<CFStringRef> url = trackSource->url().createCFString();
636             [outOfBandTracks addObject:
637                 [NSDictionary dictionaryWithObjectsAndKeys:
638                     reinterpret_cast<const NSString*>(label.get()), AVOutOfBandAlternateTrackDisplayNameKey,
639                     reinterpret_cast<const NSString*>(language.get()), AVOutOfBandAlternateTrackExtendedLanguageTagKey,
640                     [NSNumber numberWithBool: (trackSource->isDefault() ? YES : NO)], AVOutOfBandAlternateTrackIsDefaultKey,
641                     reinterpret_cast<const NSString*>(uniqueID.get()), AVOutOfBandAlternateTrackIdentifierKey,
642                     reinterpret_cast<const NSString*>(url.get()), AVOutOfBandAlternateTrackSourceKey,
643                     mediaDescriptionForKind(trackSource->kind()), AVOutOfBandAlternateTrackMediaCharactersticsKey,
644                     nil]];
645         }
646
647         [options.get() setObject: outOfBandTracks forKey: AVURLAssetOutOfBandAlternateTracksKey];
648     }
649 #endif
650     
651     NSURL *cocoaURL = URL(ParsedURLString, url);
652     m_avAsset = adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
653
654 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
655     [[m_avAsset.get() resourceLoader] setDelegate:m_loaderDelegate.get() queue:globalLoaderDelegateQueue()];
656 #endif
657
658     m_haveCheckedPlayability = false;
659
660     setDelayCallbacks(false);
661 }
662
663 void MediaPlayerPrivateAVFoundationObjC::createAVPlayer()
664 {
665     if (m_avPlayer)
666         return;
667
668     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayer(%p)", this);
669
670     setDelayCallbacks(true);
671
672     m_avPlayer = adoptNS([[AVPlayer alloc] init]);
673     [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
674 #if ENABLE(IOS_AIRPLAY)
675     [m_avPlayer.get() addObserver:m_objcObserver.get() forKeyPath:@"externalPlaybackActive" options:NSKeyValueObservingOptionNew context:(void *)MediaPlayerAVFoundationObservationContextPlayer];
676 #endif
677
678 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
679     [m_avPlayer.get() setAppliesMediaSelectionCriteriaAutomatically:YES];
680 #endif
681
682 #if ENABLE(IOS_AIRPLAY)
683     [m_avPlayer.get() setAllowsExternalPlayback:m_allowsWirelessVideoPlayback];
684 #endif
685
686     if (m_avPlayerItem)
687         [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
688
689     setDelayCallbacks(false);
690 }
691
692 void MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem()
693 {
694     if (m_avPlayerItem)
695         return;
696
697     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createAVPlayerItem(%p)", this);
698
699     setDelayCallbacks(true);
700
701     // Create the player item so we can load media data. 
702     m_avPlayerItem = adoptNS([[AVPlayerItem alloc] initWithAsset:m_avAsset.get()]);
703
704     [[NSNotificationCenter defaultCenter] addObserver:m_objcObserver.get() selector:@selector(didEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:m_avPlayerItem.get()];
705
706     NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionPrior;
707     for (NSString *keyName in itemKVOProperties())
708         [m_avPlayerItem.get() addObserver:m_objcObserver.get() forKeyPath:keyName options:options context:(void *)MediaPlayerAVFoundationObservationContextPlayerItem];
709
710     if (m_avPlayer)
711         [m_avPlayer.get() replaceCurrentItemWithPlayerItem:m_avPlayerItem.get()];
712
713 #if PLATFORM(IOS)
714     AtomicString value;
715     if (player()->doesHaveAttribute("data-youtube-id", &value))
716         [m_avPlayerItem.get() setDataYouTubeID: value];
717  #endif
718
719 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
720     const NSTimeInterval legibleOutputAdvanceInterval = 2;
721
722     m_legibleOutput = adoptNS([[AVPlayerItemLegibleOutput alloc] initWithMediaSubtypesForNativeRepresentation:[NSArray array]]);
723     [m_legibleOutput.get() setSuppressesPlayerRendering:YES];
724
725     [m_legibleOutput.get() setDelegate:m_objcObserver.get() queue:dispatch_get_main_queue()];
726     [m_legibleOutput.get() setAdvanceIntervalForDelegateInvocation:legibleOutputAdvanceInterval];
727     [m_legibleOutput.get() setTextStylingResolution:AVPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly];
728     [m_avPlayerItem.get() addOutput:m_legibleOutput.get()];
729 #endif
730
731     setDelayCallbacks(false);
732 }
733
734 void MediaPlayerPrivateAVFoundationObjC::checkPlayability()
735 {
736     if (m_haveCheckedPlayability)
737         return;
738     m_haveCheckedPlayability = true;
739
740     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::checkPlayability(%p)", this);
741     auto weakThis = createWeakPtr();
742
743     [m_avAsset.get() loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"playable"] completionHandler:^{
744         callOnMainThread([weakThis] {
745             if (weakThis)
746                 weakThis->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
747         });
748     }];
749 }
750
751 void MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata()
752 {
753     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::beginLoadingMetadata(%p) - requesting metadata loading", this);
754     [m_avAsset.get() loadValuesAsynchronouslyForKeys:[assetMetadataKeyNames() retain] completionHandler:^{
755         [m_objcObserver.get() metadataLoaded];
756     }];
757 }
758
759 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationObjC::playerItemStatus() const
760 {
761     if (!m_avPlayerItem)
762         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
763
764     if (m_cachedItemStatus == AVPlayerItemStatusUnknown)
765         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
766     if (m_cachedItemStatus == AVPlayerItemStatusFailed)
767         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
768     if (m_cachedLikelyToKeepUp)
769         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
770     if (m_cachedBufferFull)
771         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
772     if (m_cachedBufferEmpty)
773         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
774
775     return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
776 }
777
778 PlatformMedia MediaPlayerPrivateAVFoundationObjC::platformMedia() const
779 {
780     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformMedia(%p)", this);
781     PlatformMedia pm;
782     pm.type = PlatformMedia::AVFoundationMediaPlayerType;
783     pm.media.avfMediaPlayer = m_avPlayer.get();
784     return pm;
785 }
786
787 PlatformLayer* MediaPlayerPrivateAVFoundationObjC::platformLayer() const
788 {
789     return m_videoLayer.get();
790 }
791
792 #if PLATFORM(IOS)
793 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenLayer(PlatformLayer* videoFullscreenLayer)
794 {
795     if (m_videoFullscreenLayer == videoFullscreenLayer)
796         return;
797
798     if (m_videoFullscreenLayer)
799        [m_videoLayer removeFromSuperlayer];
800
801     m_videoFullscreenLayer = videoFullscreenLayer;
802
803     CGRect frame = CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height());
804
805     if (m_videoFullscreenLayer && m_videoLayer) {
806         [m_videoLayer setFrame:frame];
807         [m_videoFullscreenLayer insertSublayer:m_videoLayer.get() atIndex:0];
808     }
809
810     if (m_videoFullscreenLayer && m_textTrackRepresentationLayer) {
811         CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : frame;
812         [m_textTrackRepresentationLayer setFrame:textFrame];
813         [m_videoFullscreenLayer addSublayer:m_textTrackRepresentationLayer.get()];
814     }
815 }
816
817 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenFrame(FloatRect frame)
818 {
819     m_videoFullscreenFrame = frame;
820     if (!m_videoFullscreenLayer)
821         return;
822
823     if (m_videoLayer)
824         [m_videoLayer setFrame:CGRectMake(0, 0, frame.width(), frame.height())];
825
826     if (m_textTrackRepresentationLayer) {
827         CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : static_cast<CGRect>(frame);
828         [m_textTrackRepresentationLayer setFrame:textFrame];
829     }
830 }
831
832 void MediaPlayerPrivateAVFoundationObjC::setVideoFullscreenGravity(MediaPlayer::VideoGravity gravity)
833 {
834     m_videoFullscreenGravity = gravity;
835     if (!m_videoLayer)
836         return;
837
838     NSString *videoGravity = AVLayerVideoGravityResizeAspect;
839     if (gravity == MediaPlayer::VideoGravityResize)
840         videoGravity = AVLayerVideoGravityResize;
841     else if (gravity == MediaPlayer::VideoGravityResizeAspect)
842         videoGravity = AVLayerVideoGravityResizeAspect;
843     else if (gravity == MediaPlayer::VideoGravityResizeAspectFill)
844         videoGravity = AVLayerVideoGravityResizeAspectFill;
845     else
846         ASSERT_NOT_REACHED();
847
848     [m_videoLayer setVideoGravity:videoGravity];
849 }
850 #endif
851
852 void MediaPlayerPrivateAVFoundationObjC::platformSetVisible(bool isVisible)
853 {
854     [CATransaction begin];
855     [CATransaction setDisableActions:YES];    
856     if (m_videoLayer)
857         [m_videoLayer.get() setHidden:!isVisible];
858     [CATransaction commit];
859 }
860     
861 void MediaPlayerPrivateAVFoundationObjC::platformPlay()
862 {
863     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformPlay(%p)", this);
864     if (!metaDataAvailable())
865         return;
866
867     setDelayCallbacks(true);
868     m_cachedRate = requestedRate();
869     [m_avPlayer.get() setRate:requestedRate()];
870     setDelayCallbacks(false);
871 }
872
873 void MediaPlayerPrivateAVFoundationObjC::platformPause()
874 {
875     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformPause(%p)", this);
876     if (!metaDataAvailable())
877         return;
878
879     setDelayCallbacks(true);
880     m_cachedRate = 0;
881     [m_avPlayer.get() setRate:nil];
882     setDelayCallbacks(false);
883 }
884
885 float MediaPlayerPrivateAVFoundationObjC::platformDuration() const
886 {
887     // Do not ask the asset for duration before it has been loaded or it will fetch the
888     // answer synchronously.
889     if (!m_avAsset || assetStatus() < MediaPlayerAVAssetStatusLoaded)
890          return MediaPlayer::invalidTime();
891     
892     CMTime cmDuration;
893     
894     // Check the AVItem if we have one and it has loaded duration, some assets never report duration.
895     if (m_avPlayerItem && playerItemStatus() >= MediaPlayerAVPlayerItemStatusReadyToPlay)
896         cmDuration = [m_avPlayerItem.get() duration];
897     else
898         cmDuration= [m_avAsset.get() duration];
899
900     if (CMTIME_IS_NUMERIC(cmDuration))
901         return narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
902
903     if (CMTIME_IS_INDEFINITE(cmDuration)) {
904         return std::numeric_limits<float>::infinity();
905     }
906
907     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::platformDuration(%p) - invalid duration, returning %.0f", this, MediaPlayer::invalidTime());
908     return MediaPlayer::invalidTime();
909 }
910
911 float MediaPlayerPrivateAVFoundationObjC::currentTime() const
912 {
913     if (!metaDataAvailable() || !m_avPlayerItem)
914         return 0;
915
916     CMTime itemTime = [m_avPlayerItem.get() currentTime];
917     if (CMTIME_IS_NUMERIC(itemTime))
918         return std::max(narrowPrecisionToFloat(CMTimeGetSeconds(itemTime)), 0.0f);
919
920     return 0;
921 }
922
923 void MediaPlayerPrivateAVFoundationObjC::seekToTime(double time, double negativeTolerance, double positiveTolerance)
924 {
925     // setCurrentTime generates several event callbacks, update afterwards.
926     setDelayCallbacks(true);
927
928     CMTime cmTime = CMTimeMakeWithSeconds(time, 600);
929     CMTime cmBefore = CMTimeMakeWithSeconds(negativeTolerance, 600);
930     CMTime cmAfter = CMTimeMakeWithSeconds(positiveTolerance, 600);
931
932     auto weakThis = createWeakPtr();
933
934     [m_avPlayerItem.get() seekToTime:cmTime toleranceBefore:cmBefore toleranceAfter:cmAfter completionHandler:^(BOOL finished) {
935         callOnMainThread([weakThis, finished] {
936             auto _this = weakThis.get();
937             if (!_this)
938                 return;
939
940             _this->seekCompleted(finished);
941         });
942     }];
943
944     setDelayCallbacks(false);
945 }
946
947 void MediaPlayerPrivateAVFoundationObjC::setVolume(float volume)
948 {
949     if (!metaDataAvailable())
950         return;
951
952     [m_avPlayer.get() setVolume:volume];
953 }
954
955 void MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(bool closedCaptionsVisible)
956 {
957     UNUSED_PARAM(closedCaptionsVisible);
958
959     if (!metaDataAvailable())
960         return;
961
962     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setClosedCaptionsVisible(%p) - set to %s", this, boolString(closedCaptionsVisible));
963 }
964
965 void MediaPlayerPrivateAVFoundationObjC::updateRate()
966 {
967     setDelayCallbacks(true);
968     m_cachedRate = requestedRate();
969     [m_avPlayer.get() setRate:requestedRate()];
970     setDelayCallbacks(false);
971 }
972
973 float MediaPlayerPrivateAVFoundationObjC::rate() const
974 {
975     if (!metaDataAvailable())
976         return 0;
977
978     return m_cachedRate;
979 }
980
981 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateAVFoundationObjC::platformBufferedTimeRanges() const
982 {
983     auto timeRanges = PlatformTimeRanges::create();
984
985     if (!m_avPlayerItem)
986         return timeRanges;
987
988     for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
989         CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
990         if (CMTIMERANGE_IS_VALID(timeRange) && !CMTIMERANGE_IS_EMPTY(timeRange)) {
991             float rangeStart = narrowPrecisionToFloat(CMTimeGetSeconds(timeRange.start));
992             float rangeEnd = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange)));
993             timeRanges->add(rangeStart, rangeEnd);
994         }
995     }
996     return timeRanges;
997 }
998
999 double MediaPlayerPrivateAVFoundationObjC::platformMinTimeSeekable() const
1000 {
1001     if (!m_cachedSeekableRanges || ![m_cachedSeekableRanges count])
1002         return 0;
1003
1004     double minTimeSeekable = std::numeric_limits<double>::infinity();
1005     bool hasValidRange = false;
1006     for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1007         CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1008         if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1009             continue;
1010
1011         hasValidRange = true;
1012         double startOfRange = CMTimeGetSeconds(timeRange.start);
1013         if (minTimeSeekable > startOfRange)
1014             minTimeSeekable = startOfRange;
1015     }
1016     return hasValidRange ? minTimeSeekable : 0;
1017 }
1018
1019 double MediaPlayerPrivateAVFoundationObjC::platformMaxTimeSeekable() const
1020 {
1021     if (!m_cachedSeekableRanges)
1022         m_cachedSeekableRanges = [m_avPlayerItem seekableTimeRanges];
1023
1024     double maxTimeSeekable = 0;
1025     for (NSValue *thisRangeValue in m_cachedSeekableRanges.get()) {
1026         CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1027         if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1028             continue;
1029         
1030         double endOfRange = CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange));
1031         if (maxTimeSeekable < endOfRange)
1032             maxTimeSeekable = endOfRange;
1033     }
1034     return maxTimeSeekable;
1035 }
1036
1037 float MediaPlayerPrivateAVFoundationObjC::platformMaxTimeLoaded() const
1038 {
1039 #if !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED <= 1080
1040     // AVFoundation on Mountain Lion will occasionally not send a KVO notification
1041     // when loadedTimeRanges changes when there is no video output. In that case
1042     // update the cached value explicitly.
1043     if (!hasLayerRenderer() && !hasContextRenderer())
1044         m_cachedLoadedRanges = [m_avPlayerItem loadedTimeRanges];
1045 #endif
1046
1047     if (!m_cachedLoadedRanges)
1048         return 0;
1049
1050     float maxTimeLoaded = 0;
1051     for (NSValue *thisRangeValue in m_cachedLoadedRanges.get()) {
1052         CMTimeRange timeRange = [thisRangeValue CMTimeRangeValue];
1053         if (!CMTIMERANGE_IS_VALID(timeRange) || CMTIMERANGE_IS_EMPTY(timeRange))
1054             continue;
1055         
1056         float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeRangeGetEnd(timeRange)));
1057         if (maxTimeLoaded < endOfRange)
1058             maxTimeLoaded = endOfRange;
1059     }
1060
1061     return maxTimeLoaded;   
1062 }
1063
1064 unsigned long long MediaPlayerPrivateAVFoundationObjC::totalBytes() const
1065 {
1066     if (!metaDataAvailable())
1067         return 0;
1068
1069     long long totalMediaSize = 0;
1070     for (AVPlayerItemTrack *thisTrack in m_cachedTracks.get())
1071         totalMediaSize += [[thisTrack assetTrack] totalSampleDataLength];
1072
1073     return totalMediaSize;
1074 }
1075
1076 void MediaPlayerPrivateAVFoundationObjC::setAsset(id asset)
1077 {
1078     m_avAsset = asset;
1079 }
1080
1081 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationObjC::assetStatus() const
1082 {
1083     if (!m_avAsset)
1084         return MediaPlayerAVAssetStatusDoesNotExist;
1085
1086     for (NSString *keyName in assetMetadataKeyNames()) {
1087         AVKeyValueStatus keyStatus = [m_avAsset.get() statusOfValueForKey:keyName error:nil];
1088
1089         if (keyStatus < AVKeyValueStatusLoaded)
1090             return MediaPlayerAVAssetStatusLoading;// At least one key is not loaded yet.
1091         
1092         if (keyStatus == AVKeyValueStatusFailed)
1093             return MediaPlayerAVAssetStatusFailed; // At least one key could not be loaded.
1094
1095         if (keyStatus == AVKeyValueStatusCancelled)
1096             return MediaPlayerAVAssetStatusCancelled; // Loading of at least one key was cancelled.
1097     }
1098
1099     if ([[m_avAsset.get() valueForKey:@"playable"] boolValue])
1100         return MediaPlayerAVAssetStatusPlayable;
1101
1102     return MediaPlayerAVAssetStatusLoaded;
1103 }
1104
1105 void MediaPlayerPrivateAVFoundationObjC::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
1106 {
1107     if (!metaDataAvailable() || context->paintingDisabled())
1108         return;
1109
1110     setDelayCallbacks(true);
1111     BEGIN_BLOCK_OBJC_EXCEPTIONS;
1112
1113 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1114     if (videoOutputHasAvailableFrame())
1115         paintWithVideoOutput(context, rect);
1116     else
1117 #endif
1118         paintWithImageGenerator(context, rect);
1119
1120     END_BLOCK_OBJC_EXCEPTIONS;
1121     setDelayCallbacks(false);
1122
1123     m_videoFrameHasDrawn = true;
1124 }
1125
1126 void MediaPlayerPrivateAVFoundationObjC::paint(GraphicsContext* context, const IntRect& rect)
1127 {
1128     if (!metaDataAvailable() || context->paintingDisabled())
1129         return;
1130
1131     // We can ignore the request if we are already rendering to a layer.
1132     if (currentRenderingMode() == MediaRenderingToLayer)
1133         return;
1134
1135     paintCurrentFrameInContext(context, rect);
1136 }
1137
1138 void MediaPlayerPrivateAVFoundationObjC::paintWithImageGenerator(GraphicsContext* context, const IntRect& rect)
1139 {
1140     RetainPtr<CGImageRef> image = createImageForTimeInRect(currentTime(), rect);
1141     if (image) {
1142         GraphicsContextStateSaver stateSaver(*context);
1143         context->translate(rect.x(), rect.y() + rect.height());
1144         context->scale(FloatSize(1.0f, -1.0f));
1145         context->setImageInterpolationQuality(InterpolationLow);
1146         IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
1147         CGContextDrawImage(context->platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
1148         image = 0;
1149     }
1150 }
1151
1152 static HashSet<String> mimeTypeCache()
1153 {
1154     DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
1155     static bool typeListInitialized = false;
1156
1157     if (typeListInitialized)
1158         return cache;
1159     typeListInitialized = true;
1160
1161     NSArray *types = [AVURLAsset audiovisualMIMETypes];
1162     for (NSString *mimeType in types)
1163         cache.add(mimeType);
1164
1165     return cache;
1166
1167
1168 RetainPtr<CGImageRef> MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(float time, const IntRect& rect)
1169 {
1170     if (!m_imageGenerator)
1171         createImageGenerator();
1172     ASSERT(m_imageGenerator);
1173
1174 #if !LOG_DISABLED
1175     double start = monotonicallyIncreasingTime();
1176 #endif
1177
1178     [m_imageGenerator.get() setMaximumSize:CGSize(rect.size())];
1179     RetainPtr<CGImageRef> rawImage = adoptCF([m_imageGenerator.get() copyCGImageAtTime:CMTimeMakeWithSeconds(time, 600) actualTime:nil error:nil]);
1180     RetainPtr<CGImageRef> image = adoptCF(CGImageCreateCopyWithColorSpace(rawImage.get(), deviceRGBColorSpaceRef()));
1181
1182 #if !LOG_DISABLED
1183     double duration = monotonicallyIncreasingTime() - start;
1184     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createImageForTimeInRect(%p) - creating image took %.4f", this, narrowPrecisionToFloat(duration));
1185 #endif
1186
1187     return image;
1188 }
1189
1190 void MediaPlayerPrivateAVFoundationObjC::getSupportedTypes(HashSet<String>& supportedTypes)
1191 {
1192     supportedTypes = mimeTypeCache();
1193
1194
1195 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1196 static bool keySystemIsSupported(const String& keySystem)
1197 {
1198     if (equalIgnoringCase(keySystem, "com.apple.fps") || equalIgnoringCase(keySystem, "com.apple.fps.1_0"))
1199         return true;
1200     return false;
1201 }
1202 #endif
1203
1204 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationObjC::supportsType(const MediaEngineSupportParameters& parameters)
1205 {
1206 #if ENABLE(ENCRYPTED_MEDIA)
1207     // From: <http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-canplaytype>
1208     // In addition to the steps in the current specification, this method must run the following steps:
1209
1210     // 1. Check whether the Key System is supported with the specified container and codec type(s) by following the steps for the first matching condition from the following list:
1211     //    If keySystem is null, continue to the next step.
1212     if (!parameters.keySystem.isNull() && !parameters.keySystem.isEmpty()) {
1213         // If keySystem contains an unrecognized or unsupported Key System, return the empty string
1214         if (!keySystemIsSupported(parameters.keySystem))
1215             return MediaPlayer::IsNotSupported;
1216
1217         // If the Key System specified by keySystem does not support decrypting the container and/or codec specified in the rest of the type string.
1218         // (AVFoundation does not provide an API which would allow us to determine this, so this is a no-op)
1219     }
1220
1221     // 2. Return "maybe" or "probably" as appropriate per the existing specification of canPlayType().
1222 #endif
1223
1224 #if ENABLE(MEDIA_SOURCE)
1225     if (parameters.isMediaSource)
1226         return MediaPlayer::IsNotSupported;
1227 #endif
1228
1229     if (!mimeTypeCache().contains(parameters.type))
1230         return MediaPlayer::IsNotSupported;
1231
1232     // The spec says:
1233     // "Implementors are encouraged to return "maybe" unless the type can be confidently established as being supported or not."
1234     if (parameters.codecs.isEmpty())
1235         return MediaPlayer::MayBeSupported;
1236
1237     NSString *typeString = [NSString stringWithFormat:@"%@; codecs=\"%@\"", (NSString *)parameters.type, (NSString *)parameters.codecs];
1238     return [AVURLAsset isPlayableExtendedMIMEType:typeString] ? MediaPlayer::IsSupported : MediaPlayer::MayBeSupported;;
1239 }
1240
1241 bool MediaPlayerPrivateAVFoundationObjC::supportsKeySystem(const String& keySystem, const String& mimeType)
1242 {
1243 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1244     if (!keySystem.isEmpty()) {
1245         if (!keySystemIsSupported(keySystem))
1246             return false;
1247
1248         if (!mimeType.isEmpty() && !mimeTypeCache().contains(mimeType))
1249             return false;
1250
1251         return true;
1252     }
1253 #else
1254     UNUSED_PARAM(keySystem);
1255     UNUSED_PARAM(mimeType);
1256 #endif
1257     return false;
1258 }
1259
1260 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
1261 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest* avRequest)
1262 {
1263     String scheme = [[[avRequest request] URL] scheme];
1264     String keyURI = [[[avRequest request] URL] absoluteString];
1265
1266 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1267     if (scheme == "skd") {
1268         // Create an initData with the following layout:
1269         // [4 bytes: keyURI size], [keyURI size bytes: keyURI]
1270         unsigned keyURISize = keyURI.length() * sizeof(UChar);
1271         RefPtr<ArrayBuffer> initDataBuffer = ArrayBuffer::create(4 + keyURISize, 1);
1272         RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
1273         initDataView->set<uint32_t>(0, keyURISize, true);
1274
1275         RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, 4, keyURI.length());
1276         keyURIArray->setRange(StringView(keyURI).upconvertedCharacters(), keyURI.length() / sizeof(unsigned char), 0);
1277
1278 #if ENABLE(ENCRYPTED_MEDIA)
1279         if (!player()->keyNeeded("com.apple.lskd", emptyString(), static_cast<const unsigned char*>(initDataBuffer->data()), initDataBuffer->byteLength()))
1280 #elif ENABLE(ENCRYPTED_MEDIA_V2)
1281         RefPtr<Uint8Array> initData = Uint8Array::create(initDataBuffer, 0, initDataBuffer->byteLength());
1282         if (!player()->keyNeeded(initData.get()))
1283 #endif
1284             return false;
1285
1286         m_keyURIToRequestMap.set(keyURI, avRequest);
1287         return true;
1288     }
1289 #endif
1290
1291     RefPtr<WebCoreAVFResourceLoader> resourceLoader = WebCoreAVFResourceLoader::create(this, avRequest);
1292     m_resourceLoaderMap.add(avRequest, resourceLoader);
1293     resourceLoader->startLoading();
1294     return true;
1295 }
1296
1297 bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForResponseToAuthenticationChallenge(NSURLAuthenticationChallenge* nsChallenge)
1298 {
1299 #if USE(CFNETWORK)
1300     UNUSED_PARAM(nsChallenge);
1301     // FIXME: <rdar://problem/15799844>
1302     return false;
1303 #else
1304     AuthenticationChallenge challenge(nsChallenge);
1305
1306     return player()->shouldWaitForResponseToAuthenticationChallenge(challenge);
1307 #endif
1308 }
1309
1310 void MediaPlayerPrivateAVFoundationObjC::didCancelLoadingRequest(AVAssetResourceLoadingRequest* avRequest)
1311 {
1312     String scheme = [[[avRequest request] URL] scheme];
1313
1314     WebCoreAVFResourceLoader* resourceLoader = m_resourceLoaderMap.get(avRequest);
1315
1316     if (resourceLoader)
1317         resourceLoader->stopLoading();
1318 }
1319
1320 void MediaPlayerPrivateAVFoundationObjC::didStopLoadingRequest(AVAssetResourceLoadingRequest *avRequest)
1321 {
1322     m_resourceLoaderMap.remove(avRequest);
1323 }
1324 #endif
1325
1326 bool MediaPlayerPrivateAVFoundationObjC::isAvailable()
1327 {
1328     return AVFoundationLibrary() && CoreMediaLibrary();
1329 }
1330
1331 float MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(float timeValue) const
1332 {
1333     if (!metaDataAvailable())
1334         return timeValue;
1335
1336     // FIXME - impossible to implement until rdar://8721510 is fixed.
1337     return timeValue;
1338 }
1339
1340 void MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity()
1341 {
1342     if (!m_videoLayer)
1343         return;
1344
1345     [CATransaction begin];
1346     [CATransaction setDisableActions:YES];    
1347     NSString* gravity = shouldMaintainAspectRatio() ? AVLayerVideoGravityResizeAspect : AVLayerVideoGravityResize;
1348     [m_videoLayer.get() setVideoGravity:gravity];
1349     [CATransaction commit];
1350 }
1351
1352 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
1353 {
1354     String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
1355     m_languageOfPrimaryAudioTrack = String();
1356
1357     if (!m_avAsset)
1358         return;
1359
1360     setDelayCharacteristicsChangedNotification(true);
1361
1362     bool haveCCTrack = false;
1363     bool hasCaptions = false;
1364
1365     // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
1366     // asked about those fairly fequently.
1367     if (!m_avPlayerItem) {
1368         // We don't have a player item yet, so check with the asset because some assets support inspection
1369         // prior to becoming ready to play.
1370         setHasVideo([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicVisual] count]);
1371         setHasAudio([[m_avAsset.get() tracksWithMediaCharacteristic:AVMediaCharacteristicAudible] count]);
1372 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1373         hasCaptions = [[m_avAsset.get() tracksWithMediaType:AVMediaTypeClosedCaption] count];
1374 #endif
1375     } else {
1376         bool hasVideo = false;
1377         bool hasAudio = false;
1378         for (AVPlayerItemTrack *track in m_cachedTracks.get()) {
1379             if ([track isEnabled]) {
1380                 AVAssetTrack *assetTrack = [track assetTrack];
1381                 if ([[assetTrack mediaType] isEqualToString:AVMediaTypeVideo])
1382                     hasVideo = true;
1383                 else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeAudio])
1384                     hasAudio = true;
1385                 else if ([[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption]) {
1386 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1387                     hasCaptions = true;
1388 #endif
1389                     haveCCTrack = true;
1390                 }
1391             }
1392         }
1393         setHasVideo(hasVideo);
1394         setHasAudio(hasAudio);
1395
1396
1397 #if ENABLE(VIDEO_TRACK)
1398         updateAudioTracks();
1399         updateVideoTracks();
1400 #endif
1401     }
1402
1403 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1404     if (AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia()) {
1405         hasCaptions = [[AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]] count];
1406         if (hasCaptions)
1407             processMediaSelectionOptions();
1408     }
1409 #endif
1410
1411 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT) && HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1412     if (!hasCaptions && haveCCTrack)
1413         processLegacyClosedCaptionsTracks();
1414 #elif !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1415     if (haveCCTrack)
1416         processLegacyClosedCaptionsTracks();
1417 #endif
1418
1419     setHasClosedCaptions(hasCaptions);
1420
1421     LOG(Media, "WebCoreAVFMovieObserver:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s",
1422         this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions()));
1423
1424     sizeChanged();
1425
1426     if (primaryAudioTrackLanguage != languageOfPrimaryAudioTrack())
1427         characteristicsChanged();
1428
1429     setDelayCharacteristicsChangedNotification(false);
1430 }
1431
1432 #if ENABLE(VIDEO_TRACK)
1433 template <typename RefT, typename PassRefT>
1434 void determineChangedTracksFromNewTracksAndOldItems(NSArray* tracks, NSString* trackType, Vector<RefT>& oldItems, RefT (*itemFactory)(AVPlayerItemTrack*), MediaPlayer* player, void (MediaPlayer::*removedFunction)(PassRefT), void (MediaPlayer::*addedFunction)(PassRefT))
1435 {
1436     RetainPtr<NSSet> newTracks = adoptNS([[NSSet alloc] initWithArray:[tracks objectsAtIndexes:[tracks indexesOfObjectsPassingTest:^(id track, NSUInteger, BOOL*){
1437         return [[[track assetTrack] mediaType] isEqualToString:trackType];
1438     }]]]);
1439     RetainPtr<NSMutableSet> oldTracks = adoptNS([[NSMutableSet alloc] initWithCapacity:oldItems.size()]);
1440
1441     typedef Vector<RefT> ItemVector;
1442     for (auto i = oldItems.begin(); i != oldItems.end(); ++i)
1443         [oldTracks addObject:(*i)->playerItemTrack()];
1444
1445     RetainPtr<NSMutableSet> removedTracks = adoptNS([oldTracks mutableCopy]);
1446     [removedTracks minusSet:newTracks.get()];
1447
1448     RetainPtr<NSMutableSet> addedTracks = adoptNS([newTracks mutableCopy]);
1449     [addedTracks minusSet:oldTracks.get()];
1450
1451     ItemVector replacementItems;
1452     ItemVector addedItems;
1453     ItemVector removedItems;
1454     for (auto i = oldItems.begin(); i != oldItems.end(); ++i) {
1455         if ([removedTracks containsObject:(*i)->playerItemTrack()])
1456             removedItems.append(*i);
1457         else
1458             replacementItems.append(*i);
1459     }
1460
1461     for (AVPlayerItemTrack* track in addedTracks.get())
1462         addedItems.append(itemFactory(track));
1463
1464     replacementItems.appendVector(addedItems);
1465     oldItems.swap(replacementItems);
1466
1467     for (auto i = removedItems.begin(); i != removedItems.end(); ++i)
1468         (player->*removedFunction)(*i);
1469
1470     for (auto i = addedItems.begin(); i != addedItems.end(); ++i)
1471         (player->*addedFunction)(*i);
1472 }
1473
1474 void MediaPlayerPrivateAVFoundationObjC::updateAudioTracks()
1475 {
1476     determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeAudio, m_audioTracks, &AudioTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeAudioTrack, &MediaPlayer::addAudioTrack);
1477 }
1478
1479 void MediaPlayerPrivateAVFoundationObjC::updateVideoTracks()
1480 {
1481     determineChangedTracksFromNewTracksAndOldItems(m_cachedTracks.get(), AVMediaTypeVideo, m_videoTracks, &VideoTrackPrivateAVFObjC::create, player(), &MediaPlayer::removeVideoTrack, &MediaPlayer::addVideoTrack);
1482 }
1483
1484 bool MediaPlayerPrivateAVFoundationObjC::requiresTextTrackRepresentation() const
1485 {
1486 #if PLATFORM(IOS)
1487     if (m_videoFullscreenLayer)
1488         return true;
1489 #endif
1490     return false;
1491 }
1492
1493 void MediaPlayerPrivateAVFoundationObjC::setTextTrackRepresentation(TextTrackRepresentation* representation)
1494 {
1495 #if PLATFORM(IOS)
1496     PlatformLayer* representationLayer = representation ? representation->platformLayer() : nil;
1497     if (representationLayer == m_textTrackRepresentationLayer)
1498         return;
1499
1500     if (m_textTrackRepresentationLayer)
1501         [m_textTrackRepresentationLayer removeFromSuperlayer];
1502
1503     m_textTrackRepresentationLayer = representationLayer;
1504
1505     if (m_videoFullscreenLayer && m_textTrackRepresentationLayer) {
1506         CGRect textFrame = m_videoLayer ? [m_videoLayer videoRect] : CGRectMake(0, 0, m_videoFullscreenFrame.width(), m_videoFullscreenFrame.height());
1507
1508         [m_textTrackRepresentationLayer setFrame:textFrame];
1509         [m_videoFullscreenLayer addSublayer:m_textTrackRepresentationLayer.get()];
1510     }
1511
1512 #else
1513     UNUSED_PARAM(representation);
1514 #endif
1515 }
1516 #endif // ENABLE(VIDEO_TRACK)
1517
1518 void MediaPlayerPrivateAVFoundationObjC::sizeChanged()
1519 {
1520     if (!m_avAsset)
1521         return;
1522
1523     setNaturalSize(roundedIntSize(m_cachedPresentationSize));
1524 }
1525
1526 #if PLATFORM(IOS)
1527 // FIXME: Implement for iOS in WebKit System Interface.
1528 static inline NSURL *wkAVAssetResolvedURL(AVAsset*)
1529 {
1530     return nil;
1531 }
1532 #endif
1533
1534 bool MediaPlayerPrivateAVFoundationObjC::hasSingleSecurityOrigin() const 
1535 {
1536     if (!m_avAsset)
1537         return false;
1538     
1539     RefPtr<SecurityOrigin> resolvedOrigin = SecurityOrigin::create(URL(wkAVAssetResolvedURL(m_avAsset.get())));
1540     RefPtr<SecurityOrigin> requestedOrigin = SecurityOrigin::createFromString(assetURL());
1541     return resolvedOrigin->isSameSchemeHostPort(requestedOrigin.get());
1542 }
1543
1544 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
1545 void MediaPlayerPrivateAVFoundationObjC::createVideoOutput()
1546 {
1547     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoOutput(%p)", this);
1548
1549     if (!m_avPlayerItem || m_videoOutput)
1550         return;
1551
1552 #if USE(VIDEOTOOLBOX)
1553     NSDictionary* attributes = @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_422YpCbCr8) };
1554 #else
1555     NSDictionary* attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
1556                                 nil];
1557 #endif
1558     m_videoOutput = adoptNS([[getAVPlayerItemVideoOutputClass() alloc] initWithPixelBufferAttributes:attributes]);
1559     ASSERT(m_videoOutput);
1560
1561     [m_videoOutput setDelegate:m_videoOutputDelegate.get() queue:globalPullDelegateQueue()];
1562
1563     [m_avPlayerItem.get() addOutput:m_videoOutput.get()];
1564
1565     waitForVideoOutputMediaDataWillChange();
1566
1567     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoOutput(%p) - returning %p", this, m_videoOutput.get());
1568 }
1569
1570 void MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput()
1571 {
1572     if (!m_videoOutput)
1573         return;
1574
1575     if (m_avPlayerItem)
1576         [m_avPlayerItem.get() removeOutput:m_videoOutput.get()];
1577     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::destroyVideoOutput(%p) - destroying  %p", this, m_videoOutput.get());
1578
1579     m_videoOutput = 0;
1580 }
1581
1582 RetainPtr<CVPixelBufferRef> MediaPlayerPrivateAVFoundationObjC::createPixelBuffer()
1583 {
1584     if (!m_videoOutput)
1585         createVideoOutput();
1586     ASSERT(m_videoOutput);
1587
1588 #if !LOG_DISABLED
1589     double start = monotonicallyIncreasingTime();
1590 #endif
1591
1592     CMTime currentTime = [m_avPlayerItem.get() currentTime];
1593
1594     if (![m_videoOutput.get() hasNewPixelBufferForItemTime:currentTime])
1595         return 0;
1596
1597     RetainPtr<CVPixelBufferRef> buffer = adoptCF([m_videoOutput.get() copyPixelBufferForItemTime:currentTime itemTimeForDisplay:nil]);
1598     if (!buffer)
1599         return 0;
1600
1601 #if USE(VIDEOTOOLBOX)
1602     // Create a VTPixelTransferSession, if necessary, as we cannot guarantee timely delivery of ARGB pixels.
1603     if (!m_pixelTransferSession) {
1604         VTPixelTransferSessionRef session = 0;
1605         VTPixelTransferSessionCreate(kCFAllocatorDefault, &session);
1606         m_pixelTransferSession = adoptCF(session);
1607     }
1608
1609     CVPixelBufferRef outputBuffer;
1610     CVPixelBufferCreate(kCFAllocatorDefault, CVPixelBufferGetWidth(buffer.get()), CVPixelBufferGetHeight(buffer.get()), kCVPixelFormatType_32BGRA, 0, &outputBuffer);
1611     VTPixelTransferSessionTransferImage(m_pixelTransferSession.get(), buffer.get(), outputBuffer);
1612     buffer = adoptCF(outputBuffer);
1613 #endif
1614
1615 #if !LOG_DISABLED
1616     double duration = monotonicallyIncreasingTime() - start;
1617     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createPixelBuffer(%p) - creating buffer took %.4f", this, narrowPrecisionToFloat(duration));
1618 #endif
1619
1620     return buffer;
1621 }
1622
1623 bool MediaPlayerPrivateAVFoundationObjC::videoOutputHasAvailableFrame()
1624 {
1625     if (!m_avPlayerItem)
1626         return false;
1627
1628     if (m_lastImage)
1629         return true;
1630
1631     if (!m_videoOutput)
1632         createVideoOutput();
1633
1634     return [m_videoOutput hasNewPixelBufferForItemTime:[m_avPlayerItem currentTime]];
1635 }
1636
1637 static const void* CVPixelBufferGetBytePointerCallback(void* info)
1638 {
1639     CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1640     CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
1641     return CVPixelBufferGetBaseAddress(pixelBuffer);
1642 }
1643
1644 static void CVPixelBufferReleaseBytePointerCallback(void* info, const void*)
1645 {
1646     CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1647     CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
1648 }
1649
1650 static void CVPixelBufferReleaseInfoCallback(void* info)
1651 {
1652     CVPixelBufferRef pixelBuffer = static_cast<CVPixelBufferRef>(info);
1653     CFRelease(pixelBuffer);
1654 }
1655
1656 static RetainPtr<CGImageRef> createImageFromPixelBuffer(CVPixelBufferRef pixelBuffer)
1657 {
1658     // pixelBuffer will be of type kCVPixelFormatType_32BGRA.
1659     ASSERT(CVPixelBufferGetPixelFormatType(pixelBuffer) == kCVPixelFormatType_32BGRA);
1660
1661     size_t width = CVPixelBufferGetWidth(pixelBuffer);
1662     size_t height = CVPixelBufferGetHeight(pixelBuffer);
1663     size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
1664     size_t byteLength = CVPixelBufferGetDataSize(pixelBuffer);
1665     CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little | kCGImageAlphaFirst;
1666
1667     CFRetain(pixelBuffer); // Balanced by CVPixelBufferReleaseInfoCallback in providerCallbacks.
1668     CGDataProviderDirectCallbacks providerCallbacks = { 0, CVPixelBufferGetBytePointerCallback, CVPixelBufferReleaseBytePointerCallback, 0, CVPixelBufferReleaseInfoCallback };
1669     RetainPtr<CGDataProviderRef> provider = adoptCF(CGDataProviderCreateDirect(pixelBuffer, byteLength, &providerCallbacks));
1670
1671     return adoptCF(CGImageCreate(width, height, 8, 32, bytesPerRow, deviceRGBColorSpaceRef(), bitmapInfo, provider.get(), NULL, false, kCGRenderingIntentDefault));
1672 }
1673
1674 void MediaPlayerPrivateAVFoundationObjC::updateLastImage()
1675 {
1676     RetainPtr<CVPixelBufferRef> pixelBuffer = createPixelBuffer();
1677
1678     // Calls to copyPixelBufferForItemTime:itemTimeForDisplay: may return nil if the pixel buffer
1679     // for the requested time has already been retrieved. In this case, the last valid image (if any)
1680     // should be displayed.
1681     if (pixelBuffer)
1682         m_lastImage = createImageFromPixelBuffer(pixelBuffer.get());
1683 }
1684
1685 void MediaPlayerPrivateAVFoundationObjC::paintWithVideoOutput(GraphicsContext* context, const IntRect& outputRect)
1686 {
1687     updateLastImage();
1688
1689     if (m_lastImage) {
1690         GraphicsContextStateSaver stateSaver(*context);
1691
1692         IntRect imageRect(0, 0, CGImageGetWidth(m_lastImage.get()), CGImageGetHeight(m_lastImage.get()));
1693
1694         context->drawNativeImage(m_lastImage.get(), imageRect.size(), ColorSpaceDeviceRGB, outputRect, imageRect);
1695
1696         // If we have created an AVAssetImageGenerator in the past due to m_videoOutput not having an available
1697         // video frame, destroy it now that it is no longer needed.
1698         if (m_imageGenerator)
1699             destroyImageGenerator();
1700     }
1701 }
1702
1703 PassNativeImagePtr MediaPlayerPrivateAVFoundationObjC::nativeImageForCurrentTime()
1704 {
1705     updateLastImage();
1706     return m_lastImage.get();
1707 }
1708
1709 void MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange()
1710 {
1711     if (!m_videoOutputSemaphore)
1712         m_videoOutputSemaphore = dispatch_semaphore_create(0);
1713
1714     [m_videoOutput requestNotificationOfMediaDataChangeWithAdvanceInterval:0];
1715
1716     // Wait for 1 second.
1717     long result = dispatch_semaphore_wait(m_videoOutputSemaphore, dispatch_time(0, 1 * NSEC_PER_SEC));
1718
1719     if (result)
1720         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::waitForVideoOutputMediaDataWillChange(%p) timed out", this);
1721 }
1722
1723 void MediaPlayerPrivateAVFoundationObjC::outputMediaDataWillChange(AVPlayerItemVideoOutput*)
1724 {
1725     dispatch_semaphore_signal(m_videoOutputSemaphore);
1726 }
1727 #endif
1728
1729 #if ENABLE(ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA_V2)
1730 bool MediaPlayerPrivateAVFoundationObjC::extractKeyURIKeyIDAndCertificateFromInitData(Uint8Array* initData, String& keyURI, String& keyID, RefPtr<Uint8Array>& certificate)
1731 {
1732     // initData should have the following layout:
1733     // [4 bytes: keyURI length][N bytes: keyURI][4 bytes: contentID length], [N bytes: contentID], [4 bytes: certificate length][N bytes: certificate]
1734     if (initData->byteLength() < 4)
1735         return false;
1736
1737     RefPtr<ArrayBuffer> initDataBuffer = initData->buffer();
1738
1739     // Use a DataView to read uint32 values from the buffer, as Uint32Array requires the reads be aligned on 4-byte boundaries. 
1740     RefPtr<JSC::DataView> initDataView = JSC::DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
1741     uint32_t offset = 0;
1742     bool status = true;
1743
1744     uint32_t keyURILength = initDataView->get<uint32_t>(offset, true, &status);
1745     offset += 4;
1746     if (!status || offset + keyURILength > initData->length())
1747         return false;
1748
1749     RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, offset, keyURILength);
1750     if (!keyURIArray)
1751         return false;
1752
1753     keyURI = String(keyURIArray->data(), keyURILength / sizeof(unsigned short));
1754     offset += keyURILength;
1755
1756     uint32_t keyIDLength = initDataView->get<uint32_t>(offset, true, &status);
1757     offset += 4;
1758     if (!status || offset + keyIDLength > initData->length())
1759         return false;
1760
1761     RefPtr<Uint16Array> keyIDArray = Uint16Array::create(initDataBuffer, offset, keyIDLength);
1762     if (!keyIDArray)
1763         return false;
1764
1765     keyID = String(keyIDArray->data(), keyIDLength / sizeof(unsigned short));
1766     offset += keyIDLength;
1767
1768     uint32_t certificateLength = initDataView->get<uint32_t>(offset, true, &status);
1769     offset += 4;
1770     if (!status || offset + certificateLength > initData->length())
1771         return false;
1772
1773     certificate = Uint8Array::create(initDataBuffer, offset, certificateLength);
1774     if (!certificate)
1775         return false;
1776
1777     return true;
1778 }
1779 #endif
1780
1781 #if ENABLE(ENCRYPTED_MEDIA)
1782 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::generateKeyRequest(const String& keySystem, const unsigned char* initDataPtr, unsigned initDataLength)
1783 {
1784     if (!keySystemIsSupported(keySystem))
1785         return MediaPlayer::KeySystemNotSupported;
1786
1787     RefPtr<Uint8Array> initData = Uint8Array::create(initDataPtr, initDataLength);
1788     String keyURI;
1789     String keyID;
1790     RefPtr<Uint8Array> certificate;
1791     if (!extractKeyURIKeyIDAndCertificateFromInitData(initData.get(), keyURI, keyID, certificate))
1792         return MediaPlayer::InvalidPlayerState;
1793
1794     if (!m_keyURIToRequestMap.contains(keyURI))
1795         return MediaPlayer::InvalidPlayerState;
1796
1797     String sessionID = createCanonicalUUIDString();
1798
1799     RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_keyURIToRequestMap.get(keyURI);
1800
1801     RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:certificate->baseAddress() length:certificate->byteLength()]);
1802     NSString* assetStr = keyID;
1803     RetainPtr<NSData> assetID = [NSData dataWithBytes: [assetStr cStringUsingEncoding:NSUTF8StringEncoding] length:[assetStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
1804     NSError* error = 0;
1805     RetainPtr<NSData> keyRequest = [avRequest.get() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:assetID.get() options:nil error:&error];
1806
1807     if (!keyRequest) {
1808         NSError* underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
1809         player()->keyError(keySystem, sessionID, MediaPlayerClient::DomainError, [underlyingError code]);
1810         return MediaPlayer::NoError;
1811     }
1812
1813     RefPtr<ArrayBuffer> keyRequestBuffer = ArrayBuffer::create([keyRequest.get() bytes], [keyRequest.get() length]);
1814     RefPtr<Uint8Array> keyRequestArray = Uint8Array::create(keyRequestBuffer, 0, keyRequestBuffer->byteLength());
1815     player()->keyMessage(keySystem, sessionID, keyRequestArray->data(), keyRequestArray->byteLength(), URL());
1816
1817     // Move ownership of the AVAssetResourceLoadingRequestfrom the keyIDToRequestMap to the sessionIDToRequestMap:
1818     m_sessionIDToRequestMap.set(sessionID, avRequest);
1819     m_keyURIToRequestMap.remove(keyURI);
1820
1821     return MediaPlayer::NoError;
1822 }
1823
1824 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::addKey(const String& keySystem, const unsigned char* keyPtr, unsigned keyLength, const unsigned char* initDataPtr, unsigned initDataLength, const String& sessionID)
1825 {
1826     if (!keySystemIsSupported(keySystem))
1827         return MediaPlayer::KeySystemNotSupported;
1828
1829     if (!m_sessionIDToRequestMap.contains(sessionID))
1830         return MediaPlayer::InvalidPlayerState;
1831
1832     RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_sessionIDToRequestMap.get(sessionID);
1833     RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:keyPtr length:keyLength]);
1834     [[avRequest.get() dataRequest] respondWithData:keyData.get()];
1835     [avRequest.get() finishLoading];
1836     m_sessionIDToRequestMap.remove(sessionID);
1837
1838     player()->keyAdded(keySystem, sessionID);
1839
1840     UNUSED_PARAM(initDataPtr);
1841     UNUSED_PARAM(initDataLength);
1842     return MediaPlayer::NoError;
1843 }
1844
1845 MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::cancelKeyRequest(const String& keySystem, const String& sessionID)
1846 {
1847     if (!keySystemIsSupported(keySystem))
1848         return MediaPlayer::KeySystemNotSupported;
1849
1850     if (!m_sessionIDToRequestMap.contains(sessionID))
1851         return MediaPlayer::InvalidPlayerState;
1852
1853     m_sessionIDToRequestMap.remove(sessionID);
1854     return MediaPlayer::NoError;
1855 }
1856 #endif
1857
1858 #if ENABLE(ENCRYPTED_MEDIA_V2)
1859 RetainPtr<AVAssetResourceLoadingRequest> MediaPlayerPrivateAVFoundationObjC::takeRequestForKeyURI(const String& keyURI)
1860 {
1861     return m_keyURIToRequestMap.take(keyURI);
1862 }
1863
1864 std::unique_ptr<CDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem)
1865 {
1866     if (!keySystemIsSupported(keySystem))
1867         return nullptr;
1868
1869     return std::make_unique<CDMSessionAVFoundationObjC>(this);
1870 }
1871 #endif
1872
1873 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1874 void MediaPlayerPrivateAVFoundationObjC::processLegacyClosedCaptionsTracks()
1875 {
1876 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1877     [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
1878 #endif
1879
1880     Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
1881     for (AVPlayerItemTrack *playerItemTrack in m_cachedTracks.get()) {
1882
1883         AVAssetTrack *assetTrack = [playerItemTrack assetTrack];
1884         if (![[assetTrack mediaType] isEqualToString:AVMediaTypeClosedCaption])
1885             continue;
1886
1887         bool newCCTrack = true;
1888         for (unsigned i = removedTextTracks.size(); i > 0; --i) {
1889             if (removedTextTracks[i - 1]->textTrackCategory() != InbandTextTrackPrivateAVF::LegacyClosedCaption)
1890                 continue;
1891
1892             RefPtr<InbandTextTrackPrivateLegacyAVFObjC> track = static_cast<InbandTextTrackPrivateLegacyAVFObjC*>(m_textTracks[i - 1].get());
1893             if (track->avPlayerItemTrack() == playerItemTrack) {
1894                 removedTextTracks.remove(i - 1);
1895                 newCCTrack = false;
1896                 break;
1897             }
1898         }
1899
1900         if (!newCCTrack)
1901             continue;
1902         
1903         m_textTracks.append(InbandTextTrackPrivateLegacyAVFObjC::create(this, playerItemTrack));
1904     }
1905
1906     processNewAndRemovedTextTracks(removedTextTracks);
1907 }
1908 #endif
1909
1910 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1911 AVMediaSelectionGroupType* MediaPlayerPrivateAVFoundationObjC::safeMediaSelectionGroupForLegibleMedia()
1912 {
1913     if (!m_avAsset)
1914         return nil;
1915     
1916     if ([m_avAsset.get() statusOfValueForKey:@"availableMediaCharacteristicsWithMediaSelectionOptions" error:NULL] != AVKeyValueStatusLoaded)
1917         return nil;
1918     
1919     return [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicLegible];
1920 }
1921
1922 void MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions()
1923 {
1924     AVMediaSelectionGroupType *legibleGroup = safeMediaSelectionGroupForLegibleMedia();
1925     if (!legibleGroup) {
1926         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::processMediaSelectionOptions(%p) - nil mediaSelectionGroup", this);
1927         return;
1928     }
1929
1930     // We enabled automatic media selection because we want alternate audio tracks to be enabled/disabled automatically,
1931     // but set the selected legible track to nil so text tracks will not be automatically configured.
1932     if (!m_textTracks.size())
1933         [m_avPlayerItem.get() selectMediaOption:nil inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
1934
1935     Vector<RefPtr<InbandTextTrackPrivateAVF>> removedTextTracks = m_textTracks;
1936     NSArray *legibleOptions = [AVMediaSelectionGroup playableMediaSelectionOptionsFromArray:[legibleGroup options]];
1937     for (AVMediaSelectionOptionType *option in legibleOptions) {
1938         bool newTrack = true;
1939         for (unsigned i = removedTextTracks.size(); i > 0; --i) {
1940             if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
1941                 continue;
1942             
1943             RetainPtr<AVMediaSelectionOptionType> currentOption;
1944 #if ENABLE(AVF_CAPTIONS)
1945             if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand) {
1946                 RefPtr<OutOfBandTextTrackPrivateAVF> track = static_cast<OutOfBandTextTrackPrivateAVF*>(removedTextTracks[i - 1].get());
1947                 currentOption = track->mediaSelectionOption();
1948             } else
1949 #endif
1950             {
1951                 RefPtr<InbandTextTrackPrivateAVFObjC> track = static_cast<InbandTextTrackPrivateAVFObjC*>(removedTextTracks[i - 1].get());
1952                 currentOption = track->mediaSelectionOption();
1953             }
1954             
1955             if ([currentOption.get() isEqual:option]) {
1956                 removedTextTracks.remove(i - 1);
1957                 newTrack = false;
1958                 break;
1959             }
1960         }
1961         if (!newTrack)
1962             continue;
1963
1964 #if ENABLE(AVF_CAPTIONS)
1965         if ([option outOfBandSource]) {
1966             m_textTracks.append(OutOfBandTextTrackPrivateAVF::create(this, option));
1967             m_textTracks.last()->setHasBeenReported(true); // Ignore out-of-band tracks that we passed to AVFoundation so we do not double-count them
1968             continue;
1969         }
1970 #endif
1971
1972         m_textTracks.append(InbandTextTrackPrivateAVFObjC::create(this, option));
1973     }
1974
1975     processNewAndRemovedTextTracks(removedTextTracks);
1976 }
1977
1978 void MediaPlayerPrivateAVFoundationObjC::processCue(NSArray *attributedStrings, double time)
1979 {
1980     if (!m_currentTrack)
1981         return;
1982
1983     m_currentTrack->processCue(reinterpret_cast<CFArrayRef>(attributedStrings), time);
1984 }
1985
1986 void MediaPlayerPrivateAVFoundationObjC::flushCues()
1987 {
1988     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::flushCues(%p)", this);
1989
1990     if (!m_currentTrack)
1991         return;
1992     
1993     m_currentTrack->resetCueValues();
1994 }
1995 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1996
1997 void MediaPlayerPrivateAVFoundationObjC::setCurrentTrack(InbandTextTrackPrivateAVF *track)
1998 {
1999     if (m_currentTrack == track)
2000         return;
2001
2002     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setCurrentTrack(%p) - selecting track %p, language = %s", this, track, track ? track->language().string().utf8().data() : "");
2003         
2004     m_currentTrack = track;
2005
2006     if (track) {
2007         if (track->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
2008             [m_avPlayer.get() setClosedCaptionDisplayEnabled:YES];
2009 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2010 #if ENABLE(AVF_CAPTIONS)
2011         else if (track->textTrackCategory() == InbandTextTrackPrivateAVF::OutOfBand)
2012             [m_avPlayerItem.get() selectMediaOption:static_cast<OutOfBandTextTrackPrivateAVF*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2013 #endif
2014         else
2015             [m_avPlayerItem.get() selectMediaOption:static_cast<InbandTextTrackPrivateAVFObjC*>(track)->mediaSelectionOption() inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2016 #endif
2017     } else {
2018 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2019         [m_avPlayerItem.get() selectMediaOption:0 inMediaSelectionGroup:safeMediaSelectionGroupForLegibleMedia()];
2020 #endif
2021         [m_avPlayer.get() setClosedCaptionDisplayEnabled:NO];
2022     }
2023
2024 }
2025
2026 String MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack() const
2027 {
2028     if (!m_languageOfPrimaryAudioTrack.isNull())
2029         return m_languageOfPrimaryAudioTrack;
2030
2031     if (!m_avPlayerItem.get())
2032         return emptyString();
2033
2034 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2035     // If AVFoundation has an audible group, return the language of the currently selected audible option.
2036     AVMediaSelectionGroupType *audibleGroup = [m_avAsset.get() mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
2037     AVMediaSelectionOptionType *currentlySelectedAudibleOption = [m_avPlayerItem.get() selectedMediaOptionInMediaSelectionGroup:audibleGroup];
2038     if (currentlySelectedAudibleOption) {
2039         m_languageOfPrimaryAudioTrack = [[currentlySelectedAudibleOption locale] localeIdentifier];
2040         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of selected audible option: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
2041
2042         return m_languageOfPrimaryAudioTrack;
2043     }
2044 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2045
2046     // AVFoundation synthesizes an audible group when there is only one ungrouped audio track if there is also a legible group (one or
2047     // more in-band text tracks). It doesn't know about out-of-band tracks, so if there is a single audio track return its language.
2048     NSArray *tracks = [m_avAsset.get() tracksWithMediaType:AVMediaTypeAudio];
2049     if (!tracks || [tracks count] != 1) {
2050         m_languageOfPrimaryAudioTrack = emptyString();
2051         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - %lu audio tracks, returning emptyString()", this, static_cast<unsigned long>(tracks ? [tracks count] : 0));
2052         return m_languageOfPrimaryAudioTrack;
2053     }
2054
2055     AVAssetTrack *track = [tracks objectAtIndex:0];
2056     m_languageOfPrimaryAudioTrack = AVTrackPrivateAVFObjCImpl::languageForAVAssetTrack(track);
2057
2058 #if !LOG_DISABLED
2059     if (m_languageOfPrimaryAudioTrack == emptyString())
2060         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - single audio track has no language, returning emptyString()", this);
2061     else
2062         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::languageOfPrimaryAudioTrack(%p) - returning language of single audio track: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
2063 #endif
2064
2065     return m_languageOfPrimaryAudioTrack;
2066 }
2067
2068 #if ENABLE(IOS_AIRPLAY)
2069 bool MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless() const
2070 {
2071     if (!m_avPlayer)
2072         return false;
2073
2074     bool wirelessTarget = [m_avPlayer.get() isExternalPlaybackActive];
2075     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::isCurrentPlaybackTargetWireless(%p) - returning %s", this, boolString(wirelessTarget));
2076     return wirelessTarget;
2077 }
2078
2079 MediaPlayer::WirelessPlaybackTargetType MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetType() const
2080 {
2081     if (!m_avPlayer)
2082         return MediaPlayer::TargetTypeNone;
2083
2084     switch (wkExernalDeviceTypeForPlayer(m_avPlayer.get())) {
2085     case wkExternalPlaybackTypeNone:
2086         return MediaPlayer::TargetTypeNone;
2087     case wkExternalPlaybackTypeAirPlay:
2088         return MediaPlayer::TargetTypeAirPlay;
2089     case wkExternalPlaybackTypeTVOut:
2090         return MediaPlayer::TargetTypeTVOut;
2091     }
2092
2093     ASSERT_NOT_REACHED();
2094     return MediaPlayer::TargetTypeNone;
2095 }
2096
2097 String MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName() const
2098 {
2099     if (!m_avPlayer)
2100         return emptyString();
2101     
2102     String wirelessTargetName = wkExernalDeviceDisplayNameForPlayer(m_avPlayer.get());
2103     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::wirelessPlaybackTargetName(%p) - returning %s", this, wirelessTargetName.utf8().data());
2104
2105     return wirelessTargetName;
2106 }
2107
2108 bool MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled() const
2109 {
2110     if (!m_avPlayer)
2111         return !m_allowsWirelessVideoPlayback;
2112     
2113     m_allowsWirelessVideoPlayback = [m_avPlayer.get() allowsExternalPlayback];
2114     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::wirelessVideoPlaybackDisabled(%p) - returning %s", this, boolString(!m_allowsWirelessVideoPlayback));
2115
2116     return !m_allowsWirelessVideoPlayback;
2117 }
2118
2119 void MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(bool disabled)
2120 {
2121     LOG(Media, "MediaPlayerPrivateAVFoundationObjC::setWirelessVideoPlaybackDisabled(%p) - %s", this, boolString(disabled));
2122     m_allowsWirelessVideoPlayback = !disabled;
2123     if (!m_avPlayer)
2124         return;
2125     
2126     [m_avPlayer.get() setAllowsExternalPlayback:!disabled];
2127 }
2128 #endif
2129
2130 void MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange(int status)
2131 {
2132     m_cachedItemStatus = status;
2133
2134     updateStates();
2135 }
2136
2137 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange()
2138 {
2139     m_pendingStatusChanges++;
2140 }
2141
2142 void MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange(bool likelyToKeepUp)
2143 {
2144     m_cachedLikelyToKeepUp = likelyToKeepUp;
2145
2146     ASSERT(m_pendingStatusChanges);
2147     if (!--m_pendingStatusChanges)
2148         updateStates();
2149 }
2150
2151 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange()
2152 {
2153     m_pendingStatusChanges++;
2154 }
2155
2156 void MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange(bool bufferEmpty)
2157 {
2158     m_cachedBufferEmpty = bufferEmpty;
2159
2160     ASSERT(m_pendingStatusChanges);
2161     if (!--m_pendingStatusChanges)
2162         updateStates();
2163 }
2164
2165 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange()
2166 {
2167     m_pendingStatusChanges++;
2168 }
2169
2170 void MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange(bool bufferFull)
2171 {
2172     m_cachedBufferFull = bufferFull;
2173
2174     ASSERT(m_pendingStatusChanges);
2175     if (!--m_pendingStatusChanges)
2176         updateStates();
2177 }
2178
2179 void MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange(RetainPtr<NSArray> seekableRanges)
2180 {
2181     m_cachedSeekableRanges = seekableRanges;
2182
2183     seekableTimeRangesChanged();
2184     updateStates();
2185 }
2186
2187 void MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange(RetainPtr<NSArray> loadedRanges)
2188 {
2189     m_cachedLoadedRanges = loadedRanges;
2190
2191     loadedTimeRangesChanged();
2192     updateStates();
2193 }
2194
2195 void MediaPlayerPrivateAVFoundationObjC::tracksDidChange(RetainPtr<NSArray> tracks)
2196 {
2197     m_cachedTracks = tracks;
2198
2199     tracksChanged();
2200     updateStates();
2201 }
2202
2203 void MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange(bool hasEnabledAudio)
2204 {
2205     m_cachedHasEnabledAudio = hasEnabledAudio;
2206
2207     tracksChanged();
2208     updateStates();
2209 }
2210
2211 void MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange(FloatSize size)
2212 {
2213     m_cachedPresentationSize = size;
2214
2215     sizeChanged();
2216     updateStates();
2217 }
2218
2219 void MediaPlayerPrivateAVFoundationObjC::durationDidChange(double duration)
2220 {
2221     m_cachedDuration = duration;
2222
2223     invalidateCachedDuration();
2224 }
2225
2226 void MediaPlayerPrivateAVFoundationObjC::rateDidChange(double rate)
2227 {
2228     m_cachedRate = rate;
2229
2230     updateStates();
2231     rateChanged();
2232 }
2233     
2234 #if ENABLE(IOS_AIRPLAY)
2235 void MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange()
2236 {
2237     playbackTargetIsWirelessChanged();
2238 }
2239 #endif
2240
2241 NSArray* assetMetadataKeyNames()
2242 {
2243     static NSArray* keys;
2244     if (!keys) {
2245         keys = [[NSArray alloc] initWithObjects:@"duration",
2246                     @"naturalSize",
2247                     @"preferredTransform",
2248                     @"preferredVolume",
2249                     @"preferredRate",
2250                     @"playable",
2251                     @"tracks",
2252                     @"availableMediaCharacteristicsWithMediaSelectionOptions",
2253                    nil];
2254     }
2255     return keys;
2256 }
2257
2258 NSArray* itemKVOProperties()
2259 {
2260     static NSArray* keys;
2261     if (!keys) {
2262         keys = [[NSArray alloc] initWithObjects:@"presentationSize",
2263                 @"status",
2264                 @"asset",
2265                 @"tracks",
2266                 @"seekableTimeRanges",
2267                 @"loadedTimeRanges",
2268                 @"playbackLikelyToKeepUp",
2269                 @"playbackBufferFull",
2270                 @"playbackBufferEmpty",
2271                 @"duration",
2272                 @"hasEnabledAudio",
2273                 nil];
2274     }
2275     return keys;
2276 }
2277
2278 } // namespace WebCore
2279
2280 @implementation WebCoreAVFMovieObserver
2281
2282 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2283 {
2284     self = [super init];
2285     if (!self)
2286         return nil;
2287     m_callback = callback;
2288     return self;
2289 }
2290
2291 - (void)disconnect
2292 {
2293     [NSObject cancelPreviousPerformRequestsWithTarget:self];
2294     m_callback = 0;
2295 }
2296
2297 - (void)metadataLoaded
2298 {
2299     if (!m_callback)
2300         return;
2301
2302     m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
2303 }
2304
2305 - (void)didEnd:(NSNotification *)unusedNotification
2306 {
2307     UNUSED_PARAM(unusedNotification);
2308     if (!m_callback)
2309         return;
2310     m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
2311 }
2312
2313 - (void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context
2314 {
2315     UNUSED_PARAM(object);
2316     id newValue = [change valueForKey:NSKeyValueChangeNewKey];
2317
2318     LOG(Media, "WebCoreAVFMovieObserver::observeValueForKeyPath(%p) - keyPath = %s", self, [keyPath UTF8String]);
2319
2320     if (!m_callback)
2321         return;
2322
2323     bool willChange = [[change valueForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];
2324
2325     WTF::Function<void ()> function;
2326
2327     if (context == MediaPlayerAVFoundationObservationContextPlayerItem && willChange) {
2328         if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
2329             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpWillChange, m_callback);
2330         else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
2331             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyWillChange, m_callback);
2332         else if ([keyPath isEqualToString:@"playbackBufferFull"])
2333             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullWillChange, m_callback);
2334     }
2335
2336     if (context == MediaPlayerAVFoundationObservationContextPlayerItem && !willChange) {
2337         // A value changed for an AVPlayerItem
2338         if ([keyPath isEqualToString:@"status"])
2339             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playerItemStatusDidChange, m_callback, [newValue intValue]);
2340         else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"])
2341             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackLikelyToKeepUpDidChange, m_callback, [newValue boolValue]);
2342         else if ([keyPath isEqualToString:@"playbackBufferEmpty"])
2343             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferEmptyDidChange, m_callback, [newValue boolValue]);
2344         else if ([keyPath isEqualToString:@"playbackBufferFull"])
2345             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackBufferFullDidChange, m_callback, [newValue boolValue]);
2346         else if ([keyPath isEqualToString:@"asset"])
2347             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::setAsset, m_callback, RetainPtr<NSArray>(newValue));
2348         else if ([keyPath isEqualToString:@"loadedTimeRanges"])
2349             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::loadedTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
2350         else if ([keyPath isEqualToString:@"seekableTimeRanges"])
2351             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::seekableTimeRangesDidChange, m_callback, RetainPtr<NSArray>(newValue));
2352         else if ([keyPath isEqualToString:@"tracks"])
2353             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::tracksDidChange, m_callback, RetainPtr<NSArray>(newValue));
2354         else if ([keyPath isEqualToString:@"hasEnabledAudio"])
2355             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::hasEnabledAudioDidChange, m_callback, [newValue boolValue]);
2356         else if ([keyPath isEqualToString:@"presentationSize"])
2357             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::presentationSizeDidChange, m_callback, FloatSize([newValue sizeValue]));
2358         else if ([keyPath isEqualToString:@"duration"])
2359             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::durationDidChange, m_callback, CMTimeGetSeconds([newValue CMTimeValue]));
2360     }
2361
2362     if (context == MediaPlayerAVFoundationObservationContextPlayer && !willChange) {
2363         // A value changed for an AVPlayer.
2364         if ([keyPath isEqualToString:@"rate"])
2365             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::rateDidChange, m_callback, [newValue doubleValue]);
2366 #if ENABLE(IOS_AIRPLAY)
2367         else if ([keyPath isEqualToString:@"externalPlaybackActive"])
2368             function = WTF::bind(&MediaPlayerPrivateAVFoundationObjC::playbackTargetIsWirelessDidChange, m_callback);
2369 #endif
2370     }
2371     
2372     if (function.isNull())
2373         return;
2374
2375     auto weakThis = m_callback->createWeakPtr();
2376     m_callback->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification([weakThis, function]{
2377         // weakThis and function both refer to the same MediaPlayerPrivateAVFoundationObjC instance. If the WeakPtr has
2378         // been cleared, the underlying object has been destroyed, and it is unsafe to call function().
2379         if (!weakThis)
2380             return;
2381         function();
2382     }));
2383 }
2384
2385 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
2386 - (void)legibleOutput:(id)output didOutputAttributedStrings:(NSArray *)strings nativeSampleBuffers:(NSArray *)nativeSamples forItemTime:(CMTime)itemTime
2387 {
2388     UNUSED_PARAM(output);
2389     UNUSED_PARAM(nativeSamples);
2390
2391     if (!m_callback)
2392         return;
2393
2394     RetainPtr<WebCoreAVFMovieObserver> strongSelf = self;
2395     RetainPtr<NSArray> strongStrings = strings;
2396     callOnMainThread([strongSelf, strongStrings, itemTime] {
2397         MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2398         if (!callback)
2399             return;
2400         callback->processCue(strongStrings.get(), CMTimeGetSeconds(itemTime));
2401     });
2402 }
2403
2404 - (void)outputSequenceWasFlushed:(id)output
2405 {
2406     UNUSED_PARAM(output);
2407
2408     if (!m_callback)
2409         return;
2410     
2411     RetainPtr<WebCoreAVFMovieObserver> strongSelf = self;
2412     callOnMainThread([strongSelf] {
2413         if (MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback)
2414             callback->flushCues();
2415     });
2416 }
2417 #endif
2418
2419 @end
2420
2421 #if HAVE(AVFOUNDATION_LOADER_DELEGATE)
2422 @implementation WebCoreAVFLoaderDelegate
2423
2424 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2425 {
2426     self = [super init];
2427     if (!self)
2428         return nil;
2429     m_callback = callback;
2430     return self;
2431 }
2432
2433 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
2434 {
2435     UNUSED_PARAM(resourceLoader);
2436     if (!m_callback)
2437         return NO;
2438
2439     RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2440     RetainPtr<AVAssetResourceLoadingRequest> strongRequest = loadingRequest;
2441     callOnMainThread([strongSelf, strongRequest] {
2442         MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2443         if (!callback) {
2444             [strongRequest finishLoadingWithError:nil];
2445             return;
2446         }
2447
2448         if (!callback->shouldWaitForLoadingOfResource(strongRequest.get()))
2449             [strongRequest finishLoadingWithError:nil];
2450     });
2451
2452     return YES;
2453 }
2454
2455 - (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForResponseToAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
2456 {
2457     UNUSED_PARAM(resourceLoader);
2458     if (!m_callback)
2459         return NO;
2460
2461     if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
2462         return NO;
2463
2464     RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2465     RetainPtr<NSURLAuthenticationChallenge> strongChallenge = challenge;
2466     callOnMainThread([strongSelf, strongChallenge] {
2467         MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2468         if (!callback) {
2469             [[strongChallenge sender] cancelAuthenticationChallenge:strongChallenge.get()];
2470             return;
2471         }
2472
2473         if (!callback->shouldWaitForResponseToAuthenticationChallenge(strongChallenge.get()))
2474             [[strongChallenge sender] cancelAuthenticationChallenge:strongChallenge.get()];
2475     });
2476
2477     return YES;
2478 }
2479
2480 - (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
2481 {
2482     UNUSED_PARAM(resourceLoader);
2483     if (!m_callback)
2484         return;
2485
2486     RetainPtr<WebCoreAVFLoaderDelegate> strongSelf = self;
2487     RetainPtr<AVAssetResourceLoadingRequest> strongRequest = loadingRequest;
2488     callOnMainThread([strongSelf, strongRequest] {
2489         MediaPlayerPrivateAVFoundationObjC* callback = strongSelf->m_callback;
2490         if (callback)
2491             callback->didCancelLoadingRequest(strongRequest.get());
2492     });
2493 }
2494
2495 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
2496 {
2497     m_callback = callback;
2498 }
2499 @end
2500 #endif
2501
2502 #if HAVE(AVFOUNDATION_VIDEO_OUTPUT)
2503 @implementation WebCoreAVFPullDelegate
2504 - (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC *)callback
2505 {
2506     self = [super init];
2507     if (self)
2508         m_callback = callback;
2509     return self;
2510 }
2511
2512 - (void)setCallback:(MediaPlayerPrivateAVFoundationObjC *)callback
2513 {
2514     m_callback = callback;
2515 }
2516
2517 - (void)outputMediaDataWillChange:(AVPlayerItemVideoOutput *)output
2518 {
2519     if (m_callback)
2520         m_callback->outputMediaDataWillChange(output);
2521 }
2522
2523 - (void)outputSequenceWasFlushed:(AVPlayerItemVideoOutput *)output
2524 {
2525     UNUSED_PARAM(output);
2526     // No-op.
2527 }
2528 @end
2529 #endif
2530
2531 #endif