blob: dc9b5a3a4fac597ccaeade75f3edc1ce1d73718b [file] [log] [blame]
/* 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(ENCRYPTED_MEDIA) && USE(GSTREAMER)
#include "CDMClearKey.h"
#include "CDMProxyClearKey.h"
#include "GStreamerCommon.h"
#include "GStreamerEMEUtilities.h"
#include <gcrypt.h>
#include <gst/base/gstbytereader.h>
#include <wtf/RunLoop.h>
#include <wtf/glib/WTFGType.h>
using namespace WebCore;
#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 {
RefPtr<CDMProxyClearKey> cdmProxy;
};
static const char* protectionSystemId(WebKitMediaCommonEncryptionDecrypt*);
static bool cdmProxyAttached(WebKitMediaCommonEncryptionDecrypt* self, const RefPtr<CDMProxy>&);
static bool decrypt(WebKitMediaCommonEncryptionDecrypt*, GstBuffer* iv, GstBuffer* keyid, GstBuffer* sample, unsigned subSamplesCount, GstBuffer* subSamples);
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)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID "; "
"application/x-cenc, original-media-type=(string)audio/mpeg, protection-system=(string)" WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID";"
"application/x-webm-enc, original-media-type=(string)video/x-vp8;"
"application/x-webm-enc, original-media-type=(string)video/x-vp9;"));
static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS("video/x-h264; audio/mpeg; video/x-vp8; video/x-vp9"));
#define webkit_media_clear_key_decrypt_parent_class parent_class
WEBKIT_DEFINE_TYPE(WebKitMediaClearKeyDecrypt, webkit_media_clear_key_decrypt, WEBKIT_TYPE_MEDIA_CENC_DECRYPT)
static void webkit_media_clear_key_decrypt_class_init(WebKitMediaClearKeyDecryptClass* klass)
{
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 = GST_DEBUG_FUNCPTR(protectionSystemId);
cencClass->cdmProxyAttached = GST_DEBUG_FUNCPTR(cdmProxyAttached);
cencClass->decrypt = GST_DEBUG_FUNCPTR(decrypt);
}
static const char* protectionSystemId(WebKitMediaCommonEncryptionDecrypt*)
{
return GStreamerEMEUtilities::s_ClearKeyUUID;
}
static bool cdmProxyAttached(WebKitMediaCommonEncryptionDecrypt* self, const RefPtr<CDMProxy>& cdmProxy)
{
WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
priv->cdmProxy = reinterpret_cast<CDMProxyClearKey*>(cdmProxy.get());
return priv->cdmProxy;
}
static bool decrypt(WebKitMediaCommonEncryptionDecrypt* self, GstBuffer* ivBuffer, GstBuffer* keyIDBuffer, GstBuffer* buffer, unsigned subsampleCount, GstBuffer* subsamplesBuffer)
{
WebKitMediaClearKeyDecryptPrivate* priv = WEBKIT_MEDIA_CK_DECRYPT_GET_PRIVATE(WEBKIT_MEDIA_CK_DECRYPT(self));
if (!ivBuffer || !keyIDBuffer || !buffer) {
GST_ERROR_OBJECT(self, "invalid decrypt() parameter");
return false;
}
WebCore::GstMappedBuffer mappedIVBuffer(ivBuffer, GST_MAP_READ);
if (!mappedIVBuffer) {
GST_ERROR_OBJECT(self, "failed to map IV buffer");
return false;
}
WebCore::GstMappedBuffer mappedKeyIdBuffer(keyIDBuffer, GST_MAP_READ);
if (!mappedKeyIdBuffer) {
GST_ERROR_OBJECT(self, "Failed to map key id buffer");
return false;
}
WebCore::GstMappedBuffer mappedBuffer(buffer, GST_MAP_READWRITE);
if (!mappedBuffer) {
GST_ERROR_OBJECT(self, "Failed to map buffer");
return false;
}
CDMProxyClearKey::cencDecryptContext context = { };
context.keyID = mappedKeyIdBuffer.data();
context.keyIDSizeInBytes = mappedKeyIdBuffer.size();
context.iv = mappedIVBuffer.data();
context.ivSizeInBytes = mappedIVBuffer.size();
context.encryptedBuffer = mappedBuffer.data();
context.encryptedBufferSizeInBytes = mappedBuffer.size();
context.numSubsamples = subsampleCount;
if (!subsampleCount)
context.subsamplesBuffer = nullptr;
else {
ASSERT(subsamplesBuffer);
WebCore::GstMappedBuffer mappedSubsamplesBuffer(subsamplesBuffer, GST_MAP_READ);
if (!mappedSubsamplesBuffer) {
GST_ERROR_OBJECT(self, "Failed to map subsample buffer");
return false;
}
context.subsamplesBuffer = mappedSubsamplesBuffer.data();
context.subsamplesBufferSizeInBytes = mappedSubsamplesBuffer.size();
}
context.cdmProxyDecryptionClient = webKitMediaCommonEncryptionDecryptGetCDMProxyDecryptionClient(self);
return priv->cdmProxy->cencDecrypt(context);
}
#endif // ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)