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
This commit is contained in:
Vivia Nikolaidou 2016-05-15 16:04:14 +03:00 committed by Sebastian Dröge
parent 402ab38f07
commit 8faa36e105
4 changed files with 161 additions and 7 deletions

View file

@ -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:

View file

@ -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;

View file

@ -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 %"

View file

@ -58,6 +58,7 @@ struct _GstDecklinkVideoSrc
GstVideoInfo info;
GstDecklinkVideoFormat video_format;
BMDTimecodeFormat timecode_format;
GstDecklinkInput *input;