From cbeebfea4927b474d56a40f446af67f3230ef6fe Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Fri, 10 Nov 2017 15:56:40 +0200 Subject: avwait: Added "avwait-status" element message "avwait-status" is posted when avwait starts or stops passing through data (e.g. because target-timecode and end-timecode respectively have been reached). The attached structure includes a "dropping" boolean (set to TRUE if we are currently dropping data, FALSE otherwise), and a "running-time" GST_CLOCK_TIME which contains the running time of the change. https://bugzilla.gnome.org/show_bug.cgi?id=790170 --- gst/timecode/gstavwait.c | 109 ++++++++++++++++++++++++++++++++++++++++------- gst/timecode/gstavwait.h | 4 ++ 2 files changed, 98 insertions(+), 15 deletions(-) diff --git a/gst/timecode/gstavwait.c b/gst/timecode/gstavwait.c index e550afdd7..e82034932 100644 --- a/gst/timecode/gstavwait.c +++ b/gst/timecode/gstavwait.c @@ -242,10 +242,13 @@ gst_avwait_init (GstAvWait * self) GST_PAD_SET_PROXY_SCHEDULING (self->vsrcpad); self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; + self->last_seen_video_running_time = GST_CLOCK_TIME_NONE; + self->last_seen_tc = NULL; self->video_eos_flag = FALSE; self->audio_flush_flag = FALSE; self->shutdown_flag = FALSE; + self->dropping = TRUE; self->tc = gst_video_time_code_new_empty (); self->end_tc = NULL; self->running_time_to_end_at = GST_CLOCK_TIME_NONE; @@ -253,10 +256,24 @@ gst_avwait_init (GstAvWait * self) self->target_running_time = DEFAULT_TARGET_RUNNING_TIME; self->mode = DEFAULT_MODE; + gst_video_info_init (&self->vinfo); g_mutex_init (&self->mutex); g_cond_init (&self->cond); } +static void +gst_avwait_send_element_message (GstAvWait * self, gboolean dropping, + GstClockTime running_time) +{ + if (!gst_element_post_message (GST_ELEMENT (self), + gst_message_new_element (GST_OBJECT (self), + gst_structure_new ("avwait-status", + "dropping", G_TYPE_BOOLEAN, dropping, + "running-time", GST_TYPE_CLOCK_TIME, running_time, NULL)))) { + GST_ERROR_OBJECT (self, "Unable to send element message!"); + } +} + static GstStateChangeReturn gst_avwait_change_state (GstElement * element, GstStateChange transition) { @@ -290,10 +307,17 @@ gst_avwait_change_state (GstElement * element, GstStateChange transition) self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; self->running_time_to_end_at = GST_CLOCK_TIME_NONE; } + if (!self->dropping) { + self->dropping = TRUE; + gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE); + } gst_segment_init (&self->asegment, GST_FORMAT_UNDEFINED); self->asegment.position = GST_CLOCK_TIME_NONE; gst_segment_init (&self->vsegment, GST_FORMAT_UNDEFINED); self->vsegment.position = GST_CLOCK_TIME_NONE; + gst_video_info_init (&self->vinfo); + self->last_seen_video_running_time = GST_CLOCK_TIME_NONE; + self->last_seen_tc = NULL; g_mutex_unlock (&self->mutex); break; default: @@ -452,7 +476,7 @@ gst_avwait_set_property (GObject * object, guint prop_id, self->end_tc = NULL; g_free (start_tc); g_free (end_tc); - } else { + } else if (self->end_tc) { if (self->end_tc->config.fps_n == 0 && GST_VIDEO_INFO_FORMAT (&self->vinfo) != GST_VIDEO_FORMAT_UNKNOWN && self->vinfo.fps_n != 0) { @@ -466,17 +490,40 @@ gst_avwait_set_property (GObject * object, guint prop_id, self->target_running_time = g_value_get_uint64 (value); if (self->mode == MODE_RUNNING_TIME) { self->running_time_to_wait_for = self->target_running_time; + if (self->target_running_time < self->last_seen_video_running_time) { + self->dropping = TRUE; + gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE); + } } break; } case PROP_MODE:{ GstAvWaitMode old_mode = self->mode; self->mode = g_value_get_enum (value); - if (self->mode == MODE_RUNNING_TIME) { - self->running_time_to_wait_for = self->target_running_time; - } else if (self->mode != old_mode) { - GST_DEBUG_OBJECT (self, "First time reset in settings"); - self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; + if (self->mode != old_mode) { + switch (self->mode) { + case MODE_TIMECODE: + if (self->last_seen_tc && self->tc && + gst_video_time_code_compare (self->last_seen_tc, + self->tc) < 0) { + self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; + self->dropping = TRUE; + gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE); + } + break; + case MODE_RUNNING_TIME: + self->running_time_to_wait_for = self->target_running_time; + if (self->target_running_time < self->last_seen_video_running_time) { + self->dropping = TRUE; + gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE); + } + break; + /* Let the chain functions handle the rest */ + case MODE_VIDEO_FIRST: + /* pass-through */ + default: + break; + } } break; } @@ -506,6 +553,10 @@ gst_avwait_vsink_event (GstPad * pad, GstObject * parent, GstEvent * event) GST_DEBUG_OBJECT (self, "First time reset in video segment"); self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; self->running_time_to_end_at = GST_CLOCK_TIME_NONE; + if (!self->dropping) { + self->dropping = TRUE; + gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE); + } } self->vsegment.position = GST_CLOCK_TIME_NONE; g_mutex_unlock (&self->mutex); @@ -525,6 +576,10 @@ gst_avwait_vsink_event (GstPad * pad, GstObject * parent, GstEvent * event) GST_DEBUG_OBJECT (self, "First time reset in video flush"); self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; self->running_time_to_end_at = GST_CLOCK_TIME_NONE; + if (!self->dropping) { + self->dropping = TRUE; + gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE); + } } gst_segment_init (&self->vsegment, GST_FORMAT_UNDEFINED); self->vsegment.position = GST_CLOCK_TIME_NONE; @@ -612,6 +667,9 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) { GstClockTime timestamp; GstAvWait *self = GST_AVWAIT (parent); + GstClockTime running_time; + GstVideoTimeCode *tc = NULL; + GstVideoTimeCodeMeta *tc_meta; timestamp = GST_BUFFER_TIMESTAMP (inbuf); if (timestamp == GST_CLOCK_TIME_NONE) { @@ -620,15 +678,20 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) } g_mutex_lock (&self->mutex); self->vsegment.position = timestamp; + running_time = + gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME, + self->vsegment.position); + self->last_seen_video_running_time = running_time; + + tc_meta = gst_buffer_get_video_time_code_meta (inbuf); + if (tc_meta) { + tc = &tc_meta->tc; + self->last_seen_tc = tc; + } switch (self->mode) { case MODE_TIMECODE:{ - GstVideoTimeCode *tc = NULL; - GstVideoTimeCodeMeta *tc_meta; - - tc_meta = gst_buffer_get_video_time_code_meta (inbuf); - if (tc_meta) - tc = &tc_meta->tc; if (self->tc != NULL && tc != NULL) { + gboolean emit_passthrough_signal = FALSE; if (gst_video_time_code_compare (tc, self->tc) < 0 && self->running_time_to_wait_for == GST_CLOCK_TIME_NONE) { GST_DEBUG_OBJECT (self, "Timecode not yet reached, ignoring frame"); @@ -637,6 +700,10 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) } else if (self->running_time_to_wait_for == GST_CLOCK_TIME_NONE) { GST_INFO_OBJECT (self, "Target timecode reached at %" GST_TIME_FORMAT, GST_TIME_ARGS (self->vsegment.position)); + /* Don't emit a signal if we weren't dropping (e.g. settings changed + * mid-flight) */ + emit_passthrough_signal = self->dropping; + self->dropping = FALSE; self->running_time_to_wait_for = gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME, self->vsegment.position); @@ -645,20 +712,23 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) if (self->running_time_to_end_at == GST_CLOCK_TIME_NONE) { GST_INFO_OBJECT (self, "End timecode reached at %" GST_TIME_FORMAT, GST_TIME_ARGS (self->vsegment.position)); + self->dropping = TRUE; self->running_time_to_end_at = gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME, self->vsegment.position); + gst_avwait_send_element_message (self, TRUE, + self->running_time_to_end_at); } gst_buffer_unref (inbuf); inbuf = NULL; + } else if (emit_passthrough_signal) { + gst_avwait_send_element_message (self, FALSE, + self->running_time_to_wait_for); } } break; } case MODE_RUNNING_TIME:{ - GstClockTime running_time = - gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME, - self->vsegment.position); if (running_time < self->running_time_to_wait_for) { GST_DEBUG_OBJECT (self, "Have %" GST_TIME_FORMAT ", waiting for %" GST_TIME_FORMAT, @@ -667,6 +737,10 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) gst_buffer_unref (inbuf); inbuf = NULL; } else { + if (self->dropping) { + self->dropping = FALSE; + gst_avwait_send_element_message (self, FALSE, running_time); + } GST_INFO_OBJECT (self, "Have %" GST_TIME_FORMAT ", waiting for %" GST_TIME_FORMAT, GST_TIME_ARGS (running_time), @@ -681,6 +755,11 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) self->vsegment.position); GST_DEBUG_OBJECT (self, "First video running time is %" GST_TIME_FORMAT, GST_TIME_ARGS (self->running_time_to_wait_for)); + if (self->dropping) { + self->dropping = FALSE; + gst_avwait_send_element_message (self, FALSE, + self->running_time_to_wait_for); + } } break; } diff --git a/gst/timecode/gstavwait.h b/gst/timecode/gstavwait.h index 3e27f0b2c..7cac85129 100644 --- a/gst/timecode/gstavwait.h +++ b/gst/timecode/gstavwait.h @@ -62,11 +62,15 @@ struct _GstAvWait GstSegment asegment, vsegment; GstClockTime running_time_to_wait_for; + GstClockTime last_seen_video_running_time; + GstVideoTimeCode *last_seen_tc; gboolean video_eos_flag; gboolean audio_flush_flag; gboolean shutdown_flag; + gboolean dropping; + GCond cond; GMutex mutex; }; -- cgit v1.2.3