From 9480d91ad41bdeb836a45a415e911fb870dc0955 Mon Sep 17 00:00:00 2001 From: Florian Langlois Date: Thu, 8 Jan 2015 16:42:31 +0100 Subject: [PATCH] decklinkvideosrc: Add automatic mode detection https://bugzilla.gnome.org/show_bug.cgi?id=739284 --- sys/decklink/gstdecklink.cpp | 134 ++++++++++++++++++++++++++- sys/decklink/gstdecklink.h | 6 +- sys/decklink/gstdecklinkvideosrc.cpp | 48 ++++++++-- sys/decklink/gstdecklinkvideosrc.h | 1 + 4 files changed, 178 insertions(+), 11 deletions(-) diff --git a/sys/decklink/gstdecklink.cpp b/sys/decklink/gstdecklink.cpp index de85a4b0cc..67ec9f6e22 100644 --- a/sys/decklink/gstdecklink.cpp +++ b/sys/decklink/gstdecklink.cpp @@ -38,6 +38,8 @@ gst_decklink_mode_get_type (void) { static gsize id = 0; static const GEnumValue modes[] = { + {GST_DECKLINK_MODE_AUTO, "auto", "Automatic detection"}, + {GST_DECKLINK_MODE_NTSC, "ntsc", "NTSC SD 60i"}, {GST_DECKLINK_MODE_NTSC2398, "ntsc2398", "NTSC SD 60i (24 fps)"}, {GST_DECKLINK_MODE_PAL, "pal", "PAL SD 50i"}, @@ -140,6 +142,8 @@ gst_decklink_audio_connection_get_type (void) #define HD 1, 1, false, true static const GstDecklinkMode modes[] = { + {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC}, // default is ntsc + {bmdModeNTSC, 720, 486, 30000, 1001, true, NTSC}, {bmdModeNTSC2398, 720, 486, 24000, 1001, true, NTSC}, {bmdModePAL, 720, 576, 25, 1, true, PAL}, @@ -181,11 +185,113 @@ static const GstDecklinkMode modes[] = { const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e) { - if (e < GST_DECKLINK_MODE_NTSC || e > GST_DECKLINK_MODE_3184p60) + if (e < GST_DECKLINK_MODE_AUTO || e > GST_DECKLINK_MODE_3184p60) return NULL; return &modes[e]; } +const GstDecklinkModeEnum +gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode) +{ + GstDecklinkModeEnum displayMode = GST_DECKLINK_MODE_NTSC; + switch (mode) { + case bmdModeNTSC: + displayMode = GST_DECKLINK_MODE_NTSC; + break; + case bmdModeNTSC2398: + displayMode = GST_DECKLINK_MODE_NTSC2398; + break; + case bmdModePAL: + displayMode = GST_DECKLINK_MODE_PAL; + break; + case bmdModeNTSCp: + displayMode = GST_DECKLINK_MODE_NTSC_P; + break; + case bmdModePALp: + displayMode = GST_DECKLINK_MODE_PAL_P; + break; + case bmdModeHD1080p2398: + displayMode = GST_DECKLINK_MODE_1080p2398; + break; + case bmdModeHD1080p24: + displayMode = GST_DECKLINK_MODE_1080p24; + break; + case bmdModeHD1080p25: + displayMode = GST_DECKLINK_MODE_1080p25; + break; + case bmdModeHD1080p2997: + displayMode = GST_DECKLINK_MODE_1080p2997; + break; + case bmdModeHD1080p30: + displayMode = GST_DECKLINK_MODE_1080p30; + break; + case bmdModeHD1080i50: + displayMode = GST_DECKLINK_MODE_1080i50; + break; + case bmdModeHD1080i5994: + displayMode = GST_DECKLINK_MODE_1080i5994; + break; + case bmdModeHD1080i6000: + displayMode = GST_DECKLINK_MODE_1080i60; + break; + case bmdModeHD1080p50: + displayMode = GST_DECKLINK_MODE_1080p50; + break; + case bmdModeHD1080p5994: + displayMode = GST_DECKLINK_MODE_1080p5994; + break; + case bmdModeHD1080p6000: + displayMode = GST_DECKLINK_MODE_1080p60; + break; + case bmdModeHD720p50: + displayMode = GST_DECKLINK_MODE_720p50; + break; + case bmdModeHD720p5994: + displayMode = GST_DECKLINK_MODE_720p5994; + break; + case bmdModeHD720p60: + displayMode = GST_DECKLINK_MODE_720p60; + break; + case bmdMode2k2398: + displayMode = GST_DECKLINK_MODE_2048p2398; + break; + case bmdMode2k24: + displayMode = GST_DECKLINK_MODE_2048p24; + break; + case bmdMode2k25: + displayMode = GST_DECKLINK_MODE_2048p25; + break; + case bmdMode4K2160p2398: + displayMode = GST_DECKLINK_MODE_3184p2398; + break; + case bmdMode4K2160p24: + displayMode = GST_DECKLINK_MODE_3184p24; + break; + case bmdMode4K2160p25: + displayMode = GST_DECKLINK_MODE_3184p25; + break; + case bmdMode4K2160p2997: + displayMode = GST_DECKLINK_MODE_3184p2997; + break; + case bmdMode4K2160p30: + displayMode = GST_DECKLINK_MODE_3184p30; + break; + case bmdMode4K2160p50: + displayMode = GST_DECKLINK_MODE_3184p50; + break; + case bmdMode4K2160p5994: + displayMode = GST_DECKLINK_MODE_3184p5994; + break; + case bmdMode4K2160p60: + displayMode = GST_DECKLINK_MODE_3184p60; + break; + default: + g_assert_not_reached (); + break; + } + return displayMode; +} + static const BMDVideoConnection connections[] = { 0, /* auto */ bmdVideoConnectionSDI, @@ -346,9 +452,21 @@ public: virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged (BMDVideoInputFormatChangedEvents, - IDeckLinkDisplayMode *, BMDDetectedVideoInputFormatFlags) + IDeckLinkDisplayMode * mode, BMDDetectedVideoInputFormatFlags) { - GST_FIXME ("Video input format change not supported yet"); + GST_INFO ("Video input format changed"); + + g_mutex_lock (&m_input->lock); + m_input->input->PauseStreams (); + m_input->input->EnableVideoInput (mode->GetDisplayMode (), + bmdFormat8BitYUV, bmdVideoInputEnableFormatDetection); + m_input->input->FlushStreams (); + m_input->input->StartStreams (); + m_input->mode = + gst_decklink_get_mode (gst_decklink_get_mode_enum_from_bmd + (mode->GetDisplayMode ())); + g_mutex_unlock (&m_input->lock); + return S_OK; } @@ -362,7 +480,9 @@ public: if (m_input->got_video_frame) { GstClockTime capture_time = clock_time - gst_element_get_base_time (m_input->videosrc); - m_input->got_video_frame (m_input->videosrc, video_frame, capture_time); + m_input->got_video_frame (m_input->videosrc, video_frame, + gst_decklink_get_mode_enum_from_bmd (m_input->mode->mode), + capture_time); } if (m_input->got_audio_packet) { @@ -493,6 +613,12 @@ init_devices (gpointer data) GST_WARNING ("selected device does not have config interface"); } + ret = decklink->QueryInterface (IID_IDeckLinkAttributes, + (void **) &devices[i].input.attributes); + if (ret != S_OK) { + GST_WARNING ("selected device does not have attributes interface"); + } + ret = iterator->Next (&decklink); i++; diff --git a/sys/decklink/gstdecklink.h b/sys/decklink/gstdecklink.h index ddf570844b..6c3df144f7 100644 --- a/sys/decklink/gstdecklink.h +++ b/sys/decklink/gstdecklink.h @@ -44,6 +44,8 @@ #endif /* _MSC_VER */ typedef enum { + GST_DECKLINK_MODE_AUTO, + GST_DECKLINK_MODE_NTSC, GST_DECKLINK_MODE_NTSC2398, GST_DECKLINK_MODE_PAL, @@ -122,6 +124,7 @@ struct _GstDecklinkMode { }; const GstDecklinkMode * gst_decklink_get_mode (GstDecklinkModeEnum e); +const GstDecklinkModeEnum gst_decklink_get_mode_enum_from_bmd (BMDDisplayMode mode); const BMDVideoConnection gst_decklink_get_connection (GstDecklinkConnectionEnum e); GstCaps * gst_decklink_mode_get_caps (GstDecklinkModeEnum e); GstCaps * gst_decklink_mode_get_template_caps (void); @@ -152,13 +155,14 @@ struct _GstDecklinkInput { IDeckLink *device; IDeckLinkInput *input; IDeckLinkConfiguration *config; + IDeckLinkAttributes *attributes; GstClock *clock; /* Everything below protected by mutex */ GMutex lock; /* Set by the video source */ - void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstClockTime capture_time); + void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, GstClockTime capture_time); /* Configured mode or NULL */ const GstDecklinkMode *mode; diff --git a/sys/decklink/gstdecklinkvideosrc.cpp b/sys/decklink/gstdecklinkvideosrc.cpp index 2f3f74e9a4..3dff4d5b10 100644 --- a/sys/decklink/gstdecklinkvideosrc.cpp +++ b/sys/decklink/gstdecklinkvideosrc.cpp @@ -29,7 +29,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_decklink_video_src_debug); #define GST_CAT_DEFAULT gst_decklink_video_src_debug -#define DEFAULT_MODE (GST_DECKLINK_MODE_NTSC) +#define DEFAULT_MODE (GST_DECKLINK_MODE_AUTO) #define DEFAULT_CONNECTION (GST_DECKLINK_CONNECTION_AUTO) #define DEFAULT_BUFFER_SIZE (5) @@ -46,6 +46,7 @@ typedef struct { IDeckLinkVideoInputFrame *frame; GstClockTime capture_time; + GstDecklinkModeEnum mode; } CaptureFrame; static void @@ -169,6 +170,7 @@ static void gst_decklink_video_src_init (GstDecklinkVideoSrc * self) { self->mode = DEFAULT_MODE; + self->caps_mode = DEFAULT_MODE; self->connection = DEFAULT_CONNECTION; self->device_number = 0; self->buffer_size = DEFAULT_BUFFER_SIZE; @@ -249,7 +251,7 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (bsrc); GstCaps *mode_caps, *caps; - mode_caps = gst_decklink_mode_get_caps (self->mode); + mode_caps = gst_decklink_mode_get_caps (self->caps_mode); if (filter) { caps = gst_caps_intersect_full (filter, mode_caps, GST_CAPS_INTERSECT_FIRST); @@ -263,7 +265,8 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) static void gst_decklink_video_src_got_frame (GstElement * element, - IDeckLinkVideoInputFrame * frame, GstClockTime capture_time) + IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, + GstClockTime capture_time) { GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element); @@ -284,6 +287,7 @@ gst_decklink_video_src_got_frame (GstElement * element, f = (CaptureFrame *) g_malloc0 (sizeof (CaptureFrame)); f->frame = frame; f->capture_time = capture_time; + f->mode = mode; frame->AddRef (); g_queue_push_tail (&self->current_frames, f); g_cond_signal (&self->cond); @@ -301,6 +305,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer) VideoFrame *vf; CaptureFrame *f; GstClockTime timestamp, duration; + GstCaps *caps; g_mutex_lock (&self->lock); while (g_queue_is_empty (&self->current_frames) && !self->flushing) { @@ -316,6 +321,14 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer) return GST_FLOW_FLUSHING; } + if (self->caps_mode != f->mode) { + self->caps_mode = f->mode; + caps = gst_decklink_mode_get_caps (self->caps_mode); + gst_video_info_from_caps (&self->info, caps); + gst_base_src_set_caps (GST_BASE_SRC_CAST (bsrc), caps); + gst_caps_unref (caps); + } + f->frame->GetBytes ((gpointer *) & data); data_size = self->info.size; @@ -362,7 +375,7 @@ gst_decklink_video_src_query (GstBaseSrc * bsrc, GstQuery * query) GstClockTime min, max; const GstDecklinkMode *mode; - mode = gst_decklink_get_mode (self->mode); + mode = gst_decklink_get_mode (self->caps_mode); min = gst_util_uint64_scale_ceil (GST_SECOND, mode->fps_d, mode->fps_n); max = self->buffer_size * min; @@ -414,6 +427,8 @@ static gboolean gst_decklink_video_src_open (GstDecklinkVideoSrc * self) { const GstDecklinkMode *mode; + BMDVideoInputFlags flags; + bool autoDetection; GstCaps *caps; HRESULT ret; @@ -446,11 +461,31 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self) } } + flags = bmdVideoInputFlagDefault; + if (self->mode == GST_DECKLINK_MODE_AUTO) { + if (self->input->attributes) { + ret = + self->input-> + attributes->GetFlag (BMDDeckLinkSupportsInputFormatDetection, + &autoDetection); + if (ret != S_OK) { + GST_ERROR_OBJECT (self, "Failed to get attribute (autodetection)"); + return FALSE; + } + if (autoDetection) + flags |= bmdVideoInputEnableFormatDetection; + } + if (!autoDetection) { + GST_ERROR_OBJECT (self, "Failed to activate auto-detection"); + return FALSE; + } + } + mode = gst_decklink_get_mode (self->mode); g_assert (mode != NULL); ret = self->input->input->EnableVideoInput (mode->mode, - bmdFormat8BitYUV, bmdVideoOutputFlagDefault); + bmdFormat8BitYUV, flags); if (ret != S_OK) { GST_WARNING_OBJECT (self, "Failed to enable video input"); gst_decklink_release_nth_input (self->device_number, @@ -463,7 +498,8 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self) self->input->got_video_frame = gst_decklink_video_src_got_frame; g_mutex_unlock (&self->input->lock); - caps = gst_decklink_mode_get_caps (self->mode); + self->caps_mode = gst_decklink_get_mode_enum_from_bmd (mode->mode); + caps = gst_decklink_mode_get_caps (self->caps_mode); gst_video_info_from_caps (&self->info, caps); gst_caps_unref (caps); diff --git a/sys/decklink/gstdecklinkvideosrc.h b/sys/decklink/gstdecklinkvideosrc.h index da2da7dd0b..6cbf2af722 100644 --- a/sys/decklink/gstdecklinkvideosrc.h +++ b/sys/decklink/gstdecklinkvideosrc.h @@ -51,6 +51,7 @@ struct _GstDecklinkVideoSrc GstPushSrc parent; GstDecklinkModeEnum mode; + GstDecklinkModeEnum caps_mode; GstDecklinkConnectionEnum connection; gint device_number;