Commit c6286418 authored by Olivier Crete's avatar Olivier Crete Committed by Wim Taymans
Browse files

theoraenc: implement upstream keyframe force

Implement handling of upstream keyframe forcing.
Update the design documents too.
Fixes #578656
parent d0c36826
...@@ -48,7 +48,7 @@ Implementation: ...@@ -48,7 +48,7 @@ Implementation:
triggered the event. triggered the event.
(G_TYPE_UINT64)"stream-time" : the stream position that triggered the (G_TYPE_UINT64)"stream-time" : the stream position that triggered the
event. 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. event was triggered.
.... : optional other data fields. .... : optional other data fields.
...@@ -70,3 +70,18 @@ Implementation: ...@@ -70,3 +70,18 @@ Implementation:
3) The application receives the GstForceKeyUnit on a sink padprobe of the sink 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 and reconfigures the sink to make it perform new actions after receiving
the next buffer. 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.
...@@ -99,6 +99,8 @@ struct _GstTheoraEnc ...@@ -99,6 +99,8 @@ struct _GstTheoraEnc
GstClockTime expected_ts; GstClockTime expected_ts;
gboolean next_discont; gboolean next_discont;
gboolean force_keyframe;
guint packetno; guint packetno;
guint64 bytes_out; guint64 bytes_out;
guint64 granulepos_offset; guint64 granulepos_offset;
......
...@@ -184,6 +184,7 @@ GST_STATIC_PAD_TEMPLATE ("src", ...@@ -184,6 +184,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
GST_BOILERPLATE (GstTheoraEnc, gst_theora_enc, GstElement, GST_TYPE_ELEMENT); GST_BOILERPLATE (GstTheoraEnc, gst_theora_enc, GstElement, GST_TYPE_ELEMENT);
static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event); 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 GstFlowReturn theora_enc_chain (GstPad * pad, GstBuffer * buffer);
static GstStateChangeReturn theora_enc_change_state (GstElement * element, static GstStateChangeReturn theora_enc_change_state (GstElement * element,
GstStateChange transition); GstStateChange transition);
...@@ -292,6 +293,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class) ...@@ -292,6 +293,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class)
enc->srcpad = enc->srcpad =
gst_pad_new_from_static_template (&theora_enc_src_factory, "src"); 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_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED); gst_segment_init (&enc->segment, GST_FORMAT_UNDEFINED);
...@@ -667,6 +669,41 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event) ...@@ -667,6 +669,41 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
return res; return res;
} }
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 static gboolean
theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp, theora_enc_is_discontinuous (GstTheoraEnc * enc, GstClockTime timestamp,
GstClockTime duration) GstClockTime duration)
...@@ -704,6 +741,7 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer) ...@@ -704,6 +741,7 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
ogg_packet op; ogg_packet op;
GstClockTime timestamp, duration, running_time; GstClockTime timestamp, duration, running_time;
GstFlowReturn ret; GstFlowReturn ret;
gboolean force_keyframe;
enc = GST_THEORA_ENC (GST_PAD_PARENT (pad)); enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
...@@ -716,9 +754,34 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer) ...@@ -716,9 +754,34 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
*/ */
timestamp = GST_BUFFER_TIMESTAMP (buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer);
duration = GST_BUFFER_DURATION (buffer); duration = GST_BUFFER_DURATION (buffer);
running_time = running_time =
gst_segment_to_running_time (&enc->segment, GST_FORMAT_TIME, timestamp); 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 /* make sure we copy the discont flag to the next outgoing buffer when it's
* set on the incomming buffer */ * set on the incomming buffer */
if (GST_BUFFER_IS_DISCONT (buffer)) { if (GST_BUFFER_IS_DISCONT (buffer)) {
...@@ -1044,6 +1107,7 @@ theora_enc_change_state (GstElement * element, GstStateChange transition) ...@@ -1044,6 +1107,7 @@ theora_enc_change_state (GstElement * element, GstStateChange transition)
theora_info_init (&enc->info); theora_info_init (&enc->info);
theora_comment_init (&enc->comment); theora_comment_init (&enc->comment);
enc->packetno = 0; enc->packetno = 0;
enc->force_keyframe = FALSE;
break; break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING: case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break; break;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment