[GStreamer][EME] Base class for decryption support
authoreocanha@igalia.com <eocanha@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 26 Oct 2016 08:45:53 +0000 (08:45 +0000)
committereocanha@igalia.com <eocanha@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 26 Oct 2016 08:45:53 +0000 (08:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=162915

Reviewed by Xabier Rodriguez-Calvar.

Add common encryption base class.

This patch is authored by Philippe Normand <philn@igalia.com>.

* platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp: Added.
(webkit_media_common_encryption_decrypt_class_init):
(webkit_media_common_encryption_decrypt_init):
(webKitMediaCommonEncryptionDecryptorFinalize):
(webkitMediaCommonEncryptionDecryptTransformCaps):
(webkitMediaCommonEncryptionDecryptTransformInPlace):
(webkitMediaCommonEncryptionDecryptSinkEventHandler):
(webKitMediaCommonEncryptionDecryptorChangeState):
(webKitMediaCommonEncryptionDecryptDefaultSetupCipher):
(webKitMediaCommonEncryptionDecryptDefaultReleaseCipher):
* platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h: Added.

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

Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp [new file with mode: 0644]
Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h [new file with mode: 0644]

index 9cb07a4..5a2ee38 100644 (file)
@@ -1,5 +1,28 @@
 2016-10-26  Enrique Ocaña González  <eocanha@igalia.com>
 
+        [GStreamer][EME] Base class for decryption support
+        https://bugs.webkit.org/show_bug.cgi?id=162915
+
+        Reviewed by Xabier Rodriguez-Calvar.
+
+        Add common encryption base class.
+
+        This patch is authored by Philippe Normand <philn@igalia.com>.
+
+        * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp: Added.
+        (webkit_media_common_encryption_decrypt_class_init):
+        (webkit_media_common_encryption_decrypt_init):
+        (webKitMediaCommonEncryptionDecryptorFinalize):
+        (webkitMediaCommonEncryptionDecryptTransformCaps):
+        (webkitMediaCommonEncryptionDecryptTransformInPlace):
+        (webkitMediaCommonEncryptionDecryptSinkEventHandler):
+        (webKitMediaCommonEncryptionDecryptorChangeState):
+        (webKitMediaCommonEncryptionDecryptDefaultSetupCipher):
+        (webKitMediaCommonEncryptionDecryptDefaultReleaseCipher):
+        * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h: Added.
+
+2016-10-26  Enrique Ocaña González  <eocanha@igalia.com>
+
         [GStreamer][EME] Utility function to create decryptor
         https://bugs.webkit.org/show_bug.cgi?id=162914
 
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp
new file mode 100644 (file)
index 0000000..b6c6ffa
--- /dev/null
@@ -0,0 +1,374 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * Copyright (C) 2013 YouView TV Ltd. <alex.ashley@youview.com>
+ * Copyright (C) 2016 Metrological
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
+ * Boston, MA 02110-1335, USA.
+ */
+
+#include "config.h"
+#include "WebKitCommonEncryptionDecryptorGStreamer.h"
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) && USE(GSTREAMER)
+
+#include "GRefPtrGStreamer.h"
+#include <wtf/Condition.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))
+struct _WebKitMediaCommonEncryptionDecryptPrivate {
+    GRefPtr<GstEvent> protectionEvent;
+
+    bool keyReceived;
+    Lock mutex;
+    Condition condition;
+};
+
+static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement*, GstStateChange transition);
+static void webKitMediaCommonEncryptionDecryptorFinalize(GObject*);
+static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform*, GstPadDirection, GstCaps*, GstCaps*);
+static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform*, GstBuffer*);
+static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform*, GstEvent*);
+
+static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*);
+static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*);
+
+GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category);
+#define GST_CAT_DEFAULT webkit_media_common_encryption_decrypt_debug_category
+
+#define webkit_media_common_encryption_decrypt_parent_class parent_class
+G_DEFINE_TYPE(WebKitMediaCommonEncryptionDecrypt, webkit_media_common_encryption_decrypt, GST_TYPE_BASE_TRANSFORM);
+
+static void webkit_media_common_encryption_decrypt_class_init(WebKitMediaCommonEncryptionDecryptClass* klass)
+{
+    GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
+    gobjectClass->finalize = webKitMediaCommonEncryptionDecryptorFinalize;
+
+    GST_DEBUG_CATEGORY_INIT(webkit_media_common_encryption_decrypt_debug_category,
+        "webkitcenc", 0, "Common Encryption base class");
+
+    GstElementClass* elementClass = GST_ELEMENT_CLASS(klass);
+    elementClass->change_state = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptorChangeState);
+
+    GstBaseTransformClass* baseTransformClass = GST_BASE_TRANSFORM_CLASS(klass);
+    baseTransformClass->transform_ip = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformInPlace);
+    baseTransformClass->transform_caps = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptTransformCaps);
+    baseTransformClass->transform_ip_on_passthrough = FALSE;
+    baseTransformClass->sink_event = GST_DEBUG_FUNCPTR(webkitMediaCommonEncryptionDecryptSinkEventHandler);
+
+    klass->setupCipher = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptDefaultSetupCipher);
+    klass->releaseCipher = GST_DEBUG_FUNCPTR(webKitMediaCommonEncryptionDecryptDefaultReleaseCipher);
+
+    g_type_class_add_private(klass, sizeof(WebKitMediaCommonEncryptionDecryptPrivate));
+}
+
+static void webkit_media_common_encryption_decrypt_init(WebKitMediaCommonEncryptionDecrypt* self)
+{
+    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+
+    self->priv = priv;
+    new (priv) WebKitMediaCommonEncryptionDecryptPrivate();
+
+    GstBaseTransform* base = GST_BASE_TRANSFORM(self);
+    gst_base_transform_set_in_place(base, TRUE);
+    gst_base_transform_set_passthrough(base, FALSE);
+    gst_base_transform_set_gap_aware(base, FALSE);
+}
+
+static void webKitMediaCommonEncryptionDecryptorFinalize(GObject* object)
+{
+    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(object);
+    WebKitMediaCommonEncryptionDecryptPrivate* priv = self->priv;
+
+    priv->~WebKitMediaCommonEncryptionDecryptPrivate();
+    GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static GstCaps* webkitMediaCommonEncryptionDecryptTransformCaps(GstBaseTransform* base, GstPadDirection direction, GstCaps* caps, GstCaps* filter)
+{
+    if (direction == GST_PAD_UNKNOWN)
+        return nullptr;
+
+    GST_DEBUG_OBJECT(base, "direction: %s, caps: %" GST_PTR_FORMAT " filter: %" GST_PTR_FORMAT, (direction == GST_PAD_SRC) ? "src" : "sink", caps, filter);
+
+    GstCaps* transformedCaps = gst_caps_new_empty();
+    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base);
+    WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+
+    unsigned size = gst_caps_get_size(caps);
+    for (unsigned i = 0; i < size; ++i) {
+        GstStructure* incomingStructure = gst_caps_get_structure(caps, i);
+        GRefPtr<GstStructure> outgoingStructure = nullptr;
+
+        if (direction == GST_PAD_SINK) {
+            if (!gst_structure_has_field(incomingStructure, "original-media-type"))
+                continue;
+
+            outgoingStructure = adoptGRef(gst_structure_copy(incomingStructure));
+            gst_structure_set_name(outgoingStructure.get(), gst_structure_get_string(outgoingStructure.get(), "original-media-type"));
+
+            // Filter out the DRM related fields from the down-stream caps.
+            for (int j = 0; j < gst_structure_n_fields(incomingStructure); ++j) {
+                const gchar* fieldName = gst_structure_nth_field_name(incomingStructure, j);
+
+                if (g_str_has_prefix(fieldName, "protection-system")
+                    || g_str_has_prefix(fieldName, "original-media-type"))
+                    gst_structure_remove_field(outgoingStructure.get(), fieldName);
+            }
+        } else {
+            outgoingStructure = adoptGRef(gst_structure_copy(incomingStructure));
+            // Filter out the video related fields from the up-stream caps,
+            // because they are not relevant to the input caps of this element and
+            // can cause caps negotiation failures with adaptive bitrate streams.
+            for (int index = gst_structure_n_fields(outgoingStructure.get()) - 1; index >= 0; --index) {
+                const gchar* fieldName = gst_structure_nth_field_name(outgoingStructure.get(), index);
+                GST_TRACE("Check field \"%s\" for removal", fieldName);
+
+                if (!g_strcmp0(fieldName, "base-profile")
+                    || !g_strcmp0(fieldName, "codec_data")
+                    || !g_strcmp0(fieldName, "height")
+                    || !g_strcmp0(fieldName, "framerate")
+                    || !g_strcmp0(fieldName, "level")
+                    || !g_strcmp0(fieldName, "pixel-aspect-ratio")
+                    || !g_strcmp0(fieldName, "profile")
+                    || !g_strcmp0(fieldName, "rate")
+                    || !g_strcmp0(fieldName, "width")) {
+                    gst_structure_remove_field(outgoingStructure.get(), fieldName);
+                    GST_TRACE("Removing field %s", fieldName);
+                }
+            }
+
+            gst_structure_set(outgoingStructure.get(), "protection-system", G_TYPE_STRING, klass->protectionSystemId,
+                "original-media-type", G_TYPE_STRING, gst_structure_get_name(incomingStructure), nullptr);
+
+            gst_structure_set_name(outgoingStructure.get(), "application/x-cenc");
+        }
+
+        bool duplicate = false;
+        unsigned size = gst_caps_get_size(transformedCaps);
+
+        for (unsigned index = 0; !duplicate && index < size; ++index) {
+            GstStructure* structure = gst_caps_get_structure(transformedCaps, index);
+            if (gst_structure_is_equal(structure, outgoingStructure.get()))
+                duplicate = true;
+        }
+
+        if (!duplicate)
+            gst_caps_append_structure(transformedCaps, outgoingStructure.leakRef());
+    }
+
+    if (filter) {
+        GstCaps* intersection;
+
+        GST_DEBUG_OBJECT(base, "Using filter caps %" GST_PTR_FORMAT, filter);
+        intersection = gst_caps_intersect_full(transformedCaps, filter, GST_CAPS_INTERSECT_FIRST);
+        gst_caps_unref(transformedCaps);
+        transformedCaps = intersection;
+    }
+
+    GST_DEBUG_OBJECT(base, "returning %" GST_PTR_FORMAT, transformedCaps);
+    return transformedCaps;
+}
+
+static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform* base, GstBuffer* buffer)
+{
+    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(base);
+    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+    LockHolder locker(priv->mutex);
+
+    // The key might not have been received yet. Wait for it.
+    if (!priv->keyReceived) {
+        GST_DEBUG_OBJECT(self, "key not available yet, waiting for it");
+        if (GST_STATE(GST_ELEMENT(self)) < GST_STATE_PAUSED || (GST_STATE_TARGET(GST_ELEMENT(self)) != GST_STATE_VOID_PENDING && GST_STATE_TARGET(GST_ELEMENT(self)) < GST_STATE_PAUSED)) {
+            GST_ERROR_OBJECT(self, "can't process key requests in less than PAUSED state");
+            return GST_FLOW_NOT_SUPPORTED;
+        }
+        priv->condition.waitFor(priv->mutex, std::chrono::seconds(5), [priv] {
+            return priv->keyReceived;
+        });
+        if (!priv->keyReceived) {
+            GST_ERROR_OBJECT(self, "key not available");
+            return GST_FLOW_NOT_SUPPORTED;
+        }
+        GST_DEBUG_OBJECT(self, "key received, continuing");
+    }
+
+    GstProtectionMeta* protectionMeta = reinterpret_cast<GstProtectionMeta*>(gst_buffer_get_protection_meta(buffer));
+    if (!protectionMeta) {
+        GST_ERROR_OBJECT(self, "Failed to get GstProtection metadata from buffer %p", buffer);
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    unsigned ivSize;
+    if (!gst_structure_get_uint(protectionMeta->info, "iv_size", &ivSize)) {
+        GST_ERROR_OBJECT(self, "Failed to get iv_size");
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    gboolean encrypted;
+    if (!gst_structure_get_boolean(protectionMeta->info, "encrypted", &encrypted)) {
+        GST_ERROR_OBJECT(self, "Failed to get encrypted flag");
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    if (!ivSize || !encrypted) {
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_OK;
+    }
+
+    GST_DEBUG_OBJECT(base, "protection meta: %" GST_PTR_FORMAT, protectionMeta->info);
+
+    unsigned subSampleCount;
+    if (!gst_structure_get_uint(protectionMeta->info, "subsample_count", &subSampleCount)) {
+        GST_ERROR_OBJECT(self, "Failed to get subsample_count");
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    const GValue* value;
+    GstBuffer* subSamplesBuffer = nullptr;
+    if (subSampleCount) {
+        value = gst_structure_get_value(protectionMeta->info, "subsamples");
+        if (!value) {
+            GST_ERROR_OBJECT(self, "Failed to get subsamples");
+            gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+            return GST_FLOW_NOT_SUPPORTED;
+        }
+        subSamplesBuffer = gst_value_get_buffer(value);
+    }
+
+    WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+    if (!klass->setupCipher(self)) {
+        GST_ERROR_OBJECT(self, "Failed to configure cipher");
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    value = gst_structure_get_value(protectionMeta->info, "iv");
+    if (!value) {
+        GST_ERROR_OBJECT(self, "Failed to get IV for sample");
+        klass->releaseCipher(self);
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    GstBuffer* ivBuffer = gst_value_get_buffer(value);
+    GST_TRACE_OBJECT(self, "decrypting");
+    if (!klass->decrypt(self, ivBuffer, buffer, subSampleCount, subSamplesBuffer)) {
+        GST_ERROR_OBJECT(self, "Decryption failed");
+        klass->releaseCipher(self);
+        gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+        return GST_FLOW_NOT_SUPPORTED;
+    }
+
+    klass->releaseCipher(self);
+    gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+    return GST_FLOW_OK;
+}
+
+
+static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform* trans, GstEvent* event)
+{
+    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(trans);
+    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+    WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+    gboolean result = FALSE;
+
+    switch (GST_EVENT_TYPE(event)) {
+    case GST_EVENT_PROTECTION: {
+        const char* systemId;
+        const char* origin;
+        GstBuffer* initDataBuffer;
+
+        GST_DEBUG_OBJECT(self, "received protection event");
+        gst_event_parse_protection(event, &systemId, &initDataBuffer, &origin);
+        GST_DEBUG_OBJECT(self, "systemId: %s", systemId);
+
+        if (!g_str_equal(systemId, klass->protectionSystemId)) {
+            gst_event_unref(event);
+            result = TRUE;
+            break;
+        }
+
+        // Keep the event ref around so that the parsed event data
+        // remains valid until the drm-key-need message has been sent.
+        priv->protectionEvent = adoptGRef(event);
+        RunLoop::main().dispatch([self, initDataBuffer] {
+            WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
+            if (klass) {
+                klass->requestDecryptionKey(self, initDataBuffer);
+                self->priv->protectionEvent = nullptr;
+            }});
+
+        result = TRUE;
+        break;
+    }
+    case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: {
+        if (klass->handleKeyResponse(self, event)) {
+            GST_DEBUG_OBJECT(self, "key received");
+            priv->keyReceived = true;
+            priv->condition.notifyOne();
+        }
+
+        gst_event_unref(event);
+        result = TRUE;
+        break;
+    }
+    default:
+        result = GST_BASE_TRANSFORM_CLASS(parent_class)->sink_event(trans, event);
+        break;
+    }
+
+    return result;
+}
+
+static GstStateChangeReturn webKitMediaCommonEncryptionDecryptorChangeState(GstElement* element, GstStateChange transition)
+{
+    WebKitMediaCommonEncryptionDecrypt* self = WEBKIT_MEDIA_CENC_DECRYPT(element);
+    WebKitMediaCommonEncryptionDecryptPrivate* priv = WEBKIT_MEDIA_CENC_DECRYPT_GET_PRIVATE(self);
+
+    switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+        GST_DEBUG_OBJECT(self, "PAUSED->READY");
+        priv->condition.notifyOne();
+        break;
+    default:
+        break;
+    }
+
+    GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
+
+    // Add post-transition code here.
+
+    return result;
+}
+
+
+static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*)
+{
+    return true;
+}
+
+
+static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*)
+{
+}
+
+
+#endif // ENABLE(LEGACY_ENCRYPTED_MEDIA) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h
new file mode 100644 (file)
index 0000000..4cb87d7
--- /dev/null
@@ -0,0 +1,65 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * Copyright (C) 2013 YouView TV Ltd. <alex.ashley@youview.com>
+ * Copyright (C) 2016 Metrological
+ * Copyright (C) 2016 Igalia S.L
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#if ENABLE(LEGACY_ENCRYPTED_MEDIA) && USE(GSTREAMER)
+
+#include <gst/base/gstbasetransform.h>
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_MEDIA_CENC_DECRYPT          (webkit_media_common_encryption_decrypt_get_type())
+#define WEBKIT_MEDIA_CENC_DECRYPT(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecrypt))
+#define WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptClass))
+#define WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT, WebKitMediaCommonEncryptionDecryptClass))
+
+#define WEBKIT_IS_MEDIA_CENC_DECRYPT(obj)       (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CENC_DECRYPT))
+#define WEBKIT_IS_MEDIA_CENC_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CENC_DECRYPT))
+
+typedef struct _WebKitMediaCommonEncryptionDecrypt        WebKitMediaCommonEncryptionDecrypt;
+typedef struct _WebKitMediaCommonEncryptionDecryptClass   WebKitMediaCommonEncryptionDecryptClass;
+typedef struct _WebKitMediaCommonEncryptionDecryptPrivate WebKitMediaCommonEncryptionDecryptPrivate;
+
+GType webkit_media_common_encryption_decrypt_get_type(void);
+
+struct _WebKitMediaCommonEncryptionDecrypt {
+    GstBaseTransform parent;
+
+    WebKitMediaCommonEncryptionDecryptPrivate* priv;
+};
+
+struct _WebKitMediaCommonEncryptionDecryptClass {
+    GstBaseTransformClass parentClass;
+
+    const char* protectionSystemId;
+    void (*requestDecryptionKey)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* initData);
+    gboolean (*handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, GstEvent* event);
+    gboolean (*setupCipher)(WebKitMediaCommonEncryptionDecrypt*);
+    gboolean (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSamplesCount, GstBuffer* subSamplesBuffer);
+    void (*releaseCipher)(WebKitMediaCommonEncryptionDecrypt*);
+};
+
+G_END_DECLS
+
+#endif