Adopt AVContentKeySession
authorjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Dec 2015 22:41:44 +0000 (22:41 +0000)
committerjer.noble@apple.com <jer.noble@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Dec 2015 22:41:44 +0000 (22:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151221

Reviewed by Eric Carlson.

Adopt a new API for managing key state, AVContentKeySession. Because this necessitates a change
in both the initialization data returned by the needkey event, and passed into the createSession()
method, bump the protocol version number (to 3), and keep supporting the old key management API
for legacy content.

To do so, move most of the implementation of CDMPrivateMediaSourceAVFObjC into a new subclass,
CDMSessionAVStreamSession, and add a new subclass, CDMSessionAVContentKeySession, to support the
new API.

* platform/graphics/avfoundation/CDMPrivateMediaSourceAVFObjC.h:
(WebCore::CDMPrivateMediaSourceAVFObjC::CDMPrivateMediaSourceAVFObjC): Moved to implementation file.
* platform/graphics/avfoundation/CDMPrivateMediaSourceAVFObjC.mm:
(WebCore::validKeySystemRE): Support "com.apple.fps.3_x".
(WebCore::CDMPrivateMediaSourceAVFObjC::~CDMPrivateMediaSourceAVFObjC): Invalidate all outstanding sessions.
(WebCore::CDMPrivateMediaSourceAVFObjC::supportsKeySystem): Only support "com.apple.fps.3_x" if the AVContentKeySession class is available.
(WebCore::CDMPrivateMediaSourceAVFObjC::createSession): Create an instance of CDMSessionAVContentKeySession if "com.apple.fps.3_x" is specified and AVContentKeySession is available.
(WebCore::CDMPrivateMediaSourceAVFObjC::invalidateSession): Remove session from the list of outstanding sessions.
(WebCore::CDMPrivateMediaSourceAVFObjC::supportsMIMEType): Deleted.
* platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.h: Copied from Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h.
(WebCore::CDMSessionAVContentKeySession::hasContentKeySession): Simple accessor.
(WebCore::toCDMSessionAVContentKeySession): Safe casting.
* platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm: Added.
(-[CDMSessionAVContentKeySessionDelegate initWithParent:]): Simple constructor.
(-[CDMSessionAVContentKeySessionDelegate invalidate]): Remove reference to parent.
(-[CDMSessionAVContentKeySessionDelegate contentKeySession:willProvideKeyRequestInitializationDataForTrackID:]): Pass to parent.
(-[CDMSessionAVContentKeySessionDelegate contentKeySession:didProvideKeyRequestInitializationData:requestHandling:]): Ditto.
(-[CDMSessionAVContentKeySessionDelegate contentKeySessionContentProtectionSessionIdentifierDidChange:]): Ditto.
(WebCore::CDMSessionAVContentKeySession::CDMSessionAVContentKeySession): Create the delegate.
(WebCore::CDMSessionAVContentKeySession::~CDMSessionAVContentKeySession): Invalidate the delegate and remove all parsers.
(WebCore::CDMSessionAVContentKeySession::isAvailable): Return true if AVContentKeySession class is available.
(WebCore::CDMSessionAVContentKeySession::generateKeyRequest): Support "keyrelease" message, setting of the certificate, and creating key request object.
(WebCore::CDMSessionAVContentKeySession::releaseKeys): Retrieve keys from storage location.
(WebCore::isEqual): Compares a Uint8Array to a char*.
(WebCore::CDMSessionAVContentKeySession::update): Support "acknowledged" message, "renew" message, and key addition.
(WebCore::CDMSessionAVContentKeySession::addParser): Add the parser to the AVContentKeySession.
(WebCore::CDMSessionAVContentKeySession::removeParser): Remove parser from same.
(WebCore::CDMSessionAVContentKeySession::generateKeyReleaseMessage): Retrieve key release message from AVContentKeySession.
(WebCore::CDMSessionAVContentKeySession::didProvideContentKeyRequest): Simple setter.
(WebCore::CDMSessionAVContentKeySession::contentKeySession): Lazily create the AVContentKeySession.
* platform/graphics/avfoundation/objc/CDMSessionAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/CDMSessionAVFoundationObjC.mm:
(WebCore::CDMSessionAVFoundationObjC::CDMSessionAVFoundationObjC):
* platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.h: Copied from Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h.
(WebCore::toCDMSessionAVStreamSession):
* platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.mm: Copied from Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.mm.
(-[CDMSessionAVStreamSessionObserver initWithParent:]): Moved from CDMSessionMediaSourceAVFObjcObserver.
(-[CDMSessionAVStreamSessionObserver contentProtectionSessionIdentifierChanged:]): Ditto.
(WebCore::CDMSessionAVStreamSession::CDMSessionAVStreamSession): Ditto.
(WebCore::CDMSessionAVStreamSession::~CDMSessionAVStreamSession): Ditto.
(WebCore::CDMSessionAVStreamSession::generateKeyRequest): Ditto.
(WebCore::CDMSessionAVStreamSession::releaseKeys): Ditto.
(WebCore::isEqual): Ditto.
(WebCore::CDMSessionAVStreamSession::update): Ditto.
(WebCore::CDMSessionAVStreamSession::setStreamSession): Ditto.
(WebCore::CDMSessionAVStreamSession::addParser): Ditto.
(WebCore::CDMSessionAVStreamSession::removeParser): Ditto.
(WebCore::CDMSessionAVStreamSession::generateKeyReleaseMessage): Ditto.
* platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h:
(WebCore::CDMSessionMediaSourceAVFObjC::invalidateCDM): Clear the m_cdm.
(WebCore::toCDMSessionMediaSourceAVFObjC):
* platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.mm:
(WebCore::CDMSessionMediaSourceAVFObjC::CDMSessionMediaSourceAVFObjC):
(WebCore::CDMSessionMediaSourceAVFObjC::~CDMSessionMediaSourceAVFObjC): Instruct our CDM to invalidate their references to us.
(WebCore::CDMSessionMediaSourceAVFObjC::addSourceBuffer): Call addParser().
(WebCore::CDMSessionMediaSourceAVFObjC::removeSourceBuffer): Call removeParser().
(WebCore::CDMSessionMediaSourceAVFObjC::layerDidReceiveError): Deleted.
(WebCore::CDMSessionMediaSourceAVFObjC::rendererDidReceiveError): Deleted.

To give us a chance to create a CDMPrivate before we continue decoding media data, "block" further decoding
on the background thread by creating a semaphore and passing it to the main thread, to be triggered when
a CDM is created and attached to this source buffer.

* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
* platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
(-[WebAVStreamDataParserListener streamDataParser:didProvideContentKeyRequestInitializationData:forTrackID:]):
(WebCore::SourceBufferPrivateAVFObjC::~SourceBufferPrivateAVFObjC):
(WebCore::SourceBufferPrivateAVFObjC::willProvideContentKeyRequestInitializationDataForTrackID):
(WebCore::SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID):
(WebCore::SourceBufferPrivateAVFObjC::setCDMSession):
(-[WebAVStreamDataParserListener streamDataParserWillProvideContentKeyRequestInitializationData:forTrackID:]): Deleted.
* platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm:

Drive-by fix: Only throw an error from keyRequestTimerFired() if the underlying call to
generateKeyRequest() returned an error, rather than just failed to create a message.

* Modules/encryptedmedia/MediaKeySession.cpp:
(WebCore::MediaKeySession::keyRequestTimerFired):

Drive-by fix: Pass the CDMSessionClient into CDM::createSession() so that it is immediately available
in the CDMSessionPrivate constructor, rather than setting the client immediately after construction.

* Modules/encryptedmedia/CDM.cpp:
(WebCore::CDM::createSession):
* Modules/encryptedmedia/CDM.h:
* Modules/encryptedmedia/CDMPrivate.h:
* Modules/encryptedmedia/CDMPrivateClearKey.cpp:
(WebCore::CDMPrivateClearKey::createSession):
* Modules/encryptedmedia/CDMPrivateClearKey.h:
* Modules/encryptedmedia/CDMPrivateMediaPlayer.cpp:
(WebCore::CDMPrivateMediaPlayer::createSession):
* Modules/encryptedmedia/CDMPrivateMediaPlayer.h:
* Modules/encryptedmedia/CDMSessionClearKey.cpp:
(WebCore::CDMSessionClearKey::CDMSessionClearKey):
* Modules/encryptedmedia/CDMSessionClearKey.h:
* Modules/encryptedmedia/MediaKeySession.cpp:
(WebCore::MediaKeySession::MediaKeySession):
* platform/graphics/CDMSession.h:
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::createSession):
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::createSession):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::createSession):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmSession):
* platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::setCDMSession):
(WebCore::MediaPlayerPrivateMediaSourceAVFObjC::keyNeeded): Deleted.
* testing/MockCDM.cpp:
(WebCore::MockCDM::createSession):
(WebCore::MockCDMSession::MockCDMSession):
* testing/MockCDM.h:

Add new files to the project:

* WebCore.xcodeproj/project.pbxproj:

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

35 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Modules/encryptedmedia/CDM.cpp
Source/WebCore/Modules/encryptedmedia/CDM.h
Source/WebCore/Modules/encryptedmedia/CDMPrivate.h
Source/WebCore/Modules/encryptedmedia/CDMPrivateClearKey.cpp
Source/WebCore/Modules/encryptedmedia/CDMPrivateClearKey.h
Source/WebCore/Modules/encryptedmedia/CDMPrivateMediaPlayer.cpp
Source/WebCore/Modules/encryptedmedia/CDMPrivateMediaPlayer.h
Source/WebCore/Modules/encryptedmedia/CDMSessionClearKey.cpp
Source/WebCore/Modules/encryptedmedia/CDMSessionClearKey.h
Source/WebCore/Modules/encryptedmedia/MediaKeySession.cpp
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/platform/graphics/CDMSession.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h
Source/WebCore/platform/graphics/avfoundation/CDMPrivateMediaSourceAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/CDMPrivateMediaSourceAVFObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.h [new file with mode: 0644]
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm [new file with mode: 0644]
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVFoundationObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVFoundationObjC.mm
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.h [new file with mode: 0644]
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.mm [new file with mode: 0644]
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.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/SourceBufferPrivateAVFObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm
Source/WebCore/platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm
Source/WebCore/testing/MockCDM.cpp
Source/WebCore/testing/MockCDM.h

index 51411bd..87380a0 100644 (file)
@@ -1,3 +1,140 @@
+2015-12-01  Jer Noble  <jer.noble@apple.com>
+
+        Adopt AVContentKeySession
+        https://bugs.webkit.org/show_bug.cgi?id=151221
+
+        Reviewed by Eric Carlson.
+
+        Adopt a new API for managing key state, AVContentKeySession. Because this necessitates a change
+        in both the initialization data returned by the needkey event, and passed into the createSession()
+        method, bump the protocol version number (to 3), and keep supporting the old key management API
+        for legacy content.
+
+        To do so, move most of the implementation of CDMPrivateMediaSourceAVFObjC into a new subclass,
+        CDMSessionAVStreamSession, and add a new subclass, CDMSessionAVContentKeySession, to support the
+        new API.
+
+        * platform/graphics/avfoundation/CDMPrivateMediaSourceAVFObjC.h:
+        (WebCore::CDMPrivateMediaSourceAVFObjC::CDMPrivateMediaSourceAVFObjC): Moved to implementation file.
+        * platform/graphics/avfoundation/CDMPrivateMediaSourceAVFObjC.mm:
+        (WebCore::validKeySystemRE): Support "com.apple.fps.3_x".
+        (WebCore::CDMPrivateMediaSourceAVFObjC::~CDMPrivateMediaSourceAVFObjC): Invalidate all outstanding sessions.
+        (WebCore::CDMPrivateMediaSourceAVFObjC::supportsKeySystem): Only support "com.apple.fps.3_x" if the AVContentKeySession class is available.
+        (WebCore::CDMPrivateMediaSourceAVFObjC::createSession): Create an instance of CDMSessionAVContentKeySession if "com.apple.fps.3_x" is specified and AVContentKeySession is available.
+        (WebCore::CDMPrivateMediaSourceAVFObjC::invalidateSession): Remove session from the list of outstanding sessions.
+        (WebCore::CDMPrivateMediaSourceAVFObjC::supportsMIMEType): Deleted.
+        * platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.h: Copied from Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h.
+        (WebCore::CDMSessionAVContentKeySession::hasContentKeySession): Simple accessor.
+        (WebCore::toCDMSessionAVContentKeySession): Safe casting.
+        * platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm: Added.
+        (-[CDMSessionAVContentKeySessionDelegate initWithParent:]): Simple constructor.
+        (-[CDMSessionAVContentKeySessionDelegate invalidate]): Remove reference to parent.
+        (-[CDMSessionAVContentKeySessionDelegate contentKeySession:willProvideKeyRequestInitializationDataForTrackID:]): Pass to parent.
+        (-[CDMSessionAVContentKeySessionDelegate contentKeySession:didProvideKeyRequestInitializationData:requestHandling:]): Ditto.
+        (-[CDMSessionAVContentKeySessionDelegate contentKeySessionContentProtectionSessionIdentifierDidChange:]): Ditto.
+        (WebCore::CDMSessionAVContentKeySession::CDMSessionAVContentKeySession): Create the delegate.
+        (WebCore::CDMSessionAVContentKeySession::~CDMSessionAVContentKeySession): Invalidate the delegate and remove all parsers.
+        (WebCore::CDMSessionAVContentKeySession::isAvailable): Return true if AVContentKeySession class is available.
+        (WebCore::CDMSessionAVContentKeySession::generateKeyRequest): Support "keyrelease" message, setting of the certificate, and creating key request object. 
+        (WebCore::CDMSessionAVContentKeySession::releaseKeys): Retrieve keys from storage location.
+        (WebCore::isEqual): Compares a Uint8Array to a char*.
+        (WebCore::CDMSessionAVContentKeySession::update): Support "acknowledged" message, "renew" message, and key addition.
+        (WebCore::CDMSessionAVContentKeySession::addParser): Add the parser to the AVContentKeySession.
+        (WebCore::CDMSessionAVContentKeySession::removeParser): Remove parser from same.
+        (WebCore::CDMSessionAVContentKeySession::generateKeyReleaseMessage): Retrieve key release message from AVContentKeySession.
+        (WebCore::CDMSessionAVContentKeySession::didProvideContentKeyRequest): Simple setter.
+        (WebCore::CDMSessionAVContentKeySession::contentKeySession): Lazily create the AVContentKeySession.
+        * platform/graphics/avfoundation/objc/CDMSessionAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/CDMSessionAVFoundationObjC.mm:
+        (WebCore::CDMSessionAVFoundationObjC::CDMSessionAVFoundationObjC):
+        * platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.h: Copied from Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h.
+        (WebCore::toCDMSessionAVStreamSession):
+        * platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.mm: Copied from Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.mm.
+        (-[CDMSessionAVStreamSessionObserver initWithParent:]): Moved from CDMSessionMediaSourceAVFObjcObserver.
+        (-[CDMSessionAVStreamSessionObserver contentProtectionSessionIdentifierChanged:]): Ditto.
+        (WebCore::CDMSessionAVStreamSession::CDMSessionAVStreamSession): Ditto.
+        (WebCore::CDMSessionAVStreamSession::~CDMSessionAVStreamSession): Ditto.
+        (WebCore::CDMSessionAVStreamSession::generateKeyRequest): Ditto.
+        (WebCore::CDMSessionAVStreamSession::releaseKeys): Ditto.
+        (WebCore::isEqual): Ditto.
+        (WebCore::CDMSessionAVStreamSession::update): Ditto.
+        (WebCore::CDMSessionAVStreamSession::setStreamSession): Ditto.
+        (WebCore::CDMSessionAVStreamSession::addParser): Ditto.
+        (WebCore::CDMSessionAVStreamSession::removeParser): Ditto.
+        (WebCore::CDMSessionAVStreamSession::generateKeyReleaseMessage): Ditto.
+        * platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.h:
+        (WebCore::CDMSessionMediaSourceAVFObjC::invalidateCDM): Clear the m_cdm.
+        (WebCore::toCDMSessionMediaSourceAVFObjC):
+        * platform/graphics/avfoundation/objc/CDMSessionMediaSourceAVFObjC.mm:
+        (WebCore::CDMSessionMediaSourceAVFObjC::CDMSessionMediaSourceAVFObjC):
+        (WebCore::CDMSessionMediaSourceAVFObjC::~CDMSessionMediaSourceAVFObjC): Instruct our CDM to invalidate their references to us.
+        (WebCore::CDMSessionMediaSourceAVFObjC::addSourceBuffer): Call addParser().
+        (WebCore::CDMSessionMediaSourceAVFObjC::removeSourceBuffer): Call removeParser().
+        (WebCore::CDMSessionMediaSourceAVFObjC::layerDidReceiveError): Deleted.
+        (WebCore::CDMSessionMediaSourceAVFObjC::rendererDidReceiveError): Deleted.
+        
+        To give us a chance to create a CDMPrivate before we continue decoding media data, "block" further decoding
+        on the background thread by creating a semaphore and passing it to the main thread, to be triggered when
+        a CDM is created and attached to this source buffer.
+
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.h:
+        * platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:
+        (-[WebAVStreamDataParserListener streamDataParser:didProvideContentKeyRequestInitializationData:forTrackID:]):
+        (WebCore::SourceBufferPrivateAVFObjC::~SourceBufferPrivateAVFObjC):
+        (WebCore::SourceBufferPrivateAVFObjC::willProvideContentKeyRequestInitializationDataForTrackID):
+        (WebCore::SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID):
+        (WebCore::SourceBufferPrivateAVFObjC::setCDMSession):
+        (-[WebAVStreamDataParserListener streamDataParserWillProvideContentKeyRequestInitializationData:forTrackID:]): Deleted.
+        * platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm:
+
+        Drive-by fix: Only throw an error from keyRequestTimerFired() if the underlying call to
+        generateKeyRequest() returned an error, rather than just failed to create a message.
+
+        * Modules/encryptedmedia/MediaKeySession.cpp:
+        (WebCore::MediaKeySession::keyRequestTimerFired):
+
+        Drive-by fix: Pass the CDMSessionClient into CDM::createSession() so that it is immediately available
+        in the CDMSessionPrivate constructor, rather than setting the client immediately after construction.
+
+        * Modules/encryptedmedia/CDM.cpp:
+        (WebCore::CDM::createSession):
+        * Modules/encryptedmedia/CDM.h:
+        * Modules/encryptedmedia/CDMPrivate.h:
+        * Modules/encryptedmedia/CDMPrivateClearKey.cpp:
+        (WebCore::CDMPrivateClearKey::createSession):
+        * Modules/encryptedmedia/CDMPrivateClearKey.h:
+        * Modules/encryptedmedia/CDMPrivateMediaPlayer.cpp:
+        (WebCore::CDMPrivateMediaPlayer::createSession):
+        * Modules/encryptedmedia/CDMPrivateMediaPlayer.h:
+        * Modules/encryptedmedia/CDMSessionClearKey.cpp:
+        (WebCore::CDMSessionClearKey::CDMSessionClearKey):
+        * Modules/encryptedmedia/CDMSessionClearKey.h:
+        * Modules/encryptedmedia/MediaKeySession.cpp:
+        (WebCore::MediaKeySession::MediaKeySession):
+        * platform/graphics/CDMSession.h:
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::createSession):
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::createSession):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::createSession):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.h:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::cdmSession):
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateMediaSourceAVFObjC.mm:
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::setCDMSession):
+        (WebCore::MediaPlayerPrivateMediaSourceAVFObjC::keyNeeded): Deleted.
+        * testing/MockCDM.cpp:
+        (WebCore::MockCDM::createSession):
+        (WebCore::MockCDMSession::MockCDMSession):
+        * testing/MockCDM.h:
+
+        Add new files to the project:
+
+        * WebCore.xcodeproj/project.pbxproj:
+
+
 2015-12-04  Brady Eidson  <beidson@apple.com>
 
         Modern IDB: storage/indexeddb/cursor-update.html fails.
index d63a4f4..02b897b 100644 (file)
@@ -132,9 +132,9 @@ bool CDM::supportsMIMEType(const String& mimeType) const
     return m_private->supportsMIMEType(mimeType);
 }
 
-std::unique_ptr<CDMSession> CDM::createSession()
+std::unique_ptr<CDMSession> CDM::createSession(CDMSessionClient* client)
 {
-    std::unique_ptr<CDMSession> session = m_private->createSession();
+    std::unique_ptr<CDMSession> session = m_private->createSession(client);
     if (mediaPlayer())
         mediaPlayer()->setCDMSession(session.get());
     return session;
index 5aecfc9..9bfd1bc 100644 (file)
@@ -63,7 +63,7 @@ public:
     ~CDM();
 
     bool supportsMIMEType(const String&) const;
-    std::unique_ptr<CDMSession> createSession();
+    std::unique_ptr<CDMSession> createSession(CDMSessionClient*);
 
     const String& keySystem() const { return m_keySystem; }
 
index 46bd78d..43df3cb 100644 (file)
@@ -33,6 +33,7 @@
 namespace WebCore {
 
 class CDMSession;
+class CDMSessionClient;
 
 class CDMPrivateInterface {
 public:
@@ -41,7 +42,7 @@ public:
 
     virtual bool supportsMIMEType(const String&) = 0;
 
-    virtual std::unique_ptr<CDMSession> createSession() = 0;
+    virtual std::unique_ptr<CDMSession> createSession(CDMSessionClient*) = 0;
 };
 
 }
index d617ad3..8a36f3b 100644 (file)
@@ -59,9 +59,9 @@ bool CDMPrivateClearKey::supportsMIMEType(const String& mimeType)
     return MediaPlayer::supportsKeySystem(m_cdm->keySystem(), mimeType);
 }
 
-std::unique_ptr<CDMSession> CDMPrivateClearKey::createSession()
+std::unique_ptr<CDMSession> CDMPrivateClearKey::createSession(CDMSessionClient* client)
 {
-    return std::make_unique<CDMSessionClearKey>();
+    return std::make_unique<CDMSessionClearKey>(client);
 }
 
 }
index d53b49c..efd1fa2 100644 (file)
@@ -47,7 +47,7 @@ public:
     static bool supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType);
 
     virtual bool supportsMIMEType(const String& mimeType) override;
-    virtual std::unique_ptr<CDMSession> createSession() override;
+    virtual std::unique_ptr<CDMSession> createSession(CDMSessionClient*) override;
 
 protected:
     CDM* m_cdm;
index 1271a7a..f29eb48 100644 (file)
@@ -55,13 +55,13 @@ bool CDMPrivateMediaPlayer::supportsMIMEType(const String& mimeType)
     return MediaPlayer::supportsKeySystem(m_cdm->keySystem(), mimeType);
 }
 
-std::unique_ptr<CDMSession> CDMPrivateMediaPlayer::createSession()
+std::unique_ptr<CDMSession> CDMPrivateMediaPlayer::createSession(CDMSessionClient* client)
 {
     MediaPlayer* mediaPlayer = m_cdm->mediaPlayer();
     if (!mediaPlayer)
         return nullptr;
 
-    return mediaPlayer->createSession(m_cdm->keySystem());
+    return mediaPlayer->createSession(m_cdm->keySystem(), client);
 }
 
 }
index eb58510..a3c6531 100644 (file)
@@ -47,7 +47,7 @@ public:
     virtual ~CDMPrivateMediaPlayer() { }
 
     virtual bool supportsMIMEType(const String& mimeType) override;
-    virtual std::unique_ptr<CDMSession> createSession() override;
+    virtual std::unique_ptr<CDMSession> createSession(CDMSessionClient*) override;
 
     CDM* cdm() const { return m_cdm; }
 
index 8c12bd2..1c29644 100644 (file)
@@ -59,8 +59,9 @@ static VM& clearKeyVM()
     return *vm.get();
 }
 
-CDMSessionClearKey::CDMSessionClearKey()
-    : m_sessionId(createCanonicalUUIDString())
+CDMSessionClearKey::CDMSessionClearKey(CDMSessionClient* client)
+    : m_client(client)
+    , m_sessionId(createCanonicalUUIDString())
 {
 }
 
index a931d36..e509fb6 100644 (file)
@@ -35,7 +35,7 @@ namespace WebCore {
 
 class CDMSessionClearKey : public CDMSession {
 public:
-    CDMSessionClearKey();
+    CDMSessionClearKey(CDMSessionClient*);
     virtual ~CDMSessionClearKey();
 
     // CDMSessionPrivate
index b218104..bf162de 100644 (file)
@@ -52,11 +52,10 @@ MediaKeySession::MediaKeySession(ScriptExecutionContext* context, MediaKeys* key
     , m_keys(keys)
     , m_keySystem(keySystem)
     , m_asyncEventQueue(*this)
-    , m_session(keys->cdm()->createSession())
+    , m_session(keys->cdm()->createSession(this))
     , m_keyRequestTimer(*this, &MediaKeySession::keyRequestTimerFired)
     , m_addKeyTimer(*this, &MediaKeySession::addKeyTimerFired)
 {
-    m_session->setClient(this);
 }
 
 MediaKeySession::~MediaKeySession()
@@ -119,7 +118,7 @@ void MediaKeySession::keyRequestTimerFired()
         RefPtr<Uint8Array> keyRequest = m_session->generateKeyRequest(request.mimeType, request.initData.get(), destinationURL, errorCode, systemCode);
 
         // Otherwise [if a request is not successfully generated]:
-        if (!keyRequest) {
+        if (errorCode) {
             // 3.1. Create a new MediaKeyError object with the following attributes:
             //      code = the appropriate MediaKeyError code
             //      systemCode = a Key System-specific value, if provided, and 0 otherwise
@@ -134,7 +133,8 @@ void MediaKeySession::keyRequestTimerFired()
         //    The event is of type MediaKeyMessageEvent and has:
         //    message = key request
         //    destinationURL = destinationURL
-        sendMessage(keyRequest.get(), destinationURL);
+        if (keyRequest)
+            sendMessage(keyRequest.get(), destinationURL);
     }
 }
 
index 98165c7..b1c1ddd 100644 (file)
                CDDC1E7A18A952F30027A9D4 /* MediaSourcePrivateClient.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDC1E7918A952F30027A9D4 /* MediaSourcePrivateClient.h */; };
                CDDD571518B57A8200A94FCB /* CDMSession.h in Headers */ = {isa = PBXBuildFile; fileRef = CDDE02E918B3DFC700CF7FF1 /* CDMSession.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CDDE02ED18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDDE02EB18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm */; };
-               CDDE02F018B5651300CF7FF1 /* CDMSessionMediaSourceAVFObjC.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDDE02EF18B5651200CF7FF1 /* CDMSessionMediaSourceAVFObjC.mm */; };
+               CDDE02F018B5651300CF7FF1 /* CDMSessionAVStreamSession.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDDE02EF18B5651200CF7FF1 /* CDMSessionAVStreamSession.mm */; };
                CDE3A85417F5FCE600C5BE20 /* AudioTrackPrivateAVF.h in Headers */ = {isa = PBXBuildFile; fileRef = CDE3A85217F5FCE600C5BE20 /* AudioTrackPrivateAVF.h */; };
                CDE3A85717F6020400C5BE20 /* AudioTrackPrivateAVFObjC.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDE3A85517F6020400C5BE20 /* AudioTrackPrivateAVFObjC.mm */; };
                CDE3A85817F6020400C5BE20 /* AudioTrackPrivateAVFObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = CDE3A85617F6020400C5BE20 /* AudioTrackPrivateAVFObjC.h */; };
+               CDE595951BF16DF300A1CBE8 /* CDMSessionAVContentKeySession.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDE595931BF166AD00A1CBE8 /* CDMSessionAVContentKeySession.mm */; };
+               CDE595971BF26E2100A1CBE8 /* CDMSessionMediaSourceAVFObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = CDE595961BF26E2100A1CBE8 /* CDMSessionMediaSourceAVFObjC.h */; };
+               CDE5959D1BF2757100A1CBE8 /* CDMSessionMediaSourceAVFObjC.mm in Sources */ = {isa = PBXBuildFile; fileRef = CDE5959C1BF2757100A1CBE8 /* CDMSessionMediaSourceAVFObjC.mm */; };
                CDE7FC44181904B1002BBB77 /* OrderIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDE7FC42181904B1002BBB77 /* OrderIterator.cpp */; };
                CDE7FC45181904B1002BBB77 /* OrderIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = CDE7FC43181904B1002BBB77 /* OrderIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                CDE83DB1183C44060031EAA3 /* VideoPlaybackQuality.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDE83DAF183C44060031EAA3 /* VideoPlaybackQuality.cpp */; };
                CDDE02E918B3DFC700CF7FF1 /* CDMSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDMSession.h; sourceTree = "<group>"; };
                CDDE02EA18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMSessionAVFoundationObjC.h; sourceTree = "<group>"; };
                CDDE02EB18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDMSessionAVFoundationObjC.mm; sourceTree = "<group>"; };
-               CDDE02EE18B564FA00CF7FF1 /* CDMSessionMediaSourceAVFObjC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDMSessionMediaSourceAVFObjC.h; sourceTree = "<group>"; };
-               CDDE02EF18B5651200CF7FF1 /* CDMSessionMediaSourceAVFObjC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDMSessionMediaSourceAVFObjC.mm; sourceTree = "<group>"; };
+               CDDE02EE18B564FA00CF7FF1 /* CDMSessionAVStreamSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CDMSessionAVStreamSession.h; sourceTree = "<group>"; };
+               CDDE02EF18B5651200CF7FF1 /* CDMSessionAVStreamSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDMSessionAVStreamSession.mm; sourceTree = "<group>"; };
                CDE3A85217F5FCE600C5BE20 /* AudioTrackPrivateAVF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTrackPrivateAVF.h; sourceTree = "<group>"; };
                CDE3A85517F6020400C5BE20 /* AudioTrackPrivateAVFObjC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AudioTrackPrivateAVFObjC.mm; sourceTree = "<group>"; };
                CDE3A85617F6020400C5BE20 /* AudioTrackPrivateAVFObjC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioTrackPrivateAVFObjC.h; sourceTree = "<group>"; };
+               CDE595931BF166AD00A1CBE8 /* CDMSessionAVContentKeySession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDMSessionAVContentKeySession.mm; sourceTree = "<group>"; };
+               CDE595941BF166D100A1CBE8 /* CDMSessionAVContentKeySession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMSessionAVContentKeySession.h; sourceTree = "<group>"; };
+               CDE595961BF26E2100A1CBE8 /* CDMSessionMediaSourceAVFObjC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDMSessionMediaSourceAVFObjC.h; sourceTree = "<group>"; };
+               CDE5959C1BF2757100A1CBE8 /* CDMSessionMediaSourceAVFObjC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CDMSessionMediaSourceAVFObjC.mm; sourceTree = "<group>"; };
                CDE6560E17CA6E7600526BA7 /* mediaControlsApple.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = mediaControlsApple.js; sourceTree = "<group>"; };
                CDE7FC42181904B1002BBB77 /* OrderIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OrderIterator.cpp; sourceTree = "<group>"; };
                CDE7FC43181904B1002BBB77 /* OrderIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OrderIterator.h; sourceTree = "<group>"; };
                                CDE3A85517F6020400C5BE20 /* AudioTrackPrivateAVFObjC.mm */,
                                CD54A760180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.cpp */,
                                CD54A761180F9F7000B076C9 /* AudioTrackPrivateMediaSourceAVFObjC.h */,
+                               CDE595941BF166D100A1CBE8 /* CDMSessionAVContentKeySession.h */,
+                               CDE595931BF166AD00A1CBE8 /* CDMSessionAVContentKeySession.mm */,
                                CDDE02EA18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.h */,
                                CDDE02EB18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm */,
-                               CDDE02EE18B564FA00CF7FF1 /* CDMSessionMediaSourceAVFObjC.h */,
-                               CDDE02EF18B5651200CF7FF1 /* CDMSessionMediaSourceAVFObjC.mm */,
+                               CDDE02EE18B564FA00CF7FF1 /* CDMSessionAVStreamSession.h */,
+                               CDDE02EF18B5651200CF7FF1 /* CDMSessionAVStreamSession.mm */,
+                               CDE595961BF26E2100A1CBE8 /* CDMSessionMediaSourceAVFObjC.h */,
+                               CDE5959C1BF2757100A1CBE8 /* CDMSessionMediaSourceAVFObjC.mm */,
                                07AA6B69166D019500D45671 /* InbandTextTrackPrivateAVFObjC.h */,
                                07AA6B6A166D019500D45671 /* InbandTextTrackPrivateAVFObjC.mm */,
                                07367DDD172CA67F00D861B9 /* InbandTextTrackPrivateLegacyAVFObjC.h */,
                                FD3160AF12B026F700C1A359 /* VectorMath.h in Headers */,
                                BCA257151293C010007A263D /* VerticalPositionCache.h in Headers */,
                                CDE83DB2183C44060031EAA3 /* VideoPlaybackQuality.h in Headers */,
+                               CDE595971BF26E2100A1CBE8 /* CDMSessionMediaSourceAVFObjC.h in Headers */,
                                BE88E0DF1715D2A200658D98 /* VideoTrack.h in Headers */,
                                BE88E0E21715D2A200658D98 /* VideoTrackList.h in Headers */,
                                CD8B5A46180DFF4E008B8E65 /* VideoTrackMediaSource.h in Headers */,
                                CD318622199F1E2A0030A0F7 /* CDMPrivateMediaSourceAVFObjC.mm in Sources */,
                                CDDE02ED18B3ED6D00CF7FF1 /* CDMSessionAVFoundationObjC.mm in Sources */,
                                CDE8B5F01A69778B00B4B66A /* CDMSessionClearKey.cpp in Sources */,
-                               CDDE02F018B5651300CF7FF1 /* CDMSessionMediaSourceAVFObjC.mm in Sources */,
+                               CDDE02F018B5651300CF7FF1 /* CDMSessionAVStreamSession.mm in Sources */,
                                5F2DBBE9178E3C8100141486 /* CertificateInfoMac.mm in Sources */,
                                E1A8E56617552B2A007488E7 /* CFURLExtras.cpp in Sources */,
                                97BC69DC1505F076001B74AC /* ChangeVersionWrapper.cpp in Sources */,
                                85DF2EEE0AA387CB00AD64C5 /* DOMHTMLElement.mm in Sources */,
                                85C050BB0AD84F5E005532E7 /* DOMHTMLEmbedElement.mm in Sources */,
                                85BA4CE00AA6861B0088052D /* DOMHTMLFieldSetElement.mm in Sources */,
+                               CDE5959D1BF2757100A1CBE8 /* CDMSessionMediaSourceAVFObjC.mm in Sources */,
                                85ECBEF00AA7626900544F0B /* DOMHTMLFontElement.mm in Sources */,
                                85DF2C5D0AA341F600AD64C5 /* DOMHTMLFormElement.mm in Sources */,
                                85DF81960AA77E4B00486AD7 /* DOMHTMLFrameElement.mm in Sources */,
                                CD9DE18117AAD6A400EA386D /* DOMURLMediaSource.cpp in Sources */,
                                15FCC9FC1B4DF7F200E72326 /* DOMURLMediaStream.cpp in Sources */,
                                BC1A37BF097C715F0019F3D8 /* DOMUtility.mm in Sources */,
+                               CDE595951BF16DF300A1CBE8 /* CDMSessionAVContentKeySession.mm in Sources */,
                                15C770A5100D41CD005BA267 /* DOMValidityState.mm in Sources */,
                                31C0FF4A0E4CEFDD007D6FE5 /* DOMWebKitAnimationEvent.mm in Sources */,
                                3106037A143281CD00ABF4BA /* DOMWebKitCSSFilterValue.mm in Sources */,
index d30bfe3..4551405 100644 (file)
@@ -58,7 +58,8 @@ enum CDMSessionType {
     CDMSessionTypeUnknown,
     CDMSessionTypeClearKey,
     CDMSessionTypeAVFoundationObjC,
-    CDMSessionTypeMediaSourceAVFObjC,
+    CDMSessionTypeAVStreamSession,
+    CDMSessionTypeAVContentKeySession,
 };
 
 class CDMSession {
index a79e891..50cea33 100644 (file)
@@ -532,9 +532,9 @@ MediaPlayer::MediaKeyException MediaPlayer::cancelKeyRequest(const String& keySy
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA_V2)
-std::unique_ptr<CDMSession> MediaPlayer::createSession(const String& keySystem)
+std::unique_ptr<CDMSession> MediaPlayer::createSession(const String& keySystem, CDMSessionClient* client)
 {
-    return m_private->createSession(keySystem);
+    return m_private->createSession(keySystem, client);
 }
 
 void MediaPlayer::setCDMSession(CDMSession* session)
index 093641f..345775b 100644 (file)
@@ -116,6 +116,7 @@ struct MediaEngineSupportParameters {
 
 extern const PlatformMedia NoPlatformMedia;
 
+class CDMSessionClient;
 class CachedResourceLoader;
 class ContentType;
 class GraphicsContext;
@@ -359,7 +360,7 @@ public:
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA_V2)
-    std::unique_ptr<CDMSession> createSession(const String& keySystem);
+    std::unique_ptr<CDMSession> createSession(const String& keySystem, CDMSessionClient*);
     void setCDMSession(CDMSession*);
     void keyAdded();
 #endif
index b9fd0e4..1f70ff0 100644 (file)
@@ -230,7 +230,7 @@ public:
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA_V2)
-    virtual std::unique_ptr<CDMSession> createSession(const String&) { return nullptr; }
+    virtual std::unique_ptr<CDMSession> createSession(const String&, CDMSessionClient*) { return nullptr; }
     virtual void setCDMSession(CDMSession*) { }
     virtual void keyAdded() { }
 #endif
index 7a82b7a..a1c71b1 100644 (file)
 #if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
 
 #include "CDMPrivate.h"
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
 class CDM;
+class CDMSessionMediaSourceAVFObjC;
 
 class CDMPrivateMediaSourceAVFObjC : public CDMPrivateInterface {
 public:
     explicit CDMPrivateMediaSourceAVFObjC(CDM* cdm)
         : m_cdm(cdm)
     { }
-    virtual ~CDMPrivateMediaSourceAVFObjC() { }
+    virtual ~CDMPrivateMediaSourceAVFObjC();
 
     static bool supportsKeySystem(const String&);
     static bool supportsKeySystemAndMimeType(const String& keySystem, const String& mimeType);
 
     virtual bool supportsMIMEType(const String& mimeType) override;
-    virtual std::unique_ptr<CDMSession> createSession() override;
+    virtual std::unique_ptr<CDMSession> createSession(CDMSessionClient*) override;
 
     CDM* cdm() const { return m_cdm; }
 
+    void invalidateSession(CDMSessionMediaSourceAVFObjC*);
+
 protected:
     CDM* m_cdm;
+    Vector<CDMSessionMediaSourceAVFObjC*> m_sessions;
 };
 
 }
index b1cfeb1..8b1bf32 100644 (file)
@@ -29,7 +29,8 @@
 #if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
 
 #import "CDM.h"
-#import "CDMSessionMediaSourceAVFObjC.h"
+#import "CDMSessionAVContentKeySession.h"
+#import "CDMSessionAVStreamSession.h"
 #import "ContentType.h"
 #import "ExceptionCode.h"
 #import "MediaPlayerPrivateMediaSourceAVFObjC.h"
@@ -43,10 +44,16 @@ namespace WebCore {
 
 static RegularExpression& validKeySystemRE()
 {
-    static NeverDestroyed<RegularExpression> keySystemRE("^com\\.apple\\.fps\\.2_\\d+(?:,\\d+)*$", TextCaseInsensitive);
+    static NeverDestroyed<RegularExpression> keySystemRE("^com\\.apple\\.fps\\.[23]_\\d+(?:,\\d+)*$", TextCaseInsensitive);
     return keySystemRE;
 }
 
+CDMPrivateMediaSourceAVFObjC::~CDMPrivateMediaSourceAVFObjC()
+{
+    for (auto& session : m_sessions)
+        session->invalidateCDM();
+}
+
 bool CDMPrivateMediaSourceAVFObjC::supportsKeySystem(const String& keySystem)
 {
     if (!wkQueryDecoderAvailability())
@@ -55,6 +62,9 @@ bool CDMPrivateMediaSourceAVFObjC::supportsKeySystem(const String& keySystem)
     if (!keySystem.isEmpty() && validKeySystemRE().match(keySystem) < 0)
         return false;
 
+    if (keySystem.substring(14, 1).toInt() == 3 && !CDMSessionAVContentKeySession::isAvailable())
+        return false;
+
     return true;
 }
 
@@ -89,7 +99,7 @@ bool CDMPrivateMediaSourceAVFObjC::supportsMIMEType(const String& mimeType)
     return MediaPlayerPrivateMediaSourceAVFObjC::supportsType(parameters) != MediaPlayer::IsNotSupported;
 }
 
-std::unique_ptr<CDMSession> CDMPrivateMediaSourceAVFObjC::createSession()
+std::unique_ptr<CDMSession> CDMPrivateMediaSourceAVFObjC::createSession(CDMSessionClient* client)
 {
     String keySystem = m_cdm->keySystem();
     ASSERT(validKeySystemRE().match(keySystem) >= 0);
@@ -101,7 +111,20 @@ std::unique_ptr<CDMSession> CDMPrivateMediaSourceAVFObjC::createSession()
     for (auto& protocolVersionString : protocolVersionsStrings)
         protocolVersions.append(protocolVersionString.toInt());
 
-    return std::make_unique<CDMSessionMediaSourceAVFObjC>(protocolVersions);
+    std::unique_ptr<CDMSessionMediaSourceAVFObjC> session;
+    if (keySystem.substring(14, 1).toInt() == 3 && CDMSessionAVContentKeySession::isAvailable())
+        session = std::make_unique<CDMSessionAVContentKeySession>(protocolVersions, *this, client);
+    else
+        session = std::make_unique<CDMSessionAVStreamSession>(protocolVersions, *this, client);
+
+    m_sessions.append(session.get());
+    return WTF::move(session);
+}
+
+void CDMPrivateMediaSourceAVFObjC::invalidateSession(CDMSessionMediaSourceAVFObjC* session)
+{
+    ASSERT(m_sessions.contains(session));
+    m_sessions.removeAll(session);
 }
 
 }
diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.h b/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.h
new file mode 100644 (file)
index 0000000..7b2791e
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CDMSessionAVContentKeySession_h
+#define CDMSessionAVContentKeySession_h
+
+#include "CDMSessionMediaSourceAVFObjC.h"
+#include "SourceBufferPrivateAVFObjC.h"
+#include <wtf/RetainPtr.h>
+
+#if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
+
+OBJC_CLASS AVContentKeyRequest;
+OBJC_CLASS AVContentKeySession;
+OBJC_CLASS CDMSessionAVContentKeySessionDelegate;
+
+namespace WebCore {
+
+class CDMPrivateMediaSourceAVFObjC;
+
+class CDMSessionAVContentKeySession : public CDMSessionMediaSourceAVFObjC {
+public:
+    CDMSessionAVContentKeySession(const Vector<int>& protocolVersions, CDMPrivateMediaSourceAVFObjC&, CDMSessionClient*);
+    virtual ~CDMSessionAVContentKeySession();
+
+    static bool isAvailable();
+
+    // CDMSession
+    virtual CDMSessionType type() override { return CDMSessionTypeAVContentKeySession; }
+    virtual RefPtr<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode) override;
+    virtual void releaseKeys() override;
+    virtual bool update(Uint8Array* key, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode) override;
+
+    // CDMSessionMediaSourceAVFObjC
+    void addParser(AVStreamDataParser *) override;
+    void removeParser(AVStreamDataParser *) override;
+
+    void didProvideContentKeyRequest(AVContentKeyRequest *);
+
+protected:
+    PassRefPtr<Uint8Array> generateKeyReleaseMessage(unsigned short& errorCode, unsigned long& systemCode);
+
+    bool hasContentKeySession() const { return m_contentKeySession; }
+    AVContentKeySession* contentKeySession();
+
+    RetainPtr<AVContentKeySession> m_contentKeySession;
+    RetainPtr<CDMSessionAVContentKeySessionDelegate> m_contentKeySessionDelegate;
+    RetainPtr<AVContentKeyRequest> m_keyRequest;
+    RefPtr<Uint8Array> m_initData;
+    RetainPtr<NSData> m_expiredSession;
+    Vector<int> m_protocolVersions;
+    int32_t m_protectedTrackID { 1 };
+    enum { Normal, KeyRelease } m_mode;
+};
+
+inline CDMSessionAVContentKeySession* toCDMSessionAVContentKeySession(CDMSession* session)
+{
+    if (!session || session->type() != CDMSessionTypeAVContentKeySession)
+        return nullptr;
+    return static_cast<CDMSessionAVContentKeySession*>(session);
+}
+
+}
+
+#endif
+
+#endif // CDMSessionAVContentKeySession_h
diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm b/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVContentKeySession.mm
new file mode 100644 (file)
index 0000000..d36be54
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "CDMSessionAVContentKeySession.h"
+
+#if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
+
+#import "CDM.h"
+#import "CDMPrivateMediaSourceAVFObjC.h"
+#import "ExceptionCode.h"
+#import "FileSystem.h"
+#import "Logging.h"
+#import "MediaPlayer.h"
+#import "SoftLinking.h"
+#import "SourceBufferPrivateAVFObjC.h"
+#import "UUID.h"
+#import <AVFoundation/AVError.h>
+#import <CoreMedia/CMBase.h>
+#import <cstdlib>
+#import <objc/objc-runtime.h>
+#import <runtime/TypedArrayInlines.h>
+#import <wtf/NeverDestroyed.h>
+
+SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
+SOFT_LINK_CLASS(AVFoundation, AVStreamDataParser);
+SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVContentKeySession);
+SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVContentKeyRequestProtocolVersionsKey, NSString *)
+
+@interface AVContentKeySession : NSObject
+- (instancetype)initWithStorageDirectoryAtURL:(NSURL *)storageURL;
+@property (assign) id delegate;
+- (void)addStreamDataParser:(AVStreamDataParser *)streamDataParser;
+- (void)removeStreamDataParser:(AVStreamDataParser *)streamDataParser;
+@property (readonly) NSArray *streamDataParsers;
+- (void)expire;
+@property (readonly) NSData *contentProtectionSessionIdentifier;
+- (void)processContentKeyRequestInitializationData:(NSData *)initializationData options:(NSDictionary *)options;
++ (NSArray *)pendingExpiredSessionReportsWithAppIdentifier:(NSData *)appIdentifier storageDirectoryAtURL:(NSURL *)storageURL;
++ (void)removePendingExpiredSessionReports:(NSArray *)expiredSessionReports withAppIdentifier:(NSData *)appIdentifier storageDirectoryAtURL:(NSURL *)storageURL;
+@end
+
+typedef NS_ENUM(NSInteger, AVContentKeyRequestStatus) {
+    AVContentKeySessionStatusNoKey,
+    AVContentKeySessionStatusRequestingKey,
+    AVContentKeySessionStatusKeyPresent,
+    AVContentKeySessionStatusExpired,
+    AVContentKeySessionStatusFailed
+};
+
+@interface AVContentKeyRequest : NSObject
+@property (readonly) AVContentKeyRequestStatus status;
+@property (readonly) NSError *error;
+@property (readonly) NSData *initializationData;
+- (NSData *)contentKeyRequestDataForApp:(NSData *)appIdentifier contentIdentifier:(NSData *)contentIdentifier options:(NSDictionary *)options error:(NSError **)outError;
+- (void)processContentKeyResponseData:(NSData *)contentKeyResponseData;
+- (void)processContentKeyResponseError:(NSError *)error;
+- (void)renewExpiringContentKeyResponseData;
+@end
+
+@interface CDMSessionAVContentKeySessionDelegate : NSObject {
+    WebCore::CDMSessionAVContentKeySession *m_parent;
+}
+- (void)invalidate;
+@end
+
+@implementation CDMSessionAVContentKeySessionDelegate
+- (id)initWithParent:(WebCore::CDMSessionAVContentKeySession *)parent
+{
+    if ((self = [super init]))
+        m_parent = parent;
+    return self;
+}
+
+
+- (void)invalidate
+{
+    m_parent = nullptr;
+}
+
+- (void)contentKeySession:(AVContentKeySession *)session didProvideContentKeyRequest:(AVContentKeyRequest *)keyRequest
+{
+    UNUSED_PARAM(session);
+
+    if (m_parent)
+        m_parent->didProvideContentKeyRequest(keyRequest);
+}
+
+- (void)contentKeySessionContentProtectionSessionIdentifierDidChange:(AVContentKeySession *)session
+{
+    if (!m_parent)
+        return;
+
+    NSData* identifier = [session contentProtectionSessionIdentifier];
+    RetainPtr<NSString> sessionIdentifierString = identifier ? adoptNS([[NSString alloc] initWithData:identifier encoding:NSUTF8StringEncoding]) : nil;
+    m_parent->setSessionId(sessionIdentifierString.get());
+}
+@end
+
+static const NSString *PlaybackSessionIdKey = @"PlaybackSessionID";
+
+namespace WebCore {
+
+CDMSessionAVContentKeySession::CDMSessionAVContentKeySession(const Vector<int>& protocolVersions, CDMPrivateMediaSourceAVFObjC& cdm, CDMSessionClient* client)
+    : CDMSessionMediaSourceAVFObjC(cdm, client)
+    , m_contentKeySessionDelegate(adoptNS([[CDMSessionAVContentKeySessionDelegate alloc] initWithParent:this]))
+    , m_protocolVersions(protocolVersions)
+    , m_mode(Normal)
+{
+}
+
+CDMSessionAVContentKeySession::~CDMSessionAVContentKeySession()
+{
+    [m_contentKeySessionDelegate invalidate];
+
+    for (auto& sourceBuffer : m_sourceBuffers)
+        removeParser(sourceBuffer->parser());
+}
+
+bool CDMSessionAVContentKeySession::isAvailable()
+{
+    return getAVContentKeySessionClass();
+}
+
+RefPtr<Uint8Array> CDMSessionAVContentKeySession::generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode)
+{
+    UNUSED_PARAM(mimeType);
+    UNUSED_PARAM(destinationURL);
+    ASSERT(initData);
+
+    LOG(Media, "CDMSessionAVContentKeySession::generateKeyRequest(%p)", this);
+
+    errorCode = MediaPlayer::NoError;
+    systemCode = 0;
+
+    m_initData = initData;
+
+    if (equalIgnoringCase(mimeType, "keyrelease")) {
+        m_mode = KeyRelease;
+        return generateKeyReleaseMessage(errorCode, systemCode);
+    }
+
+    if (!m_certificate) {
+        String certificateString(ASCIILiteral("certificate"));
+        RefPtr<Uint8Array> array = Uint8Array::create(certificateString.length());
+        for (unsigned i = 0, length = certificateString.length(); i < length; ++i)
+            array->set(i, certificateString[i]);
+        return array;
+    }
+
+    if (!m_keyRequest) {
+        NSData* nsInitData = [NSData dataWithBytes:m_initData->data() length:m_initData->length()];
+        [contentKeySession() processContentKeyRequestInitializationData:nsInitData options:nil];
+    }
+
+    return nullptr;
+}
+
+void CDMSessionAVContentKeySession::releaseKeys()
+{
+    if (hasContentKeySession()) {
+        m_stopped = true;
+        for (auto& sourceBuffer : m_sourceBuffers)
+            sourceBuffer->flush();
+
+        LOG(Media, "CDMSessionAVContentKeySession::releaseKeys(%p) - expiring stream session", this);
+        [contentKeySession() expire];
+
+        if (!m_certificate)
+            return;
+
+        if (![getAVContentKeySessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)])
+            return;
+
+        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+        NSArray* expiredSessions = [getAVContentKeySessionClass() pendingExpiredSessionReportsWithAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
+        for (NSData* expiredSessionData in expiredSessions) {
+            NSDictionary *expiredSession = [NSPropertyListSerialization propertyListWithData:expiredSessionData options:kCFPropertyListImmutable format:nullptr error:nullptr];
+            NSString *playbackSessionIdValue = (NSString *)[expiredSession objectForKey:PlaybackSessionIdKey];
+            if (![playbackSessionIdValue isKindOfClass:[NSString class]])
+                continue;
+
+            if (m_sessionId == String(playbackSessionIdValue)) {
+                LOG(Media, "CDMSessionAVContentKeySession::releaseKeys(%p) - found session, sending expiration message");
+                m_expiredSession = expiredSessionData;
+                m_client->sendMessage(Uint8Array::create(static_cast<const uint8_t*>([m_expiredSession bytes]), [m_expiredSession length]).get(), emptyString());
+                break;
+            }
+        }
+    }
+}
+
+static bool isEqual(Uint8Array* data, const char* literal)
+{
+    ASSERT(data);
+    ASSERT(literal);
+    unsigned length = data->length();
+
+    for (unsigned i = 0; i < length; ++i) {
+        if (!literal[i])
+            return false;
+
+        if (data->item(i) != static_cast<uint8_t>(literal[i]))
+            return false;
+    }
+    return !literal[length];
+}
+
+bool CDMSessionAVContentKeySession::update(Uint8Array* key, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode)
+{
+    UNUSED_PARAM(nextMessage);
+
+    bool shouldGenerateKeyRequest = !m_certificate || isEqual(key, "renew");
+    if (!m_certificate) {
+        LOG(Media, "CDMSessionAVContentKeySession::update(%p) - certificate data", this);
+
+        m_certificate = key;
+    }
+
+    if (isEqual(key, "acknowledged")) {
+        LOG(Media, "CDMSessionAVContentKeySession::update(%p) - acknowleding secure stop message", this);
+
+        if (!m_expiredSession) {
+            errorCode = MediaPlayer::InvalidPlayerState;
+            return false;
+        }
+
+        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+
+        if ([getAVContentKeySessionClass() respondsToSelector:@selector(removePendingExpiredSessionReports:withAppIdentifier:storageDirectoryAtURL:)])
+            [getAVContentKeySessionClass() removePendingExpiredSessionReports:@[m_expiredSession.get()] withAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
+        m_expiredSession = nullptr;
+        return true;
+    }
+
+    if (m_mode == KeyRelease)
+        return false;
+
+    if (!m_keyRequest) {
+        NSData* nsInitData = [NSData dataWithBytes:m_initData->data() length:m_initData->length()];
+        [contentKeySession() processContentKeyRequestInitializationData:nsInitData options:nil];
+    }
+
+    if (shouldGenerateKeyRequest) {
+        ASSERT(m_keyRequest);
+        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+
+        RetainPtr<NSData> initData = adoptNS([[NSData alloc] initWithBytes:m_initData->data() length:m_initData->length()]);
+
+        RetainPtr<NSDictionary> options;
+        if (!m_protocolVersions.isEmpty() && canLoadAVContentKeyRequestProtocolVersionsKey()) {
+            RetainPtr<NSMutableArray> protocolVersionsOption = adoptNS([[NSMutableArray alloc] init]);
+            for (auto& version : m_protocolVersions) {
+                if (!version)
+                    continue;
+                [protocolVersionsOption addObject:@(version)];
+            }
+
+            options = @{ getAVContentKeyRequestProtocolVersionsKey(): protocolVersionsOption.get() };
+        }
+
+        errorCode = MediaPlayer::NoError;
+        systemCode = 0;
+        NSData* requestData = [m_keyRequest contentKeyRequestDataForApp:certificateData.get() contentIdentifier:initData.get() options:options.get() error:nil];
+        nextMessage = Uint8Array::create(static_cast<const uint8_t*>([requestData bytes]), [requestData length]);
+
+        return false;
+    }
+
+    LOG(Media, "CDMSessionAVContentKeySession::update(%p) - key data", this);
+    errorCode = MediaPlayer::NoError;
+    systemCode = 0;
+    RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:key->data() length:key->length()]);
+    [m_keyRequest processContentKeyResponseData:keyData.get()];
+
+    return true;
+}
+
+void CDMSessionAVContentKeySession::addParser(AVStreamDataParser* parser)
+{
+    [contentKeySession() addStreamDataParser:parser];
+}
+
+void CDMSessionAVContentKeySession::removeParser(AVStreamDataParser* parser)
+{
+    [contentKeySession() removeStreamDataParser:parser];
+}
+
+PassRefPtr<Uint8Array> CDMSessionAVContentKeySession::generateKeyReleaseMessage(unsigned short& errorCode, unsigned long& systemCode)
+{
+    ASSERT(m_mode == KeyRelease);
+    m_certificate = m_initData;
+    RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+
+    if (![getAVContentKeySessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)]) {
+        errorCode = MediaPlayer::KeySystemNotSupported;
+        systemCode = '!mor';
+        return nullptr;
+    }
+
+    NSArray* expiredSessions = [getAVContentKeySessionClass() pendingExpiredSessionReportsWithAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
+    if (![expiredSessions count]) {
+        LOG(Media, "CDMSessionAVContentKeySession::generateKeyReleaseMessage(%p) - no expired sessions found", this);
+
+        errorCode = MediaPlayer::KeySystemNotSupported;
+        systemCode = '!mor';
+        return nullptr;
+    }
+
+    LOG(Media, "CDMSessionAVContentKeySession::generateKeyReleaseMessage(%p) - found %d expired sessions", this, [expiredSessions count]);
+
+    errorCode = 0;
+    systemCode = 0;
+    m_expiredSession = [expiredSessions firstObject];
+    return Uint8Array::create(static_cast<const uint8_t*>([m_expiredSession bytes]), [m_expiredSession length]);
+}
+
+void CDMSessionAVContentKeySession::didProvideContentKeyRequest(AVContentKeyRequest *keyRequest)
+{
+    m_keyRequest = keyRequest;
+}
+
+AVContentKeySession* CDMSessionAVContentKeySession::contentKeySession()
+{
+    if (!m_contentKeySession) {
+        m_contentKeySession = adoptNS([[getAVContentKeySessionClass() alloc] initWithStorageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]]);
+        m_contentKeySession.get().delegate = m_contentKeySessionDelegate.get();
+    }
+
+    return m_contentKeySession.get();
+}
+
+}
+
+#endif
index 2185ff2..5d93073 100644 (file)
@@ -39,7 +39,7 @@ class MediaPlayerPrivateAVFoundationObjC;
 
 class CDMSessionAVFoundationObjC : public CDMSession {
 public:
-    CDMSessionAVFoundationObjC(MediaPlayerPrivateAVFoundationObjC* parent);
+    CDMSessionAVFoundationObjC(MediaPlayerPrivateAVFoundationObjC* parent, CDMSessionClient*);
     virtual ~CDMSessionAVFoundationObjC() { }
 
     virtual CDMSessionType type() override { return CDMSessionTypeAVFoundationObjC; }
index 24dd108..cbde198 100644 (file)
@@ -46,9 +46,9 @@ SOFT_LINK_CLASS(AVFoundation, AVAssetResourceLoadingRequest)
 
 namespace WebCore {
 
-CDMSessionAVFoundationObjC::CDMSessionAVFoundationObjC(MediaPlayerPrivateAVFoundationObjC* parent)
+CDMSessionAVFoundationObjC::CDMSessionAVFoundationObjC(MediaPlayerPrivateAVFoundationObjC* parent, CDMSessionClient* client)
     : m_parent(parent)
-    , m_client(nullptr)
+    , m_client(client)
     , m_sessionId(createCanonicalUUIDString())
 {
 }
diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.h b/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.h
new file mode 100644 (file)
index 0000000..5576ac6
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CDMSessionAVStreamSession_h
+#define CDMSessionAVStreamSession_h
+
+#include "CDMSessionMediaSourceAVFObjC.h"
+#include "SourceBufferPrivateAVFObjC.h"
+#include <wtf/RetainPtr.h>
+#include <wtf/WeakPtr.h>
+
+#if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
+
+OBJC_CLASS AVStreamSession;
+OBJC_CLASS CDMSessionAVStreamSessionObserver;
+
+namespace WebCore {
+
+class CDMPrivateMediaSourceAVFObjC;
+
+class CDMSessionAVStreamSession : public CDMSessionMediaSourceAVFObjC {
+public:
+    CDMSessionAVStreamSession(const Vector<int>& protocolVersions, CDMPrivateMediaSourceAVFObjC&, CDMSessionClient*);
+    virtual ~CDMSessionAVStreamSession();
+
+    // CDMSession
+    virtual CDMSessionType type() override { return CDMSessionTypeAVStreamSession; }
+    virtual RefPtr<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode) override;
+    virtual void releaseKeys() override;
+    virtual bool update(Uint8Array*, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode) override;
+
+    // CDMSessionMediaSourceAVFObjC
+    void addParser(AVStreamDataParser*) override;
+    void removeParser(AVStreamDataParser*) override;
+
+    void setStreamSession(AVStreamSession*);
+
+protected:
+    PassRefPtr<Uint8Array> generateKeyReleaseMessage(unsigned short& errorCode, unsigned long& systemCode);
+
+    WeakPtrFactory<CDMSessionAVStreamSession> m_weakPtrFactory;
+    RetainPtr<AVStreamSession> m_streamSession;
+    RefPtr<Uint8Array> m_initData;
+    RefPtr<Uint8Array> m_certificate;
+    RetainPtr<NSData> m_expiredSession;
+    RetainPtr<CDMSessionAVStreamSessionObserver> m_dataParserObserver;
+    Vector<int> m_protocolVersions;
+    enum { Normal, KeyRelease } m_mode;
+};
+
+inline CDMSessionAVStreamSession* toCDMSessionAVStreamSession(CDMSession* session)
+{
+    if (!session || session->type() != CDMSessionTypeAVStreamSession)
+        return nullptr;
+    return static_cast<CDMSessionAVStreamSession*>(session);
+}
+
+}
+
+#endif
+
+#endif // CDMSessionAVStreamSession_h
diff --git a/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.mm b/Source/WebCore/platform/graphics/avfoundation/objc/CDMSessionAVStreamSession.mm
new file mode 100644 (file)
index 0000000..5b1afbf
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "CDMSessionAVStreamSession.h"
+
+#if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
+
+#import "CDM.h"
+#import "CDMPrivateMediaSourceAVFObjC.h"
+#import "ExceptionCode.h"
+#import "FileSystem.h"
+#import "Logging.h"
+#import "MediaPlayer.h"
+#import "SoftLinking.h"
+#import "SourceBufferPrivateAVFObjC.h"
+#import "UUID.h"
+#import <AVFoundation/AVError.h>
+#import <CoreMedia/CMBase.h>
+#import <cstdlib>
+#import <objc/objc-runtime.h>
+#import <runtime/TypedArrayInlines.h>
+#import <wtf/NeverDestroyed.h>
+
+SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
+SOFT_LINK_CLASS(AVFoundation, AVStreamDataParser);
+SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVStreamSession);
+SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVStreamDataParserContentKeyRequestProtocolVersionsKey, NSString *)
+SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVStreamSessionContentProtectionSessionIdentifierChangedNotification, NSString *)
+
+@interface AVStreamDataParser : NSObject
+- (void)processContentKeyResponseData:(NSData *)contentKeyResponseData forTrackID:(CMPersistentTrackID)trackID;
+- (void)processContentKeyResponseError:(NSError *)error forTrackID:(CMPersistentTrackID)trackID;
+- (void)renewExpiringContentKeyResponseDataForTrackID:(CMPersistentTrackID)trackID;
+- (NSData *)streamingContentKeyRequestDataForApp:(NSData *)appIdentifier contentIdentifier:(NSData *)contentIdentifier trackID:(CMPersistentTrackID)trackID options:(NSDictionary *)options error:(NSError **)outError;
+@end
+
+@interface AVStreamSession : NSObject
+- (void)addStreamDataParser:(AVStreamDataParser *)streamDataParser;
+- (void)removeStreamDataParser:(AVStreamDataParser *)streamDataParser;
+- (void)expire;
+- (NSData *)contentProtectionSessionIdentifier;
++ (NSArray *)pendingExpiredSessionReportsWithAppIdentifier:(NSData *)appIdentifier storageDirectoryAtURL:(NSURL *)storageURL;
++ (void)removePendingExpiredSessionReports:(NSArray *)expiredSessionReports withAppIdentifier:(NSData *)appIdentifier storageDirectoryAtURL:(NSURL *)storageURL;
+@end
+
+@interface CDMSessionAVStreamSessionObserver : NSObject {
+    WebCore::CDMSessionAVStreamSession *m_parent;
+}
+@end
+
+@implementation CDMSessionAVStreamSessionObserver
+- (id)initWithParent:(WebCore::CDMSessionAVStreamSession *)parent
+{
+    if ((self = [super init]))
+        m_parent = parent;
+    return self;
+}
+
+- (void)contentProtectionSessionIdentifierChanged:(NSNotification *)notification
+{
+    AVStreamSession* streamSession = (AVStreamSession*)[notification object];
+
+    NSData* identifier = [streamSession contentProtectionSessionIdentifier];
+    RetainPtr<NSString> sessionIdentifierString = identifier ? adoptNS([[NSString alloc] initWithData:identifier encoding:NSUTF8StringEncoding]) : nil;
+
+    if (m_parent)
+        m_parent->setSessionId(sessionIdentifierString.get());
+}
+@end
+
+static const NSString *PlaybackSessionIdKey = @"PlaybackSessionID";
+
+namespace WebCore {
+
+CDMSessionAVStreamSession::CDMSessionAVStreamSession(const Vector<int>& protocolVersions, CDMPrivateMediaSourceAVFObjC& cdm, CDMSessionClient* client)
+    : CDMSessionMediaSourceAVFObjC(cdm, client)
+    , m_weakPtrFactory(this)
+    , m_dataParserObserver(adoptNS([[CDMSessionAVStreamSessionObserver alloc] initWithParent:this]))
+    , m_protocolVersions(protocolVersions)
+    , m_mode(Normal)
+{
+}
+
+CDMSessionAVStreamSession::~CDMSessionAVStreamSession()
+{
+    setStreamSession(nullptr);
+
+    for (auto& sourceBuffer : m_sourceBuffers)
+        removeParser(sourceBuffer->parser());
+}
+
+RefPtr<Uint8Array> CDMSessionAVStreamSession::generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode)
+{
+    UNUSED_PARAM(mimeType);
+    UNUSED_PARAM(destinationURL);
+    ASSERT(initData);
+
+    LOG(Media, "CDMSessionAVStreamSession::generateKeyRequest(%p)", this);
+
+    errorCode = MediaPlayer::NoError;
+    systemCode = 0;
+
+    m_initData = initData;
+
+    if (equalIgnoringCase(mimeType, "keyrelease")) {
+        m_mode = KeyRelease;
+        return generateKeyReleaseMessage(errorCode, systemCode);
+    }
+
+    String certificateString(ASCIILiteral("certificate"));
+    RefPtr<Uint8Array> array = Uint8Array::create(certificateString.length());
+    for (unsigned i = 0, length = certificateString.length(); i < length; ++i)
+        array->set(i, certificateString[i]);
+    return array;
+}
+
+void CDMSessionAVStreamSession::releaseKeys()
+{
+    if (m_streamSession) {
+        m_stopped = true;
+        for (auto& sourceBuffer : m_sourceBuffers)
+            sourceBuffer->flush();
+
+        LOG(Media, "CDMSessionAVStreamSession::releaseKeys(%p) - expiring stream session", this);
+        [m_streamSession expire];
+
+        if (!m_certificate)
+            return;
+
+        if (![getAVStreamSessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)])
+            return;
+
+        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+        NSArray* expiredSessions = [getAVStreamSessionClass() pendingExpiredSessionReportsWithAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
+        for (NSData* expiredSessionData in expiredSessions) {
+            NSDictionary *expiredSession = [NSPropertyListSerialization propertyListWithData:expiredSessionData options:kCFPropertyListImmutable format:nullptr error:nullptr];
+            NSString *playbackSessionIdValue = (NSString *)[expiredSession objectForKey:PlaybackSessionIdKey];
+            if (![playbackSessionIdValue isKindOfClass:[NSString class]])
+                continue;
+
+            if (m_sessionId == String(playbackSessionIdValue)) {
+                LOG(Media, "CDMSessionAVStreamSession::releaseKeys(%p) - found session, sending expiration message");
+                m_expiredSession = expiredSessionData;
+                m_client->sendMessage(Uint8Array::create(static_cast<const uint8_t*>([m_expiredSession bytes]), [m_expiredSession length]).get(), emptyString());
+                break;
+            }
+        }
+    }
+}
+
+static bool isEqual(Uint8Array* data, const char* literal)
+{
+    ASSERT(data);
+    ASSERT(literal);
+    unsigned length = data->length();
+
+    for (unsigned i = 0; i < length; ++i) {
+        if (!literal[i])
+            return false;
+
+        if (data->item(i) != static_cast<uint8_t>(literal[i]))
+            return false;
+    }
+    return !literal[length];
+}
+
+bool CDMSessionAVStreamSession::update(Uint8Array* key, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode)
+{
+    bool shouldGenerateKeyRequest = !m_certificate || isEqual(key, "renew");
+    if (!m_certificate) {
+        LOG(Media, "CDMSessionAVStreamSession::update(%p) - certificate data", this);
+
+        m_certificate = key;
+    }
+
+    if (isEqual(key, "acknowledged")) {
+        LOG(Media, "CDMSessionAVStreamSession::update(%p) - acknowleding secure stop message", this);
+
+        if (!m_expiredSession) {
+            errorCode = MediaPlayer::InvalidPlayerState;
+            return false;
+        }
+
+        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+
+        if ([getAVStreamSessionClass() respondsToSelector:@selector(removePendingExpiredSessionReports:withAppIdentifier:storageDirectoryAtURL:)])
+            [getAVStreamSessionClass() removePendingExpiredSessionReports:@[m_expiredSession.get()] withAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
+        m_expiredSession = nullptr;
+        return true;
+    }
+
+    if (m_mode == KeyRelease)
+        return false;
+
+    RefPtr<SourceBufferPrivateAVFObjC> protectedSourceBuffer;
+    for (auto& sourceBuffer : m_sourceBuffers) {
+        if (sourceBuffer->protectedTrackID() != -1) {
+            protectedSourceBuffer = sourceBuffer;
+            break;
+        }
+    }
+
+    if (shouldGenerateKeyRequest) {
+        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+
+        if (m_sourceBuffers.isEmpty())
+            return true;
+
+        if (!protectedSourceBuffer)
+            return true;
+
+        RetainPtr<NSData> initData = adoptNS([[NSData alloc] initWithBytes:m_initData->data() length:m_initData->length()]);
+
+        RetainPtr<NSDictionary> options;
+        if (!m_protocolVersions.isEmpty() && canLoadAVStreamDataParserContentKeyRequestProtocolVersionsKey()) {
+            RetainPtr<NSMutableArray> protocolVersionsOption = adoptNS([[NSMutableArray alloc] init]);
+            for (auto& version : m_protocolVersions) {
+                if (!version)
+                    continue;
+                [protocolVersionsOption addObject:@(version)];
+            }
+
+            options = @{ getAVStreamDataParserContentKeyRequestProtocolVersionsKey(): protocolVersionsOption.get() };
+        }
+
+        NSError* error = nil;
+        RetainPtr<NSData> request = [protectedSourceBuffer->parser() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:initData.get() trackID:protectedSourceBuffer->protectedTrackID() options:options.get() error:&error];
+
+        if (![protectedSourceBuffer->parser() respondsToSelector:@selector(contentProtectionSessionIdentifier)])
+            m_sessionId = createCanonicalUUIDString();
+
+        if (error) {
+            LOG(Media, "CDMSessionAVStreamSession::update(%p) - error:%@", this, [error description]);
+            errorCode = MediaPlayer::InvalidPlayerState;
+            systemCode = std::abs(systemCodeForError(error));
+            return false;
+        }
+
+        nextMessage = Uint8Array::create([request length]);
+        [request getBytes:nextMessage->data() length:nextMessage->length()];
+        return false;
+    }
+
+    ASSERT(!m_sourceBuffers.isEmpty());
+    LOG(Media, "CDMSessionAVStreamSession::update(%p) - key data", this);
+    errorCode = MediaPlayer::NoError;
+    systemCode = 0;
+    RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:key->data() length:key->length()]);
+    [protectedSourceBuffer->parser() processContentKeyResponseData:keyData.get() forTrackID:protectedSourceBuffer->protectedTrackID()];
+
+    return true;
+}
+
+void CDMSessionAVStreamSession::setStreamSession(AVStreamSession *streamSession)
+{
+    if (m_streamSession && canLoadAVStreamSessionContentProtectionSessionIdentifierChangedNotification())
+        [[NSNotificationCenter defaultCenter] removeObserver:m_dataParserObserver.get() name:getAVStreamSessionContentProtectionSessionIdentifierChangedNotification() object:m_streamSession.get()];
+
+    m_streamSession = streamSession;
+
+    if (!m_streamSession)
+        return;
+
+    if (canLoadAVStreamSessionContentProtectionSessionIdentifierChangedNotification())
+        [[NSNotificationCenter defaultCenter] addObserver:m_dataParserObserver.get() selector:@selector(contentProtectionSessionIdentifierChanged:) name:getAVStreamSessionContentProtectionSessionIdentifierChangedNotification() object:m_streamSession.get()];
+
+    NSData* identifier = [streamSession contentProtectionSessionIdentifier];
+    RetainPtr<NSString> sessionIdentifierString = identifier ? adoptNS([[NSString alloc] initWithData:identifier encoding:(NSUTF8StringEncoding)]) : nil;
+    setSessionId(sessionIdentifierString.get());
+}
+
+void CDMSessionAVStreamSession::addParser(AVStreamDataParser* parser)
+{
+    if (m_streamSession)
+        [m_streamSession addStreamDataParser:parser];
+}
+
+void CDMSessionAVStreamSession::removeParser(AVStreamDataParser* parser)
+{
+    if (m_streamSession)
+        [m_streamSession removeStreamDataParser:parser];
+}
+
+PassRefPtr<Uint8Array> CDMSessionAVStreamSession::generateKeyReleaseMessage(unsigned short& errorCode, unsigned long& systemCode)
+{
+    ASSERT(m_mode == KeyRelease);
+    m_certificate = m_initData;
+    RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
+
+    if (![getAVStreamSessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)]) {
+        errorCode = MediaPlayer::KeySystemNotSupported;
+        systemCode = '!mor';
+        return nullptr;
+    }
+
+    NSArray* expiredSessions = [getAVStreamSessionClass() pendingExpiredSessionReportsWithAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
+    if (![expiredSessions count]) {
+        LOG(Media, "CDMSessionAVStreamSession::generateKeyReleaseMessage(%p) - no expired sessions found", this);
+
+        errorCode = MediaPlayer::KeySystemNotSupported;
+        systemCode = '!mor';
+        return nullptr;
+    }
+
+    LOG(Media, "CDMSessionAVStreamSession::generateKeyReleaseMessage(%p) - found %d expired sessions", this, [expiredSessions count]);
+
+    errorCode = 0;
+    systemCode = 0;
+    m_expiredSession = [expiredSessions firstObject];
+    return Uint8Array::create(static_cast<const uint8_t*>([m_expiredSession bytes]), [m_expiredSession length]);
+}
+
+}
+
+#endif
index 680e5ef..75a448b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "CDMSession.h"
 #include "SourceBufferPrivateAVFObjC.h"
 #include <wtf/RetainPtr.h>
+#include <wtf/WeakPtr.h>
 
 #if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
 
-OBJC_CLASS AVStreamSession;
-OBJC_CLASS CDMSessionMediaSourceAVFObjCObserver;
+OBJC_CLASS AVStreamDataParser;
+OBJC_CLASS NSError;
 
 namespace WebCore {
 
+class CDMPrivateMediaSourceAVFObjC;
+
 class CDMSessionMediaSourceAVFObjC : public CDMSession, public SourceBufferPrivateAVFObjCErrorClient {
 public:
-    CDMSessionMediaSourceAVFObjC(const Vector<int>& protocolVersions);
+    CDMSessionMediaSourceAVFObjC(CDMPrivateMediaSourceAVFObjC&, CDMSessionClient*);
     virtual ~CDMSessionMediaSourceAVFObjC();
 
-    virtual CDMSessionType type() override { return CDMSessionTypeMediaSourceAVFObjC; }
+    virtual void addParser(AVStreamDataParser*) = 0;
+    virtual void removeParser(AVStreamDataParser*) = 0;
+
+    // CDMSession
     virtual void setClient(CDMSessionClient* client) override { m_client = client; }
     virtual const String& sessionId() const override { return m_sessionId; }
-    virtual RefPtr<Uint8Array> generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode) override;
-    virtual void releaseKeys() override;
-    virtual bool update(Uint8Array*, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode) override;
 
+    // SourceBufferPrivateAVFObjCErrorClient
     virtual void layerDidReceiveError(AVSampleBufferDisplayLayer *, NSError *, bool& shouldIgnore) override;
     virtual void rendererDidReceiveError(AVSampleBufferAudioRenderer *, NSError *, bool& shouldIgnore) override;
 
-    void setStreamSession(AVStreamSession *);
-
     void addSourceBuffer(SourceBufferPrivateAVFObjC*);
     void removeSourceBuffer(SourceBufferPrivateAVFObjC*);
-
     void setSessionId(const String& sessionId) { m_sessionId = sessionId; }
 
+    void invalidateCDM() { m_cdm = nullptr; }
+
 protected:
+    static long systemCodeForError(NSError *);
     String storagePath() const;
-    PassRefPtr<Uint8Array> generateKeyReleaseMessage(unsigned short& errorCode, unsigned long& systemCode);
 
+    CDMPrivateMediaSourceAVFObjC* m_cdm;
+    CDMSessionClient* m_client { nullptr };
     Vector<RefPtr<SourceBufferPrivateAVFObjC>> m_sourceBuffers;
-    CDMSessionClient* m_client;
-    RetainPtr<AVStreamSession> m_streamSession;
-    RefPtr<Uint8Array> m_initData;
     RefPtr<Uint8Array> m_certificate;
-    RetainPtr<NSData> m_expiredSession;
-    RetainPtr<CDMSessionMediaSourceAVFObjCObserver> m_dataParserObserver;
-    Vector<int> m_protocolVersions;
     String m_sessionId;
-    enum { Normal, KeyRelease } m_mode;
-    bool m_stopped = { false };
+    bool m_stopped { false };
 };
 
 inline CDMSessionMediaSourceAVFObjC* toCDMSessionMediaSourceAVFObjC(CDMSession* session)
 {
-    if (!session || session->type() != CDMSessionTypeMediaSourceAVFObjC)
+    if (!session || (session->type() != CDMSessionTypeAVStreamSession && session->type() != CDMSessionTypeAVContentKeySession))
         return nullptr;
     return static_cast<CDMSessionMediaSourceAVFObjC*>(session);
 }
 
 }
 
-#endif
+#endif // ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
 
 #endif // CDMSessionMediaSourceAVFObjC_h
index fc9773e..42cb88c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
 
-#import "CDM.h"
-#import "CDMSession.h"
-#import "ExceptionCode.h"
+#import "CDMPrivateMediaSourceAVFObjC.h"
 #import "FileSystem.h"
-#import "Logging.h"
-#import "MediaPlayer.h"
-#import "SourceBufferPrivateAVFObjC.h"
-#import "SoftLinking.h"
-#import "UUID.h"
 #import <AVFoundation/AVError.h>
-#import <CoreMedia/CMBase.h>
 #import <cstdlib>
-#import <objc/objc-runtime.h>
-#import <wtf/NeverDestroyed.h>
-#import <runtime/TypedArrayInlines.h>
-
-SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
-SOFT_LINK_CLASS(AVFoundation, AVStreamDataParser);
-SOFT_LINK_CLASS_OPTIONAL(AVFoundation, AVStreamSession);
-SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVStreamDataParserContentKeyRequestProtocolVersionsKey, NSString *)
-SOFT_LINK_CONSTANT_MAY_FAIL(AVFoundation, AVStreamSessionContentProtectionSessionIdentifierChangedNotification, NSString *)
-
-@interface AVStreamDataParser : NSObject
-- (void)processContentKeyResponseData:(NSData *)contentKeyResponseData forTrackID:(CMPersistentTrackID)trackID;
-- (void)processContentKeyResponseError:(NSError *)error forTrackID:(CMPersistentTrackID)trackID;
-- (void)renewExpiringContentKeyResponseDataForTrackID:(CMPersistentTrackID)trackID;
-- (NSData *)streamingContentKeyRequestDataForApp:(NSData *)appIdentifier contentIdentifier:(NSData *)contentIdentifier trackID:(CMPersistentTrackID)trackID options:(NSDictionary *)options error:(NSError **)outError;
-@end
-
-@interface AVStreamSession : NSObject
-- (void)addStreamDataParser:(AVStreamDataParser *)streamDataParser;
-- (void)removeStreamDataParser:(AVStreamDataParser *)streamDataParser;
-- (void)expire;
-- (NSData *)contentProtectionSessionIdentifier;
-+ (NSArray *)pendingExpiredSessionReportsWithAppIdentifier:(NSData *)appIdentifier storageDirectoryAtURL:(NSURL *)storageURL;
-+ (void)removePendingExpiredSessionReports:(NSArray *)expiredSessionReports withAppIdentifier:(NSData *)appIdentifier storageDirectoryAtURL:(NSURL *)storageURL;
-@end
-
-@interface CDMSessionMediaSourceAVFObjCObserver : NSObject {
-    WebCore::CDMSessionMediaSourceAVFObjC *m_parent;
-}
-@end
-
-@implementation CDMSessionMediaSourceAVFObjCObserver
-- (id)initWithParent:(WebCore::CDMSessionMediaSourceAVFObjC *)parent
-{
-    if ((self = [super init]))
-        m_parent = parent;
-    return self;
-}
-
-- (void)contentProtectionSessionIdentifierChanged:(NSNotification *)notification
-{
-    AVStreamSession* streamSession = (AVStreamSession*)[notification object];
-
-    NSData* identifier = [streamSession contentProtectionSessionIdentifier];
-    RetainPtr<NSString> sessionIdentifierString = identifier ? adoptNS([[NSString alloc] initWithData:identifier encoding:NSUTF8StringEncoding]) : nil;
-
-    if (m_parent)
-        m_parent->setSessionId(sessionIdentifierString.get());
-}
-@end
-
-static const NSString *PlaybackSessionIdKey = @"PlaybackSessionID";
 
 namespace WebCore {
 
-CDMSessionMediaSourceAVFObjC::CDMSessionMediaSourceAVFObjC(const Vector<int>& protocolVersions)
-    : m_client(nullptr)
-    , m_dataParserObserver(adoptNS([[CDMSessionMediaSourceAVFObjCObserver alloc] initWithParent:this]))
-    , m_protocolVersions(protocolVersions)
-    , m_mode(Normal)
+CDMSessionMediaSourceAVFObjC::CDMSessionMediaSourceAVFObjC(CDMPrivateMediaSourceAVFObjC& cdm, CDMSessionClient* client)
+    : m_cdm(&cdm)
+    , m_client(client)
 {
 }
 
 CDMSessionMediaSourceAVFObjC::~CDMSessionMediaSourceAVFObjC()
 {
-    for (auto& sourceBuffer : m_sourceBuffers) {
-        if (m_streamSession)
-            [m_streamSession removeStreamDataParser:sourceBuffer->parser()];
-    }
-
-    setStreamSession(nullptr);
-}
-
-RefPtr<Uint8Array> CDMSessionMediaSourceAVFObjC::generateKeyRequest(const String& mimeType, Uint8Array* initData, String& destinationURL, unsigned short& errorCode, unsigned long& systemCode)
-{
-    UNUSED_PARAM(mimeType);
-    UNUSED_PARAM(destinationURL);
-    ASSERT(initData);
-
-    LOG(Media, "CDMSessionMediaSourceAVFObjC::generateKeyRequest(%p)", this);
-
-    errorCode = MediaPlayer::NoError;
-    systemCode = 0;
-
-    m_initData = initData;
-
-    if (equalIgnoringCase(mimeType, "keyrelease")) {
-        m_mode = KeyRelease;
-        return generateKeyReleaseMessage(errorCode, systemCode);
-    }
-
-    String certificateString(ASCIILiteral("certificate"));
-    RefPtr<Uint8Array> array = Uint8Array::create(certificateString.length());
-    for (unsigned i = 0, length = certificateString.length(); i < length; ++i)
-        array->set(i, certificateString[i]);
-    return array;
-}
-
-void CDMSessionMediaSourceAVFObjC::releaseKeys()
-{
-    if (m_streamSession) {
-        m_stopped = true;
-        for (auto& sourceBuffer : m_sourceBuffers)
-            sourceBuffer->flush();
-
-        LOG(Media, "CDMSessionMediaSourceAVFObjC::releaseKeys(%p) - expiring stream session", this);
-        [m_streamSession expire];
-
-        if (!m_certificate)
-            return;
-
-        if (![getAVStreamSessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)])
-            return;
-
-        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
-        NSArray* expiredSessions = [getAVStreamSessionClass() pendingExpiredSessionReportsWithAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
-        for (NSData* expiredSessionData in expiredSessions) {
-            NSDictionary *expiredSession = [NSPropertyListSerialization propertyListWithData:expiredSessionData options:kCFPropertyListImmutable format:nullptr error:nullptr];
-            NSString *playbackSessionIdValue = (NSString *)[expiredSession objectForKey:PlaybackSessionIdKey];
-            if (![playbackSessionIdValue isKindOfClass:[NSString class]])
-                continue;
-
-            if (m_sessionId == String(playbackSessionIdValue)) {
-                LOG(Media, "CDMSessionMediaSourceAVFObjC::releaseKeys(%p) - found session, sending expiration message");
-                m_expiredSession = expiredSessionData;
-                m_client->sendMessage(Uint8Array::create(static_cast<const uint8_t*>([m_expiredSession bytes]), [m_expiredSession length]).get(), emptyString());
-                break;
-            }
-        }
-    }
-}
-
-static bool isEqual(Uint8Array* data, const char* literal)
-{
-    ASSERT(data);
-    ASSERT(literal);
-    unsigned length = data->length();
-
-    for (unsigned i = 0; i < length; ++i) {
-        if (!literal[i])
-            return false;
-
-        if (data->item(i) != static_cast<uint8_t>(literal[i]))
-            return false;
-    }
-    return !literal[length];
-}
-
-static NSInteger systemCodeForError(NSError *error)
-{
-    NSInteger code = [error code];
-    if (code != AVErrorUnknown)
-        return code;
-
-    NSError* underlyingError = [error.userInfo valueForKey:NSUnderlyingErrorKey];
-    if (!underlyingError || ![underlyingError isKindOfClass:[NSError class]])
-        return code;
-    
-    return [underlyingError code];
-}
-
-bool CDMSessionMediaSourceAVFObjC::update(Uint8Array* key, RefPtr<Uint8Array>& nextMessage, unsigned short& errorCode, unsigned long& systemCode)
-{
-    bool shouldGenerateKeyRequest = !m_certificate || isEqual(key, "renew");
-    if (!m_certificate) {
-        LOG(Media, "CDMSessionMediaSourceAVFObjC::update(%p) - certificate data", this);
-
-        m_certificate = key;
-    }
-
-    if (isEqual(key, "acknowledged")) {
-        LOG(Media, "CDMSessionMediaSourceAVFObjC::update(%p) - acknowleding secure stop message", this);
-
-        if (!m_expiredSession) {
-            errorCode = MediaPlayer::InvalidPlayerState;
-            return false;
-        }
-
-        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
-
-        if ([getAVStreamSessionClass() respondsToSelector:@selector(removePendingExpiredSessionReports:withAppIdentifier:storageDirectoryAtURL:)])
-            [getAVStreamSessionClass() removePendingExpiredSessionReports:@[m_expiredSession.get()] withAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
-        m_expiredSession = nullptr;
-        return true;
-    }
-
-    if (m_mode == KeyRelease)
-        return false;
-
-    RefPtr<SourceBufferPrivateAVFObjC> protectedSourceBuffer;
-    for (auto& sourceBuffer : m_sourceBuffers) {
-        if (sourceBuffer->protectedTrackID() != -1) {
-            protectedSourceBuffer = sourceBuffer;
-            break;
-        }
-    }
-
-    if (shouldGenerateKeyRequest) {
-        RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
-
-        if (m_sourceBuffers.isEmpty())
-            return true;
-
-        if (!protectedSourceBuffer)
-            return true;
-
-        RetainPtr<NSData> initData = adoptNS([[NSData alloc] initWithBytes:m_initData->data() length:m_initData->length()]);
-
-
-        RetainPtr<NSDictionary> options;
-        if (!m_protocolVersions.isEmpty() && canLoadAVStreamDataParserContentKeyRequestProtocolVersionsKey()) {
-            RetainPtr<NSMutableArray> protocolVersionsOption = adoptNS([[NSMutableArray alloc] init]);
-            for (auto& version : m_protocolVersions) {
-                if (!version)
-                    continue;
-                [protocolVersionsOption addObject:@(version)];
-            }
-
-            options = @{ getAVStreamDataParserContentKeyRequestProtocolVersionsKey(): protocolVersionsOption.get() };
-        }
-
-        NSError* error = nil;
-        RetainPtr<NSData> request = [protectedSourceBuffer->parser() streamingContentKeyRequestDataForApp:certificateData.get() contentIdentifier:initData.get() trackID:protectedSourceBuffer->protectedTrackID() options:options.get() error:&error];
-
-        if (![protectedSourceBuffer->parser() respondsToSelector:@selector(contentProtectionSessionIdentifier)])
-            m_sessionId = createCanonicalUUIDString();
-
-        if (error) {
-            LOG(Media, "CDMSessionMediaSourceAVFObjC::update(%p) - error:%@", this, [error description]);
-            errorCode = MediaPlayer::InvalidPlayerState;
-            systemCode = std::abs(systemCodeForError(error));
-            return false;
-        }
-
-        nextMessage = Uint8Array::create([request length]);
-        [request getBytes:nextMessage->data() length:nextMessage->length()];
-        return false;
-    }
-
-    ASSERT(!m_sourceBuffers.isEmpty());
-    LOG(Media, "CDMSessionMediaSourceAVFObjC::update(%p) - key data", this);
-    errorCode = MediaPlayer::NoError;
-    systemCode = 0;
-    RetainPtr<NSData> keyData = adoptNS([[NSData alloc] initWithBytes:key->data() length:key->length()]);
-    [protectedSourceBuffer->parser() processContentKeyResponseData:keyData.get() forTrackID:protectedSourceBuffer->protectedTrackID()];
-
-    return true;
+    if (m_cdm)
+        m_cdm->invalidateSession(this);
 }
 
 void CDMSessionMediaSourceAVFObjC::layerDidReceiveError(AVSampleBufferDisplayLayer *, NSError *error, bool& shouldIgnore)
@@ -315,29 +73,14 @@ void CDMSessionMediaSourceAVFObjC::rendererDidReceiveError(AVSampleBufferAudioRe
         m_client->sendError(CDMSessionClient::MediaKeyErrorDomain, code);
 }
 
-void CDMSessionMediaSourceAVFObjC::setStreamSession(AVStreamSession *streamSession)
-{
-    if (m_streamSession && canLoadAVStreamSessionContentProtectionSessionIdentifierChangedNotification())
-        [[NSNotificationCenter defaultCenter] removeObserver:m_dataParserObserver.get() name:getAVStreamSessionContentProtectionSessionIdentifierChangedNotification() object:m_streamSession.get()];
-
-    m_streamSession = streamSession;
-
-    if (!m_streamSession)
-        return;
-
-    if (canLoadAVStreamSessionContentProtectionSessionIdentifierChangedNotification())
-        [[NSNotificationCenter defaultCenter] addObserver:m_dataParserObserver.get() selector:@selector(contentProtectionSessionIdentifierChanged:) name:getAVStreamSessionContentProtectionSessionIdentifierChangedNotification() object:m_streamSession.get()];
-
-    NSData* identifier = [streamSession contentProtectionSessionIdentifier];
-    RetainPtr<NSString> sessionIdentifierString = identifier ? adoptNS([[NSString alloc] initWithData:identifier encoding:(NSUTF8StringEncoding)]) : nil;
-    setSessionId(sessionIdentifierString.get());
-}
 
 void CDMSessionMediaSourceAVFObjC::addSourceBuffer(SourceBufferPrivateAVFObjC* sourceBuffer)
 {
     ASSERT(!m_sourceBuffers.contains(sourceBuffer));
     ASSERT(sourceBuffer);
 
+    addParser(sourceBuffer->parser());
+
     m_sourceBuffers.append(sourceBuffer);
     sourceBuffer->registerForErrorNotifications(this);
 }
@@ -347,47 +90,30 @@ void CDMSessionMediaSourceAVFObjC::removeSourceBuffer(SourceBufferPrivateAVFObjC
     ASSERT(m_sourceBuffers.contains(sourceBuffer));
     ASSERT(sourceBuffer);
 
-    if (m_streamSession)
-        [m_streamSession removeStreamDataParser:sourceBuffer->parser()];
+    removeParser(sourceBuffer->parser());
 
     sourceBuffer->unregisterForErrorNotifications(this);
     m_sourceBuffers.remove(m_sourceBuffers.find(sourceBuffer));
 }
 
-String CDMSessionMediaSourceAVFObjC::storagePath() const
-{
-    return m_client ? pathByAppendingComponent(m_client->mediaKeysStorageDirectory(), "SecureStop.plist") : emptyString();
-}
-
-PassRefPtr<Uint8Array> CDMSessionMediaSourceAVFObjC::generateKeyReleaseMessage(unsigned short& errorCode, unsigned long& systemCode)
+long CDMSessionMediaSourceAVFObjC::systemCodeForError(NSError *error)
 {
-    ASSERT(m_mode == KeyRelease);
-    m_certificate = m_initData;
-    RetainPtr<NSData> certificateData = adoptNS([[NSData alloc] initWithBytes:m_certificate->data() length:m_certificate->length()]);
-
-    if (![getAVStreamSessionClass() respondsToSelector:@selector(pendingExpiredSessionReportsWithAppIdentifier:storageDirectoryAtURL:)]) {
-        errorCode = MediaPlayer::KeySystemNotSupported;
-        systemCode = '!mor';
-        return nullptr;
-    }
-
-    NSArray* expiredSessions = [getAVStreamSessionClass() pendingExpiredSessionReportsWithAppIdentifier:certificateData.get() storageDirectoryAtURL:[NSURL fileURLWithPath:storagePath()]];
-    if (![expiredSessions count]) {
-        LOG(Media, "CDMSessionMediaSourceAVFObjC::generateKeyReleaseMessage(%p) - no expired sessions found", this);
+    NSInteger code = [error code];
+    if (code != AVErrorUnknown)
+        return code;
 
-        errorCode = MediaPlayer::KeySystemNotSupported;
-        systemCode = '!mor';
-        return nullptr;
-    }
+    NSError* underlyingError = [error.userInfo valueForKey:NSUnderlyingErrorKey];
+    if (!underlyingError || ![underlyingError isKindOfClass:[NSError class]])
+        return code;
 
-    LOG(Media, "CDMSessionMediaSourceAVFObjC::generateKeyReleaseMessage(%p) - found %d expired sessions", this, [expiredSessions count]);
+    return [underlyingError code];
+}
 
-    errorCode = 0;
-    systemCode = 0;
-    m_expiredSession = [expiredSessions firstObject];
-    return Uint8Array::create(static_cast<const uint8_t*>([m_expiredSession bytes]), [m_expiredSession length]);
+String CDMSessionMediaSourceAVFObjC::storagePath() const
+{
+    return m_client ? pathByAppendingComponent(m_client->mediaKeysStorageDirectory(), "SecureStop.plist") : emptyString();
 }
 
 }
 
-#endif
+#endif // ENABLE(ENCRYPTED_MEDIA_V2) && ENABLE(MEDIA_SOURCE)
index 54cac71..0af3ea3 100644 (file)
@@ -255,7 +255,7 @@ private:
 #endif
 
 #if ENABLE(ENCRYPTED_MEDIA_V2)
-    std::unique_ptr<CDMSession> createSession(const String& keySystem) override;
+    std::unique_ptr<CDMSession> createSession(const String& keySystem, CDMSessionClient*) override;
 #endif
 
     virtual String languageOfPrimaryAudioTrack() const override;
index af8ba23..980e431 100644 (file)
@@ -162,6 +162,7 @@ typedef AVMediaSelectionOption AVMediaSelectionOptionType;
 #import "CoreMediaSoftLink.h"
 
 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
+
 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreImage)
 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreVideo)
 
@@ -2766,12 +2767,12 @@ void MediaPlayerPrivateAVFoundationObjC::keyAdded()
         m_keyURIToRequestMap.remove(keyId);
 }
 
-std::unique_ptr<CDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem)
+std::unique_ptr<CDMSession> MediaPlayerPrivateAVFoundationObjC::createSession(const String& keySystem, CDMSessionClient* client)
 {
     if (!keySystemIsSupported(keySystem))
         return nullptr;
 
-    return std::make_unique<CDMSessionAVFoundationObjC>(this);
+    return std::make_unique<CDMSessionAVFoundationObjC>(this, client);
 }
 #endif
 
index 5371d66..7eb99da 100644 (file)
@@ -86,6 +86,7 @@ public:
     bool hasStreamSession() { return m_streamSession; }
     AVStreamSession *streamSession();
     virtual void setCDMSession(CDMSession*) override;
+    CDMSessionMediaSourceAVFObjC* cdmSession() const { return m_session; }
     void keyNeeded(Uint8Array*);
 #endif
 
index bc4c698..155719b 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION)
 
+#import "CDMSessionAVStreamSession.h"
 #import "CDMSessionMediaSourceAVFObjC.h"
 #import "FileSystem.h"
 #import "Logging.h"
@@ -703,19 +704,15 @@ AVStreamSession* MediaPlayerPrivateMediaSourceAVFObjC::streamSession()
 
 void MediaPlayerPrivateMediaSourceAVFObjC::setCDMSession(CDMSession* session)
 {
-    if (m_session) {
-        for (auto& sourceBuffer : m_mediaSourcePrivate->sourceBuffers())
-            m_session->removeSourceBuffer(sourceBuffer.get());
-        m_session = nullptr;
-    }
+    if (session == m_session)
+        return;
 
     m_session = toCDMSessionMediaSourceAVFObjC(session);
 
-    if (m_session) {
-        m_session->setStreamSession(m_streamSession.get());
-        for (auto& sourceBuffer : m_mediaSourcePrivate->sourceBuffers())
-            m_session->addSourceBuffer(sourceBuffer.get());
-    }
+    if (CDMSessionAVStreamSession* cdmStreamSession = toCDMSessionAVStreamSession(m_session))
+        cdmStreamSession->setStreamSession(m_streamSession.get());
+    for (auto& sourceBuffer : m_mediaSourcePrivate->sourceBuffers())
+        sourceBuffer->setCDMSession(m_session);
 }
 
 void MediaPlayerPrivateMediaSourceAVFObjC::keyNeeded(Uint8Array* initData)
index 777ba30..f948c86 100644 (file)
 #if ENABLE(MEDIA_SOURCE) && USE(AVFOUNDATION)
 
 #include "SourceBufferPrivate.h"
+#include <dispatch/semaphore.h>
 #include <wtf/Deque.h>
 #include <wtf/HashMap.h>
 #include <wtf/MediaTime.h>
+#include <wtf/OSObjectPtr.h>
 #include <wtf/RefPtr.h>
 #include <wtf/RetainPtr.h>
 #include <wtf/Vector.h>
@@ -53,6 +55,7 @@ typedef const struct opaqueCMFormatDescription *CMFormatDescriptionRef;
 
 namespace WebCore {
 
+class CDMSessionMediaSourceAVFObjC;
 class MediaSourcePrivateAVFObjC;
 class TimeRanges;
 class AudioTrackPrivate;
@@ -80,7 +83,7 @@ public:
     void didProvideMediaDataForTrackID(int trackID, CMSampleBufferRef, const String& mediaType, unsigned flags);
     void didReachEndOfTrackWithTrackID(int trackID, const String& mediaType);
     void willProvideContentKeyRequestInitializationDataForTrackID(int trackID);
-    void didProvideContentKeyRequestInitializationDataForTrackID(NSData*, int trackID);
+    void didProvideContentKeyRequestInitializationDataForTrackID(NSData*, int trackID, OSObjectPtr<dispatch_semaphore_t>);
 
     bool processCodedFrame(int trackID, CMSampleBufferRef, const String& mediaType);
 
@@ -96,6 +99,7 @@ public:
 
     int protectedTrackID() const { return m_protectedTrackID; }
     AVStreamDataParser* parser() const { return m_parser.get(); }
+    void setCDMSession(CDMSessionMediaSourceAVFObjC*);
 
     void flush();
 
@@ -142,9 +146,11 @@ private:
     HashMap<int, RetainPtr<AVSampleBufferAudioRenderer>> m_audioRenderers;
     RetainPtr<WebAVStreamDataParserListener> m_delegate;
     RetainPtr<WebAVSampleBufferErrorListener> m_errorListener;
+    OSObjectPtr<dispatch_semaphore_t> m_hasSessionSemaphore;
 
     MediaSourcePrivateAVFObjC* m_mediaSource;
     SourceBufferPrivateClient* m_client;
+    CDMSessionMediaSourceAVFObjC* m_session { nullptr };
 
     FloatSize m_cachedSize;
     bool m_parsingSucceeded;
index effbd71..e5df75f 100644 (file)
@@ -255,11 +255,6 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
 #endif
     ASSERT(streamDataParser == _parser);
 
-    if (isMainThread()) {
-        _parent->willProvideContentKeyRequestInitializationDataForTrackID(trackID);
-        return;
-    }
-
     // We must call synchronously to the main thread, as the AVStreamSession must be associated
     // with the streamDataParser before the delegate method returns.
     RetainPtr<WebAVStreamDataParserListener> strongSelf = self;
@@ -278,10 +273,12 @@ SOFT_LINK_CONSTANT(AVFoundation, AVSampleBufferDisplayLayerFailedToDecodeNotific
     RetainPtr<WebAVStreamDataParserListener> strongSelf = self;
 
     RetainPtr<NSData> strongData = initData;
-    callOnMainThread([strongSelf, strongData, trackID] {
+    OSObjectPtr<dispatch_semaphore_t> hasSessionSemaphore = adoptOSObject(dispatch_semaphore_create(0));
+    callOnMainThread([strongSelf, strongData, trackID,  hasSessionSemaphore] {
         if (strongSelf->_parent)
-            strongSelf->_parent->didProvideContentKeyRequestInitializationDataForTrackID(strongData.get(), trackID);
+            strongSelf->_parent->didProvideContentKeyRequestInitializationDataForTrackID(strongData.get(), trackID, hasSessionSemaphore);
     });
+    dispatch_semaphore_wait(hasSessionSemaphore.get(), DISPATCH_TIME_FOREVER);
 }
 @end
 
@@ -614,6 +611,9 @@ SourceBufferPrivateAVFObjC::~SourceBufferPrivateAVFObjC()
     ASSERT(!m_client);
     destroyParser();
     destroyRenderers();
+
+    if (m_hasSessionSemaphore)
+        dispatch_semaphore_signal(m_hasSessionSemaphore.get());
 }
 
 void SourceBufferPrivateAVFObjC::didParseStreamDataAsAsset(AVAsset* asset)
@@ -719,15 +719,14 @@ void SourceBufferPrivateAVFObjC::willProvideContentKeyRequestInitializationDataF
     LOG(MediaSource, "SourceBufferPrivateAVFObjC::willProvideContentKeyRequestInitializationDataForTrackID(%p) - track:%d", this, trackID);
     m_protectedTrackID = trackID;
 
-    BEGIN_BLOCK_OBJC_EXCEPTIONS;
-    [m_mediaSource->player()->streamSession() addStreamDataParser:m_parser.get()];
-    END_BLOCK_OBJC_EXCEPTIONS;
+    if (CDMSessionMediaSourceAVFObjC* session = m_mediaSource->player()->cdmSession())
+        session->addParser(m_parser.get());
 #else
     UNUSED_PARAM(trackID);
 #endif
 }
 
-void SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID(NSData* initData, int trackID)
+void SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataForTrackID(NSData* initData, int trackID, OSObjectPtr<dispatch_semaphore_t> hasSessionSemaphore)
 {
     if (!m_mediaSource)
         return;
@@ -739,6 +738,14 @@ void SourceBufferPrivateAVFObjC::didProvideContentKeyRequestInitializationDataFo
     RefPtr<Uint8Array> initDataArray = Uint8Array::create([initData length]);
     [initData getBytes:initDataArray->data() length:initDataArray->length()];
     m_mediaSource->sourceBufferKeyNeeded(this, initDataArray.get());
+    if (auto session = m_mediaSource->player()->cdmSession()) {
+        session->addParser(m_parser.get());
+        dispatch_semaphore_signal(hasSessionSemaphore.get());
+    } else {
+        if (m_hasSessionSemaphore)
+            dispatch_semaphore_signal(m_hasSessionSemaphore.get());
+        m_hasSessionSemaphore = hasSessionSemaphore;
+    }
 #else
     UNUSED_PARAM(initData);
 #endif
@@ -921,6 +928,25 @@ void SourceBufferPrivateAVFObjC::trackDidChangeEnabled(AudioTrackPrivateMediaSou
     }
 }
 
+void SourceBufferPrivateAVFObjC::setCDMSession(CDMSessionMediaSourceAVFObjC* session)
+{
+    if (session == m_session)
+        return;
+
+    if (m_session)
+        m_session->removeSourceBuffer(this);
+
+    m_session = session;
+
+    if (m_session) {
+        m_session->addSourceBuffer(this);
+        if (m_hasSessionSemaphore) {
+            dispatch_semaphore_signal(m_hasSessionSemaphore.get());
+            m_hasSessionSemaphore = nullptr;
+        }
+    }
+}
+
 void SourceBufferPrivateAVFObjC::flush()
 {
     if (m_displayLayer)
index d085d3a..d0bd2bd 100644 (file)
@@ -66,6 +66,7 @@
 #endif
 
 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
+
 SOFT_LINK_CLASS(AVFoundation, AVPlayerLayer)
 
 using namespace WebCore;
index df2518f..b734ffd 100644 (file)
@@ -39,7 +39,7 @@ namespace WebCore {
 
 class MockCDMSession : public CDMSession {
 public:
-    MockCDMSession();
+    MockCDMSession(CDMSessionClient*);
     virtual ~MockCDMSession() { }
 
     virtual void setClient(CDMSessionClient* client) override { m_client = client; }
@@ -71,9 +71,9 @@ bool MockCDM::supportsMIMEType(const String& mimeType)
     return equalIgnoringCase(mimeType, "video/mock");
 }
 
-std::unique_ptr<CDMSession> MockCDM::createSession()
+std::unique_ptr<CDMSession> MockCDM::createSession(CDMSessionClient* client)
 {
-    return std::make_unique<MockCDMSession>();
+    return std::make_unique<MockCDMSession>(client);
 }
 
 static Uint8Array* initDataPrefix()
@@ -106,8 +106,9 @@ static String generateSessionId()
     return String::number(monotonicallyIncreasingSessionId++);
 }
 
-MockCDMSession::MockCDMSession()
-    : m_sessionId(generateSessionId())
+MockCDMSession::MockCDMSession(CDMSessionClient* client)
+    : m_client(client)
+    , m_sessionId(generateSessionId())
 {
 }
 
index 74f38b9..2cd818d 100644 (file)
@@ -47,7 +47,7 @@ public:
     virtual ~MockCDM() { }
 
     virtual bool supportsMIMEType(const String& mimeType) override;
-    virtual std::unique_ptr<CDMSession> createSession() override;
+    virtual std::unique_ptr<CDMSession> createSession(CDMSessionClient*) override;
 
 protected:
     CDM* m_cdm;