- Revision
- 275599
- Author
- eoca...@igalia.com
- Date
- 2021-04-07 02:53:11 -0700 (Wed, 07 Apr 2021)
Log Message
[EME][GStreamer] Abort decryptor operations immediately and without errors on flush
https://bugs.webkit.org/show_bug.cgi?id=223742
Reviewed by Xabier Rodriguez-Calvar.
A decryptor transformInPlace() operation can cause potentially long waits in
two situations:
- transformInPlace() is waiting to get the cdmProxy.
- The CDMProxy::decrypt() method is internally waiting for a specific key to
become available.
If a seek operation is performed during those long waits, the main thread will
be blocked until the seek finishes the conditions those long waits are waiting
for will never be fulfilled (because the operations that complete them happen
in the main thread, which is blocked), the internal wait timeouts will trigger
and the decoder will trigger an unrecoverable error.
The solution for this is to break the waits by issuing the right notifications,
and to detect the flushes performed by the seek, distinguising this special
"abort" case from a regular error, so that the situation is no longer
unrecoverable.
This solution involves changes in several layers. A public
CDMProxy::abortWaitingForKey() method is exposed to allow the decryptor to
awake the inner waitFor() that checks for the key. The cdmProxy wait is also
awaken in case there's no cdmProxy available yet.
In order to distinguish if the awakenings are caused by real errors (no
cdmProxy, no key available) or by a flush operation (caused by the seek), the
decryptor first needs to keep track of the "flushing" state and allow other
objects to know about it. CDMProxy is one of those objects, but due to layer
limitations it can't directly ask about it to the decryptor (it can't "see"
it).
A new CDMProxyDecryptionClient interface is created, and the decryptor will
hold an implementation of it (as CDMProxyDecryptorClientImplementation).
CDMProxy can then know the client isAborting(), and the client will know the
decryptor and will ask about it.
As the pipeline, and thus the decryptor, can be destroyed at any moment,
the client holds a WeakPtr to the decryptor and will only ask if isAborting()
if the pointer is still alive.
* platform/encryptedmedia/CDMProxy.cpp:
(WebCore::CDMProxy::abortWaitingForKey const): Notify waiting threads.
(WebCore::CDMProxy::tryWaitForKeyHandle const): Take a client and ask it if it's aborting.
(WebCore::CDMProxy::getOrWaitForKeyHandle const): Pass the client along.
(WebCore::CDMProxy::getOrWaitForKeyValue const): Ditto.
* platform/encryptedmedia/CDMProxy.h: Declaration and usage of the CDMProxyDecryptionClient interface.
* platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:
(WebCore::CDMProxyClearKey::cencSetDecryptionKey): Pass the client along from the context.
* platform/graphics/gstreamer/eme/CDMProxyClearKey.h: Store client in the cencDecryptContext.
* platform/graphics/gstreamer/eme/CDMProxyThunder.cpp:
(WebCore::CDMProxyThunder::getDecryptionSession const): Pass the client along from the context.
(WebCore::CDMProxyThunder::decrypt): Abort operations aren't an error, so just trigger a warning if there's no session.
* platform/graphics/gstreamer/eme/CDMProxyThunder.h: Store client in DecryptionContext.
* platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:
(decrypt): Ask for client to the superclass and store it in the context.
* platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
(CDMProxyDecryptionClientImplementation::CDMProxyDecryptionClientImplementation): Implementation of the CDMProxyDecryptionClient interface.
(CDMProxyDecryptionClientImplementation::isAborting): Ask if the decryptor is flushing.
(constructed): Initialize the client.
(transformInPlace): Check the flush status after waiting for the CDMProxy and for the decryption performed by the subclasses. Release the lock when not needed.
(isCDMProxyAvailable): Renamed mutex to be more generic.
(attachCDMProxy): Ditto. Also renamed condition for the same reason.
(sinkEventHandler): Manage isFlushing status and awake waits for cdmProxy or for session depending on the lifecycle stage.
(webKitMediaCommonEncryptionDecryptIsFlushing): Expose isFlushing status.
(webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient): Return Weak reference to the client.
(changeState): Renamed condition.
(setContext): Renamed mutex.
* platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h: New getters for isFlushing and the client.
* platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp:
(decrypt): Ask for client to the superclass and store it in the context.
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (275598 => 275599)
--- trunk/Source/WebCore/ChangeLog 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/ChangeLog 2021-04-07 09:53:11 UTC (rev 275599)
@@ -1,5 +1,82 @@
2021-04-07 Enrique Ocaña González <eoca...@igalia.com>
+ [EME][GStreamer] Abort decryptor operations immediately and without errors on flush
+ https://bugs.webkit.org/show_bug.cgi?id=223742
+
+ Reviewed by Xabier Rodriguez-Calvar.
+
+ A decryptor transformInPlace() operation can cause potentially long waits in
+ two situations:
+
+ - transformInPlace() is waiting to get the cdmProxy.
+ - The CDMProxy::decrypt() method is internally waiting for a specific key to
+ become available.
+
+ If a seek operation is performed during those long waits, the main thread will
+ be blocked until the seek finishes the conditions those long waits are waiting
+ for will never be fulfilled (because the operations that complete them happen
+ in the main thread, which is blocked), the internal wait timeouts will trigger
+ and the decoder will trigger an unrecoverable error.
+
+ The solution for this is to break the waits by issuing the right notifications,
+ and to detect the flushes performed by the seek, distinguising this special
+ "abort" case from a regular error, so that the situation is no longer
+ unrecoverable.
+
+ This solution involves changes in several layers. A public
+ CDMProxy::abortWaitingForKey() method is exposed to allow the decryptor to
+ awake the inner waitFor() that checks for the key. The cdmProxy wait is also
+ awaken in case there's no cdmProxy available yet.
+
+ In order to distinguish if the awakenings are caused by real errors (no
+ cdmProxy, no key available) or by a flush operation (caused by the seek), the
+ decryptor first needs to keep track of the "flushing" state and allow other
+ objects to know about it. CDMProxy is one of those objects, but due to layer
+ limitations it can't directly ask about it to the decryptor (it can't "see"
+ it).
+
+ A new CDMProxyDecryptionClient interface is created, and the decryptor will
+ hold an implementation of it (as CDMProxyDecryptorClientImplementation).
+ CDMProxy can then know the client isAborting(), and the client will know the
+ decryptor and will ask about it.
+
+ As the pipeline, and thus the decryptor, can be destroyed at any moment,
+ the client holds a WeakPtr to the decryptor and will only ask if isAborting()
+ if the pointer is still alive.
+
+ * platform/encryptedmedia/CDMProxy.cpp:
+ (WebCore::CDMProxy::abortWaitingForKey const): Notify waiting threads.
+ (WebCore::CDMProxy::tryWaitForKeyHandle const): Take a client and ask it if it's aborting.
+ (WebCore::CDMProxy::getOrWaitForKeyHandle const): Pass the client along.
+ (WebCore::CDMProxy::getOrWaitForKeyValue const): Ditto.
+ * platform/encryptedmedia/CDMProxy.h: Declaration and usage of the CDMProxyDecryptionClient interface.
+ * platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp:
+ (WebCore::CDMProxyClearKey::cencSetDecryptionKey): Pass the client along from the context.
+ * platform/graphics/gstreamer/eme/CDMProxyClearKey.h: Store client in the cencDecryptContext.
+ * platform/graphics/gstreamer/eme/CDMProxyThunder.cpp:
+ (WebCore::CDMProxyThunder::getDecryptionSession const): Pass the client along from the context.
+ (WebCore::CDMProxyThunder::decrypt): Abort operations aren't an error, so just trigger a warning if there's no session.
+ * platform/graphics/gstreamer/eme/CDMProxyThunder.h: Store client in DecryptionContext.
+ * platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:
+ (decrypt): Ask for client to the superclass and store it in the context.
+ * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
+ (CDMProxyDecryptionClientImplementation::CDMProxyDecryptionClientImplementation): Implementation of the CDMProxyDecryptionClient interface.
+ (CDMProxyDecryptionClientImplementation::isAborting): Ask if the decryptor is flushing.
+ (constructed): Initialize the client.
+ (transformInPlace): Check the flush status after waiting for the CDMProxy and for the decryption performed by the subclasses. Release the lock when not needed.
+ (isCDMProxyAvailable): Renamed mutex to be more generic.
+ (attachCDMProxy): Ditto. Also renamed condition for the same reason.
+ (sinkEventHandler): Manage isFlushing status and awake waits for cdmProxy or for session depending on the lifecycle stage.
+ (webKitMediaCommonEncryptionDecryptIsFlushing): Expose isFlushing status.
+ (webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient): Return Weak reference to the client.
+ (changeState): Renamed condition.
+ (setContext): Renamed mutex.
+ * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h: New getters for isFlushing and the client.
+ * platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp:
+ (decrypt): Ask for client to the superclass and store it in the context.
+
+2021-04-07 Enrique Ocaña González <eoca...@igalia.com>
+
[GStreamer] Only seek to change the rate un updatePlaybackRate() when needed
https://bugs.webkit.org/show_bug.cgi?id=224235
Modified: trunk/Source/WebCore/platform/encryptedmedia/CDMProxy.cpp (275598 => 275599)
--- trunk/Source/WebCore/platform/encryptedmedia/CDMProxy.cpp 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/encryptedmedia/CDMProxy.cpp 2021-04-07 09:53:11 UTC (rev 275599)
@@ -271,8 +271,14 @@
m_instance->stoppedWaitingForKey();
}
-Optional<Ref<KeyHandle>> CDMProxy::tryWaitForKeyHandle(const KeyIDType& keyID) const
+void CDMProxy::abortWaitingForKey() const
{
+ LOG(EME, "EME - CDMProxy - abort waiting for key ID");
+ m_keysCondition.notifyAll();
+}
+
+Optional<Ref<KeyHandle>> CDMProxy::tryWaitForKeyHandle(const KeyIDType& keyID, WeakPtr<CDMProxyDecryptionClient>&& client) const
+{
startedWaitingForKey();
// Unconditionally saying we have stopped waiting for a key means that decryptors only get
// one shot at fetching a key. If MaxKeyWaitTimeSeconds expires, that's it, no more clear bytes
@@ -281,21 +287,25 @@
stoppedWaitingForKey();
});
LOG(EME, "EME - CDMProxy - trying to wait for key ID %s", vectorToHexString(keyID).ascii().data());
- bool wasKeyReceived = false;
+ bool wasKeyAvailable = false;
{
auto locker = holdLock(m_keysMutex);
- wasKeyReceived = m_keysCondition.waitFor(m_keysMutex, CDMProxy::MaxKeyWaitTimeSeconds, [&, this, keyID]() {
- return keyAvailableUnlocked(keyID);
+
+ m_keysCondition.waitFor(m_keysMutex, CDMProxy::MaxKeyWaitTimeSeconds, [this, keyID, client = WTFMove(client), &wasKeyAvailable]() {
+ if (!client || client->isAborting())
+ return true;
+ wasKeyAvailable = keyAvailableUnlocked(keyID);
+ return wasKeyAvailable;
});
}
- if (wasKeyReceived) {
+ if (wasKeyAvailable) {
+ RefPtr<KeyHandle> handle = keyHandle(keyID);
LOG(EME, "EME - CDMProxy - successfully waited for key ID %s", vectorToHexString(keyID).ascii().data());
- RefPtr<KeyHandle> handle = keyHandle(keyID);
return makeOptional(handle.releaseNonNull());
}
-
- LOG(EME, "EME - CDMProxy - key ID %s not available", vectorToHexString(keyID).ascii().data());
+
+ LOG(EME, "EME - CDMProxy - key ID %s not available or operation aborted", vectorToHexString(keyID).ascii().data());
return WTF::nullopt;
}
@@ -310,11 +320,11 @@
return keyAvailableUnlocked(keyID);
}
-Optional<Ref<KeyHandle>> CDMProxy::getOrWaitForKeyHandle(const KeyIDType& keyID) const
+Optional<Ref<KeyHandle>> CDMProxy::getOrWaitForKeyHandle(const KeyIDType& keyID, WeakPtr<CDMProxyDecryptionClient>&& client) const
{
if (!keyAvailable(keyID)) {
LOG(EME, "EME - CDMProxy key cache does not contain key ID %s", vectorToHexString(keyID).ascii().data());
- return tryWaitForKeyHandle(keyID);
+ return tryWaitForKeyHandle(keyID, WTFMove(client));
}
RefPtr<KeyHandle> handle = keyHandle(keyID);
@@ -321,9 +331,9 @@
return makeOptional(handle.releaseNonNull());
}
-Optional<KeyHandleValueVariant> CDMProxy::getOrWaitForKeyValue(const KeyIDType& keyID) const
+Optional<KeyHandleValueVariant> CDMProxy::getOrWaitForKeyValue(const KeyIDType& keyID, WeakPtr<CDMProxyDecryptionClient>&& client) const
{
- if (auto keyHandle = getOrWaitForKeyHandle(keyID))
+ if (auto keyHandle = getOrWaitForKeyHandle(keyID, WTFMove(client)))
return makeOptional((*keyHandle)->value());
return WTF::nullopt;
}
Modified: trunk/Source/WebCore/platform/encryptedmedia/CDMProxy.h (275598 => 275599)
--- trunk/Source/WebCore/platform/encryptedmedia/CDMProxy.h 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/encryptedmedia/CDMProxy.h 2021-04-07 09:53:11 UTC (rev 275599)
@@ -146,6 +146,7 @@
};
class CDMInstanceProxy;
+class CDMProxyDecryptionClient;
// Handle to a "real" CDM, not the _javascript_ facade. This can be used
// from background threads (i.e. decryptors).
@@ -157,14 +158,15 @@
void updateKeyStore(const KeyStore& newKeyStore);
void setInstance(CDMInstanceProxy*);
+ void abortWaitingForKey() const;
protected:
RefPtr<KeyHandle> keyHandle(const KeyIDType&) const;
bool keyAvailable(const KeyIDType&) const;
bool keyAvailableUnlocked(const KeyIDType&) const;
- Optional<Ref<KeyHandle>> tryWaitForKeyHandle(const KeyIDType&) const;
- Optional<Ref<KeyHandle>> getOrWaitForKeyHandle(const KeyIDType&) const;
- Optional<KeyHandleValueVariant> getOrWaitForKeyValue(const KeyIDType&) const;
+ Optional<Ref<KeyHandle>> tryWaitForKeyHandle(const KeyIDType&, WeakPtr<CDMProxyDecryptionClient>&&) const;
+ Optional<Ref<KeyHandle>> getOrWaitForKeyHandle(const KeyIDType&, WeakPtr<CDMProxyDecryptionClient>&&) const;
+ Optional<KeyHandleValueVariant> getOrWaitForKeyValue(const KeyIDType&, WeakPtr<CDMProxyDecryptionClient>&&) const;
void startedWaitingForKey() const;
void stoppedWaitingForKey() const;
const CDMInstanceProxy* instance() const { return m_instance; }
@@ -253,6 +255,12 @@
KeyStore m_keyStore;
};
+class CDMProxyDecryptionClient : public CanMakeWeakPtr<CDMProxyDecryptionClient, WeakPtrFactoryInitialization::Eager> {
+public:
+ virtual bool isAborting() = 0;
+ virtual ~CDMProxyDecryptionClient() = default;
+};
+
} // namespace WebCore
#endif // ENABLE(ENCRYPTED_MEDIA)
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.cpp 2021-04-07 09:53:11 UTC (rev 275599)
@@ -113,7 +113,7 @@
return true;
}
-bool CDMProxyClearKey::cencSetDecryptionKey(const cencDecryptContext& in)
+bool CDMProxyClearKey::cencSetDecryptionKey(cencDecryptContext& in)
{
// FIXME: Unnecessary copy, can we avoid this while still exposing
// a non-GStreamer-specific DecryptInput API? These buffers are
@@ -121,7 +121,7 @@
Vector<uint8_t> keyIDVec;
keyIDVec.append(in.keyID, in.keyIDSizeInBytes);
- auto keyData = getOrWaitForKeyValue(keyIDVec);
+ auto keyData = getOrWaitForKeyValue(keyIDVec, WTFMove(in.cdmProxyDecryptionClient));
if (!keyData)
return false;
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyClearKey.h 2021-04-07 09:53:11 UTC (rev 275599)
@@ -91,6 +91,7 @@
const uint8_t* subsamplesBuffer;
size_t subsamplesBufferSizeInBytes;
size_t numSubsamples;
+ WeakPtr<CDMProxyDecryptionClient> cdmProxyDecryptionClient;
bool isSubsampled() const { return numSubsamples; }
};
@@ -107,7 +108,7 @@
// we will surely have to support more protection schemes. Can we
// reuse some Crypto APIs in WebCore?
bool cencSetCounterVector(const cencDecryptContext&);
- bool cencSetDecryptionKey(const cencDecryptContext&);
+ bool cencSetDecryptionKey(cencDecryptContext&);
bool cencDecryptFullSample(cencDecryptContext&);
bool cencDecryptSubsampled(cencDecryptContext&);
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyThunder.cpp (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyThunder.cpp 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyThunder.cpp 2021-04-07 09:53:11 UTC (rev 275599)
@@ -44,7 +44,7 @@
// NOTE: YouTube 2019 EME conformance tests expect this to be >=5s.
const WTF::Seconds s_licenseKeyResponseTimeout = WTF::Seconds(6);
-BoxPtr<OpenCDMSession> CDMProxyThunder::getDecryptionSession(const DecryptionContext& in) const
+BoxPtr<OpenCDMSession> CDMProxyThunder::getDecryptionSession(DecryptionContext& in) const
{
GstMappedBuffer mappedKeyID(in.keyIDBuffer, GST_MAP_READ);
if (!mappedKeyID) {
@@ -54,7 +54,7 @@
auto keyID = mappedKeyID.createVector();
- auto keyHandle = getOrWaitForKeyHandle(keyID);
+ auto keyHandle = getOrWaitForKeyHandle(keyID, WTFMove(in.cdmProxyDecryptionClient));
if (!keyHandle.hasValue() || !keyHandle.value()->isStatusCurrentlyValid())
return nullptr;
@@ -84,7 +84,7 @@
{
BoxPtr<OpenCDMSession> session = getDecryptionSession(input);
if (!session) {
- GST_ERROR("there is no valid session to decrypt for the provided key ID");
+ GST_WARNING("there is no valid session to decrypt for the provided key ID (or the operation was aborted)");
return false;
}
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyThunder.h (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyThunder.h 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/CDMProxyThunder.h 2021-04-07 09:53:11 UTC (rev 275599)
@@ -53,6 +53,7 @@
GstBuffer* dataBuffer;
GstBuffer* subsamplesBuffer;
size_t numSubsamples;
+ WeakPtr<CDMProxyDecryptionClient> cdmProxyDecryptionClient;
};
bool decrypt(DecryptionContext&);
@@ -59,7 +60,7 @@
const String& keySystem() { return m_keySystem; }
private:
- BoxPtr<OpenCDMSession> getDecryptionSession(const DecryptionContext&) const;
+ BoxPtr<OpenCDMSession> getDecryptionSession(DecryptionContext&) const;
String m_keySystem;
};
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp 2021-04-07 09:53:11 UTC (rev 275599)
@@ -125,7 +125,7 @@
return false;
}
- CDMProxyClearKey::cencDecryptContext context;
+ CDMProxyClearKey::cencDecryptContext context = { };
context.keyID = mappedKeyIdBuffer.data();
context.keyIDSizeInBytes = mappedKeyIdBuffer.size();
context.iv = mappedIVBuffer.data();
@@ -145,6 +145,7 @@
context.subsamplesBuffer = mappedSubsamplesBuffer.data();
context.subsamplesBufferSizeInBytes = mappedSubsamplesBuffer.size();
}
+ context.cdmProxyDecryptionClient = webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient(self);
return priv->cdmProxy->cencDecrypt(context);
}
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp 2021-04-07 09:53:11 UTC (rev 275599)
@@ -36,12 +36,31 @@
using WebCore::CDMProxy;
+// Instances of this class are tied to the decryptor lifecycle. They can't be alive after the decryptor has been destroyed.
+class CDMProxyDecryptionClientImplementation : public WebCore::CDMProxyDecryptionClient {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ CDMProxyDecryptionClientImplementation(WebKitMediaCommonEncryptionDecrypt* decryptor)
+ : m_decryptor(decryptor) { }
+ virtual bool isAborting()
+ {
+ return webKitMediaCommonEncryptionDecryptIsFlushing(m_decryptor);
+ }
+ virtual ~CDMProxyDecryptionClientImplementation() = default;
+private:
+ WebKitMediaCommonEncryptionDecrypt* m_decryptor;
+};
+
#define WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptPrivate))
struct _WebKitMediaCommonEncryptionDecryptPrivate {
RefPtr<CDMProxy> cdmProxy;
- Lock cdmAttachmentMutex;
- Condition cdmAttachmentCondition;
+ // Protect the access to the structure members.
+ Lock mutex;
+ Condition condition;
+
+ bool isFlushing { false };
+ std::unique_ptr<CDMProxyDecryptionClientImplementation> cdmProxyDecryptionClientImplementation;
};
static constexpr Seconds MaxSecondsToWaitForCDMProxy = 5_s;
@@ -85,6 +104,10 @@
gst_base_transform_set_in_place(base, TRUE);
gst_base_transform_set_passthrough(base, FALSE);
gst_base_transform_set_gap_aware(base, FALSE);
+
+ WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base);
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+ priv->cdmProxyDecryptionClientImplementation = WTF::makeUnique<CDMProxyDecryptionClientImplementation>(self);
}
static GstCaps* transformCaps(GstBaseTransform* base, GstPadDirection direction, GstCaps* caps, GstCaps* filter)
@@ -168,14 +191,24 @@
return GST_FLOW_OK;
}
- LockHolder locker(priv->cdmAttachmentMutex);
+ LockHolder locker(priv->mutex);
+ if (priv->isFlushing) {
+ GST_DEBUG_OBJECT(self, "Decryption aborted because of flush");
+ return GST_FLOW_FLUSHING;
+ }
+
// The CDM instance needs to be negotiated before we can begin decryption.
if (!priv->cdmProxy) {
GST_DEBUG_OBJECT(self, "CDM not available, going to wait for it");
- priv->cdmAttachmentCondition.waitFor(priv->cdmAttachmentMutex, MaxSecondsToWaitForCDMProxy, [priv] {
- return priv->cdmProxy;
+ priv->condition.waitFor(priv->mutex, MaxSecondsToWaitForCDMProxy, [priv] {
+ return priv->isFlushing || priv->cdmProxy;
});
+ // Note that waitFor() releases the mutex lock internally while it waits, so isFlushing may have been changed.
+ if (priv->isFlushing) {
+ GST_DEBUG_OBJECT(self, "Decryption aborted because of flush");
+ return GST_FLOW_FLUSHING;
+ }
if (!priv->cdmProxy) {
GST_ERROR_OBJECT(self, "CDMProxy was not retrieved in time");
return GST_FLOW_NOT_SUPPORTED;
@@ -182,6 +215,7 @@
}
GST_DEBUG_OBJECT(self, "CDM now available with address %p", priv->cdmProxy.get());
}
+
auto removeProtectionMetaOnReturn = makeScopeExit([buffer, protectionMeta] {
gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
});
@@ -245,7 +279,21 @@
WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
GST_TRACE_OBJECT(self, "decrypting");
- if (!klass->decrypt(self, ivBuffer, keyIDBuffer, buffer, subSampleCount, subSamplesBuffer)) {
+
+ // Temporarily release the lock while we don't need to access priv. The lower level API is used
+ // in order to avoid creating several scopes with different LockHolder instances in each one.
+ priv->mutex.unlock();
+
+ bool didDecryptionSucceed = klass->decrypt(self, ivBuffer, keyIDBuffer, buffer, subSampleCount, subSamplesBuffer);
+
+ // Accessing priv members again.
+ priv->mutex.lock();
+
+ if (!didDecryptionSucceed) {
+ if (priv->isFlushing) {
+ GST_DEBUG_OBJECT(self, "Decryption aborted because of flush");
+ return GST_FLOW_FLUSHING;
+ }
GST_ERROR_OBJECT(self, "Decryption failed");
return GST_FLOW_NOT_SUPPORTED;
}
@@ -256,7 +304,7 @@
static bool isCDMProxyAvailable(WebKitMediaCommonEncryptionDecrypt* self)
{
WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
- auto locker = holdLock(priv->cdmAttachmentMutex);
+ auto locker = holdLock(priv->mutex);
return priv->cdmProxy;
}
@@ -289,11 +337,11 @@
WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
- auto locker = holdLock(priv->cdmAttachmentMutex);
+ auto locker = holdLock(priv->mutex);
GST_ERROR_OBJECT(self, "Attaching CDMProxy %p", proxy);
priv->cdmProxy = proxy;
klass->cdmProxyAttached(self, priv->cdmProxy);
- priv->cdmAttachmentCondition.notifyOne();
+ priv->condition.notifyOne();
}
static gboolean installCDMProxyIfNotAvailable(WebKitMediaCommonEncryptionDecrypt* self)
@@ -318,7 +366,7 @@
static gboolean sinkEventHandler(GstBaseTransform* trans, GstEvent* event)
{
WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(trans);
- gboolean result = FALSE;
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
// FIXME: https://bugs.webkit.org/show_bug.cgi?id=191355
// We should be handling protection events in this class in
@@ -330,18 +378,50 @@
case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: {
ASSERT(gst_event_has_name(event, "attempt-to-decrypt"));
GST_DEBUG_OBJECT(self, "Handling attempt-to-decrypt");
- result = installCDMProxyIfNotAvailable(self);
+ gboolean result = installCDMProxyIfNotAvailable(self);
gst_event_unref(event);
+ return result;
+ }
+ case GST_EVENT_FLUSH_START:
+ GST_DEBUG_OBJECT(self, "Flush-start");
+ {
+ LockHolder locker(priv->mutex);
+ bool isCdmProxyAttached = priv->cdmProxy;
+ priv->isFlushing = true;
+ if (isCdmProxyAttached) {
+ locker.unlockEarly();
+ priv->cdmProxy->abortWaitingForKey();
+ } else
+ priv->condition.notifyOne();
+ }
break;
- }
+ case GST_EVENT_FLUSH_STOP:
+ GST_DEBUG_OBJECT(self, "Flush-stop");
+ {
+ LockHolder locker(priv->mutex);
+ priv->isFlushing = false;
+ }
+ break;
default:
- result = GST_BASE_TRANSFORM_CLASS(parent_class)->sink_event(trans, event);
break;
}
- return result;
+ return GST_BASE_TRANSFORM_CLASS(parent_class)->sink_event(trans, event);
}
+bool webKitMediaCommonEncryptionDecryptIsFlushing(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+ LockHolder locker(priv->mutex);
+ return priv->isFlushing;
+}
+
+WeakPtr<WebCore::CDMProxyDecryptionClient> webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+ return makeWeakPtr(*priv->cdmProxyDecryptionClientImplementation);
+}
+
static GstStateChangeReturn changeState(GstElement* element, GstStateChange transition)
{
WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element);
@@ -350,7 +430,7 @@
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_DEBUG_OBJECT(self, "PAUSED->READY");
- priv->cdmAttachmentCondition.notifyOne();
+ priv->condition.notifyOne();
break;
default:
break;
@@ -371,7 +451,7 @@
if (gst_context_has_context_type(context, "drm-cdm-proxy")) {
const GValue* value = gst_structure_get_value(gst_context_get_structure(context), "cdm-proxy");
- LockHolder locker(priv->cdmAttachmentMutex);
+ LockHolder locker(priv->mutex);
priv->cdmProxy = value ? reinterpret_cast<CDMProxy*>(g_value_get_pointer(value)) : nullptr;
GST_DEBUG_OBJECT(self, "received new CDMInstance %p", priv->cdmProxy.get());
klass->cdmProxyAttached(self, priv->cdmProxy);
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h 2021-04-07 09:53:11 UTC (rev 275599)
@@ -28,6 +28,7 @@
#include <gst/base/gstbasetransform.h>
#include <gst/gst.h>
#include <wtf/RefPtr.h>
+#include <wtf/WeakPtr.h>
G_BEGIN_DECLS
@@ -45,6 +46,9 @@
GType webkit_media_common_encryption_decrypt_get_type(void);
+bool webKitMediaCommonEncryptionDecryptIsFlushing(WebKitMediaCommonEncryptionDecrypt*);
+WeakPtr<WebCore::CDMProxyDecryptionClient> webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient(WebKitMediaCommonEncryptionDecrypt*);
+
struct _WebKitMediaCommonEncryptionDecrypt {
GstBaseTransform parent;
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp (275598 => 275599)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp 2021-04-07 09:30:51 UTC (rev 275598)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp 2021-04-07 09:53:11 UTC (rev 275599)
@@ -156,14 +156,16 @@
return false;
}
- CDMProxyThunder::DecryptionContext context;
+ CDMProxyThunder::DecryptionContext context = { };
context.keyIDBuffer = keyIDBuffer;
context.ivBuffer = ivBuffer;
context.dataBuffer = buffer;
context.numSubsamples = subsampleCount;
context.subsamplesBuffer = subsampleCount ? subsamplesBuffer : nullptr;
+ context.cdmProxyDecryptionClient = webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient(self);
+ bool result = priv->cdmProxy->decrypt(context);
- return priv->cdmProxy->decrypt(context);
+ return result;
}
#endif // ENABLE(ENCRYPTED_MEDIA) && ENABLE(THUNDER) && USE(GSTREAMER)