win-dshow module: Fix configuration issues
The biggest problem with DirectShow is that available configuration capabilities can change if you so much as look at it the wrong way. Previously, configuring devices often didn't configure the device settings correctly, you would choose one setting and then another setting wouldn't be compatible with that settings. Let's take the terrible microsoft lifecam series for example. First, you'd be at 640x480 happily webcam'ing away, which is using the YUY2 format. Then you decide "hey, this webcam resolution is a bit low. I would love to have it a bit high resolution so it's a bit more crisp and clear." You'd select 1280x720, and then suddenly the only format supported is MJPEG output. However, the interface has to update that fact, it can't list YUY2 if MJPEG is the only one available for this resolution. This doesn't just apply to formats either, this applies to framerates and such as well. Some framerates will only be supported by certain resolutions which can in turn only be supported by certain formats. This causes user interface for configuration to be really be a nightmare to manage if you want these features to be available to the user. It's extremely annoying because you have to update all the configuration UI after something has changed, double check the configuration values, and if the values aren't supported, update those configuration values.master
parent
040615c6bc
commit
e72c0925be
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue