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