dashdemux: add support for generating Protection events from ContentProtection elements

If a ContentProtection element is present in an AdaptationSet element,
send Protection events on the source pad, so that qtdemux can use this
information to correctly generate its source caps for DASH CENC
encrypted streams.

This allows qtdemux to support CENC encrypted DASH streams where the
content protection specific information is carried in the MPD file
rather than in pssh boxes in the initialisation segments.

This commit adds a new function to the adaptivedemux base class to allow
a GstEvent to be queued for a stream. The queue of events are sent the
next time a buffer is pushed for that stream.

https://bugzilla.gnome.org/show_bug.cgi?id=705991
This commit is contained in:
Alex Ashley 2015-02-06 13:22:14 +00:00 committed by Tim-Philipp Müller
parent f8b7c38bed
commit 71a1e3669a
4 changed files with 102 additions and 1 deletions

View file

@ -241,6 +241,9 @@ static GstPad *gst_dash_demux_create_pad (GstDashDemux * demux,
#define SIDX_ENTRY(s,i) (&(SIDX(s)->entries[(i)]))
#define SIDX_CURRENT_ENTRY(s) SIDX_ENTRY(s, SIDX(s)->entry_index)
static void gst_dash_demux_send_content_protection_event (gpointer cp_data,
gpointer stream);
#define gst_dash_demux_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstDashDemux, gst_dash_demux, GST_TYPE_ADAPTIVE_DEMUX,
GST_DEBUG_CATEGORY_INIT (gst_dash_demux_debug, "dashdemux", 0,
@ -525,12 +528,52 @@ gst_dash_demux_setup_all_streams (GstDashDemux * demux)
(stream), tags);
stream->index = i;
stream->pending_seek_ts = GST_CLOCK_TIME_NONE;
if (active_stream->cur_adapt_set &&
active_stream->cur_adapt_set->RepresentationBase &&
active_stream->cur_adapt_set->RepresentationBase->ContentProtection) {
GST_DEBUG_OBJECT (demux, "Adding ContentProtection events to source pad");
g_list_foreach (active_stream->cur_adapt_set->
RepresentationBase->ContentProtection,
gst_dash_demux_send_content_protection_event, stream);
}
gst_isoff_sidx_parser_init (&stream->sidx_parser);
}
return TRUE;
}
static void
gst_dash_demux_send_content_protection_event (gpointer data, gpointer userdata)
{
GstDescriptorType *cp = (GstDescriptorType *) data;
GstDashDemuxStream *stream = (GstDashDemuxStream *) userdata;
GstEvent *event;
GstBuffer *pssi;
glong pssi_len;
gchar *schemeIdUri;
if (cp->schemeIdUri == NULL)
return;
GST_TRACE_OBJECT (stream, "check schemeIdUri %s", cp->schemeIdUri);
/* RFC 2141 states: The leading "urn:" sequence is case-insensitive */
schemeIdUri = g_ascii_strdown (cp->schemeIdUri, -1);
if (g_str_has_prefix (schemeIdUri, "urn:uuid:")) {
pssi_len = strlen (cp->value);
pssi = gst_buffer_new_wrapped (g_memdup (cp->value, pssi_len), pssi_len);
GST_LOG_OBJECT (stream, "Queuing Protection event on source pad");
/* RFC 4122 states that the hex part of a UUID is in lower case,
* but some streams seem to ignore this and use upper case for the
* protection system ID */
event = gst_event_new_protection (cp->schemeIdUri + 9, pssi, "dash/mpd");
gst_adaptive_demux_stream_queue_event ((GstAdaptiveDemuxStream *) stream,
event);
gst_buffer_unref (pssi);
}
g_free (schemeIdUri);
}
static GstClockTime
gst_dash_demux_get_duration (GstAdaptiveDemux * ademux)
{

View file

@ -69,6 +69,8 @@ static gboolean gst_mpdparser_get_xml_node_content (xmlNode * a_node,
gchar ** content);
static gchar *gst_mpdparser_get_xml_node_namespace (xmlNode * a_node,
const gchar * prefix);
static gboolean gst_mpdparser_get_xml_node_as_string (xmlNode * a_node,
gchar ** content);
/* XML node parsing */
static void gst_mpdparser_parse_baseURL_node (GList ** list, xmlNode * a_node);
@ -934,6 +936,24 @@ gst_mpdparser_get_xml_node_content (xmlNode * a_node, gchar ** content)
return exists;
}
static gboolean
gst_mpdparser_get_xml_node_as_string (xmlNode * a_node, gchar ** content)
{
gboolean exists = FALSE;
xmlBufferPtr buffer = xmlBufferCreate ();
int size;
size = xmlNodeDump (buffer, a_node->doc, a_node, 0, /* indent */
0 /* format */ );
if (size > 0) {
*content = (gchar *) xmlBufferDetach (buffer);
exists = TRUE;
GST_LOG (" - %s: %s", a_node->name, *content);
}
xmlBufferFree (buffer);
return exists;
}
static gchar *
gst_mpdparser_get_xml_node_namespace (xmlNode * a_node, const gchar * prefix)
{
@ -992,7 +1012,11 @@ gst_mpdparser_parse_descriptor_type_node (GList ** list, xmlNode * a_node)
GST_LOG ("attributes of %s node:", a_node->name);
gst_mpdparser_get_xml_prop_string (a_node, "schemeIdUri",
&new_descriptor->schemeIdUri);
gst_mpdparser_get_xml_prop_string (a_node, "value", &new_descriptor->value);
if (!gst_mpdparser_get_xml_prop_string (a_node, "value",
&new_descriptor->value)) {
/* if no value attribute, use XML string representation of the node */
gst_mpdparser_get_xml_node_as_string (a_node, &new_descriptor->value);
}
}
static void

View file

@ -657,6 +657,17 @@ gst_adaptive_demux_set_stream_struct_size (GstAdaptiveDemux * demux,
demux->stream_struct_size = struct_size;
}
static void
gst_adaptive_demux_send_event (gpointer data, gpointer userdata)
{
GstEvent *event = (GstEvent *) data;
GstPad *pad = (GstPad *) userdata;
if (!gst_pad_push_event (pad, event)) {
GST_ERROR_OBJECT (pad, "Failed to send pending event");
}
}
static gboolean
gst_adaptive_demux_expose_stream (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream)
@ -877,6 +888,11 @@ gst_adaptive_demux_stream_free (GstAdaptiveDemuxStream * stream)
stream->pending_segment = NULL;
}
if (stream->pending_events) {
g_list_free_full (stream->pending_events, (GDestroyNotify) gst_event_unref);
stream->pending_events = NULL;
}
if (stream->src_srcpad) {
gst_object_unref (stream->src_srcpad);
stream->src_srcpad = NULL;
@ -1296,6 +1312,13 @@ gst_adaptive_demux_stream_set_tags (GstAdaptiveDemuxStream * stream,
stream->pending_tags = tags;
}
void
gst_adaptive_demux_stream_queue_event (GstAdaptiveDemuxStream * stream,
GstEvent * event)
{
stream->pending_events = g_list_append (stream->pending_events, event);
}
static guint64
_update_average_bitrate (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, guint64 new_bitrate)
@ -1447,6 +1470,13 @@ gst_adaptive_demux_stream_push_buffer (GstAdaptiveDemuxStream * stream,
gst_pad_push_event (stream->pad, gst_event_new_tag (stream->pending_tags));
stream->pending_tags = NULL;
}
if (G_UNLIKELY (stream->pending_events)) {
g_list_foreach (stream->pending_events, gst_adaptive_demux_send_event,
stream->pad);
g_list_free (stream->pending_events);
stream->pending_events = NULL;
}
ret = gst_pad_push (stream->pad, buffer);
GST_LOG_OBJECT (stream->pad, "Push result: %d %s", ret,

View file

@ -118,6 +118,7 @@ struct _GstAdaptiveDemuxStream
GstEvent *pending_segment;
GstTagList *pending_tags;
gboolean need_header;
GList *pending_events;
GstFlowReturn last_ret;
GError *last_error;
@ -427,6 +428,9 @@ GstFlowReturn gst_adaptive_demux_stream_push_buffer (GstAdaptiveDemuxStream * st
GstFlowReturn
gst_adaptive_demux_stream_advance_fragment (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, GstClockTime duration);
void gst_adaptive_demux_stream_queue_event (GstAdaptiveDemuxStream * stream,
GstEvent * event);
GstFlowReturn
gst_adaptive_demux_stream_advance_fragment_unlocked (GstAdaptiveDemux * demux,
GstAdaptiveDemuxStream * stream, GstClockTime duration);