mssdemux: PlayReady WRM parsing support

If the manifest has a ProtectionHeader node then parse it and emit
protection events according to the specified protection SystemID.

https://bugzilla.gnome.org/show_bug.cgi?id=753613
This commit is contained in:
Philippe Normand 2015-06-08 15:33:22 +02:00 committed by Tim-Philipp Müller
parent 5d4388bf9e
commit ae7d938842
4 changed files with 140 additions and 0 deletions

View file

@ -367,12 +367,34 @@ _create_pad (GstMssDemux * mssdemux, GstMssStream * manifeststream)
return srcpad;
}
static void
gst_mss_demux_apply_protection_system (GstCaps * caps,
const gchar * selected_system)
{
GstStructure *s;
g_return_if_fail (selected_system);
s = gst_caps_get_structure (caps, 0);
gst_structure_set (s,
"original-media-type", G_TYPE_STRING, gst_structure_get_name (s),
GST_PROTECTION_SYSTEM_ID_CAPS_FIELD, G_TYPE_STRING, selected_system,
NULL);
gst_structure_set_name (s, "application/x-cenc");
}
static gboolean
gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
{
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (demux);
GSList *streams = gst_mss_manifest_get_streams (mssdemux->manifest);
GSList *iter;
const gchar *protection_system_id =
gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
const gchar *protection_data =
gst_mss_manifest_get_protection_data (mssdemux->manifest);
gboolean protected = protection_system_id && protection_data;
const gchar *selected_system = NULL;
if (streams == NULL) {
GST_INFO_OBJECT (mssdemux, "No streams found in the manifest");
@ -382,6 +404,17 @@ gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
return FALSE;
}
if (protected) {
const gchar *sys_ids[2] = { protection_system_id, NULL };
selected_system = gst_protection_select_system (sys_ids);
if (!selected_system) {
GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
"suitable decryptor element has been found");
return FALSE;
}
}
GST_INFO_OBJECT (mssdemux, "Changing max bitrate to %u",
demux->connection_speed);
gst_mss_manifest_change_bitrate (mssdemux->manifest, demux->connection_speed);
@ -404,7 +437,13 @@ gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
srcpad);
stream->manifest_stream = manifeststream;
gst_mss_stream_set_active (manifeststream, TRUE);
caps = gst_mss_stream_get_caps (stream->manifest_stream);
if (protected) {
gst_mss_demux_apply_protection_system (caps, selected_system);
}
gst_adaptive_demux_stream_set_caps (GST_ADAPTIVE_DEMUX_STREAM_CAST (stream),
create_mss_caps (stream, caps));
gst_caps_unref (caps);
@ -417,6 +456,22 @@ gst_mss_demux_setup_streams (GstAdaptiveDemux * demux)
gst_adaptive_demux_stream_set_tags (GST_ADAPTIVE_DEMUX_STREAM_CAST
(stream), tags);
}
if (protected) {
gsize protection_data_len;
guchar *decoded_data =
g_base64_decode (protection_data, &protection_data_len);
GstBuffer *protection_buffer =
gst_buffer_new_wrapped (decoded_data, protection_data_len);
GstEvent *event =
gst_event_new_protection (protection_system_id, protection_buffer,
"smooth-streaming");
GST_LOG_OBJECT (stream, "Queuing Protection event on source pad");
gst_adaptive_demux_stream_queue_event ((GstAdaptiveDemuxStream *) stream,
event);
gst_buffer_unref (protection_buffer);
}
}
return TRUE;
@ -476,10 +531,31 @@ gst_mss_demux_stream_select_bitrate (GstAdaptiveDemuxStream * stream,
if (gst_mss_stream_select_bitrate (mssstream->manifest_stream, bitrate)) {
GstCaps *caps;
GstCaps *msscaps;
GstMssDemux *mssdemux = GST_MSS_DEMUX_CAST (stream->demux);
const gchar *protection_system_id =
gst_mss_manifest_get_protection_system_id (mssdemux->manifest);
const gchar *protection_data =
gst_mss_manifest_get_protection_data (mssdemux->manifest);
gboolean protected = protection_system_id && protection_data;
caps = gst_mss_stream_get_caps (mssstream->manifest_stream);
GST_DEBUG_OBJECT (stream->pad,
"Starting streams reconfiguration due to bitrate changes");
if (protected) {
const gchar *sys_ids[2] = { protection_system_id, NULL };
const gchar *selected_system = gst_protection_select_system (sys_ids);
if (!selected_system) {
GST_ERROR_OBJECT (mssdemux, "stream is protected, but no "
"suitable decryptor element has been found");
return FALSE;
}
gst_mss_demux_apply_protection_system (caps, selected_system);
}
msscaps = create_mss_caps (mssstream, caps);
GST_DEBUG_OBJECT (stream->pad,

View file

@ -27,6 +27,7 @@
#include <gst/adaptivedemux/gstadaptivedemux.h>
#include <gst/base/gstadapter.h>
#include <gst/base/gstdataqueue.h>
#include <gst/gstprotection.h>
#include "gstmssmanifest.h"
#include <gst/uridownloader/gsturidownloader.h>

View file

@ -95,6 +95,9 @@ struct _GstMssManifest
gboolean is_live;
GString *protection_system_id;
gchar *protection_data;
GSList *streams;
};
@ -267,6 +270,41 @@ _gst_mss_stream_init (GstMssStream * stream, xmlNodePtr node)
stream->regex_position = g_regex_new ("\\{start[ _]time\\}", 0, 0, NULL);
}
static void
_gst_mss_parse_protection (GstMssManifest * manifest,
xmlNodePtr protection_node)
{
xmlNodePtr nodeiter;
for (nodeiter = protection_node->children; nodeiter;
nodeiter = nodeiter->next) {
if (nodeiter->type == XML_ELEMENT_NODE
&& (strcmp ((const char *) nodeiter->name, "ProtectionHeader") == 0)) {
xmlChar *system_id_attribute =
xmlGetProp (nodeiter, (xmlChar *) "SystemID");
gchar *value = (gchar *) system_id_attribute;
int id_len = strlen (value);
GString *system_id;
if (value[0] == '{') {
value++;
id_len--;
}
system_id = g_string_new (value);
system_id = g_string_ascii_down (system_id);
if (value[id_len - 1] == '}')
system_id = g_string_truncate (system_id, id_len - 1);
manifest->protection_system_id = system_id;
manifest->protection_data = (gchar *) xmlNodeGetContent (nodeiter);
xmlFree (system_id_attribute);
break;
}
}
}
GstMssManifest *
gst_mss_manifest_new (GstBuffer * data)
{
@ -300,6 +338,11 @@ gst_mss_manifest_new (GstBuffer * data)
manifest->streams = g_slist_append (manifest->streams, stream);
_gst_mss_stream_init (stream, nodeiter);
}
if (nodeiter->type == XML_ELEMENT_NODE
&& (strcmp ((const char *) nodeiter->name, "Protection") == 0)) {
_gst_mss_parse_protection (manifest, nodeiter);
}
}
gst_buffer_unmap (data, &mapinfo);
@ -327,10 +370,28 @@ gst_mss_manifest_free (GstMssManifest * manifest)
g_slist_free_full (manifest->streams, (GDestroyNotify) gst_mss_stream_free);
if (manifest->protection_system_id != NULL)
g_string_free (manifest->protection_system_id, TRUE);
xmlFree (manifest->protection_data);
xmlFreeDoc (manifest->xml);
g_free (manifest);
}
const gchar *
gst_mss_manifest_get_protection_system_id (GstMssManifest * manifest)
{
if (manifest->protection_system_id != NULL)
return manifest->protection_system_id->str;
return NULL;
}
const gchar *
gst_mss_manifest_get_protection_data (GstMssManifest * manifest)
{
return manifest->protection_data;
}
GSList *
gst_mss_manifest_get_streams (GstMssManifest * manifest)
{

View file

@ -52,6 +52,8 @@ gint64 gst_mss_manifest_get_dvr_window_length (GstMssManifest * manifest);
gint gst_mss_manifest_get_look_ahead_fragments_count (GstMssManifest * manifest);
void gst_mss_manifest_reload_fragments (GstMssManifest * manifest, GstBuffer * data);
GstClockTime gst_mss_manifest_get_min_fragment_duration (GstMssManifest * manifest);
const gchar * gst_mss_manifest_get_protection_system_id (GstMssManifest * manifest);
const gchar * gst_mss_manifest_get_protection_data (GstMssManifest * manifest);
GstMssStreamType gst_mss_stream_get_type (GstMssStream *stream);
GstCaps * gst_mss_stream_get_caps (GstMssStream * stream);