1 /*
2 * GStreamer
3 * Copyright (C) 2016 Vivia Nikolaidou <vivia@toolsonair.com>
4 * Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
5 *
6 * gsttimecodestamper.c
7 *
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.
12 *
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.
17 *
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.
22 */
23
24 /**
25 * SECTION:element-timecodestamper
26 * @title: timecodestamper
27 * @short_description: Attach a timecode into incoming video frames
28 *
29 * This element attaches a timecode into every incoming video frame. It starts
30 * counting from the stream time of each segment start, which it converts into
31 * a timecode.
32 *
33 * ## Example launch line
34 * |[
35 * gst-launch-1.0 videotestsrc ! timecodestamper ! autovideosink
36 * ]|
37 *
38 */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include "gsttimecodestamper.h"
45
46 #include <gst/gst.h>
47 #include <gst/video/video.h>
48 #include <gst/audio/audio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #define ABSDIFF(a,b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a)))
53
54 GST_DEBUG_CATEGORY_STATIC (timecodestamper_debug);
55 #define GST_CAT_DEFAULT timecodestamper_debug
56
57 /* GstTimeCodeStamper properties */
58 enum
59 {
60 PROP_0,
61 PROP_SOURCE,
62 PROP_SET,
63 PROP_AUTO_RESYNC,
64 PROP_TIMEOUT,
65 PROP_DROP_FRAME,
66 PROP_POST_MESSAGES,
67 PROP_SET_INTERNAL_TIMECODE,
68 PROP_LTC_DAILY_JAM,
69 PROP_LTC_AUTO_RESYNC,
70 PROP_LTC_EXTRA_LATENCY,
71 PROP_LTC_TIMEOUT,
72 PROP_RTC_MAX_DRIFT,
73 PROP_RTC_AUTO_RESYNC,
74 PROP_TIMECODE_OFFSET
75 };
76
77 #define DEFAULT_SOURCE GST_TIME_CODE_STAMPER_SOURCE_INTERNAL
78 #define DEFAULT_SET GST_TIME_CODE_STAMPER_SET_KEEP
79 #define DEFAULT_AUTO_RESYNC TRUE
80 #define DEFAULT_TIMEOUT GST_CLOCK_TIME_NONE
81 #define DEFAULT_DROP_FRAME FALSE
82 #define DEFAULT_POST_MESSAGES FALSE
83 #define DEFAULT_SET_INTERNAL_TIMECODE NULL
84 #define DEFAULT_LTC_DAILY_JAM NULL
85 #define DEFAULT_LTC_AUTO_RESYNC TRUE
86 #define DEFAULT_LTC_TIMEOUT GST_CLOCK_TIME_NONE
87 #define DEFAULT_LTC_EXTRA_LATENCY (150 * GST_MSECOND)
88 #define DEFAULT_RTC_MAX_DRIFT 250000000
89 #define DEFAULT_RTC_AUTO_RESYNC TRUE
90 #define DEFAULT_TIMECODE_OFFSET 0
91
92 #define DEFAULT_LTC_QUEUE 100
93
94 static GstStaticPadTemplate gst_timecodestamper_src_template =
95 GST_STATIC_PAD_TEMPLATE ("src",
96 GST_PAD_SRC,
97 GST_PAD_ALWAYS,
98 GST_STATIC_CAPS ("video/x-raw, framerate=[1/2147483647, 2147483647/1]; "
99 "closedcaption/x-cea-608, framerate=[1/2147483647, 2147483647/1]; "
100 "closedcaption/x-cea-708, framerate=[1/2147483647, 2147483647/1]; ")
101 );
102
103 static GstStaticPadTemplate gst_timecodestamper_sink_template =
104 GST_STATIC_PAD_TEMPLATE ("sink",
105 GST_PAD_SINK,
106 GST_PAD_ALWAYS,
107 GST_STATIC_CAPS ("video/x-raw, framerate=[1/2147483647, 2147483647/1]; "
108 "closedcaption/x-cea-608, framerate=[1/2147483647, 2147483647/1]; "
109 "closedcaption/x-cea-708, framerate=[1/2147483647, 2147483647/1]; ")
110 );
111
112 static GstStaticPadTemplate gst_timecodestamper_ltc_template =
113 GST_STATIC_PAD_TEMPLATE ("ltc_sink",
114 GST_PAD_SINK,
115 GST_PAD_REQUEST,
116 GST_STATIC_CAPS ("audio/x-raw,format=U8,rate=[1,max],channels=1")
117 );
118
119 static void gst_timecodestamper_set_property (GObject * object, guint prop_id,
120 const GValue * value, GParamSpec * pspec);
121 static void gst_timecodestamper_get_property (GObject * object, guint prop_id,
122 GValue * value, GParamSpec * pspec);
123 static void gst_timecodestamper_dispose (GObject * object);
124 static gboolean gst_timecodestamper_sink_event (GstBaseTransform * trans,
125 GstEvent * event);
126 static gboolean gst_timecodestamper_src_event (GstBaseTransform * trans,
127 GstEvent * event);
128 static GstFlowReturn gst_timecodestamper_transform_ip (GstBaseTransform *
129 vfilter, GstBuffer * buffer);
130 static gboolean gst_timecodestamper_stop (GstBaseTransform * trans);
131 static gboolean gst_timecodestamper_start (GstBaseTransform * trans);
132 static GstPad *gst_timecodestamper_request_new_pad (GstElement * element,
133 GstPadTemplate * temp, const gchar * unused, const GstCaps * caps);
134 static void gst_timecodestamper_release_pad (GstElement * element,
135 GstPad * pad);
136
137 #if HAVE_LTC
138 typedef struct
139 {
140 GstClockTime running_time;
141 GstVideoTimeCode timecode;
142 } TimestampedTimecode;
143
144 static gboolean gst_timecodestamper_query (GstBaseTransform * trans,
145 GstPadDirection direction, GstQuery * query);
146
147 static GstFlowReturn gst_timecodestamper_ltcpad_chain (GstPad * pad,
148 GstObject * parent, GstBuffer * buffer);
149 static gboolean gst_timecodestamper_ltcpad_event (GstPad * pad,
150 GstObject * parent, GstEvent * event);
151 static gboolean gst_timecodestamper_ltcpad_query (GstPad * pad,
152 GstObject * parent, GstQuery * query);
153 static gboolean gst_timecodestamper_ltcpad_activatemode (GstPad * pad,
154 GstObject * parent, GstPadMode mode, gboolean active);
155
156 static gboolean gst_timecodestamper_videopad_activatemode (GstPad * pad,
157 GstObject * parent, GstPadMode mode, gboolean active);
158
159 static GstIterator *gst_timecodestamper_src_iterate_internal_link (GstPad * pad,
160 GstObject * parent);
161 #endif
162
163 static void gst_timecodestamper_update_drop_frame (GstTimeCodeStamper *
164 timecodestamper);
165
166 G_DEFINE_TYPE (GstTimeCodeStamper, gst_timecodestamper,
167 GST_TYPE_BASE_TRANSFORM);
168 GST_ELEMENT_REGISTER_DEFINE (timecodestamper, "timecodestamper",
169 GST_RANK_NONE, GST_TYPE_TIME_CODE_STAMPER);
170
171 GType
gst_timecodestamper_source_get_type(void)172 gst_timecodestamper_source_get_type (void)
173 {
174 static GType type = 0;
175 static const GEnumValue values[] = {
176 {GST_TIME_CODE_STAMPER_SOURCE_INTERNAL,
177 "Use internal timecode counter, starting at zero or value set by property",
178 "internal"},
179 {GST_TIME_CODE_STAMPER_SOURCE_ZERO,
180 "Always use zero", "zero"},
181 {GST_TIME_CODE_STAMPER_SOURCE_LAST_KNOWN,
182 "Count up from the last known upstream timecode or internal if unknown",
183 "last-known"},
184 {GST_TIME_CODE_STAMPER_SOURCE_LAST_KNOWN_OR_ZERO,
185 "Count up from the last known upstream timecode or zero if unknown",
186 "last-known-or-zero"},
187 {GST_TIME_CODE_STAMPER_SOURCE_LTC,
188 "Linear timecode from an audio device", "ltc"},
189 {GST_TIME_CODE_STAMPER_SOURCE_RTC,
190 "Timecode from real time clock", "rtc"},
191 {0, NULL, NULL},
192 };
193
194 if (!type) {
195 type = g_enum_register_static ("GstTimeCodeStamperSource", values);
196 }
197 return type;
198 }
199
200 GType
gst_timecodestamper_set_get_type(void)201 gst_timecodestamper_set_get_type (void)
202 {
203 static GType type = 0;
204 static const GEnumValue values[] = {
205 {GST_TIME_CODE_STAMPER_SET_NEVER,
206 "Never set timecodes", "never"},
207 {GST_TIME_CODE_STAMPER_SET_KEEP,
208 "Keep upstream timecodes and only set if no upstream timecode", "keep"},
209 {GST_TIME_CODE_STAMPER_SET_ALWAYS,
210 "Always set timecode and remove upstream timecode", "always"},
211 {0, NULL, NULL},
212 };
213
214 if (!type) {
215 type = g_enum_register_static ("GstTimeCodeStamperSet", values);
216 }
217 return type;
218 }
219
220 static void
gst_timecodestamper_class_init(GstTimeCodeStamperClass * klass)221 gst_timecodestamper_class_init (GstTimeCodeStamperClass * klass)
222 {
223 GObjectClass *gobject_class = (GObjectClass *) klass;
224 GstElementClass *element_class = (GstElementClass *) klass;
225 GstBaseTransformClass *trans_class = (GstBaseTransformClass *) klass;
226
227 GST_DEBUG_CATEGORY_INIT (timecodestamper_debug, "timecodestamper", 0,
228 "timecodestamper");
229 gst_element_class_set_static_metadata (element_class, "Timecode stamper",
230 "Filter/Video", "Attaches a timecode meta into each video frame",
231 "Vivia Nikolaidou <vivia@toolsonair.com>");
232
233 gobject_class->set_property = gst_timecodestamper_set_property;
234 gobject_class->get_property = gst_timecodestamper_get_property;
235 gobject_class->dispose = gst_timecodestamper_dispose;
236
237 g_object_class_install_property (gobject_class, PROP_SOURCE,
238 g_param_spec_enum ("source", "Timecode Source",
239 "Choose from what source the timecode should be taken",
240 GST_TYPE_TIME_CODE_STAMPER_SOURCE,
241 DEFAULT_SOURCE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
242 g_object_class_install_property (gobject_class, PROP_SET,
243 g_param_spec_enum ("set", "Timecode Set",
244 "Choose whether timecodes should be overridden or not",
245 GST_TYPE_TIME_CODE_STAMPER_SET,
246 DEFAULT_SET, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
247 g_object_class_install_property (gobject_class, PROP_AUTO_RESYNC,
248 g_param_spec_boolean ("auto-resync",
249 "Auto Resync",
250 "If true resync last known timecode from upstream, otherwise only "
251 "count up from the last known one",
252 DEFAULT_AUTO_RESYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
253 g_object_class_install_property (gobject_class, PROP_TIMEOUT,
254 g_param_spec_uint64 ("timeout",
255 "Timeout",
256 "Time out upstream timecode if no new timecode was detected after this time",
257 0, G_MAXUINT64, DEFAULT_TIMEOUT,
258 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
259 g_object_class_install_property (gobject_class, PROP_DROP_FRAME,
260 g_param_spec_boolean ("drop-frame", "Drop Frame",
261 "Use drop-frame timecodes for 29.97 and 59.94 FPS",
262 DEFAULT_DROP_FRAME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
263 g_object_class_install_property (gobject_class, PROP_POST_MESSAGES,
264 g_param_spec_boolean ("post-messages", "Post element message",
265 "Post element message containing the current timecode",
266 DEFAULT_POST_MESSAGES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
267 g_object_class_install_property (gobject_class, PROP_SET_INTERNAL_TIMECODE,
268 g_param_spec_boxed ("set-internal-timecode",
269 "Set Internal Timecode",
270 "If set, take this timecode as the internal timecode for the first "
271 "frame and increment from it. Only the values itself and daily jam are taken, "
272 "flags and frame rate are always determined by timecodestamper "
273 "itself. If unset, the internal timecode will start at 0 with the daily jam "
274 "being the current real-time clock time",
275 GST_TYPE_VIDEO_TIME_CODE,
276 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
277 g_object_class_install_property (gobject_class, PROP_LTC_DAILY_JAM,
278 g_param_spec_boxed ("ltc-daily-jam",
279 "LTC Daily jam",
280 "The daily jam of the LTC timecode",
281 G_TYPE_DATE_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
282 g_object_class_install_property (gobject_class, PROP_LTC_AUTO_RESYNC,
283 g_param_spec_boolean ("ltc-auto-resync",
284 "LTC Auto Resync",
285 "If true the LTC timecode will be automatically resynced if it drifts, "
286 "otherwise it will only be counted up from the last known one",
287 DEFAULT_LTC_AUTO_RESYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
288 g_object_class_install_property (gobject_class, PROP_LTC_EXTRA_LATENCY,
289 g_param_spec_uint64 ("ltc-extra-latency", "LTC Extra Latency",
290 "Extra latency to introduce for waiting for LTC timecodes",
291 0, G_MAXUINT64, DEFAULT_LTC_EXTRA_LATENCY,
292 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
293 g_object_class_install_property (gobject_class, PROP_LTC_TIMEOUT,
294 g_param_spec_uint64 ("ltc-timeout", "LTC Timeout",
295 "Time out LTC timecode if no new timecode was detected after this time",
296 0, G_MAXUINT64, DEFAULT_LTC_TIMEOUT,
297 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
298 g_object_class_install_property (gobject_class, PROP_RTC_MAX_DRIFT,
299 g_param_spec_uint64 ("rtc-max-drift",
300 "RTC Maximum Offset",
301 "Maximum number of nanoseconds the RTC clock is allowed to drift from "
302 "the video before it is resynced",
303 0, G_MAXUINT64, DEFAULT_RTC_MAX_DRIFT,
304 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
305 g_object_class_install_property (gobject_class, PROP_RTC_AUTO_RESYNC,
306 g_param_spec_boolean ("rtc-auto-resync",
307 "RTC Auto Resync",
308 "If true the RTC timecode will be automatically resynced if it drifts, "
309 "otherwise it will only be counted up from the last known one",
310 DEFAULT_RTC_AUTO_RESYNC, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
311 g_object_class_install_property (gobject_class, PROP_TIMECODE_OFFSET,
312 g_param_spec_int ("timecode-offset",
313 "Timecode Offset",
314 "Add this offset in frames to internal, LTC or RTC timecode, "
315 "useful if there is an offset between the timecode source and video",
316 G_MININT, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
317
318 gst_element_class_add_pad_template (element_class,
319 gst_static_pad_template_get (&gst_timecodestamper_sink_template));
320 gst_element_class_add_pad_template (element_class,
321 gst_static_pad_template_get (&gst_timecodestamper_src_template));
322 gst_element_class_add_pad_template (element_class,
323 gst_static_pad_template_get (&gst_timecodestamper_ltc_template));
324
325 element_class->request_new_pad =
326 GST_DEBUG_FUNCPTR (gst_timecodestamper_request_new_pad);
327 element_class->release_pad =
328 GST_DEBUG_FUNCPTR (gst_timecodestamper_release_pad);
329
330 trans_class->sink_event = GST_DEBUG_FUNCPTR (gst_timecodestamper_sink_event);
331 trans_class->src_event = GST_DEBUG_FUNCPTR (gst_timecodestamper_src_event);
332 #if HAVE_LTC
333 trans_class->query = GST_DEBUG_FUNCPTR (gst_timecodestamper_query);
334 #endif
335 trans_class->stop = GST_DEBUG_FUNCPTR (gst_timecodestamper_stop);
336 trans_class->start = GST_DEBUG_FUNCPTR (gst_timecodestamper_start);
337
338 trans_class->transform_ip =
339 GST_DEBUG_FUNCPTR (gst_timecodestamper_transform_ip);
340
341 gst_type_mark_as_plugin_api (GST_TYPE_TIME_CODE_STAMPER_SOURCE, 0);
342 gst_type_mark_as_plugin_api (GST_TYPE_TIME_CODE_STAMPER_SET, 0);
343 }
344
345 static void
gst_timecodestamper_init(GstTimeCodeStamper * timecodestamper)346 gst_timecodestamper_init (GstTimeCodeStamper * timecodestamper)
347 {
348 timecodestamper->ltcpad = NULL;
349
350 timecodestamper->tc_source = GST_TIME_CODE_STAMPER_SOURCE_INTERNAL;
351 timecodestamper->tc_set = GST_TIME_CODE_STAMPER_SET_KEEP;
352 timecodestamper->tc_auto_resync = DEFAULT_AUTO_RESYNC;
353 timecodestamper->tc_timeout = DEFAULT_TIMEOUT;
354 timecodestamper->drop_frame = DEFAULT_DROP_FRAME;
355 timecodestamper->post_messages = DEFAULT_POST_MESSAGES;
356 timecodestamper->set_internal_tc = NULL;
357 timecodestamper->ltc_daily_jam = DEFAULT_LTC_DAILY_JAM;
358 timecodestamper->ltc_auto_resync = DEFAULT_LTC_AUTO_RESYNC;
359 timecodestamper->ltc_extra_latency = DEFAULT_LTC_EXTRA_LATENCY;
360 timecodestamper->ltc_timeout = DEFAULT_LTC_TIMEOUT;
361 timecodestamper->rtc_max_drift = DEFAULT_RTC_MAX_DRIFT;
362 timecodestamper->rtc_auto_resync = DEFAULT_RTC_AUTO_RESYNC;
363 timecodestamper->timecode_offset = 0;
364
365 timecodestamper->internal_tc = NULL;
366 timecodestamper->last_tc = NULL;
367 timecodestamper->last_tc_running_time = GST_CLOCK_TIME_NONE;
368 timecodestamper->rtc_tc = NULL;
369
370 timecodestamper->seeked_frames = -1;
371
372 #if HAVE_LTC
373 g_mutex_init (&timecodestamper->mutex);
374 g_cond_init (&timecodestamper->ltc_cond_video);
375 g_cond_init (&timecodestamper->ltc_cond_audio);
376
377 gst_segment_init (&timecodestamper->ltc_segment, GST_FORMAT_UNDEFINED);
378 timecodestamper->ltc_first_running_time = GST_CLOCK_TIME_NONE;
379 timecodestamper->ltc_current_running_time = GST_CLOCK_TIME_NONE;
380
381 g_queue_init (&timecodestamper->ltc_current_tcs);
382 timecodestamper->ltc_internal_tc = NULL;
383 timecodestamper->ltc_internal_running_time = GST_CLOCK_TIME_NONE;
384 timecodestamper->ltc_dec = NULL;
385 timecodestamper->ltc_total = 0;
386
387 timecodestamper->ltc_eos = TRUE;
388 timecodestamper->ltc_flushing = TRUE;
389
390 timecodestamper->audio_live = FALSE;
391 timecodestamper->audio_latency = GST_CLOCK_TIME_NONE;
392 timecodestamper->video_live = FALSE;
393 timecodestamper->video_latency = GST_CLOCK_TIME_NONE;
394 timecodestamper->latency = GST_CLOCK_TIME_NONE;
395
396 timecodestamper->video_activatemode_default =
397 GST_PAD_ACTIVATEMODEFUNC (GST_BASE_TRANSFORM_SINK_PAD (timecodestamper));
398 GST_PAD_ACTIVATEMODEFUNC (GST_BASE_TRANSFORM_SINK_PAD (timecodestamper)) =
399 gst_timecodestamper_videopad_activatemode;
400 gst_pad_set_iterate_internal_links_function (GST_BASE_TRANSFORM_SRC_PAD
401 (timecodestamper), gst_timecodestamper_src_iterate_internal_link);
402 #endif
403 }
404
405 static void
gst_timecodestamper_dispose(GObject * object)406 gst_timecodestamper_dispose (GObject * object)
407 {
408 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (object);
409
410 if (timecodestamper->ltc_daily_jam) {
411 g_date_time_unref (timecodestamper->ltc_daily_jam);
412 timecodestamper->ltc_daily_jam = NULL;
413 }
414
415 if (timecodestamper->internal_tc != NULL) {
416 gst_video_time_code_free (timecodestamper->internal_tc);
417 timecodestamper->internal_tc = NULL;
418 }
419
420 if (timecodestamper->set_internal_tc != NULL) {
421 gst_video_time_code_free (timecodestamper->set_internal_tc);
422 timecodestamper->set_internal_tc = NULL;
423 }
424
425 if (timecodestamper->last_tc != NULL) {
426 gst_video_time_code_free (timecodestamper->last_tc);
427 timecodestamper->last_tc = NULL;
428 }
429 timecodestamper->last_tc_running_time = GST_CLOCK_TIME_NONE;
430
431 if (timecodestamper->rtc_tc != NULL) {
432 gst_video_time_code_free (timecodestamper->rtc_tc);
433 timecodestamper->rtc_tc = NULL;
434 }
435 #if HAVE_LTC
436 g_cond_clear (&timecodestamper->ltc_cond_video);
437 g_cond_clear (&timecodestamper->ltc_cond_audio);
438 g_mutex_clear (&timecodestamper->mutex);
439 {
440 TimestampedTimecode *tc;
441 while ((tc = g_queue_pop_tail (&timecodestamper->ltc_current_tcs))) {
442 gst_video_time_code_clear (&tc->timecode);
443 g_free (tc);
444 }
445 }
446 if (timecodestamper->ltc_internal_tc != NULL) {
447 gst_video_time_code_free (timecodestamper->ltc_internal_tc);
448 timecodestamper->ltc_internal_tc = NULL;
449 }
450 timecodestamper->ltc_internal_running_time = GST_CLOCK_TIME_NONE;
451
452 if (timecodestamper->ltc_dec) {
453 ltc_decoder_free (timecodestamper->ltc_dec);
454 timecodestamper->ltc_dec = NULL;
455 }
456
457 if (timecodestamper->stream_align) {
458 gst_audio_stream_align_free (timecodestamper->stream_align);
459 timecodestamper->stream_align = NULL;
460 }
461 #endif
462
463 G_OBJECT_CLASS (gst_timecodestamper_parent_class)->dispose (object);
464 }
465
466 static void
gst_timecodestamper_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)467 gst_timecodestamper_set_property (GObject * object, guint prop_id,
468 const GValue * value, GParamSpec * pspec)
469 {
470 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (object);
471
472 GST_OBJECT_LOCK (timecodestamper);
473 switch (prop_id) {
474 case PROP_SOURCE:
475 timecodestamper->tc_source = (GstTimeCodeStamperSource)
476 g_value_get_enum (value);
477 break;
478 case PROP_SET:
479 timecodestamper->tc_set = (GstTimeCodeStamperSet)
480 g_value_get_enum (value);
481 break;
482 case PROP_AUTO_RESYNC:
483 timecodestamper->tc_auto_resync = g_value_get_boolean (value);
484 break;
485 case PROP_TIMEOUT:
486 timecodestamper->tc_timeout = g_value_get_uint64 (value);
487 break;
488 case PROP_DROP_FRAME:
489 timecodestamper->drop_frame = g_value_get_boolean (value);
490 gst_timecodestamper_update_drop_frame (timecodestamper);
491 break;
492 case PROP_LTC_DAILY_JAM:
493 if (timecodestamper->ltc_daily_jam)
494 g_date_time_unref (timecodestamper->ltc_daily_jam);
495 timecodestamper->ltc_daily_jam = g_value_dup_boxed (value);
496
497 #if HAVE_LTC
498 {
499 GList *l;
500
501 for (l = timecodestamper->ltc_current_tcs.head; l; l = l->next) {
502 TimestampedTimecode *tc = l->data;
503
504 if (tc->timecode.config.latest_daily_jam) {
505 g_date_time_unref (tc->timecode.config.latest_daily_jam);
506 }
507 tc->timecode.config.latest_daily_jam =
508 g_date_time_ref (timecodestamper->ltc_daily_jam);
509 }
510 }
511
512 if (timecodestamper->ltc_internal_tc) {
513 if (timecodestamper->ltc_internal_tc->config.latest_daily_jam) {
514 g_date_time_unref (timecodestamper->ltc_internal_tc->
515 config.latest_daily_jam);
516 }
517 timecodestamper->ltc_internal_tc->config.latest_daily_jam =
518 g_date_time_ref (timecodestamper->ltc_daily_jam);
519 }
520 #endif
521 break;
522 case PROP_POST_MESSAGES:
523 timecodestamper->post_messages = g_value_get_boolean (value);
524 break;
525 case PROP_SET_INTERNAL_TIMECODE:{
526 if (timecodestamper->set_internal_tc)
527 gst_video_time_code_free (timecodestamper->set_internal_tc);
528 timecodestamper->set_internal_tc = g_value_dup_boxed (value);
529
530 /* Reset the internal timecode on the next opportunity if a new
531 * timecode was set here. If none was set we just continue counting
532 * from the previous one */
533 if (timecodestamper->set_internal_tc && timecodestamper->internal_tc) {
534 gst_video_time_code_free (timecodestamper->internal_tc);
535 timecodestamper->internal_tc = NULL;
536 }
537 break;
538 }
539 case PROP_LTC_AUTO_RESYNC:
540 timecodestamper->ltc_auto_resync = g_value_get_boolean (value);
541 break;
542 case PROP_LTC_TIMEOUT:
543 timecodestamper->ltc_timeout = g_value_get_uint64 (value);
544 break;
545 case PROP_LTC_EXTRA_LATENCY:
546 timecodestamper->ltc_extra_latency = g_value_get_uint64 (value);
547 break;
548 case PROP_RTC_MAX_DRIFT:
549 timecodestamper->rtc_max_drift = g_value_get_uint64 (value);
550 break;
551 case PROP_RTC_AUTO_RESYNC:
552 timecodestamper->rtc_auto_resync = g_value_get_boolean (value);
553 break;
554 case PROP_TIMECODE_OFFSET:
555 timecodestamper->timecode_offset = g_value_get_int (value);
556 break;
557 default:
558 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
559 break;
560 }
561
562 GST_OBJECT_UNLOCK (timecodestamper);
563 }
564
565 static void
gst_timecodestamper_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)566 gst_timecodestamper_get_property (GObject * object, guint prop_id,
567 GValue * value, GParamSpec * pspec)
568 {
569 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (object);
570
571 GST_OBJECT_LOCK (timecodestamper);
572 switch (prop_id) {
573 case PROP_SOURCE:
574 g_value_set_enum (value, timecodestamper->tc_source);
575 break;
576 case PROP_SET:
577 g_value_set_enum (value, timecodestamper->tc_set);
578 break;
579 case PROP_AUTO_RESYNC:
580 g_value_set_boolean (value, timecodestamper->tc_auto_resync);
581 break;
582 case PROP_TIMEOUT:
583 g_value_set_uint64 (value, timecodestamper->tc_timeout);
584 break;
585 case PROP_DROP_FRAME:
586 g_value_set_boolean (value, timecodestamper->drop_frame);
587 break;
588 case PROP_LTC_DAILY_JAM:
589 g_value_set_boxed (value, timecodestamper->ltc_daily_jam);
590 break;
591 case PROP_POST_MESSAGES:
592 g_value_set_boolean (value, timecodestamper->post_messages);
593 break;
594 case PROP_SET_INTERNAL_TIMECODE:
595 g_value_set_boxed (value, timecodestamper->set_internal_tc);
596 break;
597 case PROP_LTC_AUTO_RESYNC:
598 g_value_set_boolean (value, timecodestamper->ltc_auto_resync);
599 break;
600 case PROP_LTC_TIMEOUT:
601 g_value_set_uint64 (value, timecodestamper->ltc_timeout);
602 break;
603 case PROP_LTC_EXTRA_LATENCY:
604 g_value_set_uint64 (value, timecodestamper->ltc_extra_latency);
605 break;
606 case PROP_RTC_MAX_DRIFT:
607 g_value_set_uint64 (value, timecodestamper->rtc_max_drift);
608 break;
609 case PROP_RTC_AUTO_RESYNC:
610 g_value_set_boolean (value, timecodestamper->rtc_auto_resync);
611 break;
612 case PROP_TIMECODE_OFFSET:
613 g_value_set_int (value, timecodestamper->timecode_offset);
614 break;
615 default:
616 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
617 break;
618 }
619 GST_OBJECT_UNLOCK (timecodestamper);
620 }
621
622 static gboolean
gst_timecodestamper_stop(GstBaseTransform * trans)623 gst_timecodestamper_stop (GstBaseTransform * trans)
624 {
625 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
626
627 #if HAVE_LTC
628 g_mutex_lock (&timecodestamper->mutex);
629 timecodestamper->video_flushing = TRUE;
630 timecodestamper->video_current_running_time = GST_CLOCK_TIME_NONE;
631 if (timecodestamper->video_clock_id)
632 gst_clock_id_unschedule (timecodestamper->video_clock_id);
633 timecodestamper->ltc_flushing = TRUE;
634 g_cond_signal (&timecodestamper->ltc_cond_video);
635 g_cond_signal (&timecodestamper->ltc_cond_audio);
636 g_mutex_unlock (&timecodestamper->mutex);
637 #endif
638
639 timecodestamper->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
640 timecodestamper->fps_n = 0;
641 timecodestamper->fps_d = 1;
642
643 if (timecodestamper->internal_tc != NULL) {
644 gst_video_time_code_free (timecodestamper->internal_tc);
645 timecodestamper->internal_tc = NULL;
646 }
647
648 if (timecodestamper->rtc_tc != NULL) {
649 gst_video_time_code_free (timecodestamper->rtc_tc);
650 timecodestamper->rtc_tc = NULL;
651 }
652
653 if (timecodestamper->last_tc != NULL) {
654 gst_video_time_code_free (timecodestamper->last_tc);
655 timecodestamper->last_tc = NULL;
656 }
657 timecodestamper->last_tc_running_time = GST_CLOCK_TIME_NONE;
658 #if HAVE_LTC
659 g_mutex_lock (&timecodestamper->mutex);
660 gst_audio_info_init (&timecodestamper->ainfo);
661 gst_segment_init (&timecodestamper->ltc_segment, GST_FORMAT_UNDEFINED);
662
663 timecodestamper->ltc_first_running_time = GST_CLOCK_TIME_NONE;
664 timecodestamper->ltc_current_running_time = GST_CLOCK_TIME_NONE;
665
666 if (timecodestamper->ltc_internal_tc != NULL) {
667 gst_video_time_code_free (timecodestamper->ltc_internal_tc);
668 timecodestamper->ltc_internal_tc = NULL;
669 }
670 timecodestamper->ltc_internal_running_time = GST_CLOCK_TIME_NONE;
671
672 {
673 TimestampedTimecode *tc;
674 while ((tc = g_queue_pop_tail (&timecodestamper->ltc_current_tcs))) {
675 gst_video_time_code_clear (&tc->timecode);
676 g_free (tc);
677 }
678 }
679
680 if (timecodestamper->ltc_dec) {
681 ltc_decoder_free (timecodestamper->ltc_dec);
682 timecodestamper->ltc_dec = NULL;
683 }
684
685 if (timecodestamper->stream_align) {
686 gst_audio_stream_align_free (timecodestamper->stream_align);
687 timecodestamper->stream_align = NULL;
688 }
689
690 timecodestamper->ltc_total = 0;
691 g_mutex_unlock (&timecodestamper->mutex);
692 #endif
693
694 return TRUE;
695 }
696
697 static gboolean
gst_timecodestamper_start(GstBaseTransform * trans)698 gst_timecodestamper_start (GstBaseTransform * trans)
699 {
700 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
701
702 #if HAVE_LTC
703 g_mutex_lock (&timecodestamper->mutex);
704 timecodestamper->video_flushing = FALSE;
705 timecodestamper->video_eos = FALSE;
706 g_mutex_unlock (&timecodestamper->mutex);
707 #endif
708
709 timecodestamper->interlace_mode = GST_VIDEO_INTERLACE_MODE_PROGRESSIVE;
710 timecodestamper->fps_n = 0;
711 timecodestamper->fps_d = 1;
712
713 return TRUE;
714 }
715
716 /* Must be called with object lock */
717 static void
gst_timecodestamper_update_drop_frame(GstTimeCodeStamper * timecodestamper)718 gst_timecodestamper_update_drop_frame (GstTimeCodeStamper * timecodestamper)
719 {
720 if (timecodestamper->drop_frame && timecodestamper->fps_d == 1001 &&
721 (timecodestamper->fps_n == 30000 || timecodestamper->fps_n == 60000)) {
722 if (timecodestamper->internal_tc)
723 timecodestamper->internal_tc->config.flags |=
724 GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
725 if (timecodestamper->rtc_tc)
726 timecodestamper->rtc_tc->config.flags |=
727 GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
728 #if HAVE_LTC
729 {
730 GList *l;
731
732 for (l = timecodestamper->ltc_current_tcs.head; l; l = l->next) {
733 TimestampedTimecode *tc = l->data;
734
735 tc->timecode.config.flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
736 }
737 }
738 if (timecodestamper->ltc_internal_tc)
739 timecodestamper->ltc_internal_tc->config.flags |=
740 GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
741 #endif
742 } else {
743 if (timecodestamper->internal_tc)
744 timecodestamper->internal_tc->config.flags &=
745 ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
746 if (timecodestamper->rtc_tc)
747 timecodestamper->rtc_tc->config.flags &=
748 ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
749 #if HAVE_LTC
750 {
751 GList *l;
752
753 for (l = timecodestamper->ltc_current_tcs.head; l; l = l->next) {
754 TimestampedTimecode *tc = l->data;
755
756 tc->timecode.config.flags &= ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
757 }
758 }
759 if (timecodestamper->ltc_internal_tc)
760 timecodestamper->ltc_internal_tc->config.flags &=
761 ~GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
762 #endif
763 }
764 }
765
766 static void
gst_timecodestamper_update_timecode_framerate(GstTimeCodeStamper * timecodestamper,gint fps_n,gint fps_d,GstVideoTimeCode * timecode,gboolean is_ltc)767 gst_timecodestamper_update_timecode_framerate (GstTimeCodeStamper *
768 timecodestamper, gint fps_n, gint fps_d, GstVideoTimeCode * timecode,
769 gboolean is_ltc)
770 {
771 guint64 nframes;
772 GstClockTime time;
773 GDateTime *jam = NULL;
774 GstVideoTimeCodeFlags tc_flags = 0;
775
776 if (!timecode)
777 return;
778
779 if (timecodestamper->interlace_mode != GST_VIDEO_INTERLACE_MODE_PROGRESSIVE)
780 tc_flags |= GST_VIDEO_TIME_CODE_FLAGS_INTERLACED;
781
782 if (timecodestamper->drop_frame && timecodestamper->fps_d == 1001 &&
783 (timecodestamper->fps_n == 30000 || timecodestamper->fps_n == 60000))
784 tc_flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
785
786 /* If this is an LTC timecode and we have no framerate yet in there then
787 * just do nothing. We're going to set the framerate at a later time */
788 if (timecode->config.fps_d != 0 || !is_ltc) {
789 nframes = gst_video_time_code_frames_since_daily_jam (timecode);
790 time =
791 gst_util_uint64_scale (nframes,
792 GST_SECOND * timecodestamper->fps_d, timecodestamper->fps_n);
793 jam =
794 timecode->config.latest_daily_jam ? g_date_time_ref (timecode->config.
795 latest_daily_jam) : NULL;
796 gst_video_time_code_clear (timecode);
797 gst_video_time_code_init (timecode, timecodestamper->fps_n,
798 timecodestamper->fps_d, jam, tc_flags, 0, 0, 0, 0, 0);
799 if (jam)
800 g_date_time_unref (jam);
801
802 nframes = gst_util_uint64_scale (time, fps_n, GST_SECOND * fps_d);
803 gst_video_time_code_add_frames (timecode, nframes);
804 }
805 }
806
807 /* Must be called with object lock */
808 static gboolean
gst_timecodestamper_update_framerate(GstTimeCodeStamper * timecodestamper,gint fps_n,gint fps_d)809 gst_timecodestamper_update_framerate (GstTimeCodeStamper * timecodestamper,
810 gint fps_n, gint fps_d)
811 {
812 /* Nothing changed */
813 if (fps_n == timecodestamper->fps_n && fps_d == timecodestamper->fps_d)
814 return FALSE;
815
816 gst_timecodestamper_update_timecode_framerate (timecodestamper, fps_n, fps_d,
817 timecodestamper->internal_tc, FALSE);
818 gst_timecodestamper_update_timecode_framerate (timecodestamper, fps_n, fps_d,
819 timecodestamper->last_tc, FALSE);
820 gst_timecodestamper_update_timecode_framerate (timecodestamper, fps_n, fps_d,
821 timecodestamper->rtc_tc, FALSE);
822
823 #if HAVE_LTC
824 {
825 GList *l;
826
827 for (l = timecodestamper->ltc_current_tcs.head; l; l = l->next) {
828 TimestampedTimecode *tc = l->data;
829
830 gst_timecodestamper_update_timecode_framerate (timecodestamper, fps_n,
831 fps_d, &tc->timecode, TRUE);
832 }
833 }
834 gst_timecodestamper_update_timecode_framerate (timecodestamper, fps_n, fps_d,
835 timecodestamper->ltc_internal_tc, FALSE);
836 #endif
837
838 return TRUE;
839 }
840
841 static gboolean
gst_timecodestamper_sink_event(GstBaseTransform * trans,GstEvent * event)842 gst_timecodestamper_sink_event (GstBaseTransform * trans, GstEvent * event)
843 {
844 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
845 gboolean ret = FALSE;
846
847 GST_DEBUG_OBJECT (trans, "received event %" GST_PTR_FORMAT, event);
848 switch (GST_EVENT_TYPE (event)) {
849 case GST_EVENT_SEGMENT:
850 {
851 GstSegment segment;
852
853 gst_event_copy_segment (event, &segment);
854 if (segment.format != GST_FORMAT_TIME) {
855 GST_ERROR_OBJECT (timecodestamper, "Invalid segment format");
856 gst_event_unref (event);
857 return FALSE;
858 }
859
860 GST_OBJECT_LOCK (timecodestamper);
861 if (timecodestamper->tc_source == GST_TIME_CODE_STAMPER_SOURCE_INTERNAL
862 && GST_EVENT_SEQNUM (event) == timecodestamper->prev_seek_seqnum) {
863 timecodestamper->reset_internal_tc_from_seek = TRUE;
864 timecodestamper->prev_seek_seqnum = GST_SEQNUM_INVALID;
865 }
866 GST_OBJECT_UNLOCK (timecodestamper);
867
868 break;
869 }
870 case GST_EVENT_CAPS:
871 {
872 GstCaps *caps;
873 gboolean latency_changed;
874 const gchar *interlace_mode;
875 GstStructure *s;
876 gint fps_n, fps_d;
877
878 GST_OBJECT_LOCK (timecodestamper);
879 gst_event_parse_caps (event, &caps);
880
881 s = gst_caps_get_structure (caps, 0);
882
883 if (!gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
884 GST_ERROR_OBJECT (timecodestamper, "Expected framerate in caps");
885 GST_OBJECT_UNLOCK (timecodestamper);
886 gst_event_unref (event);
887 return FALSE;
888 }
889
890 if (fps_n == 0) {
891 GST_ERROR_OBJECT (timecodestamper,
892 "Non-constant frame rate found. Refusing to create a timecode");
893 GST_OBJECT_UNLOCK (timecodestamper);
894 gst_event_unref (event);
895 return FALSE;
896 }
897
898 if ((interlace_mode = gst_structure_get_string (s, "interlace-mode"))) {
899 timecodestamper->interlace_mode =
900 gst_video_interlace_mode_from_string (interlace_mode);
901 }
902
903 latency_changed =
904 gst_timecodestamper_update_framerate (timecodestamper, fps_n, fps_d);
905
906 timecodestamper->fps_n = fps_n;
907 timecodestamper->fps_d = fps_d;
908
909 GST_OBJECT_UNLOCK (timecodestamper);
910
911 if (latency_changed)
912 gst_element_post_message (GST_ELEMENT_CAST (timecodestamper),
913 gst_message_new_latency (GST_OBJECT_CAST (timecodestamper)));
914 break;
915 }
916 #if HAVE_LTC
917 case GST_EVENT_FLUSH_START:
918 g_mutex_lock (&timecodestamper->mutex);
919 timecodestamper->video_flushing = TRUE;
920 timecodestamper->video_current_running_time = GST_CLOCK_TIME_NONE;
921 if (timecodestamper->video_clock_id)
922 gst_clock_id_unschedule (timecodestamper->video_clock_id);
923 g_cond_signal (&timecodestamper->ltc_cond_video);
924 g_mutex_unlock (&timecodestamper->mutex);
925 break;
926 case GST_EVENT_FLUSH_STOP:
927 g_mutex_lock (&timecodestamper->mutex);
928 timecodestamper->video_flushing = FALSE;
929 timecodestamper->video_eos = FALSE;
930 g_mutex_unlock (&timecodestamper->mutex);
931 break;
932 case GST_EVENT_EOS:
933 g_mutex_lock (&timecodestamper->mutex);
934 timecodestamper->video_eos = TRUE;
935 g_cond_signal (&timecodestamper->ltc_cond_audio);
936 g_mutex_unlock (&timecodestamper->mutex);
937 break;
938 #endif
939 default:
940 break;
941 }
942 ret =
943 GST_BASE_TRANSFORM_CLASS (gst_timecodestamper_parent_class)->sink_event
944 (trans, event);
945 return ret;
946 }
947
948 static gboolean
gst_timecodestamper_src_event(GstBaseTransform * trans,GstEvent * event)949 gst_timecodestamper_src_event (GstBaseTransform * trans, GstEvent * event)
950 {
951 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
952
953 GST_DEBUG_OBJECT (trans, "received event %" GST_PTR_FORMAT, event);
954 switch (GST_EVENT_TYPE (event)) {
955 case GST_EVENT_SEEK:
956 {
957 gdouble rate;
958 GstSeekType start_type;
959 gint64 start;
960 GstFormat format;
961
962 gst_event_parse_seek (event, &rate, &format, NULL, &start_type, &start,
963 NULL, NULL);
964
965 if (rate < 0) {
966 GST_ERROR_OBJECT (timecodestamper, "Reverse playback is not supported");
967 return FALSE;
968 }
969
970 if (format != GST_FORMAT_TIME) {
971 GST_ERROR_OBJECT (timecodestamper,
972 "Seeking is only supported in TIME format");
973 return FALSE;
974 }
975
976 GST_OBJECT_LOCK (timecodestamper);
977 if (timecodestamper->fps_d && timecodestamper->fps_n) {
978 timecodestamper->prev_seek_seqnum = GST_EVENT_SEQNUM (event);
979 timecodestamper->seeked_frames = gst_util_uint64_scale (start,
980 timecodestamper->fps_n, timecodestamper->fps_d * GST_SECOND);
981 }
982 GST_OBJECT_UNLOCK (timecodestamper);
983 break;
984 }
985 default:
986 break;
987 }
988
989 return
990 GST_BASE_TRANSFORM_CLASS (gst_timecodestamper_parent_class)->src_event
991 (trans, event);
992 }
993
994 #if HAVE_LTC
995 static gboolean
gst_timecodestamper_query(GstBaseTransform * trans,GstPadDirection direction,GstQuery * query)996 gst_timecodestamper_query (GstBaseTransform * trans,
997 GstPadDirection direction, GstQuery * query)
998 {
999 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (trans);
1000
1001 if (direction == GST_PAD_SINK)
1002 return
1003 GST_BASE_TRANSFORM_CLASS (gst_timecodestamper_parent_class)->query
1004 (trans, direction, query);
1005
1006 switch (GST_QUERY_TYPE (query)) {
1007 case GST_QUERY_LATENCY:{
1008 gboolean res;
1009 gboolean live;
1010 GstClockTime min_latency, max_latency;
1011 GstClockTime latency;
1012
1013 res =
1014 gst_pad_query_default (GST_BASE_TRANSFORM_SRC_PAD (trans),
1015 GST_OBJECT_CAST (trans), query);
1016 g_mutex_lock (&timecodestamper->mutex);
1017 if (res && timecodestamper->fps_n && timecodestamper->fps_d) {
1018 gst_query_parse_latency (query, &live, &min_latency, &max_latency);
1019 if (live && timecodestamper->ltcpad) {
1020 /* Introduce additional LTC for waiting for LTC timecodes. The
1021 * LTC library introduces some as well as the encoding of the LTC
1022 * signal. */
1023 latency = timecodestamper->ltc_extra_latency;
1024 min_latency += latency;
1025 if (max_latency != GST_CLOCK_TIME_NONE)
1026 max_latency += latency;
1027 timecodestamper->latency = min_latency;
1028 GST_DEBUG_OBJECT (timecodestamper,
1029 "Reporting latency min %" GST_TIME_FORMAT " max %" GST_TIME_FORMAT
1030 " ours %" GST_TIME_FORMAT, GST_TIME_ARGS (min_latency),
1031 GST_TIME_ARGS (max_latency), GST_TIME_ARGS (latency));
1032 gst_query_set_latency (query, live, min_latency, max_latency);
1033 } else {
1034 timecodestamper->latency = 0;
1035 }
1036 } else if (res) {
1037 GST_ERROR_OBJECT (timecodestamper,
1038 "Need a known, non-variable framerate to answer LATENCY query");
1039 res = FALSE;
1040 timecodestamper->latency = GST_CLOCK_TIME_NONE;
1041 }
1042 g_mutex_unlock (&timecodestamper->mutex);
1043
1044 return res;
1045 }
1046 default:
1047 return
1048 GST_BASE_TRANSFORM_CLASS (gst_timecodestamper_parent_class)->query
1049 (trans, direction, query);
1050 }
1051 }
1052 #endif
1053
1054 static gboolean
remove_timecode_meta(GstBuffer * buffer,GstMeta ** meta,gpointer user_data)1055 remove_timecode_meta (GstBuffer * buffer, GstMeta ** meta, gpointer user_data)
1056 {
1057 if (meta && *meta && (*meta)->info->api == GST_VIDEO_TIME_CODE_META_API_TYPE) {
1058 *meta = NULL;
1059 }
1060
1061 return TRUE;
1062 }
1063
1064 #if HAVE_LTC
1065 static void
gst_timecodestamper_update_latency(GstTimeCodeStamper * timecodestamper,GstPad * pad,gboolean * live,GstClockTime * latency)1066 gst_timecodestamper_update_latency (GstTimeCodeStamper * timecodestamper,
1067 GstPad * pad, gboolean * live, GstClockTime * latency)
1068 {
1069 GstQuery *query;
1070
1071 query = gst_query_new_latency ();
1072 if (!gst_pad_peer_query (pad, query)) {
1073 GST_WARNING_OBJECT (pad, "Failed to query latency");
1074 gst_pad_mark_reconfigure (pad);
1075 gst_query_unref (query);
1076 return;
1077 }
1078
1079 g_mutex_lock (&timecodestamper->mutex);
1080 gst_query_parse_latency (query, live, latency, NULL);
1081 /* If we're not live, consider a latency of 0 */
1082 if (!*live)
1083 *latency = 0;
1084 GST_DEBUG_OBJECT (pad,
1085 "Queried latency: live %d, min latency %" GST_TIME_FORMAT, *live,
1086 GST_TIME_ARGS (*latency));
1087 g_mutex_unlock (&timecodestamper->mutex);
1088 gst_query_unref (query);
1089 }
1090 #endif
1091
1092 static GstFlowReturn
gst_timecodestamper_transform_ip(GstBaseTransform * vfilter,GstBuffer * buffer)1093 gst_timecodestamper_transform_ip (GstBaseTransform * vfilter,
1094 GstBuffer * buffer)
1095 {
1096 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (vfilter);
1097 GstClockTime running_time, base_time, clock_time;
1098 GstClock *clock;
1099 GstClockTime clock_time_now;
1100 GDateTime *dt_now, *dt_frame;
1101 GstVideoTimeCode *tc = NULL;
1102 gboolean free_tc = FALSE;
1103 GstVideoTimeCodeMeta *tc_meta;
1104 GstFlowReturn flow_ret = GST_FLOW_OK;
1105 GstVideoTimeCodeFlags tc_flags = 0;
1106
1107 if (timecodestamper->fps_n == 0 || timecodestamper->fps_d == 0
1108 || !GST_BUFFER_PTS_IS_VALID (buffer)) {
1109 gst_buffer_unref (buffer);
1110 return GST_FLOW_NOT_NEGOTIATED;
1111 }
1112 #if HAVE_LTC
1113 if (timecodestamper->video_latency == -1
1114 || gst_pad_check_reconfigure (GST_BASE_TRANSFORM_SINK_PAD (vfilter))) {
1115 gst_timecodestamper_update_latency (timecodestamper,
1116 GST_BASE_TRANSFORM_SINK_PAD (vfilter), &timecodestamper->video_live,
1117 &timecodestamper->video_latency);
1118 }
1119 #endif
1120
1121 /* Collect all the current times */
1122 base_time = gst_element_get_base_time (GST_ELEMENT (timecodestamper));
1123 clock = gst_element_get_clock (GST_ELEMENT (timecodestamper));
1124 if (clock) {
1125 clock_time_now = gst_clock_get_time (clock);
1126 gst_object_unref (clock);
1127 } else {
1128 clock_time_now = GST_CLOCK_TIME_NONE;
1129 }
1130
1131 dt_now = g_date_time_new_now_local ();
1132
1133 running_time =
1134 gst_segment_to_running_time (&vfilter->segment, GST_FORMAT_TIME,
1135 GST_BUFFER_PTS (buffer));
1136
1137 if (clock_time_now != GST_CLOCK_TIME_NONE) {
1138 gdouble seconds_diff;
1139
1140 clock_time = running_time + base_time;
1141 if (clock_time_now > clock_time) {
1142 seconds_diff = (clock_time_now - clock_time) / -1000000000.0;
1143 } else {
1144 seconds_diff = (clock_time - clock_time_now) / 1000000000.0;
1145 }
1146 dt_frame = g_date_time_add_seconds (dt_now, seconds_diff);
1147 } else {
1148 /* If we have no clock we can't really know the time of the frame */
1149 dt_frame = g_date_time_ref (dt_now);
1150 }
1151
1152 GST_DEBUG_OBJECT (timecodestamper,
1153 "Handling video frame with running time %" GST_TIME_FORMAT,
1154 GST_TIME_ARGS (running_time));
1155
1156 tc_meta = gst_buffer_get_video_time_code_meta (buffer);
1157
1158 /* Update all our internal timecodes as needed */
1159 GST_OBJECT_LOCK (timecodestamper);
1160
1161 if (timecodestamper->interlace_mode != GST_VIDEO_INTERLACE_MODE_PROGRESSIVE)
1162 tc_flags |= GST_VIDEO_TIME_CODE_FLAGS_INTERLACED;
1163
1164 if (timecodestamper->drop_frame && timecodestamper->fps_d == 1001 &&
1165 (timecodestamper->fps_n == 30000 || timecodestamper->fps_n == 60000))
1166 tc_flags |= GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME;
1167
1168 /* If we don't have an internal timecode yet then either a new one was just
1169 * set via the property or we just started. Initialize it here, otherwise
1170 * increment it by one */
1171 if (!timecodestamper->internal_tc
1172 || timecodestamper->reset_internal_tc_from_seek) {
1173 gchar *tc_str;
1174
1175 if (timecodestamper->internal_tc)
1176 gst_video_time_code_free (timecodestamper->internal_tc);
1177
1178 timecodestamper->reset_internal_tc_from_seek = FALSE;
1179 if (timecodestamper->set_internal_tc) {
1180 timecodestamper->internal_tc =
1181 gst_video_time_code_new (timecodestamper->fps_n,
1182 timecodestamper->fps_d,
1183 timecodestamper->set_internal_tc->config.latest_daily_jam, tc_flags,
1184 timecodestamper->set_internal_tc->hours,
1185 timecodestamper->set_internal_tc->minutes,
1186 timecodestamper->set_internal_tc->seconds,
1187 timecodestamper->set_internal_tc->frames,
1188 timecodestamper->set_internal_tc->field_count);
1189 } else {
1190 timecodestamper->internal_tc =
1191 gst_video_time_code_new (timecodestamper->fps_n,
1192 timecodestamper->fps_d, dt_frame, tc_flags, 0, 0, 0, 0, 0);
1193 if (timecodestamper->seeked_frames > 0) {
1194 GST_DEBUG_OBJECT (timecodestamper,
1195 "Adding %" G_GINT64_FORMAT " frames that were seeked",
1196 timecodestamper->seeked_frames);
1197 gst_video_time_code_add_frames (timecodestamper->internal_tc,
1198 timecodestamper->seeked_frames);
1199 timecodestamper->seeked_frames = -1;
1200 }
1201 }
1202
1203 tc_str = gst_video_time_code_to_string (timecodestamper->internal_tc);
1204 GST_DEBUG_OBJECT (timecodestamper, "Initialized internal timecode to %s",
1205 tc_str);
1206 g_free (tc_str);
1207 } else {
1208 gchar *tc_str;
1209
1210 gst_video_time_code_increment_frame (timecodestamper->internal_tc);
1211 tc_str = gst_video_time_code_to_string (timecodestamper->internal_tc);
1212 GST_DEBUG_OBJECT (timecodestamper, "Incremented internal timecode to %s",
1213 tc_str);
1214 g_free (tc_str);
1215 }
1216
1217 /* If we have a new timecode on the incoming frame, update our last known
1218 * timecode or otherwise increment it by one */
1219 if (tc_meta && (!timecodestamper->last_tc || timecodestamper->tc_auto_resync)) {
1220 gchar *tc_str;
1221
1222 if (timecodestamper->last_tc)
1223 gst_video_time_code_free (timecodestamper->last_tc);
1224 timecodestamper->last_tc = gst_video_time_code_copy (&tc_meta->tc);
1225 timecodestamper->last_tc_running_time = running_time;
1226
1227 tc_str = gst_video_time_code_to_string (timecodestamper->last_tc);
1228 GST_DEBUG_OBJECT (timecodestamper, "Updated upstream timecode to %s",
1229 tc_str);
1230 g_free (tc_str);
1231 } else {
1232 if (timecodestamper->last_tc) {
1233 if (timecodestamper->tc_auto_resync
1234 && timecodestamper->tc_timeout != GST_CLOCK_TIME_NONE
1235 && (running_time + timecodestamper->tc_timeout <
1236 timecodestamper->last_tc_running_time
1237 || running_time >=
1238 timecodestamper->last_tc_running_time +
1239 timecodestamper->tc_timeout)) {
1240 if (timecodestamper->last_tc)
1241 gst_video_time_code_free (timecodestamper->last_tc);
1242 timecodestamper->last_tc = NULL;
1243 timecodestamper->last_tc_running_time = GST_CLOCK_TIME_NONE;
1244 GST_DEBUG_OBJECT (timecodestamper, "Upstream timecode timed out");
1245 } else {
1246 gchar *tc_str;
1247
1248 gst_video_time_code_increment_frame (timecodestamper->last_tc);
1249
1250 tc_str = gst_video_time_code_to_string (timecodestamper->last_tc);
1251 GST_DEBUG_OBJECT (timecodestamper,
1252 "Incremented upstream timecode to %s", tc_str);
1253 g_free (tc_str);
1254 }
1255 } else {
1256 GST_DEBUG_OBJECT (timecodestamper, "Never saw an upstream timecode");
1257 }
1258 }
1259
1260 /* Update RTC-based timecode */
1261 {
1262 GstVideoTimeCode rtc_timecode_now;
1263 gchar *tc_str, *dt_str;
1264
1265 /* Create timecode for the current frame time */
1266 memset (&rtc_timecode_now, 0, sizeof (rtc_timecode_now));
1267 gst_video_time_code_init_from_date_time_full (&rtc_timecode_now,
1268 timecodestamper->fps_n, timecodestamper->fps_d, dt_frame, tc_flags, 0);
1269
1270 tc_str = gst_video_time_code_to_string (&rtc_timecode_now);
1271 dt_str = g_date_time_format (dt_frame, "%F %R %z");
1272 GST_DEBUG_OBJECT (timecodestamper,
1273 "Created RTC timecode %s for %s (%06u us)", tc_str, dt_str,
1274 g_date_time_get_microsecond (dt_frame));
1275 g_free (dt_str);
1276 g_free (tc_str);
1277
1278 /* If we don't have an RTC timecode yet, directly initialize with this one */
1279 if (!timecodestamper->rtc_tc) {
1280 timecodestamper->rtc_tc = gst_video_time_code_copy (&rtc_timecode_now);
1281 tc_str = gst_video_time_code_to_string (timecodestamper->rtc_tc);
1282 GST_DEBUG_OBJECT (timecodestamper, "Initialized RTC timecode to %s",
1283 tc_str);
1284 g_free (tc_str);
1285 } else {
1286 GstClockTime rtc_now_time, rtc_tc_time;
1287 GstClockTime rtc_diff;
1288
1289 /* Increment the old RTC timecode to this frame */
1290 gst_video_time_code_increment_frame (timecodestamper->rtc_tc);
1291
1292 /* Otherwise check if we drifted too much and need to resync */
1293 rtc_tc_time =
1294 gst_video_time_code_nsec_since_daily_jam (timecodestamper->rtc_tc);
1295 rtc_now_time =
1296 gst_video_time_code_nsec_since_daily_jam (&rtc_timecode_now);
1297 if (rtc_tc_time > rtc_now_time)
1298 rtc_diff = rtc_tc_time - rtc_now_time;
1299 else
1300 rtc_diff = rtc_now_time - rtc_tc_time;
1301
1302 if (timecodestamper->rtc_auto_resync
1303 && timecodestamper->rtc_max_drift != GST_CLOCK_TIME_NONE
1304 && rtc_diff > timecodestamper->rtc_max_drift) {
1305 gst_video_time_code_free (timecodestamper->rtc_tc);
1306 timecodestamper->rtc_tc = gst_video_time_code_copy (&rtc_timecode_now);
1307 tc_str = gst_video_time_code_to_string (timecodestamper->rtc_tc);
1308 GST_DEBUG_OBJECT (timecodestamper,
1309 "Updated RTC timecode to %s (%s%" GST_TIME_FORMAT " drift)", tc_str,
1310 (rtc_tc_time > rtc_now_time ? "-" : "+"), GST_TIME_ARGS (rtc_diff));
1311 g_free (tc_str);
1312 } else {
1313 /* Else nothing to do here, we use the current one */
1314 tc_str = gst_video_time_code_to_string (timecodestamper->rtc_tc);
1315 GST_DEBUG_OBJECT (timecodestamper,
1316 "Incremented RTC timecode to %s (%s%" GST_TIME_FORMAT " drift)",
1317 tc_str, (rtc_tc_time > rtc_now_time ? "-" : "+"),
1318 GST_TIME_ARGS (rtc_diff));
1319 g_free (tc_str);
1320 }
1321 }
1322
1323 gst_video_time_code_clear (&rtc_timecode_now);
1324 }
1325 GST_OBJECT_UNLOCK (timecodestamper);
1326
1327 /* Update LTC-based timecode as needed */
1328 #if HAVE_LTC
1329 if (timecodestamper->ltcpad) {
1330 GstClockTime frame_duration;
1331 gchar *tc_str;
1332 TimestampedTimecode *ltc_tc;
1333 gboolean updated_internal = FALSE;
1334
1335 frame_duration = gst_util_uint64_scale_int_ceil (GST_SECOND,
1336 timecodestamper->fps_d, timecodestamper->fps_n);
1337
1338 g_mutex_lock (&timecodestamper->mutex);
1339
1340 timecodestamper->video_current_running_time = running_time;
1341
1342 /* Wait to compensate for the latency we introduce and to allow the LTC
1343 * audio to provide enough audio to extract timecodes, or until the video
1344 * pad is flushing or the LTC pad is EOS.
1345 * In non-live mode we introduce 4 frames of latency compared to the LTC
1346 * audio, see LATENCY query handling for details. */
1347 if (timecodestamper->video_live) {
1348 GstClock *clock =
1349 gst_element_get_clock (GST_ELEMENT_CAST (timecodestamper));
1350
1351 if (clock) {
1352 GstClockID clock_id;
1353 GstClockTime base_time =
1354 gst_element_get_base_time (GST_ELEMENT_CAST (timecodestamper));
1355 GstClockTime wait_time;
1356
1357 /* If we have no latency yet then wait at least for the LTC extra
1358 * latency. See LATENCY query handling for details. */
1359 if (timecodestamper->latency == GST_CLOCK_TIME_NONE) {
1360 wait_time =
1361 base_time + running_time + timecodestamper->ltc_extra_latency;
1362 } else {
1363 wait_time = base_time + running_time + timecodestamper->latency;
1364 }
1365
1366 GST_TRACE_OBJECT (timecodestamper,
1367 "Waiting for clock to reach %" GST_TIME_FORMAT
1368 " (base time %" GST_TIME_FORMAT
1369 " + running time %" GST_TIME_FORMAT
1370 " + latency %" GST_TIME_FORMAT
1371 "), now %" GST_TIME_FORMAT,
1372 GST_TIME_ARGS (wait_time),
1373 GST_TIME_ARGS (base_time),
1374 GST_TIME_ARGS (running_time),
1375 GST_TIME_ARGS (timecodestamper->latency ==
1376 GST_CLOCK_TIME_NONE ? timecodestamper->ltc_extra_latency :
1377 timecodestamper->latency),
1378 GST_TIME_ARGS (gst_clock_get_time (clock))
1379 );
1380 clock_id = gst_clock_new_single_shot_id (clock, wait_time);
1381
1382 timecodestamper->video_clock_id = clock_id;
1383 g_mutex_unlock (&timecodestamper->mutex);
1384 gst_clock_id_wait (clock_id, NULL);
1385 g_mutex_lock (&timecodestamper->mutex);
1386 timecodestamper->video_clock_id = NULL;
1387 gst_clock_id_unref (clock_id);
1388 gst_object_unref (clock);
1389 } else {
1390 GST_WARNING_OBJECT (timecodestamper,
1391 "No clock in live mode, not waiting");
1392 }
1393 } else {
1394 while ((timecodestamper->ltc_current_running_time == GST_CLOCK_TIME_NONE
1395 || timecodestamper->ltc_current_running_time <
1396 running_time + 8 * frame_duration)
1397 && !timecodestamper->video_flushing && !timecodestamper->ltc_eos) {
1398 GST_TRACE_OBJECT (timecodestamper,
1399 "Waiting for LTC audio to advance, EOS or flushing");
1400 g_cond_wait (&timecodestamper->ltc_cond_video, &timecodestamper->mutex);
1401 }
1402 }
1403
1404 if (timecodestamper->video_flushing) {
1405 g_mutex_unlock (&timecodestamper->mutex);
1406 flow_ret = GST_FLOW_FLUSHING;
1407 goto out;
1408 }
1409
1410 GST_OBJECT_LOCK (timecodestamper);
1411 /* Take timecodes out of the queue until we're at the current video
1412 * position. */
1413 while ((ltc_tc = g_queue_pop_head (&timecodestamper->ltc_current_tcs))) {
1414 /* First update framerate and flags according to the video stream if not
1415 * done yet */
1416 if (ltc_tc->timecode.config.fps_d == 0) {
1417 gint fps_n_div =
1418 ((gdouble) timecodestamper->fps_n) /
1419 timecodestamper->fps_d > 30 ? 2 : 1;
1420
1421 ltc_tc->timecode.config.flags = tc_flags;
1422 ltc_tc->timecode.config.fps_n = timecodestamper->fps_n / fps_n_div;
1423 ltc_tc->timecode.config.fps_d = timecodestamper->fps_d;
1424 }
1425
1426 tc_str = gst_video_time_code_to_string (<c_tc->timecode);
1427 GST_INFO_OBJECT (timecodestamper,
1428 "Retrieved LTC timecode %s at %" GST_TIME_FORMAT
1429 " (%u timecodes queued)", tc_str,
1430 GST_TIME_ARGS (ltc_tc->running_time),
1431 g_queue_get_length (&timecodestamper->ltc_current_tcs));
1432 g_free (tc_str);
1433
1434 if (!gst_video_time_code_is_valid (<c_tc->timecode)) {
1435 tc_str = gst_video_time_code_to_string (<c_tc->timecode);
1436 GST_INFO_OBJECT (timecodestamper, "Invalid LTC timecode %s", tc_str);
1437 g_free (tc_str);
1438 gst_video_time_code_clear (<c_tc->timecode);
1439 g_free (ltc_tc);
1440 ltc_tc = NULL;
1441 continue;
1442 }
1443
1444 /* A timecode frame that starts +/- half a frame to the
1445 * video frame is considered belonging to that video frame.
1446 *
1447 * If it's further ahead than half a frame duration, break out of
1448 * the loop here and reconsider on the next frame. */
1449 if (ABSDIFF (running_time, ltc_tc->running_time) <= frame_duration / 2) {
1450 /* If we're resyncing LTC in general, directly replace the current
1451 * LTC timecode with the new one we read. Otherwise we'll continue
1452 * counting based on the previous timecode we had
1453 */
1454 if (timecodestamper->ltc_auto_resync) {
1455 if (timecodestamper->ltc_internal_tc)
1456 gst_video_time_code_free (timecodestamper->ltc_internal_tc);
1457 timecodestamper->ltc_internal_tc =
1458 gst_video_time_code_copy (<c_tc->timecode);
1459 timecodestamper->ltc_internal_running_time = ltc_tc->running_time;
1460 updated_internal = TRUE;
1461 GST_INFO_OBJECT (timecodestamper, "Resynced internal LTC counter");
1462 }
1463
1464 /* And store it back for the next frame in case it has more or less
1465 * the same running time */
1466 g_queue_push_head (&timecodestamper->ltc_current_tcs,
1467 g_steal_pointer (<c_tc));
1468 break;
1469 } else if (ltc_tc->running_time > running_time
1470 && ltc_tc->running_time - running_time > frame_duration / 2) {
1471 /* Store it back for the next frame */
1472 g_queue_push_head (&timecodestamper->ltc_current_tcs,
1473 g_steal_pointer (<c_tc));
1474 ltc_tc = NULL;
1475 break;
1476 }
1477
1478 /* otherwise it's in the past and we need to consider the next
1479 * timecode. Read a new one */
1480 gst_video_time_code_clear (<c_tc->timecode);
1481 g_free (ltc_tc);
1482 ltc_tc = NULL;
1483 }
1484
1485 /* If we didn't update from LTC above, increment our internal timecode
1486 * for this frame */
1487 if (!updated_internal && timecodestamper->ltc_internal_tc) {
1488 gst_video_time_code_increment_frame (timecodestamper->ltc_internal_tc);
1489 }
1490
1491 if (timecodestamper->ltc_internal_tc) {
1492 if (timecodestamper->ltc_auto_resync
1493 && timecodestamper->ltc_timeout != GST_CLOCK_TIME_NONE
1494 && (running_time + timecodestamper->ltc_timeout <
1495 timecodestamper->ltc_internal_running_time
1496 || running_time >=
1497 timecodestamper->ltc_internal_running_time +
1498 timecodestamper->ltc_timeout)) {
1499 if (timecodestamper->ltc_internal_tc)
1500 gst_video_time_code_free (timecodestamper->ltc_internal_tc);
1501 timecodestamper->ltc_internal_tc = NULL;
1502 GST_DEBUG_OBJECT (timecodestamper, "LTC timecode timed out");
1503 timecodestamper->ltc_internal_running_time = GST_CLOCK_TIME_NONE;
1504 } else {
1505 tc_str =
1506 gst_video_time_code_to_string (timecodestamper->ltc_internal_tc);
1507 GST_DEBUG_OBJECT (timecodestamper, "Updated LTC timecode to %s",
1508 tc_str);
1509 g_free (tc_str);
1510 }
1511 } else {
1512 GST_DEBUG_OBJECT (timecodestamper, "Have no LTC timecode yet");
1513 }
1514
1515 GST_OBJECT_UNLOCK (timecodestamper);
1516
1517 g_cond_signal (&timecodestamper->ltc_cond_audio);
1518
1519 g_mutex_unlock (&timecodestamper->mutex);
1520 }
1521 #endif
1522
1523 GST_OBJECT_LOCK (timecodestamper);
1524 switch (timecodestamper->tc_source) {
1525 case GST_TIME_CODE_STAMPER_SOURCE_INTERNAL:
1526 tc = timecodestamper->internal_tc;
1527 break;
1528 case GST_TIME_CODE_STAMPER_SOURCE_ZERO:
1529 tc = gst_video_time_code_new (timecodestamper->fps_n,
1530 timecodestamper->fps_d, NULL, tc_flags, 0, 0, 0, 0, 0);
1531 free_tc = TRUE;
1532 break;
1533 case GST_TIME_CODE_STAMPER_SOURCE_LAST_KNOWN:
1534 tc = timecodestamper->last_tc;
1535 if (!tc)
1536 tc = timecodestamper->internal_tc;
1537 break;
1538 case GST_TIME_CODE_STAMPER_SOURCE_LAST_KNOWN_OR_ZERO:
1539 tc = timecodestamper->last_tc;
1540 if (!tc) {
1541 tc = gst_video_time_code_new (timecodestamper->fps_n,
1542 timecodestamper->fps_d, NULL, tc_flags, 0, 0, 0, 0, 0);
1543 free_tc = TRUE;
1544 }
1545 break;
1546 case GST_TIME_CODE_STAMPER_SOURCE_LTC:
1547 #if HAVE_LTC
1548 if (timecodestamper->ltc_internal_tc)
1549 tc = timecodestamper->ltc_internal_tc;
1550 #endif
1551 if (!tc) {
1552 tc = gst_video_time_code_new (timecodestamper->fps_n,
1553 timecodestamper->fps_d, NULL, tc_flags, 0, 0, 0, 0, 0);
1554 free_tc = TRUE;
1555 }
1556 break;
1557 case GST_TIME_CODE_STAMPER_SOURCE_RTC:
1558 tc = timecodestamper->rtc_tc;
1559 break;
1560 }
1561
1562 switch (timecodestamper->tc_set) {
1563 case GST_TIME_CODE_STAMPER_SET_NEVER:
1564 break;
1565 case GST_TIME_CODE_STAMPER_SET_KEEP:
1566 if (!tc_meta && tc) {
1567 gchar *tc_str;
1568
1569 if (timecodestamper->timecode_offset) {
1570 if (!free_tc) {
1571 tc = gst_video_time_code_copy (tc);
1572 free_tc = TRUE;
1573 }
1574 gst_video_time_code_add_frames (tc, timecodestamper->timecode_offset);
1575 }
1576
1577 tc_str = gst_video_time_code_to_string (tc);
1578 GST_DEBUG_OBJECT (timecodestamper, "Storing timecode %s", tc_str);
1579 g_free (tc_str);
1580
1581 gst_buffer_add_video_time_code_meta (buffer, tc);
1582 }
1583 break;
1584 case GST_TIME_CODE_STAMPER_SET_ALWAYS:
1585 gst_buffer_foreach_meta (buffer, remove_timecode_meta, NULL);
1586 if (tc) {
1587 gchar *tc_str;
1588
1589 if (timecodestamper->timecode_offset) {
1590 if (!free_tc) {
1591 tc = gst_video_time_code_copy (tc);
1592 free_tc = TRUE;
1593 }
1594 gst_video_time_code_add_frames (tc, timecodestamper->timecode_offset);
1595 }
1596
1597 tc_str = gst_video_time_code_to_string (tc);
1598 GST_DEBUG_OBJECT (timecodestamper, "Storing timecode %s", tc_str);
1599 g_free (tc_str);
1600
1601 gst_buffer_add_video_time_code_meta (buffer, tc);
1602 }
1603 break;
1604 }
1605
1606 GST_OBJECT_UNLOCK (timecodestamper);
1607
1608 if (timecodestamper->post_messages && tc) {
1609 GstClockTime stream_time, running_time, duration;
1610 GstStructure *s;
1611 GstMessage *msg;
1612
1613 running_time =
1614 gst_segment_to_running_time (&vfilter->segment, GST_FORMAT_TIME,
1615 GST_BUFFER_PTS (buffer));
1616 stream_time =
1617 gst_segment_to_stream_time (&vfilter->segment, GST_FORMAT_TIME,
1618 GST_BUFFER_PTS (buffer));
1619 duration =
1620 gst_util_uint64_scale_int (GST_SECOND, timecodestamper->fps_d,
1621 timecodestamper->fps_n);
1622 s = gst_structure_new ("timecodestamper", "timestamp", G_TYPE_UINT64,
1623 GST_BUFFER_PTS (buffer), "stream-time", G_TYPE_UINT64, stream_time,
1624 "running-time", G_TYPE_UINT64, running_time, "duration",
1625 G_TYPE_UINT64, duration, "timecode", GST_TYPE_VIDEO_TIME_CODE, tc,
1626 NULL);
1627 msg = gst_message_new_element (GST_OBJECT (timecodestamper), s);
1628 gst_element_post_message (GST_ELEMENT (timecodestamper), msg);
1629 }
1630 #if HAVE_LTC
1631 out:
1632 #endif
1633
1634 if (dt_now)
1635 g_date_time_unref (dt_now);
1636 if (dt_frame)
1637 g_date_time_unref (dt_frame);
1638 if (free_tc && tc)
1639 gst_video_time_code_free (tc);
1640
1641 return flow_ret;
1642 }
1643
1644 static GstPad *
gst_timecodestamper_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name_templ,const GstCaps * caps)1645 gst_timecodestamper_request_new_pad (GstElement * element,
1646 GstPadTemplate * templ, const gchar * name_templ, const GstCaps * caps)
1647 {
1648 #if HAVE_LTC
1649 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (element);
1650
1651 GST_OBJECT_LOCK (timecodestamper);
1652 if (timecodestamper->ltcpad) {
1653 GST_OBJECT_UNLOCK (timecodestamper);
1654 return NULL;
1655 }
1656
1657 if (GST_STATE (timecodestamper) > GST_STATE_READY ||
1658 GST_STATE_TARGET (timecodestamper) > GST_STATE_READY) {
1659 GST_ERROR_OBJECT (timecodestamper,
1660 "LTC audio pad can only be requested in NULL or READY state");
1661 GST_OBJECT_UNLOCK (timecodestamper);
1662 return NULL;
1663 }
1664
1665 timecodestamper->ltcpad = gst_pad_new_from_static_template
1666 (&gst_timecodestamper_ltc_template, "ltc_sink");
1667
1668 gst_pad_set_chain_function (timecodestamper->ltcpad,
1669 GST_DEBUG_FUNCPTR (gst_timecodestamper_ltcpad_chain));
1670 gst_pad_set_event_function (timecodestamper->ltcpad,
1671 GST_DEBUG_FUNCPTR (gst_timecodestamper_ltcpad_event));
1672 gst_pad_set_query_function (timecodestamper->ltcpad,
1673 GST_DEBUG_FUNCPTR (gst_timecodestamper_ltcpad_query));
1674 gst_pad_set_activatemode_function (timecodestamper->ltcpad,
1675 GST_DEBUG_FUNCPTR (gst_timecodestamper_ltcpad_activatemode));
1676
1677 GST_OBJECT_UNLOCK (timecodestamper);
1678
1679 g_mutex_lock (&timecodestamper->mutex);
1680 timecodestamper->audio_live = FALSE;
1681 timecodestamper->audio_latency = GST_CLOCK_TIME_NONE;
1682 g_mutex_unlock (&timecodestamper->mutex);
1683
1684 gst_element_add_pad (element, timecodestamper->ltcpad);
1685
1686 gst_element_post_message (GST_ELEMENT_CAST (timecodestamper),
1687 gst_message_new_latency (GST_OBJECT_CAST (timecodestamper)));
1688
1689 return timecodestamper->ltcpad;
1690 #else
1691 return NULL;
1692 #endif
1693 }
1694
1695 static void
gst_timecodestamper_release_pad(GstElement * element,GstPad * pad)1696 gst_timecodestamper_release_pad (GstElement * element, GstPad * pad)
1697 {
1698 #if HAVE_LTC
1699 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (element);
1700
1701 GST_OBJECT_LOCK (timecodestamper);
1702 if (timecodestamper->ltcpad != pad) {
1703 GST_OBJECT_UNLOCK (timecodestamper);
1704 return;
1705 }
1706
1707 timecodestamper->ltcpad = NULL;
1708
1709 if (timecodestamper->ltc_internal_tc != NULL) {
1710 gst_video_time_code_free (timecodestamper->ltc_internal_tc);
1711 timecodestamper->ltc_internal_tc = NULL;
1712 }
1713 timecodestamper->ltc_internal_running_time = GST_CLOCK_TIME_NONE;
1714
1715 {
1716 TimestampedTimecode *tc;
1717 while ((tc = g_queue_pop_tail (&timecodestamper->ltc_current_tcs))) {
1718 gst_video_time_code_clear (&tc->timecode);
1719 g_free (tc);
1720 }
1721 }
1722 GST_OBJECT_UNLOCK (timecodestamper);
1723
1724 gst_pad_set_active (pad, FALSE);
1725
1726 g_mutex_lock (&timecodestamper->mutex);
1727 timecodestamper->ltc_flushing = TRUE;
1728 timecodestamper->ltc_eos = TRUE;
1729 g_cond_signal (&timecodestamper->ltc_cond_video);
1730 g_cond_signal (&timecodestamper->ltc_cond_audio);
1731
1732 gst_audio_info_init (&timecodestamper->ainfo);
1733 gst_segment_init (&timecodestamper->ltc_segment, GST_FORMAT_UNDEFINED);
1734
1735 timecodestamper->ltc_first_running_time = GST_CLOCK_TIME_NONE;
1736 timecodestamper->ltc_current_running_time = GST_CLOCK_TIME_NONE;
1737
1738 if (timecodestamper->ltc_dec) {
1739 ltc_decoder_free (timecodestamper->ltc_dec);
1740 timecodestamper->ltc_dec = NULL;
1741 }
1742
1743 if (timecodestamper->stream_align) {
1744 gst_audio_stream_align_free (timecodestamper->stream_align);
1745 timecodestamper->stream_align = NULL;
1746 }
1747
1748 timecodestamper->ltc_total = 0;
1749
1750 timecodestamper->audio_live = FALSE;
1751 timecodestamper->audio_latency = GST_CLOCK_TIME_NONE;
1752 g_mutex_unlock (&timecodestamper->mutex);
1753
1754 gst_element_post_message (GST_ELEMENT_CAST (timecodestamper),
1755 gst_message_new_latency (GST_OBJECT_CAST (timecodestamper)));
1756
1757 gst_element_remove_pad (element, pad);
1758 #endif
1759 }
1760
1761 #if HAVE_LTC
1762 static GstFlowReturn
gst_timecodestamper_ltcpad_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)1763 gst_timecodestamper_ltcpad_chain (GstPad * pad,
1764 GstObject * parent, GstBuffer * buffer)
1765 {
1766 GstFlowReturn fr = GST_FLOW_OK;
1767 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (parent);
1768 GstMapInfo map;
1769 GstClockTime timestamp, running_time, duration;
1770 guint nsamples;
1771 gboolean discont;
1772
1773 if (timecodestamper->audio_latency == -1 || gst_pad_check_reconfigure (pad)) {
1774 gst_timecodestamper_update_latency (timecodestamper, pad,
1775 &timecodestamper->audio_live, &timecodestamper->audio_latency);
1776 }
1777
1778 g_mutex_lock (&timecodestamper->mutex);
1779 if (timecodestamper->ltc_flushing) {
1780 g_mutex_unlock (&timecodestamper->mutex);
1781 gst_buffer_unref (buffer);
1782 return GST_FLOW_FLUSHING;
1783 }
1784
1785 nsamples = gst_buffer_get_size (buffer) /
1786 GST_AUDIO_INFO_BPF (&timecodestamper->ainfo);
1787
1788 if (!timecodestamper->stream_align) {
1789 timecodestamper->stream_align =
1790 gst_audio_stream_align_new (timecodestamper->ainfo.rate,
1791 500 * GST_MSECOND, 20 * GST_MSECOND);
1792 }
1793
1794 discont =
1795 gst_audio_stream_align_process (timecodestamper->stream_align,
1796 GST_BUFFER_IS_DISCONT (buffer), GST_BUFFER_PTS (buffer), nsamples,
1797 ×tamp, &duration, NULL);
1798
1799 if (discont) {
1800 if (timecodestamper->ltc_dec) {
1801 GST_WARNING_OBJECT (timecodestamper, "Got discont at %" GST_TIME_FORMAT,
1802 GST_TIME_ARGS (timestamp));
1803 ltc_decoder_queue_flush (timecodestamper->ltc_dec);
1804 }
1805 timecodestamper->ltc_total = 0;
1806 }
1807
1808 if (!timecodestamper->ltc_dec) {
1809 gint samples_per_frame = 1920;
1810
1811 GST_OBJECT_LOCK (timecodestamper);
1812 /* This is only for initialization and needs to be somewhat close to the
1813 * real value. It will be tracked automatically afterwards */
1814 if (timecodestamper->fps_n) {
1815 samples_per_frame = timecodestamper->ainfo.rate *
1816 timecodestamper->fps_d / timecodestamper->fps_n;
1817 }
1818 GST_OBJECT_UNLOCK (timecodestamper);
1819
1820 timecodestamper->ltc_dec =
1821 ltc_decoder_create (samples_per_frame, DEFAULT_LTC_QUEUE);
1822 timecodestamper->ltc_total = 0;
1823 }
1824
1825 running_time = gst_segment_to_running_time (&timecodestamper->ltc_segment,
1826 GST_FORMAT_TIME, timestamp);
1827
1828 GST_DEBUG_OBJECT (timecodestamper,
1829 "Handling LTC audio buffer at %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT
1830 " (offset %" G_GUINT64_FORMAT ")",
1831 GST_TIME_ARGS (running_time),
1832 GST_TIME_ARGS (running_time + duration),
1833 (guint64) timecodestamper->ltc_total);
1834
1835 if (timecodestamper->ltc_total == 0) {
1836 timecodestamper->ltc_first_running_time = running_time;
1837 }
1838
1839 gst_buffer_map (buffer, &map, GST_MAP_READ);
1840 ltc_decoder_write (timecodestamper->ltc_dec, map.data, map.size,
1841 timecodestamper->ltc_total);
1842 timecodestamper->ltc_total += map.size;
1843 gst_buffer_unmap (buffer, &map);
1844
1845 /* Now read all the timecodes from the decoder that are currently available
1846 * and store them in our own queue, which gives us more control over how
1847 * things are working. */
1848 {
1849 LTCFrameExt ltc_frame;
1850
1851 while (ltc_decoder_read (timecodestamper->ltc_dec, <c_frame) == 1) {
1852 SMPTETimecode stc;
1853 TimestampedTimecode *ltc_tc;
1854 GstClockTime ltc_running_time;
1855
1856 if (ltc_frame.off_start < 0) {
1857 GstClockTime offset =
1858 gst_util_uint64_scale (GST_SECOND, -ltc_frame.off_start,
1859 timecodestamper->ainfo.rate);
1860
1861 if (offset > timecodestamper->ltc_first_running_time)
1862 ltc_running_time = 0;
1863 else
1864 ltc_running_time = timecodestamper->ltc_first_running_time - offset;
1865 } else {
1866 ltc_running_time = timecodestamper->ltc_first_running_time +
1867 gst_util_uint64_scale (GST_SECOND, ltc_frame.off_start,
1868 timecodestamper->ainfo.rate);
1869 }
1870
1871 ltc_frame_to_time (&stc, <c_frame.ltc, 0);
1872 GST_INFO_OBJECT (timecodestamper,
1873 "Got LTC timecode %02d:%02d:%02d:%02d at %" GST_TIME_FORMAT,
1874 stc.hours, stc.mins, stc.secs, stc.frame,
1875 GST_TIME_ARGS (ltc_running_time));
1876
1877 ltc_tc = g_new0 (TimestampedTimecode, 1);
1878 ltc_tc->running_time = ltc_running_time;
1879 /* We fill in the framerate and other metadata later */
1880 gst_video_time_code_init (<c_tc->timecode,
1881 0, 0, timecodestamper->ltc_daily_jam, 0,
1882 stc.hours, stc.mins, stc.secs, stc.frame, 0);
1883
1884 /* If we have a discontinuity it might happen that we're getting
1885 * timecodes that are in the past relative to timecodes we already have
1886 * in our queue. We have to get rid of all the timecodes that are in the
1887 * future now. */
1888 if (discont) {
1889 TimestampedTimecode *tmp;
1890
1891 while ((tmp = g_queue_peek_tail (&timecodestamper->ltc_current_tcs)) &&
1892 tmp->running_time >= ltc_running_time) {
1893 gst_video_time_code_clear (&tmp->timecode);
1894 g_free (tmp);
1895 g_queue_pop_tail (&timecodestamper->ltc_current_tcs);
1896 }
1897
1898 g_queue_push_tail (&timecodestamper->ltc_current_tcs,
1899 g_steal_pointer (<c_tc));
1900 } else {
1901 g_queue_push_tail (&timecodestamper->ltc_current_tcs,
1902 g_steal_pointer (<c_tc));
1903 }
1904 }
1905 }
1906
1907 /* Notify the video streaming thread that new data is available */
1908 g_cond_signal (&timecodestamper->ltc_cond_video);
1909
1910 /* Wait until video has caught up, if needed */
1911 if (timecodestamper->audio_live) {
1912 /* In live-mode, do no waiting as we're guaranteed to be more or less in
1913 * sync (~latency) with the video */
1914 } else {
1915 /* If we're ahead of the video, wait until the video has caught up.
1916 * Otherwise don't wait and drop any too old items from the ringbuffer */
1917 while ((timecodestamper->video_current_running_time == GST_CLOCK_TIME_NONE
1918 || running_time + duration >=
1919 timecodestamper->video_current_running_time)
1920 && timecodestamper->ltc_dec
1921 && g_queue_get_length (&timecodestamper->ltc_current_tcs) >
1922 DEFAULT_LTC_QUEUE / 2 && !timecodestamper->video_eos
1923 && !timecodestamper->ltc_flushing) {
1924 GST_TRACE_OBJECT (timecodestamper,
1925 "Waiting for video to advance, EOS or flushing");
1926 g_cond_wait (&timecodestamper->ltc_cond_audio, &timecodestamper->mutex);
1927 }
1928 }
1929
1930 if (timecodestamper->ltc_flushing)
1931 fr = GST_FLOW_FLUSHING;
1932 else
1933 fr = GST_FLOW_OK;
1934
1935 g_mutex_unlock (&timecodestamper->mutex);
1936
1937 gst_buffer_unref (buffer);
1938 return fr;
1939 }
1940
1941 static gboolean
gst_timecodestamper_ltcpad_event(GstPad * pad,GstObject * parent,GstEvent * event)1942 gst_timecodestamper_ltcpad_event (GstPad * pad,
1943 GstObject * parent, GstEvent * event)
1944 {
1945 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (parent);
1946
1947 GstCaps *caps;
1948 gboolean ret = TRUE;
1949
1950 switch (GST_EVENT_TYPE (event)) {
1951 case GST_EVENT_CAPS:
1952 gst_event_parse_caps (event, &caps);
1953
1954 if (!gst_audio_info_from_caps (&timecodestamper->ainfo, caps)) {
1955 gst_event_unref (event);
1956 return FALSE;
1957 }
1958
1959 if (timecodestamper->stream_align) {
1960 gst_audio_stream_align_set_rate (timecodestamper->stream_align,
1961 timecodestamper->ainfo.rate);
1962 }
1963
1964 break;
1965 case GST_EVENT_SEGMENT:
1966 gst_event_copy_segment (event, &timecodestamper->ltc_segment);
1967 break;
1968
1969 case GST_EVENT_FLUSH_START:
1970 g_mutex_lock (&timecodestamper->mutex);
1971 timecodestamper->ltc_flushing = TRUE;
1972 g_cond_signal (&timecodestamper->ltc_cond_audio);
1973 g_mutex_unlock (&timecodestamper->mutex);
1974 break;
1975 case GST_EVENT_FLUSH_STOP:
1976 g_mutex_lock (&timecodestamper->mutex);
1977 timecodestamper->ltc_flushing = FALSE;
1978 timecodestamper->ltc_eos = FALSE;
1979 gst_segment_init (&timecodestamper->ltc_segment, GST_FORMAT_UNDEFINED);
1980 g_mutex_unlock (&timecodestamper->mutex);
1981 break;
1982 case GST_EVENT_EOS:
1983 g_mutex_lock (&timecodestamper->mutex);
1984 timecodestamper->ltc_eos = TRUE;
1985 g_cond_signal (&timecodestamper->ltc_cond_video);
1986 g_mutex_unlock (&timecodestamper->mutex);
1987 break;
1988
1989 default:
1990 break;
1991 }
1992
1993 gst_event_unref (event);
1994 return ret;
1995 }
1996
1997 static gboolean
gst_timecodestamper_ltcpad_query(GstPad * pad,GstObject * parent,GstQuery * query)1998 gst_timecodestamper_ltcpad_query (GstPad * pad,
1999 GstObject * parent, GstQuery * query)
2000 {
2001 GstCaps *caps, *filter, *tcaps;
2002
2003 switch (GST_QUERY_TYPE (query)) {
2004 case GST_QUERY_CAPS:
2005 gst_query_parse_caps (query, &filter);
2006 tcaps = gst_pad_get_pad_template_caps (pad);
2007 if (filter)
2008 caps = gst_caps_intersect_full (tcaps, filter,
2009 GST_CAPS_INTERSECT_FIRST);
2010 else
2011 caps = gst_caps_ref (tcaps);
2012 gst_query_set_caps_result (query, caps);
2013 gst_caps_unref (tcaps);
2014 gst_caps_unref (caps);
2015 return TRUE;
2016 default:
2017 return gst_pad_query_default (pad, parent, query);
2018 }
2019 }
2020
2021 static gboolean
gst_timecodestamper_ltcpad_activatemode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)2022 gst_timecodestamper_ltcpad_activatemode (GstPad * pad,
2023 GstObject * parent, GstPadMode mode, gboolean active)
2024 {
2025 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (parent);
2026
2027 if (active) {
2028 g_mutex_lock (&timecodestamper->mutex);
2029 timecodestamper->ltc_flushing = FALSE;
2030 timecodestamper->ltc_eos = FALSE;
2031 timecodestamper->audio_live = FALSE;
2032 timecodestamper->audio_latency = GST_CLOCK_TIME_NONE;
2033 g_mutex_unlock (&timecodestamper->mutex);
2034 } else {
2035 g_mutex_lock (&timecodestamper->mutex);
2036 timecodestamper->ltc_flushing = TRUE;
2037 timecodestamper->ltc_eos = TRUE;
2038 g_cond_signal (&timecodestamper->ltc_cond_audio);
2039 g_mutex_unlock (&timecodestamper->mutex);
2040 }
2041
2042 return TRUE;
2043 }
2044
2045 static gboolean
gst_timecodestamper_videopad_activatemode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)2046 gst_timecodestamper_videopad_activatemode (GstPad * pad,
2047 GstObject * parent, GstPadMode mode, gboolean active)
2048 {
2049 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (parent);
2050
2051 if (active) {
2052 g_mutex_lock (&timecodestamper->mutex);
2053 timecodestamper->video_flushing = FALSE;
2054 timecodestamper->video_eos = FALSE;
2055 timecodestamper->video_live = FALSE;
2056 timecodestamper->video_latency = GST_CLOCK_TIME_NONE;
2057 timecodestamper->video_current_running_time = GST_CLOCK_TIME_NONE;
2058 g_mutex_unlock (&timecodestamper->mutex);
2059 } else {
2060 g_mutex_lock (&timecodestamper->mutex);
2061 timecodestamper->video_flushing = TRUE;
2062 timecodestamper->video_current_running_time = GST_CLOCK_TIME_NONE;
2063 if (timecodestamper->video_clock_id)
2064 gst_clock_id_unschedule (timecodestamper->video_clock_id);
2065 g_cond_signal (&timecodestamper->ltc_cond_video);
2066 g_mutex_unlock (&timecodestamper->mutex);
2067 }
2068
2069 return timecodestamper->video_activatemode_default (pad, parent, mode,
2070 active);
2071 }
2072
2073 static GstIterator *
gst_timecodestamper_src_iterate_internal_link(GstPad * pad,GstObject * parent)2074 gst_timecodestamper_src_iterate_internal_link (GstPad * pad, GstObject * parent)
2075 {
2076 GstTimeCodeStamper *timecodestamper = GST_TIME_CODE_STAMPER (parent);
2077 GValue value = G_VALUE_INIT;
2078 GstIterator *it;
2079
2080 g_value_init (&value, GST_TYPE_PAD);
2081 g_value_set_object (&value, GST_BASE_TRANSFORM_SINK_PAD (timecodestamper));
2082 it = gst_iterator_new_single (GST_TYPE_PAD, &value);
2083 g_value_unset (&value);
2084
2085 return it;
2086 }
2087 #endif
2088