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.
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.
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);
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);
}
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)
{
ogg_packet op;
GstClockTime timestamp, duration, running_time;
GstFlowReturn ret;
+ gboolean force_keyframe;
enc = GST_THEORA_ENC (GST_PAD_PARENT (pad));
*/
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)) {
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;