[Mac] process raw VTT in-band captions
[WebKit-https.git] / Source / WebCore / platform / graphics / avfoundation / cf / MediaPlayerPrivateAVFoundationCF.cpp
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 #include "config.h"
27
28 #if PLATFORM(WIN) && ENABLE(VIDEO) 
29
30 #if USE(AVFOUNDATION)
31
32 #include "MediaPlayerPrivateAVFoundationCF.h"
33
34 #include "ApplicationCacheResource.h"
35 #include "COMPtr.h"
36 #include "FloatConversion.h"
37 #include "FrameView.h"
38 #include "GraphicsContext.h"
39 #if HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
40 #include "InbandTextTrackPrivateAVCF.h"
41 #else
42 #include "InbandTextTrackPrivateLegacyAVCF.h"
43 #endif
44 #include "URL.h"
45 #include "Logging.h"
46 #include "PlatformCALayerWin.h"
47 #include "SoftLinking.h"
48 #include "TimeRanges.h"
49
50 #include <AVFoundationCF/AVCFPlayerItem.h>
51 #if HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
52 #include <AVFoundationCF/AVCFPlayerItemLegibleOutput.h>
53 #endif
54 #include <AVFoundationCF/AVCFPlayerLayer.h>
55 #include <AVFoundationCF/AVFoundationCF.h>
56 #include <CoreMedia/CoreMedia.h>
57 #include <d3d9.h>
58 #include <delayimp.h>
59 #include <dispatch/dispatch.h>
60 #include <wtf/HashMap.h>
61 #include <wtf/Threading.h>
62 #include <wtf/text/CString.h>
63
64 // The softlink header files must be included after the AVCF and CoreMedia header files.
65 #include "AVFoundationCFSoftLinking.h"
66 #include "CoreMediaSoftLinking.h"
67
68 // We don't bother softlinking against libdispatch since it's already been loaded by AAS.
69 #ifdef DEBUG_ALL
70 #pragma comment(lib, "libdispatch_debug.lib")
71 #else
72 #pragma comment(lib, "libdispatch.lib")
73 #endif
74
75 using namespace std;
76
77 namespace WebCore {
78
79 class LayerClient;
80
81 class AVFWrapper {
82 public:
83     AVFWrapper(MediaPlayerPrivateAVFoundationCF*);
84     ~AVFWrapper();
85
86     void scheduleDisconnectAndDelete();
87
88     void createAVCFVideoLayer();
89     void destroyVideoLayer();
90     PlatformLayer* platformLayer();
91
92     CACFLayerRef caVideoLayer() { return m_caVideoLayer.get(); }
93     PlatformLayer* videoLayerWrapper() { return m_videoLayerWrapper ? m_videoLayerWrapper->platformLayer() : 0; };
94     void setVideoLayerNeedsCommit();
95     void setVideoLayerHidden(bool);
96
97     void createImageGenerator();
98     void destroyImageGenerator();
99     RetainPtr<CGImageRef> createImageForTimeInRect(float, const IntRect&);
100
101     void createAssetForURL(const String& url, bool inheritURI);
102     void setAsset(AVCFURLAssetRef);
103     
104     void createPlayer(IDirect3DDevice9*);
105     void createPlayerItem();
106     
107     void checkPlayability();
108     void beginLoadingMetadata();
109     
110     void seekToTime(double, double, double);
111     void updateVideoLayerGravity();
112
113     void setCurrentTextTrack(InbandTextTrackPrivateAVF*);
114     InbandTextTrackPrivateAVF* currentTextTrack() const { return m_currentTextTrack; }
115
116 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
117     static void legibleOutputCallback(void* context, AVCFPlayerItemLegibleOutputRef, CFArrayRef attributedString, CFArrayRef nativeSampleBuffers, CMTime itemTime);
118     static void processCue(void* context);
119 #endif
120     static void loadMetadataCompletionCallback(AVCFAssetRef, void*);
121     static void loadPlayableCompletionCallback(AVCFAssetRef, void*);
122     static void periodicTimeObserverCallback(AVCFPlayerRef, CMTime, void*);
123     static void seekCompletedCallback(AVCFPlayerItemRef, Boolean, void*);
124     static void notificationCallback(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef);
125     static void processNotification(void* context);
126
127     inline AVCFPlayerLayerRef videoLayer() const { return (AVCFPlayerLayerRef)m_avCFVideoLayer.get(); }
128     inline AVCFPlayerRef avPlayer() const { return (AVCFPlayerRef)m_avPlayer.get(); }
129     inline AVCFURLAssetRef avAsset() const { return (AVCFURLAssetRef)m_avAsset.get(); }
130     inline AVCFPlayerItemRef avPlayerItem() const { return (AVCFPlayerItemRef)m_avPlayerItem.get(); }
131     inline AVCFPlayerObserverRef timeObserver() const { return (AVCFPlayerObserverRef)m_timeObserver.get(); }
132     inline AVCFAssetImageGeneratorRef imageGenerator() const { return m_imageGenerator.get(); }
133 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
134     inline AVCFPlayerItemLegibleOutputRef legibleOutput() const { return m_legibleOutput.get(); }
135     AVCFMediaSelectionGroupRef safeMediaSelectionGroupForLegibleMedia() const;
136 #endif
137     inline dispatch_queue_t dispatchQueue() const { return m_notificationQueue; }
138
139 private:
140     inline void* callbackContext() const { return reinterpret_cast<void*>(m_objectID); }
141
142     static Mutex& mapLock();
143     static HashMap<uintptr_t, AVFWrapper*>& map();
144     static AVFWrapper* avfWrapperForCallbackContext(void*);
145     void addToMap();
146     void removeFromMap() const;
147
148     static void disconnectAndDeleteAVFWrapper(void*);
149
150     static uintptr_t s_nextAVFWrapperObjectID;
151     uintptr_t m_objectID;
152
153     MediaPlayerPrivateAVFoundationCF* m_owner;
154
155     RetainPtr<AVCFPlayerRef> m_avPlayer;
156     RetainPtr<AVCFURLAssetRef> m_avAsset;
157     RetainPtr<AVCFPlayerItemRef> m_avPlayerItem;
158     RetainPtr<AVCFPlayerLayerRef> m_avCFVideoLayer;
159     RetainPtr<AVCFPlayerObserverRef> m_timeObserver;
160     RetainPtr<AVCFAssetImageGeneratorRef> m_imageGenerator;
161 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
162     RetainPtr<AVCFPlayerItemLegibleOutputRef> m_legibleOutput;
163     RetainPtr<AVCFMediaSelectionGroupRef> m_selectionGroup;
164 #endif
165     dispatch_queue_t m_notificationQueue;
166
167     mutable RetainPtr<CACFLayerRef> m_caVideoLayer;
168     RefPtr<PlatformCALayer> m_videoLayerWrapper;
169
170     OwnPtr<LayerClient> m_layerClient;
171     COMPtr<IDirect3DDevice9Ex> m_d3dDevice;
172
173     InbandTextTrackPrivateAVF* m_currentTextTrack;
174 };
175
176 uintptr_t AVFWrapper::s_nextAVFWrapperObjectID;
177
178 class LayerClient : public PlatformCALayerClient {
179 public:
180     LayerClient(AVFWrapper* parent) : m_parent(parent) { }
181     virtual ~LayerClient() { m_parent = 0; }
182
183 private:
184     virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*);
185     virtual bool platformCALayerRespondsToLayoutChanges() const { return true; }
186
187     virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { }
188     virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerContentsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp; }
189     virtual void platformCALayerPaintContents(PlatformCALayer*, GraphicsContext&, const FloatRect&) { }
190     virtual bool platformCALayerShowDebugBorders() const { return false; }
191     virtual bool platformCALayerShowRepaintCounter(PlatformCALayer*) const { return false; }
192     virtual int platformCALayerIncrementRepaintCount(PlatformCALayer*) { return 0; }
193
194     virtual bool platformCALayerContentsOpaque() const { return false; }
195     virtual bool platformCALayerDrawsContent() const { return false; }
196     virtual float platformCALayerDeviceScaleFactor() const { return 1; }
197
198     AVFWrapper* m_parent;
199 };
200
201 #if !LOG_DISABLED
202 static const char* boolString(bool val)
203 {
204     return val ? "true" : "false";
205 }
206 #endif
207
208 static CFArrayRef createMetadataKeyNames()
209 {
210     static const CFStringRef keyNames[] = {
211         AVCFAssetPropertyDuration,
212         AVCFAssetPropertyNaturalSize,
213         AVCFAssetPropertyPreferredTransform,
214         AVCFAssetPropertyPreferredRate,
215         AVCFAssetPropertyPlayable,
216         AVCFAssetPropertyTracks,
217 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
218         AVCFAssetPropertyAvailableMediaCharacteristicsWithMediaSelectionOptions,
219 #endif
220     };
221     
222     return CFArrayCreate(0, (const void**)keyNames, sizeof(keyNames) / sizeof(keyNames[0]), &kCFTypeArrayCallBacks);
223 }
224
225 static CFArrayRef metadataKeyNames()
226 {
227     static CFArrayRef keys = createMetadataKeyNames();
228     return keys;
229 }
230
231 // FIXME: It would be better if AVCFTimedMetadataGroup.h exported this key.
232 static CFStringRef CMTimeRangeStartKey()
233 {
234     DEPRECATED_DEFINE_STATIC_LOCAL(CFStringRef, key, (CFSTR("start")));
235     return key;
236 }
237
238 // FIXME: It would be better if AVCFTimedMetadataGroup.h exported this key.
239 static CFStringRef CMTimeRangeDurationKey()
240 {
241     DEPRECATED_DEFINE_STATIC_LOCAL(CFStringRef, key, (CFSTR("duration")));
242     return key;
243 }
244
245 // FIXME: It would be better if AVCF exported this notification name.
246 static CFStringRef CACFContextNeedsFlushNotification()
247 {
248     DEPRECATED_DEFINE_STATIC_LOCAL(CFStringRef, name, (CFSTR("kCACFContextNeedsFlushNotification")));
249     return name;
250 }
251
252 // Define AVCF object accessors as inline functions here instead of in MediaPlayerPrivateAVFoundationCF so we don't have
253 // to include the AVCF headers in MediaPlayerPrivateAVFoundationCF.h
254 inline AVCFPlayerLayerRef videoLayer(AVFWrapper* wrapper)
255
256     return wrapper ? wrapper->videoLayer() : 0; 
257 }
258
259 inline AVCFPlayerRef avPlayer(AVFWrapper* wrapper)
260
261     return wrapper ? wrapper->avPlayer() : 0; 
262 }
263
264 inline AVCFURLAssetRef avAsset(AVFWrapper* wrapper)
265
266     return wrapper ? wrapper->avAsset() : 0; 
267 }
268
269 inline AVCFPlayerItemRef avPlayerItem(AVFWrapper* wrapper)
270
271     return wrapper ? wrapper->avPlayerItem() : 0; 
272 }
273
274 inline AVCFAssetImageGeneratorRef imageGenerator(AVFWrapper* wrapper)
275
276     return wrapper ? wrapper->imageGenerator() : 0; 
277 }
278
279 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
280 inline AVCFPlayerItemLegibleOutputRef avLegibleOutput(AVFWrapper* wrapper)
281 {
282     return wrapper ? wrapper->legibleOutput() : 0;
283 }
284
285 inline AVCFMediaSelectionGroupRef safeMediaSelectionGroupForLegibleMedia(AVFWrapper* wrapper)
286 {
287     return wrapper ? wrapper->safeMediaSelectionGroupForLegibleMedia() : 0;
288 }
289 #endif
290
291 PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateAVFoundationCF::create(MediaPlayer* player) 
292
293     return adoptPtr(new MediaPlayerPrivateAVFoundationCF(player));
294 }
295
296 void MediaPlayerPrivateAVFoundationCF::registerMediaEngine(MediaEngineRegistrar registrar)
297 {
298     if (isAvailable())
299         registrar(create, getSupportedTypes, supportsType, 0, 0, 0, 0);
300 }
301
302 MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF(MediaPlayer* player)
303     : MediaPlayerPrivateAVFoundation(player)
304     , m_avfWrapper(0)
305     , m_videoFrameHasDrawn(false)
306 {
307     LOG(Media, "MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF(%p)", this);
308 }
309
310 MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF()
311 {
312     LOG(Media, "MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF(%p)", this);
313     cancelLoad();
314 }
315
316 void MediaPlayerPrivateAVFoundationCF::cancelLoad()
317 {
318     LOG(Media, "MediaPlayerPrivateAVFoundationCF::cancelLoad(%p)", this);
319
320     // Do nothing when our cancellation of pending loading calls its completion handler
321     setDelayCallbacks(true);
322     setIgnoreLoadStateChanges(true);
323
324     tearDownVideoRendering();
325
326     clearTextTracks();
327
328     if (m_avfWrapper) {
329         // The AVCF objects have to be destroyed on the same dispatch queue used for notifications, so schedule a call to 
330         // disconnectAndDeleteAVFWrapper on that queue. 
331         m_avfWrapper->scheduleDisconnectAndDelete();
332         m_avfWrapper = 0;
333     }
334
335     setIgnoreLoadStateChanges(false);
336     setDelayCallbacks(false);
337 }
338
339 void MediaPlayerPrivateAVFoundationCF::updateVideoLayerGravity()
340 {
341     ASSERT(supportsAcceleratedRendering());
342
343     if (m_avfWrapper)
344         m_avfWrapper->updateVideoLayerGravity();
345 }
346
347 bool MediaPlayerPrivateAVFoundationCF::hasLayerRenderer() const
348 {
349     return videoLayer(m_avfWrapper);
350 }
351
352 bool MediaPlayerPrivateAVFoundationCF::hasContextRenderer() const
353 {
354     return imageGenerator(m_avfWrapper);
355 }
356
357 void MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer()
358 {
359     LOG(Media, "MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer(%p)", this);
360
361     if (imageGenerator(m_avfWrapper))
362         return;
363
364     if (m_avfWrapper)
365         m_avfWrapper->createImageGenerator();
366 }
367
368 void MediaPlayerPrivateAVFoundationCF::destroyContextVideoRenderer()
369 {
370     if (m_avfWrapper)
371         m_avfWrapper->destroyImageGenerator();
372 }
373
374 void MediaPlayerPrivateAVFoundationCF::createVideoLayer()
375 {
376     ASSERT(supportsAcceleratedRendering());
377
378     if (m_avfWrapper)
379         m_avfWrapper->createAVCFVideoLayer();
380 }
381
382 void MediaPlayerPrivateAVFoundationCF::destroyVideoLayer()
383 {
384     LOG(Media, "MediaPlayerPrivateAVFoundationCF::destroyVideoLayer(%p) - destroying %p", this, videoLayer(m_avfWrapper));
385     if (m_avfWrapper)
386         m_avfWrapper->destroyVideoLayer();
387 }
388
389 bool MediaPlayerPrivateAVFoundationCF::hasAvailableVideoFrame() const
390 {
391     return (m_videoFrameHasDrawn || (videoLayer(m_avfWrapper) && AVCFPlayerLayerIsReadyForDisplay(videoLayer(m_avfWrapper))));
392 }
393
394 void MediaPlayerPrivateAVFoundationCF::setCurrentTextTrack(InbandTextTrackPrivateAVF* track)
395 {
396     if (m_avfWrapper)
397         m_avfWrapper->setCurrentTextTrack(track);
398 }
399
400 InbandTextTrackPrivateAVF* MediaPlayerPrivateAVFoundationCF::currentTextTrack() const
401 {
402     if (m_avfWrapper)
403         return m_avfWrapper->currentTextTrack();
404
405     return 0;
406 }
407
408 void MediaPlayerPrivateAVFoundationCF::createAVAssetForURL(const String& url)
409 {
410     ASSERT(!m_avfWrapper);
411
412     setDelayCallbacks(true);
413
414     bool inheritURI = player()->doesHaveAttribute("x-itunes-inherit-uri-query-component");
415
416     m_avfWrapper = new AVFWrapper(this);
417     m_avfWrapper->createAssetForURL(url, inheritURI);
418     setDelayCallbacks(false);
419 }
420
421 void MediaPlayerPrivateAVFoundationCF::createAVPlayer()
422 {
423     ASSERT(m_avfWrapper);
424     
425     setDelayCallbacks(true);
426     m_avfWrapper->createPlayer(reinterpret_cast<IDirect3DDevice9*>(player()->graphicsDeviceAdapter()));
427     setDelayCallbacks(false);
428 }
429
430 void MediaPlayerPrivateAVFoundationCF::createAVPlayerItem()
431 {
432     ASSERT(m_avfWrapper);
433     
434     setDelayCallbacks(true);
435     m_avfWrapper->createPlayerItem();
436
437     setDelayCallbacks(false);
438 }
439
440 void MediaPlayerPrivateAVFoundationCF::checkPlayability()
441 {
442     ASSERT(m_avfWrapper);
443     m_avfWrapper->checkPlayability();
444 }
445
446 void MediaPlayerPrivateAVFoundationCF::beginLoadingMetadata()
447 {
448     ASSERT(m_avfWrapper);
449     m_avfWrapper->beginLoadingMetadata();
450 }
451
452 MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationCF::playerItemStatus() const
453 {
454     if (!avPlayerItem(m_avfWrapper))
455         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusDoesNotExist;
456
457     AVCFPlayerItemStatus status = AVCFPlayerItemGetStatus(avPlayerItem(m_avfWrapper), 0);
458     if (status == AVCFPlayerItemStatusUnknown)
459         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
460     if (status == AVCFPlayerItemStatusFailed)
461         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
462     if (AVCFPlayerItemIsPlaybackLikelyToKeepUp(avPlayerItem(m_avfWrapper)))
463         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
464     if (AVCFPlayerItemIsPlaybackBufferFull(avPlayerItem(m_avfWrapper)))
465         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
466     if (AVCFPlayerItemIsPlaybackBufferEmpty(avPlayerItem(m_avfWrapper)))
467         return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
468     return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
469 }
470
471 PlatformMedia MediaPlayerPrivateAVFoundationCF::platformMedia() const
472 {
473     LOG(Media, "MediaPlayerPrivateAVFoundationCF::platformMedia(%p)", this);
474     PlatformMedia pm;
475     pm.type = PlatformMedia::AVFoundationCFMediaPlayerType;
476     pm.media.avcfMediaPlayer = (AVCFPlayer*)avPlayer(m_avfWrapper);
477     return pm;
478 }
479
480 PlatformLayer* MediaPlayerPrivateAVFoundationCF::platformLayer() const
481 {
482     if (!m_avfWrapper)
483         return 0;
484
485     return m_avfWrapper->platformLayer();
486 }
487
488 void MediaPlayerPrivateAVFoundationCF::platformSetVisible(bool isVisible)
489 {
490     if (!m_avfWrapper)
491         return;
492     
493     // FIXME: We use a CATransaction here on the Mac, we need to figure out why this was done there and
494     // whether we're affected by the same issue.
495     setDelayCallbacks(true);
496     m_avfWrapper->setVideoLayerHidden(!isVisible);    
497     if (!isVisible)
498         tearDownVideoRendering();
499     setDelayCallbacks(false);
500 }
501
502 void MediaPlayerPrivateAVFoundationCF::platformPlay()
503 {
504     LOG(Media, "MediaPlayerPrivateAVFoundationCF::play(%p)", this);
505     if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
506         return;
507
508     setDelayCallbacks(true);
509     AVCFPlayerSetRate(avPlayer(m_avfWrapper), requestedRate());
510     setDelayCallbacks(false);
511 }
512
513 void MediaPlayerPrivateAVFoundationCF::platformPause()
514 {
515     LOG(Media, "MediaPlayerPrivateAVFoundationCF::pause(%p)", this);
516     if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
517         return;
518
519     setDelayCallbacks(true);
520     AVCFPlayerSetRate(avPlayer(m_avfWrapper), 0);
521     setDelayCallbacks(false);
522 }
523
524 float MediaPlayerPrivateAVFoundationCF::platformDuration() const
525 {
526     if (!metaDataAvailable() || !avAsset(m_avfWrapper))
527         return 0;
528
529     CMTime cmDuration;
530
531     // Check the AVItem if we have one and it has loaded duration, some assets never report duration.
532     if (avPlayerItem(m_avfWrapper) && playerItemStatus() >= MediaPlayerAVPlayerItemStatusReadyToPlay)
533         cmDuration = AVCFPlayerItemGetDuration(avPlayerItem(m_avfWrapper));
534     else
535         cmDuration = AVCFAssetGetDuration(avAsset(m_avfWrapper));
536
537     if (CMTIME_IS_NUMERIC(cmDuration))
538         return narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
539
540     if (CMTIME_IS_INDEFINITE(cmDuration))
541         return numeric_limits<float>::infinity();
542
543     LOG(Media, "MediaPlayerPrivateAVFoundationCF::platformDuration(%p) - invalid duration, returning %.0f", this, static_cast<float>(MediaPlayer::invalidTime()));
544     return static_cast<float>(MediaPlayer::invalidTime());
545 }
546
547 float MediaPlayerPrivateAVFoundationCF::currentTime() const
548 {
549     if (!metaDataAvailable() || !avPlayerItem(m_avfWrapper))
550         return 0;
551
552     CMTime itemTime = AVCFPlayerItemGetCurrentTime(avPlayerItem(m_avfWrapper));
553     if (CMTIME_IS_NUMERIC(itemTime))
554         return max(narrowPrecisionToFloat(CMTimeGetSeconds(itemTime)), 0.0f);
555
556     return 0;
557 }
558
559 void MediaPlayerPrivateAVFoundationCF::seekToTime(double time, double negativeTolerance, double positiveTolerance)
560 {
561     if (!m_avfWrapper)
562         return;
563     
564     // seekToTime generates several event callbacks, update afterwards.
565     setDelayCallbacks(true);
566     m_avfWrapper->seekToTime(time, negativeTolerance, positiveTolerance);
567     setDelayCallbacks(false);
568 }
569
570 void MediaPlayerPrivateAVFoundationCF::setVolume(float volume)
571 {
572     if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
573         return;
574
575     AVCFPlayerSetVolume(avPlayer(m_avfWrapper), volume);
576 }
577
578 void MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible(bool closedCaptionsVisible)
579 {
580     if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
581         return;
582
583     LOG(Media, "MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible(%p) - setting to %s", this, boolString(closedCaptionsVisible));
584     AVCFPlayerSetClosedCaptionDisplayEnabled(avPlayer(m_avfWrapper), closedCaptionsVisible);
585 }
586
587 void MediaPlayerPrivateAVFoundationCF::updateRate()
588 {
589     LOG(Media, "MediaPlayerPrivateAVFoundationCF::updateRate(%p)", this);
590     if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
591         return;
592
593     setDelayCallbacks(true);
594     AVCFPlayerSetRate(avPlayer(m_avfWrapper), requestedRate());
595     setDelayCallbacks(false);
596 }
597
598 float MediaPlayerPrivateAVFoundationCF::rate() const
599 {
600     if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
601         return 0;
602
603     setDelayCallbacks(true);
604     float currentRate = AVCFPlayerGetRate(avPlayer(m_avfWrapper));
605     setDelayCallbacks(false);
606
607     return currentRate;
608 }
609
610 static bool timeRangeIsValidAndNotEmpty(CMTime start, CMTime duration)
611 {
612     // Is the range valid?
613     if (!CMTIME_IS_VALID(start) || !CMTIME_IS_VALID(duration) || duration.epoch || duration.value < 0)
614         return false;
615
616     if (CMTIME_COMPARE_INLINE(duration, ==, kCMTimeZero))
617         return false;
618
619     return true;
620 }
621
622 std::unique_ptr<PlatformTimeRanges> MediaPlayerPrivateAVFoundationCF::platformBufferedTimeRanges() const
623 {
624     auto timeRanges = PlatformTimeRanges::create();
625
626     if (!avPlayerItem(m_avfWrapper))
627         return timeRanges;
628
629     RetainPtr<CFArrayRef> loadedRanges = adoptCF(AVCFPlayerItemCopyLoadedTimeRanges(avPlayerItem(m_avfWrapper)));
630     if (!loadedRanges)
631         return timeRanges;
632
633     CFIndex rangeCount = CFArrayGetCount(loadedRanges.get());
634     for (CFIndex i = 0; i < rangeCount; i++) {
635         CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loadedRanges.get(), i));
636         CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
637         CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
638         
639         if (timeRangeIsValidAndNotEmpty(start, duration)) {
640             double rangeStart = CMTimeGetSeconds(start);
641             double rangeEnd = rangeStart + CMTimeGetSeconds(duration);
642             timeRanges->add(MediaTime::createWithDouble(rangeStart), MediaTime::createWithDouble(rangeEnd));
643         }
644     }
645
646     return timeRanges;
647 }
648
649 double MediaPlayerPrivateAVFoundationCF::platformMinTimeSeekable() const 
650
651     RetainPtr<CFArrayRef> seekableRanges = adoptCF(AVCFPlayerItemCopySeekableTimeRanges(avPlayerItem(m_avfWrapper)));
652     if (!seekableRanges) 
653         return 0; 
654
655     double minTimeSeekable = std::numeric_limits<double>::infinity(); 
656     bool hasValidRange = false; 
657     CFIndex rangeCount = CFArrayGetCount(seekableRanges.get());
658     for (CFIndex i = 0; i < rangeCount; i++) {
659         CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(seekableRanges.get(), i));
660         CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
661         CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
662         if (!timeRangeIsValidAndNotEmpty(start, duration))
663             continue;
664
665         hasValidRange = true; 
666         double startOfRange = CMTimeGetSeconds(start); 
667         if (minTimeSeekable > startOfRange) 
668             minTimeSeekable = startOfRange; 
669     } 
670     return hasValidRange ? minTimeSeekable : 0; 
671
672
673 double MediaPlayerPrivateAVFoundationCF::platformMaxTimeSeekable() const
674 {
675     if (!avPlayerItem(m_avfWrapper))
676         return 0;
677
678     RetainPtr<CFArrayRef> seekableRanges = adoptCF(AVCFPlayerItemCopySeekableTimeRanges(avPlayerItem(m_avfWrapper)));
679     if (!seekableRanges)
680         return 0;
681
682     double maxTimeSeekable = 0;
683     CFIndex rangeCount = CFArrayGetCount(seekableRanges.get());
684     for (CFIndex i = 0; i < rangeCount; i++) {
685         CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(seekableRanges.get(), i));
686         CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
687         CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
688         if (!timeRangeIsValidAndNotEmpty(start, duration))
689             continue;
690         
691         double endOfRange = CMTimeGetSeconds(CMTimeAdd(start, duration));
692         if (maxTimeSeekable < endOfRange)
693             maxTimeSeekable = endOfRange;
694     }
695
696     return maxTimeSeekable;   
697 }
698
699 float MediaPlayerPrivateAVFoundationCF::platformMaxTimeLoaded() const
700 {
701     if (!avPlayerItem(m_avfWrapper))
702         return 0;
703
704     RetainPtr<CFArrayRef> loadedRanges = adoptCF(AVCFPlayerItemCopyLoadedTimeRanges(avPlayerItem(m_avfWrapper)));
705     if (!loadedRanges)
706         return 0;
707
708     float maxTimeLoaded = 0;
709     CFIndex rangeCount = CFArrayGetCount(loadedRanges.get());
710     for (CFIndex i = 0; i < rangeCount; i++) {
711         CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loadedRanges.get(), i));
712         CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
713         CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
714         if (!timeRangeIsValidAndNotEmpty(start, duration))
715             continue;
716         
717         float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
718         if (maxTimeLoaded < endOfRange)
719             maxTimeLoaded = endOfRange;
720     }
721
722     return maxTimeLoaded;   
723 }
724
725 unsigned long long MediaPlayerPrivateAVFoundationCF::totalBytes() const
726 {
727     if (!metaDataAvailable() || !avAsset(m_avfWrapper))
728         return 0;
729
730     int64_t totalMediaSize = 0;
731     RetainPtr<CFArrayRef> tracks = adoptCF(AVCFAssetCopyAssetTracks(avAsset(m_avfWrapper)));
732     CFIndex trackCount = CFArrayGetCount(tracks.get());
733     for (CFIndex i = 0; i < trackCount; i++) {
734         AVCFAssetTrackRef assetTrack = (AVCFAssetTrackRef)CFArrayGetValueAtIndex(tracks.get(), i);
735         totalMediaSize += AVCFAssetTrackGetTotalSampleDataLength(assetTrack);
736     }
737
738     return static_cast<unsigned long long>(totalMediaSize);
739 }
740
741 MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationCF::assetStatus() const
742 {
743     if (!avAsset(m_avfWrapper))
744         return MediaPlayerAVAssetStatusDoesNotExist;
745
746     // First, make sure all metadata properties we rely on are loaded.
747     CFArrayRef keys = metadataKeyNames();
748     CFIndex keyCount = CFArrayGetCount(keys);
749     for (CFIndex i = 0; i < keyCount; i++) {
750         CFStringRef keyName = static_cast<CFStringRef>(CFArrayGetValueAtIndex(keys, i));
751         AVCFPropertyValueStatus keyStatus = AVCFAssetGetStatusOfValueForProperty(avAsset(m_avfWrapper), keyName, 0);
752
753         if (keyStatus < AVCFPropertyValueStatusLoaded)
754             return MediaPlayerAVAssetStatusLoading;
755         if (keyStatus == AVCFPropertyValueStatusFailed) {
756             if (CFStringCompare(keyName, AVCFAssetPropertyNaturalSize, 0) == kCFCompareEqualTo) {
757                 // Don't treat a failure to retrieve @"naturalSize" as fatal. We will use @"presentationSize" instead.
758                 // <rdar://problem/15966685>
759                 continue;
760             }
761 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
762             if (CFStringCompare(keyName, AVCFAssetPropertyAvailableMediaCharacteristicsWithMediaSelectionOptions, 0) == kCFCompareEqualTo) {
763                 // On Windows, the media selection options are not available when initially interacting with a streaming source.
764                 // <rdar://problem/16160699>
765                 continue;
766             }
767 #endif
768             return MediaPlayerAVAssetStatusFailed;
769         }
770         if (keyStatus == AVCFPropertyValueStatusCancelled)
771             return MediaPlayerAVAssetStatusCancelled;
772     }
773
774     if (AVCFAssetIsPlayable(avAsset(m_avfWrapper)))
775         return MediaPlayerAVAssetStatusPlayable;
776
777     return MediaPlayerAVAssetStatusLoaded;
778 }
779
780 void MediaPlayerPrivateAVFoundationCF::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
781 {
782     if (!metaDataAvailable() || context->paintingDisabled())
783         return;
784
785     if (currentRenderingMode() == MediaRenderingToLayer && !imageGenerator(m_avfWrapper)) {
786         // We're being told to render into a context, but we already have the
787         // video layer, which probably means we've been called from <canvas>.
788         createContextVideoRenderer();
789     }
790
791     paint(context, rect);
792 }
793
794 void MediaPlayerPrivateAVFoundationCF::paint(GraphicsContext* context, const IntRect& rect)
795 {
796     if (!metaDataAvailable() || context->paintingDisabled() || !imageGenerator(m_avfWrapper))
797         return;
798
799     LOG(Media, "MediaPlayerPrivateAVFoundationCF::paint(%p)", this);
800
801     setDelayCallbacks(true);
802     RetainPtr<CGImageRef> image = m_avfWrapper->createImageForTimeInRect(currentTime(), rect);
803     if (image) {
804         context->save();
805         context->translate(rect.x(), rect.y() + rect.height());
806         context->scale(FloatSize(1.0f, -1.0f));
807         context->setImageInterpolationQuality(InterpolationLow);
808         IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
809         CGContextDrawImage(context->platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
810         context->restore();
811         image = 0;
812     }
813     setDelayCallbacks(false);
814     
815     m_videoFrameHasDrawn = true;
816 }
817
818 static HashSet<String> mimeTypeCache()
819 {
820     DEPRECATED_DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
821     static bool typeListInitialized = false;
822
823     if (typeListInitialized)
824         return cache;
825     typeListInitialized = true;
826
827     RetainPtr<CFArrayRef> supportedTypes = adoptCF(AVCFURLAssetCopyAudiovisualMIMETypes());
828     
829     ASSERT(supportedTypes);
830     if (!supportedTypes)
831         return cache;
832
833     CFIndex typeCount = CFArrayGetCount(supportedTypes.get());
834     for (CFIndex i = 0; i < typeCount; i++)
835         cache.add(static_cast<CFStringRef>(CFArrayGetValueAtIndex(supportedTypes.get(), i)));
836
837     return cache;
838
839
840 void MediaPlayerPrivateAVFoundationCF::getSupportedTypes(HashSet<String>& supportedTypes)
841 {
842     supportedTypes = mimeTypeCache();
843
844
845 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationCF::supportsType(const MediaEngineSupportParameters& parameters)
846 {
847     // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask if it supports an
848     // extended MIME type until rdar://8721715 is fixed.
849     if (mimeTypeCache().contains(parameters.type))
850         return parameters.codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
851
852     return MediaPlayer::IsNotSupported;
853 }
854
855
856 bool MediaPlayerPrivateAVFoundationCF::isAvailable()
857 {
858     return AVFoundationCFLibrary() && CoreMediaLibrary();
859 }
860
861 float MediaPlayerPrivateAVFoundationCF::mediaTimeForTimeValue(float timeValue) const
862 {
863     if (!metaDataAvailable())
864         return timeValue;
865
866     // FIXME - can not implement until rdar://8721669 is fixed.
867     return timeValue;
868 }
869
870 void MediaPlayerPrivateAVFoundationCF::tracksChanged()
871 {
872     String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
873     m_languageOfPrimaryAudioTrack = String();
874
875     if (!avAsset(m_avfWrapper))
876         return;
877
878     setDelayCharacteristicsChangedNotification(true);
879
880     bool haveCCTrack = false;
881     bool hasCaptions = false;
882
883     // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
884     // asked about those fairly frequently.
885     if (!avPlayerItem(m_avfWrapper)) {
886         // We don't have a player item yet, so check with the asset because some assets support inspection
887         // prior to becoming ready to play.
888         RetainPtr<CFArrayRef> visualTracks = adoptCF(AVCFAssetCopyTracksWithMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicVisual));
889         setHasVideo(CFArrayGetCount(visualTracks.get()));
890
891         RetainPtr<CFArrayRef> audioTracks = adoptCF(AVCFAssetCopyTracksWithMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicAudible));
892         setHasAudio(CFArrayGetCount(audioTracks.get()));
893
894 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
895         RetainPtr<CFArrayRef> captionTracks = adoptCF(AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaTypeClosedCaption));
896         hasCaptions = CFArrayGetCount(captionTracks.get());
897 #endif
898     } else {
899         bool hasVideo = false;
900         bool hasAudio = false;
901
902         RetainPtr<CFArrayRef> tracks = adoptCF(AVCFPlayerItemCopyTracks(avPlayerItem(m_avfWrapper)));
903
904         CFIndex trackCount = CFArrayGetCount(tracks.get());
905         for (CFIndex i = 0; i < trackCount; i++) {
906             AVCFPlayerItemTrackRef track = (AVCFPlayerItemTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
907             
908             if (AVCFPlayerItemTrackIsEnabled(track)) {
909                 RetainPtr<AVCFAssetTrackRef> assetTrack = adoptCF(AVCFPlayerItemTrackCopyAssetTrack(track));
910                 if (!assetTrack) {
911                     // Asset tracks may not be available yet when streaming. <rdar://problem/16160699>
912                     LOG(Media, "MediaPlayerPrivateAVFoundationCF:tracksChanged(%p) - track = %d is enabled, but has no asset track.", this, track);
913                     continue;
914                 }
915                 CFStringRef mediaType = AVCFAssetTrackGetMediaType(assetTrack.get());
916                 if (!mediaType)
917                     continue;
918                 
919                 if (CFStringCompare(mediaType, AVCFMediaTypeVideo, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
920                     hasVideo = true;
921                 else if (CFStringCompare(mediaType, AVCFMediaTypeAudio, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
922                     hasAudio = true;
923                 else if (CFStringCompare(mediaType, AVCFMediaTypeClosedCaption, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
924 #if !HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
925                     hasCaptions = true;
926 #endif
927                     haveCCTrack = true;
928                 }
929             }
930         }
931
932         setHasVideo(hasVideo);
933         setHasAudio(hasAudio);
934     }
935
936 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
937     AVCFMediaSelectionGroupRef legibleGroup = safeMediaSelectionGroupForLegibleMedia(m_avfWrapper);
938     if (legibleGroup) {
939         RetainPtr<CFArrayRef> playableOptions = adoptCF(AVCFMediaSelectionCopyPlayableOptionsFromArray(AVCFMediaSelectionGroupGetOptions(legibleGroup)));
940         hasCaptions = CFArrayGetCount(playableOptions.get());
941         if (hasCaptions)
942             processMediaSelectionOptions();
943     }
944 #endif
945
946 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
947     if (haveCCTrack)
948         processLegacyClosedCaptionsTracks();
949 #endif
950
951     setHasClosedCaptions(hasCaptions);
952
953     LOG(Media, "MediaPlayerPrivateAVFoundationCF:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s", 
954         this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions()));
955
956     sizeChanged();
957
958     if (primaryAudioTrackLanguage != languageOfPrimaryAudioTrack())
959         characteristicsChanged();
960
961     setDelayCharacteristicsChangedNotification(false);
962 }
963
964 void MediaPlayerPrivateAVFoundationCF::sizeChanged()
965 {
966     if (!avAsset(m_avfWrapper))
967         return;
968     
969     // AVAsset's 'naturalSize' property only considers the movie's first video track, so we need to compute
970     // the union of all visual track rects.
971     CGRect trackRectUnion = CGRectZero;
972     RetainPtr<CFArrayRef> tracks = adoptCF(AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaCharacteristicVisual));
973     CFIndex trackCount = CFArrayGetCount(tracks.get());
974     for (CFIndex i = 0; i < trackCount; i++) {
975         AVCFAssetTrackRef assetTrack = (AVCFAssetTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
976         
977         CGSize trackSize = AVCFAssetTrackGetNaturalSize(assetTrack);
978         CGRect trackRect = CGRectMake(0, 0, trackSize.width, trackSize.height);
979         trackRectUnion = CGRectUnion(trackRectUnion, CGRectApplyAffineTransform(trackRect, AVCFAssetTrackGetPreferredTransform(assetTrack)));
980     }
981     // The movie is always displayed at 0,0 so move the track rect to the origin before using width and height.
982     trackRectUnion = CGRectOffset(trackRectUnion, trackRectUnion.origin.x, trackRectUnion.origin.y);
983     CGSize naturalSize = trackRectUnion.size;
984
985     if (!naturalSize.height && !naturalSize.width && avPlayerItem(m_avfWrapper))
986         naturalSize = AVCFPlayerItemGetPresentationSize(avPlayerItem(m_avfWrapper));
987
988     // Also look at the asset's preferred transform so we account for a movie matrix.
989     CGSize movieSize = CGSizeApplyAffineTransform(AVCFAssetGetNaturalSize(avAsset(m_avfWrapper)), AVCFAssetGetPreferredTransform(avAsset(m_avfWrapper)));
990     if (movieSize.width > naturalSize.width)
991         naturalSize.width = movieSize.width;
992     if (movieSize.height > naturalSize.height)
993         naturalSize.height = movieSize.height;
994     setNaturalSize(IntSize(naturalSize));
995 }
996
997 bool MediaPlayerPrivateAVFoundationCF::requiresImmediateCompositing() const
998 {
999     // The AVFoundationCF player needs to have the root compositor available at construction time
1000     // so it can attach to the rendering device. Otherwise it falls back to CPU-only mode.
1001     //
1002     // It would be nice if AVCFPlayer had some way to switch to hardware-accelerated mode
1003     // when asked, then we could follow AVFoundation's model and switch to compositing
1004     // mode when beginning to play media.
1005     return true;
1006 }
1007
1008 #if !HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1009 void MediaPlayerPrivateAVFoundationCF::processLegacyClosedCaptionsTracks()
1010 {
1011 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1012     AVCFPlayerItemSelectMediaOptionInMediaSelectionGroup(avPlayerItem(m_avfWrapper), 0, safeMediaSelectionGroupForLegibleMedia(m_avfWrapper));
1013 #endif
1014
1015     Vector<RefPtr<InbandTextTrackPrivateAVF> > removedTextTracks = m_textTracks;
1016     RetainPtr<CFArrayRef> tracks = adoptCF(AVCFPlayerItemCopyTracks(avPlayerItem(m_avfWrapper)));
1017     CFIndex trackCount = CFArrayGetCount(tracks.get());
1018     for (CFIndex i = 0; i < trackCount; ++i) {
1019         AVCFPlayerItemTrackRef playerItemTrack = (AVCFPlayerItemTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
1020
1021         RetainPtr<AVCFAssetTrackRef> assetTrack = adoptCF(AVCFPlayerItemTrackCopyAssetTrack(playerItemTrack));
1022         if (!assetTrack) {
1023             // Asset tracks may not be available yet when streaming. <rdar://problem/16160699>
1024             LOG(Media, "MediaPlayerPrivateAVFoundationCF:tracksChanged(%p) - track = %d is enabled, but has no asset track.", this, track);
1025             continue;
1026         }
1027         CFStringRef mediaType = AVCFAssetTrackGetMediaType(assetTrack.get());
1028         if (!mediaType)
1029             continue;
1030                 
1031         if (CFStringCompare(mediaType, AVCFMediaTypeClosedCaption, kCFCompareCaseInsensitive) != kCFCompareEqualTo)
1032             continue;
1033
1034         bool newCCTrack = true;
1035         for (unsigned i = removedTextTracks.size(); i > 0; --i) {
1036             if (removedTextTracks[i - 1]->textTrackCategory() != InbandTextTrackPrivateAVF::LegacyClosedCaption)
1037                 continue;
1038
1039             RefPtr<InbandTextTrackPrivateLegacyAVCF> track = static_cast<InbandTextTrackPrivateLegacyAVCF*>(m_textTracks[i - 1].get());
1040             if (track->avPlayerItemTrack() == playerItemTrack) {
1041                 removedTextTracks.remove(i - 1);
1042                 newCCTrack = false;
1043                 break;
1044             }
1045         }
1046
1047         if (!newCCTrack)
1048             continue;
1049         
1050         m_textTracks.append(InbandTextTrackPrivateLegacyAVCF::create(this, playerItemTrack));
1051     }
1052
1053     processNewAndRemovedTextTracks(removedTextTracks);
1054 }
1055 #endif
1056
1057 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1058 void MediaPlayerPrivateAVFoundationCF::processMediaSelectionOptions()
1059 {
1060     AVCFMediaSelectionGroupRef legibleGroup = safeMediaSelectionGroupForLegibleMedia(m_avfWrapper);
1061     if (!legibleGroup) {
1062         LOG(Media, "MediaPlayerPrivateAVFoundationCF::processMediaSelectionOptions(%p) - nil mediaSelectionGroup", this);
1063         return;
1064     }
1065
1066     // We enabled automatic media selection because we want alternate audio tracks to be enabled/disabled automatically,
1067     // but set the selected legible track to nil so text tracks will not be automatically configured.
1068     if (!m_textTracks.size()) {
1069         ASSERT(AVCFMediaSelectionGroupAllowsEmptySelection(legibleGroup));
1070         AVCFPlayerItemRef playerItem = avPlayerItem(m_avfWrapper);
1071
1072         if (playerItem)
1073             AVCFPlayerItemSelectMediaOptionInMediaSelectionGroup(playerItem, 0, legibleGroup);
1074     }
1075
1076     Vector<RefPtr<InbandTextTrackPrivateAVF> > removedTextTracks = m_textTracks;
1077     RetainPtr<CFArrayRef> legibleOptions = adoptCF(AVCFMediaSelectionCopyPlayableOptionsFromArray(AVCFMediaSelectionGroupGetOptions(legibleGroup)));
1078     CFIndex legibleOptionsCount = CFArrayGetCount(legibleOptions.get());
1079     for (CFIndex i = 0; i < legibleOptionsCount; ++i) {
1080         AVCFMediaSelectionOptionRef option = static_cast<AVCFMediaSelectionOptionRef>(CFArrayGetValueAtIndex(legibleOptions.get(), i));
1081         bool newTrack = true;
1082         for (unsigned i = removedTextTracks.size(); i > 0; --i) {
1083             if (removedTextTracks[i - 1]->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
1084                 continue;
1085
1086             RefPtr<InbandTextTrackPrivateAVCF> track = static_cast<InbandTextTrackPrivateAVCF*>(removedTextTracks[i - 1].get());
1087             if (CFEqual(track->mediaSelectionOption(), option)) {
1088                 removedTextTracks.remove(i - 1);
1089                 newTrack = false;
1090                 break;
1091             }
1092         }
1093         if (!newTrack)
1094             continue;
1095
1096         m_textTracks.append(InbandTextTrackPrivateAVCF::create(this, option, InbandTextTrackPrivate::Generic));
1097     }
1098
1099     processNewAndRemovedTextTracks(removedTextTracks);
1100 }
1101
1102 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1103
1104 void AVFWrapper::setCurrentTextTrack(InbandTextTrackPrivateAVF* track)
1105 {
1106     if (m_currentTextTrack == track)
1107         return;
1108
1109     LOG(Media, "MediaPlayerPrivateAVFoundationCF::setCurrentTextTrack(%p) - selecting track %p, language = %s", this, track, track ? track->language().string().utf8().data() : "");
1110         
1111     m_currentTextTrack = track;
1112
1113     if (track) {
1114         if (track->textTrackCategory() == InbandTextTrackPrivateAVF::LegacyClosedCaption)
1115             AVCFPlayerSetClosedCaptionDisplayEnabled(avPlayer(), TRUE);
1116 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1117         else
1118             AVCFPlayerItemSelectMediaOptionInMediaSelectionGroup(avPlayerItem(), static_cast<InbandTextTrackPrivateAVCF*>(track)->mediaSelectionOption(), safeMediaSelectionGroupForLegibleMedia());
1119 #endif
1120     } else {
1121 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1122         AVCFPlayerItemSelectMediaOptionInMediaSelectionGroup(avPlayerItem(), 0, safeMediaSelectionGroupForLegibleMedia());
1123 #endif
1124         AVCFPlayerSetClosedCaptionDisplayEnabled(avPlayer(), FALSE);
1125     }
1126 }
1127
1128 String MediaPlayerPrivateAVFoundationCF::languageOfPrimaryAudioTrack() const
1129 {
1130     if (!m_languageOfPrimaryAudioTrack.isNull())
1131         return m_languageOfPrimaryAudioTrack;
1132
1133     if (!avPlayerItem(m_avfWrapper))
1134         return emptyString();
1135
1136 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1137     // If AVFoundation has an audible group, return the language of the currently selected audible option.
1138     AVCFMediaSelectionGroupRef audibleGroup = AVCFAssetGetSelectionGroupForMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicAudible);
1139     AVCFMediaSelectionOptionRef currentlySelectedAudibleOption = AVCFPlayerItemGetSelectedMediaOptionInMediaSelectionGroup(avPlayerItem(m_avfWrapper), audibleGroup);
1140     if (currentlySelectedAudibleOption) {
1141         RetainPtr<CFLocaleRef> audibleOptionLocale = adoptCF(AVCFMediaSelectionOptionCopyLocale(currentlySelectedAudibleOption));
1142         m_languageOfPrimaryAudioTrack = CFLocaleGetIdentifier(audibleOptionLocale.get());
1143         LOG(Media, "MediaPlayerPrivateAVFoundationCF::languageOfPrimaryAudioTrack(%p) - returning language of selected audible option: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
1144
1145         return m_languageOfPrimaryAudioTrack;
1146     }
1147 #endif // HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1148
1149     // AVFoundation synthesizes an audible group when there is only one ungrouped audio track if there is also a legible group (one or
1150     // 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.
1151     RetainPtr<CFArrayRef> tracks = adoptCF(AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaTypeAudio));
1152     CFIndex trackCount = CFArrayGetCount(tracks.get());
1153     if (!tracks || trackCount != 1) {
1154         m_languageOfPrimaryAudioTrack = emptyString();
1155         LOG(Media, "MediaPlayerPrivateAVFoundationCF::languageOfPrimaryAudioTrack(%p) - %i audio tracks, returning emptyString()", this, (tracks ? trackCount : 0));
1156         return m_languageOfPrimaryAudioTrack;
1157     }
1158
1159     AVCFAssetTrackRef track = (AVCFAssetTrackRef)CFArrayGetValueAtIndex(tracks.get(), 0);
1160     RetainPtr<CFStringRef> language = adoptCF(AVCFAssetTrackCopyExtendedLanguageTag(track));
1161
1162     // If the language code is stored as a QuickTime 5-bit packed code there aren't enough bits for a full
1163     // RFC 4646 language tag so extendedLanguageTag returns null. In this case languageCode will return the
1164     // ISO 639-2/T language code so check it.
1165     if (!language)
1166         language = adoptCF(AVCFAssetTrackCopyLanguageCode(track));
1167
1168     // Some legacy tracks have "und" as a language, treat that the same as no language at all.
1169     if (language && CFStringCompare(language.get(), CFSTR("und"), kCFCompareCaseInsensitive) != kCFCompareEqualTo) {
1170         m_languageOfPrimaryAudioTrack = language.get();
1171         LOG(Media, "MediaPlayerPrivateAVFoundationCF::languageOfPrimaryAudioTrack(%p) - returning language of single audio track: %s", this, m_languageOfPrimaryAudioTrack.utf8().data());
1172         return m_languageOfPrimaryAudioTrack;
1173     }
1174
1175     LOG(Media, "MediaPlayerPrivateAVFoundationCF::languageOfPrimaryAudioTrack(%p) - single audio track has no language, returning emptyString()", this);
1176     m_languageOfPrimaryAudioTrack = emptyString();
1177     return m_languageOfPrimaryAudioTrack;
1178 }
1179
1180 void MediaPlayerPrivateAVFoundationCF::contentsNeedsDisplay()
1181 {
1182     if (m_avfWrapper)
1183         m_avfWrapper->setVideoLayerNeedsCommit();
1184 }
1185
1186 AVFWrapper::AVFWrapper(MediaPlayerPrivateAVFoundationCF* owner)
1187     : m_owner(owner)
1188     , m_objectID(s_nextAVFWrapperObjectID++)
1189     , m_currentTextTrack(0)
1190 {
1191     ASSERT(isMainThread());
1192     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1193     LOG(Media, "AVFWrapper::AVFWrapper(%p)", this);
1194
1195     m_notificationQueue = dispatch_queue_create("MediaPlayerPrivateAVFoundationCF.notificationQueue", 0);
1196     addToMap();
1197 }
1198
1199 AVFWrapper::~AVFWrapper()
1200 {
1201     ASSERT(isMainThread());
1202     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1203     LOG(Media, "AVFWrapper::~AVFWrapper(%p %d)", this, m_objectID);
1204
1205     destroyVideoLayer();
1206     destroyImageGenerator();
1207
1208     if (m_notificationQueue)
1209         dispatch_release(m_notificationQueue);
1210
1211     if (avAsset()) {
1212         AVCFAssetCancelLoading(avAsset());
1213         m_avAsset = 0;
1214     }
1215
1216 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1217     if (legibleOutput()) {
1218         if (avPlayerItem())
1219             AVCFPlayerItemRemoveOutput(avPlayerItem(), legibleOutput());
1220         m_legibleOutput = 0;
1221     }
1222 #endif
1223
1224     m_avPlayerItem = 0;
1225     m_timeObserver = 0;
1226     m_avPlayer = 0;
1227 }
1228
1229 Mutex& AVFWrapper::mapLock()
1230 {
1231     static Mutex mapLock;
1232     return mapLock;
1233 }
1234
1235 HashMap<uintptr_t, AVFWrapper*>& AVFWrapper::map()
1236 {
1237     static HashMap<uintptr_t, AVFWrapper*>& map = *new HashMap<uintptr_t, AVFWrapper*>;
1238     return map;
1239 }
1240
1241 void AVFWrapper::addToMap()
1242 {
1243     MutexLocker locker(mapLock());
1244     
1245     // HashMap doesn't like a key of 0, and also make sure we aren't
1246     // using an object ID that's already in use.
1247     while (!m_objectID || (map().find(m_objectID) != map().end()))
1248         m_objectID = s_nextAVFWrapperObjectID++;
1249        
1250     LOG(Media, "AVFWrapper::addToMap(%p %d)", this, m_objectID);
1251
1252     map().add(m_objectID, this);
1253 }
1254
1255 void AVFWrapper::removeFromMap() const
1256 {
1257     LOG(Media, "AVFWrapper::removeFromMap(%p %d)", this, m_objectID);
1258
1259     MutexLocker locker(mapLock());
1260     map().remove(m_objectID);
1261 }
1262
1263 AVFWrapper* AVFWrapper::avfWrapperForCallbackContext(void* context)
1264 {
1265     // Assumes caller has locked mapLock().
1266     HashMap<uintptr_t, AVFWrapper*>::iterator it = map().find(reinterpret_cast<uintptr_t>(context));
1267     if (it == map().end())
1268         return 0;
1269
1270     return it->value;
1271 }
1272
1273 void AVFWrapper::scheduleDisconnectAndDelete()
1274 {
1275     // Ignore any subsequent notifications we might receive in notificationCallback().
1276     removeFromMap();
1277
1278     dispatch_async_f(dispatchQueue(), this, disconnectAndDeleteAVFWrapper);
1279 }
1280
1281 static void destroyAVFWrapper(void* context)
1282 {
1283     ASSERT(isMainThread());
1284     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1285     AVFWrapper* avfWrapper = static_cast<AVFWrapper*>(context);
1286     if (!avfWrapper)
1287         return;
1288
1289     delete avfWrapper;
1290 }
1291
1292 void AVFWrapper::disconnectAndDeleteAVFWrapper(void* context)
1293 {
1294     AVFWrapper* avfWrapper = static_cast<AVFWrapper*>(context);
1295
1296     LOG(Media, "AVFWrapper::disconnectAndDeleteAVFWrapper(%p)", avfWrapper);
1297
1298     if (avfWrapper->avPlayerItem()) {
1299         CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
1300         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemDidPlayToEndTimeNotification, avfWrapper->avPlayerItem());
1301         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemStatusChangedNotification, avfWrapper->avPlayerItem());
1302         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemTracksChangedNotification, avfWrapper->avPlayerItem());
1303         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemSeekableTimeRangesChangedNotification, avfWrapper->avPlayerItem());
1304         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemLoadedTimeRangesChangedNotification, avfWrapper->avPlayerItem());
1305         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemPresentationSizeChangedNotification, avfWrapper->avPlayerItem());
1306         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification, avfWrapper->avPlayerItem());
1307         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification, avfWrapper->avPlayerItem());
1308         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemIsPlaybackBufferFullChangedNotification, avfWrapper->avPlayerItem());
1309         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), AVCFPlayerItemDurationChangedNotification, avfWrapper->avPlayerItem());
1310         CFNotificationCenterRemoveObserver(center, avfWrapper->callbackContext(), CACFContextNeedsFlushNotification(), 0);
1311     }
1312
1313     if (avfWrapper->avPlayer()) {
1314         if (avfWrapper->timeObserver())
1315             AVCFPlayerRemoveObserver(avfWrapper->avPlayer(), avfWrapper->timeObserver());
1316
1317         CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), avfWrapper->callbackContext(), AVCFPlayerRateChangedNotification, avfWrapper->avPlayer());
1318     }
1319
1320 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1321     AVCFPlayerItemRemoveOutput(avfWrapper->avPlayerItem(), avfWrapper->legibleOutput());
1322 #endif
1323
1324     // We must release the AVCFPlayer and other items on the same thread that created them.
1325     dispatch_async_f(dispatch_get_main_queue(), context, destroyAVFWrapper);
1326 }
1327
1328 void AVFWrapper::createAssetForURL(const String& url, bool inheritURI)
1329 {
1330     ASSERT(!avAsset());
1331
1332     RetainPtr<CFURLRef> urlRef = URL(ParsedURLString, url).createCFURL();
1333
1334     RetainPtr<CFMutableDictionaryRef> optionsRef = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1335
1336     if (inheritURI)
1337         CFDictionarySetValue(optionsRef.get(), AVCFURLAssetInheritURIQueryComponentFromReferencingURIKey, kCFBooleanTrue);
1338
1339     AVCFURLAssetRef assetRef = AVCFURLAssetCreateWithURLAndOptions(kCFAllocatorDefault, urlRef.get(), optionsRef.get(), m_notificationQueue);
1340     m_avAsset = adoptCF(assetRef);
1341 }
1342
1343 void AVFWrapper::createPlayer(IDirect3DDevice9* d3dDevice)
1344 {
1345     ASSERT(isMainThread());
1346     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1347     ASSERT(avPlayerItem());
1348
1349     if (avPlayer())
1350         return;
1351
1352     RetainPtr<CFMutableDictionaryRef> optionsRef = adoptCF(CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
1353
1354     if (d3dDevice) {
1355         // QI for an IDirect3DDevice9Ex interface, it is required to do HW video decoding.
1356         COMPtr<IDirect3DDevice9Ex> d3dEx(Query, d3dDevice);
1357         m_d3dDevice = d3dEx;
1358     } else
1359         m_d3dDevice = 0;
1360
1361     if (m_d3dDevice && AVCFPlayerEnableHardwareAcceleratedVideoDecoderKey)
1362         CFDictionarySetValue(optionsRef.get(), AVCFPlayerEnableHardwareAcceleratedVideoDecoderKey, kCFBooleanTrue);
1363
1364 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1365     CFDictionarySetValue(optionsRef.get(), AVCFPlayerAppliesMediaSelectionCriteriaAutomaticallyKey, kCFBooleanTrue);
1366 #endif
1367
1368     // FIXME: We need a way to create a AVPlayer without an AVPlayerItem, see <rdar://problem/9877730>.
1369     AVCFPlayerRef playerRef = AVCFPlayerCreateWithPlayerItemAndOptions(kCFAllocatorDefault, avPlayerItem(), optionsRef.get(), m_notificationQueue);
1370     m_avPlayer = adoptCF(playerRef);
1371 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1372     AVCFPlayerSetClosedCaptionDisplayEnabled(playerRef, FALSE);
1373 #endif
1374
1375     if (m_d3dDevice && AVCFPlayerSetDirect3DDevicePtr())
1376         AVCFPlayerSetDirect3DDevicePtr()(playerRef, m_d3dDevice.get());
1377
1378     CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
1379     ASSERT(center);
1380
1381     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerRateChangedNotification, playerRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1382
1383     // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but
1384     // our observer will also be called whenever a seek happens.
1385     const double veryLongInterval = 60*60*60*24*30;
1386     m_timeObserver = adoptCF(AVCFPlayerCreatePeriodicTimeObserverForInterval(playerRef, CMTimeMake(veryLongInterval, 10), m_notificationQueue, &periodicTimeObserverCallback, callbackContext()));
1387 }
1388
1389 void AVFWrapper::createPlayerItem()
1390 {
1391     ASSERT(isMainThread());
1392     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1393     ASSERT(avAsset());
1394
1395     if (avPlayerItem())
1396         return;
1397
1398     // Create the player item so we begin loading media data.
1399     AVCFPlayerItemRef itemRef = AVCFPlayerItemCreateWithAsset(kCFAllocatorDefault, avAsset(), m_notificationQueue);
1400     m_avPlayerItem = adoptCF(itemRef);
1401
1402     CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
1403     ASSERT(center);
1404
1405     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemDidPlayToEndTimeNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1406     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemStatusChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1407     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemTracksChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1408     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemSeekableTimeRangesChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1409     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemLoadedTimeRangesChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1410     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemPresentationSizeChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1411     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1412     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1413     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemIsPlaybackBufferFullChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1414     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, AVCFPlayerItemDurationChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
1415     // FIXME: Are there other legible output things we need to register for? asset and hasEnabledAudio are not exposed by AVCF
1416
1417     CFNotificationCenterAddObserver(center, callbackContext(), notificationCallback, CACFContextNeedsFlushNotification(), 0, CFNotificationSuspensionBehaviorDeliverImmediately);
1418
1419 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1420     const CFTimeInterval legibleOutputAdvanceInterval = 2;
1421
1422     m_legibleOutput = adoptCF(AVCFPlayerItemLegibleOutputCreateWithMediaSubtypesForNativeRepresentation(kCFAllocatorDefault, 0));
1423     AVCFPlayerItemOutputSetSuppressPlayerRendering(m_legibleOutput.get(), TRUE);
1424
1425     AVCFPlayerItemLegibleOutputCallbacks callbackInfo;
1426     callbackInfo.version = kAVCFPlayerItemLegibleOutput_CallbacksVersion_1;
1427     ASSERT(callbackContext());
1428     callbackInfo.context = callbackContext();
1429     callbackInfo.legibleOutputCallback = AVFWrapper::legibleOutputCallback;
1430
1431     AVCFPlayerItemLegibleOutputSetCallbacks(m_legibleOutput.get(), &callbackInfo, dispatchQueue());
1432     AVCFPlayerItemLegibleOutputSetAdvanceIntervalForCallbackInvocation(m_legibleOutput.get(), legibleOutputAdvanceInterval);
1433     AVCFPlayerItemLegibleOutputSetTextStylingResolution(m_legibleOutput.get(), AVCFPlayerItemLegibleOutputTextStylingResolutionSourceAndRulesOnly);
1434     AVCFPlayerItemAddOutput(m_avPlayerItem.get(), m_legibleOutput.get());
1435 #endif
1436 }
1437
1438 void AVFWrapper::periodicTimeObserverCallback(AVCFPlayerRef, CMTime cmTime, void* context)
1439 {
1440     MutexLocker locker(mapLock());
1441     AVFWrapper* self = avfWrapperForCallbackContext(context);
1442     if (!self) {
1443         LOG(Media, "AVFWrapper::periodicTimeObserverCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1444         return;
1445     }
1446
1447     double time = std::max(0.0, CMTimeGetSeconds(cmTime)); // Clamp to zero, negative values are sometimes reported.
1448     self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::PlayerTimeChanged, time);
1449 }
1450
1451 struct NotificationCallbackData {
1452     RetainPtr<CFStringRef> m_propertyName;
1453     void* m_context;
1454
1455     NotificationCallbackData(CFStringRef propertyName, void* context)
1456         : m_propertyName(propertyName), m_context(context)
1457     {
1458     }
1459 };
1460
1461 void AVFWrapper::processNotification(void* context)
1462 {
1463     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1464     ASSERT(context);
1465
1466     if (!context)
1467         return;
1468
1469     OwnPtr<NotificationCallbackData> notificationData = adoptPtr(reinterpret_cast<NotificationCallbackData*>(context));
1470
1471     MutexLocker locker(mapLock());
1472     AVFWrapper* self = avfWrapperForCallbackContext(notificationData->m_context);
1473     if (!self) {
1474         LOG(Media, "AVFWrapper::processNotification invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1475         return;
1476     }
1477
1478     CFStringRef propertyName = notificationData->m_propertyName.get();
1479
1480     if (CFEqual(propertyName, AVCFPlayerItemDidPlayToEndTimeNotification))
1481         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
1482     else if (CFEqual(propertyName, AVCFPlayerItemTracksChangedNotification))
1483         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemTracksChanged);
1484     else if (CFEqual(propertyName, AVCFPlayerItemStatusChangedNotification)) {
1485         AVCFURLAssetRef asset = AVCFPlayerItemGetAsset(self->avPlayerItem());
1486         if (asset)
1487             self->setAsset(asset);
1488         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemStatusChanged);
1489     } else if (CFEqual(propertyName, AVCFPlayerItemSeekableTimeRangesChangedNotification))
1490         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemSeekableTimeRangesChanged);
1491     else if (CFEqual(propertyName, AVCFPlayerItemLoadedTimeRangesChangedNotification))
1492         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemLoadedTimeRangesChanged);
1493     else if (CFEqual(propertyName, AVCFPlayerItemPresentationSizeChangedNotification))
1494         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemPresentationSizeChanged);
1495     else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification))
1496         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackLikelyToKeepUpChanged);
1497     else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification))
1498         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackBufferEmptyChanged);
1499     else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackBufferFullChangedNotification))
1500         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackBufferFullChanged);
1501     else if (CFEqual(propertyName, AVCFPlayerRateChangedNotification))
1502         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::PlayerRateChanged);
1503     else if (CFEqual(propertyName, CACFContextNeedsFlushNotification()))
1504         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ContentsNeedsDisplay);
1505     else if (CFEqual(propertyName, AVCFPlayerItemDurationChangedNotification))
1506         self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::DurationChanged);
1507     else
1508         ASSERT_NOT_REACHED();
1509 }
1510
1511 void AVFWrapper::notificationCallback(CFNotificationCenterRef, void* observer, CFStringRef propertyName, const void* object, CFDictionaryRef)
1512 {
1513 #if !LOG_DISABLED
1514     char notificationName[256];
1515     CFStringGetCString(propertyName, notificationName, sizeof(notificationName), kCFStringEncodingASCII);
1516     LOG(Media, "AVFWrapper::notificationCallback(if=%d) %s", reinterpret_cast<uintptr_t>(observer), notificationName);
1517 #endif
1518
1519     OwnPtr<NotificationCallbackData> notificationData = adoptPtr(new NotificationCallbackData(propertyName, observer));
1520
1521     dispatch_async_f(dispatch_get_main_queue(), notificationData.leakPtr(), processNotification);
1522 }
1523
1524 void AVFWrapper::loadPlayableCompletionCallback(AVCFAssetRef, void* context)
1525 {
1526     MutexLocker locker(mapLock());
1527     AVFWrapper* self = avfWrapperForCallbackContext(context);
1528     if (!self) {
1529         LOG(Media, "AVFWrapper::loadPlayableCompletionCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1530         return;
1531     }
1532
1533     LOG(Media, "AVFWrapper::loadPlayableCompletionCallback(%p)", self);
1534     self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
1535 }
1536
1537 void AVFWrapper::checkPlayability()
1538 {
1539     LOG(Media, "AVFWrapper::checkPlayability(%p)", this);
1540
1541     static CFArrayRef propertyKeyName;
1542     if (!propertyKeyName) {
1543         static const CFStringRef keyNames[] = { 
1544             AVCFAssetPropertyPlayable
1545         };
1546         propertyKeyName = CFArrayCreate(0, (const void**)keyNames, sizeof(keyNames) / sizeof(keyNames[0]), &kCFTypeArrayCallBacks);
1547     }
1548
1549     AVCFAssetLoadValuesAsynchronouslyForProperties(avAsset(), propertyKeyName, loadPlayableCompletionCallback, callbackContext());
1550 }
1551
1552 void AVFWrapper::loadMetadataCompletionCallback(AVCFAssetRef, void* context)
1553 {
1554     MutexLocker locker(mapLock());
1555     AVFWrapper* self = avfWrapperForCallbackContext(context);
1556     if (!self) {
1557         LOG(Media, "AVFWrapper::loadMetadataCompletionCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1558         return;
1559     }
1560
1561     LOG(Media, "AVFWrapper::loadMetadataCompletionCallback(%p)", self);
1562     self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
1563 }
1564
1565 void AVFWrapper::beginLoadingMetadata()
1566 {
1567     ASSERT(avAsset());
1568     LOG(Media, "AVFWrapper::beginLoadingMetadata(%p) - requesting metadata loading", this);
1569     AVCFAssetLoadValuesAsynchronouslyForProperties(avAsset(), metadataKeyNames(), loadMetadataCompletionCallback, callbackContext());
1570 }
1571
1572 void AVFWrapper::seekCompletedCallback(AVCFPlayerItemRef, Boolean finished, void* context)
1573 {
1574     MutexLocker locker(mapLock());
1575     AVFWrapper* self = avfWrapperForCallbackContext(context);
1576     if (!self) {
1577         LOG(Media, "AVFWrapper::seekCompletedCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1578         return;
1579     }
1580
1581     LOG(Media, "AVFWrapper::seekCompletedCallback(%p)", self);
1582     self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::SeekCompleted, static_cast<bool>(finished));
1583 }
1584
1585 void AVFWrapper::seekToTime(double time, double negativeTolerance, double positiveTolerance)
1586 {
1587     ASSERT(avPlayerItem());
1588     CMTime cmTime = CMTimeMakeWithSeconds(time, 600);
1589     CMTime cmBefore = CMTimeMakeWithSeconds(negativeTolerance, 600);
1590     CMTime cmAfter = CMTimeMakeWithSeconds(positiveTolerance, 600);
1591     AVCFPlayerItemSeekToTimeWithToleranceAndCompletionCallback(avPlayerItem(), cmTime, cmBefore, cmAfter, &seekCompletedCallback, callbackContext());
1592 }
1593
1594 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP) && HAVE(AVFOUNDATION_LEGIBLE_OUTPUT_SUPPORT)
1595 struct LegibleOutputData {
1596     RetainPtr<CFArrayRef> m_attributedStrings;
1597     RetainPtr<CFArrayRef> m_samples;
1598     double m_time;
1599     void* m_context;
1600
1601     LegibleOutputData(CFArrayRef strings, CFArrayRef samples, double time, void* context)
1602         : m_attributedStrings(strings), m_samples(samples), m_time(time), m_context(context)
1603     {
1604     }
1605 };
1606
1607 void AVFWrapper::processCue(void* context)
1608 {
1609     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1610     ASSERT(context);
1611
1612     if (!context)
1613         return;
1614
1615     OwnPtr<LegibleOutputData> legibleOutputData = adoptPtr(reinterpret_cast<LegibleOutputData*>(context));
1616
1617     MutexLocker locker(mapLock());
1618     AVFWrapper* self = avfWrapperForCallbackContext(legibleOutputData->m_context);
1619     if (!self) {
1620         LOG(Media, "AVFWrapper::processCue invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1621         return;
1622     }
1623
1624     if (!self->m_currentTextTrack)
1625         return;
1626
1627     self->m_currentTextTrack->processCue(legibleOutputData->m_attributedStrings.get(), legibleOutputData->m_samples.get(), legibleOutputData->m_time);
1628 }
1629
1630 void AVFWrapper::legibleOutputCallback(void* context, AVCFPlayerItemLegibleOutputRef legibleOutput, CFArrayRef attributedStrings, CFArrayRef nativeSampleBuffers, CMTime itemTime)
1631 {
1632     ASSERT(dispatch_get_main_queue() != dispatch_get_current_queue());
1633     MutexLocker locker(mapLock());
1634     AVFWrapper* self = avfWrapperForCallbackContext(context);
1635     if (!self) {
1636         LOG(Media, "AVFWrapper::legibleOutputCallback invoked for deleted AVFWrapper %d", reinterpret_cast<uintptr_t>(context));
1637         return;
1638     }
1639
1640     LOG(Media, "AVFWrapper::legibleOutputCallback(%p)", self);
1641
1642     ASSERT(legibleOutput == self->m_legibleOutput);
1643
1644     OwnPtr<LegibleOutputData> legibleOutputData = adoptPtr(new LegibleOutputData(attributedStrings, nativeSampleBuffers, CMTimeGetSeconds(itemTime), context));
1645
1646     dispatch_async_f(dispatch_get_main_queue(), legibleOutputData.leakPtr(), processCue);
1647 }
1648 #endif
1649
1650 void AVFWrapper::setAsset(AVCFURLAssetRef asset)
1651 {
1652     if (asset == avAsset())
1653         return;
1654
1655     AVCFAssetCancelLoading(avAsset());
1656     m_avAsset = adoptCF(asset);
1657 }
1658
1659 PlatformLayer* AVFWrapper::platformLayer()
1660 {
1661     if (m_videoLayerWrapper)
1662         return m_videoLayerWrapper->platformLayer();
1663
1664     if (!videoLayer())
1665         return 0;
1666
1667     // Create a PlatformCALayer so we can resize the video layer to match the element size.
1668     m_layerClient = adoptPtr(new LayerClient(this));
1669     if (!m_layerClient)
1670         return 0;
1671
1672     m_videoLayerWrapper = PlatformCALayerWin::create(PlatformCALayer::LayerTypeLayer, m_layerClient.get());
1673     if (!m_videoLayerWrapper)
1674         return 0;
1675
1676     m_caVideoLayer = adoptCF(AVCFPlayerLayerCopyCACFLayer(m_avCFVideoLayer.get()));
1677
1678     CACFLayerInsertSublayer(m_videoLayerWrapper->platformLayer(), m_caVideoLayer.get(), 0);
1679     m_videoLayerWrapper->setAnchorPoint(FloatPoint3D());
1680     m_videoLayerWrapper->setNeedsLayout();
1681     updateVideoLayerGravity();
1682
1683     return m_videoLayerWrapper->platformLayer();
1684 }
1685
1686 void AVFWrapper::createAVCFVideoLayer()
1687 {
1688     ASSERT(isMainThread());
1689     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1690     if (!avPlayer() || m_avCFVideoLayer)
1691         return;
1692
1693     // The layer will get hooked up via RenderLayerBacking::updateConfiguration().
1694     m_avCFVideoLayer = adoptCF(AVCFPlayerLayerCreateWithAVCFPlayer(kCFAllocatorDefault, avPlayer(), m_notificationQueue));
1695     LOG(Media, "AVFWrapper::createAVCFVideoLayer(%p) - returning %p", this, videoLayer());
1696 }
1697
1698 void AVFWrapper::destroyVideoLayer()
1699 {
1700     ASSERT(isMainThread());
1701     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1702     LOG(Media, "AVFWrapper::destroyVideoLayer(%p)", this);
1703     m_layerClient = nullptr;
1704     m_caVideoLayer = 0;
1705     m_videoLayerWrapper = 0;
1706     if (!m_avCFVideoLayer.get())
1707         return;
1708
1709     AVCFPlayerLayerSetPlayer((AVCFPlayerLayerRef)m_avCFVideoLayer.get(), 0);
1710     m_avCFVideoLayer = 0;
1711 }
1712
1713 void AVFWrapper::setVideoLayerNeedsCommit()
1714 {
1715     if (m_videoLayerWrapper)
1716         m_videoLayerWrapper->setNeedsCommit();
1717 }
1718
1719 void AVFWrapper::setVideoLayerHidden(bool value)
1720 {
1721     if (m_videoLayerWrapper)
1722         m_videoLayerWrapper->setHidden(value);
1723 }
1724
1725 void AVFWrapper::createImageGenerator()
1726 {
1727     ASSERT(isMainThread());
1728     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1729     if (!avAsset() || m_imageGenerator)
1730         return;
1731
1732     m_imageGenerator = adoptCF(AVCFAssetImageGeneratorCreateWithAsset(kCFAllocatorDefault, avAsset()));
1733
1734     AVCFAssetImageGeneratorSetApertureMode(m_imageGenerator.get(), AVCFAssetImageGeneratorApertureModeCleanAperture);
1735     AVCFAssetImageGeneratorSetRequestedTimeToleranceBefore(m_imageGenerator.get(), kCMTimeZero);
1736     AVCFAssetImageGeneratorSetRequestedTimeToleranceAfter(m_imageGenerator.get(), kCMTimeZero);
1737     AVCFAssetImageGeneratorSetAppliesPreferredTrackTransform(m_imageGenerator.get(), true);
1738
1739     LOG(Media, "AVFWrapper::createImageGenerator(%p) - returning %p", this, m_imageGenerator.get());
1740 }
1741
1742 void AVFWrapper::destroyImageGenerator()
1743 {
1744     ASSERT(isMainThread());
1745     ASSERT(dispatch_get_main_queue() == dispatch_get_current_queue());
1746     LOG(Media, "AVFWrapper::destroyImageGenerator(%p)", this);
1747     m_imageGenerator = 0;
1748 }
1749
1750 RetainPtr<CGImageRef> AVFWrapper::createImageForTimeInRect(float time, const IntRect& rect)
1751 {
1752     if (!m_imageGenerator)
1753         return 0;
1754
1755 #if !LOG_DISABLED
1756     double start = monotonicallyIncreasingTime();
1757 #endif
1758
1759     AVCFAssetImageGeneratorSetMaximumSize(m_imageGenerator.get(), CGSize(rect.size()));
1760     RetainPtr<CGImageRef> rawimage = adoptCF(AVCFAssetImageGeneratorCopyCGImageAtTime(m_imageGenerator.get(), CMTimeMakeWithSeconds(time, 600), 0, 0));
1761     RetainPtr<CGImageRef> image = adoptCF(CGImageCreateCopyWithColorSpace(rawimage.get(), adoptCF(CGColorSpaceCreateDeviceRGB()).get()));
1762
1763 #if !LOG_DISABLED
1764     double duration = monotonicallyIncreasingTime() - start;
1765     LOG(Media, "AVFWrapper::createImageForTimeInRect(%p) - creating image took %.4f", this, narrowPrecisionToFloat(duration));
1766 #endif
1767
1768     return image;
1769 }
1770
1771 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
1772 AVCFMediaSelectionGroupRef AVFWrapper::safeMediaSelectionGroupForLegibleMedia() const
1773 {
1774     if (!avAsset())
1775         return 0;
1776
1777     if (AVCFAssetGetStatusOfValueForProperty(avAsset(), AVCFAssetPropertyAvailableMediaCharacteristicsWithMediaSelectionOptions, 0) != AVCFPropertyValueStatusLoaded)
1778         return 0;
1779
1780     return AVCFAssetGetSelectionGroupForMediaCharacteristic(avAsset(), AVCFMediaCharacteristicLegible);
1781 }
1782 #endif
1783
1784 void AVFWrapper::updateVideoLayerGravity()
1785 {
1786     // We should call AVCFPlayerLayerSetVideoGravity() here, but it is not yet implemented.
1787     // FIXME: <rdar://problem/14884340>
1788 }
1789
1790 void LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* wrapperLayer)
1791 {
1792     ASSERT(m_parent);
1793     ASSERT(m_parent->videoLayerWrapper() == wrapperLayer->platformLayer());
1794
1795     CGRect bounds = wrapperLayer->bounds();
1796     CGPoint anchor = CACFLayerGetAnchorPoint(m_parent->caVideoLayer());
1797     FloatPoint position(bounds.size.width * anchor.x, bounds.size.height * anchor.y); 
1798
1799     CACFLayerSetPosition(m_parent->caVideoLayer(), position);
1800     CACFLayerSetBounds(m_parent->caVideoLayer(), bounds);
1801
1802     AVCFPlayerLayerSetFrame(m_parent->videoLayer(), CGRectMake(0, 0, bounds.size.width, bounds.size.height));
1803 }
1804
1805 } // namespace WebCore
1806
1807 #else
1808 // AVFoundation should always be enabled for Apple production builds.
1809 #if __PRODUCTION__ && !USE(AVFOUNDATION)
1810 #error AVFoundation is not enabled!
1811 #endif // __PRODUCTION__ && !USE(AVFOUNDATION)
1812 #endif // USE(AVFOUNDATION)
1813 #endif // PLATFORM(WIN) && ENABLE(VIDEO)