mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
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:
parent
402ab38f07
commit
8faa36e105
4 changed files with 161 additions and 7 deletions
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 %"
|
||||
|
|
|
@ -58,6 +58,7 @@ struct _GstDecklinkVideoSrc
|
|||
|
||||
GstVideoInfo info;
|
||||
GstDecklinkVideoFormat video_format;
|
||||
BMDTimecodeFormat timecode_format;
|
||||
|
||||
GstDecklinkInput *input;
|
||||
|
||||
|
|
Loading…
Reference in a new issue