summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/graphics/gstreamer/eme
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/graphics/gstreamer/eme
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/platform/graphics/gstreamer/eme')
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp260
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h57
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp362
-rw-r--r--Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h64
4 files changed, 743 insertions, 0 deletions
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp
new file mode 100644
index 000000000..dc697089f
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp
@@ -0,0 +1,260 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * 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 "WebKitClearKeyDecryptorGStreamer.h"
+
+#if (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
+
+#include "GRefPtrGStreamer.h"
+#include <gcrypt.h>
+#include <gst/base/gstbytereader.h>
+#include <wtf/RunLoop.h>
+
+#define CLEARKEY_SIZE 16
+
+#define WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptPrivate))
+struct _WebKitMediaClearKeyDecryptPrivate {
+ GRefPtr<GstBuffer> key;
+ gcry_cipher_hd_t handle;
+};
+
+static void webKitMediaClearKeyDecryptorFinalize(GObject*);
+static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent*);
+static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt*);
+static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples);
+static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt*);
+
+GST_DEBUG_CATEGORY_STATIC(webkit_media_clear_key_decrypt_debug_category);
+#define GST_CAT_DEFAULT webkit_media_clear_key_decrypt_debug_category
+
+static GstStaticPadTemplate sinkTemplate = GST_STATIC_PAD_TEMPLATE("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("application/x-cenc, original-media-type=(string)video/x-h264, protection-system=(string)" CLEAR_KEY_PROTECTION_SYSTEM_UUID "; "
+ "application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" CLEAR_KEY_PROTECTION_SYSTEM_UUID));
+
+static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS("video/x-h264; audio/mpeg"));
+
+#define webkit_media_clear_key_decrypt_parent_class parent_class
+G_DEFINE_TYPE(WebKitMediaClearKeyDecrypt, webkit_media_clear_key_decrypt, WEBKIT_TYPE_MEDIA_CENC_DECRYPT);
+
+static void webkit_media_clear_key_decrypt_class_init(WebKitMediaClearKeyDecryptClass* klass)
+{
+ GObjectClass* gobjectClass = G_OBJECT_CLASS(klass);
+ gobjectClass->finalize = webKitMediaClearKeyDecryptorFinalize;
+
+ GstElementClass* elementClass = GST_ELEMENT_CLASS(klass);
+ gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&sinkTemplate));
+ gst_element_class_add_pad_template(elementClass, gst_static_pad_template_get(&srcTemplate));
+
+ gst_element_class_set_static_metadata(elementClass,
+ "Decrypt content encrypted using ISOBMFF ClearKey Common Encryption",
+ GST_ELEMENT_FACTORY_KLASS_DECRYPTOR,
+ "Decrypts media that has been encrypted using ISOBMFF ClearKey Common Encryption.",
+ "Philippe Normand <philn@igalia.com>");
+
+ GST_DEBUG_CATEGORY_INIT(webkit_media_clear_key_decrypt_debug_category,
+ "webkitclearkey", 0, "ClearKey decryptor");
+
+ WebKitMediaCommonEncryptionDecryptClass* cencClass = WEBKIT_MEDIA_CENC_DECRYPT_CLASS(klass);
+ cencClass->protectionSystemId = CLEAR_KEY_PROTECTION_SYSTEM_UUID;
+ cencClass->handleKeyResponse = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorHandleKeyResponse);
+ cencClass->setupCipher = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorSetupCipher);
+ cencClass->decrypt = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorDecrypt);
+ cencClass->releaseCipher = GST_DEBUG_FUNCPTR(webKitMediaClearKeyDecryptorReleaseCipher);
+
+ g_type_class_add_private(klass, sizeof(WebKitMediaClearKeyDecryptPrivate));
+}
+
+static void webkit_media_clear_key_decrypt_init(WebKitMediaClearKeyDecrypt* self)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(self);
+
+ if (!gcry_check_version(GCRYPT_VERSION))
+ GST_ERROR_OBJECT(self, "Libgcrypt failed to initialize");
+
+ // Allocate a pool of 16k secure memory. This make the secure memory
+ // available and also drops privileges where needed.
+ gcry_control(GCRYCTL_INIT_SECMEM, 16384, 0);
+
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+
+ self->priv = priv;
+ new (priv) WebKitMediaClearKeyDecryptPrivate();
+}
+
+static void webKitMediaClearKeyDecryptorFinalize(GObject* object)
+{
+ WebKitMediaClearKeyDecrypt* self = WEBKIT_MEDIA_CK_DECRYPT(object);
+ WebKitMediaClearKeyDecryptPrivate* priv = self->priv;
+
+ priv->~WebKitMediaClearKeyDecryptPrivate();
+
+ GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
+}
+
+static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent* event)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ const GstStructure* structure = gst_event_get_structure(event);
+
+ if (!gst_structure_has_name(structure, "drm-cipher"))
+ return FALSE;
+
+ const GValue* value = gst_structure_get_value(structure, "key");
+ priv->key.clear();
+ priv->key = adoptGRef(gst_buffer_copy(gst_value_get_buffer(value)));
+ return TRUE;
+}
+
+static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ gcry_error_t error;
+
+ ASSERT(priv->key);
+ if (!priv->key) {
+ GST_ERROR_OBJECT(self, "Decryption key not provided");
+ return false;
+ }
+
+ error = gcry_cipher_open(&(priv->handle), GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, GCRY_CIPHER_SECURE);
+ if (error) {
+ GST_ERROR_OBJECT(self, "Failed to create AES 128 CTR cipher handle: %s", gpg_strerror(error));
+ return false;
+ }
+
+ GstMapInfo keyMap;
+ if (!gst_buffer_map(priv->key.get(), &keyMap, GST_MAP_READ)) {
+ GST_ERROR_OBJECT(self, "Failed to map decryption key");
+ return false;
+ }
+
+ ASSERT(keyMap.size == CLEARKEY_SIZE);
+ error = gcry_cipher_setkey(priv->handle, keyMap.data, keyMap.size);
+ gst_buffer_unmap(priv->key.get(), &keyMap);
+ if (error) {
+ GST_ERROR_OBJECT(self, "gcry_cipher_setkey failed: %s", gpg_strerror(error));
+ return false;
+ }
+
+ return true;
+}
+
+static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSampleCount, GstBuffer* subSamplesBuffer)
+{
+ GstMapInfo ivMap;
+ if (!gst_buffer_map(ivBuffer, &ivMap, GST_MAP_READ)) {
+ GST_ERROR_OBJECT(self, "Failed to map IV");
+ return false;
+ }
+
+ uint8_t ctr[CLEARKEY_SIZE];
+ if (ivMap.size == 8) {
+ memset(ctr + 8, 0, 8);
+ memcpy(ctr, ivMap.data, 8);
+ } else {
+ ASSERT(ivMap.size == CLEARKEY_SIZE);
+ memcpy(ctr, ivMap.data, CLEARKEY_SIZE);
+ }
+ gst_buffer_unmap(ivBuffer, &ivMap);
+
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ gcry_error_t error = gcry_cipher_setctr(priv->handle, ctr, CLEARKEY_SIZE);
+ if (error) {
+ GST_ERROR_OBJECT(self, "gcry_cipher_setctr failed: %s", gpg_strerror(error));
+ return false;
+ }
+
+ GstMapInfo map;
+ gboolean bufferMapped = gst_buffer_map(buffer, &map, static_cast<GstMapFlags>(GST_MAP_READWRITE));
+ if (!bufferMapped) {
+ GST_ERROR_OBJECT(self, "Failed to map buffer");
+ return false;
+ }
+
+ GstMapInfo subSamplesMap;
+ gboolean subsamplesBufferMapped = gst_buffer_map(subSamplesBuffer, &subSamplesMap, GST_MAP_READ);
+ if (!subsamplesBufferMapped) {
+ GST_ERROR_OBJECT(self, "Failed to map subsample buffer");
+ gst_buffer_unmap(buffer, &map);
+ return false;
+ }
+
+ GstByteReader* reader = gst_byte_reader_new(subSamplesMap.data, subSamplesMap.size);
+ unsigned position = 0;
+ unsigned sampleIndex = 0;
+
+ GST_DEBUG_OBJECT(self, "position: %d, size: %zu", position, map.size);
+
+ while (position < map.size) {
+ guint16 nBytesClear = 0;
+ guint32 nBytesEncrypted = 0;
+
+ if (sampleIndex < subSampleCount) {
+ if (!gst_byte_reader_get_uint16_be(reader, &nBytesClear)
+ || !gst_byte_reader_get_uint32_be(reader, &nBytesEncrypted)) {
+ GST_DEBUG_OBJECT(self, "unsupported");
+ gst_byte_reader_free(reader);
+ gst_buffer_unmap(buffer, &map);
+ gst_buffer_unmap(subSamplesBuffer, &subSamplesMap);
+ return false;
+ }
+
+ sampleIndex++;
+ } else {
+ nBytesClear = 0;
+ nBytesEncrypted = map.size - position;
+ }
+
+ GST_TRACE_OBJECT(self, "%d bytes clear (todo=%zu)", nBytesClear, map.size - position);
+ position += nBytesClear;
+ if (nBytesEncrypted) {
+ GST_TRACE_OBJECT(self, "%d bytes encrypted (todo=%zu)", nBytesEncrypted, map.size - position);
+ error = gcry_cipher_decrypt(priv->handle, map.data + position, nBytesEncrypted, 0, 0);
+ if (error) {
+ GST_ERROR_OBJECT(self, "decryption failed: %s", gpg_strerror(error));
+ gst_byte_reader_free(reader);
+ gst_buffer_unmap(buffer, &map);
+ gst_buffer_unmap(subSamplesBuffer, &subSamplesMap);
+ return false;
+ }
+ position += nBytesEncrypted;
+ }
+ }
+
+ gst_byte_reader_free(reader);
+ gst_buffer_unmap(buffer, &map);
+ gst_buffer_unmap(subSamplesBuffer, &subSamplesMap);
+ return true;
+}
+
+static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt* self)
+{
+ WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
+ gcry_cipher_close(priv->handle);
+}
+
+#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h
new file mode 100644
index 000000000..30cfa299b
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.h
@@ -0,0 +1,57 @@
+/* GStreamer ClearKey common encryption decryptor
+ *
+ * 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) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
+
+#include "WebKitCommonEncryptionDecryptorGStreamer.h"
+
+#define CLEAR_KEY_PROTECTION_SYSTEM_UUID "58147ec8-0423-4659-92e6-f52c5ce8c3cc"
+#define CLEAR_KEY_PROTECTION_SYSTEM_ID "org.w3.clearkey"
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_MEDIA_CK_DECRYPT (webkit_media_clear_key_decrypt_get_type())
+#define WEBKIT_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecrypt))
+#define WEBKIT_MEDIA_CK_DECRYPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT, WebKitMediaClearKeyDecryptClass))
+#define WEBKIT_IS_MEDIA_CK_DECRYPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_MEDIA_CK_DECRYPT))
+#define WEBKIT_IS_MEDIA_CK_DECRYPT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_MEDIA_CK_DECRYPT))
+
+typedef struct _WebKitMediaClearKeyDecrypt WebKitMediaClearKeyDecrypt;
+typedef struct _WebKitMediaClearKeyDecryptClass WebKitMediaClearKeyDecryptClass;
+typedef struct _WebKitMediaClearKeyDecryptPrivate WebKitMediaClearKeyDecryptPrivate;
+
+GType webkit_media_clear_key_decrypt_get_type(void);
+
+struct _WebKitMediaClearKeyDecrypt {
+ WebKitMediaCommonEncryptionDecrypt parent;
+
+ WebKitMediaClearKeyDecryptPrivate* priv;
+};
+
+struct _WebKitMediaClearKeyDecryptClass {
+ WebKitMediaCommonEncryptionDecryptClass parentClass;
+};
+
+G_END_DECLS
+
+#endif // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)
diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp
new file mode 100644
index 000000000..389808050
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp
@@ -0,0 +1,362 @@
+/* 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) || ENABLE(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, 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 = nullptr;
+
+ gst_event_parse_protection(event, &systemId, nullptr, nullptr);
+ GST_TRACE_OBJECT(self, "received protection event for %s", systemId);
+
+ if (!g_strcmp0(systemId, klass->protectionSystemId)) {
+ GST_DEBUG_OBJECT(self, "sending protection event to the pipeline");
+ gst_element_post_message(GST_ELEMENT(self),
+ gst_message_new_element(GST_OBJECT(self),
+ gst_structure_new("drm-key-needed", "event", GST_TYPE_EVENT, event, nullptr)));
+ }
+
+ gst_event_unref(event);
+ 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) || ENABLE(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
index 000000000..dcae82790
--- /dev/null
+++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h
@@ -0,0 +1,64 @@
+/* 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) || ENABLE(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;
+ 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 // (ENABLE(LEGACY_ENCRYPTED_MEDIA) || ENABLE(ENCRYPTED_MEDIA)) && USE(GSTREAMER)