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