3 * Copyright (C) 2016 Vivia Nikolaidou <vivia@toolsonair.com>
5 * Based on gstvideoframe-audiolevel.c:
6 * Copyright (C) 2015 Vivia Nikolaidou <vivia@toolsonair.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 * SECTION:element-avwait
28 * This element will drop all buffers until a specific timecode or running
29 * time has been reached. It will then pass-through both audio and video,
30 * starting from that specific timecode or running time, making sure that
31 * audio starts as early as possible after the video (or at the same time as
32 * the video). In the "video-first" mode, it only drops audio buffers until
35 * The "recording" property acts essentially like a valve connected before
36 * everything else. If recording is FALSE, all buffers are dropped regardless
37 * of settings. If recording is TRUE, the other settings (mode,
38 * target-timecode, target-running-time, etc) are taken into account. Audio
39 * will always start and end together with the video, as long as the stream
40 * itself doesn't start too late or end too early.
42 * ## Example launch line
44 * gst-launch-1.0 filesrc location="my_file" ! decodebin name=d ! "audio/x-raw" ! avwait name=l target-timecode-str="00:00:04:00" ! autoaudiosink d. ! "video/x-raw" ! timecodestamper ! l. l. ! queue ! timeoverlay time-mode=time-code ! autovideosink
52 #include "gstavwait.h"
54 #define GST_CAT_DEFAULT gst_avwait_debug
55 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
57 static GstStaticPadTemplate audio_sink_template =
58 GST_STATIC_PAD_TEMPLATE ("asink",
61 GST_STATIC_CAPS ("audio/x-raw")
64 static GstStaticPadTemplate audio_src_template =
65 GST_STATIC_PAD_TEMPLATE ("asrc",
68 GST_STATIC_CAPS ("audio/x-raw")
71 static GstStaticPadTemplate video_sink_template =
72 GST_STATIC_PAD_TEMPLATE ("vsink",
75 GST_STATIC_CAPS ("video/x-raw")
78 static GstStaticPadTemplate video_src_template =
79 GST_STATIC_PAD_TEMPLATE ("vsrc",
82 GST_STATIC_CAPS ("video/x-raw")
85 #define parent_class gst_avwait_parent_class
86 G_DEFINE_TYPE (GstAvWait, gst_avwait, GST_TYPE_ELEMENT);
91 PROP_TARGET_TIME_CODE,
92 PROP_TARGET_TIME_CODE_STRING,
93 PROP_TARGET_RUNNING_TIME,
99 #define DEFAULT_TARGET_TIMECODE_STR "00:00:00:00"
100 #define DEFAULT_TARGET_RUNNING_TIME GST_CLOCK_TIME_NONE
101 #define DEFAULT_MODE MODE_TIMECODE
103 /* flags for self->must_send_end_message */
106 END_MESSAGE_NORMAL = 0,
107 END_MESSAGE_STREAM_ENDED = 1,
108 END_MESSAGE_VIDEO_PUSHED = 2,
109 END_MESSAGE_AUDIO_PUSHED = 4
112 static void gst_avwait_set_property (GObject * object,
113 guint prop_id, const GValue * value, GParamSpec * pspec);
114 static void gst_avwait_get_property (GObject * object,
115 guint prop_id, GValue * value, GParamSpec * pspec);
117 static GstFlowReturn gst_avwait_asink_chain (GstPad * pad,
118 GstObject * parent, GstBuffer * inbuf);
119 static GstFlowReturn gst_avwait_vsink_chain (GstPad * pad,
120 GstObject * parent, GstBuffer * inbuf);
121 static gboolean gst_avwait_asink_event (GstPad * pad,
122 GstObject * parent, GstEvent * event);
123 static gboolean gst_avwait_vsink_event (GstPad * pad,
124 GstObject * parent, GstEvent * event);
125 static GstIterator *gst_avwait_iterate_internal_links (GstPad *
126 pad, GstObject * parent);
128 static void gst_avwait_finalize (GObject * gobject);
130 static GstStateChangeReturn gst_avwait_change_state (GstElement *
131 element, GstStateChange transition);
134 gst_avwait_mode_get_type (void)
136 static GType gtype = 0;
139 static const GEnumValue values[] = {
140 {MODE_TIMECODE, "time code (default)", "timecode"},
141 {MODE_RUNNING_TIME, "running time", "running-time"},
142 {MODE_VIDEO_FIRST, "video first", "video-first"},
146 gtype = g_enum_register_static ("GstAvWaitMode", values);
152 gst_avwait_class_init (GstAvWaitClass * klass)
154 GstElementClass *gstelement_class;
155 GObjectClass *gobject_class = (GObjectClass *) klass;
157 GST_DEBUG_CATEGORY_INIT (gst_avwait_debug, "avwait", 0, "avwait");
159 gstelement_class = (GstElementClass *) klass;
161 gst_element_class_set_static_metadata (gstelement_class,
162 "Timecode Wait", "Filter/Audio/Video",
163 "Drops all audio/video until a specific timecode or running time has been reached",
164 "Vivia Nikolaidou <vivia@toolsonair.com>");
166 gobject_class->set_property = gst_avwait_set_property;
167 gobject_class->get_property = gst_avwait_get_property;
169 g_object_class_install_property (gobject_class, PROP_TARGET_TIME_CODE_STRING,
170 g_param_spec_string ("target-timecode-string", "Target timecode (string)",
171 "Timecode to wait for in timecode mode (string). Must take the form 00:00:00:00",
172 DEFAULT_TARGET_TIMECODE_STR,
173 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
175 g_object_class_install_property (gobject_class, PROP_TARGET_TIME_CODE,
176 g_param_spec_boxed ("target-timecode", "Target timecode (object)",
177 "Timecode to wait for in timecode mode (object)",
178 GST_TYPE_VIDEO_TIME_CODE,
179 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
180 G_PARAM_STATIC_STRINGS));
182 g_object_class_install_property (gobject_class, PROP_TARGET_RUNNING_TIME,
183 g_param_spec_uint64 ("target-running-time", "Target running time",
184 "Running time to wait for in running-time mode",
186 DEFAULT_TARGET_RUNNING_TIME,
187 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
188 G_PARAM_STATIC_STRINGS));
190 g_object_class_install_property (gobject_class, PROP_MODE,
191 g_param_spec_enum ("mode", "Mode",
192 "Operation mode: What to wait for",
193 GST_TYPE_AVWAIT_MODE,
195 GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
196 G_PARAM_STATIC_STRINGS));
198 g_object_class_install_property (gobject_class, PROP_END_TIME_CODE,
199 g_param_spec_boxed ("end-timecode", "End timecode (object)",
200 "Timecode to end at in timecode mode (object)",
201 GST_TYPE_VIDEO_TIME_CODE,
202 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204 g_object_class_install_property (gobject_class, PROP_RECORDING,
205 g_param_spec_boolean ("recording",
207 "Whether the element is stopped or recording. "
208 "If set to FALSE, all buffers will be dropped regardless of settings.",
209 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
211 gobject_class->finalize = gst_avwait_finalize;
212 gstelement_class->change_state = gst_avwait_change_state;
214 gst_element_class_add_static_pad_template (gstelement_class,
215 &audio_src_template);
216 gst_element_class_add_static_pad_template (gstelement_class,
217 &audio_sink_template);
219 gst_element_class_add_static_pad_template (gstelement_class,
220 &video_src_template);
221 gst_element_class_add_static_pad_template (gstelement_class,
222 &video_sink_template);
226 gst_avwait_init (GstAvWait * self)
229 gst_pad_new_from_static_template (&audio_sink_template, "asink");
230 gst_pad_set_chain_function (self->asinkpad,
231 GST_DEBUG_FUNCPTR (gst_avwait_asink_chain));
232 gst_pad_set_event_function (self->asinkpad,
233 GST_DEBUG_FUNCPTR (gst_avwait_asink_event));
234 gst_pad_set_iterate_internal_links_function (self->asinkpad,
235 GST_DEBUG_FUNCPTR (gst_avwait_iterate_internal_links));
236 gst_element_add_pad (GST_ELEMENT (self), self->asinkpad);
239 gst_pad_new_from_static_template (&video_sink_template, "vsink");
240 gst_pad_set_chain_function (self->vsinkpad,
241 GST_DEBUG_FUNCPTR (gst_avwait_vsink_chain));
242 gst_pad_set_event_function (self->vsinkpad,
243 GST_DEBUG_FUNCPTR (gst_avwait_vsink_event));
244 gst_pad_set_iterate_internal_links_function (self->vsinkpad,
245 GST_DEBUG_FUNCPTR (gst_avwait_iterate_internal_links));
246 gst_element_add_pad (GST_ELEMENT (self), self->vsinkpad);
249 gst_pad_new_from_static_template (&audio_src_template, "asrc");
250 gst_pad_set_iterate_internal_links_function (self->asrcpad,
251 GST_DEBUG_FUNCPTR (gst_avwait_iterate_internal_links));
252 gst_element_add_pad (GST_ELEMENT (self), self->asrcpad);
255 gst_pad_new_from_static_template (&video_src_template, "vsrc");
256 gst_pad_set_iterate_internal_links_function (self->vsrcpad,
257 GST_DEBUG_FUNCPTR (gst_avwait_iterate_internal_links));
258 gst_element_add_pad (GST_ELEMENT (self), self->vsrcpad);
260 GST_PAD_SET_PROXY_CAPS (self->asinkpad);
261 GST_PAD_SET_PROXY_ALLOCATION (self->asinkpad);
263 GST_PAD_SET_PROXY_CAPS (self->asrcpad);
264 GST_PAD_SET_PROXY_SCHEDULING (self->asrcpad);
266 GST_PAD_SET_PROXY_CAPS (self->vsinkpad);
267 GST_PAD_SET_PROXY_ALLOCATION (self->vsinkpad);
269 GST_PAD_SET_PROXY_CAPS (self->vsrcpad);
270 GST_PAD_SET_PROXY_SCHEDULING (self->vsrcpad);
272 self->running_time_to_wait_for = GST_CLOCK_TIME_NONE;
273 self->last_seen_video_running_time = GST_CLOCK_TIME_NONE;
274 self->first_audio_running_time = GST_CLOCK_TIME_NONE;
275 self->last_seen_tc = NULL;
277 self->video_eos_flag = FALSE;
278 self->audio_eos_flag = FALSE;
279 self->video_flush_flag = FALSE;
280 self->audio_flush_flag = FALSE;
281 self->shutdown_flag = FALSE;
282 self->dropping = TRUE;
283 self->tc = gst_video_time_code_new_empty ();
285 self->running_time_to_end_at = GST_CLOCK_TIME_NONE;
286 self->audio_running_time_to_wait_for = GST_CLOCK_TIME_NONE;
287 self->audio_running_time_to_end_at = GST_CLOCK_TIME_NONE;
288 self->recording = TRUE;
290 self->target_running_time = DEFAULT_TARGET_RUNNING_TIME;
291 self->mode = DEFAULT_MODE;
293 gst_video_info_init (&self->vinfo);
294 g_mutex_init (&self->mutex);
295 g_cond_init (&self->cond);
296 g_cond_init (&self->audio_cond);
300 gst_avwait_send_element_message (GstAvWait * self, gboolean dropping,
301 GstClockTime running_time)
303 if (!gst_element_post_message (GST_ELEMENT (self),
304 gst_message_new_element (GST_OBJECT (self),
305 gst_structure_new ("avwait-status",
306 "dropping", G_TYPE_BOOLEAN, dropping,
307 "running-time", GST_TYPE_CLOCK_TIME, running_time, NULL)))) {
308 GST_ERROR_OBJECT (self, "Unable to send element message!");
309 g_assert_not_reached ();
313 static GstStateChangeReturn
314 gst_avwait_change_state (GstElement * element, GstStateChange transition)
316 GstStateChangeReturn ret;
317 GstAvWait *self = GST_AVWAIT (element);
319 switch (transition) {
320 case GST_STATE_CHANGE_PAUSED_TO_READY:
321 g_mutex_lock (&self->mutex);
322 self->shutdown_flag = TRUE;
323 g_cond_signal (&self->cond);
324 g_cond_signal (&self->audio_cond);
325 g_mutex_unlock (&self->mutex);
327 case GST_STATE_CHANGE_READY_TO_PAUSED:
328 g_mutex_lock (&self->mutex);
329 self->shutdown_flag = FALSE;
330 self->video_eos_flag = FALSE;
331 self->audio_eos_flag = FALSE;
332 self->video_flush_flag = FALSE;
333 self->audio_flush_flag = FALSE;
334 self->must_send_end_message = END_MESSAGE_NORMAL;
335 g_mutex_unlock (&self->mutex);
340 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
342 switch (transition) {
343 case GST_STATE_CHANGE_PAUSED_TO_READY:
344 g_mutex_lock (&self->mutex);
345 if (self->mode != MODE_RUNNING_TIME) {
346 GST_DEBUG_OBJECT (self, "First time reset in paused to ready");
347 self->running_time_to_wait_for = GST_CLOCK_TIME_NONE;
348 self->running_time_to_end_at = GST_CLOCK_TIME_NONE;
349 self->audio_running_time_to_wait_for = GST_CLOCK_TIME_NONE;
350 self->audio_running_time_to_end_at = GST_CLOCK_TIME_NONE;
352 if (!self->dropping) {
353 self->dropping = TRUE;
354 gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE);
356 gst_segment_init (&self->asegment, GST_FORMAT_UNDEFINED);
357 self->asegment.position = GST_CLOCK_TIME_NONE;
358 gst_segment_init (&self->vsegment, GST_FORMAT_UNDEFINED);
359 self->vsegment.position = GST_CLOCK_TIME_NONE;
360 gst_video_info_init (&self->vinfo);
361 self->last_seen_video_running_time = GST_CLOCK_TIME_NONE;
362 self->first_audio_running_time = GST_CLOCK_TIME_NONE;
363 if (self->last_seen_tc)
364 gst_video_time_code_free (self->last_seen_tc);
365 self->last_seen_tc = NULL;
366 g_mutex_unlock (&self->mutex);
376 gst_avwait_finalize (GObject * object)
378 GstAvWait *self = GST_AVWAIT (object);
381 gst_video_time_code_free (self->tc);
386 gst_video_time_code_free (self->end_tc);
390 g_mutex_clear (&self->mutex);
391 g_cond_clear (&self->cond);
392 g_cond_clear (&self->audio_cond);
394 G_OBJECT_CLASS (parent_class)->finalize (object);
398 gst_avwait_get_property (GObject * object, guint prop_id,
399 GValue * value, GParamSpec * pspec)
401 GstAvWait *self = GST_AVWAIT (object);
404 case PROP_TARGET_TIME_CODE_STRING:{
406 g_value_take_string (value, gst_video_time_code_to_string (self->tc));
408 g_value_set_string (value, DEFAULT_TARGET_TIMECODE_STR);
411 case PROP_TARGET_TIME_CODE:{
412 g_value_set_boxed (value, self->tc);
415 case PROP_END_TIME_CODE:{
416 g_value_set_boxed (value, self->end_tc);
419 case PROP_TARGET_RUNNING_TIME:{
420 g_value_set_uint64 (value, self->target_running_time);
423 case PROP_RECORDING:{
424 g_value_set_boolean (value, self->recording);
428 g_value_set_enum (value, self->mode);
432 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
438 gst_avwait_set_property (GObject * object, guint prop_id,
439 const GValue * value, GParamSpec * pspec)
441 GstAvWait *self = GST_AVWAIT (object);
444 case PROP_TARGET_TIME_CODE_STRING:{
447 guint hours, minutes, seconds, frames;
449 tc_str = g_value_get_string (value);
450 parts = g_strsplit (tc_str, ":", 4);
451 if (!parts || parts[3] == NULL) {
452 GST_ERROR_OBJECT (self,
453 "Error: Could not parse timecode %s. Please input a timecode in the form 00:00:00:00",
458 hours = g_ascii_strtoll (parts[0], NULL, 10);
459 minutes = g_ascii_strtoll (parts[1], NULL, 10);
460 seconds = g_ascii_strtoll (parts[2], NULL, 10);
461 frames = g_ascii_strtoll (parts[3], NULL, 10);
462 gst_video_time_code_init (self->tc, 0, 1, NULL, 0, hours, minutes,
465 && gst_video_time_code_compare (self->tc, self->end_tc) != -1) {
468 end_tc = gst_video_time_code_to_string (self->end_tc);
470 ("ERROR: End timecode %s must be after start timecode %s. Start timecode rejected",
472 gst_video_time_code_free (self->tc);
474 self->tc = gst_video_time_code_new_empty ();
476 if (GST_VIDEO_INFO_FORMAT (&self->vinfo) != GST_VIDEO_FORMAT_UNKNOWN
477 && self->vinfo.fps_n != 0) {
478 self->tc->config.fps_n = self->vinfo.fps_n;
479 self->tc->config.fps_d = self->vinfo.fps_d;
485 case PROP_TARGET_TIME_CODE:{
487 gst_video_time_code_free (self->tc);
488 self->tc = g_value_dup_boxed (value);
490 && gst_video_time_code_compare (self->tc, self->end_tc) != -1) {
491 gchar *start_tc, *end_tc;
493 start_tc = gst_video_time_code_to_string (self->tc);
494 end_tc = gst_video_time_code_to_string (self->end_tc);
496 ("ERROR: End timecode %s must be after start timecode %s. Start timecode rejected",
498 gst_video_time_code_free (self->tc);
501 self->tc = gst_video_time_code_new_empty ();
503 if (self->tc->config.fps_n == 0
504 && GST_VIDEO_INFO_FORMAT (&self->vinfo) !=
505 GST_VIDEO_FORMAT_UNKNOWN && self->vinfo.fps_n != 0) {
506 self->tc->config.fps_n = self->vinfo.fps_n;
507 self->tc->config.fps_d = self->vinfo.fps_d;
512 case PROP_END_TIME_CODE:{
514 gst_video_time_code_free (self->end_tc);
515 self->end_tc = g_value_dup_boxed (value);
516 if (self->tc && self->end_tc
517 && gst_video_time_code_compare (self->tc, self->end_tc) != -1) {
518 gchar *start_tc, *end_tc;
520 start_tc = gst_video_time_code_to_string (self->tc);
521 end_tc = gst_video_time_code_to_string (self->end_tc);
523 ("ERROR: End timecode %s must be after start timecode %s. End timecode rejected",
525 gst_video_time_code_free (self->end_tc);
529 } else if (self->end_tc) {
530 if (self->end_tc->config.fps_n == 0
531 && GST_VIDEO_INFO_FORMAT (&self->vinfo) !=
532 GST_VIDEO_FORMAT_UNKNOWN && self->vinfo.fps_n != 0) {
533 self->end_tc->config.fps_n = self->vinfo.fps_n;
534 self->end_tc->config.fps_d = self->vinfo.fps_d;
539 case PROP_TARGET_RUNNING_TIME:{
540 self->target_running_time = g_value_get_uint64 (value);
541 if (self->mode == MODE_RUNNING_TIME) {
542 self->running_time_to_wait_for = self->target_running_time;
543 if (self->recording) {
544 self->audio_running_time_to_wait_for = self->running_time_to_wait_for;
546 if (self->target_running_time < self->last_seen_video_running_time) {
547 self->dropping = TRUE;
553 GstAvWaitMode old_mode = self->mode;
554 self->mode = g_value_get_enum (value);
555 if (self->mode != old_mode) {
556 switch (self->mode) {
558 if (self->last_seen_tc && self->tc &&
559 gst_video_time_code_compare (self->last_seen_tc,
561 self->running_time_to_wait_for = GST_CLOCK_TIME_NONE;
562 self->dropping = TRUE;
565 case MODE_RUNNING_TIME:
566 self->running_time_to_wait_for = self->target_running_time;
567 if (self->recording) {
568 self->audio_running_time_to_wait_for =
569 self->running_time_to_wait_for;
571 if (self->target_running_time < self->last_seen_video_running_time) {
572 self->dropping = TRUE;
575 /* Let the chain functions handle the rest */
576 case MODE_VIDEO_FIRST:
584 case PROP_RECORDING:{
585 g_mutex_lock (&self->mutex);
586 self->recording = g_value_get_boolean (value);
587 g_mutex_unlock (&self->mutex);
591 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
597 gst_avwait_vsink_event (GstPad * pad, GstObject * parent, GstEvent * event)
599 GstAvWait *self = GST_AVWAIT (parent);
600 GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
602 switch (GST_EVENT_TYPE (event)) {
603 case GST_EVENT_SEGMENT:
604 g_mutex_lock (&self->mutex);
605 gst_event_copy_segment (event, &self->vsegment);
606 if (self->vsegment.format != GST_FORMAT_TIME) {
607 GST_ERROR_OBJECT (self, "Invalid segment format");
608 g_mutex_unlock (&self->mutex);
609 gst_event_unref (event);
612 if (self->mode != MODE_RUNNING_TIME) {
613 GST_DEBUG_OBJECT (self, "First time reset in video segment");
614 self->running_time_to_wait_for = GST_CLOCK_TIME_NONE;
615 self->running_time_to_end_at = GST_CLOCK_TIME_NONE;
616 self->audio_running_time_to_wait_for = GST_CLOCK_TIME_NONE;
617 self->audio_running_time_to_end_at = GST_CLOCK_TIME_NONE;
618 if (!self->dropping) {
619 self->dropping = TRUE;
620 gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE);
623 self->vsegment.position = GST_CLOCK_TIME_NONE;
624 g_mutex_unlock (&self->mutex);
627 gst_event_unref (event);
630 g_mutex_lock (&self->mutex);
631 self->video_eos_flag = TRUE;
632 g_cond_signal (&self->cond);
633 g_mutex_unlock (&self->mutex);
635 case GST_EVENT_FLUSH_START:
636 g_mutex_lock (&self->mutex);
637 self->video_flush_flag = TRUE;
638 g_cond_signal (&self->audio_cond);
639 g_mutex_unlock (&self->mutex);
641 case GST_EVENT_FLUSH_STOP:
642 g_mutex_lock (&self->mutex);
643 self->video_flush_flag = FALSE;
644 if (self->mode != MODE_RUNNING_TIME) {
645 GST_DEBUG_OBJECT (self, "First time reset in video flush");
646 self->running_time_to_wait_for = GST_CLOCK_TIME_NONE;
647 self->running_time_to_end_at = GST_CLOCK_TIME_NONE;
648 self->audio_running_time_to_wait_for = GST_CLOCK_TIME_NONE;
649 self->audio_running_time_to_end_at = GST_CLOCK_TIME_NONE;
650 if (!self->dropping) {
651 self->dropping = TRUE;
652 gst_avwait_send_element_message (self, TRUE, GST_CLOCK_TIME_NONE);
655 gst_segment_init (&self->vsegment, GST_FORMAT_UNDEFINED);
656 self->vsegment.position = GST_CLOCK_TIME_NONE;
657 g_mutex_unlock (&self->mutex);
659 case GST_EVENT_CAPS:{
661 gst_event_parse_caps (event, &caps);
662 GST_DEBUG_OBJECT (self, "Got caps %" GST_PTR_FORMAT, caps);
663 if (!gst_video_info_from_caps (&self->vinfo, caps)) {
664 gst_event_unref (event);
667 g_mutex_lock (&self->mutex);
668 if (self->tc && self->tc->config.fps_n == 0 && self->vinfo.fps_n != 0) {
669 self->tc->config.fps_n = self->vinfo.fps_n;
670 self->tc->config.fps_d = self->vinfo.fps_d;
672 if (self->end_tc && self->end_tc->config.fps_n == 0
673 && self->vinfo.fps_n != 0) {
674 self->end_tc->config.fps_n = self->vinfo.fps_n;
675 self->end_tc->config.fps_d = self->vinfo.fps_d;
677 g_mutex_unlock (&self->mutex);
683 return gst_pad_event_default (pad, parent, event);
687 gst_avwait_asink_event (GstPad * pad, GstObject * parent, GstEvent * event)
689 GstAvWait *self = GST_AVWAIT (parent);
690 GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event));
692 switch (GST_EVENT_TYPE (event)) {
693 case GST_EVENT_SEGMENT:
694 g_mutex_lock (&self->mutex);
695 gst_event_copy_segment (event, &self->asegment);
696 if (self->asegment.format != GST_FORMAT_TIME) {
697 GST_ERROR_OBJECT (self, "Invalid segment format");
698 g_mutex_unlock (&self->mutex);
701 self->asegment.position = GST_CLOCK_TIME_NONE;
702 g_mutex_unlock (&self->mutex);
704 case GST_EVENT_FLUSH_START:
705 g_mutex_lock (&self->mutex);
706 self->audio_flush_flag = TRUE;
707 g_cond_signal (&self->cond);
708 g_mutex_unlock (&self->mutex);
711 g_mutex_lock (&self->mutex);
712 self->audio_eos_flag = TRUE;
713 self->must_send_end_message = END_MESSAGE_NORMAL;
714 g_cond_signal (&self->audio_cond);
715 g_mutex_unlock (&self->mutex);
717 case GST_EVENT_FLUSH_STOP:
718 g_mutex_lock (&self->mutex);
719 self->audio_flush_flag = FALSE;
720 gst_segment_init (&self->asegment, GST_FORMAT_UNDEFINED);
721 self->asegment.position = GST_CLOCK_TIME_NONE;
722 g_mutex_unlock (&self->mutex);
724 case GST_EVENT_CAPS:{
726 gst_event_parse_caps (event, &caps);
727 GST_DEBUG_OBJECT (self, "Got caps %" GST_PTR_FORMAT, caps);
728 g_mutex_lock (&self->mutex);
729 if (!gst_audio_info_from_caps (&self->ainfo, caps)) {
730 g_mutex_unlock (&self->mutex);
733 g_mutex_unlock (&self->mutex);
740 return gst_pad_event_default (pad, parent, event);
744 gst_avwait_vsink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf)
746 GstClockTime timestamp;
747 GstAvWait *self = GST_AVWAIT (parent);
748 GstClockTime running_time;
749 GstVideoTimeCode *tc = NULL;
750 GstVideoTimeCodeMeta *tc_meta;
751 gboolean retry = FALSE;
752 gboolean ret = GST_FLOW_OK;
754 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
755 if (timestamp == GST_CLOCK_TIME_NONE) {
756 gst_buffer_unref (inbuf);
757 return GST_FLOW_ERROR;
759 g_mutex_lock (&self->mutex);
760 self->vsegment.position = timestamp;
762 gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME,
763 self->vsegment.position);
764 self->last_seen_video_running_time = running_time;
766 tc_meta = gst_buffer_get_video_time_code_meta (inbuf);
768 tc = gst_video_time_code_copy (&tc_meta->tc);
769 if (self->last_seen_tc) {
770 gst_video_time_code_free (self->last_seen_tc);
772 self->last_seen_tc = tc;
774 while (self->mode == MODE_VIDEO_FIRST
775 && self->first_audio_running_time == GST_CLOCK_TIME_NONE
776 && !self->audio_eos_flag
777 && !self->shutdown_flag && !self->video_flush_flag) {
778 g_cond_wait (&self->audio_cond, &self->mutex);
780 if (self->video_flush_flag || self->shutdown_flag) {
781 GST_DEBUG_OBJECT (self, "Shutting down, ignoring buffer");
782 gst_buffer_unref (inbuf);
783 g_mutex_unlock (&self->mutex);
784 return GST_FLOW_FLUSHING;
786 switch (self->mode) {
788 if (self->tc != NULL && tc != NULL) {
789 gboolean emit_passthrough_signal = FALSE;
790 if (gst_video_time_code_compare (tc, self->tc) < 0
791 && self->running_time_to_wait_for == GST_CLOCK_TIME_NONE) {
792 GST_DEBUG_OBJECT (self, "Timecode not yet reached, ignoring frame");
793 gst_buffer_unref (inbuf);
795 } else if (self->running_time_to_wait_for == GST_CLOCK_TIME_NONE) {
796 GST_INFO_OBJECT (self, "Target timecode reached at %" GST_TIME_FORMAT,
797 GST_TIME_ARGS (self->vsegment.position));
798 /* Don't emit a signal if we weren't dropping (e.g. settings changed
800 emit_passthrough_signal = self->dropping;
801 self->dropping = FALSE;
802 self->running_time_to_wait_for =
803 gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME,
804 self->vsegment.position);
805 if (self->recording) {
806 self->audio_running_time_to_wait_for =
807 self->running_time_to_wait_for;
810 if (self->end_tc && gst_video_time_code_compare (tc, self->end_tc) >= 0) {
811 if (self->running_time_to_end_at == GST_CLOCK_TIME_NONE) {
812 GST_INFO_OBJECT (self, "End timecode reached at %" GST_TIME_FORMAT,
813 GST_TIME_ARGS (self->vsegment.position));
814 self->dropping = TRUE;
815 self->running_time_to_end_at =
816 gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME,
817 self->vsegment.position);
818 if (self->recording) {
819 self->audio_running_time_to_end_at = self->running_time_to_end_at;
820 self->must_send_end_message |= END_MESSAGE_STREAM_ENDED;
823 gst_buffer_unref (inbuf);
825 } else if (emit_passthrough_signal && self->recording) {
826 gst_avwait_send_element_message (self, FALSE,
827 self->running_time_to_wait_for);
832 case MODE_RUNNING_TIME:{
833 if (running_time < self->running_time_to_wait_for) {
834 GST_DEBUG_OBJECT (self,
835 "Have %" GST_TIME_FORMAT ", waiting for %" GST_TIME_FORMAT,
836 GST_TIME_ARGS (running_time),
837 GST_TIME_ARGS (self->running_time_to_wait_for));
838 gst_buffer_unref (inbuf);
841 if (self->dropping) {
842 self->dropping = FALSE;
844 gst_avwait_send_element_message (self, FALSE, running_time);
846 GST_INFO_OBJECT (self,
847 "Have %" GST_TIME_FORMAT ", waiting for %" GST_TIME_FORMAT,
848 GST_TIME_ARGS (running_time),
849 GST_TIME_ARGS (self->running_time_to_wait_for));
853 case MODE_VIDEO_FIRST:{
854 if (self->running_time_to_wait_for == GST_CLOCK_TIME_NONE) {
855 self->running_time_to_wait_for =
856 gst_segment_to_running_time (&self->vsegment, GST_FORMAT_TIME,
857 self->vsegment.position);
858 GST_DEBUG_OBJECT (self, "First video running time is %" GST_TIME_FORMAT,
859 GST_TIME_ARGS (self->running_time_to_wait_for));
860 if (self->recording) {
861 self->audio_running_time_to_wait_for = self->running_time_to_wait_for;
863 if (self->dropping) {
864 self->dropping = FALSE;
866 gst_avwait_send_element_message (self, FALSE,
867 self->running_time_to_wait_for);
874 if (!self->recording) {
875 if (self->was_recording) {
876 GST_INFO_OBJECT (self, "Recording stopped at %" GST_TIME_FORMAT,
877 GST_TIME_ARGS (running_time));
878 if (running_time > self->running_time_to_wait_for
879 && running_time <= self->running_time_to_end_at) {
880 /* We just stopped recording: synchronise the audio */
881 self->audio_running_time_to_end_at = running_time;
882 self->must_send_end_message |= END_MESSAGE_STREAM_ENDED;
883 } else if (running_time < self->running_time_to_wait_for
884 && self->running_time_to_wait_for != GST_CLOCK_TIME_NONE) {
885 self->audio_running_time_to_wait_for = GST_CLOCK_TIME_NONE;
888 /* Recording is FALSE: we drop all buffers */
890 gst_buffer_unref (inbuf);
894 if (!self->was_recording) {
895 GST_INFO_OBJECT (self,
896 "Recording started at %" GST_TIME_FORMAT " waiting for %"
897 GST_TIME_FORMAT " inbuf %p", GST_TIME_ARGS (running_time),
898 GST_TIME_ARGS (self->running_time_to_wait_for), inbuf);
899 if (self->mode != MODE_VIDEO_FIRST ||
900 self->first_audio_running_time <= running_time ||
901 self->audio_eos_flag) {
902 if (running_time < self->running_time_to_end_at ||
903 self->running_time_to_end_at == GST_CLOCK_TIME_NONE) {
904 /* We are before the end of the recording. Check if we just actually
906 if (running_time > self->running_time_to_wait_for) {
907 /* We just started recording: synchronise the audio */
908 self->audio_running_time_to_wait_for = running_time;
909 gst_avwait_send_element_message (self, FALSE, running_time);
911 /* We will start in the future when running_time_to_wait_for is
913 self->audio_running_time_to_wait_for =
914 self->running_time_to_wait_for;
916 self->audio_running_time_to_end_at = self->running_time_to_end_at;
919 /* We are in video-first mode and behind the first audio timestamp. We
920 * should drop all video buffers until the first audio timestamp, so
921 * we can catch up with it. (In timecode mode and running-time mode, we
922 * don't care about when the audio starts, we start as soon as the
923 * target timecode or running time has been reached) */
924 gst_buffer_unref (inbuf);
932 self->was_recording = self->recording;
933 g_cond_signal (&self->cond);
934 g_mutex_unlock (&self->mutex);
936 GST_WARNING_OBJECT (self, "Pass video buffer ending at %" GST_TIME_FORMAT,
937 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf) +
938 GST_BUFFER_DURATION (inbuf)));
939 ret = gst_pad_push (self->vsrcpad, inbuf);
941 g_mutex_lock (&self->mutex);
942 if (self->must_send_end_message & END_MESSAGE_AUDIO_PUSHED) {
943 self->must_send_end_message = END_MESSAGE_NORMAL;
944 g_mutex_unlock (&self->mutex);
945 gst_avwait_send_element_message (self, TRUE,
946 self->audio_running_time_to_end_at);
947 } else if (self->must_send_end_message & END_MESSAGE_STREAM_ENDED) {
948 if (self->audio_eos_flag) {
949 self->must_send_end_message = END_MESSAGE_NORMAL;
950 g_mutex_unlock (&self->mutex);
951 gst_avwait_send_element_message (self, TRUE,
952 self->audio_running_time_to_end_at);
954 self->must_send_end_message |= END_MESSAGE_VIDEO_PUSHED;
955 g_mutex_unlock (&self->mutex);
958 g_mutex_unlock (&self->mutex);
965 * assumes sign1 and sign2 are either 1 or -1
966 * returns 0 if sign1*num1 == sign2*num2
967 * -1 if sign1*num1 < sign2*num2
968 * 1 if sign1*num1 > sign2*num2
971 gst_avwait_compare_guint64_with_signs (gint sign1,
972 guint64 num1, gint sign2, guint64 num2)
976 else if (num1 == num2)
979 return num1 > num2 ? sign1 : -sign1;
983 gst_avwait_asink_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf)
985 GstClockTime timestamp;
986 GstAvWait *self = GST_AVWAIT (parent);
987 GstClockTime current_running_time;
988 GstClockTime video_running_time = GST_CLOCK_TIME_NONE;
989 GstClockTime duration;
990 GstClockTime running_time_at_end = GST_CLOCK_TIME_NONE;
991 gint asign, vsign = 1, esign = 1;
992 GstFlowReturn ret = GST_FLOW_OK;
993 /* Make sure the video thread doesn't send the element message before we
994 * actually call gst_pad_push */
995 gboolean send_element_message = FALSE;
997 timestamp = GST_BUFFER_TIMESTAMP (inbuf);
998 if (timestamp == GST_CLOCK_TIME_NONE) {
999 gst_buffer_unref (inbuf);
1000 return GST_FLOW_ERROR;
1002 g_mutex_lock (&self->mutex);
1003 self->asegment.position = timestamp;
1005 gst_segment_to_running_time_full (&self->asegment, GST_FORMAT_TIME,
1006 self->asegment.position, ¤t_running_time);
1008 g_mutex_unlock (&self->mutex);
1009 gst_buffer_unref (inbuf);
1010 GST_ERROR_OBJECT (self, "Could not get current running time");
1011 return GST_FLOW_ERROR;
1013 if (self->first_audio_running_time == GST_CLOCK_TIME_NONE) {
1014 self->first_audio_running_time = current_running_time;
1016 g_cond_signal (&self->audio_cond);
1017 if (self->vsegment.format == GST_FORMAT_TIME) {
1019 gst_segment_to_running_time_full (&self->vsegment, GST_FORMAT_TIME,
1020 self->vsegment.position, &video_running_time);
1022 video_running_time = GST_CLOCK_TIME_NONE;
1026 gst_util_uint64_scale (gst_buffer_get_size (inbuf) / self->ainfo.bpf,
1027 GST_SECOND, self->ainfo.rate);
1028 if (duration != GST_CLOCK_TIME_NONE) {
1030 gst_segment_to_running_time_full (&self->asegment, GST_FORMAT_TIME,
1031 self->asegment.position + duration, &running_time_at_end);
1033 g_mutex_unlock (&self->mutex);
1034 GST_ERROR_OBJECT (self, "Could not get running time at end");
1035 gst_buffer_unref (inbuf);
1036 return GST_FLOW_ERROR;
1039 while (!(self->video_eos_flag || self->audio_flush_flag
1040 || self->shutdown_flag) &&
1041 /* Start at timecode */
1042 /* Wait if we haven't received video yet */
1043 (video_running_time == GST_CLOCK_TIME_NONE
1044 /* Wait if audio is after the video: dunno what to do */
1045 || gst_avwait_compare_guint64_with_signs (asign,
1046 running_time_at_end, vsign, video_running_time) == 1)) {
1047 g_cond_wait (&self->cond, &self->mutex);
1049 gst_segment_to_running_time_full (&self->vsegment, GST_FORMAT_TIME,
1050 self->vsegment.position, &video_running_time);
1052 video_running_time = GST_CLOCK_TIME_NONE;
1055 if (self->audio_flush_flag || self->shutdown_flag) {
1056 GST_DEBUG_OBJECT (self, "Shutting down, ignoring frame");
1057 gst_buffer_unref (inbuf);
1058 g_mutex_unlock (&self->mutex);
1059 return GST_FLOW_FLUSHING;
1061 if (self->audio_running_time_to_wait_for == GST_CLOCK_TIME_NONE
1062 /* Audio ends before start : drop */
1063 || gst_avwait_compare_guint64_with_signs (esign,
1064 running_time_at_end, 1, self->audio_running_time_to_wait_for) == -1
1065 /* Audio starts after end: drop */
1066 || current_running_time >= self->audio_running_time_to_end_at) {
1067 GST_DEBUG_OBJECT (self,
1068 "Dropped an audio buf at %" GST_TIME_FORMAT " waiting for %"
1069 GST_TIME_FORMAT " video time %" GST_TIME_FORMAT,
1070 GST_TIME_ARGS (current_running_time),
1071 GST_TIME_ARGS (self->audio_running_time_to_wait_for),
1072 GST_TIME_ARGS (video_running_time));
1073 GST_DEBUG_OBJECT (self, "Would have ended at %i %" GST_TIME_FORMAT,
1074 esign, GST_TIME_ARGS (running_time_at_end));
1075 gst_buffer_unref (inbuf);
1077 if (current_running_time >= self->audio_running_time_to_end_at &&
1078 (self->must_send_end_message & END_MESSAGE_STREAM_ENDED) &&
1079 !(self->must_send_end_message & END_MESSAGE_AUDIO_PUSHED)) {
1080 send_element_message = TRUE;
1083 } else if (gst_avwait_compare_guint64_with_signs (esign, running_time_at_end,
1084 1, self->audio_running_time_to_wait_for) >= 0
1085 && gst_avwait_compare_guint64_with_signs (esign, running_time_at_end, 1,
1086 self->audio_running_time_to_end_at) == -1) {
1087 /* Audio ends after start, but before end: clip */
1088 GstSegment asegment2 = self->asegment;
1090 gst_segment_set_running_time (&asegment2, GST_FORMAT_TIME,
1091 self->audio_running_time_to_wait_for);
1093 gst_audio_buffer_clip (inbuf, &asegment2, self->ainfo.rate,
1095 } else if (gst_avwait_compare_guint64_with_signs (esign, running_time_at_end,
1096 1, self->audio_running_time_to_end_at) >= 0) {
1097 /* Audio starts after start, but before end: clip from the other side */
1098 GstSegment asegment2 = self->asegment;
1103 gst_segment_position_from_running_time_full (&asegment2,
1104 GST_FORMAT_TIME, self->audio_running_time_to_end_at, &stop);
1106 asegment2.stop = stop;
1108 /* Stopping before the start of the audio segment?! */
1109 /* This shouldn't happen: we already know that the current audio is
1110 * inside the segment, and that the end is after the current audio
1112 GST_ELEMENT_ERROR (self, CORE, FAILED,
1113 ("Failed to clip audio: it should have ended before the current segment"),
1117 gst_audio_buffer_clip (inbuf, &asegment2, self->ainfo.rate,
1119 if (self->must_send_end_message & END_MESSAGE_STREAM_ENDED) {
1120 send_element_message = TRUE;
1123 /* Programming error? Shouldn't happen */
1124 g_assert_not_reached ();
1126 g_mutex_unlock (&self->mutex);
1128 GstClockTime new_duration =
1129 gst_util_uint64_scale (gst_buffer_get_size (inbuf) / self->ainfo.bpf,
1130 GST_SECOND, self->ainfo.rate);
1131 GstClockTime new_running_time_at_end =
1132 gst_segment_to_running_time (&self->asegment, GST_FORMAT_TIME,
1133 self->asegment.position + new_duration);
1134 GST_WARNING_OBJECT (self, "Pass audio buffer ending at %" GST_TIME_FORMAT,
1135 GST_TIME_ARGS (new_running_time_at_end));
1136 ret = gst_pad_push (self->asrcpad, inbuf);
1138 if (send_element_message) {
1139 g_mutex_lock (&self->mutex);
1140 if ((self->must_send_end_message & END_MESSAGE_VIDEO_PUSHED) ||
1141 self->video_eos_flag) {
1142 self->must_send_end_message = END_MESSAGE_NORMAL;
1143 g_mutex_unlock (&self->mutex);
1144 gst_avwait_send_element_message (self, TRUE,
1145 self->audio_running_time_to_end_at);
1146 } else if (self->must_send_end_message & END_MESSAGE_STREAM_ENDED) {
1147 self->must_send_end_message |= END_MESSAGE_AUDIO_PUSHED;
1148 g_mutex_unlock (&self->mutex);
1150 g_assert_not_reached ();
1153 send_element_message = FALSE;
1157 static GstIterator *
1158 gst_avwait_iterate_internal_links (GstPad * pad, GstObject * parent)
1160 GstIterator *it = NULL;
1162 GValue val = G_VALUE_INIT;
1163 GstAvWait *self = GST_AVWAIT (parent);
1165 if (self->asinkpad == pad)
1166 opad = gst_object_ref (self->asrcpad);
1167 else if (self->asrcpad == pad)
1168 opad = gst_object_ref (self->asinkpad);
1169 else if (self->vsinkpad == pad)
1170 opad = gst_object_ref (self->vsrcpad);
1171 else if (self->vsrcpad == pad)
1172 opad = gst_object_ref (self->vsinkpad);
1176 g_value_init (&val, GST_TYPE_PAD);
1177 g_value_set_object (&val, opad);
1178 it = gst_iterator_new_single (GST_TYPE_PAD, &val);
1179 g_value_unset (&val);
1181 gst_object_unref (opad);