diff --git a/plugins/linux-v4l2/data/locale/en-US.ini b/plugins/linux-v4l2/data/locale/en-US.ini index d798c18ad..a1baf84ba 100644 --- a/plugins/linux-v4l2/data/locale/en-US.ini +++ b/plugins/linux-v4l2/data/locale/en-US.ini @@ -2,6 +2,7 @@ V4L2Input="Video Capture Device (V4L2)" Device="Device" Input="Input" ImageFormat="Video Format" +VideoStandard="Video Standard" Resolution="Resolution" FrameRate="Frame Rate" LeaveUnchanged="Leave Unchanged" diff --git a/plugins/linux-v4l2/v4l2-helpers.c b/plugins/linux-v4l2/v4l2-helpers.c index 657b221b9..2c824e55c 100644 --- a/plugins/linux-v4l2/v4l2-helpers.c +++ b/plugins/linux-v4l2/v4l2-helpers.c @@ -133,6 +133,27 @@ int_fast32_t v4l2_set_input(int_fast32_t dev, int *input) : v4l2_ioctl(dev, VIDIOC_S_INPUT, input); } +int_fast32_t v4l2_get_input_caps(int_fast32_t dev, int input, uint32_t *caps) +{ + if (!dev || !caps) + return -1; + + if (input == -1) { + if (v4l2_ioctl(dev, VIDIOC_G_INPUT, &input) < 0) + return -1; + } + + struct v4l2_input in; + memset(&in, 0, sizeof(in)); + in.index = input; + + if (v4l2_ioctl(dev, VIDIOC_ENUMINPUT, &in) < 0) + return -1; + + *caps = in.capabilities; + return 0; +} + int_fast32_t v4l2_set_format(int_fast32_t dev, int *resolution, int *pixelformat, int *bytesperline) { @@ -199,3 +220,19 @@ int_fast32_t v4l2_set_framerate(int_fast32_t dev, int *framerate) par.parm.capture.timeperframe.denominator); return 0; } + +int_fast32_t v4l2_set_standard(int_fast32_t dev, int *standard) +{ + if (!dev || !standard) + return -1; + + if (*standard == -1) { + if (v4l2_ioctl(dev, VIDIOC_G_STD, standard) < 0) + return -1; + } else { + if (v4l2_ioctl(dev, VIDIOC_S_STD, standard) < 0) + return -1; + } + + return 0; +} diff --git a/plugins/linux-v4l2/v4l2-helpers.h b/plugins/linux-v4l2/v4l2-helpers.h index 6b5d862e7..5ee28fd4a 100644 --- a/plugins/linux-v4l2/v4l2-helpers.h +++ b/plugins/linux-v4l2/v4l2-helpers.h @@ -219,6 +219,17 @@ int_fast32_t v4l2_destroy_mmap(struct v4l2_buffer_data *buf); */ int_fast32_t v4l2_set_input(int_fast32_t dev, int *input); +/** + * Get capabilities for an input. + * + * @param dev handle for the v4l2 device + * @param input index of the input or -1 to use the currently selected + * @param caps capabilities for the input + * + * @return negative on failure + */ +int_fast32_t v4l2_get_input_caps(int_fast32_t dev, int input, uint32_t *caps); + /** * Set the video format on the device. * @@ -247,6 +258,18 @@ int_fast32_t v4l2_set_format(int_fast32_t dev, int *resolution, */ int_fast32_t v4l2_set_framerate(int_fast32_t dev, int *framerate); +/** + * Set a video standard on the device. + * + * If the action succeeds standard is set to the used video standard id. + * + * @param dev handle to the v4l2 device + * @param standard id of the standard to use or -1 to leave as is + * + * @return negative on failure + */ +int_fast32_t v4l2_set_standard(int_fast32_t dev, int *standard); + #ifdef __cplusplus } #endif diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 1276b4d12..cf0bfebd3 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -56,6 +56,7 @@ struct v4l2_data { char *device_id; int input; int pixfmt; + int standard; int resolution; int framerate; bool sys_timing; @@ -214,6 +215,7 @@ static void v4l2_defaults(obs_data_t *settings) { obs_data_set_default_int(settings, "input", -1); obs_data_set_default_int(settings, "pixelformat", -1); + obs_data_set_default_int(settings, "standard", -1); obs_data_set_default_int(settings, "resolution", -1); obs_data_set_default_int(settings, "framerate", -1); obs_data_set_default_bool(settings, "system_timing", false); @@ -336,12 +338,9 @@ static void v4l2_input_list(int_fast32_t dev, obs_property_t *prop) obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); while (v4l2_ioctl(dev, VIDIOC_ENUMINPUT, &in) == 0) { - if (in.type & V4L2_INPUT_TYPE_CAMERA) { - obs_property_list_add_int(prop, (char *) in.name, - in.index); - blog(LOG_INFO, "Found input '%s' (Index %d)", in.name, - in.index); - } + obs_property_list_add_int(prop, (char *) in.name, in.index); + blog(LOG_INFO, "Found input '%s' (Index %d)", in.name, + in.index); in.index++; } } @@ -382,6 +381,24 @@ static void v4l2_format_list(int dev, obs_property_t *prop) dstr_free(&buffer); } +/* + * List video standards for the device + */ +static void v4l2_standard_list(int dev, obs_property_t *prop) +{ + struct v4l2_standard std; + std.index = 0; + + obs_property_list_clear(prop); + + obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); + + while (v4l2_ioctl(dev, VIDIOC_ENUMSTD, &std) == 0) { + obs_property_list_add_int(prop, (char *) std.name, std.id); + std.index++; + } +} + /* * List resolutions for device and format */ @@ -535,12 +552,34 @@ static bool format_selected(obs_properties_t *props, obs_property_t *p, if (dev == -1) return false; - obs_property_t *prop = obs_properties_get(props, "resolution"); - v4l2_resolution_list(dev, obs_data_get_int(settings, "pixelformat"), - prop); + int input = (int) obs_data_get_int(settings, "input"); + uint32_t caps = 0; + if (v4l2_get_input_caps(dev, input, &caps) < 0) + return false; + caps &= V4L2_IN_CAP_STD; + + obs_property_t *resolution = obs_properties_get(props, "resolution"); + obs_property_t *framerate = obs_properties_get(props, "framerate"); + obs_property_t *standard = obs_properties_get(props, "standard"); + + obs_property_set_visible(resolution, (!caps) ? true : false); + obs_property_set_visible(framerate, (!caps) ? true : false); + obs_property_set_visible(standard, + (caps & V4L2_IN_CAP_STD) ? true : false); + + if (!caps) { + v4l2_resolution_list(dev, obs_data_get_int( + settings, "pixelformat"), resolution); + } + if (caps & V4L2_IN_CAP_STD) + v4l2_standard_list(dev, standard); + v4l2_close(dev); - obs_property_modified(prop, settings); + if (!caps) + obs_property_modified(resolution, settings); + if (caps & V4L2_IN_CAP_STD) + obs_property_modified(standard, settings); return true; } @@ -629,6 +668,11 @@ static obs_properties_t *v4l2_properties(void *vptr) "pixelformat", obs_module_text("VideoFormat"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_t *standard_list = obs_properties_add_list(props, + "standard", obs_module_text("VideoStandard"), + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_visible(standard_list, false); + obs_property_t *resolution_list = obs_properties_add_list(props, "resolution", obs_module_text("Resolution"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); @@ -701,6 +745,7 @@ static void v4l2_destroy(void *vptr) */ static void v4l2_init(struct v4l2_data *data) { + uint32_t input_caps; int fps_num, fps_denom; blog(LOG_INFO, "Start capture from %s", data->device_id); @@ -716,6 +761,20 @@ static void v4l2_init(struct v4l2_data *data) goto fail; } blog(LOG_INFO, "Input: %d", data->input); + if (v4l2_get_input_caps(data->dev, -1, &input_caps) < 0) { + blog(LOG_ERROR, "Unable to get input capabilities"); + goto fail; + } + + /* set video standard if supported */ + if (input_caps & V4L2_IN_CAP_STD) { + if (v4l2_set_standard(data->dev, &data->standard) < 0) { + blog(LOG_ERROR, "Unable to set video standard"); + goto fail; + } + data->resolution = -1; + data->framerate = -1; + } /* set pixel format and resolution */ if (v4l2_set_format(data->dev, &data->resolution, &data->pixfmt, @@ -777,6 +836,7 @@ static void v4l2_update(void *vptr, obs_data_t *settings) data->device_id = bstrdup(obs_data_get_string(settings, "device_id")); data->input = obs_data_get_int(settings, "input"); data->pixfmt = obs_data_get_int(settings, "pixelformat"); + data->standard = obs_data_get_int(settings, "standard"); data->resolution = obs_data_get_int(settings, "resolution"); data->framerate = obs_data_get_int(settings, "framerate"); data->sys_timing = obs_data_get_bool(settings, "system_timing");