diff options
author | Vivia Nikolaidou <vivia@ahiru.eu> | 2018-08-16 17:47:55 +0300 |
---|---|---|
committer | Vivia Nikolaidou <vivia@ahiru.eu> | 2018-08-17 14:57:36 +0300 |
commit | ff952374b5b7961fd45bc3fb5218e212cae9f434 (patch) | |
tree | a18dc1133ed7a4b07b988b598a62829ff46f47bf | |
parent | 5177f7c7ee583fc9e8582914a29694a40d781337 (diff) |
avwait: Start video and audio together if audio starts late
Also add test to meson
https://bugzilla.gnome.org/show_bug.cgi?id=796977
-rw-r--r-- | gst/timecode/gstavwait.c | 82 | ||||
-rw-r--r-- | gst/timecode/gstavwait.h | 4 | ||||
-rw-r--r-- | tests/check/elements/avwait.c | 41 | ||||
-rw-r--r-- | tests/check/meson.build | 1 |
4 files changed, 110 insertions, 18 deletions
diff --git a/gst/timecode/gstavwait.c b/gst/timecode/gstavwait.c index e20550634..681ae226f 100644 --- a/gst/timecode/gstavwait.c +++ b/gst/timecode/gstavwait.c @@ -262,9 +262,12 @@ gst_avwait_init (GstAvWait * self) self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; self->last_seen_video_running_time = GST_CLOCK_TIME_NONE; + self->first_audio_running_time = GST_CLOCK_TIME_NONE; self->last_seen_tc = NULL; self->video_eos_flag = FALSE; + self->audio_eos_flag = FALSE; + self->video_flush_flag = FALSE; self->audio_flush_flag = FALSE; self->shutdown_flag = FALSE; self->dropping = TRUE; @@ -281,6 +284,7 @@ gst_avwait_init (GstAvWait * self) gst_video_info_init (&self->vinfo); g_mutex_init (&self->mutex); g_cond_init (&self->cond); + g_cond_init (&self->audio_cond); } static void @@ -308,12 +312,15 @@ gst_avwait_change_state (GstElement * element, GstStateChange transition) g_mutex_lock (&self->mutex); self->shutdown_flag = TRUE; g_cond_signal (&self->cond); + g_cond_signal (&self->audio_cond); g_mutex_unlock (&self->mutex); break; case GST_STATE_CHANGE_READY_TO_PAUSED: g_mutex_lock (&self->mutex); self->shutdown_flag = FALSE; self->video_eos_flag = FALSE; + self->audio_eos_flag = FALSE; + self->video_flush_flag = FALSE; self->audio_flush_flag = FALSE; g_mutex_unlock (&self->mutex); default: @@ -342,6 +349,7 @@ gst_avwait_change_state (GstElement * element, GstStateChange transition) self->vsegment.position = GST_CLOCK_TIME_NONE; gst_video_info_init (&self->vinfo); self->last_seen_video_running_time = GST_CLOCK_TIME_NONE; + self->first_audio_running_time = GST_CLOCK_TIME_NONE; if (self->last_seen_tc) gst_video_time_code_free (self->last_seen_tc); self->last_seen_tc = NULL; @@ -371,6 +379,7 @@ gst_avwait_finalize (GObject * object) g_mutex_clear (&self->mutex); g_cond_clear (&self->cond); + g_cond_clear (&self->audio_cond); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -613,8 +622,15 @@ gst_avwait_vsink_event (GstPad * pad, GstObject * parent, GstEvent * event) g_cond_signal (&self->cond); g_mutex_unlock (&self->mutex); break; + case GST_EVENT_FLUSH_START: + g_mutex_lock (&self->mutex); + self->video_flush_flag = TRUE; + g_cond_signal (&self->audio_cond); + g_mutex_unlock (&self->mutex); + break; case GST_EVENT_FLUSH_STOP: g_mutex_lock (&self->mutex); + self->video_flush_flag = FALSE; if (self->mode != MODE_RUNNING_TIME) { GST_DEBUG_OBJECT (self, "First time reset in video flush"); self->running_time_to_wait_for = GST_CLOCK_TIME_NONE; @@ -681,6 +697,12 @@ gst_avwait_asink_event (GstPad * pad, GstObject * parent, GstEvent * event) g_cond_signal (&self->cond); g_mutex_unlock (&self->mutex); break; + case GST_EVENT_EOS: + g_mutex_lock (&self->mutex); + self->audio_eos_flag = TRUE; + g_cond_signal (&self->audio_cond); + g_mutex_unlock (&self->mutex); + break; case GST_EVENT_FLUSH_STOP: g_mutex_lock (&self->mutex); self->audio_flush_flag = FALSE; @@ -715,6 +737,7 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) GstClockTime running_time; GstVideoTimeCode *tc = NULL; GstVideoTimeCodeMeta *tc_meta; + gboolean retry = FALSE; timestamp = GST_BUFFER_TIMESTAMP (inbuf); if (timestamp == GST_CLOCK_TIME_NONE) { @@ -736,6 +759,18 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) } self->last_seen_tc = tc; } + while (self->mode == MODE_VIDEO_FIRST + && self->first_audio_running_time == GST_CLOCK_TIME_NONE + && !self->audio_eos_flag + && !self->shutdown_flag && !self->video_flush_flag) { + g_cond_wait (&self->audio_cond, &self->mutex); + } + if (self->video_flush_flag || self->shutdown_flag) { + GST_DEBUG_OBJECT (self, "Shutting down, ignoring buffer"); + gst_buffer_unref (inbuf); + g_mutex_unlock (&self->mutex); + return GST_FLOW_FLUSHING; + } switch (self->mode) { case MODE_TIMECODE:{ if (self->tc != NULL && tc != NULL) { @@ -850,25 +885,40 @@ gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) "Recording started at %" GST_TIME_FORMAT " waiting for %" GST_TIME_FORMAT " inbuf %p", GST_TIME_ARGS (running_time), GST_TIME_ARGS (self->running_time_to_wait_for), inbuf); - if (running_time < self->running_time_to_end_at || - self->running_time_to_end_at == GST_CLOCK_TIME_NONE) { - /* We are before the end of the recording. Check if we just actually - * started */ - if (running_time > self->running_time_to_wait_for) { - /* We just started recording: synchronise the audio */ - self->audio_running_time_to_wait_for = running_time; - gst_avwait_send_element_message (self, FALSE, running_time); - } else { - /* We will start in the future when running_time_to_wait_for is - * reached */ - self->audio_running_time_to_wait_for = self->running_time_to_wait_for; + if (self->mode != MODE_VIDEO_FIRST || + self->first_audio_running_time <= running_time || + self->audio_eos_flag) { + if (running_time < self->running_time_to_end_at || + self->running_time_to_end_at == GST_CLOCK_TIME_NONE) { + /* We are before the end of the recording. Check if we just actually + * started */ + if (running_time > self->running_time_to_wait_for) { + /* We just started recording: synchronise the audio */ + self->audio_running_time_to_wait_for = running_time; + gst_avwait_send_element_message (self, FALSE, running_time); + } else { + /* We will start in the future when running_time_to_wait_for is + * reached */ + self->audio_running_time_to_wait_for = + self->running_time_to_wait_for; + } + self->audio_running_time_to_end_at = self->running_time_to_end_at; } - self->audio_running_time_to_end_at = self->running_time_to_end_at; + } else { + /* We are in video-first mode and behind the first audio timestamp. We + * should drop all video buffers until the first audio timestamp, so + * we can catch up with it. (In timecode mode and running-time mode, we + * don't care about when the audio starts, we start as soon as the + * target timecode or running time has been reached) */ + gst_buffer_unref (inbuf); + inbuf = NULL; + retry = TRUE; } } } - self->was_recording = self->recording; + if (!retry) + self->was_recording = self->recording; g_cond_signal (&self->cond); g_mutex_unlock (&self->mutex); if (inbuf) @@ -922,6 +972,10 @@ gst_avwait_asink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf) GST_ERROR_OBJECT (self, "Could not get current running time"); return GST_FLOW_ERROR; } + if (self->first_audio_running_time == GST_CLOCK_TIME_NONE) { + self->first_audio_running_time = current_running_time; + } + g_cond_signal (&self->audio_cond); if (self->vsegment.format == GST_FORMAT_TIME) { vsign = gst_segment_to_running_time_full (&self->vsegment, GST_FORMAT_TIME, diff --git a/gst/timecode/gstavwait.h b/gst/timecode/gstavwait.h index 48e6ea3b1..0e1df64af 100644 --- a/gst/timecode/gstavwait.h +++ b/gst/timecode/gstavwait.h @@ -63,6 +63,7 @@ struct _GstAvWait GstClockTime running_time_to_wait_for; GstClockTime last_seen_video_running_time; + GstClockTime first_audio_running_time; GstVideoTimeCode *last_seen_tc; /* If running_time_to_wait_for has been reached but we are @@ -75,6 +76,8 @@ struct _GstAvWait GstClockTime audio_running_time_to_end_at; gboolean video_eos_flag; + gboolean audio_eos_flag; + gboolean video_flush_flag; gboolean audio_flush_flag; gboolean shutdown_flag; @@ -84,6 +87,7 @@ struct _GstAvWait GCond cond; GMutex mutex; + GCond audio_cond; }; struct _GstAvWaitClass diff --git a/tests/check/elements/avwait.c b/tests/check/elements/avwait.c index 6855e20e8..829fd96ed 100644 --- a/tests/check/elements/avwait.c +++ b/tests/check/elements/avwait.c @@ -40,6 +40,7 @@ static GstVideoTimeCode *end_tc; static GstClockTime target_running_time; static gboolean recording; static gint mode; +static gboolean audio_late; static GstAudioInfo ainfo; @@ -54,6 +55,12 @@ typedef struct _ElementPadAndSwitchType SwitchType switch_after_2s; } ElementPadAndSwitchType; +typedef struct _PadAndBoolean +{ + GstPad *pad; + gboolean b; +} PadAndBoolean; + static void set_default_params (void) { @@ -65,6 +72,7 @@ set_default_params (void) target_running_time = GST_CLOCK_TIME_NONE; recording = TRUE; mode = 2; + audio_late = FALSE; first_audio_timestamp = GST_CLOCK_TIME_NONE; last_audio_timestamp = GST_CLOCK_TIME_NONE; @@ -114,12 +122,20 @@ static gpointer push_abuffers (gpointer data) { GstSegment segment; - GstPad *pad = data; gint i; - GstClockTime timestamp = 0; GstCaps *caps; guint buf_size = 1000; guint channels = 2; + PadAndBoolean *e = data; + GstPad *pad = e->pad; + gboolean audio_late = e->b; + GstClockTime timestamp; + + if (audio_late) { + timestamp = 50 * GST_MSECOND; + } else { + timestamp = 0; + } gst_pad_send_event (pad, gst_event_new_stream_start ("test")); @@ -195,6 +211,7 @@ test_avwait_generic (void) GThread *athread, *vthread; GstBus *bus; ElementPadAndSwitchType *e; + PadAndBoolean *pb; audio_buffer_count = 0; video_buffer_count = 0; @@ -239,8 +256,11 @@ test_avwait_generic (void) e->element = avwait; e->pad = vsink; e->switch_after_2s = switch_after_2s; + pb = g_new0 (PadAndBoolean, 1); + pb->pad = asink; + pb->b = audio_late; - athread = g_thread_new ("athread", (GThreadFunc) push_abuffers, asink); + athread = g_thread_new ("athread", (GThreadFunc) push_abuffers, pb); vthread = g_thread_new ("vthread", (GThreadFunc) push_vbuffers, e); g_thread_join (vthread); @@ -251,6 +271,7 @@ test_avwait_generic (void) gst_bus_set_flushing (bus, TRUE); gst_object_unref (bus); g_free (e); + g_free (pb); gst_pad_unlink (asrc, aoutput_sink); gst_object_unref (asrc); gst_pad_unlink (vsrc, voutput_sink); @@ -282,7 +303,7 @@ GST_START_TEST (test_avwait_switch_to_false) recording = TRUE; switch_after_2s = SWITCH_FALSE; test_avwait_generic (); - fail_unless_equals_uint64 (first_audio_timestamp, 0); + fail_unless_equals_uint64 (first_audio_timestamp, first_video_timestamp); fail_unless_equals_uint64 (first_video_timestamp, 0); fail_unless_equals_uint64 (last_video_timestamp, 2 * GST_SECOND); fail_unless_equals_uint64 (last_audio_timestamp, 2 * GST_SECOND); @@ -426,6 +447,17 @@ GST_START_TEST (test_avwait_3stc_switch_to_false) GST_END_TEST; +GST_START_TEST (test_avwait_audio_late) +{ + set_default_params (); + recording = TRUE; + audio_late = TRUE; + test_avwait_generic (); + fail_unless_equals_uint64 (first_audio_timestamp, 50 * GST_MSECOND); + fail_unless_equals_uint64 (first_video_timestamp, 50 * GST_MSECOND); +} + +GST_END_TEST; static Suite * avwait_suite (void) @@ -444,6 +476,7 @@ avwait_suite (void) tcase_add_test (tc_chain, test_avwait_1stc_switch_to_false); tcase_add_test (tc_chain, test_avwait_3stc_switch_to_true); tcase_add_test (tc_chain, test_avwait_3stc_switch_to_false); + tcase_add_test (tc_chain, test_avwait_audio_late); suite_add_tcase (s, tc_chain); return s; diff --git a/tests/check/meson.build b/tests/check/meson.build index bff5c8680..11d16a401 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -21,6 +21,7 @@ base_tests = [ [['elements/assrender.c'], not ass_dep.found(), [ass_dep]], [['elements/autoconvert.c']], [['elements/autovideoconvert.c']], + [['elements/avwait.c']], [['elements/camerabin.c']], [['elements/compositor.c']], [['elements/curlhttpsink.c'], not curl_dep.found(), [curl_dep]], |