From c628641880d9b0a1c7dacd2be8e67ecd47754d4e Mon Sep 17 00:00:00 2001 From: Olivier Crete Date: Tue, 14 Apr 2009 18:16:37 +0200 Subject: [PATCH] theoraenc: implement upstream keyframe force Implement handling of upstream keyframe forcing. Update the design documents too. Fixes #578656 --- docs/design/draft-keyframe-force.txt | 17 +++++++++- ext/theora/gsttheoraenc.h | 2 ++ ext/theora/theoraenc.c | 64 ++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/docs/design/draft-keyframe-force.txt b/docs/design/draft-keyframe-force.txt index d315e8d..c1d408a 100644 --- a/docs/design/draft-keyframe-force.txt +++ b/docs/design/draft-keyframe-force.txt @@ -48,7 +48,7 @@ Implementation: triggered the event. (G_TYPE_UINT64)"stream-time" : the stream position that triggered the event. - (G_TYPE_UINT64)"running_time" : the running time of the stream when the + (G_TYPE_UINT64)"running-time" : the running time of the stream when the event was triggered. .... : optional other data fields. @@ -70,3 +70,18 @@ Implementation: 3) The application receives the GstForceKeyUnit on a sink padprobe of the sink and reconfigures the sink to make it perform new actions after receiving the next buffer. + + +Upstream +-------- + +When using RTP packets can get lost or receivers can be added at any time, +they may request a new key frame. + +An downstream element sends an upstream "GstForceKeyUnit" event up the +pipeline. + +When an element produces some kind of key unit in output, but has +no such concept in its input (like an encoder that takes raw frames), +it consumes the event (doesn't pass it upstream), and instead sends +a downstream GstForceKeyUnit event and a new keyframe. diff --git a/ext/theora/gsttheoraenc.h b/ext/theora/gsttheoraenc.h index cad5496..9d45414 100644 --- a/ext/theora/gsttheoraenc.h +++ b/ext/theora/gsttheoraenc.h @@ -99,6 +99,8 @@ struct _GstTheoraEnc GstClockTime expected_ts; gboolean next_discont; + gboolean force_keyframe; + guint packetno; guint64 bytes_out; guint64 granulepos_offset; diff --git a/ext/theora/theoraenc.c b/ext/theora/theoraenc.c index 7d79091..6704a57 100644 --- a/ext/theora/theoraenc.c +++ b/ext/theora/theoraenc.c @@ -184,6 +184,7 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_BOILERPLATE (GstTheoraEnc, gst_theora_enc, GstElement, GST_TYPE_ELEMENT); static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event); +static gboolean theora_enc_src_event (GstPad * pad, GstEvent * event); static GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer); static GstStateChangeReturn theora_enc_change_state (GstElement * element, GstStateChange transition); @@ -292,6 +293,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class) enc->srcpad = gst_pad_new_from_static_template (&theora_enc_src_factory, "src"); + gst_pad_set_event_function (enc->srcpad, theora_enc_src_event); gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad); gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED); @@ -668,6 +670,41 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event) } static gboolean +theora_enc_src_event (GstPad * pad, GstEvent * event) +{ + GstTheoraEnc *enc; + gboolean res = TRUE; + + enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CUSTOM_UPSTREAM: + { + const GstStructure *s; + + s = gst_event_get_structure (event); + + if (gst_structure_has_name (s, "GstForceKeyUnit")) { + GST_OBJECT_LOCK (enc); + enc->force_keyframe = TRUE; + GST_OBJECT_UNLOCK (enc); + /* consume the event */ + res = TRUE; + gst_event_unref (event); + } else { + res = gst_pad_push_event (enc->sinkpad, event); + } + break; + } + default: + res = gst_pad_push_event (enc->sinkpad, event); + break; + } + + return res; +} + +static gboolean theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp, GstClockTime duration) { @@ -704,6 +741,7 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer) ogg_packet op; GstClockTime timestamp, duration, running_time; GstFlowReturn ret; + gboolean force_keyframe; enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); @@ -716,9 +754,34 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer) */ timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); + running_time = gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp); + /* see if we need to schedule a keyframe */ + GST_OBJECT_LOCK (enc); + force_keyframe = enc->force_keyframe; + enc->force_keyframe = FALSE; + GST_OBJECT_UNLOCK (enc); + + if (force_keyframe) { + GstClockTime stream_time; + GstStructure *s; + + stream_time = gst_segment_to_stream_time (&enc->segment, + GST_FORMAT_TIME, timestamp); + + s = gst_structure_new ("GstForceKeyUnit", + "timestamp", G_TYPE_UINT64, timestamp, + "stream-time", G_TYPE_UINT64, stream_time, + "running-time", G_TYPE_UINT64, running_time, NULL); + + theora_enc_force_keyframe (enc); + + gst_pad_push_event (enc->srcpad, + gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s)); + } + /* make sure we copy the discont flag to the next outgoing buffer when it's * set on the incomming buffer */ if (GST_BUFFER_IS_DISCONT (buffer)) { @@ -1044,6 +1107,7 @@ theora_enc_change_state (GstElement * element, GstStateChange transition) theora_info_init (&enc->info); theora_comment_init (&enc->comment); enc->packetno = 0; + enc->force_keyframe = FALSE; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; -- 2.7.4