• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (&ltc_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 (&ltc_tc->timecode)) {
1435         tc_str = gst_video_time_code_to_string (&ltc_tc->timecode);
1436         GST_INFO_OBJECT (timecodestamper, "Invalid LTC timecode %s", tc_str);
1437         g_free (tc_str);
1438         gst_video_time_code_clear (&ltc_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 (&ltc_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 (&ltc_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 (&ltc_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 (&ltc_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       &timestamp, &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, &ltc_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, &ltc_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 (&ltc_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 (&ltc_tc));
1900       } else {
1901         g_queue_push_tail (&timecodestamper->ltc_current_tcs,
1902             g_steal_pointer (&ltc_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