[EME] support update() for FairPlayStreaming in Modern EME API
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2017 01:17:21 +0000 (01:17 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2017 01:17:21 +0000 (01:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180542

Reviewed by Eric Carlson.

Support the updateLicense() method in CDMInstanceFairPlayStreaming. Also, support adding a
AVStreamDataParser to the AVContentKeySession.

Drive-by fixes:

- Sometimes, AVFoundation will give us a base64 encoded string with spaces url-disallowed
characters, so use base64Decode() rather than base64URLDecode().

* platform/graphics/avfoundation/CDMFairPlayStreaming.cpp:
(WebCore::validFairPlayStreamingSchemes):
(WebCore::extractSinfData):
* platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
* platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm:
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::updateLicense):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::didProvideRequest):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::didFailToProvideRequest):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstance const):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceAttached):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceDetached):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance):
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(WebCore::SourceBufferPrivateAVFObjC::destroyParser):
(WebCore::SourceBufferPrivateAVFObjC::setCDMInstance):

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/avfoundation/CDMFairPlayStreaming.cpp
Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm

index c31c071..9e4911c 100644 (file)
@@ -1,3 +1,37 @@
+2017-12-11  Jer Noble  <jer.noble@apple.com>
+
+        [EME] support update() for FairPlayStreaming in Modern EME API
+        https://bugs.webkit.org/show_bug.cgi?id=180542
+
+        Reviewed by Eric Carlson.
+
+        Support the updateLicense() method in CDMInstanceFairPlayStreaming. Also, support adding a
+        AVStreamDataParser to the AVContentKeySession.
+
+        Drive-by fixes:
+
+        - Sometimes, AVFoundation will give us a base64 encoded string with spaces url-disallowed
+        characters, so use base64Decode() rather than base64URLDecode().
+
+        * platform/graphics/avfoundation/CDMFairPlayStreaming.cpp:
+        (WebCore::validFairPlayStreamingSchemes):
+        (WebCore::extractSinfData):
+        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
+        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm:
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::updateLicense):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::didProvideRequest):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::didFailToProvideRequest):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstance const):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceAttached):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceDetached):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance):
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+        (WebCore::SourceBufferPrivateAVFObjC::destroyParser):
+        (WebCore::SourceBufferPrivateAVFObjC::setCDMInstance):
+
 2017-12-11  Eric Carlson  <eric.carlson@apple.com>
 
         Web Inspector: Optionally log WebKit log parameters as JSON
index c151ee0..3ffbb65 100644 (file)
@@ -56,6 +56,7 @@ static const Vector<FourCC>& validFairPlayStreamingSchemes()
         "cbcs",
         "cbc2",
         "cbc1",
+        "cenc",
     });
 
     return validSchemes;
@@ -95,7 +96,7 @@ static Vector<Ref<SharedBuffer>> extractSinfData(const SharedBuffer& buffer)
             continue;
 
         Vector<char> sinfData;
-        if (!WTF::base64URLDecode(keyID, { sinfData }))
+        if (!WTF::base64Decode(keyID, { sinfData }, WTF::Base64IgnoreSpacesAndNewLines))
             continue;
 
         sinfs.uncheckedAppend(SharedBuffer::create(WTFMove(sinfData)));
index 7b422c8..1c6c7fb 100644 (file)
@@ -75,6 +75,8 @@ public:
     bool shouldRetryRequestForReason(AVContentKeyRequest *, NSString *);
     void sessionIdentifierChanged(NSData *);
 
+    AVContentKeySession *contentKeySession() { return m_session.get(); }
+
 private:
     WeakPtr<CDMInstanceFairPlayStreamingAVFObjC> createWeakPtr() { return m_weakPtrFactory.createWeakPtr(*this); }
     bool isLicenseTypeSupported(LicenseType) const;
@@ -95,4 +97,6 @@ private:
 };
 }
 
+SPECIALIZE_TYPE_TRAITS_CDM_INSTANCE(WebCore::CDMInstanceFairPlayStreamingAVFObjC, WebCore::CDMInstance::ImplementationType::FairPlayStreaming)
+
 #endif // HAVE(AVCONTENTKEYSESSION)
index 76e03cc..1e9c239 100644 (file)
@@ -39,6 +39,7 @@
 
 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
 SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVContentKeySession);
+SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVContentKeyResponse);
 SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVURLAsset);
 SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVContentKeySystemFairPlayStreaming, NSString*)
 
@@ -238,9 +239,27 @@ void CDMInstanceFairPlayStreamingAVFObjC::requestLicense(LicenseType licenseType
     [m_session processContentKeyRequestWithIdentifier:nil initializationData:initData->createNSData().get() options:nil];
 }
 
-void CDMInstanceFairPlayStreamingAVFObjC::updateLicense(const String&, LicenseType, const SharedBuffer&, LicenseUpdateCallback)
+void CDMInstanceFairPlayStreamingAVFObjC::updateLicense(const String&, LicenseType, const SharedBuffer& responseData, LicenseUpdateCallback callback)
 {
-    notImplemented();
+    if (!m_request) {
+        callback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
+        return;
+    }
+    // FIXME(rdar://problem/35597141): use the future AVContentKeyRequest keyID property, rather than parsing it out of the init
+    // data, to get the keyID.
+    Vector<Ref<SharedBuffer>> keyIDs = CDMPrivateFairPlayStreaming::extractKeyIDsSinf(SharedBuffer::create(m_request.get().initializationData));
+    if (keyIDs.isEmpty()) {
+        callback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
+        return;
+    }
+
+    [m_request processContentKeyResponse:[getAVContentKeyResponseClass() contentKeyResponseWithFairPlayStreamingKeyResponseData:responseData.createNSData().get()]];
+
+    // FIXME(rdar://problem/35592277): stash the callback and call it once AVContentKeyResponse supports a success callback.
+    KeyStatusVector keyStatuses;
+    keyStatuses.reserveInitialCapacity(1);
+    keyStatuses.uncheckedAppend(std::make_pair(WTFMove(keyIDs.first()), KeyStatus::Usable));
+    callback(false, std::make_optional(WTFMove(keyStatuses)), std::nullopt, std::nullopt, Succeeded);
 }
 
 void CDMInstanceFairPlayStreamingAVFObjC::loadSession(LicenseType, const String&, const String&, LoadSessionCallback)
@@ -279,6 +298,7 @@ void CDMInstanceFairPlayStreamingAVFObjC::didProvideRequest(AVContentKeyRequest
     Vector<Ref<SharedBuffer>> keyIDs = CDMPrivateFairPlayStreaming::extractKeyIDsSinf(SharedBuffer::create(request.initializationData));
     if (keyIDs.isEmpty()) {
         m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
+        m_requestLicenseCallback = nullptr;
         return;
     }
 
@@ -292,6 +312,7 @@ void CDMInstanceFairPlayStreamingAVFObjC::didProvideRequest(AVContentKeyRequest
                 m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
             else
                 m_requestLicenseCallback(SharedBuffer::create(contentKeyRequestData.get()), m_sessionId, false, Succeeded);
+            m_requestLicenseCallback = nullptr;
         });
     }];
 }
@@ -310,7 +331,8 @@ void CDMInstanceFairPlayStreamingAVFObjC::didFailToProvideRequest(AVContentKeyRe
 {
     UNUSED_PARAM(request);
     UNUSED_PARAM(error);
-    m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
+    if (m_requestLicenseCallback)
+        m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
 }
 
 bool CDMInstanceFairPlayStreamingAVFObjC::shouldRetryRequestForReason(AVContentKeyRequest *request, NSString *reason)
index a72f488..6ae5b87 100644 (file)
@@ -124,6 +124,16 @@ public:
     AVStreamSession *streamSession();
     void setCDMSession(LegacyCDMSession*) override;
     CDMSessionMediaSourceAVFObjC* cdmSession() const { return m_session; }
+#endif
+
+#if ENABLE(ENCRYPTED_MEDIA)
+    void cdmInstanceAttached(CDMInstance&) final;
+    void cdmInstanceDetached(CDMInstance&) final;
+    void attemptToDecryptWithInstance(CDMInstance&) final;
+    CDMInstance* cdmInstance() const { return m_cdmInstance.get(); }
+#endif
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
     void keyNeeded(Uint8Array*);
 #endif
 #if ENABLE(ENCRYPTED_MEDIA)
@@ -305,6 +315,9 @@ private:
 #if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
     std::unique_ptr<VideoFullscreenLayerManager> m_videoFullscreenLayerManager;
 #endif
+#if ENABLE(ENCRYPTED_MEDIA)
+    RefPtr<CDMInstance> m_cdmInstance;
+#endif
 };
 
 }
index 4c3f439..0d907d4 100644 (file)
@@ -30,6 +30,7 @@
 
 #import "AVAssetTrackUtilities.h"
 #import "AVFoundationMIMETypeCache.h"
+#import "CDMInstance.h"
 #import "CDMSessionAVStreamSession.h"
 #import "CDMSessionMediaSourceAVFObjC.h"
 #import "FileSystem.h"
@@ -958,6 +959,27 @@ void MediaPlayerPrivateMediaSourceAVFObjC::keyNeeded(Uint8Array* initData)
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA)
+void MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceAttached(CDMInstance& instance)
+{
+    ASSERT(!m_cdmInstance);
+    m_cdmInstance = &instance;
+    for (auto& sourceBuffer : m_mediaSourcePrivate->sourceBuffers())
+        sourceBuffer->setCDMInstance(&instance);
+}
+
+void MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceDetached(CDMInstance& instance)
+{
+    ASSERT_UNUSED(instance, m_cdmInstance && m_cdmInstance == &instance);
+    for (auto& sourceBuffer : m_mediaSourcePrivate->sourceBuffers())
+        sourceBuffer->setCDMInstance(nullptr);
+
+    m_cdmInstance = nullptr;
+}
+
+void MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance(CDMInstance&)
+{
+}
+
 void MediaPlayerPrivateMediaSourceAVFObjC::initializationDataEncountered(const String& initDataType, RefPtr<ArrayBuffer>&& initData)
 {
     m_player->initializationDataEncountered(initDataType, WTFMove(initData));
index 07ddd87..90d556b 100644 (file)
@@ -55,6 +55,8 @@ typedef const struct opaqueCMFormatDescription *CMFormatDescriptionRef;
 
 namespace WebCore {
 
+class CDMInstance;
+class CDMInstanceFairPlayStreamingAVFObjC;
 class CDMSessionMediaSourceAVFObjC;
 class MediaSourcePrivateAVFObjC;
 class TimeRanges;
@@ -107,6 +109,7 @@ public:
     int protectedTrackID() const { return m_protectedTrackID; }
     AVStreamDataParser* parser() const { return m_parser.get(); }
     void setCDMSession(CDMSessionMediaSourceAVFObjC*);
+    void setCDMInstance(CDMInstance*);
 
     void flush();
 
@@ -182,6 +185,9 @@ private:
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA)
     CDMSessionMediaSourceAVFObjC* m_session { nullptr };
 #endif
+#if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
+    RefPtr<CDMInstanceFairPlayStreamingAVFObjC> m_cdmInstance;
+#endif
 
     std::optional<FloatSize> m_cachedSize;
     FloatSize m_currentSize;
index 813d64f..d6160ef 100644 (file)
@@ -30,6 +30,7 @@
 
 #import "AVAssetTrackUtilities.h"
 #import "AudioTrackPrivateMediaSourceAVFObjC.h"
+#import "CDMInstanceFairPlayStreamingAVFObjC.h"
 #import "CDMSessionAVContentKeySession.h"
 #import "CDMSessionMediaSourceAVFObjC.h"
 #import "InbandTextTrackPrivateAVFObjC.h"
@@ -675,18 +676,16 @@ void SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataFo
     }
 #endif
 
-#if ENABLE(ENCRYPTED_MEDIA)
+#if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
     if (m_mediaSource) {
         auto initDataBuffer = SharedBuffer::create(initData);
         m_mediaSource->player()->initializationDataEncountered("sinf", initDataBuffer->tryCreateArrayBuffer());
     }
 #endif
 
-#if !ENABLE(ENCRYPTED_MEDIA) && !ENABLE(LEGACY_ENCRYPTED_MEDIA)
     UNUSED_PARAM(initData);
     UNUSED_PARAM(trackID);
     UNUSED_PARAM(hasSessionSemaphore);
-#endif
 }
 
 void SourceBufferPrivateAVFObjC::setClient(SourceBufferPrivateClient* client)
@@ -766,6 +765,10 @@ void SourceBufferPrivateAVFObjC::destroyParser()
     if (m_mediaSource && m_mediaSource->player()->hasStreamSession())
         [m_mediaSource->player()->streamSession() removeStreamDataParser:m_parser.get()];
 #endif
+#if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
+    if (m_cdmInstance)
+        [m_cdmInstance->contentKeySession() removeContentKeyRecipient:m_parser.get()];
+#endif
 
     [m_delegate invalidate];
     m_delegate = nullptr;
@@ -918,6 +921,30 @@ void SourceBufferPrivateAVFObjC::setCDMSession(CDMSessionMediaSourceAVFObjC* ses
 #endif
 }
 
+void SourceBufferPrivateAVFObjC::setCDMInstance(CDMInstance* instance)
+{
+#if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
+    auto* fpsInstance = downcast<CDMInstanceFairPlayStreamingAVFObjC>(instance);
+    if (!fpsInstance || fpsInstance == m_cdmInstance)
+        return;
+
+    if (m_cdmInstance)
+        [m_cdmInstance->contentKeySession() removeContentKeyRecipient:m_parser.get()];
+
+    m_cdmInstance = fpsInstance;
+
+    if (m_cdmInstance) {
+        [m_cdmInstance->contentKeySession() addContentKeyRecipient:m_parser.get()];
+        if (m_hasSessionSemaphore) {
+            dispatch_semaphore_signal(m_hasSessionSemaphore.get());
+            m_hasSessionSemaphore = nullptr;
+        }
+    }
+#else
+    UNUSED_PARAM(instance);
+#endif
+}
+
 void SourceBufferPrivateAVFObjC::flush()
 {
     flushVideo();