Adopt AVCF media back end on Windows
authorjeffm@apple.com <jeffm@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Aug 2011 20:06:48 +0000 (20:06 +0000)
committerjeffm@apple.com <jeffm@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 4 Aug 2011 20:06:48 +0000 (20:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=65400
<rdar://problem/9894105>

First cut at implementation in MediaPlayerPrivateAVFoundationCF.cpp/.h, based on
work by Eric Carlson. Note that use of AVFoundation is determined at runtime, and
defaults to off.

Reviewed by Darin Adler.

No new tests, uses existing media tests.

* config.h: Turn on WTF_USE_AVFOUNDATION on Windows if AVFoundationCF is available.

* platform/graphics/MediaPlayer.cpp:
(WebCore::installedMediaEngines): Register MediaPlayerPrivateAVFoundationCF on Windows.
* platform/graphics/MediaPlayer.h: Add support for an AVCFPlayer.

* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
(WebCore::MediaPlayerPrivateAVFoundation::setPreload): Workaround an AVCF limitation that prevents an AVCFPlayer from being created without an AVCFItem.
(WebCore::MediaPlayerPrivateAVFoundation::dispatchNotification): Added support for ContentsNeedsDisplay notification.

* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
(WebCore::MediaPlayerPrivateAVFoundation::contentsNeedsDisplay): Added support for ContentsNeedsDisplay notification.

* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationCF.cpp: Added implementation.
(WebCore::AVFWrapper::caVideoLayer):
(WebCore::AVFWrapper::videoLayerWrapper):
(WebCore::AVFWrapper::videoLayer):
(WebCore::AVFWrapper::avPlayer):
(WebCore::AVFWrapper::avAsset):
(WebCore::AVFWrapper::avPlayerItem):
(WebCore::AVFWrapper::timeObserver):
(WebCore::AVFWrapper::imageGenerator):
(WebCore::AVFWrapper::dispatchQueue):
(WebCore::LayerClient::LayerClient):
(WebCore::LayerClient::~LayerClient):
(WebCore::LayerClient::platformCALayerRespondsToLayoutChanges):
(WebCore::LayerClient::platformCALayerAnimationStarted):
(WebCore::LayerClient::platformCALayerContentsOrientation):
(WebCore::LayerClient::platformCALayerPaintContents):
(WebCore::LayerClient::platformCALayerShowDebugBorders):
(WebCore::LayerClient::platformCALayerShowRepaintCounter):
(WebCore::LayerClient::platformCALayerIncrementRepaintCount):
(WebCore::LayerClient::platformCALayerContentsOpaque):
(WebCore::LayerClient::platformCALayerDrawsContent):
(WebCore::LayerClient::platformCALayerLayerDidDisplay):
(WebCore::boolString):
(WebCore::metadataKeyNames):
(WebCore::CMTimeRangeStartKey):
(WebCore::CMTimeRangeDurationKey):
(WebCore::CACFContextNeedsFlushNotification):
(WebCore::videoLayer):
(WebCore::avPlayer):
(WebCore::avAsset):
(WebCore::avPlayerItem):
(WebCore::imageGenerator):
(WebCore::MediaPlayerPrivateAVFoundationCF::create):
(WebCore::MediaPlayerPrivateAVFoundationCF::registerMediaEngine):
(WebCore::MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF):
(WebCore::MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF):
(WebCore::MediaPlayerPrivateAVFoundationCF::cancelLoad):
(WebCore::MediaPlayerPrivateAVFoundationCF::hasLayerRenderer):
(WebCore::MediaPlayerPrivateAVFoundationCF::hasContextRenderer):
(WebCore::MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer):
(WebCore::MediaPlayerPrivateAVFoundationCF::destroyContextVideoRenderer):
(WebCore::MediaPlayerPrivateAVFoundationCF::createVideoLayer):
(WebCore::MediaPlayerPrivateAVFoundationCF::destroyVideoLayer):
(WebCore::MediaPlayerPrivateAVFoundationCF::hasAvailableVideoFrame):
(WebCore::MediaPlayerPrivateAVFoundationCF::createAVPlayer):
(WebCore::MediaPlayerPrivateAVFoundationCF::createAVPlayerItem):
(WebCore::MediaPlayerPrivateAVFoundationCF::createAVAssetForURL):
(WebCore::MediaPlayerPrivateAVFoundationCF::checkPlayability):
(WebCore::MediaPlayerPrivateAVFoundationCF::beginLoadingMetadata):
(WebCore::MediaPlayerPrivateAVFoundationCF::playerItemStatus):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformMedia):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformLayer):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformSetVisible):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformPlay):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformPause):
(WebCore::MediaPlayerPrivateAVFoundationCF::updateRate):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformDuration):
(WebCore::MediaPlayerPrivateAVFoundationCF::currentTime):
(WebCore::MediaPlayerPrivateAVFoundationCF::seekToTime):
(WebCore::MediaPlayerPrivateAVFoundationCF::setVolume):
(WebCore::MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible):
(WebCore::MediaPlayerPrivateAVFoundationCF::rate):
(WebCore::timeRangeIsValidAndNotEmpty):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformBufferedTimeRanges):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformMaxTimeSeekable):
(WebCore::MediaPlayerPrivateAVFoundationCF::platformMaxTimeLoaded):
(WebCore::MediaPlayerPrivateAVFoundationCF::totalBytes):
(WebCore::MediaPlayerPrivateAVFoundationCF::assetStatus):
(WebCore::MediaPlayerPrivateAVFoundationCF::paintCurrentFrameInContext):
(WebCore::MediaPlayerPrivateAVFoundationCF::paint):
(WebCore::mimeTypeCache):
(WebCore::MediaPlayerPrivateAVFoundationCF::getSupportedTypes):
(WebCore::MediaPlayerPrivateAVFoundationCF::supportsType):
(WebCore::MediaPlayerPrivateAVFoundationCF::isAvailable):
(WebCore::MediaPlayerPrivateAVFoundationCF::mediaTimeForTimeValue):
(WebCore::MediaPlayerPrivateAVFoundationCF::tracksChanged):
(WebCore::MediaPlayerPrivateAVFoundationCF::sizeChanged):
(WebCore::MediaPlayerPrivateAVFoundationCF::contentsNeedsDisplay):
(WebCore::AVFWrapper::AVFWrapper):
(WebCore::AVFWrapper::~AVFWrapper):
(WebCore::AVFWrapper::scheduleDisconnectAndDelete):
(WebCore::AVFWrapper::disconnectAndDeleteAVFWrapper):
(WebCore::AVFWrapper::deleteAVFWrapper):
(WebCore::AVFWrapper::createAssetForURL):
(WebCore::AVFWrapper::createPlayer):
(WebCore::AVFWrapper::createPlayerItem):
(WebCore::AVFWrapper::periodicTimeObserverCallback):
(WebCore::AVFWrapper::notificationCallback):
(WebCore::AVFWrapper::loadPlayableCompletionCallback):
(WebCore::AVFWrapper::checkPlayability):
(WebCore::AVFWrapper::loadMetadataCompletionCallback):
(WebCore::AVFWrapper::beginLoadingMetadata):
(WebCore::AVFWrapper::seekCompletedCallback):
(WebCore::AVFWrapper::seekToTime):
(WebCore::AVFWrapper::setAsset):
(WebCore::AVFWrapper::platformLayer):
(WebCore::AVFWrapper::createAVCFVideoLayer):
(WebCore::AVFWrapper::destroyVideoLayer):
(WebCore::AVFWrapper::setVideoLayerNeedsCommit):
(WebCore::AVFWrapper::setVideoLayerHidden):
(WebCore::AVFWrapper::createImageGenerator):
(WebCore::AVFWrapper::destroyImageGenerator):
(WebCore::AVFWrapper::createImageForTimeInRect):
(WebCore::LayerClient::platformCALayerLayoutSublayersOfLayer):

* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationCF.h: Added implementation.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@92404 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/config.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationCF.cpp
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationCF.h

index 59b803b..94f91c0 100644 (file)
@@ -1,3 +1,137 @@
+2011-08-04  Jeff Miller  <jeffm@apple.com>
+
+        Adopt AVCF media back end on Windows
+        https://bugs.webkit.org/show_bug.cgi?id=65400
+        <rdar://problem/9894105>
+        
+        First cut at implementation in MediaPlayerPrivateAVFoundationCF.cpp/.h, based on
+        work by Eric Carlson. Note that use of AVFoundation is determined at runtime, and
+        defaults to off.
+
+        Reviewed by Darin Adler.
+
+        No new tests, uses existing media tests.
+
+        * config.h: Turn on WTF_USE_AVFOUNDATION on Windows if AVFoundationCF is available.
+        
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::installedMediaEngines): Register MediaPlayerPrivateAVFoundationCF on Windows.
+        * platform/graphics/MediaPlayer.h: Add support for an AVCFPlayer.
+        
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
+        (WebCore::MediaPlayerPrivateAVFoundation::setPreload): Workaround an AVCF limitation that prevents an AVCFPlayer from being created without an AVCFItem.
+        (WebCore::MediaPlayerPrivateAVFoundation::dispatchNotification): Added support for ContentsNeedsDisplay notification.
+        
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
+        (WebCore::MediaPlayerPrivateAVFoundation::contentsNeedsDisplay): Added support for ContentsNeedsDisplay notification.
+        
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationCF.cpp: Added implementation.
+        (WebCore::AVFWrapper::caVideoLayer):
+        (WebCore::AVFWrapper::videoLayerWrapper):
+        (WebCore::AVFWrapper::videoLayer):
+        (WebCore::AVFWrapper::avPlayer):
+        (WebCore::AVFWrapper::avAsset):
+        (WebCore::AVFWrapper::avPlayerItem):
+        (WebCore::AVFWrapper::timeObserver):
+        (WebCore::AVFWrapper::imageGenerator):
+        (WebCore::AVFWrapper::dispatchQueue):
+        (WebCore::LayerClient::LayerClient):
+        (WebCore::LayerClient::~LayerClient):
+        (WebCore::LayerClient::platformCALayerRespondsToLayoutChanges):
+        (WebCore::LayerClient::platformCALayerAnimationStarted):
+        (WebCore::LayerClient::platformCALayerContentsOrientation):
+        (WebCore::LayerClient::platformCALayerPaintContents):
+        (WebCore::LayerClient::platformCALayerShowDebugBorders):
+        (WebCore::LayerClient::platformCALayerShowRepaintCounter):
+        (WebCore::LayerClient::platformCALayerIncrementRepaintCount):
+        (WebCore::LayerClient::platformCALayerContentsOpaque):
+        (WebCore::LayerClient::platformCALayerDrawsContent):
+        (WebCore::LayerClient::platformCALayerLayerDidDisplay):
+        (WebCore::boolString):
+        (WebCore::metadataKeyNames):
+        (WebCore::CMTimeRangeStartKey):
+        (WebCore::CMTimeRangeDurationKey):
+        (WebCore::CACFContextNeedsFlushNotification):
+        (WebCore::videoLayer):
+        (WebCore::avPlayer):
+        (WebCore::avAsset):
+        (WebCore::avPlayerItem):
+        (WebCore::imageGenerator):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::create):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::registerMediaEngine):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::cancelLoad):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::hasLayerRenderer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::hasContextRenderer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::destroyContextVideoRenderer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::createVideoLayer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::destroyVideoLayer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::hasAvailableVideoFrame):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::createAVPlayer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::createAVPlayerItem):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::createAVAssetForURL):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::checkPlayability):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::beginLoadingMetadata):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::playerItemStatus):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformMedia):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformLayer):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformSetVisible):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformPlay):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformPause):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::updateRate):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformDuration):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::currentTime):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::seekToTime):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::setVolume):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::rate):
+        (WebCore::timeRangeIsValidAndNotEmpty):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformBufferedTimeRanges):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformMaxTimeSeekable):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::platformMaxTimeLoaded):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::totalBytes):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::assetStatus):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::paintCurrentFrameInContext):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::paint):
+        (WebCore::mimeTypeCache):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::getSupportedTypes):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::supportsType):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::isAvailable):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::mediaTimeForTimeValue):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::tracksChanged):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::sizeChanged):
+        (WebCore::MediaPlayerPrivateAVFoundationCF::contentsNeedsDisplay):
+        (WebCore::AVFWrapper::AVFWrapper):
+        (WebCore::AVFWrapper::~AVFWrapper):
+        (WebCore::AVFWrapper::scheduleDisconnectAndDelete):
+        (WebCore::AVFWrapper::disconnectAndDeleteAVFWrapper):
+        (WebCore::AVFWrapper::deleteAVFWrapper):
+        (WebCore::AVFWrapper::createAssetForURL):
+        (WebCore::AVFWrapper::createPlayer):
+        (WebCore::AVFWrapper::createPlayerItem):
+        (WebCore::AVFWrapper::periodicTimeObserverCallback):
+        (WebCore::AVFWrapper::notificationCallback):
+        (WebCore::AVFWrapper::loadPlayableCompletionCallback):
+        (WebCore::AVFWrapper::checkPlayability):
+        (WebCore::AVFWrapper::loadMetadataCompletionCallback):
+        (WebCore::AVFWrapper::beginLoadingMetadata):
+        (WebCore::AVFWrapper::seekCompletedCallback):
+        (WebCore::AVFWrapper::seekToTime):
+        (WebCore::AVFWrapper::setAsset):
+        (WebCore::AVFWrapper::platformLayer):
+        (WebCore::AVFWrapper::createAVCFVideoLayer):
+        (WebCore::AVFWrapper::destroyVideoLayer):
+        (WebCore::AVFWrapper::setVideoLayerNeedsCommit):
+        (WebCore::AVFWrapper::setVideoLayerHidden):
+        (WebCore::AVFWrapper::createImageGenerator):
+        (WebCore::AVFWrapper::destroyImageGenerator):
+        (WebCore::AVFWrapper::createImageForTimeInRect):
+        (WebCore::LayerClient::platformCALayerLayoutSublayersOfLayer):
+
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundationCF.h: Added implementation.
+
 2011-08-04  Jochen Eisinger  <jochen@chromium.org>
 
         Get rid of ResourceRequestBase::m_targetType. Move it to chromium's ResourceRequest.
index c547b3c..63e3d6a 100644 (file)
@@ -226,6 +226,5 @@ typedef float CGFloat;
 #endif
 
 #if PLATFORM(WIN) && HAVE(AVCF)
-/// FIXME: Adopt AVCF media back end on Windows http://webkit.org/b/65400
-#define WTF_USE_AVFOUNDATION 0
+#define WTF_USE_AVFOUNDATION 1
 #endif
index 4864b3f..4d535b6 100644 (file)
@@ -59,6 +59,9 @@
 #elif PLATFORM(WIN)
 #include "MediaPlayerPrivateQuickTimeVisualContext.h"
 #define PlatformMediaEngineClassName MediaPlayerPrivateQuickTimeVisualContext
+#if USE(AVFOUNDATION)
+#include "MediaPlayerPrivateAVFoundationCF.h"
+#endif
 #elif PLATFORM(QT)
 #if USE(QT_MULTIMEDIA) && !USE(GSTREAMER)
 #include "MediaPlayerPrivateQt.h"
@@ -188,9 +191,14 @@ static Vector<MediaPlayerFactory*>& installedMediaEngines()
         MediaPlayerPrivateGStreamer::registerMediaEngine(addMediaEngine);
 #endif
 
-#if USE(AVFOUNDATION) && PLATFORM(MAC)
-        if (Settings::isAVFoundationEnabled())
+#if USE(AVFOUNDATION)
+        if (Settings::isAVFoundationEnabled()) {
+#if PLATFORM(MAC)
             MediaPlayerPrivateAVFoundationObjC::registerMediaEngine(addMediaEngine);
+#elif PLATFORM(WIN)
+            MediaPlayerPrivateAVFoundationCF::registerMediaEngine(addMediaEngine);
+#endif
+        }
 #endif
 
 #if !PLATFORM(GTK) && !PLATFORM(EFL) && !(PLATFORM(QT) && USE(GSTREAMER))
index b022d84..80076ce 100644 (file)
@@ -52,6 +52,7 @@
 class AVPlayer;
 class QTMovie;
 #endif
+class AVCFPlayer;
 class QTMovieGWorld;
 class QTMovieVisualContext;
 
@@ -74,6 +75,7 @@ struct PlatformMedia {
         ChromiumMediaPlayerType,
         QtMediaPlayerType,
         AVFoundationMediaPlayerType,
+        AVFoundationCFMediaPlayerType
     } type;
 
     union {
@@ -84,6 +86,7 @@ struct PlatformMedia {
         MediaPlayerPrivateInterface* chromiumMediaPlayer;
         MediaPlayerPrivateInterface* qtMediaPlayer;
         AVPlayer* avfMediaPlayer;
+        AVCFPlayer* avcfMediaPlayer;
     } media;
 };
 
index 1eaf9a8..3779349 100644 (file)
@@ -618,14 +618,23 @@ void MediaPlayerPrivateAVFoundation::setPreload(MediaPlayer::Preload preload)
 
     if (m_preload >= MediaPlayer::MetaData && assetStatus() == MediaPlayerAVAssetStatusDoesNotExist) {
         createAVAssetForURL(m_assetURL);
+        // FIXME: Remove this Windows-specific code when <rdar://problem/9877730> is fixed, until then
+        // we can't create an AVPlayer without an AVPlayerItem on Windows, so we always have to create
+        // the item first.
+#if PLATFORM(WIN)
+        createAVPlayerItem();
+#endif
         createAVPlayer();
         checkPlayability();
     }
 
+    // FIXME: Enable this code on Windows when <rdar://problem/9877730> is fixed.
+#if PLATFORM(MAC)
     // Don't force creation of the player item unless we already know that the asset is playable. If we aren't
     // there yet, or if we already know it is not playable, creating it now won't help.
     if (m_preload == MediaPlayer::Auto && m_assetIsPlayable)
         createAVPlayerItem();
+#endif
 
     setDelayCallbacks(false);
 }
@@ -769,6 +778,9 @@ void MediaPlayerPrivateAVFoundation::dispatchNotification()
     case Notification::DurationChanged:
         invalidateCachedDuration();
         break;
+    case Notification::ContentsNeedsDisplay:
+        contentsNeedsDisplay();
+        break;
 
     case Notification::None:
         ASSERT_NOT_REACHED();
index 29aee98..c8fae3d 100644 (file)
@@ -47,6 +47,7 @@ public:
     virtual void timeChanged(double);
     virtual void seekCompleted(bool);
     virtual void didEnd();
+    virtual void contentsNeedsDisplay() { }
 
     class Notification {
     public:
@@ -67,6 +68,7 @@ public:
             PlayerTimeChanged,
             SeekCompleted,
             DurationChanged,
+            ContentsNeedsDisplay,
         };
         
         Notification()
@@ -238,6 +240,7 @@ protected:
     void invalidateCachedDuration();
 
     const String& assetURL() const { return m_assetURL; }
+
 private:
     MediaPlayer* m_player;
 
@@ -275,7 +278,8 @@ private:
     bool m_playWhenFramesAvailable;
 };
 
-}
+} // namespace WebCore
 
-#endif
-#endif
+#endif // ENABLE(VIDEO) && USE(AVFOUNDATION)
+
+#endif // MediaPlayerPrivateAVFoundation_h
index 84cb404..05f7a0c 100644 (file)
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
+
+#include "config.h"
+
+#if PLATFORM(WIN) && ENABLE(VIDEO) && USE(AVFOUNDATION)
+
+#include "MediaPlayerPrivateAVFoundationCF.h"
+
+#include "ApplicationCacheResource.h"
+#include "FloatConversion.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "PlatformCALayer.h"
+#include "SoftLinking.h"
+#include "TimeRanges.h"
+
+#include <AVFoundationCF/AVCFPlayerLayer.h>
+#include <AVFoundationCF/AVFoundationCF.h>
+#include <CoreMedia/CoreMedia.h>
+
+#include <delayimp.h>
+#include <dispatch/dispatch.h>
+#include <wtf/Threading.h>
+#include <wtf/UnusedParam.h>
+
+#ifdef DEBUG_ALL
+// FIXME: AVFoundationCF doesn't currently deliver a debug version.
+#pragma comment(lib, "AVFoundationCF.lib")
+#pragma comment(lib, "CoreMedia_debug.lib")
+#pragma comment(lib, "libdispatch_debug.lib")
+
+// Use the soft link macros so we can easily test for the existence of the dlls.
+// FIXME: AVFoundationCF doesn't currently deliver a debug version.
+SOFT_LINK_LIBRARY(AVFoundationCF)
+SOFT_LINK_DEBUG_LIBRARY(CoreMedia)
+SOFT_LINK_DEBUG_LIBRARY(libdispatch)
+#else
+#pragma comment(lib, "AVFoundationCF.lib")
+#pragma comment(lib, "CoreMedia.lib")
+#pragma comment(lib, "libdispatch.lib")
+
+// Use the soft link macros so we can easily test for the existence of the dlls.
+SOFT_LINK_LIBRARY(AVFoundationCF)
+SOFT_LINK_LIBRARY(CoreMedia)
+SOFT_LINK_LIBRARY(libdispatch)
+#endif // DEBUG_ALL
+
+using namespace std;
+
+namespace WebCore {
+
+class LayerClient;
+
+class AVFWrapper {
+public:
+    AVFWrapper(MediaPlayerPrivateAVFoundationCF*);
+    ~AVFWrapper();
+
+    void scheduleDisconnectAndDelete();
+
+    void createAVCFVideoLayer();
+    void destroyVideoLayer();
+    PlatformLayer* platformLayer();
+
+    CACFLayerRef caVideoLayer() { return m_caVideoLayer.get(); }
+    PlatformLayer* videoLayerWrapper() { return m_videoLayerWrapper ? m_videoLayerWrapper->platformLayer() : 0; };
+    void setVideoLayerNeedsCommit();
+    void setVideoLayerHidden(bool);
+
+    void createImageGenerator();
+    void destroyImageGenerator();
+    RetainPtr<CGImageRef> createImageForTimeInRect(float, const IntRect&);
+
+    void createAssetForURL(const String& url);
+    void setAsset(AVCFURLAssetRef);
+    
+    void createPlayer();
+    void createPlayerItem();
+    
+    void checkPlayability();
+    void beginLoadingMetadata();
+    
+    void seekToTime(float);
+
+    static void loadMetadataCompletionCallback(AVCFAssetRef, void*);
+    static void loadPlayableCompletionCallback(AVCFAssetRef, void*);
+    static void periodicTimeObserverCallback(AVCFPlayerRef, CMTime, void*);
+    static void seekCompletedCallback(AVCFPlayerItemRef, Boolean, void*);
+    static void notificationCallback(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef);
+
+    inline AVCFPlayerLayerRef videoLayer() const { return (AVCFPlayerLayerRef)m_avCFVideoLayer.get(); }
+    inline AVCFPlayerRef avPlayer() const { return (AVCFPlayerRef)m_avPlayer.get(); }
+    inline AVCFURLAssetRef avAsset() const { return (AVCFURLAssetRef)m_avAsset.get(); }
+    inline AVCFPlayerItemRef avPlayerItem() const { return (AVCFPlayerItemRef)m_avPlayerItem.get(); }
+    inline AVCFPlayerObserverRef timeObserver() const { return (AVCFPlayerObserverRef)m_timeObserver.get(); }
+    inline AVCFAssetImageGeneratorRef imageGenerator() const { return m_imageGenerator.get(); }
+    inline dispatch_queue_t dispatchQueue() const { return m_notificationQueue; }
+
+private:
+    static void disconnectAndDeleteAVFWrapper(void*);
+    static void deleteAVFWrapper(void*);
+
+    MediaPlayerPrivateAVFoundationCF* m_owner;
+
+    RetainPtr<AVCFPlayerRef> m_avPlayer;
+    RetainPtr<AVCFURLAssetRef> m_avAsset;
+    RetainPtr<AVCFPlayerItemRef> m_avPlayerItem;
+    RetainPtr<AVCFPlayerLayerRef> m_avCFVideoLayer;
+    RetainPtr<AVCFPlayerObserverRef> m_timeObserver;
+    RetainPtr<AVCFAssetImageGeneratorRef> m_imageGenerator;
+    dispatch_queue_t m_notificationQueue;
+
+    mutable RetainPtr<CACFLayerRef> m_caVideoLayer;
+    RefPtr<PlatformCALayer> m_videoLayerWrapper;
+
+    OwnPtr<LayerClient> m_layerClient;
+};
+
+class LayerClient : public PlatformCALayerClient {
+public:
+    LayerClient(AVFWrapper* parent) : m_parent(parent) { }
+    virtual ~LayerClient() { m_parent = 0; }
+
+private:
+    virtual void platformCALayerLayoutSublayersOfLayer(PlatformCALayer*);
+    virtual bool platformCALayerRespondsToLayoutChanges() const { return true; }
+
+    virtual void platformCALayerAnimationStarted(CFTimeInterval beginTime) { }
+    virtual GraphicsLayer::CompositingCoordinatesOrientation platformCALayerContentsOrientation() const { return GraphicsLayer::CompositingCoordinatesBottomUp; }
+    virtual void platformCALayerPaintContents(GraphicsContext&, const IntRect& inClip) { }
+    virtual bool platformCALayerShowDebugBorders() const { return false; }
+    virtual bool platformCALayerShowRepaintCounter() const { return false; }
+    virtual int platformCALayerIncrementRepaintCount() { return 0; }
+
+    virtual bool platformCALayerContentsOpaque() const { return false; }
+    virtual bool platformCALayerDrawsContent() const { return false; }
+    virtual void platformCALayerLayerDidDisplay(PlatformLayer*) { }
+
+    AVFWrapper* m_parent;
+};
+
+#if !LOG_DISABLED
+static const char* boolString(bool val)
+{
+    return val ? "true" : "false";
+}
+#endif
+
+static CFArrayRef createMetadataKeyNames()
+{
+    static const CFStringRef keyNames[] = {
+        AVCFAssetPropertyDuration,
+        AVCFAssetPropertyNaturalSize,
+        AVCFAssetPropertyPreferredTransform,
+        AVCFAssetPropertyPreferredRate,
+        AVCFAssetPropertyPlayable,
+        AVCFAssetPropertyTracks 
+    };
+    
+    return CFArrayCreate(0, (const void**)keyNames, sizeof(keyNames) / sizeof(keyNames[0]), &kCFTypeArrayCallBacks);
+}
+
+static CFArrayRef metadataKeyNames()
+{
+    static CFArrayRef keys = createMetadataKeyNames();
+    return keys;
+}
+
+// FIXME: It would be better if AVCFTimedMetadataGroup.h exported this key.
+static CFStringRef CMTimeRangeStartKey()
+{
+    DEFINE_STATIC_LOCAL(CFStringRef, key, (CFSTR("start")));
+    return key;
+}
+
+// FIXME: It would be better if AVCFTimedMetadataGroup.h exported this key.
+static CFStringRef CMTimeRangeDurationKey()
+{
+    DEFINE_STATIC_LOCAL(CFStringRef, key, (CFSTR("duration")));
+    return key;
+}
+
+// FIXME: It would be better if AVCF exported this notification name.
+static CFStringRef CACFContextNeedsFlushNotification()
+{
+    DEFINE_STATIC_LOCAL(CFStringRef, name, (CFSTR("kCACFContextNeedsFlushNotification")));
+    return name;
+}
+
+// Define AVCF object accessors as inline functions here instead of in MediaPlayerPrivateAVFoundationCF so we don't have
+// to include the AVCF headers in MediaPlayerPrivateAVFoundationCF.h
+inline AVCFPlayerLayerRef videoLayer(AVFWrapper* wrapper)
+{ 
+    return wrapper ? wrapper->videoLayer() : 0; 
+}
+
+inline AVCFPlayerRef avPlayer(AVFWrapper* wrapper)
+{ 
+    return wrapper ? wrapper->avPlayer() : 0; 
+}
+
+inline AVCFURLAssetRef avAsset(AVFWrapper* wrapper)
+{ 
+    return wrapper ? wrapper->avAsset() : 0; 
+}
+
+inline AVCFPlayerItemRef avPlayerItem(AVFWrapper* wrapper)
+{ 
+    return wrapper ? wrapper->avPlayerItem() : 0; 
+}
+
+inline AVCFAssetImageGeneratorRef imageGenerator(AVFWrapper* wrapper)
+{ 
+    return wrapper ? wrapper->imageGenerator() : 0; 
+}
+
+PassOwnPtr<MediaPlayerPrivateInterface> MediaPlayerPrivateAVFoundationCF::create(MediaPlayer* player) 
+{ 
+    return adoptPtr(new MediaPlayerPrivateAVFoundationCF(player));
+}
+
+void MediaPlayerPrivateAVFoundationCF::registerMediaEngine(MediaEngineRegistrar registrar)
+{
+    if (isAvailable())
+        registrar(create, getSupportedTypes, supportsType, 0, 0, 0);
+}
+
+MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF(MediaPlayer* player)
+    : MediaPlayerPrivateAVFoundation(player)
+    , m_avfWrapper(0)
+    , m_videoFrameHasDrawn(false)
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::MediaPlayerPrivateAVFoundationCF(%p)", this);
+}
+
+MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::~MediaPlayerPrivateAVFoundationCF(%p)", this);
+    cancelLoad();
+}
+
+void MediaPlayerPrivateAVFoundationCF::cancelLoad()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::cancelLoad(%p)", this);
+
+    // Do nothing when our cancellation of pending loading calls its completion handler
+    setDelayCallbacks(true);
+    setIgnoreLoadStateChanges(true);
+
+    tearDownVideoRendering();
+
+    if (m_avfWrapper) {
+        // The AVCF objects have to be destroyed on the same dispatch queue used for notifications, so schedule a call to 
+        // disconnectAndDeleteAVFWrapper on that queue. 
+        m_avfWrapper->scheduleDisconnectAndDelete();
+        m_avfWrapper = 0;
+    }
+
+    setIgnoreLoadStateChanges(false);
+    setDelayCallbacks(false);
+}
+
+bool MediaPlayerPrivateAVFoundationCF::hasLayerRenderer() const
+{
+    return videoLayer(m_avfWrapper);
+}
+
+bool MediaPlayerPrivateAVFoundationCF::hasContextRenderer() const
+{
+    return imageGenerator(m_avfWrapper);
+}
+
+void MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::createContextVideoRenderer(%p)", this);
+
+    if (imageGenerator(m_avfWrapper))
+        return;
+
+    if (m_avfWrapper)
+        m_avfWrapper->createImageGenerator();
+}
+
+void MediaPlayerPrivateAVFoundationCF::destroyContextVideoRenderer()
+{
+    if (m_avfWrapper)
+        m_avfWrapper->destroyImageGenerator();
+}
+
+void MediaPlayerPrivateAVFoundationCF::createVideoLayer()
+{
+    ASSERT(supportsAcceleratedRendering());
+
+    if (m_avfWrapper)
+        m_avfWrapper->createAVCFVideoLayer();
+}
+
+void MediaPlayerPrivateAVFoundationCF::destroyVideoLayer()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::destroyVideoLayer(%p) - destroying %p", this, videoLayer(m_avfWrapper));
+    if (m_avfWrapper)
+        m_avfWrapper->destroyVideoLayer();
+}
+
+bool MediaPlayerPrivateAVFoundationCF::hasAvailableVideoFrame() const
+{
+    return (m_videoFrameHasDrawn || (videoLayer(m_avfWrapper) && AVCFPlayerLayerIsReadyForDisplay(videoLayer(m_avfWrapper))));
+}
+
+void MediaPlayerPrivateAVFoundationCF::createAVPlayer()
+{
+    ASSERT(m_avfWrapper);
+    
+    setDelayCallbacks(true);
+    m_avfWrapper->createPlayer();
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::createAVPlayerItem()
+{
+    ASSERT(m_avfWrapper);
+    
+    setDelayCallbacks(true);
+    m_avfWrapper->createPlayerItem();
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::createAVAssetForURL(const String& url)
+{
+    ASSERT(!m_avfWrapper);
+    
+    setDelayCallbacks(true);
+    m_avfWrapper = new AVFWrapper(this);
+    m_avfWrapper->createAssetForURL(url);
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::checkPlayability()
+{
+    ASSERT(m_avfWrapper);
+    m_avfWrapper->checkPlayability();
+}
+
+void MediaPlayerPrivateAVFoundationCF::beginLoadingMetadata()
+{
+    ASSERT(m_avfWrapper);
+    m_avfWrapper->beginLoadingMetadata();
+}
+
+MediaPlayerPrivateAVFoundation::ItemStatus MediaPlayerPrivateAVFoundationCF::playerItemStatus() const
+{
+    if (!avPlayerItem(m_avfWrapper))
+        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
+
+    AVCFPlayerItemStatus status = AVCFPlayerItemGetStatus(avPlayerItem(m_avfWrapper), 0);
+    if (status == AVCFPlayerItemStatusUnknown)
+        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusUnknown;
+    if (status == AVCFPlayerItemStatusFailed)
+        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusFailed;
+    if (AVCFPlayerItemIsPlaybackLikelyToKeepUp(avPlayerItem(m_avfWrapper)))
+        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackLikelyToKeepUp;
+    if (AVCFPlayerItemIsPlaybackBufferFull(avPlayerItem(m_avfWrapper)))
+        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferFull;
+    if (AVCFPlayerItemIsPlaybackBufferEmpty(avPlayerItem(m_avfWrapper)))
+        return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusPlaybackBufferEmpty;
+    return MediaPlayerPrivateAVFoundation::MediaPlayerAVPlayerItemStatusReadyToPlay;
+}
+
+PlatformMedia MediaPlayerPrivateAVFoundationCF::platformMedia() const
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::platformMedia(%p)", this);
+    PlatformMedia pm;
+    pm.type = PlatformMedia::AVFoundationCFMediaPlayerType;
+    pm.media.avcfMediaPlayer = (AVCFPlayer*)avPlayer(m_avfWrapper);
+    return pm;
+}
+
+PlatformLayer* MediaPlayerPrivateAVFoundationCF::platformLayer() const
+{
+    if (!m_avfWrapper)
+        return 0;
+
+    return m_avfWrapper->platformLayer();
+}
+
+void MediaPlayerPrivateAVFoundationCF::platformSetVisible(bool isVisible)
+{
+    if (!m_avfWrapper)
+        return;
+    
+    // FIXME: We use a CATransaction here on the Mac, we need to figure out why this was done there and
+    // whether we're affected by the same issue.
+    setDelayCallbacks(true);
+    m_avfWrapper->setVideoLayerHidden(!isVisible);    
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::platformPlay()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::play(%p)", this);
+    if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
+        return;
+
+    setDelayCallbacks(true);
+    AVCFPlayerSetRate(avPlayer(m_avfWrapper), requestedRate());
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::platformPause()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::pause(%p)", this);
+    if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
+        return;
+
+    setDelayCallbacks(true);
+    AVCFPlayerSetRate(avPlayer(m_avfWrapper), 0);
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::updateRate()
+{
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::updateRate(%p)", this);
+    if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
+        return;
+
+    setDelayCallbacks(true);
+    AVCFPlayerSetRate(avPlayer(m_avfWrapper), requestedRate());
+    setDelayCallbacks(false);
+}
+
+float MediaPlayerPrivateAVFoundationCF::platformDuration() const
+{
+    if (!metaDataAvailable() || !avAsset(m_avfWrapper))
+        return 0;
+
+    float duration;
+    CMTime cmDuration = AVCFAssetGetDuration(avAsset(m_avfWrapper));
+    if (CMTIME_IS_NUMERIC(cmDuration))
+        duration = narrowPrecisionToFloat(CMTimeGetSeconds(cmDuration));
+    else if (CMTIME_IS_INDEFINITE(cmDuration))
+        duration = numeric_limits<float>::infinity();
+    else {
+        LOG(Media, "MediaPlayerPrivateAVFMac::duration(%p) - invalid duration, returning 0", this);
+        return 0;
+    }
+
+    return duration;
+}
+
+float MediaPlayerPrivateAVFoundationCF::currentTime() const
+{
+    if (!metaDataAvailable() || !avPlayerItem(m_avfWrapper))
+        return 0;
+
+    CMTime itemTime = AVCFPlayerItemGetCurrentTime(avPlayerItem(m_avfWrapper));
+    if (CMTIME_IS_NUMERIC(itemTime))
+        return narrowPrecisionToFloat(CMTimeGetSeconds(itemTime));
+
+    return 0;
+}
+
+void MediaPlayerPrivateAVFoundationCF::seekToTime(float time)
+{
+    if (!m_avfWrapper)
+        return;
+    
+    // seekToTime generates several event callbacks, update afterwards.
+    setDelayCallbacks(true);
+    m_avfWrapper->seekToTime(time);
+    setDelayCallbacks(false);
+}
+
+void MediaPlayerPrivateAVFoundationCF::setVolume(float volume)
+{
+    if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
+        return;
+
+    AVCFPlayerSetVolume(avPlayer(m_avfWrapper), volume);
+}
+
+void MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible(bool closedCaptionsVisible)
+{
+    if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
+        return;
+
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::setClosedCaptionsVisible(%p) - setting to %s", this, boolString(closedCaptionsVisible));
+    AVCFPlayerSetClosedCaptionDisplayEnabled(avPlayer(m_avfWrapper), closedCaptionsVisible);
+}
+
+float MediaPlayerPrivateAVFoundationCF::rate() const
+{
+    if (!metaDataAvailable() || !avPlayer(m_avfWrapper))
+        return 0;
+
+    setDelayCallbacks(true);
+    float currentRate = AVCFPlayerGetRate(avPlayer(m_avfWrapper));
+    setDelayCallbacks(false);
+
+    return currentRate;
+}
+
+static bool timeRangeIsValidAndNotEmpty(CMTime start, CMTime duration)
+{
+    // Is the range valid?
+    if (!CMTIME_IS_VALID(start) || !CMTIME_IS_VALID(duration) || duration.epoch || duration.value < 0)
+        return false;
+
+    if (CMTIME_COMPARE_INLINE(duration, ==, kCMTimeZero))
+        return false;
+
+    return true;
+}
+
+PassRefPtr<TimeRanges> MediaPlayerPrivateAVFoundationCF::platformBufferedTimeRanges() const
+{
+    RefPtr<TimeRanges> timeRanges = TimeRanges::create();
+
+    if (!avPlayerItem(m_avfWrapper))
+        return timeRanges.release();
+
+    RetainPtr<CFArrayRef> loadedRanges(AdoptCF, AVCFPlayerItemCopyLoadedTimeRanges(avPlayerItem(m_avfWrapper)));
+    if (!loadedRanges)
+        return timeRanges.release();
+
+    CFIndex rangeCount = CFArrayGetCount(loadedRanges.get());
+    for (CFIndex i = 0; i < rangeCount; i++) {
+        CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loadedRanges.get(), i));
+        CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
+        CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
+        
+        if (timeRangeIsValidAndNotEmpty(start, duration)) {
+            float rangeStart = narrowPrecisionToFloat(CMTimeGetSeconds(start));
+            float rangeEnd = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
+            timeRanges->add(rangeStart, rangeEnd);
+        }
+    }
+
+    return timeRanges.release();
+}
+
+float MediaPlayerPrivateAVFoundationCF::platformMaxTimeSeekable() const
+{
+    if (!avPlayerItem(m_avfWrapper))
+        return 0;
+
+    RetainPtr<CFArrayRef> seekableRanges(AdoptCF, AVCFPlayerItemCopySeekableTimeRanges(avPlayerItem(m_avfWrapper)));
+    if (!seekableRanges)
+        return 0;
+
+    float maxTimeSeekable = 0;
+    CFIndex rangeCount = CFArrayGetCount(seekableRanges.get());
+    for (CFIndex i = 0; i < rangeCount; i++) {
+        CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(seekableRanges.get(), i));
+        CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
+        CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
+        if (!timeRangeIsValidAndNotEmpty(start, duration))
+            continue;
+        
+        float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
+        if (maxTimeSeekable < endOfRange)
+            maxTimeSeekable = endOfRange;
+    }
+
+    return maxTimeSeekable;   
+}
+
+float MediaPlayerPrivateAVFoundationCF::platformMaxTimeLoaded() const
+{
+    if (!avPlayerItem(m_avfWrapper))
+        return 0;
+
+    RetainPtr<CFArrayRef> loadedRanges(AdoptCF, AVCFPlayerItemCopyLoadedTimeRanges(avPlayerItem(m_avfWrapper)));
+    if (!loadedRanges)
+        return 0;
+
+    float maxTimeLoaded = 0;
+    CFIndex rangeCount = CFArrayGetCount(loadedRanges.get());
+    for (CFIndex i = 0; i < rangeCount; i++) {
+        CFDictionaryRef range = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loadedRanges.get(), i));
+        CMTime start = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeStartKey())));
+        CMTime duration = CMTimeMakeFromDictionary(static_cast<CFDictionaryRef>(CFDictionaryGetValue(range, CMTimeRangeDurationKey())));
+        if (!timeRangeIsValidAndNotEmpty(start, duration))
+            continue;
+        
+        float endOfRange = narrowPrecisionToFloat(CMTimeGetSeconds(CMTimeAdd(start, duration)));
+        if (maxTimeLoaded < endOfRange)
+            maxTimeLoaded = endOfRange;
+    }
+
+    return maxTimeLoaded;   
+}
+
+unsigned MediaPlayerPrivateAVFoundationCF::totalBytes() const
+{
+    if (!metaDataAvailable() || !avAsset(m_avfWrapper))
+        return 0;
+
+    int64_t totalMediaSize = 0;
+    RetainPtr<CFArrayRef> tracks(AdoptCF, AVCFAssetCopyAssetTracks(avAsset(m_avfWrapper)));
+    CFIndex trackCount = CFArrayGetCount(tracks.get());
+    for (CFIndex i = 0; i < trackCount; i++) {
+        AVCFAssetTrackRef assetTrack = (AVCFAssetTrackRef)CFArrayGetValueAtIndex(tracks.get(), i);
+        totalMediaSize += AVCFAssetTrackGetTotalSampleDataLength(assetTrack);
+    }
+
+    // FIXME: It doesn't seem safe to cast an int64_t to unsigned.
+    return static_cast<unsigned>(totalMediaSize);
+}
+
+MediaPlayerPrivateAVFoundation::AssetStatus MediaPlayerPrivateAVFoundationCF::assetStatus() const
+{
+    if (!avAsset(m_avfWrapper))
+        return MediaPlayerAVAssetStatusDoesNotExist;
+
+    // First, make sure all metadata properties we rely on are loaded.
+    CFArrayRef keys = metadataKeyNames();
+    CFIndex keyCount = CFArrayGetCount(keys);
+    for (CFIndex i = 0; i < keyCount; i++) {
+        CFStringRef keyName = static_cast<CFStringRef>(CFArrayGetValueAtIndex(keys, i));
+        AVCFPropertyValueStatus keyStatus = AVCFAssetGetStatusOfValueForProperty(avAsset(m_avfWrapper), keyName, 0);
+        
+        if (keyStatus < AVCFPropertyValueStatusLoaded)
+            return MediaPlayerAVAssetStatusLoading;
+        if (keyStatus == AVCFPropertyValueStatusFailed)
+            return MediaPlayerAVAssetStatusFailed;
+        if (keyStatus == AVCFPropertyValueStatusCancelled)
+            return MediaPlayerAVAssetStatusCancelled;
+    }
+
+    if (AVCFAssetIsPlayable(avAsset(m_avfWrapper)))
+        return MediaPlayerAVAssetStatusPlayable;
+
+    return MediaPlayerAVAssetStatusLoaded;
+}
+
+void MediaPlayerPrivateAVFoundationCF::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect)
+{
+    if (context->paintingDisabled())
+        return;
+
+    if (currentRenderingMode() == MediaRenderingToLayer && !imageGenerator(m_avfWrapper)) {
+        // We're being told to render into a context, but we already have the
+        // video layer, which probably means we've been called from <canvas>.
+        createContextVideoRenderer();
+    }
+
+    paint(context, rect);
+}
+
+void MediaPlayerPrivateAVFoundationCF::paint(GraphicsContext* context, const IntRect& rect)
+{
+    if (context->paintingDisabled() || !imageGenerator(m_avfWrapper))
+        return;
+
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF::paint(%p)", this);
+
+    setDelayCallbacks(true);
+    RetainPtr<CGImageRef> image = m_avfWrapper->createImageForTimeInRect(currentTime(), rect);
+    if (image) {
+        context->save();
+        context->translate(rect.x(), rect.y() + rect.height());
+        context->scale(FloatSize(1.0f, -1.0f));
+        context->setImageInterpolationQuality(InterpolationLow);
+        IntRect paintRect(IntPoint(0, 0), IntSize(rect.width(), rect.height()));
+        CGContextDrawImage(context->platformContext(), CGRectMake(0, 0, paintRect.width(), paintRect.height()), image.get());
+        context->restore();
+        image = 0;
+    }
+    setDelayCallbacks(false);
+    
+    m_videoFrameHasDrawn = true;
+}
+
+static HashSet<String> mimeTypeCache()
+{
+    DEFINE_STATIC_LOCAL(HashSet<String>, cache, ());
+    static bool typeListInitialized = false;
+
+    if (typeListInitialized)
+        return cache;
+    typeListInitialized = true;
+
+    // Not possible to query AVFoundation for supported MIME types, <rdar://problem/8721693>, so
+    // hard code some types for now.
+    cache.add("video/quicktime");
+    cache.add("video/mp4");
+    cache.add("video/mpeg");
+    cache.add("audio/3gpp");
+    cache.add("audio/mp3");
+    cache.add("audio/mp4");
+    cache.add("audio/mp4");
+    cache.add("audio/m4a");
+    cache.add("audio/aac");
+    cache.add("audio/wav");
+    cache.add("application/vnd.apple.mpegurl");
+
+    return cache;
+} 
+
+void MediaPlayerPrivateAVFoundationCF::getSupportedTypes(HashSet<String>& supportedTypes)
+{
+    supportedTypes = mimeTypeCache();
+} 
+
+MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationCF::supportsType(const String& type, const String& codecs)
+{
+    // Only return "IsSupported" if there is no codecs parameter for now as there is no way to ask if it supports an
+    // extended MIME type until rdar://8721715 is fixed.
+    if (mimeTypeCache().contains(type))
+        return codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported;
+
+    return MediaPlayer::IsNotSupported;
+}
+
+
+bool MediaPlayerPrivateAVFoundationCF::isAvailable()
+{
+    return AVFoundationCFLibrary() && CoreMediaLibrary() && libdispatchLibrary();
+}
+
+float MediaPlayerPrivateAVFoundationCF::mediaTimeForTimeValue(float timeValue) const
+{
+    if (!metaDataAvailable())
+        return timeValue;
+
+    // FIXME - can not implement until rdar://8721669 is fixed.
+    return timeValue;
+}
+
+void MediaPlayerPrivateAVFoundationCF::tracksChanged()
+{
+    if (!avAsset(m_avfWrapper))
+        return;
+    
+    // This is called whenever the tracks collection changes so cache hasVideo and hasAudio since we are
+    // asked about those fairly frequently.
+    if (!avPlayerItem(m_avfWrapper)) {
+        // We don't have a player item yet, so check with the asset because some assets support inspection
+        // prior to becoming ready to play.
+        RetainPtr<CFArrayRef> visualTracks(AdoptCF, AVCFAssetCopyTracksWithMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicVisual));
+        setHasVideo(CFArrayGetCount(visualTracks.get()));
+
+        RetainPtr<CFArrayRef> audioTracks(AdoptCF, AVCFAssetCopyTracksWithMediaCharacteristic(avAsset(m_avfWrapper), AVCFMediaCharacteristicAudible));
+        setHasAudio(CFArrayGetCount(audioTracks.get()));
+
+        RetainPtr<CFArrayRef> captionTracks(AdoptCF, AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaTypeClosedCaption));
+        setHasAudio(CFArrayGetCount(captionTracks.get()));
+    } else {
+        bool hasVideo = false;
+        bool hasAudio = false;
+        bool hasCaptions = false;
+
+        RetainPtr<CFArrayRef> tracks(AdoptCF, AVCFPlayerItemCopyTracks(avPlayerItem(m_avfWrapper)));
+
+        CFIndex trackCount = CFArrayGetCount(tracks.get());
+        for (CFIndex i = 0; i < trackCount; i++) {
+            AVCFPlayerItemTrackRef track = (AVCFPlayerItemTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
+            
+            if (AVCFPlayerItemTrackIsEnabled(track)) {
+                RetainPtr<AVCFAssetTrackRef> assetTrack(AdoptCF, AVCFPlayerItemTrackCopyAssetTrack(track));
+                CFStringRef mediaType = AVCFAssetTrackGetMediaType(assetTrack.get());
+                if (!mediaType)
+                    continue;
+                
+                if (CFStringCompare(mediaType, AVCFMediaTypeVideo, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+                    hasVideo = true;
+                else if (CFStringCompare(mediaType, AVCFMediaTypeAudio, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+                    hasAudio = true;
+                else if (CFStringCompare(mediaType, AVCFMediaTypeClosedCaption, kCFCompareCaseInsensitive) == kCFCompareEqualTo)
+                    hasCaptions = true;
+            }
+        }
+
+        setHasVideo(hasVideo);
+        setHasAudio(hasAudio);
+        setHasClosedCaptions(hasCaptions);
+    }
+
+    LOG(Media, "MediaPlayerPrivateAVFoundationCF:tracksChanged(%p) - hasVideo = %s, hasAudio = %s, hasCaptions = %s", 
+        this, boolString(hasVideo()), boolString(hasAudio()), boolString(hasClosedCaptions()));
+
+    sizeChanged();
+}
+
+void MediaPlayerPrivateAVFoundationCF::sizeChanged()
+{
+    if (!avAsset(m_avfWrapper))
+        return;
+    
+    // AVAsset's 'naturalSize' property only considers the movie's first video track, so we need to compute
+    // the union of all visual track rects.
+    CGRect trackRectUnion = CGRectZero;
+    RetainPtr<CFArrayRef> tracks(AdoptCF, AVCFAssetCopyTracksWithMediaType(avAsset(m_avfWrapper), AVCFMediaCharacteristicVisual));
+    CFIndex trackCount = CFArrayGetCount(tracks.get());
+    for (CFIndex i = 0; i < trackCount; i++) {
+        AVCFAssetTrackRef assetTrack = (AVCFAssetTrackRef)(CFArrayGetValueAtIndex(tracks.get(), i));
+        
+        CGSize trackSize = AVCFAssetTrackGetNaturalSize(assetTrack);
+        CGRect trackRect = CGRectMake(0, 0, trackSize.width, trackSize.height);
+        trackRectUnion = CGRectUnion(trackRectUnion, CGRectApplyAffineTransform(trackRect, AVCFAssetTrackGetPreferredTransform(assetTrack)));
+    }
+    // The movie is always displayed at 0,0 so move the track rect to the origin before using width and height.
+    trackRectUnion = CGRectOffset(trackRectUnion, trackRectUnion.origin.x, trackRectUnion.origin.y);
+    CGSize naturalSize = trackRectUnion.size;
+
+    // Also look at the asset's preferred transform so we account for a movie matrix.
+    CGSize movieSize = CGSizeApplyAffineTransform(AVCFAssetGetNaturalSize(avAsset(m_avfWrapper)), AVCFAssetGetPreferredTransform(avAsset(m_avfWrapper)));
+    if (movieSize.width > naturalSize.width)
+        naturalSize.width = movieSize.width;
+    if (movieSize.height > naturalSize.height)
+        naturalSize.height = movieSize.height;
+    setNaturalSize(IntSize(naturalSize));
+}
+
+void MediaPlayerPrivateAVFoundationCF::contentsNeedsDisplay()
+{
+    if (m_avfWrapper)
+        m_avfWrapper->setVideoLayerNeedsCommit();
+}
+
+AVFWrapper::AVFWrapper(MediaPlayerPrivateAVFoundationCF* owner)
+    : m_owner(owner)
+{
+    LOG(Media, "AVFWrapper::AVFWrapper(%p)", this);
+
+    m_notificationQueue = dispatch_queue_create("MediaPlayerPrivateAVFoundationCF.notificationQueue", 0);
+}
+
+AVFWrapper::~AVFWrapper()
+{
+    LOG(Media, "AVFWrapper::~AVFWrapper(%p)", this);
+    ASSERT(!m_owner);
+
+    destroyVideoLayer();
+    destroyImageGenerator();
+
+    if (m_notificationQueue)
+        dispatch_release(m_notificationQueue);
+
+    if (avAsset()) {
+        AVCFAssetCancelLoading(avAsset());
+        m_avAsset = 0;
+    }
+
+    m_avPlayerItem = 0;
+    m_timeObserver = 0;
+    m_avPlayer = 0;
+}
+
+void AVFWrapper::scheduleDisconnectAndDelete()
+{
+    // Ignore any subsequent notifications we might receive in notificationCallback().
+    m_owner = 0;
+    dispatch_async_f(dispatchQueue(), this, disconnectAndDeleteAVFWrapper);
+}
+
+void AVFWrapper::disconnectAndDeleteAVFWrapper(void* context)
+{
+    AVFWrapper* avfWrapper = static_cast<AVFWrapper*>(context);
+
+    LOG(Media, "AVFWrapper::disconnectAndDeleteAVFWrapper(%p)", avfWrapper);
+
+    if (avfWrapper->avPlayerItem()) {
+        CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemDidPlayToEndTimeNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemStatusChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemTracksChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemSeekableTimeRangesChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemLoadedTimeRangesChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, AVCFPlayerItemIsPlaybackBufferFullChangedNotification, avfWrapper->avPlayerItem());
+        CFNotificationCenterRemoveObserver(center, avfWrapper, CACFContextNeedsFlushNotification(), avfWrapper->avPlayerItem());
+    }
+
+    if (avfWrapper->avPlayer()) {
+        if (avfWrapper->timeObserver())
+            AVCFPlayerRemoveObserver(avfWrapper->avPlayer(), avfWrapper->timeObserver());
+
+        CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), avfWrapper, AVCFPlayerRateChangedNotification, avfWrapper->avPlayer());
+    }
+
+    dispatch_async_f(avfWrapper->dispatchQueue(), avfWrapper, deleteAVFWrapper);
+}
+
+void AVFWrapper::deleteAVFWrapper(void* context)
+{
+    AVFWrapper* avfWrapper = static_cast<AVFWrapper*>(context);
+    LOG(Media, "AVFWrapper::deleteAVFWrapper(%p)", avfWrapper);
+    
+    // FIXME: Leak AVFWrapper until <rdar://problem/9885505> is fixed, but clean up its ivars.
+    // delete avfWrapper;
+    avfWrapper->destroyVideoLayer();
+    avfWrapper->destroyImageGenerator();
+
+    if (avfWrapper->m_notificationQueue) {
+        dispatch_release(avfWrapper->m_notificationQueue);
+        avfWrapper->m_notificationQueue = 0;
+    }
+
+    if (avfWrapper->avAsset()) {
+        AVCFAssetCancelLoading(avfWrapper->avAsset());
+        avfWrapper->m_avAsset = 0;
+    }
+
+    avfWrapper->m_avPlayerItem = 0;
+    avfWrapper->m_timeObserver = 0;
+    avfWrapper->m_avPlayer = 0;
+}
+
+void AVFWrapper::createAssetForURL(const String& url)
+{
+    ASSERT(!avAsset());
+    
+    RetainPtr<CFURLRef> urlRef(AdoptCF, KURL(ParsedURLString, url).createCFURL());
+    AVCFURLAssetRef assetRef = AVCFURLAssetCreateWithURLAndOptions(kCFAllocatorDefault, urlRef.get(), 0, m_notificationQueue);
+    m_avAsset.adoptCF(assetRef);
+}
+
+void AVFWrapper::createPlayer()
+{
+    ASSERT(!avPlayer() && avPlayerItem());
+
+    // FIXME: We need a way to create a AVPlayer without an AVPlayerItem, see <rdar://problem/9877730>.
+    AVCFPlayerRef playerRef = AVCFPlayerCreateWithPlayerItemAndOptions(kCFAllocatorDefault, avPlayerItem(), 0, m_notificationQueue);
+    m_avPlayer.adoptCF(playerRef);
+
+    CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
+    ASSERT(center);
+
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerRateChangedNotification, playerRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+
+    // Add a time observer, ask to be called infrequently because we don't really want periodic callbacks but
+    // our observer will also be called whenever a seek happens.
+    const double veryLongInterval = 60*60*60*24*30;
+    m_timeObserver.adoptCF(AVCFPlayerCreatePeriodicTimeObserverForInterval(playerRef, CMTimeMake(veryLongInterval, 10), m_notificationQueue, &periodicTimeObserverCallback, this));
+}
+
+void AVFWrapper::createPlayerItem()
+{
+    ASSERT(!avPlayerItem() && avAsset());
+
+    // Create the player item so we begin loading media data.
+    AVCFPlayerItemRef itemRef = AVCFPlayerItemCreateWithAsset(kCFAllocatorDefault, avAsset(), m_notificationQueue);
+    m_avPlayerItem.adoptCF(itemRef);
+
+    CFNotificationCenterRef center = CFNotificationCenterGetLocalCenter();
+    ASSERT(center);
+
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemDidPlayToEndTimeNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemStatusChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemTracksChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemSeekableTimeRangesChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemLoadedTimeRangesChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+    CFNotificationCenterAddObserver(center, this, notificationCallback, AVCFPlayerItemIsPlaybackBufferFullChangedNotification, itemRef, CFNotificationSuspensionBehaviorDeliverImmediately);
+
+    CFNotificationCenterAddObserver(center, this, notificationCallback, CACFContextNeedsFlushNotification(), 0, CFNotificationSuspensionBehaviorDeliverImmediately);
+}
+
+void AVFWrapper::periodicTimeObserverCallback(AVCFPlayerRef, CMTime cmTime, void* context)
+{
+    AVFWrapper* self = static_cast<AVFWrapper*>(context);
+    if (!self->m_owner)
+        return;
+
+    double time = std::max(0.0, CMTimeGetSeconds(cmTime)); // Clamp to zero, negative values are sometimes reported.
+    self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::PlayerTimeChanged, time);
+}
+
+void AVFWrapper::notificationCallback(CFNotificationCenterRef, void* observer, CFStringRef propertyName, const void* object, CFDictionaryRef)
+{
+    AVFWrapper* self = static_cast<AVFWrapper*>(observer);
+    
+    if (!self->m_owner)
+        return;
+
+    LOG(Media, "AVFWrapper::notificationCallback(%p)", self);
+
+    if (CFEqual(propertyName, AVCFPlayerItemDidPlayToEndTimeNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemDidPlayToEndTime);
+    else if (CFEqual(propertyName, AVCFPlayerItemTracksChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemTracksChanged);
+    else if (CFEqual(propertyName, AVCFPlayerItemStatusChangedNotification)) {
+        AVCFURLAssetRef asset = AVCFPlayerItemGetAsset(self->avPlayerItem());
+        if (asset)
+            self->setAsset(asset);
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemStatusChanged);
+    } else if (CFEqual(propertyName, AVCFPlayerItemSeekableTimeRangesChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemSeekableTimeRangesChanged);
+    else if (CFEqual(propertyName, AVCFPlayerItemLoadedTimeRangesChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemLoadedTimeRangesChanged);
+    else if (CFEqual(propertyName, AVCFPlayerItemPresentationSizeChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemPresentationSizeChanged);
+    else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackLikelyToKeepUpChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackLikelyToKeepUpChanged);
+    else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackBufferEmptyChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackBufferEmptyChanged);
+    else if (CFEqual(propertyName, AVCFPlayerItemIsPlaybackBufferFullChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ItemIsPlaybackBufferFullChanged);
+    else if (CFEqual(propertyName, AVCFPlayerRateChangedNotification))
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::PlayerRateChanged);
+    else if (CFEqual(propertyName, CACFContextNeedsFlushNotification())) {
+        // FIXME: Note that until <rdar://problem/9885505> is fixed, we can get a kCACFContextNeedsFlushNotification after
+        // the AVFWrapper has gone away. We work around this for now by never actually deleting the underlying AVFWrapper object. 
+        self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::ContentsNeedsDisplay);
+    } else
+        ASSERT_NOT_REACHED();
+}
+
+void AVFWrapper::loadPlayableCompletionCallback(AVCFAssetRef, void* context)
+{
+    AVFWrapper* self = static_cast<AVFWrapper*>(context);
+
+    if (!self->m_owner)
+        return;
+    self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetPlayabilityKnown);
+}
+
+void AVFWrapper::checkPlayability()
+{
+    LOG(Media, "AVFWrapper::checkPlayability(%p)", this);
+
+    static CFArrayRef propertyKeyName;
+    if (!propertyKeyName) {
+        static const CFStringRef keyNames[] = { 
+            AVCFAssetPropertyPlayable
+        };
+        propertyKeyName = CFArrayCreate(0, (const void**)keyNames, sizeof(keyNames) / sizeof(keyNames[0]), &kCFTypeArrayCallBacks);
+    }
+
+    AVCFAssetLoadValuesAsynchronouslyForProperties(avAsset(), propertyKeyName, loadPlayableCompletionCallback, this);
+}
+
+void AVFWrapper::loadMetadataCompletionCallback(AVCFAssetRef, void* context)
+{
+    LOG(Media, "AVFWrapper::loadMetadataCompletionCallback(%p)", context);
+    AVFWrapper* self = static_cast<AVFWrapper*>(context);
+
+    if (!self->m_owner)
+        return;
+    self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::AssetMetadataLoaded);
+}
+
+void AVFWrapper::beginLoadingMetadata()
+{
+    ASSERT(avAsset());
+    LOG(Media, "AVFWrapper::beginLoadingMetadata(%p) - requesting metadata loading", this);
+    AVCFAssetLoadValuesAsynchronouslyForProperties(avAsset(), metadataKeyNames(), loadMetadataCompletionCallback, this);
+}
+
+void AVFWrapper::seekCompletedCallback(AVCFPlayerItemRef, Boolean finished, void* context)
+{
+    AVFWrapper* self = static_cast<AVFWrapper*>(context);
+    if (!self->m_owner)
+        return;
+
+    self->m_owner->scheduleMainThreadNotification(MediaPlayerPrivateAVFoundation::Notification::SeekCompleted, static_cast<bool>(finished));
+}
+
+void AVFWrapper::seekToTime(float time)
+{
+    ASSERT(avPlayerItem());
+    AVCFPlayerItemSeekToTimeWithToleranceAndCompletionCallback(avPlayerItem(), CMTimeMakeWithSeconds(time, 600),
+        kCMTimeZero, kCMTimeZero, &seekCompletedCallback, this);
+}
+
+void AVFWrapper::setAsset(AVCFURLAssetRef asset)
+{
+    if (asset == avAsset())
+        return;
+
+    AVCFAssetCancelLoading(avAsset());
+    m_avAsset.adoptCF(asset);
+}
+
+PlatformLayer* AVFWrapper::platformLayer()
+{
+    LOG(Media, "AVFWrapper::platformLayer(%p)", this);
+
+    if (m_videoLayerWrapper)
+        return m_videoLayerWrapper->platformLayer();
+
+    if (!videoLayer())
+        return 0;
+
+    // Create a PlatformCALayer so we can resize the video layer to match the element size.
+    m_layerClient = adoptPtr(new LayerClient(this));
+    if (!m_layerClient)
+        return 0;
+
+    m_videoLayerWrapper = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, m_layerClient.get());
+    if (!m_videoLayerWrapper)
+        return 0;
+
+    CACFLayerRef layerRef = AVCFPlayerLayerCopyCACFLayer(m_avCFVideoLayer.get());
+    m_caVideoLayer.adoptCF(layerRef);
+
+    CACFLayerInsertSublayer(m_videoLayerWrapper->platformLayer(), m_caVideoLayer.get(), 0);
+    m_videoLayerWrapper->setAnchorPoint(FloatPoint3D());
+    m_videoLayerWrapper->setNeedsLayout();
+
+    return m_videoLayerWrapper->platformLayer();
+}
+
+void AVFWrapper::createAVCFVideoLayer()
+{
+    if (!avPlayer() || m_avCFVideoLayer)
+        return;
+
+    // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration().
+    m_avCFVideoLayer.adoptCF(AVCFPlayerLayerCreateWithAVCFPlayer(kCFAllocatorDefault, avPlayer(), m_notificationQueue));
+    LOG(Media, "AVFWrapper::createAVCFVideoLayer(%p) - returning %p", this, videoLayer());
+}
+
+void AVFWrapper::destroyVideoLayer()
+{
+    LOG(Media, "AVFWrapper::destroyVideoLayer(%p)", this);
+    m_layerClient = nullptr;
+    m_caVideoLayer = 0;
+    m_videoLayerWrapper = 0;
+    if (!m_avCFVideoLayer.get())
+        return;
+
+    AVCFPlayerLayerSetPlayer((AVCFPlayerLayerRef)m_avCFVideoLayer.get(), 0);
+    m_avCFVideoLayer = 0;
+}
+
+void AVFWrapper::setVideoLayerNeedsCommit()
+{
+    if (m_videoLayerWrapper)
+        m_videoLayerWrapper->setNeedsCommit();
+}
+
+void AVFWrapper::setVideoLayerHidden(bool value)
+{
+    if (m_videoLayerWrapper)
+        m_videoLayerWrapper->setHidden(value);
+}
+
+void AVFWrapper::createImageGenerator()
+{
+    if (!avAsset() || m_imageGenerator)
+        return;
+
+    m_imageGenerator.adoptCF(AVCFAssetImageGeneratorCreateWithAsset(kCFAllocatorDefault, avAsset()));
+
+    AVCFAssetImageGeneratorSetApertureMode(m_imageGenerator.get(), AVCFAssetImageGeneratorApertureModeCleanAperture);
+    AVCFAssetImageGeneratorSetRequestedTimeToleranceBefore(m_imageGenerator.get(), kCMTimeZero);
+    AVCFAssetImageGeneratorSetRequestedTimeToleranceAfter(m_imageGenerator.get(), kCMTimeZero);
+    AVCFAssetImageGeneratorSetAppliesPreferredTrackTransform(m_imageGenerator.get(), true);
+
+    LOG(Media, "AVFWrapper::createImageGenerator(%p) - returning %p", this, m_imageGenerator.get());
+}
+
+void AVFWrapper::destroyImageGenerator()
+{
+    LOG(Media, "AVFWrapper::destroyImageGenerator(%p)", this);
+    m_imageGenerator = 0;
+}
+
+RetainPtr<CGImageRef> AVFWrapper::createImageForTimeInRect(float time, const IntRect& rect)
+{
+    if (!m_imageGenerator)
+        return 0;
+
+#if !LOG_DISABLED
+    double start = WTF::currentTime();
+#endif
+
+    AVCFAssetImageGeneratorSetMaximumSize(m_imageGenerator.get(), CGSize(rect.size()));
+    CGImageRef image = AVCFAssetImageGeneratorCopyCGImageAtTime(m_imageGenerator.get(), CMTimeMakeWithSeconds(time, 600), 0, 0);
+
+#if !LOG_DISABLED
+    double duration = WTF::currentTime() - start;
+    LOG(Media, "AVFWrapper::createImageForTimeInRect(%p) - creating image took %.4f", this, narrowPrecisionToFloat(duration));
+#endif
+
+    return image;
+}
+
+void LayerClient::platformCALayerLayoutSublayersOfLayer(PlatformCALayer* wrapperLayer)
+{
+    ASSERT(m_parent);
+    ASSERT(m_parent->videoLayerWrapper() == wrapperLayer->platformLayer());
+
+    CGRect bounds = wrapperLayer->bounds();
+    CGPoint anchor = CACFLayerGetAnchorPoint(m_parent->caVideoLayer());
+    FloatPoint position(bounds.size.width * anchor.x, bounds.size.height * anchor.y); 
+
+    CACFLayerSetPosition(m_parent->caVideoLayer(), position);
+    CACFLayerSetBounds(m_parent->caVideoLayer(), bounds);
+}
+
+} // namespace WebCore
+
+#endif // PLATFORM(WIN) && ENABLE(VIDEO) && USE(AVFOUNDATION)
index d9ac4ef..f5dd001 100644 (file)
 #ifndef MediaPlayerPrivateAVFoundationCF_h
 #define MediaPlayerPrivateAVFoundationCF_h
 
+#if PLATFORM(WIN) && ENABLE(VIDEO) && USE(AVFOUNDATION)
+
+#include "MediaPlayerPrivateAVFoundation.h"
+
+namespace WebCore {
+
+class AVFWrapper;
+
+class MediaPlayerPrivateAVFoundationCF : public MediaPlayerPrivateAVFoundation {
+public:
+    virtual ~MediaPlayerPrivateAVFoundationCF();
+
+    static void registerMediaEngine(MediaEngineRegistrar);
+
+private:
+    MediaPlayerPrivateAVFoundationCF(MediaPlayer*);
+
+    // Engine support
+    static PassOwnPtr<MediaPlayerPrivateInterface> create(MediaPlayer*);
+    static void getSupportedTypes(HashSet<String>& types);
+    static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs);
+    static bool isAvailable();
+
+    virtual void cancelLoad();
+
+    virtual PlatformMedia platformMedia() const;
+
+    virtual void platformSetVisible(bool);
+    virtual void platformPlay();
+    virtual void platformPause();
+    virtual float currentTime() const;
+    virtual void setVolume(float);
+    virtual void setClosedCaptionsVisible(bool);
+    virtual void paint(GraphicsContext*, const IntRect&);
+    virtual void paintCurrentFrameInContext(GraphicsContext*, const IntRect&);
+    virtual PlatformLayer* platformLayer() const;
+    virtual bool supportsAcceleratedRendering() const { return true; }
+    virtual float mediaTimeForTimeValue(float) const;
+
+    virtual void createAVPlayer();
+    virtual void createAVPlayerItem();
+    virtual void createAVAssetForURL(const String& url);
+    virtual MediaPlayerPrivateAVFoundation::ItemStatus playerItemStatus() const;
+    virtual MediaPlayerPrivateAVFoundation::AssetStatus assetStatus() const;
+
+    virtual void checkPlayability();
+    virtual void updateRate();
+    virtual float rate() const;
+    virtual void seekToTime(float time);
+    virtual unsigned totalBytes() const;
+    virtual PassRefPtr<TimeRanges> platformBufferedTimeRanges() const;
+    virtual float platformMaxTimeSeekable() const;
+    virtual float platformDuration() const;
+    virtual float platformMaxTimeLoaded() const;
+    virtual void beginLoadingMetadata();
+    virtual void tracksChanged();
+    virtual void sizeChanged();
+
+    virtual bool hasAvailableVideoFrame() const;
+
+    virtual void createContextVideoRenderer();
+    virtual void destroyContextVideoRenderer();
+
+    virtual void createVideoLayer();
+    virtual void destroyVideoLayer();
+
+    virtual bool hasContextRenderer() const;
+    virtual bool hasLayerRenderer() const;
+
+    virtual void contentsNeedsDisplay();
+
+    friend class AVFWrapper;
+    AVFWrapper* m_avfWrapper;
+    
+    bool m_videoFrameHasDrawn;
+};
+
+}
+
+#endif // PLATFORM(WIN) && ENABLE(VIDEO) && USE(AVFOUNDATION)
 #endif // MediaPlayerPrivateAVFoundationCF_h