Modified: trunk/Source/WebCore/ChangeLog (219945 => 219946)
--- trunk/Source/WebCore/ChangeLog 2017-07-26 14:31:13 UTC (rev 219945)
+++ trunk/Source/WebCore/ChangeLog 2017-07-26 14:33:10 UTC (rev 219946)
@@ -1,5 +1,36 @@
2017-07-26 Zan Dobersek <[email protected]>
+ [EME][GStreamer] Multi-key support in the GStreamer ClearKey decryptor
+ https://bugs.webkit.org/show_bug.cgi?id=174779
+
+ Reviewed by Xabier Rodriguez-Calvar.
+
+ In the CENC decryptor, the key ID value is retrieved from the info structure
+ on the GstProtectionMeta object. GstBuffer for that value is retrieved and
+ passed to the setupCipher() function.
+
+ In the ClearKey decryptor (which extends the CENC decryptor), the single
+ GstBuffer object on the private instance that holds the key value is replaced
+ with a Vector object that holds pairs of key ID and value GstBuffers. In the
+ handleKeyResponse() implementation that Vector is emptied and then refilled
+ with key ID and value pairs that are passed in through the drm-cipher-clearkey
+ structure that's attached to the GstEvent that signalled new key information.
+
+ In the ClearKey decryptor's setupCipher() implementation the passed-in key ID
+ buffer is used to find a matching key ID and value pair stored on the private
+ instance. If not found, an error is thrown. If found, the matching key value
+ is used for decryption.
+
+ * platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp:
+ (webKitMediaClearKeyDecryptorHandleKeyResponse):
+ (webKitMediaClearKeyDecryptorSetupCipher):
+ * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp:
+ (webkitMediaCommonEncryptionDecryptTransformInPlace):
+ (webKitMediaCommonEncryptionDecryptDefaultSetupCipher):
+ * platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h:
+
+2017-07-26 Zan Dobersek <[email protected]>
+
[EME][GStreamer] Handle ClearKey as a supported key system
https://bugs.webkit.org/show_bug.cgi?id=174778
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp (219945 => 219946)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp 2017-07-26 14:31:13 UTC (rev 219945)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitClearKeyDecryptorGStreamer.cpp 2017-07-26 14:33:10 UTC (rev 219946)
@@ -31,15 +31,20 @@
#define CLEARKEY_SIZE 16
+struct Key {
+ GRefPtr<GstBuffer> keyID;
+ GRefPtr<GstBuffer> keyValue;
+};
+
#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;
+ Vector<Key> keys;
gcry_cipher_hd_t handle;
};
static void webKitMediaClearKeyDecryptorFinalize(GObject*);
static gboolean webKitMediaClearKeyDecryptorHandleKeyResponse(WebKitMediaCommonEncryptionDecrypt* self, GstEvent*);
-static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt*);
+static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt*, GstBuffer*);
static gboolean webKitMediaClearKeyDecryptorDecrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples);
static void webKitMediaClearKeyDecryptorReleaseCipher(WebKitMediaCommonEncryptionDecrypt*);
@@ -120,23 +125,62 @@
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"))
+ // Demand the `drm-cipher-clearkey` GstStructure.
+ if (!gst_structure_has_name(structure, "drm-cipher-clearkey"))
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)));
+ // Retrieve the `key-ids` GStreamer value list.
+ const GValue* keyIDsList = gst_structure_get_value(structure, "key-ids");
+ ASSERT(keyIDsList && GST_VALUE_HOLDS_LIST(keyIDsList));
+ unsigned keyIDsListSize = gst_value_list_get_size(keyIDsList);
+
+ // Retrieve the `key-values` GStreamer value list.
+ const GValue* keyValuesList = gst_structure_get_value(structure, "key-values");
+ ASSERT(keyValuesList && GST_VALUE_HOLDS_LIST(keyValuesList));
+ unsigned keyValuesListSize = gst_value_list_get_size(keyValuesList);
+
+ // Bail if somehow the two lists don't match in size.
+ if (keyIDsListSize != keyValuesListSize)
+ return FALSE;
+
+ // Clear out the previous list of keys.
+ priv->keys.clear();
+
+ // Append the retrieved GstBuffer objects containing each key's ID and value to the list of Key objects.
+ for (unsigned i = 0; i < keyIDsListSize; ++i) {
+ GRefPtr<GstBuffer> keyIDBuffer(gst_value_get_buffer(gst_value_list_get_value(keyIDsList, i)));
+ GRefPtr<GstBuffer> keyValueBuffer(gst_value_get_buffer(gst_value_list_get_value(keyValuesList, i)));
+ priv->keys.append(Key { WTFMove(keyIDBuffer), WTFMove(keyValueBuffer) });
+ }
+
return TRUE;
}
-static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt* self)
+static gboolean webKitMediaClearKeyDecryptorSetupCipher(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* keyIDBuffer)
{
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");
+ GRefPtr<GstBuffer> keyBuffer;
+ {
+ GstMapInfo keyIDBufferMap;
+ if (!gst_buffer_map(keyIDBuffer, &keyIDBufferMap, GST_MAP_READ)) {
+ GST_ERROR_OBJECT(self, "Failed to map key ID buffer");
+ return false;
+ }
+
+ for (auto& key : priv->keys) {
+ if (!gst_buffer_memcmp(key.keyID.get(), 0, keyIDBufferMap.data, keyIDBufferMap.size)) {
+ keyBuffer = key.keyValue;
+ break;
+ }
+ }
+
+ gst_buffer_unmap(keyIDBuffer, &keyIDBufferMap);
+ }
+
+ if (!keyBuffer) {
+ GST_ERROR_OBJECT(self, "Failed to find an appropriate key buffer");
return false;
}
@@ -147,7 +191,7 @@
}
GstMapInfo keyMap;
- if (!gst_buffer_map(priv->key.get(), &keyMap, GST_MAP_READ)) {
+ if (!gst_buffer_map(keyBuffer.get(), &keyMap, GST_MAP_READ)) {
GST_ERROR_OBJECT(self, "Failed to map decryption key");
return false;
}
@@ -154,7 +198,7 @@
ASSERT(keyMap.size == CLEARKEY_SIZE);
error = gcry_cipher_setkey(priv->handle, keyMap.data, keyMap.size);
- gst_buffer_unmap(priv->key.get(), &keyMap);
+ gst_buffer_unmap(keyBuffer.get(), &keyMap);
if (error) {
GST_ERROR_OBJECT(self, "gcry_cipher_setkey failed: %s", gpg_strerror(error));
return false;
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp (219945 => 219946)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp 2017-07-26 14:31:13 UTC (rev 219945)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp 2017-07-26 14:33:10 UTC (rev 219946)
@@ -44,7 +44,7 @@
static GstFlowReturn webkitMediaCommonEncryptionDecryptTransformInPlace(GstBaseTransform*, GstBuffer*);
static gboolean webkitMediaCommonEncryptionDecryptSinkEventHandler(GstBaseTransform*, GstEvent*);
-static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*);
+static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*, GstBuffer*);
static void webKitMediaCommonEncryptionDecryptDefaultReleaseCipher(WebKitMediaCommonEncryptionDecrypt*);
GST_DEBUG_CATEGORY_STATIC(webkit_media_common_encryption_decrypt_debug_category);
@@ -253,8 +253,16 @@
subSamplesBuffer = gst_value_get_buffer(value);
}
+ value = gst_structure_get_value(protectionMeta->info, "kid");
+ if (!value) {
+ GST_ERROR_OBJECT(self, "Failed to get key ID for sample");
+ gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
+ return GST_FLOW_NOT_SUPPORTED;
+ }
+
+ GstBuffer* keyIDBuffer = gst_value_get_buffer(value);
WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
- if (!klass->setupCipher(self)) {
+ if (!klass->setupCipher(self, keyIDBuffer)) {
GST_ERROR_OBJECT(self, "Failed to configure cipher");
gst_buffer_remove_meta(buffer, reinterpret_cast<GstMeta*>(protectionMeta));
return GST_FLOW_NOT_SUPPORTED;
@@ -349,7 +357,7 @@
}
-static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*)
+static gboolean webKitMediaCommonEncryptionDecryptDefaultSetupCipher(WebKitMediaCommonEncryptionDecrypt*, GstBuffer*)
{
return true;
}
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h (219945 => 219946)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h 2017-07-26 14:31:13 UTC (rev 219945)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.h 2017-07-26 14:33:10 UTC (rev 219946)
@@ -54,7 +54,7 @@
const char* protectionSystemId;
gboolean (*handleKeyResponse)(WebKitMediaCommonEncryptionDecrypt*, GstEvent* event);
- gboolean (*setupCipher)(WebKitMediaCommonEncryptionDecrypt*);
+ gboolean (*setupCipher)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer*);
gboolean (*decrypt)(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* ivBuffer, GstBuffer* buffer, unsigned subSamplesCount, GstBuffer* subSamplesBuffer);
void (*releaseCipher)(WebKitMediaCommonEncryptionDecrypt*);
};