[GStreamer][EME] waitingforkey event should consider decryptors' waiting status
authorcalvaris@igalia.com <calvaris@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Nov 2018 12:40:12 +0000 (12:40 +0000)
committercalvaris@igalia.com <calvaris@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 Nov 2018 12:40:12 +0000 (12:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191459

Reviewed by Carlos Garcia Campos.

The new cross platform architecture to report waitingforkey and
recover from it requires a more accurate knowledge of what is
going on with the decryptors because events are reported only once
(per key exchange run) and crossplatform only continues if we are
actually ready to continue, meaning that no decryptors are
waiting.

* platform/graphics/gstreamer/GUniquePtrGStreamer.h: Added
GstIterator deleter.
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp:
(WebCore::MediaPlayerPrivateGStreamerBase::setWaitingForKey): Bail
out if we are requested to not wait anymore but there are still
waiting decryptors.
(WebCore::MediaPlayerPrivateGStreamerBase::waitingForKey const):
Query the pipeline, just a query after pipeline is built and
manual inspection during build. The query is optimal but sometimes
we can get this request when the pipeline is under construction so
queries do not arrive at the decryptors and we have to deliver it
by ourselves.
(WebCore::MediaPlayerPrivateGStreamerBase::reportWaitingForKey:
* platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h:
(WebCore::MediaPlayerPrivateGStreamerBase::reportWaitingForKey:
Deleted because it is now inlined.
* platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:
(webKitMediaClearKeyDecryptorDecrypt): Fixed small compiler warning.
* platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
(webkit_media_common_encryption_decrypt_class_init): Override
query method.
(webkitMediaCommonEncryptionDecryptTransformInPlace): When the
decryptor is going to block to wait, report before. When the
decryptor receives the key, report it got it.
(webkitMediaCommonEncryptionDecryptSinkEventHandler): Do not
handle waitingforkey here.
(webkitMediaCommonEncryptionDecryptorQueryHandler): Report if the
decryptor is waiting.

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/gstreamer/GUniquePtrGStreamer.h
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp
Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h
Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp
Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp

index 91fee8f..f505136 100644 (file)
@@ -1,3 +1,46 @@
+2018-11-12  Xabier Rodriguez Calvar  <calvaris@igalia.com>
+
+        [GStreamer][EME] waitingforkey event should consider decryptors' waiting status
+        https://bugs.webkit.org/show_bug.cgi?id=191459
+
+        Reviewed by Carlos Garcia Campos.
+
+        The new cross platform architecture to report waitingforkey and
+        recover from it requires a more accurate knowledge of what is
+        going on with the decryptors because events are reported only once
+        (per key exchange run) and crossplatform only continues if we are
+        actually ready to continue, meaning that no decryptors are
+        waiting.
+
+        * platform/graphics/gstreamer/GUniquePtrGStreamer.h: Added
+        GstIterator deleter.
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp:
+        (WebCore::MediaPlayerPrivateGStreamerBase::setWaitingForKey): Bail
+        out if we are requested to not wait anymore but there are still
+        waiting decryptors.
+        (WebCore::MediaPlayerPrivateGStreamerBase::waitingForKey const):
+        Query the pipeline, just a query after pipeline is built and
+        manual inspection during build. The query is optimal but sometimes
+        we can get this request when the pipeline is under construction so
+        queries do not arrive at the decryptors and we have to deliver it
+        by ourselves.
+        (WebCore::MediaPlayerPrivateGStreamerBase::reportWaitingForKey:
+        * platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.h:
+        (WebCore::MediaPlayerPrivateGStreamerBase::reportWaitingForKey:
+        Deleted because it is now inlined.
+        * platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:
+        (webKitMediaClearKeyDecryptorDecrypt): Fixed small compiler warning.
+        * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
+        (webkit_media_common_encryption_decrypt_class_init): Override
+        query method.
+        (webkitMediaCommonEncryptionDecryptTransformInPlace): When the
+        decryptor is going to block to wait, report before. When the
+        decryptor receives the key, report it got it.
+        (webkitMediaCommonEncryptionDecryptSinkEventHandler): Do not
+        handle waitingforkey here.
+        (webkitMediaCommonEncryptionDecryptorQueryHandler): Report if the
+        decryptor is waiting.
+
 2018-11-12  Michael Catanzaro  <mcatanzaro@igalia.com>
 
         [GTK] Silence ATK_XY_PARENT warnings
index 1ab80b7..1858a16 100644 (file)
@@ -32,6 +32,7 @@ namespace WTF {
 
 WTF_DEFINE_GPTR_DELETER(GstStructure, gst_structure_free)
 WTF_DEFINE_GPTR_DELETER(GstInstallPluginsContext, gst_install_plugins_context_free)
+WTF_DEFINE_GPTR_DELETER(GstIterator, gst_iterator_free)
 WTF_DEFINE_GPTR_DELETER(GstSegment, gst_segment_free)
 WTF_DEFINE_GPTR_DELETER(GstFlowCombiner, gst_flow_combiner_free)
 WTF_DEFINE_GPTR_DELETER(GstByteReader, gst_byte_reader_free)
index 07d2977..dc4642c 100644 (file)
@@ -50,6 +50,7 @@
 #include "CDMInstance.h"
 #include "GStreamerEMEUtilities.h"
 #include "SharedBuffer.h"
+#include "WebKitCommonEncryptionDecryptorGStreamer.h"
 #endif
 
 #if USE(GSTREAMER_GL)
@@ -1335,24 +1336,51 @@ void MediaPlayerPrivateGStreamerBase::handleProtectionEvent(GstEvent* event)
     initializationDataEncountered(event);
 }
 
-void MediaPlayerPrivateGStreamerBase::reportWaitingForKey()
-{
-    GST_TRACE("waiting for key");
-    m_player->waitingForKeyChanged();
-}
-
 void MediaPlayerPrivateGStreamerBase::setWaitingForKey(bool waitingForKey)
 {
-    if (waitingForKey == m_waitingForKey)
+    // We bail out if values did not change or if we are requested to not wait anymore but there are still waiting decryptors.
+    GST_TRACE("waitingForKey %s, m_waitingForKey %s", boolForPrinting(waitingForKey), boolForPrinting(m_waitingForKey));
+    if (waitingForKey == m_waitingForKey || (!waitingForKey && this->waitingForKey()))
         return;
 
     m_waitingForKey = waitingForKey;
-    reportWaitingForKey();
+    GST_DEBUG("waiting for key changed %s", boolForPrinting(m_waitingForKey));
+    m_player->waitingForKeyChanged();
 }
 
 bool MediaPlayerPrivateGStreamerBase::waitingForKey() const
 {
-    return m_waitingForKey;
+    if (!m_pipeline)
+        return false;
+
+    GstState state;
+    gst_element_get_state(m_pipeline.get(), &state, nullptr, 0);
+
+    bool result = false;
+    GRefPtr<GstQuery> query = adoptGRef(gst_query_new_custom(GST_QUERY_CUSTOM, gst_structure_new_empty("any-decryptor-waiting-for-key")));
+    if (state >= GST_STATE_PAUSED) {
+        result = gst_element_query(m_pipeline.get(), query.get());
+        GST_TRACE("query result %s, on %s", boolForPrinting(result), gst_element_state_get_name(state));
+    } else if (state >= GST_STATE_READY) {
+        // Running a query in the pipeline is easier but it only works when the pipeline is set up and running, otherwise we need to inspect it and ask the decryptors directly.
+        GUniquePtr<GstIterator> iterator(gst_bin_iterate_recurse(GST_BIN(m_pipeline.get())));
+        GstIteratorResult iteratorResult;
+        do {
+            iteratorResult = gst_iterator_fold(iterator.get(), [](const GValue *item, GValue *, gpointer data) -> gboolean {
+                GstElement* element = GST_ELEMENT(g_value_get_object(item));
+                GstQuery* query = GST_QUERY(data);
+                return !WEBKIT_IS_MEDIA_CENC_DECRYPT(element) || !gst_element_query(element, query);
+            }, nullptr, query.get());
+            if (iteratorResult == GST_ITERATOR_RESYNC)
+                gst_iterator_resync(iterator.get());
+        } while (iteratorResult == GST_ITERATOR_RESYNC);
+        if (iteratorResult == GST_ITERATOR_ERROR)
+            GST_WARNING("iterator returned an error");
+        result = iteratorResult == GST_ITERATOR_OK;
+        GST_TRACE("iterator result %d, waiting %s", iteratorResult, boolForPrinting(result));
+    }
+
+    return result;
 }
 #endif
 
index 7267af1..4ca2c9d 100644 (file)
@@ -154,8 +154,7 @@ public:
     void dispatchCDMInstance();
     void initializationDataEncountered(GstEvent*);
     void setWaitingForKey(bool);
-    bool waitingForKey() const;
-    void reportWaitingForKey();
+    bool waitingForKey() const override;
 #endif
 
     static bool supportsKeySystem(const String& keySystem, const String& mimeType);
index 33f6089..f941e52 100644 (file)
@@ -283,7 +283,7 @@ static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionD
         GST_TRACE_OBJECT(self, "subsample index %u - %hu bytes clear (todo=%zu)", sampleIndex, nBytesClear, mappedBuffer.size() - position);
         position += nBytesClear;
         if (nBytesEncrypted) {
-            GST_TRACE_OBJECT(self, "subsample index %u - %hu bytes encrypted (todo=%zu)", sampleIndex, nBytesEncrypted, mappedBuffer.size() - position);
+            GST_TRACE_OBJECT(self, "subsample index %u - %u bytes encrypted (todo=%zu)", sampleIndex, nBytesEncrypted, mappedBuffer.size() - position);
             cipherError = gcry_cipher_decrypt(priv->handle, mappedBuffer.data() + position, nBytesEncrypted, 0, 0);
             if (cipherError) {
                 GST_ERROR_OBJECT(self, "sub sample index %u decryption failed: %s", sampleIndex, gpg_strerror(cipherError));
index dd2619f..bbea1ea 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "GStreamerCommon.h"
 #include <wtf/Condition.h>
+#include <wtf/PrintStream.h>
 #include <wtf/RunLoop.h>
 
 #define WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptPrivate))
@@ -44,6 +45,7 @@ static void webKitMediaCommonEncryptionDecryptorFinalize(GObject*);
 static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform*, GstPadDirection, GstCaps*, GstCaps*);
 static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform*, GstBuffer*);
 static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform*, GstEvent*);
+static gboolean webkitMediaCommonEncryptionDecryptorQueryHandler(GstBaseTransform*, GstPadDirection, GstQuery*);
 
 
 GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category);
@@ -68,6 +70,7 @@ static void webkit_media_common_encryption_decrypt_class_init(WebKitMediaCommonE
     baseTransformClass->transform_caps = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformCaps);
     baseTransformClass->transform_ip_on_passthrough = FALSE;
     baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptSinkEventHandler);
+    baseTransformClass->query = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptorQueryHandler);
 
     g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate));
 }
@@ -215,6 +218,8 @@ static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseT
             return GST_FLOW_NOT_SUPPORTED;
         }
         GST_DEBUG_OBJECT(self, "key received, continuing");
+        priv->waitingForKey = false;
+        gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-key-received")));
     }
 
     GstProtectionMeta* protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer));
@@ -306,12 +311,7 @@ static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransf
         if (klass->handleKeyResponse(self, event)) {
             GST_DEBUG_OBJECT(self, "key received");
             priv->keyReceived = true;
-            priv->waitingForKey = false;
-            gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-key-received")));
             priv->condition.notifyOne();
-        } else if (priv->waitingForKey) {
-            GST_DEBUG_OBJECT(self, "still waiting for key, reposting");
-            gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-waiting-for-key")));
         }
 
         gst_event_unref(event);
@@ -326,6 +326,16 @@ static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransf
     return result;
 }
 
+static gboolean webkitMediaCommonEncryptionDecryptorQueryHandler(GstBaseTransform* trans, GstPadDirection direction, GstQuery* query)
+{
+    if (gst_structure_has_name(gst_query_get_structure(query), "any-decryptor-waiting-for-key")) {
+        WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(trans);
+        GST_TRACE_OBJECT(trans, "any-decryptor-waiting-for-key query, waitingforkey %s", boolForPrinting(priv->waitingForKey));
+        return priv->waitingForKey;
+    }
+    return GST_BASE_TRANSFORM_CLASS(parent_class)->query(trans, direction, query);
+}
+
 static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement* element, GstStateChange transition)
 {
     WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element);