return (GType) id;
}
-#define NTSC 10, 11, false, "bt601"
-#define PAL 12, 11, true, "bt601"
-#define HD 1, 1, true, "bt709"
-#define UHD 1, 1, true, "bt2020"
+#define NTSC 10, 11, false, "bt601", FALSE
+#define PAL 12, 11, true, "bt601", FALSE
+#define HD 1, 1, true, "bt709", TRUE
+#define UHD 1, 1, true, "bt2020", TRUE
static const GstDecklinkMode modes[] = {
{bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC}, // default is ntsc
return GST_DECKLINK_VIDEO_FORMAT_AUTO;
}
+GstVideoFormat
+gst_decklink_video_format_from_type (BMDPixelFormat pf)
+{
+ guint i;
+
+ for (i = 1; i < G_N_ELEMENTS (formats); i++) {
+ if (formats[i].format == pf)
+ return formats[i].vformat;
+ }
+ GST_WARNING ("Unknown pixel format 0x%x", pf);
+ return GST_VIDEO_FORMAT_UNKNOWN;
+}
+
+
const BMDTimecodeFormat
gst_decklink_timecode_format_from_enum (GstDecklinkTimecodeFormat f)
{
#define DEFAULT_OUTPUT_STREAM_TIME (FALSE)
#define DEFAULT_SKIP_FIRST_TIME (0)
#define DEFAULT_DROP_NO_SIGNAL_FRAMES (FALSE)
+#define DEFAULT_OUTPUT_CC (FALSE)
#ifndef ABSDIFF
#define ABSDIFF(x, y) ( (x) > (y) ? ((x) - (y)) : ((y) - (x)) )
PROP_SKIP_FIRST_TIME,
PROP_DROP_NO_SIGNAL_FRAMES,
PROP_SIGNAL,
- PROP_HW_SERIAL_NUMBER
+ PROP_HW_SERIAL_NUMBER,
+ PROP_OUTPUT_CC
};
typedef struct
"The serial number (hardware ID) of the Decklink card",
NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+ g_object_class_install_property (gobject_class, PROP_OUTPUT_CC,
+ g_param_spec_boolean ("output-cc", "Output Closed Caption",
+ "Extract and output CC as GstMeta (if present)",
+ DEFAULT_OUTPUT_CC,
+ (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
templ_caps = gst_decklink_mode_get_template_caps (TRUE);
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, templ_caps));
case PROP_DROP_NO_SIGNAL_FRAMES:
self->drop_no_signal_frames = g_value_get_boolean (value);
break;
+ case PROP_OUTPUT_CC:
+ self->output_cc = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
else
g_value_set_string (value, NULL);
break;
+ case PROP_OUTPUT_CC:
+ g_value_set_boolean (value, self->output_cc);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
g_mutex_unlock (&self->lock);
}
+static void
+extract_cc_from_vbi (GstDecklinkVideoSrc * self, GstBuffer ** buffer,
+ VideoFrame * vf, const GstDecklinkMode * mode)
+{
+ IDeckLinkVideoFrameAncillary *vanc_frame = NULL;
+ gint fi;
+ guint8 *vancdata;
+ GstVideoFormat videoformat;
+ gboolean found = FALSE;
+
+ if (vf->frame->GetAncillaryData (&vanc_frame) != S_OK)
+ return;
+
+ videoformat =
+ gst_decklink_video_format_from_type (vanc_frame->GetPixelFormat ());
+
+ if (videoformat == GST_VIDEO_FORMAT_UNKNOWN) {
+ GST_DEBUG_OBJECT (self, "Unknown video format for Ancillary data");
+ vanc_frame->Release ();
+ return;
+ }
+
+ if (videoformat != self->anc_vformat) {
+ gst_video_vbi_parser_free (self->vbiparser);
+ self->vbiparser = NULL;
+ }
+
+ GST_DEBUG_OBJECT (self, "Checking for ancillary data in VBI");
+
+ fi = self->last_cc_vbi_line;
+ if (fi == -1)
+ fi = 1;
+
+ while (fi < 22 && !found) {
+ if (vanc_frame->GetBufferForVerticalBlankingLine (fi,
+ (void **) &vancdata) == S_OK) {
+ GstVideoAncillary gstanc;
+ if (self->vbiparser == NULL) {
+ self->vbiparser = gst_video_vbi_parser_new (videoformat, mode->width);
+ self->anc_vformat = videoformat;
+ }
+ GST_DEBUG_OBJECT (self, "Might have data on line %d", fi);
+ gst_video_vbi_parser_add_line (self->vbiparser, vancdata);
+
+ while (gst_video_vbi_parser_get_ancillary (self->vbiparser,
+ &gstanc) == GST_VIDEO_VBI_PARSER_RESULT_OK) {
+ if (GST_VIDEO_ANCILLARY_DID16 (&gstanc) ==
+ GST_VIDEO_ANCILLARY_DID16_S334_EIA_708) {
+ GST_DEBUG_OBJECT (self,
+ "Adding CEA-708 CDP meta to buffer for line %d", fi);
+ GST_MEMDUMP_OBJECT (self, "CDP", gstanc.data, gstanc.data_count);
+ gst_buffer_add_video_caption_meta (*buffer,
+ GST_VIDEO_CAPTION_TYPE_CEA708_CDP, gstanc.data,
+ gstanc.data_count);
+ found = TRUE;
+ self->last_cc_vbi_line = fi;
+ break;
+ }
+ }
+ }
+
+ fi++;
+ }
+
+ if (!found)
+ self->last_cc_vbi_line = -1;
+
+ vanc_frame->Release ();
+}
+
static GstFlowReturn
gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
{
g_mutex_unlock (&self->lock);
if (caps_changed) {
+ self->last_cc_vbi_line = -1;
caps = gst_decklink_mode_get_caps (f.mode, f.format, TRUE);
gst_video_info_from_caps (&self->info, caps);
gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps);
gst_element_post_message (GST_ELEMENT_CAST (self),
gst_message_new_latency (GST_OBJECT_CAST (self)));
gst_caps_unref (caps);
-
+ if (self->vbiparser) {
+ gst_video_vbi_parser_free (self->vbiparser);
+ self->vbiparser = NULL;
+ self->anc_vformat = GST_VIDEO_FORMAT_UNKNOWN;
+ }
}
f.frame->GetBytes ((gpointer *) & data);
}
}
+ mode = gst_decklink_get_mode (self->mode);
+
+ // If we have a format that supports VANC and we are asked to extract CC,
+ // then do it here.
+ if (self->output_cc && mode->vanc)
+ extract_cc_from_vbi (self, buffer, vf, mode);
+
if (f.no_signal)
GST_BUFFER_FLAG_SET (*buffer, GST_BUFFER_FLAG_GAP);
GST_BUFFER_TIMESTAMP (*buffer) = f.timestamp;
gst_static_caps_get (&hardware_reference), f.hardware_timestamp,
f.hardware_duration);
- mode = gst_decklink_get_mode (self->mode);
if (mode->interlaced && mode->tff)
GST_BUFFER_FLAG_SET (*buffer,
GST_VIDEO_BUFFER_FLAG_TFF | GST_VIDEO_BUFFER_FLAG_INTERLACED);
self->input->input->DisableVideoInput ();
}
+ if (self->vbiparser) {
+ gst_video_vbi_parser_free (self->vbiparser);
+ self->vbiparser = NULL;
+ self->anc_vformat = GST_VIDEO_FORMAT_UNKNOWN;
+ }
+
return TRUE;
}
GST_WARNING_OBJECT (self, "Warning: mode=auto and format!=auto may \
not work");
}
+ self->vbiparser = NULL;
+ self->anc_vformat = GST_VIDEO_FORMAT_UNKNOWN;
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
self->flushing = FALSE;