From a1305b949c511548e27a56470d399fde84aea49a Mon Sep 17 00:00:00 2001 From: Alessandro Decina Date: Tue, 6 Dec 2011 13:06:06 +0100 Subject: [PATCH] h264parse: handle force key unit events Send SPS/PPS on force key unit events. Useful for example when serving HLS without transcoding. --- gst/videoparsers/Makefile.am | 2 +- gst/videoparsers/gsth264parse.c | 186 ++++++++++++++++++++++++++++++++++++++++ gst/videoparsers/gsth264parse.h | 3 + 3 files changed, 190 insertions(+), 1 deletion(-) diff --git a/gst/videoparsers/Makefile.am b/gst/videoparsers/Makefile.am index 5be1563..8ac52ae 100644 --- a/gst/videoparsers/Makefile.am +++ b/gst/videoparsers/Makefile.am @@ -12,7 +12,7 @@ libgstvideoparsersbad_la_CFLAGS = \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstvideoparsersbad_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-$(GST_MAJORMINOR).la \ - $(GST_BASE_LIBS) -lgstpbutils-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) -lgstpbutils-$(GST_MAJORMINOR) -lgstvideo-$(GST_MAJORMINOR) \ $(GST_LIBS) libgstvideoparsersbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c index 85ea380..20c59a3 100644 --- a/gst/videoparsers/gsth264parse.c +++ b/gst/videoparsers/gsth264parse.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "gsth264parse.h" #include @@ -93,6 +94,9 @@ static void gst_h264_parse_get_property (GObject * object, guint prop_id, static gboolean gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps); static GstCaps *gst_h264_parse_get_caps (GstBaseParse * parse); static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_h264_parse_event (GstBaseParse * parse, GstEvent * event); +static gboolean gst_h264_parse_src_event (GstBaseParse * parse, + GstEvent * event); static void gst_h264_parse_base_init (gpointer g_class) @@ -138,6 +142,8 @@ gst_h264_parse_class_init (GstH264ParseClass * klass) GST_DEBUG_FUNCPTR (gst_h264_parse_pre_push_frame); parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_set_caps); parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_h264_parse_get_caps); + parse_class->event = GST_DEBUG_FUNCPTR (gst_h264_parse_event); + parse_class->src_event = GST_DEBUG_FUNCPTR (gst_h264_parse_src_event); } static void @@ -206,6 +212,9 @@ gst_h264_parse_reset (GstH264Parse * h264parse) h264parse->last_report = GST_CLOCK_TIME_NONE; h264parse->push_codec = FALSE; + h264parse->pending_key_unit_ts = GST_CLOCK_TIME_NONE; + h264parse->force_key_unit_event = NULL; + gst_h264_parse_reset_frame (h264parse); } @@ -1240,15 +1249,111 @@ gst_h264_parse_push_codec_buffer (GstH264Parse * h264parse, GstBuffer * nal, return gst_pad_push (GST_BASE_PARSE_SRC_PAD (h264parse), nal); } +static GstEvent * +check_pending_key_unit_event (GstEvent * pending_event, GstSegment * segment, + GstClockTime timestamp, guint flags, GstClockTime pending_key_unit_ts) +{ + GstClockTime running_time, stream_time; + gboolean all_headers; + guint count; + GstEvent *event = NULL; + + g_return_val_if_fail (segment != NULL, NULL); + + if (pending_event == NULL) + goto out; + + if (GST_CLOCK_TIME_IS_VALID (pending_key_unit_ts) && + timestamp == GST_CLOCK_TIME_NONE) + goto out; + + running_time = gst_segment_to_running_time (segment, + GST_FORMAT_TIME, timestamp); + + GST_INFO ("now %" GST_TIME_FORMAT " wanted %" GST_TIME_FORMAT, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (pending_key_unit_ts)); + if (GST_CLOCK_TIME_IS_VALID (pending_key_unit_ts) && + running_time < pending_key_unit_ts) + goto out; + +#if 0 + if (flags & GST_BUFFER_FLAG_DELTA_UNIT) { + GST_DEBUG ("pending force key unit, waiting for keyframe"); + goto out; + } +#endif + + stream_time = gst_segment_to_stream_time (segment, + GST_FORMAT_TIME, timestamp); + + gst_video_event_parse_upstream_force_key_unit (pending_event, + NULL, &all_headers, &count); + + event = + gst_video_event_new_downstream_force_key_unit (timestamp, stream_time, + running_time, all_headers, count); + gst_event_set_seqnum (event, gst_event_get_seqnum (pending_event)); + +out: + return event; +} + +static void +gst_h264_parse_prepare_key_unit (GstH264Parse * parse, GstEvent * event) +{ + GstClockTime running_time; + guint count; + gboolean have_sps, have_pps; + gint i; + + parse->pending_key_unit_ts = GST_CLOCK_TIME_NONE; + gst_event_replace (&parse->force_key_unit_event, NULL); + + gst_video_event_parse_downstream_force_key_unit (event, + NULL, NULL, &running_time, NULL, &count); + + GST_INFO_OBJECT (parse, "pushing downstream force-key-unit event %d " + "%" GST_TIME_FORMAT " count %d", gst_event_get_seqnum (event), + GST_TIME_ARGS (running_time), count); + gst_pad_push_event (GST_BASE_PARSE_SRC_PAD (parse), event); + + have_sps = have_pps = FALSE; + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if (parse->sps_nals[i] != NULL) { + have_sps = TRUE; + break; + } + } + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if (parse->pps_nals[i] != NULL) { + have_pps = TRUE; + break; + } + } + + GST_INFO_OBJECT (parse, "preparing key unit, have sps %d have pps %d", + have_sps, have_pps); + + /* set push_codec to TRUE so that pre_push_frame sends SPS/PPS again */ + parse->push_codec = TRUE; +} + static GstFlowReturn gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { GstH264Parse *h264parse; GstBuffer *buffer; + GstEvent *event; h264parse = GST_H264_PARSE (parse); buffer = frame->buffer; + if ((event = check_pending_key_unit_event (h264parse->force_key_unit_event, + &parse->segment, GST_BUFFER_TIMESTAMP (buffer), + GST_BUFFER_FLAGS (buffer), h264parse->pending_key_unit_ts))) { + gst_h264_parse_prepare_key_unit (h264parse, event); + } + /* periodic SPS/PPS sending */ if (h264parse->interval > 0 || h264parse->push_codec) { GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); @@ -1542,6 +1647,87 @@ gst_h264_parse_get_caps (GstBaseParse * parse) return res; } +static gboolean +gst_h264_parse_event (GstBaseParse * parse, GstEvent * event) +{ + gboolean handled = FALSE; + GstH264Parse *h264parse = GST_H264_PARSE (parse); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_DOWNSTREAM: + { + GstClockTime timestamp, stream_time, running_time; + gboolean all_headers; + guint count; + + if (!gst_video_event_is_force_key_unit (event)) + break; + + gst_video_event_parse_downstream_force_key_unit (event, + ×tamp, &stream_time, &running_time, &all_headers, &count); + + GST_INFO_OBJECT (h264parse, "received downstream force key unit event, " + "seqnum %d running_time %" GST_TIME_FORMAT " all_headers %d count %d", + gst_event_get_seqnum (event), GST_TIME_ARGS (running_time), + all_headers, count); + handled = TRUE; + + if (h264parse->force_key_unit_event) { + GST_INFO_OBJECT (h264parse, "ignoring force key unit event " + "as one is already queued"); + break; + } + + h264parse->pending_key_unit_ts = running_time; + gst_event_replace (&h264parse->force_key_unit_event, event); + break; + } + default: + break; + } + + return handled; +} + +static gboolean +gst_h264_parse_src_event (GstBaseParse * parse, GstEvent * event) +{ + gboolean handled = FALSE; + GstH264Parse *h264parse = GST_H264_PARSE (parse); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + { + GstClockTime running_time; + gboolean all_headers; + guint count; + + if (!gst_video_event_is_force_key_unit (event)) + break; + + gst_video_event_parse_upstream_force_key_unit (event, + &running_time, &all_headers, &count); + + GST_INFO_OBJECT (h264parse, "received upstream force-key-unit event, " + "seqnum %d running_time %" GST_TIME_FORMAT " all_headers %d count %d", + gst_event_get_seqnum (event), GST_TIME_ARGS (running_time), + all_headers, count); + + if (!all_headers) + break; + + h264parse->pending_key_unit_ts = running_time; + gst_event_replace (&h264parse->force_key_unit_event, event); + /* leave handled = FALSE so that the event gets propagated upstream */ + break; + } + default: + break; + } + + return handled; +} + static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) { diff --git a/gst/videoparsers/gsth264parse.h b/gst/videoparsers/gsth264parse.h index b346c2f..5cbb5ad 100644 --- a/gst/videoparsers/gsth264parse.h +++ b/gst/videoparsers/gsth264parse.h @@ -105,6 +105,9 @@ struct _GstH264Parse /* props */ gboolean split_packetized; guint interval; + + GstClockTime pending_key_unit_ts; + GstEvent *force_key_unit_event; }; struct _GstH264ParseClass -- 2.7.4