diff --git a/plugins/win-dshow/win-dshow.cpp b/plugins/win-dshow/win-dshow.cpp index 465f30472..463b24fac 100644 --- a/plugins/win-dshow/win-dshow.cpp +++ b/plugins/win-dshow/win-dshow.cpp @@ -20,13 +20,15 @@ using namespace std; using namespace DShow; /* settings defines that will cause errors if there are typos */ +#define VIDEO_DEVICE_ID "video_device_id" #define RES_TYPE "res_type" #define RESOLUTION "resolution" -#define LAST_RESOLUTION "last_resolution" -#define VIDEO_DEVICE_ID "video_device_id" -#define LAST_VIDEO_DEV_ID "last_video_device_id" #define FRAME_INTERVAL "frame_interval" #define VIDEO_FORMAT "video_format" +#define LAST_VIDEO_DEV_ID "last_video_device_id" +#define LAST_RESOLUTION "last_resolution" +#define LAST_RES_TYPE "last_res_type" +#define LAST_INTERVAL "last_interval" enum ResType { ResType_Preferred, @@ -158,6 +160,19 @@ static inline bool ConvertRes(int &cx, int &cy, const char *res) return sscanf(res, "%dx%d", &cx, &cy) == 2; } +static void ApplyDefaultConfigSettings(obs_data_t settings, + const VideoConfig &config) +{ + string res = to_string(config.cx) + string("x") + to_string(config.cy); + obs_data_setstring(settings, RESOLUTION, res.c_str()); + obs_data_setint(settings, FRAME_INTERVAL, config.frameInterval); + obs_data_setint(settings, VIDEO_FORMAT, (int)config.internalFormat); + + obs_data_setstring(settings, LAST_RESOLUTION, res.c_str()); + obs_data_setint(settings, LAST_INTERVAL, config.frameInterval); + obs_data_setint(settings, LAST_RES_TYPE, ResType_Preferred); +} + void DShowInput::Update(obs_data_t settings) { const char *video_device_id, *res; @@ -171,9 +186,6 @@ void DShowInput::Update(obs_data_t settings) VideoFormat format = (VideoFormat)obs_data_getint(settings, VIDEO_FORMAT); - obs_data_setstring(settings, LAST_VIDEO_DEV_ID, video_device_id); - obs_data_setstring(settings, LAST_RESOLUTION, res); - if (!comInitialized) { CoInitialize(nullptr); comInitialized = true; @@ -202,19 +214,25 @@ void DShowInput::Update(obs_data_t settings) videoConfig.frameInterval = interval; videoConfig.internalFormat = format; - if (format == VideoFormat::MJPEG) - videoConfig.format = VideoFormat::XRGB; - else - videoConfig.format = format; + if (videoConfig.internalFormat != VideoFormat::MJPEG) + videoConfig.format = videoConfig.internalFormat; device.SetVideoConfig(&videoConfig); + if (videoConfig.internalFormat == VideoFormat::MJPEG) { + videoConfig.format = VideoFormat::XRGB; + device.SetVideoConfig(&videoConfig); + } + if (!device.ConnectFilters()) return; if (device.Start() != Result::Success) return; + if (videoConfig.useDefaultConfig) + ApplyDefaultConfigSettings(settings, videoConfig); + frame.width = videoConfig.cx; frame.height = videoConfig.cy; frame.format = ConvertVideoFormat(videoConfig.format); @@ -364,45 +382,81 @@ static const FPSFormat validFPSFormats[] = { #define DEVICE_INTERVAL_DIFF_LIMIT 20 -static void AddFPSRates(obs_property_t p, const VideoInfo &cap) +static bool AddFPSRate(obs_property_t p, const FPSFormat &format, + const VideoInfo &cap) { + long long interval = format.interval; + + if (format.interval < cap.minInterval || + format.interval > cap.maxInterval) { + /* account for slight inaccuracies in intervals + * among different devices */ + long long diff = 0; + + if (format.interval < cap.minInterval) { + interval = cap.minInterval; + diff = cap.minInterval - format.interval; + } else if (format.interval > cap.maxInterval) { + interval = cap.maxInterval; + diff = format.interval - cap.minInterval; + } + + if (diff > DEVICE_INTERVAL_DIFF_LIMIT) + return false; + } + + obs_property_list_add_int(p, format.text, interval); + return true; +} + +static inline bool AddFPSRates(obs_property_t p, const VideoDevice &device, + int cx, int cy, long long &interval) +{ + long long bestInterval = MAX_LL; + bool intervalFound = false; + for (const FPSFormat &format : validFPSFormats) { - if (format.interval >= cap.minInterval && - format.interval <= cap.maxInterval) { - obs_property_list_add_int(p, format.text, - format.interval); + for (const VideoInfo &cap : device.caps) { + if (cx >= cap.minCX && cx <= cap.maxCX && + cy >= cap.minCY && cy <= cap.maxCY) { - } else { - /* account for slight inaccuracies in intervals - * among different devices */ - long long interval = 0; - long long diff = 0; + if (!intervalFound) { + if (interval >= cap.minInterval && + interval <= cap.maxInterval) + intervalFound = true; + else if (cap.minInterval < bestInterval) + bestInterval = cap.minInterval; + } - if (format.interval < cap.minInterval) { - interval = cap.minInterval; - diff = cap.minInterval - format.interval; - } else if (format.interval > cap.maxInterval) { - interval = cap.maxInterval; - diff = format.interval - cap.minInterval; + if (AddFPSRate(p, format, cap)) + break; } - - if (diff <= DEVICE_INTERVAL_DIFF_LIMIT) - obs_property_list_add_int(p, format.text, - interval); } } + + if (!intervalFound) { + interval = bestInterval; + return false; + } + + return true; } +static bool DeviceIntervalChanged(obs_properties_t props, obs_property_t p, + obs_data_t settings); + static bool DeviceResolutionChanged(obs_properties_t props, obs_property_t p, obs_data_t settings) { PropertiesData *data = (PropertiesData*)obs_properties_get_param(props); const char *res, *last_res, *id; + long long interval; VideoDevice device; id = obs_data_getstring(settings, VIDEO_DEVICE_ID); res = obs_data_getstring(settings, RESOLUTION); last_res = obs_data_getstring(settings, LAST_RESOLUTION); + interval = obs_data_getint (settings, FRAME_INTERVAL); if (!data->GetDevice(device, id)) return true; @@ -412,17 +466,14 @@ static bool DeviceResolutionChanged(obs_properties_t props, obs_property_t p, return true; p = obs_properties_get(props, FRAME_INTERVAL); + obs_property_list_clear(p); + if (!AddFPSRates(p, device, cx, cy, interval)) + obs_data_setint(settings, FRAME_INTERVAL, interval); - if (res && last_res && strcmp(res, last_res) != 0) - obs_data_setint(settings, FRAME_INTERVAL, 0); - - for (const VideoInfo &cap : device.caps) { - if (cx >= cap.minCX && cx <= cap.maxCX && - cy >= cap.minCY && cy <= cap.maxCY) { - AddFPSRates(p, cap); - break; - } + if (res && last_res && strcmp(res, last_res) != 0) { + DeviceIntervalChanged(props, p, settings); + obs_data_setstring(settings, LAST_RESOLUTION, res); } return true; @@ -526,8 +577,10 @@ static const VideoFormatName videoFormatNames[] = { {VideoFormat::H264, "H264"} }; -static void UpdateVideoFormats(obs_properties_t props, VideoDevice &device) +static bool UpdateVideoFormats(obs_properties_t props, VideoDevice &device, + long long interval, int cx, int cy, VideoFormat format) { + bool foundFormat = false; obs_property_t p = obs_properties_get(props, VIDEO_FORMAT); obs_property_list_clear(p); @@ -535,13 +588,24 @@ static void UpdateVideoFormats(obs_properties_t props, VideoDevice &device) for (const VideoFormatName &name : videoFormatNames) { for (const VideoInfo &cap : device.caps) { - if (cap.format == name.format) { + + if (interval >= cap.minInterval && + interval <= cap.maxInterval && + cx >= cap.minCX && cx <= cap.maxCX && + cy >= cap.minCY && cy <= cap.maxCY && + cap.format == name.format) { + + if (format == cap.format) + foundFormat = true; + obs_property_list_add_int(p, name.name, (int)name.format); break; } } } + + return foundFormat; } static bool DeviceSelectionChanged(obs_properties_t props, obs_property_t p, @@ -580,9 +644,9 @@ static bool DeviceSelectionChanged(obs_properties_t props, obs_property_t p, SetClosestResFPS(props, settings); DeviceResolutionChanged(props, p, settings); obs_data_setint(settings, VIDEO_FORMAT, (int)VideoFormat::Any); + obs_data_setstring(settings, LAST_VIDEO_DEV_ID, id); } - UpdateVideoFormats(props, device); return true; } @@ -658,20 +722,62 @@ static bool ResTypeChanged(obs_properties_t props, obs_property_t p, obs_data_t settings) { int val = (int)obs_data_getint(settings, RES_TYPE); - bool visible = (val != ResType_Preferred); + int lastVal = (int)obs_data_getint(settings, LAST_RES_TYPE); + bool enabled = (val != ResType_Preferred); p = obs_properties_get(props, RESOLUTION); - obs_property_set_visible(p, visible); + obs_property_set_enabled(p, enabled); p = obs_properties_get(props, FRAME_INTERVAL); - obs_property_set_visible(p, visible); + obs_property_set_enabled(p, enabled); - if (val == ResType_Custom) + p = obs_properties_get(props, VIDEO_FORMAT); + obs_property_set_enabled(p, enabled); + + if (val == ResType_Custom && lastVal != val) { SetClosestResFPS(props, settings); + obs_data_setint(settings, LAST_RES_TYPE, val); + } return true; } +static bool DeviceIntervalChanged(obs_properties_t props, obs_property_t p, + obs_data_t settings) +{ + int val = (int)obs_data_getint(settings, FRAME_INTERVAL); + int lastVal = (int)obs_data_getint(settings, LAST_INTERVAL); + + PropertiesData *data = (PropertiesData*)obs_properties_get_param(props); + const char *id = obs_data_getstring(settings, VIDEO_DEVICE_ID); + VideoDevice device; + + if (!data->GetDevice(device, id)) + return false; + + int cx, cy; + const char *res = obs_data_getstring(settings, RESOLUTION); + if (!ConvertRes(cx, cy, res)) + return true; + + VideoFormat curFormat = + (VideoFormat)obs_data_getint(settings, VIDEO_FORMAT); + + bool foundFormat = + UpdateVideoFormats(props, device, val, cx, cy, curFormat); + + if (val != lastVal) { + if (!foundFormat) + obs_data_setint(settings, VIDEO_FORMAT, + (int)VideoFormat::Any); + + obs_data_setint(settings, LAST_INTERVAL, val); + } + + UNUSED_PARAMETER(p); + return true; +} + static obs_properties_t GetDShowProperties(const char *locale) { obs_properties_t ppts = obs_properties_create(locale); @@ -710,9 +816,11 @@ static obs_properties_t GetDShowProperties(const char *locale) obs_property_set_modified_callback(p, DeviceResolutionChanged); - obs_properties_add_list(ppts, FRAME_INTERVAL, "FPS", + p = obs_properties_add_list(ppts, FRAME_INTERVAL, "FPS", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_modified_callback(p, DeviceIntervalChanged); + obs_properties_add_list(ppts, VIDEO_FORMAT, "Video Format", OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);