diff --git a/gstajacommon.cpp b/gstajacommon.cpp index 99b064ca28..fc71862b47 100644 --- a/gstajacommon.cpp +++ b/gstajacommon.cpp @@ -27,87 +27,288 @@ #include "gstajacommon.h" GST_DEBUG_CATEGORY_STATIC(gst_aja_debug); + #define GST_CAT_DEFAULT gst_aja_debug -static const NTV2VideoFormat supported_video_formats[] = { - NTV2_FORMAT_1080i_5000, NTV2_FORMAT_1080i_5994, - NTV2_FORMAT_1080i_6000, NTV2_FORMAT_720p_5994, - NTV2_FORMAT_720p_6000, NTV2_FORMAT_1080p_2997, - NTV2_FORMAT_1080p_3000, NTV2_FORMAT_1080p_2500, - NTV2_FORMAT_1080p_2398, NTV2_FORMAT_1080p_2400, - NTV2_FORMAT_720p_5000, NTV2_FORMAT_720p_2398, - NTV2_FORMAT_720p_2500, NTV2_FORMAT_1080p_5000_A, - NTV2_FORMAT_1080p_5994_A, NTV2_FORMAT_1080p_6000_A, - NTV2_FORMAT_625_5000, NTV2_FORMAT_525_5994, - NTV2_FORMAT_525_2398, NTV2_FORMAT_525_2400}; +typedef struct { + GstAjaVideoFormat gst_format; + NTV2VideoFormat aja_format; + NTV2VideoFormat quad_format; +} FormatMapEntry; + +static const FormatMapEntry format_map[] = { + {GST_AJA_VIDEO_FORMAT_1080i_5000, NTV2_FORMAT_1080i_5000, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080i_5994, NTV2_FORMAT_1080i_5994, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080i_6000, NTV2_FORMAT_1080i_6000, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_720p_5994, NTV2_FORMAT_720p_5994, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_720p_6000, NTV2_FORMAT_720p_6000, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_2997, NTV2_FORMAT_1080p_2997, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_3000, NTV2_FORMAT_1080p_3000, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_2500, NTV2_FORMAT_1080p_2500, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_2398, NTV2_FORMAT_1080p_2398, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_2400, NTV2_FORMAT_1080p_2400, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_720p_5000, NTV2_FORMAT_720p_5000, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_720p_2398, NTV2_FORMAT_720p_2398, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_720p_5000, NTV2_FORMAT_720p_2500, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_3000, NTV2_FORMAT_1080p_5000_A, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_5994_A, NTV2_FORMAT_1080p_5994_A, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_1080p_6000_A, NTV2_FORMAT_1080p_6000_A, + NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_625_5000, NTV2_FORMAT_625_5000, NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_525_5994, NTV2_FORMAT_525_5994, NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_525_2398, NTV2_FORMAT_525_2398, NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_525_2400, NTV2_FORMAT_525_2400, NTV2_FORMAT_UNKNOWN}, + {GST_AJA_VIDEO_FORMAT_2160p_2398, NTV2_FORMAT_3840x2160p_2398, + NTV2_FORMAT_4x1920x1080p_2398}, + {GST_AJA_VIDEO_FORMAT_2160p_2400, NTV2_FORMAT_3840x2160p_2400, + NTV2_FORMAT_4x1920x1080p_2400}, + {GST_AJA_VIDEO_FORMAT_2160p_2500, NTV2_FORMAT_3840x2160p_2500, + NTV2_FORMAT_4x1920x1080p_2500}, + {GST_AJA_VIDEO_FORMAT_2160p_2997, NTV2_FORMAT_3840x2160p_2997, + NTV2_FORMAT_4x1920x1080p_2997}, + {GST_AJA_VIDEO_FORMAT_2160p_3000, NTV2_FORMAT_3840x2160p_3000, + NTV2_FORMAT_4x1920x1080p_3000}, + {GST_AJA_VIDEO_FORMAT_2160p_5000, NTV2_FORMAT_3840x2160p_5000, + NTV2_FORMAT_4x1920x1080p_5000}, + {GST_AJA_VIDEO_FORMAT_2160p_5994, NTV2_FORMAT_3840x2160p_5994, + NTV2_FORMAT_4x1920x1080p_5994}, + {GST_AJA_VIDEO_FORMAT_2160p_6000, NTV2_FORMAT_3840x2160p_6000, + NTV2_FORMAT_4x1920x1080p_6000}, + {GST_AJA_VIDEO_FORMAT_4320p_2398, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_2398}, + {GST_AJA_VIDEO_FORMAT_4320p_2400, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_2400}, + {GST_AJA_VIDEO_FORMAT_4320p_2500, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_2500}, + {GST_AJA_VIDEO_FORMAT_4320p_2997, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_2997}, + {GST_AJA_VIDEO_FORMAT_4320p_3000, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_3000}, + {GST_AJA_VIDEO_FORMAT_4320p_5000, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_5000}, + {GST_AJA_VIDEO_FORMAT_4320p_5994, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_5994}, + {GST_AJA_VIDEO_FORMAT_4320p_6000, NTV2_FORMAT_UNKNOWN, + NTV2_FORMAT_4x3840x2160p_6000}, +}; GstCaps *gst_ntv2_supported_caps(NTV2DeviceID device_id) { GstCaps *caps = gst_caps_new_empty(); - for (gsize i = 0; i < G_N_ELEMENTS(supported_video_formats); i++) { - NTV2VideoFormat format = supported_video_formats[i]; + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &format = format_map[i]; - if (device_id == DEVICE_ID_INVALID || - ::NTV2DeviceCanDoVideoFormat(device_id, format)) { - gst_caps_append(caps, gst_ntv2_video_format_to_caps(format)); + if (device_id == DEVICE_ID_INVALID) { + gst_caps_append(caps, gst_aja_video_format_to_caps(format.gst_format)); + } else { + if ((format.aja_format != NTV2_FORMAT_UNKNOWN && + ::NTV2DeviceCanDoVideoFormat(device_id, format.aja_format)) || + (format.quad_format != NTV2_FORMAT_UNKNOWN && + ::NTV2DeviceCanDoVideoFormat(device_id, format.quad_format))) { + gst_caps_append(caps, gst_aja_video_format_to_caps(format.gst_format)); + } } } return caps; } +GstCaps *gst_aja_video_format_to_caps(GstAjaVideoFormat format) { + const FormatMapEntry *entry = NULL; + + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &tmp = format_map[i]; + + if (tmp.gst_format == format) { + entry = &tmp; + break; + } + } + g_assert(entry != NULL); + + if (entry->aja_format != NTV2_FORMAT_UNKNOWN) + return gst_ntv2_video_format_to_caps(entry->aja_format); + if (entry->quad_format != NTV2_FORMAT_UNKNOWN) + return gst_ntv2_video_format_to_caps(entry->quad_format); + + g_assert_not_reached(); +} + +bool gst_video_info_from_aja_video_format(GstVideoInfo *info, + GstAjaVideoFormat format) { + const FormatMapEntry *entry = NULL; + + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &tmp = format_map[i]; + + if (tmp.gst_format == format) { + entry = &tmp; + break; + } + } + g_assert(entry != NULL); + + if (entry->aja_format != NTV2_FORMAT_UNKNOWN) + return gst_video_info_from_ntv2_video_format(info, entry->aja_format); + if (entry->quad_format != NTV2_FORMAT_UNKNOWN) + return gst_video_info_from_ntv2_video_format(info, entry->quad_format); + + g_assert_not_reached(); +} + GstCaps *gst_ntv2_video_format_to_caps(NTV2VideoFormat format) { GstVideoInfo info; + if (!gst_video_info_from_ntv2_video_format(&info, format)) return NULL; + + return gst_video_info_to_caps(&info); +} + +bool gst_video_info_from_ntv2_video_format(GstVideoInfo *info, + NTV2VideoFormat format) { + if (format == NTV2_FORMAT_UNKNOWN) return false; + guint width = ::GetDisplayWidth(format); guint height = ::GetDisplayHeight(format); NTV2FrameRate fps = ::GetNTV2FrameRateFromVideoFormat(format); guint fps_n, fps_d; ::GetFramesPerSecond(fps, fps_n, fps_d); - gst_video_info_set_format(&info, GST_VIDEO_FORMAT_v210, width, height); - info.fps_n = fps_n; - info.fps_d = fps_d; + gst_video_info_set_format(info, GST_VIDEO_FORMAT_v210, width, height); + info->fps_n = fps_n; + info->fps_d = fps_d; if (NTV2_IS_525_FORMAT(format)) { - info.par_n = 10; - info.par_d = 11; + info->par_n = 10; + info->par_d = 11; } else if (NTV2_IS_625_FORMAT(format)) { - info.par_n = 12; - info.par_d = 11; + info->par_n = 12; + info->par_d = 11; } - info.interlace_mode = !::IsProgressiveTransport(format) - ? GST_VIDEO_INTERLACE_MODE_INTERLEAVED - : GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; + info->interlace_mode = !::IsProgressiveTransport(format) + ? GST_VIDEO_INTERLACE_MODE_INTERLEAVED + : GST_VIDEO_INTERLACE_MODE_PROGRESSIVE; - return gst_video_info_to_caps(&info); + return true; } -NTV2VideoFormat gst_ntv2_video_format_from_caps(GstCaps *caps) { +NTV2VideoFormat gst_ntv2_video_format_from_caps(const GstCaps *caps, + bool quad) { GstVideoInfo info; if (!gst_video_info_from_caps(&info, caps)) return NTV2_FORMAT_UNKNOWN; - for (gsize i = 0; i < G_N_ELEMENTS(supported_video_formats); i++) { - NTV2VideoFormat format = supported_video_formats[i]; + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &format = format_map[i]; + NTV2VideoFormat f = !quad ? format.aja_format : format.quad_format; - guint width = ::GetDisplayWidth(format); - guint height = ::GetDisplayHeight(format); - NTV2FrameRate fps = ::GetNTV2FrameRateFromVideoFormat(format); + if (f == NTV2_FORMAT_UNKNOWN) continue; + + guint width = ::GetDisplayWidth(f); + guint height = ::GetDisplayHeight(f); + NTV2FrameRate fps = ::GetNTV2FrameRateFromVideoFormat(f); guint fps_n, fps_d; ::GetFramesPerSecond(fps, fps_n, fps_d); if (width == (guint)info.width && height == (guint)info.height && (guint)info.fps_n == fps_n && (guint)info.fps_d == fps_d && - ((!::IsProgressiveTransport(format) && + ((!::IsProgressiveTransport(f) && info.interlace_mode == GST_VIDEO_INTERLACE_MODE_INTERLEAVED) || - (::IsProgressiveTransport(format) && + (::IsProgressiveTransport(f) && info.interlace_mode == GST_VIDEO_INTERLACE_MODE_PROGRESSIVE))) - return format; + return f; } return NTV2_FORMAT_UNKNOWN; } +GstAjaVideoFormat gst_aja_video_format_from_caps(const GstCaps *caps) { + GstVideoInfo info; + + if (!gst_video_info_from_caps(&info, caps)) + return GST_AJA_VIDEO_FORMAT_INVALID; + + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &format = format_map[i]; + NTV2VideoFormat f = (format.aja_format != NTV2_FORMAT_UNKNOWN) + ? format.aja_format + : format.quad_format; + + if (f == NTV2_FORMAT_UNKNOWN) continue; + + guint width = ::GetDisplayWidth(f); + guint height = ::GetDisplayHeight(f); + NTV2FrameRate fps = ::GetNTV2FrameRateFromVideoFormat(f); + guint fps_n, fps_d; + ::GetFramesPerSecond(fps, fps_n, fps_d); + + if (width == (guint)info.width && height == (guint)info.height && + (guint)info.fps_n == fps_n && (guint)info.fps_d == fps_d && + ((!::IsProgressiveTransport(f) && + info.interlace_mode == GST_VIDEO_INTERLACE_MODE_INTERLEAVED) || + (::IsProgressiveTransport(f) && + info.interlace_mode == GST_VIDEO_INTERLACE_MODE_PROGRESSIVE))) + return format.gst_format; + } + + return GST_AJA_VIDEO_FORMAT_INVALID; +} + +GstAjaVideoFormat gst_aja_video_format_from_ntv2_format( + NTV2VideoFormat format) { + if (format == NTV2_FORMAT_UNKNOWN) return GST_AJA_VIDEO_FORMAT_INVALID; + + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &entry = format_map[i]; + if (entry.aja_format == format || entry.quad_format == format) + return entry.gst_format; + } + + return GST_AJA_VIDEO_FORMAT_INVALID; +} + +NTV2VideoFormat gst_ntv2_video_format_from_aja_format(GstAjaVideoFormat format, + bool quad) { + if (format == GST_AJA_VIDEO_FORMAT_INVALID) return NTV2_FORMAT_UNKNOWN; + + for (gsize i = 0; i < G_N_ELEMENTS(format_map); i++) { + const FormatMapEntry &entry = format_map[i]; + if (entry.gst_format == format) { + if (!quad && entry.aja_format != NTV2_FORMAT_UNKNOWN) + return entry.aja_format; + if (quad && entry.quad_format != NTV2_FORMAT_UNKNOWN) + return entry.quad_format; + } + } + + return NTV2_FORMAT_UNKNOWN; +} + +bool gst_ntv2_video_format_is_quad(NTV2VideoFormat format) { + return (format >= NTV2_FORMAT_FIRST_4K_DEF_FORMAT && + format < NTV2_FORMAT_END_4K_DEF_FORMATS) || + (format >= NTV2_FORMAT_FIRST_4K_DEF_FORMAT2 && + format < NTV2_FORMAT_END_4K_DEF_FORMATS2) || + (format >= NTV2_FORMAT_FIRST_UHD2_DEF_FORMAT && + format < NTV2_FORMAT_END_UHD2_DEF_FORMATS) || + (format >= NTV2_FORMAT_FIRST_UHD2_FULL_DEF_FORMAT && + format < NTV2_FORMAT_END_UHD2_FULL_DEF_FORMATS); +} + GType gst_aja_audio_meta_api_get_type(void) { static volatile GType type; @@ -498,6 +699,22 @@ GType gst_aja_input_source_get_type(void) { return (GType)id; } +GType gst_aja_sdi_mode_get_type(void) { + static gsize id = 0; + static const GEnumValue modes[] = { + {GST_AJA_SDI_MODE_SINGLE_LINK, "single-link", "Single Link"}, + {GST_AJA_SDI_MODE_QUAD_LINK_SQD, "quad-link-sqd", "Quad Link SQD"}, + {GST_AJA_SDI_MODE_QUAD_LINK_TSI, "quad-link-tsi", "Quad Link TSI"}, + {0, NULL, NULL}}; + + if (g_once_init_enter(&id)) { + GType tmp = g_enum_register_static("GstAjaSdiMode", modes); + g_once_init_leave(&id, tmp); + } + + return (GType)id; +} + GType gst_aja_video_format_get_type(void) { static gsize id = 0; static const GEnumValue modes[] = { @@ -522,6 +739,22 @@ GType gst_aja_video_format_get_type(void) { {GST_AJA_VIDEO_FORMAT_525_5994, "525-5994", "525 5994"}, {GST_AJA_VIDEO_FORMAT_525_2398, "525-2398", "525 2398"}, {GST_AJA_VIDEO_FORMAT_525_2400, "525-2400", "525 2400"}, + {GST_AJA_VIDEO_FORMAT_2160p_2398, "2160p-2398", "2160p 2398"}, + {GST_AJA_VIDEO_FORMAT_2160p_2400, "2160p-2400", "2160p 2400"}, + {GST_AJA_VIDEO_FORMAT_2160p_2500, "2160p-2500", "2160p 2500"}, + {GST_AJA_VIDEO_FORMAT_2160p_2997, "2160p-2997", "2160p 2997"}, + {GST_AJA_VIDEO_FORMAT_2160p_3000, "2160p-3000", "2160p 3000"}, + {GST_AJA_VIDEO_FORMAT_2160p_5000, "2160p-5000", "2160p 5000"}, + {GST_AJA_VIDEO_FORMAT_2160p_5994, "2160p-5994", "2160p 5994"}, + {GST_AJA_VIDEO_FORMAT_2160p_6000, "2160p-6000", "2160p 6000"}, + {GST_AJA_VIDEO_FORMAT_4320p_2398, "4320p-2398", "4320p 2398"}, + {GST_AJA_VIDEO_FORMAT_4320p_2400, "4320p-2400", "4320p 2400"}, + {GST_AJA_VIDEO_FORMAT_4320p_2500, "4320p-2500", "4320p 2500"}, + {GST_AJA_VIDEO_FORMAT_4320p_2997, "4320p-2997", "4320p 2997"}, + {GST_AJA_VIDEO_FORMAT_4320p_3000, "4320p-3000", "4320p 3000"}, + {GST_AJA_VIDEO_FORMAT_4320p_5000, "4320p-5000", "4320p 5000"}, + {GST_AJA_VIDEO_FORMAT_4320p_5994, "4320p-5994", "4320p 5994"}, + {GST_AJA_VIDEO_FORMAT_4320p_6000, "4320p-6000", "4320p 6000"}, {0, NULL, NULL}}; if (g_once_init_enter(&id)) { diff --git a/gstajacommon.h b/gstajacommon.h index af646e4d27..f64103a9b6 100644 --- a/gstajacommon.h +++ b/gstajacommon.h @@ -54,13 +54,6 @@ G_GNUC_INTERNAL GstAjaAudioMeta *gst_buffer_add_aja_audio_meta(GstBuffer *buffer, GstBuffer *audio_buffer); -G_GNUC_INTERNAL -GstCaps *gst_ntv2_supported_caps(NTV2DeviceID device_id); -G_GNUC_INTERNAL -GstCaps *gst_ntv2_video_format_to_caps(NTV2VideoFormat format); -G_GNUC_INTERNAL -NTV2VideoFormat gst_ntv2_video_format_from_caps(GstCaps *caps); - typedef struct { CNTV2Card *device; } GstAjaDevice; @@ -178,6 +171,17 @@ G_GNUC_INTERNAL GType gst_aja_input_source_get_type(void); typedef enum { + GST_AJA_SDI_MODE_SINGLE_LINK, + GST_AJA_SDI_MODE_QUAD_LINK_SQD, + GST_AJA_SDI_MODE_QUAD_LINK_TSI, +} GstAjaSdiMode; + +#define GST_TYPE_AJA_SDI_MODE (gst_aja_sdi_mode_get_type()) +G_GNUC_INTERNAL +GType gst_aja_sdi_mode_get_type(void); + +typedef enum { + GST_AJA_VIDEO_FORMAT_INVALID = -1, // TODO: Implement: GST_AJA_VIDEO_FORMAT_AUTO, GST_AJA_VIDEO_FORMAT_1080i_5000, GST_AJA_VIDEO_FORMAT_1080i_5994, @@ -199,6 +203,22 @@ typedef enum { GST_AJA_VIDEO_FORMAT_525_5994, GST_AJA_VIDEO_FORMAT_525_2398, GST_AJA_VIDEO_FORMAT_525_2400, + GST_AJA_VIDEO_FORMAT_2160p_2398, + GST_AJA_VIDEO_FORMAT_2160p_2400, + GST_AJA_VIDEO_FORMAT_2160p_2500, + GST_AJA_VIDEO_FORMAT_2160p_2997, + GST_AJA_VIDEO_FORMAT_2160p_3000, + GST_AJA_VIDEO_FORMAT_2160p_5000, + GST_AJA_VIDEO_FORMAT_2160p_5994, + GST_AJA_VIDEO_FORMAT_2160p_6000, + GST_AJA_VIDEO_FORMAT_4320p_2398, + GST_AJA_VIDEO_FORMAT_4320p_2400, + GST_AJA_VIDEO_FORMAT_4320p_2500, + GST_AJA_VIDEO_FORMAT_4320p_2997, + GST_AJA_VIDEO_FORMAT_4320p_3000, + GST_AJA_VIDEO_FORMAT_4320p_5000, + GST_AJA_VIDEO_FORMAT_4320p_5994, + GST_AJA_VIDEO_FORMAT_4320p_6000, } GstAjaVideoFormat; #define GST_TYPE_AJA_VIDEO_FORMAT (gst_aja_video_format_get_type()) @@ -238,3 +258,31 @@ class ShmMutexLocker { ShmMutexLocker(); ~ShmMutexLocker(); }; + +G_GNUC_INTERNAL +GstCaps *gst_ntv2_supported_caps(NTV2DeviceID device_id); + +G_GNUC_INTERNAL +GstCaps *gst_ntv2_video_format_to_caps(NTV2VideoFormat format); +G_GNUC_INTERNAL +bool gst_video_info_from_ntv2_video_format(GstVideoInfo *info, + NTV2VideoFormat format); +G_GNUC_INTERNAL +NTV2VideoFormat gst_ntv2_video_format_from_caps(const GstCaps *caps, bool quad); + +G_GNUC_INTERNAL +GstCaps *gst_aja_video_format_to_caps(GstAjaVideoFormat format); +G_GNUC_INTERNAL +bool gst_video_info_from_aja_video_format(GstVideoInfo *info, + GstAjaVideoFormat format); +G_GNUC_INTERNAL +GstAjaVideoFormat gst_aja_video_format_from_caps(const GstCaps *caps); + +G_GNUC_INTERNAL +GstAjaVideoFormat gst_aja_video_format_from_ntv2_format(NTV2VideoFormat format); +G_GNUC_INTERNAL +NTV2VideoFormat gst_ntv2_video_format_from_aja_format(GstAjaVideoFormat format, + bool quad); + +G_GNUC_INTERNAL +bool gst_ntv2_video_format_is_quad(NTV2VideoFormat format); diff --git a/gstajasink.cpp b/gstajasink.cpp index 84f786123b..d4008a8fb7 100644 --- a/gstajasink.cpp +++ b/gstajasink.cpp @@ -35,6 +35,7 @@ GST_DEBUG_CATEGORY_STATIC(gst_aja_sink_debug); #define DEFAULT_CHANNEL (::NTV2_CHANNEL1) #define DEFAULT_AUDIO_SYSTEM (GST_AJA_AUDIO_SYSTEM_AUTO) #define DEFAULT_OUTPUT_DESTINATION (GST_AJA_OUTPUT_DESTINATION_AUTO) +#define DEFAULT_SDI_MODE (GST_AJA_SDI_MODE_SINGLE_LINK) #define DEFAULT_TIMECODE_INDEX (GST_AJA_TIMECODE_INDEX_VITC) #define DEFAULT_REFERENCE_SOURCE (GST_AJA_REFERENCE_SOURCE_AUTO) #define DEFAULT_QUEUE_SIZE (16) @@ -46,6 +47,7 @@ enum { PROP_CHANNEL, PROP_AUDIO_SYSTEM, PROP_OUTPUT_DESTINATION, + PROP_SDI_MODE, PROP_TIMECODE_INDEX, PROP_REFERENCE_SOURCE, PROP_QUEUE_SIZE, @@ -150,6 +152,14 @@ static void gst_aja_sink_class_init(GstAjaSinkClass *klass) { (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + g_object_class_install_property( + gobject_class, PROP_SDI_MODE, + g_param_spec_enum( + "sdi-mode", "SDI Mode", "SDI mode to use", GST_TYPE_AJA_SDI_MODE, + DEFAULT_SDI_MODE, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); + g_object_class_install_property( gobject_class, PROP_TIMECODE_INDEX, g_param_spec_enum( @@ -241,6 +251,9 @@ void gst_aja_sink_set_property(GObject *object, guint property_id, self->output_destination = (GstAjaOutputDestination)g_value_get_enum(value); break; + case PROP_SDI_MODE: + self->sdi_mode = (GstAjaSdiMode)g_value_get_enum(value); + break; case PROP_TIMECODE_INDEX: self->timecode_index = (GstAjaTimecodeIndex)g_value_get_enum(value); break; @@ -276,6 +289,9 @@ void gst_aja_sink_get_property(GObject *object, guint property_id, case PROP_OUTPUT_DESTINATION: g_value_set_enum(value, self->output_destination); break; + case PROP_SDI_MODE: + g_value_set_enum(value, self->sdi_mode); + break; case PROP_TIMECODE_INDEX: g_value_set_enum(value, self->timecode_index); break; @@ -540,12 +556,14 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { gst_caps_replace(&self->configured_caps, caps); GST_OBJECT_UNLOCK(self); - video_format = gst_ntv2_video_format_from_caps(caps); + bool quad_mode = (self->sdi_mode != GST_AJA_SDI_MODE_SINGLE_LINK); + video_format = gst_ntv2_video_format_from_caps(caps, quad_mode); if (video_format == NTV2_FORMAT_UNKNOWN) { GST_ERROR_OBJECT(self, "Unsupported caps %" GST_PTR_FORMAT, caps); return FALSE; } + self->quad_mode = quad_mode; self->video_format = video_format; // Configure render delay based on the framerate and queue size @@ -568,11 +586,21 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { } self->device->device->SetMode(self->channel, NTV2_MODE_DISPLAY, false); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetMode((NTV2Channel)(self->channel + i), + NTV2_MODE_DISPLAY, false); + } GST_DEBUG_OBJECT(self, "Configuring video format %d on channel %d", (int)video_format, (int)self->channel); self->device->device->SetVideoFormat(video_format, false, false, self->channel); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetVideoFormat(video_format, false, false, + (NTV2Channel)(self->channel + i)); + } if (!::NTV2DeviceCanDoFrameBufferFormat(self->device_id, ::NTV2_FBF_10BIT_YCBCR)) { @@ -582,6 +610,11 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { } self->device->device->SetFrameBufferFormat(self->channel, ::NTV2_FBF_10BIT_YCBCR); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetFrameBufferFormat( + (NTV2Channel)(self->channel + i), ::NTV2_FBF_10BIT_YCBCR); + } NTV2ReferenceSource reference_source; switch (self->reference_source) { @@ -628,11 +661,25 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { GST_ERROR_OBJECT(self, "Failed to enable channel"); return FALSE; } + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + if (!self->device->device->EnableChannel( + (NTV2Channel)(self->channel + i))) { + GST_ERROR_OBJECT(self, "Failed to enable channel"); + return FALSE; + } + } + } self->device->device->DMABufferAutoLock(false, true, 0); if (::NTV2DeviceHasBiDirectionalSDI(self->device_id)) self->device->device->SetSDITransmitEnable(self->channel, true); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetSDITransmitEnable( + (NTV2Channel)(self->channel + i), true); + } if (self->configured_audio_channels) { switch (self->audio_system_setting) { @@ -683,6 +730,14 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { self->audio_system); self->device->device->SetSDIOutputDS2AudioSystem(self->channel, self->audio_system); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + self->device->device->SetSDIOutputAudioSystem( + (NTV2Channel)(self->channel + i), self->audio_system); + self->device->device->SetSDIOutputDS2AudioSystem( + (NTV2Channel)(self->channel + i), self->audio_system); + } + } self->device->device->SetAudioLoopBack(::NTV2_AUDIO_LOOPBACK_OFF, self->audio_system); } else { @@ -805,6 +860,11 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { const NTV2Standard standard(::GetNTV2StandardFromVideoFormat(video_format)); self->device->device->SetSDIOutputStandard(self->channel, standard); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetSDIOutputStandard( + (NTV2Channel)(self->channel + i), standard); + } const NTV2FrameGeometry geometry = ::GetNTV2FrameGeometryFromVideoFormat(video_format); @@ -813,12 +873,67 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { if (self->vanc_mode == ::NTV2_VANCMODE_OFF) { self->device->device->SetFrameGeometry(geometry, false, self->channel); self->device->device->SetVANCMode(self->vanc_mode, self->channel); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + self->device->device->SetFrameGeometry( + geometry, false, (NTV2Channel)(self->channel + i)); + self->device->device->SetVANCMode(self->vanc_mode, + (NTV2Channel)(self->channel + i)); + } + } } else { const NTV2FrameGeometry vanc_geometry = ::GetVANCFrameGeometry(geometry, self->vanc_mode); self->device->device->SetFrameGeometry(vanc_geometry, false, self->channel); self->device->device->SetVANCMode(self->vanc_mode, self->channel); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + self->device->device->SetFrameGeometry( + vanc_geometry, false, (NTV2Channel)(self->channel + i)); + self->device->device->SetVANCMode(self->vanc_mode, + (NTV2Channel)(self->channel + i)); + } + } + } + + if (self->quad_mode) { + switch (self->sdi_mode) { + case GST_AJA_SDI_MODE_SINGLE_LINK: + g_assert_not_reached(); + break; + case GST_AJA_SDI_MODE_QUAD_LINK_SQD: + if (self->configured_info.height > 2160) { + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + self->device->device->SetQuadQuadFrameEnable(true, self->channel); + self->device->device->SetQuadQuadSquaresEnable(true, self->channel); + } else { + self->device->device->SetQuadQuadFrameEnable(false, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, self->channel); + self->device->device->Set4kSquaresEnable(true, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + } + break; + case GST_AJA_SDI_MODE_QUAD_LINK_TSI: + if (self->configured_info.height > 2160) { + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + self->device->device->SetQuadQuadFrameEnable(true, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, self->channel); + } else { + self->device->device->SetQuadQuadFrameEnable(false, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, self->channel); + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(true, self->channel); + } + break; + } + } else { + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + self->device->device->SetQuadQuadFrameEnable(false, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, self->channel); } NTV2SmpteLineNumber smpte_line_num_info = ::GetSmpteLineNumber(standard); @@ -834,16 +949,209 @@ static gboolean gst_aja_sink_set_caps(GstBaseSink *bsink, GstCaps *caps) { // Need to remove old routes for the output and framebuffer we're going to use NTV2ActualConnections connections = router.GetConnections(); - for (NTV2ActualConnectionsConstIter iter = connections.begin(); - iter != connections.end(); iter++) { - if (iter->first == output_destination_id || iter->second == framebuffer_id) - router.RemoveConnection(iter->first, iter->second); + if (self->quad_mode) { + if (self->channel == NTV2_CHANNEL1) { + for (auto iter = connections.begin(); iter != connections.end(); iter++) { + if (iter->first == NTV2_XptSDIOut1Input || + iter->first == NTV2_XptSDIOut1InputDS2 || + iter->first == NTV2_XptSDIOut2Input || + iter->first == NTV2_XptSDIOut2InputDS2 || + iter->first == NTV2_XptSDIOut3Input || + iter->first == NTV2_XptSDIOut4Input || + iter->second == NTV2_Xpt425Mux1AYUV || + iter->second == NTV2_Xpt425Mux1BYUV || + iter->second == NTV2_Xpt425Mux2AYUV || + iter->second == NTV2_Xpt425Mux2BYUV || + iter->first == NTV2_Xpt425Mux1AInput || + iter->first == NTV2_Xpt425Mux1BInput || + iter->first == NTV2_Xpt425Mux2AInput || + iter->first == NTV2_Xpt425Mux2BInput || + iter->second == NTV2_XptFrameBuffer1YUV || + iter->second == NTV2_XptFrameBuffer2YUV || + iter->second == NTV2_XptFrameBuffer3YUV || + iter->second == NTV2_XptFrameBuffer4YUV || + iter->second == NTV2_XptFrameBuffer1_DS2YUV || + iter->second == NTV2_XptFrameBuffer2_DS2YUV || + iter->first == NTV2_XptSDIOut1Input || + iter->first == NTV2_XptSDIOut2Input || + iter->first == NTV2_XptSDIOut3Input || + iter->first == NTV2_XptSDIOut4Input) + router.RemoveConnection(iter->first, iter->second); + } + } else if (self->channel == NTV2_CHANNEL5) { + for (auto iter = connections.begin(); iter != connections.end(); iter++) { + if (iter->first == NTV2_XptSDIOut5Input || + iter->first == NTV2_XptSDIOut5InputDS2 || + iter->first == NTV2_XptSDIOut6Input || + iter->first == NTV2_XptSDIOut6InputDS2 || + iter->first == NTV2_XptSDIOut7Input || + iter->first == NTV2_XptSDIOut8Input || + iter->second == NTV2_Xpt425Mux3AYUV || + iter->second == NTV2_Xpt425Mux3BYUV || + iter->second == NTV2_Xpt425Mux4AYUV || + iter->second == NTV2_Xpt425Mux4BYUV || + iter->first == NTV2_Xpt425Mux3AInput || + iter->first == NTV2_Xpt425Mux3BInput || + iter->first == NTV2_Xpt425Mux4AInput || + iter->first == NTV2_Xpt425Mux4BInput || + iter->second == NTV2_XptFrameBuffer5YUV || + iter->second == NTV2_XptFrameBuffer6YUV || + iter->second == NTV2_XptFrameBuffer7YUV || + iter->second == NTV2_XptFrameBuffer8YUV || + iter->second == NTV2_XptFrameBuffer3_DS2YUV || + iter->second == NTV2_XptFrameBuffer4_DS2YUV || + iter->second == NTV2_XptFrameBuffer5_DS2YUV || + iter->second == NTV2_XptFrameBuffer6_DS2YUV || + iter->first == NTV2_XptSDIOut5Input || + iter->first == NTV2_XptSDIOut6Input || + iter->first == NTV2_XptSDIOut7Input || + iter->first == NTV2_XptSDIOut8Input) + router.RemoveConnection(iter->first, iter->second); + } + } else { + g_assert_not_reached(); + } + } else { + for (auto iter = connections.begin(); iter != connections.end(); iter++) { + if (iter->first == output_destination_id || + iter->second == framebuffer_id) + router.RemoveConnection(iter->first, iter->second); + + if (((output_destination_id == NTV2_XptSDIOut6Input || + output_destination_id == NTV2_XptSDIOut8Input) && + iter->second == NTV2_XptFrameBuffer6_DS2YUV) || + ((output_destination_id == NTV2_XptSDIOut5Input || + output_destination_id == NTV2_XptSDIOut6Input) && + iter->second == NTV2_XptFrameBuffer5_DS2YUV) || + ((output_destination_id == NTV2_XptSDIOut2Input || + output_destination_id == NTV2_XptSDIOut4Input) && + iter->second == NTV2_XptFrameBuffer2_DS2YUV) || + ((output_destination_id == NTV2_XptSDIOut1Input || + output_destination_id == NTV2_XptSDIOut2Input) && + iter->second == NTV2_XptFrameBuffer1_DS2YUV)) + router.RemoveConnection(iter->first, iter->second); + } + } + + if (self->quad_mode) { + if (self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_TSI && + !NTV2_IS_QUAD_QUAD_HFR_VIDEO_FORMAT(self->video_format) && + !NTV2_IS_QUAD_QUAD_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) + framebuffer_id = NTV2_Xpt425Mux1AYUV; + else if (self->channel == NTV2_CHANNEL5) + framebuffer_id = NTV2_Xpt425Mux3AYUV; + else + g_assert_not_reached(); + } } GST_DEBUG_OBJECT(self, "Creating connection %d - %d", output_destination_id, framebuffer_id); router.AddConnection(output_destination_id, framebuffer_id); + if (self->quad_mode) { + if (self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_TSI) { + if (NTV2_IS_QUAD_QUAD_HFR_VIDEO_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptSDIOut2Input, + NTV2_XptFrameBuffer1_DS2YUV); + router.AddConnection(NTV2_XptSDIOut3Input, NTV2_XptFrameBuffer2YUV); + router.AddConnection(NTV2_XptSDIOut4Input, + NTV2_XptFrameBuffer2_DS2YUV); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptSDIOut6Input, + NTV2_XptFrameBuffer3_DS2YUV); + router.AddConnection(NTV2_XptSDIOut7Input, NTV2_XptFrameBuffer4YUV); + router.AddConnection(NTV2_XptSDIOut8Input, + NTV2_XptFrameBuffer4_DS2YUV); + } else { + g_assert_not_reached(); + } + } else if (NTV2_IS_QUAD_QUAD_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptSDIOut1InputDS2, + NTV2_XptFrameBuffer1_DS2YUV); + router.AddConnection(NTV2_XptSDIOut2Input, NTV2_XptFrameBuffer2YUV); + router.AddConnection(NTV2_XptSDIOut2InputDS2, + NTV2_XptFrameBuffer2_DS2YUV); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptSDIOut5InputDS2, + NTV2_XptFrameBuffer3_DS2YUV); + router.AddConnection(NTV2_XptSDIOut6Input, NTV2_XptFrameBuffer4YUV); + router.AddConnection(NTV2_XptSDIOut6InputDS2, + NTV2_XptFrameBuffer4_DS2YUV); + } else { + g_assert_not_reached(); + } + } else if (NTV2_IS_4K_HFR_VIDEO_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptSDIOut2Input, NTV2_Xpt425Mux1BYUV); + router.AddConnection(NTV2_XptSDIOut3Input, NTV2_Xpt425Mux2AYUV); + router.AddConnection(NTV2_XptSDIOut4Input, NTV2_Xpt425Mux2BYUV); + + router.AddConnection(NTV2_Xpt425Mux1AInput, NTV2_XptFrameBuffer1YUV); + router.AddConnection(NTV2_Xpt425Mux1BInput, + NTV2_XptFrameBuffer1_DS2YUV); + router.AddConnection(NTV2_Xpt425Mux2AInput, NTV2_XptFrameBuffer2YUV); + router.AddConnection(NTV2_Xpt425Mux2BInput, + NTV2_XptFrameBuffer2_DS2YUV); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptSDIOut6Input, NTV2_Xpt425Mux3BYUV); + router.AddConnection(NTV2_XptSDIOut7Input, NTV2_Xpt425Mux4AYUV); + router.AddConnection(NTV2_XptSDIOut8Input, NTV2_Xpt425Mux4BYUV); + + router.AddConnection(NTV2_Xpt425Mux3AInput, NTV2_XptFrameBuffer5YUV); + router.AddConnection(NTV2_Xpt425Mux3BInput, + NTV2_XptFrameBuffer5_DS2YUV); + router.AddConnection(NTV2_Xpt425Mux4AInput, NTV2_XptFrameBuffer6YUV); + router.AddConnection(NTV2_Xpt425Mux4BInput, + NTV2_XptFrameBuffer6_DS2YUV); + } else { + g_assert_not_reached(); + } + } else { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptSDIOut1InputDS2, NTV2_Xpt425Mux1BYUV); + router.AddConnection(NTV2_XptSDIOut2Input, NTV2_Xpt425Mux2AYUV); + router.AddConnection(NTV2_XptSDIOut2InputDS2, NTV2_Xpt425Mux2BYUV); + + router.AddConnection(NTV2_Xpt425Mux1AInput, NTV2_XptFrameBuffer1YUV); + router.AddConnection(NTV2_Xpt425Mux1BInput, + NTV2_XptFrameBuffer1_DS2YUV); + router.AddConnection(NTV2_Xpt425Mux2AInput, NTV2_XptFrameBuffer2YUV); + router.AddConnection(NTV2_Xpt425Mux2BInput, + NTV2_XptFrameBuffer2_DS2YUV); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptSDIOut5InputDS2, NTV2_Xpt425Mux3BYUV); + router.AddConnection(NTV2_XptSDIOut6Input, NTV2_Xpt425Mux4AYUV); + router.AddConnection(NTV2_XptSDIOut6InputDS2, NTV2_Xpt425Mux4BYUV); + + router.AddConnection(NTV2_Xpt425Mux3AInput, NTV2_XptFrameBuffer5YUV); + router.AddConnection(NTV2_Xpt425Mux3BInput, + NTV2_XptFrameBuffer5_DS2YUV); + router.AddConnection(NTV2_Xpt425Mux4AInput, NTV2_XptFrameBuffer6YUV); + router.AddConnection(NTV2_Xpt425Mux4BInput, + NTV2_XptFrameBuffer6_DS2YUV); + } else { + g_assert_not_reached(); + } + } + } else if (self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_SQD) { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptSDIOut2Input, NTV2_XptFrameBuffer2YUV); + router.AddConnection(NTV2_XptSDIOut3Input, NTV2_XptFrameBuffer3YUV); + router.AddConnection(NTV2_XptSDIOut4Input, NTV2_XptFrameBuffer4YUV); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptSDIOut6Input, NTV2_XptFrameBuffer6YUV); + router.AddConnection(NTV2_XptSDIOut7Input, NTV2_XptFrameBuffer7YUV); + router.AddConnection(NTV2_XptSDIOut8Input, NTV2_XptFrameBuffer8YUV); + } else { + g_assert_not_reached(); + } + } + } + { std::stringstream os; CNTV2SignalRouter oldRouter; @@ -1573,8 +1881,8 @@ restart: // Trivial drift calculation // - // TODO: Should probably take averages over a timespan (say 1 minute) into - // a ringbuffer and calculate a linear regression over them + // TODO: Should probably take averages over a timespan (say 1 minute) + // into a ringbuffer and calculate a linear regression over them // FIXME: Add some compensation by dropping/duplicating frames as needed // but make this configurable if (frames_rendered_start_time == GST_CLOCK_TIME_NONE && diff --git a/gstajasink.h b/gstajasink.h index 9106e267c3..1cc8e237b2 100644 --- a/gstajasink.h +++ b/gstajasink.h @@ -73,11 +73,13 @@ struct _GstAjaSink { GstAjaAudioSystem audio_system_setting; GstAjaOutputDestination output_destination; + GstAjaSdiMode sdi_mode; GstAjaTimecodeIndex timecode_index; GstAjaReferenceSource reference_source; NTV2AudioSystem audio_system; NTV2VideoFormat video_format; + bool quad_mode; NTV2VANCMode vanc_mode; guint32 f2_start_line; NTV2TCIndexes *tc_indexes; diff --git a/gstajasrc.cpp b/gstajasrc.cpp index 7bbf797db0..02cafd64fc 100644 --- a/gstajasrc.cpp +++ b/gstajasrc.cpp @@ -37,6 +37,7 @@ GST_DEBUG_CATEGORY_STATIC(gst_aja_src_debug); #define DEFAULT_VIDEO_FORMAT (GST_AJA_VIDEO_FORMAT_1080i_5000) #define DEFAULT_AUDIO_SYSTEM (GST_AJA_AUDIO_SYSTEM_AUTO) #define DEFAULT_INPUT_SOURCE (GST_AJA_INPUT_SOURCE_AUTO) +#define DEFAULT_SDI_MODE (GST_AJA_SDI_MODE_SINGLE_LINK) #define DEFAULT_AUDIO_SOURCE (GST_AJA_AUDIO_SOURCE_EMBEDDED) #define DEFAULT_TIMECODE_INDEX (GST_AJA_TIMECODE_INDEX_VITC) #define DEFAULT_REFERENCE_SOURCE (GST_AJA_REFERENCE_SOURCE_FREERUN) @@ -50,6 +51,7 @@ enum { PROP_VIDEO_FORMAT, PROP_AUDIO_SYSTEM, PROP_INPUT_SOURCE, + PROP_SDI_MODE, PROP_AUDIO_SOURCE, PROP_TIMECODE_INDEX, PROP_REFERENCE_SOURCE, @@ -159,6 +161,14 @@ static void gst_aja_src_class_init(GstAjaSrcClass *klass) { (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT))); + g_object_class_install_property( + gobject_class, PROP_SDI_MODE, + g_param_spec_enum( + "sdi-input-mode", "SDI Input Mode", "SDI input mode to use", + GST_TYPE_AJA_SDI_MODE, DEFAULT_SDI_MODE, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT))); + g_object_class_install_property( gobject_class, PROP_AUDIO_SOURCE, g_param_spec_enum( @@ -262,6 +272,9 @@ void gst_aja_src_set_property(GObject *object, guint property_id, case PROP_INPUT_SOURCE: self->input_source = (GstAjaInputSource)g_value_get_enum(value); break; + case PROP_SDI_MODE: + self->sdi_mode = (GstAjaSdiMode)g_value_get_enum(value); + break; case PROP_AUDIO_SOURCE: self->audio_source = (GstAjaAudioSource)g_value_get_enum(value); break; @@ -303,6 +316,9 @@ void gst_aja_src_get_property(GObject *object, guint property_id, GValue *value, case PROP_INPUT_SOURCE: g_value_set_enum(value, self->input_source); break; + case PROP_SDI_MODE: + g_value_set_enum(value, self->sdi_mode); + break; case PROP_AUDIO_SOURCE: g_value_set_enum(value, self->audio_source); break; @@ -400,71 +416,21 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { // global shared state ShmMutexLocker locker; - switch (self->video_format_setting) { - // TODO: GST_AJA_VIDEO_FORMAT_AUTO - case GST_AJA_VIDEO_FORMAT_1080i_5000: - self->video_format = ::NTV2_FORMAT_1080i_5000; - break; - case GST_AJA_VIDEO_FORMAT_1080i_5994: - self->video_format = ::NTV2_FORMAT_1080i_5994; - break; - case GST_AJA_VIDEO_FORMAT_1080i_6000: - self->video_format = ::NTV2_FORMAT_1080i_6000; - break; - case GST_AJA_VIDEO_FORMAT_720p_5994: - self->video_format = ::NTV2_FORMAT_720p_5994; - break; - case GST_AJA_VIDEO_FORMAT_720p_6000: - self->video_format = ::NTV2_FORMAT_720p_6000; - break; - case GST_AJA_VIDEO_FORMAT_1080p_2997: - self->video_format = ::NTV2_FORMAT_1080p_2997; - break; - case GST_AJA_VIDEO_FORMAT_1080p_3000: - self->video_format = ::NTV2_FORMAT_1080p_3000; - break; - case GST_AJA_VIDEO_FORMAT_1080p_2500: - self->video_format = ::NTV2_FORMAT_1080p_2500; - break; - case GST_AJA_VIDEO_FORMAT_1080p_2398: - self->video_format = ::NTV2_FORMAT_1080p_2398; - break; - case GST_AJA_VIDEO_FORMAT_1080p_2400: - self->video_format = ::NTV2_FORMAT_1080p_2400; - break; - case GST_AJA_VIDEO_FORMAT_720p_5000: - self->video_format = ::NTV2_FORMAT_720p_5000; - break; - case GST_AJA_VIDEO_FORMAT_720p_2398: - self->video_format = ::NTV2_FORMAT_720p_2398; - break; - case GST_AJA_VIDEO_FORMAT_720p_2500: - self->video_format = ::NTV2_FORMAT_720p_2500; - break; - case GST_AJA_VIDEO_FORMAT_1080p_5000_A: - self->video_format = ::NTV2_FORMAT_1080p_5000_A; - break; - case GST_AJA_VIDEO_FORMAT_1080p_5994_A: - self->video_format = ::NTV2_FORMAT_1080p_5994_A; - break; - case GST_AJA_VIDEO_FORMAT_1080p_6000_A: - self->video_format = ::NTV2_FORMAT_1080p_6000_A; - break; - case GST_AJA_VIDEO_FORMAT_625_5000: - self->video_format = ::NTV2_FORMAT_625_5000; - break; - case GST_AJA_VIDEO_FORMAT_525_5994: - self->video_format = ::NTV2_FORMAT_525_5994; - break; - case GST_AJA_VIDEO_FORMAT_525_2398: - self->video_format = ::NTV2_FORMAT_525_2398; - break; - case GST_AJA_VIDEO_FORMAT_525_2400: - self->video_format = ::NTV2_FORMAT_525_2400; - break; - default: - g_assert_not_reached(); - break; +#define NEEDS_QUAD_MODE(self) \ + (self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_SQD || \ + self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_TSI || \ + (self->input_source >= GST_AJA_INPUT_SOURCE_HDMI1 && \ + self->input_source <= GST_AJA_INPUT_SOURCE_HDMI4)) + + self->quad_mode = NEEDS_QUAD_MODE(self); + self->video_format = gst_ntv2_video_format_from_aja_format( + self->video_format_setting, self->quad_mode); + +#undef NEEDS_QUAD_MODE + + if (self->video_format == NTV2_FORMAT_UNKNOWN) { + GST_ERROR_OBJECT(self, "Unsupported mode"); + return FALSE; } if (!::NTV2DeviceCanDoVideoFormat(self->device_id, self->video_format)) { @@ -473,11 +439,78 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { return FALSE; } + if (self->quad_mode) { + if (self->channel != ::NTV2_CHANNEL1 && + self->channel != ::NTV2_CHANNEL5) { + GST_ERROR_OBJECT(self, "Quad modes require channels 1 or 5"); + return FALSE; + } + } + gst_clear_caps(&self->configured_caps); - self->configured_caps = gst_ntv2_video_format_to_caps(self->video_format); - gst_video_info_from_caps(&self->configured_info, self->configured_caps); + gst_video_info_from_ntv2_video_format(&self->configured_info, + self->video_format); + self->configured_caps = gst_video_info_to_caps(&self->configured_info); + + if (self->quad_mode) { + if (self->input_source >= GST_AJA_INPUT_SOURCE_HDMI1 && + self->input_source <= GST_AJA_INPUT_SOURCE_HDMI4) { + self->device->device->SetQuadQuadFrameEnable(false, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, self->channel); + self->device->device->Set4kSquaresEnable(true, self->channel); + self->device->device->SetTsiFrameEnable(true, self->channel); + } else { + switch (self->sdi_mode) { + case GST_AJA_SDI_MODE_SINGLE_LINK: + g_assert_not_reached(); + break; + case GST_AJA_SDI_MODE_QUAD_LINK_SQD: + if (self->configured_info.height > 2160) { + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + self->device->device->SetQuadQuadFrameEnable(true, self->channel); + self->device->device->SetQuadQuadSquaresEnable(true, + self->channel); + } else { + self->device->device->SetQuadQuadFrameEnable(false, + self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, + self->channel); + self->device->device->Set4kSquaresEnable(true, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + } + break; + case GST_AJA_SDI_MODE_QUAD_LINK_TSI: + if (self->configured_info.height > 2160) { + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + self->device->device->SetQuadQuadFrameEnable(true, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, + self->channel); + } else { + self->device->device->SetQuadQuadFrameEnable(false, + self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, + self->channel); + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(true, self->channel); + } + break; + } + } + } else { + self->device->device->Set4kSquaresEnable(false, self->channel); + self->device->device->SetTsiFrameEnable(false, self->channel); + self->device->device->SetQuadQuadFrameEnable(false, self->channel); + self->device->device->SetQuadQuadSquaresEnable(false, self->channel); + } self->device->device->SetMode(self->channel, NTV2_MODE_CAPTURE, false); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetMode((NTV2Channel)(self->channel + i), + NTV2_MODE_CAPTURE, false); + } GST_DEBUG_OBJECT(self, "Configuring video format %d on channel %d", (int)self->video_format, (int)self->channel); @@ -492,11 +525,22 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { } self->device->device->SetFrameBufferFormat(self->channel, ::NTV2_FBF_10BIT_YCBCR); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetFrameBufferFormat( + (NTV2Channel)(self->channel + i), ::NTV2_FBF_10BIT_YCBCR); + } self->device->device->DMABufferAutoLock(false, true, 0); - if (::NTV2DeviceHasBiDirectionalSDI(self->device_id)) + if (::NTV2DeviceHasBiDirectionalSDI(self->device_id)) { self->device->device->SetSDITransmitEnable(self->channel, false); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetSDITransmitEnable( + (NTV2Channel)(self->channel + i), false); + } + } // Always use the framebuffer associated with the channel NTV2InputCrosspointID framebuffer_id = @@ -591,6 +635,12 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { const NTV2Standard standard( ::GetNTV2StandardFromVideoFormat(self->video_format)); self->device->device->SetStandard(standard, self->channel); + if (self->quad_mode) { + for (int i = 1; i < 4; i++) + self->device->device->SetStandard(standard, + (NTV2Channel)(self->channel + i)); + } + const NTV2FrameGeometry geometry = ::GetNTV2FrameGeometryFromVideoFormat(self->video_format); @@ -599,6 +649,15 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { if (self->vanc_mode == ::NTV2_VANCMODE_OFF) { self->device->device->SetFrameGeometry(geometry, false, self->channel); self->device->device->SetVANCMode(self->vanc_mode, self->channel); + + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + self->device->device->SetFrameGeometry( + geometry, false, (NTV2Channel)(self->channel + i)); + self->device->device->SetVANCMode(self->vanc_mode, + (NTV2Channel)(self->channel + i)); + } + } } else { const NTV2FrameGeometry vanc_geometry = ::GetVANCFrameGeometry(geometry, self->vanc_mode); @@ -606,6 +665,15 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { self->device->device->SetFrameGeometry(vanc_geometry, false, self->channel); self->device->device->SetVANCMode(self->vanc_mode, self->channel); + + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + self->device->device->SetFrameGeometry( + vanc_geometry, false, (NTV2Channel)(self->channel + i)); + self->device->device->SetVANCMode(self->vanc_mode, + (NTV2Channel)(self->channel + i)); + } + } } CNTV2SignalRouter router; @@ -616,16 +684,225 @@ static gboolean gst_aja_src_start(GstAjaSrc *self) { // use NTV2ActualConnections connections = router.GetConnections(); - for (NTV2ActualConnectionsConstIter iter = connections.begin(); - iter != connections.end(); iter++) { - if (iter->first == framebuffer_id || iter->second == input_source_id) - router.RemoveConnection(iter->first, iter->second); + if (self->quad_mode) { + if (self->input_source >= GST_AJA_INPUT_SOURCE_HDMI1 && + self->input_source <= GST_AJA_INPUT_SOURCE_HDMI4) { + // Need to disconnect the 4 inputs corresponding to this channel from + // their framebuffers/muxers, and muxers from their framebuffers + for (auto iter = connections.begin(); iter != connections.end(); + iter++) { + if (iter->first == NTV2_XptFrameBuffer1Input || + iter->first == NTV2_XptFrameBuffer1BInput || + iter->first == NTV2_XptFrameBuffer2Input || + iter->first == NTV2_XptFrameBuffer2BInput || + iter->second == NTV2_Xpt425Mux1AYUV || + iter->second == NTV2_Xpt425Mux1BYUV || + iter->second == NTV2_Xpt425Mux2AYUV || + iter->second == NTV2_Xpt425Mux2BYUV || + iter->first == NTV2_Xpt425Mux1AInput || + iter->first == NTV2_Xpt425Mux1BInput || + iter->first == NTV2_Xpt425Mux2AInput || + iter->first == NTV2_Xpt425Mux2BInput || + iter->second == NTV2_XptHDMIIn1 || + iter->second == NTV2_XptHDMIIn1Q2 || + iter->second == NTV2_XptHDMIIn1Q3 || + iter->second == NTV2_XptHDMIIn1Q4) + router.RemoveConnection(iter->first, iter->second); + } + } else if (self->channel == NTV2_CHANNEL1) { + for (auto iter = connections.begin(); iter != connections.end(); + iter++) { + if (iter->first == NTV2_XptFrameBuffer1Input || + iter->first == NTV2_XptFrameBuffer1BInput || + iter->first == NTV2_XptFrameBuffer1DS2Input || + iter->first == NTV2_XptFrameBuffer2Input || + iter->first == NTV2_XptFrameBuffer2BInput || + iter->first == NTV2_XptFrameBuffer2DS2Input || + iter->second == NTV2_Xpt425Mux1AYUV || + iter->second == NTV2_Xpt425Mux1BYUV || + iter->second == NTV2_Xpt425Mux2AYUV || + iter->second == NTV2_Xpt425Mux2BYUV || + iter->first == NTV2_Xpt425Mux1AInput || + iter->first == NTV2_Xpt425Mux1BInput || + iter->first == NTV2_Xpt425Mux2AInput || + iter->first == NTV2_Xpt425Mux2BInput || + iter->second == NTV2_XptSDIIn1 || + iter->second == NTV2_XptSDIIn2 || + iter->second == NTV2_XptSDIIn3 || + iter->second == NTV2_XptSDIIn4 || + iter->second == NTV2_XptSDIIn1DS2 || + iter->second == NTV2_XptSDIIn2DS2 || + iter->first == NTV2_XptFrameBuffer1Input || + iter->first == NTV2_XptFrameBuffer2Input || + iter->first == NTV2_XptFrameBuffer3Input || + iter->first == NTV2_XptFrameBuffer4Input) + router.RemoveConnection(iter->first, iter->second); + } + } else if (self->channel == NTV2_CHANNEL5) { + for (auto iter = connections.begin(); iter != connections.end(); + iter++) { + if (iter->first == NTV2_XptFrameBuffer5Input || + iter->first == NTV2_XptFrameBuffer5BInput || + iter->first == NTV2_XptFrameBuffer5DS2Input || + iter->first == NTV2_XptFrameBuffer6Input || + iter->first == NTV2_XptFrameBuffer6BInput || + iter->first == NTV2_XptFrameBuffer6DS2Input || + iter->second == NTV2_Xpt425Mux3AYUV || + iter->second == NTV2_Xpt425Mux3BYUV || + iter->second == NTV2_Xpt425Mux4AYUV || + iter->second == NTV2_Xpt425Mux4BYUV || + iter->first == NTV2_Xpt425Mux3AInput || + iter->first == NTV2_Xpt425Mux3BInput || + iter->first == NTV2_Xpt425Mux4AInput || + iter->first == NTV2_Xpt425Mux4BInput || + iter->second == NTV2_XptSDIIn5 || + iter->second == NTV2_XptSDIIn6 || + iter->second == NTV2_XptSDIIn7 || + iter->second == NTV2_XptSDIIn8 || + iter->second == NTV2_XptSDIIn5DS2 || + iter->second == NTV2_XptSDIIn6DS2 || + iter->first == NTV2_XptFrameBuffer5Input || + iter->first == NTV2_XptFrameBuffer6Input || + iter->first == NTV2_XptFrameBuffer7Input || + iter->first == NTV2_XptFrameBuffer8Input) + router.RemoveConnection(iter->first, iter->second); + } + } else { + g_assert_not_reached(); + } + } else { + for (auto iter = connections.begin(); iter != connections.end(); iter++) { + if (iter->first == framebuffer_id || iter->second == input_source_id) + router.RemoveConnection(iter->first, iter->second); + + if (((input_source_id == NTV2_XptSDIIn6 || + input_source_id == NTV2_XptSDIIn8) && + iter->first == NTV2_XptFrameBuffer6BInput) || + ((input_source_id == NTV2_XptSDIIn5 || + input_source_id == NTV2_XptSDIIn6) && + iter->first == NTV2_XptFrameBuffer5BInput) || + ((input_source_id == NTV2_XptSDIIn4 || + input_source_id == NTV2_XptSDIIn2) && + iter->first == NTV2_XptFrameBuffer2BInput) || + ((input_source_id == NTV2_XptSDIIn1 || + input_source_id == NTV2_XptSDIIn2) && + iter->first == NTV2_XptFrameBuffer1BInput)) + router.RemoveConnection(iter->first, iter->second); + } + } + + if (self->quad_mode) { + if (self->input_source >= GST_AJA_INPUT_SOURCE_HDMI1 && + self->input_source <= GST_AJA_INPUT_SOURCE_HDMI4) { + input_source_id = NTV2_Xpt425Mux1AYUV; + } else if (self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_TSI && + !NTV2_IS_QUAD_QUAD_HFR_VIDEO_FORMAT(self->video_format) && + !NTV2_IS_QUAD_QUAD_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) + input_source_id = NTV2_Xpt425Mux1AYUV; + else if (self->channel == NTV2_CHANNEL5) + input_source_id = NTV2_Xpt425Mux3AYUV; + else + g_assert_not_reached(); + } } GST_DEBUG_OBJECT(self, "Creating connection %d - %d", framebuffer_id, input_source_id); router.AddConnection(framebuffer_id, input_source_id); + if (self->quad_mode) { + if (self->input_source >= GST_AJA_INPUT_SOURCE_HDMI1 && + self->input_source <= GST_AJA_INPUT_SOURCE_HDMI4) { + router.AddConnection(NTV2_XptFrameBuffer1BInput, NTV2_Xpt425Mux1BYUV); + router.AddConnection(NTV2_XptFrameBuffer2Input, NTV2_Xpt425Mux2AYUV); + router.AddConnection(NTV2_XptFrameBuffer2BInput, NTV2_Xpt425Mux2BYUV); + + router.AddConnection(NTV2_Xpt425Mux1AInput, NTV2_XptHDMIIn1); + router.AddConnection(NTV2_Xpt425Mux1BInput, NTV2_XptHDMIIn1Q2); + router.AddConnection(NTV2_Xpt425Mux2AInput, NTV2_XptHDMIIn1Q3); + router.AddConnection(NTV2_Xpt425Mux2BInput, NTV2_XptHDMIIn1Q4); + } else { + if (self->sdi_mode == GST_AJA_SDI_MODE_QUAD_LINK_TSI) { + if (NTV2_IS_QUAD_QUAD_HFR_VIDEO_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptFrameBuffer1DS2Input, + NTV2_XptSDIIn2); + router.AddConnection(NTV2_XptFrameBuffer2Input, NTV2_XptSDIIn3); + router.AddConnection(NTV2_XptFrameBuffer2DS2Input, + NTV2_XptSDIIn4); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptFrameBuffer5DS2Input, + NTV2_XptSDIIn6); + router.AddConnection(NTV2_XptFrameBuffer5Input, NTV2_XptSDIIn7); + router.AddConnection(NTV2_XptFrameBuffer6DS2Input, + NTV2_XptSDIIn8); + } else { + g_assert_not_reached(); + } + } else if (NTV2_IS_QUAD_QUAD_FORMAT(self->video_format)) { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptFrameBuffer1DS2Input, + NTV2_XptSDIIn1DS2); + router.AddConnection(NTV2_XptFrameBuffer2Input, NTV2_XptSDIIn2); + router.AddConnection(NTV2_XptFrameBuffer2DS2Input, + NTV2_XptSDIIn2DS2); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptFrameBuffer5DS2Input, + NTV2_XptSDIIn5DS2); + router.AddConnection(NTV2_XptFrameBuffer5Input, NTV2_XptSDIIn6); + router.AddConnection(NTV2_XptFrameBuffer6DS2Input, + NTV2_XptSDIIn6DS2); + } else { + g_assert_not_reached(); + } + // FIXME: Need special handling of NTV2_IS_4K_HFR_VIDEO_FORMAT for + // TSI? + } else { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptFrameBuffer1BInput, + NTV2_Xpt425Mux1BYUV); + router.AddConnection(NTV2_XptFrameBuffer2Input, + NTV2_Xpt425Mux2AYUV); + router.AddConnection(NTV2_XptFrameBuffer2BInput, + NTV2_Xpt425Mux2BYUV); + + router.AddConnection(NTV2_Xpt425Mux1AInput, NTV2_XptSDIIn1); + router.AddConnection(NTV2_Xpt425Mux1BInput, NTV2_XptSDIIn2); + router.AddConnection(NTV2_Xpt425Mux2AInput, NTV2_XptSDIIn3); + router.AddConnection(NTV2_Xpt425Mux2BInput, NTV2_XptSDIIn4); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptFrameBuffer5BInput, + NTV2_Xpt425Mux3BYUV); + router.AddConnection(NTV2_XptFrameBuffer6Input, + NTV2_Xpt425Mux4AYUV); + router.AddConnection(NTV2_XptFrameBuffer6BInput, + NTV2_Xpt425Mux4BYUV); + + router.AddConnection(NTV2_Xpt425Mux3AInput, NTV2_XptSDIIn5); + router.AddConnection(NTV2_Xpt425Mux3BInput, NTV2_XptSDIIn6); + router.AddConnection(NTV2_Xpt425Mux4AInput, NTV2_XptSDIIn7); + router.AddConnection(NTV2_Xpt425Mux4BInput, NTV2_XptSDIIn8); + } else { + g_assert_not_reached(); + } + } + } else { + if (self->channel == NTV2_CHANNEL1) { + router.AddConnection(NTV2_XptFrameBuffer2Input, NTV2_XptSDIIn2); + router.AddConnection(NTV2_XptFrameBuffer3Input, NTV2_XptSDIIn3); + router.AddConnection(NTV2_XptFrameBuffer4Input, NTV2_XptSDIIn4); + } else if (self->channel == NTV2_CHANNEL5) { + router.AddConnection(NTV2_XptFrameBuffer6Input, NTV2_XptSDIIn6); + router.AddConnection(NTV2_XptFrameBuffer7Input, NTV2_XptSDIIn7); + router.AddConnection(NTV2_XptFrameBuffer8Input, NTV2_XptSDIIn8); + } else { + g_assert_not_reached(); + } + } + } + } + { std::stringstream os; CNTV2SignalRouter oldRouter; @@ -1194,6 +1471,17 @@ restart: goto out; } + if (self->quad_mode) { + for (int i = 1; i < 4; i++) { + if (!self->device->device->EnableChannel( + (NTV2Channel)(self->channel + i))) { + GST_ELEMENT_ERROR(self, STREAM, FAILED, (NULL), + ("Failed to enable channel")); + goto out; + } + } + } + { // Make sure to globally lock here as the routing settings and others are // global shared state @@ -1228,6 +1516,13 @@ restart: NTV2VideoFormat current_video_format = self->device->device->GetInputVideoFormat( self->configured_input_source); + NTV2VideoFormat effective_video_format = self->video_format; + // Can't call this unconditionally as it also maps e.g. 3840x2160p to 1080p + if (self->quad_mode) { + effective_video_format = + ::GetQuarterSizedVideoFormat(effective_video_format); + } + if (current_video_format == ::NTV2_FORMAT_UNKNOWN) { GST_DEBUG_OBJECT(self, "No signal, waiting"); g_mutex_unlock(&self->queue_lock); @@ -1240,11 +1535,14 @@ restart: } g_mutex_lock(&self->queue_lock); continue; - } else if (current_video_format != self->video_format) { + } else if (current_video_format != effective_video_format && + current_video_format != self->video_format) { // TODO: Handle GST_AJA_VIDEO_FORMAT_AUTO here GST_DEBUG_OBJECT(self, - "Different input format %u than configured %u, waiting", - current_video_format, self->video_format); + "Different input format %u than configured %u " + "(effective %u), waiting", + current_video_format, self->video_format, + effective_video_format); g_mutex_unlock(&self->queue_lock); self->device->device->WaitForInputVerticalInterrupt(self->channel); frames_dropped_last = G_MAXUINT64; diff --git a/gstajasrc.h b/gstajasrc.h index a8ae0e7111..cfd661b4da 100644 --- a/gstajasrc.h +++ b/gstajasrc.h @@ -64,6 +64,7 @@ struct _GstAjaSrc { NTV2Channel channel; GstAjaAudioSystem audio_system_setting; GstAjaVideoFormat video_format_setting; + GstAjaSdiMode sdi_mode; GstAjaInputSource input_source; GstAjaAudioSource audio_source; GstAjaTimecodeIndex timecode_index; @@ -73,6 +74,7 @@ struct _GstAjaSrc { NTV2AudioSystem audio_system; NTV2VideoFormat video_format; + bool quad_mode; NTV2VANCMode vanc_mode; NTV2InputSource configured_input_source; NTV2TCIndex tc_index;