Modified: trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp (238130 => 238131)
--- trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp 2018-11-13 14:35:23 UTC (rev 238130)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp 2018-11-13 14:45:31 UTC (rev 238131)
@@ -316,32 +316,6 @@
}, this, nullptr);
}
-#if ENABLE(ENCRYPTED_MEDIA)
-static std::pair<Vector<GRefPtr<GstEvent>>, Vector<String>> extractEventsAndSystemsFromMessage(GstMessage* message)
-{
- const GstStructure* structure = gst_message_get_structure(message);
-
- const GValue* streamEncryptionEventsList = gst_structure_get_value(structure, "stream-encryption-events");
- ASSERT(streamEncryptionEventsList && GST_VALUE_HOLDS_LIST(streamEncryptionEventsList));
- unsigned streamEncryptionEventsListSize = gst_value_list_get_size(streamEncryptionEventsList);
- Vector<GRefPtr<GstEvent>> streamEncryptionEventsVector;
-
- unsigned i;
- for (i = 0; i < streamEncryptionEventsListSize; ++i)
- streamEncryptionEventsVector.append(GRefPtr<GstEvent>(static_cast<GstEvent*>(g_value_get_boxed(gst_value_list_get_value(streamEncryptionEventsList, i)))));
-
- Vector<String> streamEncryptionAllowedSystemsVector;
- const GValue* streamEncryptionAllowedSystemsValue = gst_structure_get_value(structure, "available-stream-encryption-systems");
- const char** streamEncryptionAllowedSystems = reinterpret_cast<const char**>(g_value_get_boxed(streamEncryptionAllowedSystemsValue));
- if (streamEncryptionAllowedSystems) {
- for (i = 0; streamEncryptionAllowedSystems[i]; ++i)
- streamEncryptionAllowedSystemsVector.append(streamEncryptionAllowedSystems[i]);
- }
-
- return std::make_pair(streamEncryptionEventsVector, streamEncryptionAllowedSystemsVector);
-}
-#endif
-
bool MediaPlayerPrivateGStreamerBase::handleSyncMessage(GstMessage* message)
{
UNUSED_PARAM(message);
@@ -369,56 +343,29 @@
}
GST_DEBUG_OBJECT(pipeline(), "handling drm-preferred-decryption-system-id need context message");
LockHolder lock(m_protectionMutex);
- std::pair<Vector<GRefPtr<GstEvent>>, Vector<String>> streamEncryptionInformation = extractEventsAndSystemsFromMessage(message);
- GST_TRACE("found %" G_GSIZE_FORMAT " protection events", streamEncryptionInformation.first.size());
- Vector<uint8_t> concatenatedInitDataChunks;
- unsigned concatenatedInitDataChunksNumber = 0;
- String eventKeySystemIdString;
+ ProtectionSystemEvents protectionSystemEvents(message);
+ GST_TRACE("found %lu protection events, %lu decryptors available", protectionSystemEvents.events().size(), protectionSystemEvents.availableSystems().size());
+ InitData initData;
- for (auto& event : streamEncryptionInformation.first) {
- GST_TRACE("handling protection event %u", GST_EVENT_SEQNUM(event.get()));
+ for (auto& event : protectionSystemEvents.events()) {
const char* eventKeySystemId = nullptr;
GstBuffer* data = ""
gst_event_parse_protection(event.get(), &eventKeySystemId, &data, nullptr);
- GstMappedBuffer dataMapped(data, GST_MAP_READ);
- if (!dataMapped) {
- GST_WARNING("cannot map %s protection data", eventKeySystemId);
- break;
- }
- GST_TRACE("appending init data for %s of size %" G_GSIZE_FORMAT, eventKeySystemId, dataMapped.size());
- GST_MEMDUMP("init data", reinterpret_cast<const unsigned char*>(dataMapped.data()), dataMapped.size());
- concatenatedInitDataChunks.append(dataMapped.data(), dataMapped.size());
- ++concatenatedInitDataChunksNumber;
- eventKeySystemIdString = eventKeySystemId;
- if (streamEncryptionInformation.second.contains(eventKeySystemId)) {
- GST_TRACE("considering init data handled for %s", eventKeySystemId);
- m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
- }
+ initData.append({eventKeySystemId, data});
+ m_handledProtectionEvents.add(GST_EVENT_SEQNUM(event.get()));
}
- if (!concatenatedInitDataChunksNumber)
- return false;
+ initializationDataEncountered(WTFMove(initData));
- if (concatenatedInitDataChunksNumber > 1)
- eventKeySystemIdString = emptyString();
-
- RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), eventKeySystemIdString, initData = WTFMove(concatenatedInitDataChunks)] {
- if (!weakThis)
- return;
-
- GST_DEBUG_OBJECT(weakThis->pipeline(), "scheduling initializationDataEncountered event for %s with concatenated init datas size of %" G_GSIZE_FORMAT, eventKeySystemIdString.utf8().data(), initData.size());
- GST_MEMDUMP("init datas", initData.data(), initData.size());
- weakThis->m_player->initializationDataEncountered("cenc"_s, ArrayBuffer::create(initData.data(), initData.size()));
- });
-
GST_INFO_OBJECT(pipeline(), "waiting for a CDM instance");
m_protectionCondition.waitFor(m_protectionMutex, Seconds(4), [this] {
return this->m_cdmInstance;
});
+
if (m_cdmInstance && !m_cdmInstance->keySystem().isEmpty()) {
const char* preferredKeySystemUuid = GStreamerEMEUtilities::keySystemToUuid(m_cdmInstance->keySystem());
- GST_INFO_OBJECT(pipeline(), "working with %s, continuing with %s on %s", m_cdmInstance->keySystem().utf8().data(), preferredKeySystemUuid, GST_MESSAGE_SRC_NAME(message));
+ GST_INFO_OBJECT(pipeline(), "working with key system %s, continuing with key system %s on %s", m_cdmInstance->keySystem().utf8().data(), preferredKeySystemUuid, GST_MESSAGE_SRC_NAME(message));
GRefPtr<GstContext> context = adoptGRef(gst_context_new("drm-preferred-decryption-system-id", FALSE));
GstStructure* contextStructure = gst_context_writable_structure(context.get());
@@ -425,7 +372,7 @@
gst_structure_set(contextStructure, "decryption-system-id", G_TYPE_STRING, preferredKeySystemUuid, nullptr);
gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context.get());
} else
- GST_WARNING("no proper CDM instance attached");
+ GST_WARNING("CDM instance not initializaed");
return true;
}
@@ -1234,48 +1181,17 @@
}
#if ENABLE(ENCRYPTED_MEDIA)
-void MediaPlayerPrivateGStreamerBase::initializationDataEncountered(GstEvent* event)
+void MediaPlayerPrivateGStreamerBase::initializationDataEncountered(InitData&& initData)
{
- const char* eventKeySystemUUID = nullptr;
- GstBuffer* data = ""
- gst_event_parse_protection(event, &eventKeySystemUUID, &data, nullptr);
+ ASSERT(!isMainThread());
- // Check if the system key of the protection event is the same of the CDM instance.
- // For example: we can receive a new Widevine protection event but the CDM instance initialized with
- // Playready, so we ignore this event.
- if (m_cdmInstance
-#if GST_CHECK_VERSION(1, 15, 0)
- && g_strcmp0(eventKeySystemUUID, GST_PROTECTION_UNSPECIFIED_SYSTEM_ID)
-#endif
- && g_strcmp0(GStreamerEMEUtilities::keySystemToUuid(m_cdmInstance->keySystem()), eventKeySystemUUID)) {
- GST_DEBUG("The protection event with UUID %s is ignored because it isn't supported by the CDM %s", eventKeySystemUUID, m_cdmInstance->keySystem().utf8().data());
- return;
- }
-
- GstMappedBuffer dataMapped(data, GST_MAP_READ);
- if (!dataMapped) {
- GST_WARNING("cannot map %s protection data", eventKeySystemUUID);
- return;
- }
-
- GST_TRACE("init data encountered for %s of size %" G_GSIZE_FORMAT, eventKeySystemUUID, dataMapped.size());
- GST_MEMDUMP("init data", reinterpret_cast<const uint8_t*>(dataMapped.data()), dataMapped.size());
- InitData initData(reinterpret_cast<const uint8_t*>(dataMapped.data()), dataMapped.size());
-
- String eventKeySystemUUIDString = eventKeySystemUUID;
- RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), eventKeySystemUUID = eventKeySystemUUIDString, initData] {
+ RunLoop::main().dispatch([weakThis = makeWeakPtr(*this), initData = WTFMove(initData)] {
if (!weakThis)
return;
- GST_DEBUG("scheduling initializationDataEncountered event for %s with init data size of %u", eventKeySystemUUID.utf8().data(), initData.sizeInBytes());
- GST_MEMDUMP("init datas", reinterpret_cast<const uint8_t*>(initData.characters8()), initData.sizeInBytes());
- auto initDataType = "cenc"_s;
-#if GST_CHECK_VERSION(1, 15, 0)
- if (eventKeySystemUUID == GST_PROTECTION_UNSPECIFIED_SYSTEM_ID)
- initDataType = "webm"_s;
-#endif
- weakThis->m_player->initializationDataEncountered(initDataType,
- ArrayBuffer::create(reinterpret_cast<const uint8_t*>(initData.characters8()), initData.sizeInBytes()));
+ GST_DEBUG("scheduling initializationDataEncountered event of size %lu", initData.payload()->size());
+ GST_MEMDUMP("init datas", reinterpret_cast<const uint8_t*>(initData.payload()->data()), initData.payload()->size());
+ weakThis->m_player->initializationDataEncountered(initData.payloadContainerType(), initData.payload()->tryCreateArrayBuffer());
});
}
@@ -1309,7 +1225,9 @@
void MediaPlayerPrivateGStreamerBase::attemptToDecryptWithLocalInstance()
{
- // FIXME.
+ bool eventHandled = gst_element_send_event(pipeline(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB,
+ gst_structure_new("attempt-to-decrypt", "cdm-instance", G_TYPE_POINTER, m_cdmInstance.get(), nullptr)));
+ GST_DEBUG("attempting to decrypt, event handled %s", boolForPrinting(eventHandled));
}
void MediaPlayerPrivateGStreamerBase::dispatchDecryptionKey(GstBuffer* buffer)
@@ -1316,8 +1234,7 @@
{
bool eventHandled = gst_element_send_event(pipeline(), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_OOB,
gst_structure_new("drm-cipher", "key", GST_TYPE_BUFFER, buffer, nullptr)));
- m_needToResendCredentials = m_handledProtectionEvents.size() > 0;
- GST_TRACE("emitted decryption cipher key on pipeline, event handled %s, need to resend credentials %s", boolForPrinting(eventHandled), boolForPrinting(m_needToResendCredentials));
+ GST_TRACE("emitted decryption cipher key on pipeline, event handled %s", boolForPrinting(eventHandled));
}
void MediaPlayerPrivateGStreamerBase::dispatchCDMInstance()
@@ -1333,7 +1250,11 @@
GST_DEBUG_OBJECT(pipeline(), "event %u already handled", GST_EVENT_SEQNUM(event));
return;
}
- initializationDataEncountered(event);
+ GST_DEBUG_OBJECT(pipeline(), "handling event %u from MSE", GST_EVENT_SEQNUM(event));
+ const char* eventKeySystemUUID = nullptr;
+ GstBuffer* initData = nullptr;
+ gst_event_parse_protection(event, &eventKeySystemUUID, &initData, nullptr);
+ initializationDataEncountered({eventKeySystemUUID, initData});
}
void MediaPlayerPrivateGStreamerBase::setWaitingForKey(bool waitingForKey)
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h (238130 => 238131)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h 2018-11-13 14:35:23 UTC (rev 238130)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h 2018-11-13 14:45:31 UTC (rev 238131)
@@ -24,6 +24,7 @@
#if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)
#include "GStreamerCommon.h"
+#include "SharedBuffer.h"
#include <gst/gst.h>
#include <wtf/text/WTFString.h>
@@ -30,9 +31,85 @@
#define WEBCORE_GSTREAMER_EME_UTILITIES_CLEARKEY_UUID "1077efec-c0b2-4d02-ace3-3c1e52e2fb4b"
namespace WebCore {
+class InitData {
+public:
+ InitData()
+ : m_payload(SharedBuffer::create()) { }
-using InitData = String;
+ // FIXME: We should have an enum for system uuids for better type safety.
+ InitData(const String& systemId, GstBuffer* initData)
+ : m_systemId(systemId)
+ {
+ GstMappedBuffer mappedInitData(initData, GST_MAP_READ);
+ if (!mappedInitData) {
+ GST_ERROR("cannot map %s protection data", systemId.utf8().data());
+ ASSERT_NOT_REACHED();
+ }
+ m_payload = SharedBuffer::create(mappedInitData.data(), mappedInitData.size());
+ }
+ void append(InitData&& initData)
+ {
+ // FIXME: There is some confusion here about how to detect the
+ // correct "initialization data type", if the system ID is
+ // GST_PROTECTION_UNSPECIFIED_SYSTEM_ID, then we know it came
+ // from WebM. If the system id is specified with one of the
+ // defined ClearKey / Playready / Widevine / etc UUIDs, then
+ // we know it's MP4. For the latter case, it does not matter
+ // which of the UUIDs it is, so we just overwrite it. This is
+ // a quirk of how GStreamer provides protection events, and
+ // it's not very robust, so be careful here!
+ m_systemId = initData.m_systemId;
+
+ m_payload->append(*initData.payload());
+ }
+
+ const RefPtr<SharedBuffer> payload() const { return m_payload; }
+ const String& systemId() const { return m_systemId; }
+ String payloadContainerType() const
+ {
+#if GST_CHECK_VERSION(1, 15, 0)
+ if (m_systemId == GST_PROTECTION_UNSPECIFIED_SYSTEM_ID)
+ return "webm"_s;
+#endif
+ return "cenc"_s;
+ }
+
+private:
+ String m_systemId;
+ RefPtr<SharedBuffer> m_payload;
+};
+
+class ProtectionSystemEvents {
+public:
+ using EventVector = Vector<GRefPtr<GstEvent>>;
+
+ explicit ProtectionSystemEvents(GstMessage* message)
+ {
+ const GstStructure* structure = gst_message_get_structure(message);
+
+ const GValue* streamEncryptionEventsList = gst_structure_get_value(structure, "stream-encryption-events");
+ ASSERT(streamEncryptionEventsList && GST_VALUE_HOLDS_LIST(streamEncryptionEventsList));
+ unsigned numEvents = gst_value_list_get_size(streamEncryptionEventsList);
+ m_events.reserveInitialCapacity(numEvents);
+ for (unsigned i = 0; i < numEvents; ++i)
+ m_events.uncheckedAppend(GRefPtr<GstEvent>(static_cast<GstEvent*>(g_value_get_boxed(gst_value_list_get_value(streamEncryptionEventsList, i)))));
+ const GValue* streamEncryptionAllowedSystemsValue = gst_structure_get_value(structure, "available-stream-encryption-systems");
+ const char** streamEncryptionAllowedSystems = reinterpret_cast<const char**>(g_value_get_boxed(streamEncryptionAllowedSystemsValue));
+ if (streamEncryptionAllowedSystems) {
+ for (unsigned i = 0; streamEncryptionAllowedSystems[i]; ++i)
+ m_availableSystems.append(streamEncryptionAllowedSystems[i]);
+ }
+ }
+ const EventVector& events() const { return m_events; }
+ const Vector<String>& availableSystems() const { return m_availableSystems; }
+
+private:
+ EventVector m_events;
+ Vector<String> m_availableSystems;
+};
+
+
class GStreamerEMEUtilities {
public:
Modified: trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp (238130 => 238131)
--- trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp 2018-11-13 14:35:23 UTC (rev 238130)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/eme/WebKitCommonEncryptionDecryptorGStreamer.cpp 2018-11-13 14:45:31 UTC (rev 238131)
@@ -26,6 +26,7 @@
#if ENABLE(ENCRYPTED_MEDIA) && USE(GSTREAMER)
#include "GStreamerCommon.h"
+#include <CDMInstance.h>
#include <wtf/Condition.h>
#include <wtf/PrintStream.h>
#include <wtf/RunLoop.h>
@@ -33,7 +34,7 @@
#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;
-
+ RefPtr<WebCore::CDMInstance> cdmInstance;
bool keyReceived;
bool waitingForKey { false };
Lock mutex;
@@ -206,9 +207,9 @@
return GST_FLOW_NOT_SUPPORTED;
}
// Send "drm-cdm-instance-needed" message to the player to resend the CDMInstance if available and inform we are waiting for key.
- gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-cdm-instance-needed")));
priv->waitingForKey = true;
gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-waiting-for-key")));
+ gst_element_post_message(GST_ELEMENT(self), gst_message_new_element(GST_OBJECT(self), gst_structure_new_empty("drm-cdm-instance-needed")));
priv->condition.waitFor(priv->mutex, Seconds(5), [priv] {
return priv->keyReceived;
@@ -306,8 +307,24 @@
WebKitMediaCommonEncryptionDecryptClass* klass = WEBKIT_MEDIA_CENC_DECRYPT_GET_CLASS(self);
gboolean result = FALSE;
+
switch (GST_EVENT_TYPE(event)) {
case GST_EVENT_CUSTOM_DOWNSTREAM_OOB: {
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=191355
+ // We should be handling protection events in this class in
+ // addition to out-of-band data. In regular playback, after a
+ // preferred system ID context is set, any future protection
+ // events will not be handled by the demuxer, so the must be
+ // handled in here.
+ const GstStructure* structure = gst_event_get_structure(event);
+ gst_structure_get(structure, "cdm-instance", G_TYPE_POINTER, &priv->cdmInstance, nullptr);
+ if (!priv->cdmInstance) {
+ GST_ERROR_OBJECT(self, "No CDM instance received");
+ result = FALSE;
+ break;
+ }
+ GST_DEBUG_OBJECT(self, "received a cdm instance %p", priv->cdmInstance.get());
+
if (klass->handleKeyResponse(self, event)) {
GST_DEBUG_OBJECT(self, "key received");
priv->keyReceived = true;