[EME][Cocoa] Cannot play unmuxed video and audio fMP4 streams encrypted with differen...
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 31 Oct 2018 20:23:38 +0000 (20:23 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 31 Oct 2018 20:23:38 +0000 (20:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190946

Reviewed by Eric Carlson.

Use separate AVContentKeySessions per CDMInstanceSession (rather than one AVContentKeySession per
CDMInstance).

- Add a mechanism for sending a message out from platform/CDMInstance to MediaKeySession without
  requiring MediaKeySession to send a callback first.

- Move all the AVContentKeySession delegate methods from CDMInstanceFairPlayStreamingAVFObjC to
  CDMInstanceSessionFairPlayStreamingAVFObjC.

- Add a mechanism for requesting the correct CDMInstanceSession for a given KeyID.

- Support key renewal through a "renew" message.

- Remember the keyID in SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID()
  and ask for the correct CDMInstanceSession for that keyID in attemptToDecrypt().

- Pass the CDMInstance down from MediaPlayerPrivateMediaSourceAVFObjC -> SourceBufferPrivateAVFObjC.

* Modules/encryptedmedia/MediaKeySession.cpp:
(WebCore::MediaKeySession::sendMessage):
* Modules/encryptedmedia/MediaKeySession.h:
* platform/encryptedmedia/CDMInstanceSession.h:
* platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
* platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm:
(-[WebCoreFPSContentKeySessionDelegate initWithParent:]):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::initializeWithConfiguration):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::createSession):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
(WebCore::CDMInstanceFairPlayStreamingAVFObjC::sessionForKeyIDs const):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::CDMInstanceSessionFairPlayStreamingAVFObjC):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::~CDMInstanceSessionFairPlayStreamingAVFObjC):
(WebCore::keyIDsForRequest):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::keyIDs):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::updateLicense):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::closeSession):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::removeSessionData):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRequest):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRenewingRequest):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::didFailToProvideRequest):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::requestDidSucceed):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::nextRequest):
(WebCore::requestStatusToCDMStatus):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::keyStatuses const):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::ensureSession):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource):
(WebCore::MediaPlayerPrivateAVFoundationObjC::cdmInstanceAttached):
(WebCore::MediaPlayerPrivateAVFoundationObjC::cdmInstanceDetached):
(WebCore::MediaPlayerPrivateAVFoundationObjC::attemptToDecryptWithInstance):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceAttached):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceDetached):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::waitingForKey const):
* platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
(WebCore::MediaSourcePrivateAVFObjC::addSourceBuffer):
(WebCore::MediaSourcePrivateAVFObjC::cdmInstanceAttached):
(WebCore::MediaSourcePrivateAVFObjC::cdmInstanceDetached):
(WebCore::MediaSourcePrivateAVFObjC::attemptToDecryptWithInstance):
(WebCore::MediaSourcePrivateAVFObjC::waitingForKey const):
(WebCore::MediaSourcePrivateAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(WebCore::SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID):
(WebCore::SourceBufferPrivateAVFObjC::destroyParser):
(WebCore::SourceBufferPrivateAVFObjC::setCDMInstance):
(WebCore::SourceBufferPrivateAVFObjC::attemptToDecrypt):
(WebCore::SourceBufferPrivateAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):

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

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp
Source/WebCore/Modules/encryptedmedia/MediaKeySession.h
Source/WebCore/platform/encryptedmedia/CDMInstanceSession.h
Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm

index 7004bd6..d8e0a97 100644 (file)
@@ -1,3 +1,85 @@
+2018-10-26  Jer Noble  <jer.noble@apple.com>
+
+        [EME][Cocoa] Cannot play unmuxed video and audio fMP4 streams encrypted with different keys via MSE
+        https://bugs.webkit.org/show_bug.cgi?id=190946
+
+        Reviewed by Eric Carlson.
+
+        Use separate AVContentKeySessions per CDMInstanceSession (rather than one AVContentKeySession per
+        CDMInstance).
+
+        - Add a mechanism for sending a message out from platform/CDMInstance to MediaKeySession without
+          requiring MediaKeySession to send a callback first.
+
+        - Move all the AVContentKeySession delegate methods from CDMInstanceFairPlayStreamingAVFObjC to
+          CDMInstanceSessionFairPlayStreamingAVFObjC.
+
+        - Add a mechanism for requesting the correct CDMInstanceSession for a given KeyID.
+
+        - Support key renewal through a "renew" message.
+
+        - Remember the keyID in SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID()
+          and ask for the correct CDMInstanceSession for that keyID in attemptToDecrypt().
+
+        - Pass the CDMInstance down from MediaPlayerPrivateMediaSourceAVFObjC -> SourceBufferPrivateAVFObjC.
+
+        * Modules/encryptedmedia/MediaKeySession.cpp:
+        (WebCore::MediaKeySession::sendMessage):
+        * Modules/encryptedmedia/MediaKeySession.h:
+        * platform/encryptedmedia/CDMInstanceSession.h:
+        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.h:
+        * platform/graphics/avfoundation/objc/CDMInstanceFairPlayStreamingAVFObjC.mm:
+        (-[WebCoreFPSContentKeySessionDelegate initWithParent:]):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::initializeWithConfiguration):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::createSession):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
+        (WebCore::CDMInstanceFairPlayStreamingAVFObjC::sessionForKeyIDs const):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::CDMInstanceSessionFairPlayStreamingAVFObjC):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::~CDMInstanceSessionFairPlayStreamingAVFObjC):
+        (WebCore::keyIDsForRequest):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::keyIDs):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::updateLicense):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::closeSession):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::removeSessionData):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRequest):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRenewingRequest):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::didFailToProvideRequest):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::requestDidSucceed):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::nextRequest):
+        (WebCore::requestStatusToCDMStatus):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::keyStatuses const):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
+        (WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC::ensureSession):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::cdmInstanceAttached):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::cdmInstanceDetached):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::attemptToDecryptWithInstance):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceAttached):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceDetached):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::waitingForKey const):
+        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/MediaSourcePrivateAVFObjC.mm:
+        (WebCore::MediaSourcePrivateAVFObjC::addSourceBuffer):
+        (WebCore::MediaSourcePrivateAVFObjC::cdmInstanceAttached):
+        (WebCore::MediaSourcePrivateAVFObjC::cdmInstanceDetached):
+        (WebCore::MediaSourcePrivateAVFObjC::attemptToDecryptWithInstance):
+        (WebCore::MediaSourcePrivateAVFObjC::waitingForKey const):
+        (WebCore::MediaSourcePrivateAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+        (WebCore::SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID):
+        (WebCore::SourceBufferPrivateAVFObjC::destroyParser):
+        (WebCore::SourceBufferPrivateAVFObjC::setCDMInstance):
+        (WebCore::SourceBufferPrivateAVFObjC::attemptToDecrypt):
+        (WebCore::SourceBufferPrivateAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged):
+
 2018-10-31  Zach Li  <zacharyli323@gmail.com>
 
         Add credit card autofill button
index bb30a21..d9e96cc 100644 (file)
@@ -666,6 +666,11 @@ void MediaKeySession::updateKeyStatuses(CDMInstanceSession::KeyStatusVector&& in
         });
 }
 
+void MediaKeySession::sendMessage(CDMMessageType messageType, Ref<SharedBuffer>&& message)
+{
+    enqueueMessage(messageType, message);
+}
+
 void MediaKeySession::updateExpiration(double)
 {
     notImplemented();
index 9e5fd87..35d8174 100644 (file)
@@ -86,6 +86,7 @@ private:
 
     // CDMInstanceSessionClient
     void updateKeyStatuses(CDMInstanceSessionClient::KeyStatusVector&&) override;
+    void sendMessage(CDMMessageType, Ref<SharedBuffer>&& message) final;
 
     // EventTarget
     EventTargetInterface eventTargetInterface() const override { return MediaKeySessionEventTargetInterfaceType; }
index cf3b2d4..aa48f37 100644 (file)
@@ -45,6 +45,7 @@ public:
     using KeyStatus = CDMKeyStatus;
     using KeyStatusVector = Vector<std::pair<Ref<SharedBuffer>, KeyStatus>>;
     virtual void updateKeyStatuses(KeyStatusVector&&) = 0;
+    virtual void sendMessage(CDMMessageType, Ref<SharedBuffer>&& message) = 0;
 };
 
 class CDMInstanceSession : public RefCounted<CDMInstanceSession> {
index 5ef841d..d7a1900 100644 (file)
@@ -48,8 +48,7 @@ struct CDMMediaCapability;
 
 class CDMInstanceFairPlayStreamingAVFObjC final : public CDMInstance, public CanMakeWeakPtr<CDMInstanceFairPlayStreamingAVFObjC> {
 public:
-    CDMInstanceFairPlayStreamingAVFObjC();
-    virtual ~CDMInstanceFairPlayStreamingAVFObjC();
+    virtual ~CDMInstanceFairPlayStreamingAVFObjC() = default;
 
     static bool supportsPersistableState();
     static bool supportsPersistentKeys();
@@ -65,46 +64,26 @@ public:
     SuccessValue setStorageDirectory(const String&) final;
     RefPtr<CDMInstanceSession> createSession() final;
 
-    void processContentKeyRequestForSession(CDMInstanceSessionFairPlayStreamingAVFObjC&, RetainPtr<NSString>&& identifier, RetainPtr<NSData>&& initData);
-
     const String& keySystem() const final;
 
-    void didProvideRequest(AVContentKeyRequest *);
-    void didProvideRenewingRequest(AVContentKeyRequest *);
-    void didProvidePersistableRequest(AVContentKeyRequest *);
-    void didFailToProvideRequest(AVContentKeyRequest *, NSError *);
-    void requestDidSucceed(AVContentKeyRequest *);
-    bool shouldRetryRequestForReason(AVContentKeyRequest *, NSString *);
-    void sessionIdentifierChanged(NSData *);
-    void outputObscuredDueToInsufficientExternalProtectionChanged(bool);
-
     NSURL *storageDirectory() const { return m_storageDirectory.get(); }
-    AVContentKeySession *contentKeySession() { return m_session.get(); }
     bool persistentStateAllowed() const { return m_persistentStateAllowed; }
     SharedBuffer* serverCertificate() const { return m_serverCertificate.get(); }
 
-private:
-    void processNextContentKeyRequest();
-
-    struct ContentKeyRequest {
-        WeakPtr<CDMInstanceSessionFairPlayStreamingAVFObjC> sessionInstance;
-        RetainPtr<NSString> identifier;
-        RetainPtr<NSData> initData;
-    };
-    Vector<ContentKeyRequest> m_currentRequests;
-
-    HashMap<RetainPtr<AVContentKeyRequest>, WeakPtr<CDMInstanceSessionFairPlayStreamingAVFObjC>> m_requestMap;
+    void outputObscuredDueToInsufficientExternalProtectionChanged(bool);
+    CDMInstanceSessionFairPlayStreamingAVFObjC* sessionForKeyIDs(const Vector<Ref<SharedBuffer>>&) const;
 
+private:
     RefPtr<SharedBuffer> m_serverCertificate;
     bool m_persistentStateAllowed { true };
     RetainPtr<NSURL> m_storageDirectory;
-    RetainPtr<AVContentKeySession> m_session;
-    RetainPtr<WebCoreFPSContentKeySessionDelegate> m_delegate;
+    Vector<WeakPtr<CDMInstanceSessionFairPlayStreamingAVFObjC>> m_sessions;
 };
 
 class CDMInstanceSessionFairPlayStreamingAVFObjC final : public CDMInstanceSession, public CanMakeWeakPtr<CDMInstanceSessionFairPlayStreamingAVFObjC> {
 public:
     CDMInstanceSessionFairPlayStreamingAVFObjC(Ref<CDMInstanceFairPlayStreamingAVFObjC>&&);
+    virtual ~CDMInstanceSessionFairPlayStreamingAVFObjC();
 
     // CDMInstanceSession
     void requestLicense(LicenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&&) final;
@@ -126,15 +105,26 @@ public:
     void outputObscuredDueToInsufficientExternalProtectionChanged(bool);
 
     Vector<Ref<SharedBuffer>> keyIDs();
+    AVContentKeySession* contentKeySession() { return m_session.get(); }
 
 private:
+    AVContentKeySession* ensureSession();
     bool isLicenseTypeSupported(LicenseType) const;
 
+    KeyStatusVector keyStatuses() const;
+    void nextRequest();
+
     Ref<CDMInstanceFairPlayStreamingAVFObjC> m_instance;
-    RetainPtr<AVContentKeyRequest> m_request;
+    RetainPtr<AVContentKeySession> m_session;
+    RetainPtr<AVContentKeyRequest> m_currentRequest;
+    RetainPtr<WebCoreFPSContentKeySessionDelegate> m_delegate;
     Vector<RetainPtr<NSData>> m_expiredSessions;
     WeakPtr<CDMInstanceSessionClient> m_client;
     String m_sessionId;
+    bool m_outputObscured { false };
+
+    Vector<RetainPtr<AVContentKeyRequest>> m_pendingRequests;
+    Vector<RetainPtr<AVContentKeyRequest>> m_requests;
 
     LicenseCallback m_requestLicenseCallback;
     LicenseUpdateCallback m_updateLicenseCallback;
index 5ac24e4..c6ec20a 100644 (file)
@@ -37,6 +37,7 @@
 #import <AVFoundation/AVContentKeySession.h>
 #import <objc/runtime.h>
 #import <pal/spi/mac/AVFoundationSPI.h>
+#import <wtf/Algorithms.h>
 #import <wtf/SoftLinking.h>
 #import <wtf/text/StringHash.h>
 
@@ -53,12 +54,12 @@ SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVPersistableContentKeyRequest);
 static const NSString *PlaybackSessionIdKey = @"PlaybackSessionID";
 
 @interface WebCoreFPSContentKeySessionDelegate : NSObject<AVContentKeySessionDelegate> {
-    WebCore::CDMInstanceFairPlayStreamingAVFObjC* _parent;
+    WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC* _parent;
 }
 @end
 
 @implementation WebCoreFPSContentKeySessionDelegate
-- (id)initWithParent:(WebCore::CDMInstanceFairPlayStreamingAVFObjC *)parent
+- (id)initWithParent:(WebCore::CDMInstanceSessionFairPlayStreamingAVFObjC *)parent
 {
     if (!(self = [super init]))
         return nil;
@@ -134,17 +135,6 @@ static const NSString *PlaybackSessionIdKey = @"PlaybackSessionID";
 
 namespace WebCore {
 
-CDMInstanceFairPlayStreamingAVFObjC::CDMInstanceFairPlayStreamingAVFObjC()
-    : CDMInstance()
-    , m_delegate([[WebCoreFPSContentKeySessionDelegate alloc] initWithParent:this])
-{
-}
-
-CDMInstanceFairPlayStreamingAVFObjC::~CDMInstanceFairPlayStreamingAVFObjC()
-{
-    [m_delegate invalidate];
-}
-
 bool CDMInstanceFairPlayStreamingAVFObjC::supportsPersistableState()
 {
     return [getAVContentKeySessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)];
@@ -189,16 +179,6 @@ CDMInstance::SuccessValue CDMInstanceFairPlayStreamingAVFObjC::initializeWithCon
     if (!canLoadAVContentKeySystemFairPlayStreaming())
         return Failed;
 
-    if (configuration.persistentState == CDMRequirement::NotAllowed || !m_storageDirectory)
-        m_session = [getAVContentKeySessionClass() contentKeySessionWithKeySystem:getAVContentKeySystemFairPlayStreaming()];
-    else
-        m_session = [getAVContentKeySessionClass() contentKeySessionWithKeySystem:getAVContentKeySystemFairPlayStreaming() storageDirectoryAtURL:m_storageDirectory.get()];
-
-    if (!m_session)
-        return Failed;
-
-    [m_session setDelegate:m_delegate.get() queue:dispatch_get_main_queue()];
-
     return Succeeded;
 }
 
@@ -231,23 +211,9 @@ CDMInstance::SuccessValue CDMInstanceFairPlayStreamingAVFObjC::setStorageDirecto
 
 RefPtr<CDMInstanceSession> CDMInstanceFairPlayStreamingAVFObjC::createSession()
 {
-    return adoptRef(new CDMInstanceSessionFairPlayStreamingAVFObjC(*this));
-}
-
-void CDMInstanceFairPlayStreamingAVFObjC::processContentKeyRequestForSession(CDMInstanceSessionFairPlayStreamingAVFObjC& session, RetainPtr<NSString>&& identifier, RetainPtr<NSData>&& initData)
-{
-    m_currentRequests.append({ makeWeakPtr(session), WTFMove(identifier), WTFMove(initData) });
-    if (m_currentRequests.size() == 1)
-        processNextContentKeyRequest();
-}
-
-void CDMInstanceFairPlayStreamingAVFObjC::processNextContentKeyRequest()
-{
-    if (m_currentRequests.isEmpty())
-        return;
-
-    auto& nextRequest = m_currentRequests.first();
-    [m_session processContentKeyRequestWithIdentifier:nextRequest.identifier.get() initializationData:nextRequest.initData.get() options:nil];
+    auto session = adoptRef(*new CDMInstanceSessionFairPlayStreamingAVFObjC(*this));
+    m_sessions.append(makeWeakPtr(session.get()));
+    return session;
 }
 
 const String& CDMInstanceFairPlayStreamingAVFObjC::keySystem() const
@@ -256,109 +222,64 @@ const String& CDMInstanceFairPlayStreamingAVFObjC::keySystem() const
     return keySystem;
 }
 
-void CDMInstanceFairPlayStreamingAVFObjC::didProvideRequest(AVContentKeyRequest *request)
-{
-    if (m_currentRequests.isEmpty())
-        return;
-
-    auto currentRequest = WTFMove(m_currentRequests.first());
-    m_currentRequests.remove(0);
-
-    if (!currentRequest.sessionInstance)
-        return;
-
-    m_requestMap.set(request, currentRequest.sessionInstance);
-    currentRequest.sessionInstance->didProvideRequest(request);
-    processNextContentKeyRequest();
-}
-
-void CDMInstanceFairPlayStreamingAVFObjC::didProvideRenewingRequest(AVContentKeyRequest *request)
-{
-    if (m_currentRequests.isEmpty())
-        return;
-
-    auto currentRequest = WTFMove(m_currentRequests.first());
-    m_currentRequests.remove(0);
-
-    if (!currentRequest.sessionInstance)
-        return;
-
-    m_requestMap.set(request, currentRequest.sessionInstance);
-    currentRequest.sessionInstance->didProvideRenewingRequest(request);
-    processNextContentKeyRequest();
-}
-
-void CDMInstanceFairPlayStreamingAVFObjC::didProvidePersistableRequest(AVContentKeyRequest *request)
+void CDMInstanceFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
 {
-    if (m_currentRequests.isEmpty())
-        return;
-
-    auto currentRequest = WTFMove(m_currentRequests.first());
-    m_currentRequests.remove(0);
-
-    if (!currentRequest.sessionInstance)
-        return;
-
-    m_requestMap.set(request, currentRequest.sessionInstance);
-    currentRequest.sessionInstance->didProvidePersistableRequest(request);
-    processNextContentKeyRequest();
+    for (auto& sessionInterface : m_sessions) {
+        if (sessionInterface)
+            sessionInterface->outputObscuredDueToInsufficientExternalProtectionChanged(obscured);
+    }
 }
 
-void CDMInstanceFairPlayStreamingAVFObjC::didFailToProvideRequest(AVContentKeyRequest *request, NSError *error)
+CDMInstanceSessionFairPlayStreamingAVFObjC* CDMInstanceFairPlayStreamingAVFObjC::sessionForKeyIDs(const Vector<Ref<SharedBuffer>>& keyIDs) const
 {
-    if (auto sessionInterface = m_requestMap.get(request))
-        sessionInterface->didFailToProvideRequest(request, error);
-}
+    for (auto& sessionInterface : m_sessions) {
+        if (!sessionInterface)
+            continue;
 
-void CDMInstanceFairPlayStreamingAVFObjC::requestDidSucceed(AVContentKeyRequest *request)
-{
-    if (auto sessionInterface = m_requestMap.get(request))
-        sessionInterface->requestDidSucceed(request);
+        auto sessionKeys = sessionInterface->keyIDs();
+        if (anyOf(sessionKeys, [&](auto& sessionKey) {
+            return keyIDs.contains(sessionKey);
+        }))
+            return sessionInterface.get();
+    }
+    return nullptr;
 }
 
-bool CDMInstanceFairPlayStreamingAVFObjC::shouldRetryRequestForReason(AVContentKeyRequest *request, NSString *reason)
+CDMInstanceSessionFairPlayStreamingAVFObjC::CDMInstanceSessionFairPlayStreamingAVFObjC(Ref<CDMInstanceFairPlayStreamingAVFObjC>&& instance)
+    : m_instance(WTFMove(instance))
+    , m_delegate([[WebCoreFPSContentKeySessionDelegate alloc] initWithParent:this])
 {
-    if (auto sessionInterface = m_requestMap.get(request))
-        return sessionInterface->shouldRetryRequestForReason(request, reason);
-    return false;
 }
 
-void CDMInstanceFairPlayStreamingAVFObjC::sessionIdentifierChanged(NSData *sessionIdentifier)
+CDMInstanceSessionFairPlayStreamingAVFObjC::~CDMInstanceSessionFairPlayStreamingAVFObjC()
 {
-    for (auto sessionInterface : m_requestMap.values()) {
-        if (sessionInterface)
-            sessionInterface->sessionIdentifierChanged(sessionIdentifier);
-    }
+    [m_delegate invalidate];
 }
 
-void CDMInstanceFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
+static Vector<Ref<SharedBuffer>> keyIDsForRequest(AVContentKeyRequest* request)
 {
-    for (auto sessionInterface : m_requestMap.values()) {
-        if (sessionInterface)
-            sessionInterface->outputObscuredDueToInsufficientExternalProtectionChanged(obscured);
+    if ([request.identifier isKindOfClass:[NSString class]])
+        return Vector<Ref<SharedBuffer>>::from(SharedBuffer::create([(NSString *)request.identifier dataUsingEncoding:NSUTF8StringEncoding]));
+    if ([request.identifier isKindOfClass:[NSData class]])
+        return Vector<Ref<SharedBuffer>>::from(SharedBuffer::create((NSData *)request.identifier));
+    if (request.initializationData) {
+        if (auto sinfKeyIDs = CDMPrivateFairPlayStreaming::extractKeyIDsSinf(SharedBuffer::create(request.initializationData)))
+            return WTFMove(sinfKeyIDs.value());
     }
-}
-
-CDMInstanceSessionFairPlayStreamingAVFObjC::CDMInstanceSessionFairPlayStreamingAVFObjC(Ref<CDMInstanceFairPlayStreamingAVFObjC>&& instance)
-    : m_instance(WTFMove(instance))
-{
+    return { };
 }
 
 Vector<Ref<SharedBuffer>> CDMInstanceSessionFairPlayStreamingAVFObjC::keyIDs()
 {
     // FIXME(rdar://problem/35597141): use the future AVContentKeyRequest keyID property, rather than parsing it out of the init
     // data, to get the keyID.
-    if ([m_request.get().identifier isKindOfClass:[NSString class]])
-        return Vector<Ref<SharedBuffer>>::from(SharedBuffer::create([(NSString *)m_request.get().identifier dataUsingEncoding:NSUTF8StringEncoding]));
-    if ([m_request.get().identifier isKindOfClass:[NSData class]])
-        return Vector<Ref<SharedBuffer>>::from(SharedBuffer::create((NSData *)m_request.get().identifier));
-    if (m_request.get().initializationData) {
-        auto sinfKeyIDs = CDMPrivateFairPlayStreaming::extractKeyIDsSinf(SharedBuffer::create(m_request.get().initializationData));
-        if (!sinfKeyIDs)
-            return { };
-        return WTFMove(sinfKeyIDs.value());
+    Vector<Ref<SharedBuffer>> keyIDs;
+    for (auto& request : m_requests) {
+        for (auto& key : keyIDsForRequest(request.get()))
+            keyIDs.append(WTFMove(key));
     }
-    return { };
+
+    return keyIDs;
 }
 
 void CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense(LicenseType licenseType, const AtomicString& initDataType, Ref<SharedBuffer>&& initData, LicenseCallback&& callback)
@@ -373,6 +294,11 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense(LicenseType lice
         return;
     }
 
+    if (!ensureSession()) {
+        callback(SharedBuffer::create(), emptyString(), false, Failed);
+        return;
+    }
+
     RetainPtr<NSString> identifier;
     RetainPtr<NSData> initializationData;
 
@@ -386,7 +312,7 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::requestLicense(LicenseType lice
     }
 
     m_requestLicenseCallback = WTFMove(callback);
-    m_instance->processContentKeyRequestForSession(*this, WTFMove(identifier), WTFMove(initializationData));
+    [m_session processContentKeyRequestWithIdentifier:identifier.get() initializationData:initializationData.get() options:nil];
 }
 
 static bool isEqual(const SharedBuffer& data, const String& value)
@@ -428,17 +354,23 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::updateLicense(const String&, Li
         return;
     }
 
-    if (!m_request) {
+    if (!m_requests.isEmpty() && isEqual(responseData, "renew"_s)) {
+        [m_session renewExpiringResponseDataForContentKeyRequest:m_requests.last().get()];
+        m_updateLicenseCallback = WTFMove(callback);
+        return;
+    }
+
+    if (!m_currentRequest) {
         callback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
         return;
     }
-    Vector<Ref<SharedBuffer>> keyIDs = this->keyIDs();
+    Vector<Ref<SharedBuffer>> keyIDs = keyIDsForRequest(m_currentRequest.get());
     if (keyIDs.isEmpty()) {
         callback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
         return;
     }
 
-    [m_request processContentKeyResponse:[getAVContentKeyResponseClass() contentKeyResponseWithFairPlayStreamingKeyResponseData:responseData.createNSData().get()]];
+    [m_currentRequest processContentKeyResponse:[getAVContentKeyResponseClass() contentKeyResponseWithFairPlayStreamingKeyResponseData:responseData.createNSData().get()]];
 
     // FIXME(rdar://problem/35592277): stash the callback and call it once AVContentKeyResponse supports a success callback.
     struct objc_method_description method = protocol_getMethodDescription(@protocol(AVContentKeySessionDelegate), @selector(contentKeySession:contentKeyRequestDidSucceed:), NO, YES);
@@ -506,14 +438,16 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::closeSession(const String&, Clo
         m_removeSessionDataCallback({ }, std::nullopt, Failed);
         m_removeSessionDataCallback = nullptr;
     }
-    m_request = nullptr;
+    m_currentRequest = nullptr;
+    m_pendingRequests.clear();
+    m_requests.clear();
     callback();
 }
 
 void CDMInstanceSessionFairPlayStreamingAVFObjC::removeSessionData(const String& sessionId, LicenseType licenseType, RemoveSessionDataCallback&& callback)
 {
     // FIXME: We should be able to expire individual AVContentKeyRequests rather than the entire AVContentKeySession.
-    [m_instance->contentKeySession() expire];
+    [m_session expire];
 
     if (licenseType == LicenseType::PersistentUsageRecord) {
         auto* storageDirectory = m_instance->storageDirectory();
@@ -564,31 +498,41 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::clearClient()
 
 void CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRequest(AVContentKeyRequest *request)
 {
-    m_request = request;
-    if (!m_requestLicenseCallback)
+    if (m_currentRequest) {
+        m_pendingRequests.append(request);
         return;
+    }
+
+    m_currentRequest = request;
+
+    ASSERT(!m_requests.contains(m_currentRequest));
+    m_requests.append(m_currentRequest);
 
     RetainPtr<NSData> appIdentifier;
     if (auto* certificate = m_instance->serverCertificate())
         appIdentifier = certificate->createNSData();
-    Vector<Ref<SharedBuffer>> keyIDs = this->keyIDs();
 
+    auto keyIDs = keyIDsForRequest(request);
     if (keyIDs.isEmpty()) {
-        m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
-        m_requestLicenseCallback = nullptr;
+        if (m_requestLicenseCallback) {
+            m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
+            m_requestLicenseCallback = nullptr;
+        }
         return;
     }
 
     RetainPtr<NSData> contentIdentifier = keyIDs.first()->createNSData();
-    [m_request makeStreamingContentKeyRequestDataForApp:appIdentifier.get() contentIdentifier:contentIdentifier.get() options:nil completionHandler:[this, weakThis = makeWeakPtr(*this)] (NSData *contentKeyRequestData, NSError *error) mutable {
+    [m_currentRequest makeStreamingContentKeyRequestDataForApp:appIdentifier.get() contentIdentifier:contentIdentifier.get() options:nil completionHandler:[this, weakThis = makeWeakPtr(*this)] (NSData *contentKeyRequestData, NSError *error) mutable {
         callOnMainThread([this, weakThis = WTFMove(weakThis), error = retainPtr(error), contentKeyRequestData = retainPtr(contentKeyRequestData)] {
-            if (!weakThis || !m_requestLicenseCallback)
+            if (!weakThis)
                 return;
 
-            if (error)
+            if (error && m_requestLicenseCallback)
                 m_requestLicenseCallback(SharedBuffer::create(), m_sessionId, false, Failed);
-            else
+            else if (m_requestLicenseCallback)
                 m_requestLicenseCallback(SharedBuffer::create(contentKeyRequestData.get()), m_sessionId, false, Succeeded);
+            else if (m_client)
+                m_client->sendMessage(CDMMessageType::LicenseRequest, SharedBuffer::create(contentKeyRequestData.get()));
             m_requestLicenseCallback = nullptr;
         });
     }];
@@ -596,7 +540,38 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRequest(AVContentKeyR
 
 void CDMInstanceSessionFairPlayStreamingAVFObjC::didProvideRenewingRequest(AVContentKeyRequest *request)
 {
-    UNUSED_PARAM(request);
+    ASSERT(!m_requestLicenseCallback);
+    if (m_currentRequest) {
+        m_pendingRequests.append(request);
+        return;
+    }
+
+    m_currentRequest = request;
+
+    // The assumption here is that AVContentKeyRequest will only ever notify us of a renewing request as a result of calling
+    // -renewExpiringResponseDataForContentKeyRequest: with an existing request.
+    ASSERT(m_requests.contains(m_currentRequest));
+
+    RetainPtr<NSData> appIdentifier;
+    if (auto* certificate = m_instance->serverCertificate())
+        appIdentifier = certificate->createNSData();
+    auto keyIDs = keyIDsForRequest(m_currentRequest.get());
+
+    RetainPtr<NSData> contentIdentifier = keyIDs.first()->createNSData();
+    [m_currentRequest makeStreamingContentKeyRequestDataForApp:appIdentifier.get() contentIdentifier:contentIdentifier.get() options:nil completionHandler:[this, weakThis = makeWeakPtr(*this)] (NSData *contentKeyRequestData, NSError *error) mutable {
+        callOnMainThread([this, weakThis = WTFMove(weakThis), error = retainPtr(error), contentKeyRequestData = retainPtr(contentKeyRequestData)] {
+            if (!weakThis || !m_client || error)
+                return;
+
+            if (error && m_updateLicenseCallback)
+                m_updateLicenseCallback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
+            else if (m_updateLicenseCallback)
+                m_updateLicenseCallback(false, std::nullopt, std::nullopt, Message(MessageType::LicenseRenewal, SharedBuffer::create(contentKeyRequestData.get())), Succeeded);
+            else if (m_client)
+                m_client->sendMessage(CDMMessageType::LicenseRenewal, SharedBuffer::create(contentKeyRequestData.get()));
+            m_updateLicenseCallback = nullptr;
+        });
+    }];
 }
 
 void CDMInstanceSessionFairPlayStreamingAVFObjC::didProvidePersistableRequest(AVContentKeyRequest *request)
@@ -610,19 +585,35 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::didFailToProvideRequest(AVConte
     UNUSED_PARAM(error);
     if (m_updateLicenseCallback)
         m_updateLicenseCallback(false, std::nullopt, std::nullopt, std::nullopt, Failed);
+
+    m_currentRequest = nullptr;
+
+    nextRequest();
 }
 
 void CDMInstanceSessionFairPlayStreamingAVFObjC::requestDidSucceed(AVContentKeyRequest *request)
 {
     UNUSED_PARAM(request);
-    if (!m_updateLicenseCallback)
+    if (m_updateLicenseCallback)
+        m_updateLicenseCallback(false, std::make_optional(keyStatuses()), std::nullopt, std::nullopt, Succeeded);
+
+    m_currentRequest = nullptr;
+
+    nextRequest();
+}
+
+void CDMInstanceSessionFairPlayStreamingAVFObjC::nextRequest()
+{
+    if (m_pendingRequests.isEmpty())
         return;
 
-    Vector<Ref<SharedBuffer>> keyIDs = this->keyIDs();
-    KeyStatusVector keyStatuses;
-    keyStatuses.reserveInitialCapacity(1);
-    keyStatuses.uncheckedAppend(std::make_pair(WTFMove(keyIDs.first()), KeyStatus::Usable));
-    m_updateLicenseCallback(false, std::make_optional(WTFMove(keyStatuses)), std::nullopt, std::nullopt, Succeeded);
+    RetainPtr<AVContentKeyRequest> nextRequest = m_pendingRequests.first();
+    m_pendingRequests.remove(0);
+
+    if (nextRequest.get().renewsExpiringResponseData)
+        didProvideRenewingRequest(nextRequest.get());
+    else
+        didProvideRequest(nextRequest.get());
 }
 
 bool CDMInstanceSessionFairPlayStreamingAVFObjC::shouldRetryRequestForReason(AVContentKeyRequest *request, NSString *reason)
@@ -644,37 +635,66 @@ void CDMInstanceSessionFairPlayStreamingAVFObjC::sessionIdentifierChanged(NSData
     m_sessionId = sessionIdentifierString.get();
 }
 
-void CDMInstanceSessionFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
+static auto requestStatusToCDMStatus(AVContentKeyRequestStatus status)
 {
-    if (!m_client || !m_request)
-        return;
-
-    CDMKeyStatus status;
-    if (obscured)
-        status = CDMKeyStatus::OutputRestricted;
-    else {
-        switch (m_request.get().status) {
+    switch (status) {
         case AVContentKeyRequestStatusRequestingResponse:
         case AVContentKeyRequestStatusRetried:
-            status = CDMKeyStatus::StatusPending;
-            break;
+            return CDMKeyStatus::StatusPending;
         case AVContentKeyRequestStatusReceivedResponse:
         case AVContentKeyRequestStatusRenewed:
-            status = CDMKeyStatus::Usable;
-            break;
+            return CDMKeyStatus::Usable;
         case AVContentKeyRequestStatusCancelled:
-            status = CDMKeyStatus::Released;
-            break;
+            return CDMKeyStatus::Released;
         case AVContentKeyRequestStatusFailed:
-            status = CDMKeyStatus::InternalError;
-            break;
-        }
+            return CDMKeyStatus::InternalError;
+    }
+}
+
+CDMInstanceSession::KeyStatusVector CDMInstanceSessionFairPlayStreamingAVFObjC::keyStatuses() const
+{
+    KeyStatusVector keyStatuses;
+
+    for (auto& request : m_requests) {
+        auto keyIDs = keyIDsForRequest(request.get());
+        auto status = requestStatusToCDMStatus(request.get().status);
+        if (m_outputObscured)
+            status = CDMKeyStatus::OutputRestricted;
+
+        for (auto& keyID : keyIDs)
+            keyStatuses.append({ WTFMove(keyID), status });
     }
 
-    auto keyStatuses = keyIDs().map([status] (const Ref<SharedBuffer>& keyID) -> KeyStatusVector::ValueType {
-        return { keyID.copyRef(), status };
-    });
-    m_client->updateKeyStatuses(WTFMove(keyStatuses));
+    return keyStatuses;
+}
+
+void CDMInstanceSessionFairPlayStreamingAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
+{
+    if (obscured == m_outputObscured)
+        return;
+
+    m_outputObscured = obscured;
+
+    if (m_client)
+        m_client->updateKeyStatuses(keyStatuses());
+}
+
+AVContentKeySession* CDMInstanceSessionFairPlayStreamingAVFObjC::ensureSession()
+{
+    if (m_session)
+        return m_session.get();
+
+    auto* storageDirectory = m_instance->storageDirectory();
+    if (!m_instance->persistentStateAllowed() || !storageDirectory)
+        m_session = [getAVContentKeySessionClass() contentKeySessionWithKeySystem:getAVContentKeySystemFairPlayStreaming()];
+    else
+        m_session = [getAVContentKeySessionClass() contentKeySessionWithKeySystem:getAVContentKeySystemFairPlayStreaming() storageDirectoryAtURL:storageDirectory];
+
+    if (!m_session)
+        return nullptr;
+
+    [m_session setDelegate:m_delegate.get() queue:dispatch_get_main_queue()];
+    return m_session.get();
 }
 
 bool CDMInstanceSessionFairPlayStreamingAVFObjC::isLicenseTypeSupported(LicenseType licenseType) const
index 27b3fa5..5156edd 100644 (file)
@@ -61,6 +61,7 @@ class CDMSessionAVFoundationObjC;
 class InbandMetadataTextTrackPrivateAVF;
 class MediaSelectionGroupAVFObjC;
 class PixelBufferConformerCV;
+class SharedBuffer;
 class VideoFullscreenLayerManagerObjC;
 class VideoTextureCopierCV;
 class VideoTrackPrivateAVFObjC;
@@ -411,6 +412,7 @@ private:
     RetainPtr<NSArray> m_currentMetaData;
     FloatSize m_cachedPresentationSize;
     MediaTime m_cachedDuration;
+    RefPtr<SharedBuffer> m_keyID;
     double m_cachedRate;
     mutable long long m_cachedTotalBytes;
     unsigned m_pendingStatusChanges;
index c4750fb..a7b7bb9 100644 (file)
@@ -1780,8 +1780,8 @@ bool MediaPlayerPrivateAVFoundationObjC::shouldWaitForLoadingOfResource(AVAssetR
             return false;
 
         RetainPtr<NSData> keyURIData = [keyURI dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
-        auto keyURIBuffer = SharedBuffer::create(keyURIData.get());
-        player()->initializationDataEncountered("skd"_s, keyURIBuffer->tryCreateArrayBuffer());
+        m_keyID = SharedBuffer::create(keyURIData.get());
+        player()->initializationDataEncountered("skd"_s, m_keyID->tryCreateArrayBuffer());
         setWaitingForKey(true);
 #endif
         return true;
@@ -2470,7 +2470,6 @@ void MediaPlayerPrivateAVFoundationObjC::cdmInstanceAttached(CDMInstance& instan
         cdmInstanceDetached(*m_cdmInstance);
 
     m_cdmInstance = &fpsInstance;
-    [m_cdmInstance->contentKeySession() addContentKeyRecipient:m_avAsset.get()];
 #else
     UNUSED_PARAM(instance);
 #endif
@@ -2480,7 +2479,6 @@ void MediaPlayerPrivateAVFoundationObjC::cdmInstanceDetached(CDMInstance& instan
 {
 #if HAVE(AVCONTENTKEYSESSION)
     ASSERT_UNUSED(instance, m_cdmInstance && m_cdmInstance == &instance);
-    [m_cdmInstance->contentKeySession() removeContentKeyRecipient:m_avAsset.get()];
     m_cdmInstance = nullptr;
 #else
     UNUSED_PARAM(instance);
@@ -2489,6 +2487,15 @@ void MediaPlayerPrivateAVFoundationObjC::cdmInstanceDetached(CDMInstance& instan
 
 void MediaPlayerPrivateAVFoundationObjC::attemptToDecryptWithInstance(CDMInstance&)
 {
+    if (!m_keyID || !m_cdmInstance)
+        return;
+
+    auto instanceSession = m_cdmInstance->sessionForKeyIDs(Vector<Ref<SharedBuffer>>::from(*m_keyID));
+    if (!instanceSession)
+        return;
+
+    [instanceSession->contentKeySession() addContentKeyRecipient:m_avAsset.get()];
+
     auto keyURIToRequestMap = WTFMove(m_keyURIToRequestMap);
     for (auto& request : keyURIToRequestMap.values()) {
         if (auto *infoRequest = request.get().contentInformationRequest)
index 962224e..03605fc 100644 (file)
@@ -123,7 +123,6 @@ public:
     bool waitingForKey() const final;
 
     void waitingForKeyChanged();
-    CDMInstance* cdmInstance() const { return m_cdmInstance.get(); }
 #endif
 
 #if ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)
@@ -309,9 +308,6 @@ private:
     bool m_shouldPlayToTarget { false };
 #endif
     std::unique_ptr<VideoFullscreenLayerManagerObjC> m_videoFullscreenLayerManager;
-#if ENABLE(ENCRYPTED_MEDIA)
-    RefPtr<CDMInstance> m_cdmInstance;
-#endif
 };
 
 }
index f2687a0..fa7325b 100644 (file)
@@ -30,9 +30,7 @@
 
 #import "AVAssetTrackUtilities.h"
 #import "AVFoundationMIMETypeCache.h"
-#import "CDMInstance.h"
 #import "CDMSessionAVStreamSession.h"
-#import "CDMSessionMediaSourceAVFObjC.h"
 #import "FileSystem.h"
 #import "GraphicsContextCG.h"
 #import "Logging.h"
@@ -50,7 +48,6 @@
 #import <objc_runtime.h>
 #import <pal/avfoundation/MediaTimeAVFoundation.h>
 #import <pal/spi/mac/AVFoundationSPI.h>
-#import <wtf/Algorithms.h>
 #import <wtf/Deque.h>
 #import <wtf/MainThread.h>
 #import <wtf/NeverDestroyed.h>
@@ -933,8 +930,8 @@ void MediaPlayerPrivateMediaSourceAVFObjC::keyNeeded(Uint8Array* initData)
 void MediaPlayerPrivateMediaSourceAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
 {
 #if ENABLE(ENCRYPTED_MEDIA)
-    if (m_cdmInstance)
-        m_cdmInstance->setHDCPStatus(obscured ? CDMInstance::HDCPStatus::OutputRestricted : CDMInstance::HDCPStatus::Valid);
+    if (m_mediaSourcePrivate)
+        m_mediaSourcePrivate->outputObscuredDueToInsufficientExternalProtectionChanged(obscured);
 #else
     UNUSED_PARAM(obscured);
 #endif
@@ -944,30 +941,25 @@ void MediaPlayerPrivateMediaSourceAVFObjC::outputObscuredDueToInsufficientExtern
 #if ENABLE(ENCRYPTED_MEDIA)
 void MediaPlayerPrivateMediaSourceAVFObjC::cdmInstanceAttached(CDMInstance& instance)
 {
-    ASSERT(!m_cdmInstance);
-    m_cdmInstance = &instance;
-    for (auto& sourceBuffer : m_mediaSourcePrivate->sourceBuffers())
-        sourceBuffer->setCDMInstance(&instance);
+    if (m_mediaSourcePrivate)
+        m_mediaSourcePrivate->cdmInstanceAttached(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;
+    if (m_mediaSourcePrivate)
+        m_mediaSourcePrivate->cdmInstanceDetached(instance);
 }
 
-void MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance(CDMInstance&)
+void MediaPlayerPrivateMediaSourceAVFObjC::attemptToDecryptWithInstance(CDMInstance& instance)
 {
+    if (m_mediaSourcePrivate)
+        m_mediaSourcePrivate->attemptToDecryptWithInstance(instance);
 }
 
 bool MediaPlayerPrivateMediaSourceAVFObjC::waitingForKey() const
 {
-    return anyOf(m_mediaSourcePrivate->sourceBuffers(), [] (auto& sourceBuffer) {
-        return sourceBuffer->waitingForKey();
-    });
+    return m_mediaSourcePrivate ? m_mediaSourcePrivate->waitingForKey() : false;
 }
 
 void MediaPlayerPrivateMediaSourceAVFObjC::waitingForKeyChanged()
index ac4ac90..0c04af9 100644 (file)
@@ -43,6 +43,7 @@ typedef struct opaqueCMSampleBuffer *CMSampleBufferRef;
 
 namespace WebCore {
 
+class CDMInstance;
 class LegacyCDMSession;
 class MediaPlayerPrivateMediaSourceAVFObjC;
 class MediaSourcePrivateClient;
@@ -84,6 +85,16 @@ public:
     void setVideoLayer(AVSampleBufferDisplayLayer*);
     void setDecompressionSession(WebCoreDecompressionSession*);
 
+#if ENABLE(ENCRYPTED_MEDIA)
+    void cdmInstanceAttached(CDMInstance&);
+    void cdmInstanceDetached(CDMInstance&);
+    void attemptToDecryptWithInstance(CDMInstance&);
+    bool waitingForKey() const;
+    
+    CDMInstance* cdmInstance() const { return m_cdmInstance.get(); }
+    void outputObscuredDueToInsufficientExternalProtectionChanged(bool);
+#endif
+
 private:
     MediaSourcePrivateAVFObjC(MediaPlayerPrivateMediaSourceAVFObjC*, MediaSourcePrivateClient*);
 
@@ -106,6 +117,9 @@ private:
     Deque<SourceBufferPrivateAVFObjC*> m_sourceBuffersNeedingSessions;
     SourceBufferPrivateAVFObjC* m_sourceBufferWithSelectedVideo { nullptr };
     bool m_isEnded;
+#if ENABLE(ENCRYPTED_MEDIA)
+    RefPtr<CDMInstance> m_cdmInstance;
+#endif
 };
 
 }
index 0a2b043..e084c90 100644 (file)
 
 #if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION)
 
+#import "CDMInstance.h"
 #import "CDMSessionMediaSourceAVFObjC.h"
 #import "ContentType.h"
 #import "MediaPlayerPrivateMediaSourceAVFObjC.h"
 #import "MediaSourcePrivateClient.h"
 #import "SourceBufferPrivateAVFObjC.h"
 #import <objc/runtime.h>
+#import <wtf/Algorithms.h>
 #import <wtf/SoftLinking.h>
 #import <wtf/text/AtomicString.h>
 
@@ -70,8 +72,12 @@ MediaSourcePrivate::AddStatus MediaSourcePrivateAVFObjC::addSourceBuffer(const C
     if (MediaPlayerPrivateMediaSourceAVFObjC::supportsType(parameters) == MediaPlayer::IsNotSupported)
         return NotSupported;
 
-    m_sourceBuffers.append(SourceBufferPrivateAVFObjC::create(this));
-    outPrivate = m_sourceBuffers.last();
+    auto newSourceBuffer = SourceBufferPrivateAVFObjC::create(this);
+#if ENABLE(ENCRYPTED_MEDIA)
+    newSourceBuffer->setCDMInstance(m_cdmInstance.get());
+#endif
+    outPrivate = newSourceBuffer;
+    m_sourceBuffers.append(WTFMove(newSourceBuffer));
 
     return Ok;
 }
@@ -245,6 +251,45 @@ void MediaSourcePrivateAVFObjC::setDecompressionSession(WebCoreDecompressionSess
         m_sourceBufferWithSelectedVideo->setDecompressionSession(decompressionSession);
 }
 
+#if ENABLE(ENCRYPTED_MEDIA)
+void MediaSourcePrivateAVFObjC::cdmInstanceAttached(CDMInstance& instance)
+{
+    ASSERT(!m_cdmInstance);
+    m_cdmInstance = &instance;
+    for (auto& sourceBuffer : m_sourceBuffers)
+        sourceBuffer->setCDMInstance(&instance);
+}
+
+void MediaSourcePrivateAVFObjC::cdmInstanceDetached(CDMInstance& instance)
+{
+    ASSERT_UNUSED(instance, m_cdmInstance && m_cdmInstance == &instance);
+    for (auto& sourceBuffer : m_sourceBuffers)
+        sourceBuffer->setCDMInstance(nullptr);
+
+    m_cdmInstance = nullptr;
+}
+
+void MediaSourcePrivateAVFObjC::attemptToDecryptWithInstance(CDMInstance& instance)
+{
+    ASSERT_UNUSED(instance, m_cdmInstance && m_cdmInstance == &instance);
+    for (auto& sourceBuffer : m_sourceBuffers)
+        sourceBuffer->attemptToDecrypt();
+}
+
+bool MediaSourcePrivateAVFObjC::waitingForKey() const
+{
+    return anyOf(m_sourceBuffers, [] (auto& sourceBuffer) {
+        return sourceBuffer->waitingForKey();
+    });
+}
+
+void MediaSourcePrivateAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
+{
+    if (m_cdmInstance)
+        m_cdmInstance->setHDCPStatus(obscured ? CDMInstance::HDCPStatus::OutputRestricted : CDMInstance::HDCPStatus::Valid);
+}
+#endif
+
 void MediaSourcePrivateAVFObjC::setSourceBufferWithSelectedVideo(SourceBufferPrivateAVFObjC* sourceBuffer)
 {
     if (m_sourceBufferWithSelectedVideo) {
index 52013a1..e7a0fdc 100644 (file)
@@ -66,6 +66,7 @@ class VideoTrackPrivate;
 class AudioTrackPrivateMediaSourceAVFObjC;
 class VideoTrackPrivateMediaSourceAVFObjC;
 class WebCoreDecompressionSession;
+class SharedBuffer;
 
 class SourceBufferPrivateAVFObjCErrorClient {
 public:
@@ -184,6 +185,7 @@ private:
 #endif
 #if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
     RefPtr<CDMInstanceFairPlayStreamingAVFObjC> m_cdmInstance;
+    Vector<Ref<SharedBuffer>> m_keyIDs;
 #endif
 
     std::optional<FloatSize> m_cachedSize;
index 1113e35..75e01d8 100644 (file)
@@ -30,6 +30,7 @@
 
 #import "AVAssetTrackUtilities.h"
 #import "AudioTrackPrivateMediaSourceAVFObjC.h"
+#import "CDMFairPlayStreaming.h"
 #import "CDMInstanceFairPlayStreamingAVFObjC.h"
 #import "CDMSessionAVContentKeySession.h"
 #import "CDMSessionMediaSourceAVFObjC.h"
@@ -665,11 +666,27 @@ void SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataFo
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
-    if (m_mediaSource) {
-        auto initDataBuffer = SharedBuffer::create(initData);
-        m_mediaSource->player()->initializationDataEncountered("sinf", initDataBuffer->tryCreateArrayBuffer());
+    //
+    auto initDataBuffer = SharedBuffer::create(initData);
+    auto keyIDs = CDMPrivateFairPlayStreaming::extractKeyIDsSinf(initDataBuffer);
+    if (!keyIDs)
+        return;
+
+    if (m_cdmInstance) {
+        if (auto instanceSession = m_cdmInstance->sessionForKeyIDs(keyIDs.value())) {
+            [instanceSession->contentKeySession() addContentKeyRecipient:m_parser.get()];
+            if (m_hasSessionSemaphore) {
+                m_hasSessionSemaphore->signal();
+                m_hasSessionSemaphore = nullptr;
+            }
+            m_waitingForKey = false;
+            return;
+        }
     }
 
+    m_keyIDs = WTFMove(keyIDs.value());
+    m_mediaSource->player()->initializationDataEncountered("sinf", initDataBuffer->tryCreateArrayBuffer());
+
     m_waitingForKey = true;
     m_mediaSource->player()->waitingForKeyChanged();
 #endif
@@ -758,8 +775,10 @@ void SourceBufferPrivateAVFObjC::destroyParser()
         [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()];
+    if (m_cdmInstance) {
+        if (auto instanceSession = m_cdmInstance->sessionForKeyIDs(m_keyIDs))
+            [instanceSession->contentKeySession() removeContentKeyRecipient:m_parser.get()];
+    }
 #endif
 
     [m_delegate invalidate];
@@ -918,25 +937,32 @@ void SourceBufferPrivateAVFObjC::setCDMInstance(CDMInstance* instance)
 {
 #if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
     auto* fpsInstance = downcast<CDMInstanceFairPlayStreamingAVFObjC>(instance);
-    if (!fpsInstance || fpsInstance == m_cdmInstance)
+    if (fpsInstance == m_cdmInstance)
         return;
 
-    if (m_cdmInstance)
-        [m_cdmInstance->contentKeySession() removeContentKeyRecipient:m_parser.get()];
-
     m_cdmInstance = fpsInstance;
+    attemptToDecrypt();
+#else
+    UNUSED_PARAM(instance);
+#endif
+}
 
-    if (m_cdmInstance) {
-        [m_cdmInstance->contentKeySession() addContentKeyRecipient:m_parser.get()];
-        if (m_hasSessionSemaphore) {
-            m_hasSessionSemaphore->signal();
-            m_hasSessionSemaphore = nullptr;
-        }
+void SourceBufferPrivateAVFObjC::attemptToDecrypt()
+{
+#if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
+    if (!m_cdmInstance || m_keyIDs.isEmpty() || !m_waitingForKey)
+        return;
+
+    auto instanceSession = m_cdmInstance->sessionForKeyIDs(m_keyIDs);
+    if (!instanceSession)
+        return;
+
+    [instanceSession->contentKeySession() addContentKeyRecipient:m_parser.get()];
+    if (m_hasSessionSemaphore) {
+        m_hasSessionSemaphore->signal();
+        m_hasSessionSemaphore = nullptr;
     }
     m_waitingForKey = false;
-    m_mediaSource->player()->waitingForKeyChanged();
-#else
-    UNUSED_PARAM(instance);
 #endif
 }
 
@@ -983,9 +1009,8 @@ void SourceBufferPrivateAVFObjC::layerDidReceiveError(AVSampleBufferDisplayLayer
 void SourceBufferPrivateAVFObjC::outputObscuredDueToInsufficientExternalProtectionChanged(bool obscured)
 {
 #if ENABLE(ENCRYPTED_MEDIA) && HAVE(AVCONTENTKEYSESSION)
-    auto player = m_mediaSource ? m_mediaSource->player() : nullptr;
-    if (player && player->cdmInstance()) {
-        player->outputObscuredDueToInsufficientExternalProtectionChanged(obscured);
+    if (m_mediaSource->cdmInstance()) {
+        m_mediaSource->outputObscuredDueToInsufficientExternalProtectionChanged(obscured);
         return;
     }
 #else