From 8faa36e105ef4e0e525ba03f7710bc6f5c3b857f Mon Sep 17 00:00:00 2001 From: Vivia Nikolaidou Date: Sun, 15 May 2016 16:04:14 +0300 Subject: [PATCH] decklinkvideosrc: Add support for GstVideoTimeCode The timecode will be fetched from the decklink source and attached to the video buffer. https://bugzilla.gnome.org/show_bug.cgi?id=766419 --- sys/decklink/gstdecklink.cpp | 103 ++++++++++++++++++++++++++- sys/decklink/gstdecklink.h | 16 ++++- sys/decklink/gstdecklinkvideosrc.cpp | 48 ++++++++++++- sys/decklink/gstdecklinkvideosrc.h | 1 + 4 files changed, 161 insertions(+), 7 deletions(-) diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp index 203fe4eae3..930e98bdf8 100644 --- a/sys/decklink/gstdecklink.cpp +++ b/sys/decklink/gstdecklink.cpp @@ -139,6 +139,33 @@ gst_decklink_video_format_get_type (void) return (GType) id; } +GType +gst_decklink_timecode_format_get_type (void) +{ + static gsize id = 0; + static const GEnumValue timecodeformats[] = { + {GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1, "bmdTimecodeRP188VITC1", + "rp188vitc1"}, + {GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2, "bmdTimecodeRP188VITC2", + "rp188vitc2"}, + {GST_DECKLINK_TIMECODE_FORMAT_RP188LTC, "bmdTimecodeRP188LTC", "rp188ltc"}, + {GST_DECKLINK_TIMECODE_FORMAT_RP188ANY, "bmdTimecodeRP188Any", "rp188any"}, + {GST_DECKLINK_TIMECODE_FORMAT_VITC, "bmdTimecodeVITC", "vitc"}, + {GST_DECKLINK_TIMECODE_FORMAT_VITCFIELD2, "bmdTimecodeVITCField2", + "vitcfield2"}, + {GST_DECKLINK_TIMECODE_FORMAT_SERIAL, "bmdTimecodeSerial", "serial"}, + {0, NULL, NULL} + }; + + if (g_once_init_enter (&id)) { + GType tmp = + g_enum_register_static ("GstDecklinkTimecodeFormat", timecodeformats); + g_once_init_leave (&id, tmp); + } + + return (GType) id; +} + GType gst_decklink_audio_connection_get_type (void) { @@ -232,6 +259,22 @@ static const struct /* *INDENT-ON* */ }; +static const struct +{ + BMDTimecodeFormat format; + GstDecklinkTimecodeFormat gstformat; +} tcformats[] = { + /* *INDENT-OFF* */ + {bmdTimecodeRP188VITC1, GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1}, + {bmdTimecodeRP188VITC2, GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2}, + {bmdTimecodeRP188LTC, GST_DECKLINK_TIMECODE_FORMAT_RP188LTC}, + {bmdTimecodeRP188Any, GST_DECKLINK_TIMECODE_FORMAT_RP188ANY}, + {bmdTimecodeVITC, GST_DECKLINK_TIMECODE_FORMAT_VITC}, + {bmdTimecodeVITCField2, GST_DECKLINK_TIMECODE_FORMAT_VITCFIELD2}, + {bmdTimecodeSerial, GST_DECKLINK_TIMECODE_FORMAT_SERIAL} + /* *INDENT-ON* */ +}; + const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e) { @@ -367,6 +410,25 @@ gst_decklink_type_from_video_format (GstVideoFormat f) return GST_DECKLINK_VIDEO_FORMAT_AUTO; } +const BMDTimecodeFormat +gst_decklink_timecode_format_from_enum (GstDecklinkTimecodeFormat f) +{ + return tcformats[f].format; +} + +const GstDecklinkTimecodeFormat +gst_decklink_timecode_format_to_enum (BMDTimecodeFormat f) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (tcformats); i++) { + if (tcformats[i].format == f) + return (GstDecklinkTimecodeFormat) i; + } + g_assert_not_reached (); + return GST_DECKLINK_TIMECODE_FORMAT_RP188ANY; +} + static const BMDVideoConnection connections[] = { 0, /* auto */ bmdVideoConnectionSDI, @@ -660,7 +722,9 @@ public: GstElement *videosrc = NULL, *audiosrc = NULL; void (*got_video_frame) (GstElement * videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, - GstClockTime capture_time, GstClockTime capture_duration) = NULL; + GstClockTime capture_time, GstClockTime capture_duration, guint hours, + guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags) = + NULL; void (*got_audio_packet) (GstElement * videosrc, IDeckLinkAudioInputPacket * packet, GstClockTime capture_time, gboolean discont) = NULL; @@ -668,7 +732,11 @@ public: BMDTimeValue capture_time = GST_CLOCK_TIME_NONE, capture_duration = GST_CLOCK_TIME_NONE; HRESULT res; + IDeckLinkTimecode *dtc; + uint8_t hours, minutes, seconds, frames; + BMDTimecodeFlags bflags; + hours = minutes = seconds = frames = bflags = 0; if (video_frame == NULL) goto no_video_frame; @@ -681,6 +749,35 @@ public: capture_duration = GST_CLOCK_TIME_NONE; } + if (m_input->videosrc) { + /* FIXME: Avoid circularity between gstdecklink.cpp and + * gstdecklinkvideosrc.cpp */ + videosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->videosrc)); + res = + video_frame-> + GetTimecode (GST_DECKLINK_VIDEO_SRC (videosrc)->timecode_format, + &dtc); + + if (res != S_OK) { + GST_DEBUG_OBJECT (videosrc, "Failed to get timecode: 0x%08x", res); + dtc = NULL; + } else { + res = dtc->GetComponents (&hours, &minutes, &seconds, &frames); + if (res != S_OK) { + GST_ERROR ("Could not get components for timecode %p", dtc); + hours = 0; + minutes = 0; + seconds = 0; + frames = 0; + bflags = 0; + } else { + GST_DEBUG_OBJECT (videosrc, "Got timecode %02d:%02d:%02d:%02d", hours, + minutes, seconds, frames); + bflags = dtc->GetFlags (); + } + } + } + g_mutex_lock (&m_input->lock); if (capture_time > (BMDTimeValue) m_input->clock_start_time) @@ -694,7 +791,6 @@ public: capture_time = 0; if (m_input->videosrc) { - videosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->videosrc)); got_video_frame = m_input->got_video_frame; } mode = gst_decklink_get_mode_enum_from_bmd (m_input->mode->mode); @@ -707,7 +803,8 @@ public: if (got_video_frame && videosrc) { got_video_frame (videosrc, video_frame, mode, capture_time, - capture_duration); + capture_duration, (guint8) hours, (guint8) minutes, (guint8) seconds, + (guint8) frames, bflags); } no_video_frame: diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h index 6559206d5a..81e9b7c5e7 100644 --- a/sys/decklink/gstdecklink.h +++ b/sys/decklink/gstdecklink.h @@ -125,9 +125,23 @@ typedef enum { #define GST_TYPE_DECKLINK_VIDEO_FORMAT (gst_decklink_video_format_get_type ()) GType gst_decklink_video_format_get_type (void); +typedef enum { + GST_DECKLINK_TIMECODE_FORMAT_RP188VITC1, /*bmdTimecodeRP188VITC1 */ + GST_DECKLINK_TIMECODE_FORMAT_RP188VITC2, /*bmdTimecodeRP188VITC2 */ + GST_DECKLINK_TIMECODE_FORMAT_RP188LTC, /*bmdTimecodeRP188LTC */ + GST_DECKLINK_TIMECODE_FORMAT_RP188ANY, /*bmdTimecodeRP188Any */ + GST_DECKLINK_TIMECODE_FORMAT_VITC, /*bmdTimecodeVITC */ + GST_DECKLINK_TIMECODE_FORMAT_VITCFIELD2, /*bmdTimecodeVITCField2 */ + GST_DECKLINK_TIMECODE_FORMAT_SERIAL /* bmdTimecodeSerial */ +} GstDecklinkTimecodeFormat; +#define GST_TYPE_DECKLINK_TIMECODE_FORMAT (gst_decklink_timecode_format_get_type ()) +GType gst_decklink_timecode_format_get_type (void); + const BMDPixelFormat gst_decklink_pixel_format_from_type (GstDecklinkVideoFormat t); const gint gst_decklink_bpp_from_type (GstDecklinkVideoFormat t); const GstDecklinkVideoFormat gst_decklink_type_from_video_format (GstVideoFormat f); +const BMDTimecodeFormat gst_decklink_timecode_format_from_enum (GstDecklinkTimecodeFormat f); +const GstDecklinkTimecodeFormat gst_decklink_timecode_format_to_enum (BMDTimecodeFormat f); typedef struct _GstDecklinkMode GstDecklinkMode; struct _GstDecklinkMode { @@ -189,7 +203,7 @@ struct _GstDecklinkInput { GMutex lock; /* Set by the video source */ - void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, GstClockTime capture_time, GstClockTime capture_duration); + void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, GstClockTime capture_time, GstClockTime capture_duration, guint hours, guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags); /* Configured mode or NULL */ const GstDecklinkMode *mode; BMDPixelFormat format; diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp index 15ea0c85f2..90492ae5a5 100644 --- a/sys/decklink/gstdecklinkvideosrc.cpp +++ b/sys/decklink/gstdecklinkvideosrc.cpp @@ -40,7 +40,8 @@ enum PROP_CONNECTION, PROP_DEVICE_NUMBER, PROP_BUFFER_SIZE, - PROP_VIDEO_FORMAT + PROP_VIDEO_FORMAT, + PROP_TIMECODE_FORMAT }; typedef struct @@ -49,6 +50,7 @@ typedef struct GstClockTime capture_time, capture_duration; GstDecklinkModeEnum mode; BMDPixelFormat format; + GstVideoTimeCode *tc; } CaptureFrame; static void @@ -169,6 +171,14 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass) (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + g_object_class_install_property (gobject_class, PROP_TIMECODE_FORMAT, + g_param_spec_enum ("timecode-format", "Timecode format", + "Timecode format type to use for input", + GST_TYPE_DECKLINK_TIMECODE_FORMAT, + GST_DECKLINK_TIMECODE_FORMAT_RP188ANY, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); + templ_caps = gst_decklink_mode_get_template_caps (); gst_element_class_add_pad_template (element_class, gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, templ_caps)); @@ -192,6 +202,7 @@ gst_decklink_video_src_init (GstDecklinkVideoSrc * self) self->device_number = 0; self->buffer_size = DEFAULT_BUFFER_SIZE; self->video_format = GST_DECKLINK_VIDEO_FORMAT_AUTO; + self->timecode_format = bmdTimecodeRP188Any; gst_base_src_set_live (GST_BASE_SRC (self), TRUE); gst_base_src_set_format (GST_BASE_SRC (self), GST_FORMAT_TIME); @@ -245,6 +256,11 @@ gst_decklink_video_src_set_property (GObject * object, guint property_id, break; } break; + case PROP_TIMECODE_FORMAT: + self->timecode_format = + gst_decklink_timecode_format_from_enum ((GstDecklinkTimecodeFormat) + g_value_get_enum (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -273,6 +289,10 @@ gst_decklink_video_src_get_property (GObject * object, guint property_id, case PROP_VIDEO_FORMAT: g_value_set_enum (value, self->video_format); break; + case PROP_TIMECODE_FORMAT: + g_value_set_enum (value, + gst_decklink_timecode_format_to_enum (self->timecode_format)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -493,7 +513,8 @@ gst_decklink_video_src_convert_to_external_clock (GstDecklinkVideoSrc * self, static void gst_decklink_video_src_got_frame (GstElement * element, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, - GstClockTime capture_time, GstClockTime capture_duration) + GstClockTime capture_time, GstClockTime capture_duration, guint hours, + guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags) { GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element); @@ -509,6 +530,9 @@ gst_decklink_video_src_got_frame (GstElement * element, g_mutex_lock (&self->lock); if (!self->flushing) { CaptureFrame *f; + const GstDecklinkMode *bmode; + GstVideoTimeCodeFlags flags = GST_VIDEO_TIME_CODE_FLAGS_NONE; + guint field_count = 0; while (g_queue_get_length (&self->current_frames) >= self->buffer_size) { f = (CaptureFrame *) g_queue_pop_head (&self->current_frames); @@ -523,6 +547,24 @@ gst_decklink_video_src_got_frame (GstElement * element, f->capture_duration = capture_duration; f->mode = mode; f->format = frame->GetPixelFormat (); + bmode = gst_decklink_get_mode (mode); + if (bmode->interlaced) { + flags = + (GstVideoTimeCodeFlags) (flags | + GST_VIDEO_TIME_CODE_FLAGS_INTERLACED); + if (bflags & bmdTimecodeFieldMark) + field_count = 2; + else + field_count = 1; + } + if (bflags & bmdTimecodeIsDropFrame) + flags = + (GstVideoTimeCodeFlags) (flags | + GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME); + f->tc = + gst_video_time_code_new (bmode->fps_n, bmode->fps_d, NULL, flags, hours, + minutes, seconds, frames, field_count); + frame->AddRef (); g_queue_push_tail (&self->current_frames, f); g_cond_signal (&self->cond); @@ -556,7 +598,6 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer) GST_DEBUG_OBJECT (self, "Flushing"); return GST_FLOW_FLUSHING; } - // If we're not flushing, we should have a valid frame from the queue g_assert (f != NULL); @@ -620,6 +661,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer) GST_BUFFER_TIMESTAMP (*buffer) = f->capture_time; GST_BUFFER_DURATION (*buffer) = f->capture_duration; + gst_buffer_add_video_time_code_meta (*buffer, f->tc); GST_DEBUG_OBJECT (self, "Outputting buffer %p with timestamp %" GST_TIME_FORMAT " and duration %" diff --git a/sys/decklink/gstdecklinkvideosrc.h b/sys/decklink/gstdecklinkvideosrc.h index 1309364b84..b1e7351cce 100644 --- a/sys/decklink/gstdecklinkvideosrc.h +++ b/sys/decklink/gstdecklinkvideosrc.h @@ -58,6 +58,7 @@ struct _GstDecklinkVideoSrc GstVideoInfo info; GstDecklinkVideoFormat video_format; + BMDTimecodeFormat timecode_format; GstDecklinkInput *input;