Title: [131220] trunk/Source/WebCore
Revision
131220
Author
[email protected]
Date
2012-10-12 13:47:07 -0700 (Fri, 12 Oct 2012)

Log Message

Add LSKD support to MediaPlayerPrivateAVFoundation.
https://bugs.webkit.org/show_bug.cgi?id=98090

Reviewed by Anders Carlsson.

Add support for LSKD key system to MediaPlayerPrivateAVFoundation.

* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
(MediaPlayerPrivateAVFoundationObjC): Add m_loaderDelegate, m_keyURIToRequestMap, and m_sessionToRequestMap.
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(globalLoaderDelegateQueue): Static accessor for the dispatch queue to use for the loader delegate.
(WebCore::MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC):
(WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL): Add the loader delegate to the AVURLAsset.
(WebCore::keySystemIsSupported): Convenience function; checks that the key system matches "com.apple.lskd"
(WebCore::MediaPlayerPrivateAVFoundationObjC::extendedSupportsType): Check the key system matches.
(WebCore::MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource): Fire a needkey event with an initData containing
    the key URI.
(WebCore::extractKeyURIKeyIdAndCertificateFromInitData): Extract the keyURI, keyID, and the app certificate from the initData.
(WebCore::MediaPlayerPrivateAVFoundationObjC::generateKeyRequest): Generate a streaming key request from AVFoundation and
    fire a keymessage event.
(WebCore::MediaPlayerPrivateAVFoundationObjC::addKey): Pass to AVFoundation through the AVAssetResourceLoader.
(WebCore::MediaPlayerPrivateAVFoundationObjC::cancelKeyRequest): Release the loader delegate.
(-[WebCoreAVFLoaderDelegate initWithCallback:]): Simple constructor.
(-[WebCoreAVFLoaderDelegate resourceLoader:shouldWaitForLoadingOfRequestedResource:]): Pass to the MediaPlayerPrivateAVFoundationObjC.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (131219 => 131220)


--- trunk/Source/WebCore/ChangeLog	2012-10-12 20:36:35 UTC (rev 131219)
+++ trunk/Source/WebCore/ChangeLog	2012-10-12 20:47:07 UTC (rev 131220)
@@ -1,3 +1,30 @@
+2012-10-01  Jer Noble  <[email protected]>
+
+        Add LSKD support to MediaPlayerPrivateAVFoundation.
+        https://bugs.webkit.org/show_bug.cgi?id=98090
+
+        Reviewed by Anders Carlsson.
+
+        Add support for LSKD key system to MediaPlayerPrivateAVFoundation.
+
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        (MediaPlayerPrivateAVFoundationObjC): Add m_loaderDelegate, m_keyURIToRequestMap, and m_sessionToRequestMap.
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (globalLoaderDelegateQueue): Static accessor for the dispatch queue to use for the loader delegate.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::MediaPlayerPrivateAVFoundationObjC):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::createAVAssetForURL): Add the loader delegate to the AVURLAsset.
+        (WebCore::keySystemIsSupported): Convenience function; checks that the key system matches "com.apple.lskd"
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::extendedSupportsType): Check the key system matches.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource): Fire a needkey event with an initData containing
+            the key URI.
+        (WebCore::extractKeyURIKeyIdAndCertificateFromInitData): Extract the keyURI, keyID, and the app certificate from the initData.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::generateKeyRequest): Generate a streaming key request from AVFoundation and 
+            fire a keymessage event.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::addKey): Pass to AVFoundation through the AVAssetResourceLoader.
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::cancelKeyRequest): Release the loader delegate.
+        (-[WebCoreAVFLoaderDelegate initWithCallback:]): Simple constructor.
+        (-[WebCoreAVFLoaderDelegate resourceLoader:shouldWaitForLoadingOfRequestedResource:]): Pass to the MediaPlayerPrivateAVFoundationObjC.
+
 2012-10-12  James Simonsen  <[email protected]>
 
         [RequestAnimationFrame] Remove vendor prefix

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h (131219 => 131220)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h	2012-10-12 20:36:35 UTC (rev 131219)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h	2012-10-12 20:47:07 UTC (rev 131220)
@@ -29,8 +29,9 @@
 #if ENABLE(VIDEO) && USE(AVFOUNDATION)
 
 #include "MediaPlayerPrivateAVFoundation.h"
+#include <wtf/HashMap.h>
 
-OBJC_CLASS AVAsset;
+OBJC_CLASS AVURLAsset;
 OBJC_CLASS AVPlayer;
 OBJC_CLASS AVPlayerItem;
 OBJC_CLASS AVPlayerItemVideoOutput;
@@ -38,6 +39,11 @@
 OBJC_CLASS AVAssetImageGenerator;
 OBJC_CLASS WebCoreAVFMovieObserver;
 
+#if ENABLE(ENCRYPTED_MEDIA) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
+OBJC_CLASS WebCoreAVFLoaderDelegate;
+OBJC_CLASS AVAssetResourceLoadingRequest;
+#endif
+
 #ifndef __OBJC__
 typedef struct objc_object *id;
 #endif
@@ -56,6 +62,10 @@
     void setAsset(id);
     virtual void tracksChanged();
 
+#if ENABLE(ENCRYPTED_MEDIA) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
+    bool shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest*);
+#endif
+
 private:
     MediaPlayerPrivateAVFoundationObjC(MediaPlayer*);
 
@@ -128,7 +138,13 @@
     void paintWithVideoOutput(GraphicsContext*, const IntRect&);
 #endif
 
-    RetainPtr<AVAsset> m_avAsset;
+#if ENABLE(ENCRYPTED_MEDIA)
+    virtual MediaPlayer::MediaKeyException addKey(const String&, const unsigned char*, unsigned, const unsigned char*, unsigned, const String&);
+    virtual MediaPlayer::MediaKeyException generateKeyRequest(const String&, const unsigned char*, unsigned);
+    virtual MediaPlayer::MediaKeyException cancelKeyRequest(const String&, const String&);
+#endif
+
+    RetainPtr<AVURLAsset> m_avAsset;
     RetainPtr<AVPlayer> m_avPlayer;
     RetainPtr<AVPlayerItem> m_avPlayerItem;
     RetainPtr<AVPlayerLayer> m_videoLayer;
@@ -143,6 +159,12 @@
     RetainPtr<AVPlayerItemVideoOutput> m_videoOutput;
     RetainPtr<CVPixelBufferRef> m_lastImage;
 #endif
+
+#if ENABLE(ENCRYPTED_MEDIA) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
+    RetainPtr<WebCoreAVFLoaderDelegate> m_loaderDelegate;
+    HashMap<String, RetainPtr<AVAssetResourceLoadingRequest> > m_keyURIToRequestMap;
+    HashMap<String, RetainPtr<AVAssetResourceLoadingRequest> > m_sessionIDToRequestMap;
+#endif
 };
 
 }

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm (131219 => 131220)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm	2012-10-12 20:36:35 UTC (rev 131219)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm	2012-10-12 20:47:07 UTC (rev 131220)
@@ -30,6 +30,7 @@
 #import "MediaPlayerPrivateAVFoundationObjC.h"
 
 #import "BlockExceptions.h"
+#import "DataView.h"
 #import "FloatConversion.h"
 #import "FrameView.h"
 #import "FloatConversion.h"
@@ -39,9 +40,13 @@
 #import "SecurityOrigin.h"
 #import "SoftLinking.h"
 #import "TimeRanges.h"
+#import "UUID.h"
 #import "WebCoreSystemInterface.h"
 #import <objc/objc-runtime.h>
 #import <wtf/UnusedParam.h>
+#import <wtf/Uint8Array.h>
+#import <wtf/Uint16Array.h>
+#import <wtf/Uint32Array.h>
 
 #import <CoreMedia/CoreMedia.h>
 #import <AVFoundation/AVFoundation.h>
@@ -112,6 +117,25 @@
 -(void)observeValueForKeyPath:keyPath ofObject:(id)object change:(NSDictionary *)change context:(MediaPlayerAVFoundationObservationContext)context;
 @end
 
+#if ENABLE(ENCRYPTED_MEDIA)
+@interface WebCoreAVFLoaderDelegate : NSObject<AVAssetResourceLoaderDelegate> {
+    MediaPlayerPrivateAVFoundationObjC* m_callback;
+}
+- (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback;
+- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest;
+@end
+
+static dispatch_queue_t globalLoaderDelegateQueue()
+{
+    static dispatch_queue_t globalQueue;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        globalQueue = dispatch_queue_create("WebCoreAVFLoaderDelegate queue", DISPATCH_QUEUE_SERIAL);
+    });
+    return globalQueue;
+}
+#endif
+
 namespace WebCore {
 
 static NSArray *assetMetadataKeyNames();
@@ -144,6 +168,9 @@
     , m_objcObserver(AdoptNS, [[WebCoreAVFMovieObserver alloc] initWithCallback:this])
     , m_videoFrameHasDrawn(false)
     , m_haveCheckedPlayability(false)
+#if ENABLE(ENCRYPTED_MEDIA)
+    , m_loaderDelegate(AdoptNS, [[WebCoreAVFLoaderDelegate alloc] initWithCallback:this])
+#endif
 {
 }
 
@@ -308,6 +335,10 @@
     NSURL *cocoaURL = KURL(ParsedURLString, url);
     m_avAsset.adoptNS([[AVURLAsset alloc] initWithURL:cocoaURL options:options.get()]);
 
+#if ENABLE(ENCRYPTED_MEDIA)
+    [[m_avAsset.get() resourceLoader] setDelegate:m_loaderDelegate.get() queue:globalLoaderDelegateQueue()];
+#endif
+
     m_haveCheckedPlayability = false;
 
     setDelayCallbacks(false);
@@ -730,13 +761,56 @@
 }
 
 #if ENABLE(ENCRYPTED_MEDIA)
+static bool keySystemIsSupported(const String& keySystem)
+{
+    if (equalIgnoringCase(keySystem, "com.apple.lskd") || equalIgnoringCase(keySystem, "com.apple.lskd.1_0"))
+        return true;
+
+    return false;
+}
+
 MediaPlayer::SupportsType MediaPlayerPrivateAVFoundationObjC::extendedSupportsType(const String& type, const String& codecs, const String& keySystem, const KURL& url)
 {
-    // AVFoundation does not support any encrypted media, so return IsNotSupported if the keySystem is non-NULL.
-    if (!keySystem.isNull() || !keySystem.isEmpty())
-            return MediaPlayer::IsNotSupported;
+    // From: <http://dvcs.w3.org/hg/html-media/raw-file/eme-v0.1b/encrypted-media/encrypted-media.html#dom-canplaytype>
+    // In addition to the steps in the current specification, this method must run the following steps:
+
+    // 1. Check whether the Key System is supported with the specified container and codec type(s) by following the steps for the first matching condition from the following list:
+    //    If keySystem is null, continue to the next step.
+    if (keySystem.isNull() || keySystem.isEmpty())
+        return supportsType(type, codecs, url);
+
+    // If keySystem contains an unrecognized or unsupported Key System, return the empty string
+    if (!keySystemIsSupported(keySystem))
+        return MediaPlayer::IsNotSupported;
+
+    // If the Key System specified by keySystem does not support decrypting the container and/or codec specified in the rest of the type string.
+    // (AVFoundation does not provide an API which would allow us to determine this, so this is a no-op)
+
+    // 2. Return "maybe" or "probably" as appropriate per the existing specification of canPlayType().
     return supportsType(type, codecs, url);
 }
+
+bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetResourceLoadingRequest* avRequest)
+{
+    String keyURI = [[[avRequest request] URL] absoluteString];
+
+    // Create an initData with the following layout:
+    // [4 bytes: keyURI size], [keyURI size bytes: keyURI]
+    unsigned keyURISize = keyURI.length() * sizeof(UChar);
+    RefPtr<ArrayBuffer> initDataBuffer = ArrayBuffer::create(4 + keyURISize, 1);
+    RefPtr<DataView> initDataView = DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
+    ExceptionCode ec = 0;
+    initDataView->setUint32(0, keyURISize, true, ec);
+
+    RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, 4, keyURI.length());
+    keyURIArray->setRange(keyURI.characters(), keyURI.length() / sizeof(unsigned char), 0);
+
+    if (!player()->keyNeeded("com.apple.lskd", emptyString(), static_cast<const unsigned char*>(initDataBuffer->data()), initDataBuffer->byteLength()))
+        return false;
+
+    m_keyURIToRequestMap.set(keyURI, avRequest);
+    return true;
+}
 #endif
 
 bool MediaPlayerPrivateAVFoundationObjC::isAvailable()
@@ -911,6 +985,133 @@
 
 #endif
 
+#if ENABLE(ENCRYPTED_MEDIA)
+
+static bool extractKeyURIKeyIDAndCertificateFromInitData(Uint8Array* initData, String& keyURI, String& keyID, RefPtr<Uint8Array>& certificate)
+{
+    // initData should have the following layout:
+    // [4 bytes: keyURI length][N bytes: keyURI][4 bytes: contentID length], [N bytes: contentID], [4 bytes: certificate length][N bytes: certificate]
+    if (initData->byteLength() < 4)
+        return false;
+
+    RefPtr<ArrayBuffer> initDataBuffer = initData->buffer();
+
+    // Use a DataView to read uint32 values from the buffer, as Uint32Array requires the reads be aligned on 4-byte boundaries. 
+    RefPtr<DataView> initDataView = DataView::create(initDataBuffer, 0, initDataBuffer->byteLength());
+    uint32_t offset = 0;
+    ExceptionCode ec = 0;
+
+    uint32_t keyURILength = initDataView->getUint32(offset, true, ec);
+    offset += 4;
+    if (ec || offset + keyURILength > initData->length())
+        return false;
+
+    RefPtr<Uint16Array> keyURIArray = Uint16Array::create(initDataBuffer, offset, keyURILength);
+    if (!keyURIArray)
+        return false;
+
+    keyURI = String(keyURIArray->data(), keyURILength / sizeof(unsigned short));
+    offset += keyURILength;
+
+    uint32_t keyIDLength = initDataView->getUint32(offset, true, ec);
+    offset += 4;
+    if (ec || offset + keyIDLength > initData->length())
+        return false;
+
+    RefPtr<Uint16Array> keyIDArray = Uint16Array::create(initDataBuffer, offset, keyIDLength);
+    if (!keyIDArray)
+        return false;
+
+    keyID = String(keyIDArray->data(), keyIDLength / sizeof(unsigned short));
+    offset += keyIDLength;
+
+    uint32_t certificateLength = initDataView->getUint32(offset, true, ec);
+    offset += 4;
+    if (ec || offset + certificateLength > initData->length())
+        return false;
+
+    certificate = Uint8Array::create(initDataBuffer, offset, certificateLength);
+    if (!certificate)
+        return false;
+
+    return true;
+}
+
+MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::generateKeyRequest(const String& keySystem, const unsigned char* initDataPtr, unsigned initDataLength)
+{
+    if (!keySystemIsSupported(keySystem))
+        return MediaPlayer::KeySystemNotSupported;
+
+    RefPtr<Uint8Array> initData = Uint8Array::create(initDataPtr, initDataLength);
+    String keyURI;
+    String keyID;
+    RefPtr<Uint8Array> certificate;
+    if (!extractKeyURIKeyIDAndCertificateFromInitData(initData.get(), keyURI, keyID, certificate))
+        return MediaPlayer::InvalidPlayerState;
+
+    if (!m_keyURIToRequestMap.contains(keyURI))
+        return MediaPlayer::InvalidPlayerState;
+
+    String sessionID = createCanonicalUUIDString();
+
+    RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_keyURIToRequestMap.get(keyURI);
+
+    RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:certificate->baseAddress() length:certificate->byteLength()]);
+    NSString* assetStr = keyID;
+    RetainPtr<NSData> assetID = [NSData dataWithBytes: [assetStr cStringUsingEncoding:NSUTF8StringEncoding] length:[assetStr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
+    NSError* error = 0;
+    RetainPtr<NSData> keyRequest = [avRequest.get() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:assetID.get() options:nil error:&error];
+
+    if (!keyRequest) {
+        NSError* underlyingError = [[error userInfo] objectForKey:NSUnderlyingErrorKey];
+        player()->keyError(keySystem, sessionID, MediaPlayerClient::DomainError, [underlyingError code]);
+        return MediaPlayer::NoError;
+    }
+
+    RefPtr<ArrayBuffer> keyRequestBuffer = ArrayBuffer::create([keyRequest.get() bytes], [keyRequest.get() length]);
+    RefPtr<Uint8Array> keyRequestArray = Uint8Array::create(keyRequestBuffer, 0, keyRequestBuffer->byteLength());
+    player()->keyMessage(keySystem, sessionID, keyRequestArray->data(), keyRequestArray->byteLength());
+
+    // Move ownership of the AVAssetResourceLoadingRequestfrom the keyIDToRequestMap to the sessionIDToRequestMap:
+    m_sessionIDToRequestMap.set(sessionID, avRequest);
+    m_keyURIToRequestMap.remove(keyURI);
+
+    return MediaPlayer::NoError;
+}
+
+MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::addKey(const String& keySystem, const unsigned char* keyPtr, unsigned keyLength, const unsigned char* initDataPtr, unsigned initDataLength, const String& sessionID)
+{
+    if (!keySystemIsSupported(keySystem))
+        return MediaPlayer::KeySystemNotSupported;
+
+    if (!m_sessionIDToRequestMap.contains(sessionID))
+        return MediaPlayer::InvalidPlayerState;
+
+    RetainPtr<AVAssetResourceLoadingRequest> avRequest = m_sessionIDToRequestMap.get(sessionID);
+    RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:keyPtr length:keyLength]);
+    [avRequest.get() finishLoadingWithResponse:nil data:keyData.get() redirect:nil];
+    m_sessionIDToRequestMap.remove(sessionID);
+
+    player()->keyAdded(keySystem, sessionID);
+
+    UNUSED_PARAM(initDataPtr);
+    UNUSED_PARAM(initDataLength);
+    return MediaPlayer::NoError;
+}
+
+MediaPlayer::MediaKeyException MediaPlayerPrivateAVFoundationObjC::cancelKeyRequest(const String& keySystem, const String& sessionID)
+{
+    if (!keySystemIsSupported(keySystem))
+        return MediaPlayer::KeySystemNotSupported;
+
+    if (!m_sessionIDToRequestMap.contains(sessionID))
+        return MediaPlayer::InvalidPlayerState;
+
+    m_sessionIDToRequestMap.remove(sessionID);
+    return MediaPlayer::NoError;
+}
+#endif
+
 NSArray* assetMetadataKeyNames()
 {
     static NSArray* keys;
@@ -1038,4 +1239,26 @@
 
 @end
 
+#if ENABLE(ENCRYPTED_MEDIA)
+@implementation WebCoreAVFLoaderDelegate
+
+- (id)initWithCallback:(MediaPlayerPrivateAVFoundationObjC*)callback
+{
+    m_callback = callback;
+    return [super init];
+}
+
+- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
+{
+    UNUSED_PARAM(resourceLoader);
+    dispatch_async(dispatch_get_main_queue(), ^{
+        if (!m_callback->shouldWaitForLoadingOfResource(loadingRequest))
+            [loadingRequest finishLoadingWithError:nil];
+    });
+    return TRUE;
+}
+
+@end
 #endif
+
+#endif
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to