From 841f02be87dc1c6a150b1e2f562b28e2b0927555 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Mon, 15 Sep 2014 20:35:07 +0200 Subject: [PATCH 01/10] Fix device capability checking in v4l2 input. The capabilities flags that were used previously describe all capabilities the physical device offers. This would cause devices that are accessible through multiple device nodes to show up with all device nodes while only one of the nodes might actually offer the needed video capture capability. If the device has more nodes the CAP_DEVICES_CAP flag might be set in which case the device_caps field is filled with the capabilities that only apply to that specific node that is opened. --- plugins/linux-v4l2/v4l2-input.c | 40 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 09f157d1f..493b361e6 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -233,8 +233,6 @@ static void v4l2_device_list(obs_property_t prop, obs_data_t settings) { DIR *dirp; struct dirent *dp; - int fd; - struct v4l2_capability video_cap; struct dstr device; bool first = true; @@ -247,6 +245,10 @@ static void v4l2_device_list(obs_property_t prop, obs_data_t settings) dstr_init_copy(&device, "/dev/"); while ((dp = readdir(dirp)) != NULL) { + int fd; + uint32_t caps; + struct v4l2_capability video_cap; + if (dp->d_type == DT_DIR) continue; @@ -261,23 +263,33 @@ static void v4l2_device_list(obs_property_t prop, obs_data_t settings) if (v4l2_ioctl(fd, VIDIOC_QUERYCAP, &video_cap) == -1) { blog(LOG_INFO, "Failed to query capabilities for %s", device.array); - } else if (video_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { - obs_property_list_add_string(prop, - (char *) video_cap.card, - device.array); - if (first) { - obs_data_set_string(settings, - "device_id", device.array); - first = false; - } - blog(LOG_INFO, "Found device '%s' at %s", - video_cap.card, device.array); + close(fd); + continue; } - else { + + caps = (video_cap.capabilities & V4L2_CAP_DEVICE_CAPS) + ? video_cap.device_caps + : video_cap.capabilities; + + if (!(caps & V4L2_CAP_VIDEO_CAPTURE)) { blog(LOG_INFO, "%s seems to not support video capture", device.array); + close(fd); + continue; } + obs_property_list_add_string(prop, (char *) video_cap.card, + device.array); + + if (first) { + obs_data_set_string(settings, "device_id", + device.array); + first = false; + } + + blog(LOG_INFO, "Found device '%s' at %s", video_cap.card, + device.array); + close(fd); } From 0abf0f5740f85b8fa1bb217f9cdc00b9486a6ceb Mon Sep 17 00:00:00 2001 From: fryshorts Date: Mon, 15 Sep 2014 21:06:55 +0200 Subject: [PATCH 02/10] Do not explicitly set a default device in v4l2 input. This was added at a time where the source properties dialog did not pop up automatically on source creation. Now when the properties are displayed the first device in the select input will be selected by default if there was none already specified by the source settings. This will make the code cleaner and also save one redundant round of device enumeration. --- plugins/linux-v4l2/v4l2-input.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 493b361e6..485705542 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -231,10 +231,11 @@ static void v4l2_defaults(obs_data_t settings) */ static void v4l2_device_list(obs_property_t prop, obs_data_t settings) { + UNUSED_PARAMETER(settings); + DIR *dirp; struct dirent *dp; struct dstr device; - bool first = true; dirp = opendir("/sys/class/video4linux"); if (!dirp) @@ -280,13 +281,6 @@ static void v4l2_device_list(obs_property_t prop, obs_data_t settings) obs_property_list_add_string(prop, (char *) video_cap.card, device.array); - - if (first) { - obs_data_set_string(settings, "device_id", - device.array); - first = false; - } - blog(LOG_INFO, "Found device '%s' at %s", video_cap.card, device.array); @@ -679,11 +673,6 @@ static void v4l2_update(void *vptr, obs_data_t settings) const char *new_device; new_device = obs_data_get_string(settings, "device_id"); - if (strlen(new_device) == 0) { - v4l2_device_list(NULL, settings); - new_device = obs_data_get_string(settings, "device_id"); - } - if (!data->set_device || strcmp(data->set_device, new_device) != 0) { if (data->set_device) bfree(data->set_device); From 8d95a7fb476c5bf5d3874d0dbb6a1c9d61afb082 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Mon, 15 Sep 2014 23:59:41 +0200 Subject: [PATCH 03/10] Use helper function to set input in v4l2 input. --- plugins/linux-v4l2/v4l2-helpers.c | 11 +++++++++++ plugins/linux-v4l2/v4l2-helpers.h | 12 ++++++++++++ plugins/linux-v4l2/v4l2-input.c | 11 +++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-helpers.c b/plugins/linux-v4l2/v4l2-helpers.c index 6497ff1cc..a1cb9934d 100644 --- a/plugins/linux-v4l2/v4l2-helpers.c +++ b/plugins/linux-v4l2/v4l2-helpers.c @@ -123,3 +123,14 @@ int_fast32_t v4l2_destroy_mmap(struct v4l2_buffer_data *buf) return 0; } +int_fast32_t v4l2_set_input(int_fast32_t dev, int *input) +{ + if (!dev || !input) + return -1; + + return (*input == -1) + ? v4l2_ioctl(dev, VIDIOC_G_INPUT, input) + : v4l2_ioctl(dev, VIDIOC_S_INPUT, input); +} + + diff --git a/plugins/linux-v4l2/v4l2-helpers.h b/plugins/linux-v4l2/v4l2-helpers.h index b53c9b746..7d2fce511 100644 --- a/plugins/linux-v4l2/v4l2-helpers.h +++ b/plugins/linux-v4l2/v4l2-helpers.h @@ -175,6 +175,18 @@ int_fast32_t v4l2_create_mmap(int_fast32_t dev, struct v4l2_buffer_data *buf); */ int_fast32_t v4l2_destroy_mmap(struct v4l2_buffer_data *buf); +/** + * Set the video input on the device. + * + * If the action succeeds input is set to the currently selected input. + * + * @param dev handle for the v4l2 device + * @param input index of the input or -1 to leave it as is + * + * @return negative on failure + */ +int_fast32_t v4l2_set_input(int_fast32_t dev, int *input); + #ifdef __cplusplus } #endif diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 485705542..53210543d 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -61,7 +61,7 @@ struct v4l2_data { os_event_t event; char *set_device; - int_fast32_t set_input; + int set_input; int_fast32_t set_pixfmt; int_fast32_t set_res; int_fast32_t set_fps; @@ -305,7 +305,8 @@ static void v4l2_input_list(int_fast32_t dev, obs_property_t prop) if (in.type & V4L2_INPUT_TYPE_CAMERA) { obs_property_list_add_int(prop, (char *) in.name, in.index); - blog(LOG_INFO, "Found input '%s'", in.name); + blog(LOG_INFO, "Found input '%s' (Index %d)", in.name, + in.index); } in.index++; } @@ -609,10 +610,12 @@ static void v4l2_init(struct v4l2_data *data) } /* set input */ - if (v4l2_ioctl(data->dev, VIDIOC_S_INPUT, &data->set_input) < 0) { - blog(LOG_ERROR, "Unable to set input"); + if (v4l2_set_input(data->dev, &data->set_input) < 0) { + blog(LOG_ERROR, "Unable to set input %d", + data->set_input); goto fail; } + blog(LOG_INFO, "Input: %d", data->set_input); /* set pixel format and resolution */ unpack_tuple(&width, &height, data->set_res); From 6c8216c6a6597dc027497a2e7a98adaa2d5d9eb3 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 16 Sep 2014 00:05:11 +0200 Subject: [PATCH 04/10] Use helper function to set format in v4l2 input. Since the helper function also needs to pack/unpack the resolution, the pack/unpack functions were moved to the helper library and prefixed with v4l2_ in order to avoid possible collisions. --- plugins/linux-v4l2/v4l2-helpers.c | 35 ++++++++++++++++++ plugins/linux-v4l2/v4l2-helpers.h | 48 +++++++++++++++++++++++++ plugins/linux-v4l2/v4l2-input.c | 59 ++++++++++--------------------- 3 files changed, 101 insertions(+), 41 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-helpers.c b/plugins/linux-v4l2/v4l2-helpers.c index a1cb9934d..88551b89c 100644 --- a/plugins/linux-v4l2/v4l2-helpers.c +++ b/plugins/linux-v4l2/v4l2-helpers.c @@ -133,4 +133,39 @@ int_fast32_t v4l2_set_input(int_fast32_t dev, int *input) : v4l2_ioctl(dev, VIDIOC_S_INPUT, input); } +int_fast32_t v4l2_set_format(int_fast32_t dev, int *resolution, + int *pixelformat, int *bytesperline) +{ + bool set = false; + int width, height; + struct v4l2_format fmt; + if (!dev || !resolution || !pixelformat || !bytesperline) + return -1; + + /* We need to set the type in order to query the settings */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (v4l2_ioctl(dev, VIDIOC_G_FMT, &fmt) < 0) + return -1; + + if (*resolution != -1) { + v4l2_unpack_tuple(&width, &height, *resolution); + fmt.fmt.pix.width = width; + fmt.fmt.pix.height = height; + set = true; + } + + if (*pixelformat != -1) { + fmt.fmt.pix.pixelformat = *pixelformat; + set = true; + } + + if (set && (v4l2_ioctl(dev, VIDIOC_S_FMT, &fmt) < 0)) + return -1; + + *resolution = v4l2_pack_tuple(fmt.fmt.pix.width, fmt.fmt.pix.height); + *pixelformat = fmt.fmt.pix.pixelformat; + *bytesperline = fmt.fmt.pix.bytesperline; + return 0; +} diff --git a/plugins/linux-v4l2/v4l2-helpers.h b/plugins/linux-v4l2/v4l2-helpers.h index 7d2fce511..f4a835f86 100644 --- a/plugins/linux-v4l2/v4l2-helpers.h +++ b/plugins/linux-v4l2/v4l2-helpers.h @@ -131,6 +131,38 @@ static const int v4l2_framerates[] = 0 }; +/** + * Pack two integer values into one + * + * Obviously the input integers have to be truncated in order to fit into + * one. The effective 16bits left are still enough to handle resolutions and + * framerates just fine. + * + * @param a integer one + * @param b integer two + * + * @return the packed integer + */ +static inline int v4l2_pack_tuple(int a, int b) +{ + return (a << 16) | (b & 0xffff); +} + +/** + * Unpack two integer values from one + * + * @see v4l2_pack_tuple + * + * @param a pointer to integer a + * @param b pointer to integer b + * @param packed the packed integer + */ +static void v4l2_unpack_tuple(int *a, int *b, int packed) +{ + *a = packed >> 16; + *b = packed & 0xffff; +} + /** * Start the video capture on the device. * @@ -187,6 +219,22 @@ int_fast32_t v4l2_destroy_mmap(struct v4l2_buffer_data *buf); */ int_fast32_t v4l2_set_input(int_fast32_t dev, int *input); +/** + * Set the video format on the device. + * + * If the action succeeds resolution, pixelformat and bytesperline are set + * to the used values. + * + * @param dev handle for the v4l2 device + * @param resolution packed value of the resolution or -1 to leave as is + * @param pixelformat index of the pixelformat or -1 to leave as is + * @param bytesperline this will be set accordingly on success + * + * @return negative on failure + */ +int_fast32_t v4l2_set_format(int_fast32_t dev, int *resolution, + int *pixelformat, int *bytesperline); + #ifdef __cplusplus } #endif diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 53210543d..15aa69237 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -62,36 +62,22 @@ struct v4l2_data { char *set_device; int set_input; - int_fast32_t set_pixfmt; - int_fast32_t set_res; + int set_pixfmt; + int set_res; int_fast32_t set_fps; /* data used within the capture thread */ int_fast32_t dev; uint64_t frames; - int_fast32_t width; - int_fast32_t height; + int width; + int height; int_fast32_t pixfmt; - uint_fast32_t linesize; + int linesize; struct v4l2_buffer_data buffers; }; -/* - * used to store framerate and resolution values - */ -static int pack_tuple(int a, int b) -{ - return (a << 16) | (b & 0xffff); -} - -static void unpack_tuple(int *a, int *b, int packed) -{ - *a = packed >> 16; - *b = packed & 0xffff; -} - /** * Prepare the output frame structure for obs and compute plane offsets * @@ -368,7 +354,7 @@ static void v4l2_resolution_list(int dev, uint_fast32_t pixelformat, dstr_printf(&buffer, "%dx%d", frmsize.discrete.width, frmsize.discrete.height); obs_property_list_add_int(prop, buffer.array, - pack_tuple(frmsize.discrete.width, + v4l2_pack_tuple(frmsize.discrete.width, frmsize.discrete.height)); frmsize.index++; } @@ -380,7 +366,7 @@ static void v4l2_resolution_list(int dev, uint_fast32_t pixelformat, for (const int *packed = v4l2_framesizes; *packed; ++packed) { int width; int height; - unpack_tuple(&width, &height, *packed); + v4l2_unpack_tuple(&width, &height, *packed); dstr_printf(&buffer, "%dx%d", width, height); obs_property_list_add_int(prop, buffer.array, *packed); } @@ -414,7 +400,7 @@ static void v4l2_framerate_list(int dev, uint_fast32_t pixelformat, &frmival) == 0) { float fps = (float) frmival.discrete.denominator / frmival.discrete.numerator; - int pack = pack_tuple(frmival.discrete.numerator, + int pack = v4l2_pack_tuple(frmival.discrete.numerator, frmival.discrete.denominator); dstr_printf(&buffer, "%.2f", fps); obs_property_list_add_int(prop, buffer.array, pack); @@ -428,7 +414,7 @@ static void v4l2_framerate_list(int dev, uint_fast32_t pixelformat, for (const int *packed = v4l2_framerates; *packed; ++packed) { int num; int denom; - unpack_tuple(&num, &denom, *packed); + v4l2_unpack_tuple(&num, &denom, *packed); float fps = (float) denom / num; dstr_printf(&buffer, "%.2f", fps); obs_property_list_add_int(prop, buffer.array, *packed); @@ -511,7 +497,7 @@ static bool resolution_selected(obs_properties_t props, obs_property_t p, return false; obs_property_t prop = obs_properties_get(props, "framerate"); - unpack_tuple(&width, &height, obs_data_get_int(settings, + v4l2_unpack_tuple(&width, &height, obs_data_get_int(settings, "resolution")); v4l2_framerate_list(dev, obs_data_get_int(settings, "pixelformat"), width, height, prop); @@ -596,10 +582,8 @@ static void v4l2_destroy(void *vptr) */ static void v4l2_init(struct v4l2_data *data) { - struct v4l2_format fmt; struct v4l2_streamparm par; struct dstr fps; - int width, height; int fps_num, fps_denom; blog(LOG_INFO, "Start capture from %s", data->set_device); @@ -618,26 +602,19 @@ static void v4l2_init(struct v4l2_data *data) blog(LOG_INFO, "Input: %d", data->set_input); /* set pixel format and resolution */ - unpack_tuple(&width, &height, data->set_res); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = width; - fmt.fmt.pix.height = height; - fmt.fmt.pix.pixelformat = data->set_pixfmt; - fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - if (v4l2_ioctl(data->dev, VIDIOC_S_FMT, &fmt) < 0) { + if (v4l2_set_format(data->dev, &data->set_res, &data->set_pixfmt, + &data->linesize) < 0) { blog(LOG_ERROR, "Unable to set format"); goto fail; } - data->width = fmt.fmt.pix.width; - data->height = fmt.fmt.pix.height; - data->pixfmt = fmt.fmt.pix.pixelformat; - data->linesize = fmt.fmt.pix.bytesperline; - blog(LOG_INFO, "Resolution: %"PRIuFAST32"x%"PRIuFAST32, - data->width, data->height); - blog(LOG_INFO, "Linesize: %"PRIuFAST32" Bytes", data->linesize); + v4l2_unpack_tuple(&data->width, &data->height, data->set_res); + data->pixfmt = data->set_pixfmt; + blog(LOG_INFO, "Resolution: %dx%d", data->width, data->height); + blog(LOG_INFO, "Pixelformat: %d", data->set_pixfmt); + blog(LOG_INFO, "Linesize: %d Bytes", data->linesize); /* set framerate */ - unpack_tuple(&fps_num, &fps_denom, data->set_fps); + v4l2_unpack_tuple(&fps_num, &fps_denom, data->set_fps); par.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; par.parm.capture.timeperframe.numerator = fps_num; par.parm.capture.timeperframe.denominator = fps_denom; From dcd395f77ec30dae0658d1055944b64c2556a961 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 16 Sep 2014 00:06:42 +0200 Subject: [PATCH 05/10] Use helper function to set framerate in v4l2 input. --- plugins/linux-v4l2/v4l2-helpers.c | 30 ++++++++++++++++++++++++++++++ plugins/linux-v4l2/v4l2-helpers.h | 12 ++++++++++++ plugins/linux-v4l2/v4l2-input.c | 14 ++++---------- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-helpers.c b/plugins/linux-v4l2/v4l2-helpers.c index 88551b89c..657b221b9 100644 --- a/plugins/linux-v4l2/v4l2-helpers.c +++ b/plugins/linux-v4l2/v4l2-helpers.c @@ -169,3 +169,33 @@ int_fast32_t v4l2_set_format(int_fast32_t dev, int *resolution, *bytesperline = fmt.fmt.pix.bytesperline; return 0; } + +int_fast32_t v4l2_set_framerate(int_fast32_t dev, int *framerate) +{ + bool set = false; + int num, denom; + struct v4l2_streamparm par; + + if (!dev || !framerate) + return -1; + + /* We need to set the type in order to query the stream settings */ + par.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (v4l2_ioctl(dev, VIDIOC_G_PARM, &par) < 0) + return -1; + + if (*framerate != -1) { + v4l2_unpack_tuple(&num, &denom, *framerate); + par.parm.capture.timeperframe.numerator = num; + par.parm.capture.timeperframe.denominator = denom; + set = true; + } + + if (set && (v4l2_ioctl(dev, VIDIOC_S_PARM, &par) < 0)) + return -1; + + *framerate = v4l2_pack_tuple(par.parm.capture.timeperframe.numerator, + par.parm.capture.timeperframe.denominator); + return 0; +} diff --git a/plugins/linux-v4l2/v4l2-helpers.h b/plugins/linux-v4l2/v4l2-helpers.h index f4a835f86..6b5d862e7 100644 --- a/plugins/linux-v4l2/v4l2-helpers.h +++ b/plugins/linux-v4l2/v4l2-helpers.h @@ -235,6 +235,18 @@ int_fast32_t v4l2_set_input(int_fast32_t dev, int *input); int_fast32_t v4l2_set_format(int_fast32_t dev, int *resolution, int *pixelformat, int *bytesperline); +/** + * Set the framerate on the device. + * + * If the action succeeds framerate is set to the used value. + * + * @param dev handle to the v4l2 device + * @param framerate packed value of the framerate or -1 to leave as is + * + * @return negative on failure + */ +int_fast32_t v4l2_set_framerate(int_fast32_t dev, int *framerate); + #ifdef __cplusplus } #endif diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 15aa69237..d82b5ed68 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -64,7 +64,7 @@ struct v4l2_data { int set_input; int set_pixfmt; int set_res; - int_fast32_t set_fps; + int set_fps; /* data used within the capture thread */ int_fast32_t dev; @@ -582,7 +582,6 @@ static void v4l2_destroy(void *vptr) */ static void v4l2_init(struct v4l2_data *data) { - struct v4l2_streamparm par; struct dstr fps; int fps_num, fps_denom; @@ -614,18 +613,13 @@ static void v4l2_init(struct v4l2_data *data) blog(LOG_INFO, "Linesize: %d Bytes", data->linesize); /* set framerate */ - v4l2_unpack_tuple(&fps_num, &fps_denom, data->set_fps); - par.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - par.parm.capture.timeperframe.numerator = fps_num; - par.parm.capture.timeperframe.denominator = fps_denom; - if (v4l2_ioctl(data->dev, VIDIOC_S_PARM, &par) < 0) { + if (v4l2_set_framerate(data->dev, &data->set_fps) < 0) { blog(LOG_ERROR, "Unable to set framerate"); goto fail; } + v4l2_unpack_tuple(&fps_num, &fps_denom, data->set_fps); dstr_init(&fps); - dstr_printf(&fps, "%.2f", - (float) par.parm.capture.timeperframe.denominator - / par.parm.capture.timeperframe.numerator); + dstr_printf(&fps, "%.2f", (float) fps_denom / fps_num); blog(LOG_INFO, "Framerate: %s fps", fps.array); dstr_free(&fps); From c3783d055f62179304e0ffbd8a44e0612c95901d Mon Sep 17 00:00:00 2001 From: fryshorts Date: Mon, 15 Sep 2014 23:59:05 +0200 Subject: [PATCH 06/10] Add option to leave settings unchanged in v4l2 input. This adds an additional option for all settings to leave the current configuration of the device unchanged. --- plugins/linux-v4l2/data/locale/en-US.ini | 1 + plugins/linux-v4l2/v4l2-input.c | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/linux-v4l2/data/locale/en-US.ini b/plugins/linux-v4l2/data/locale/en-US.ini index d81320bd8..e95a73f59 100644 --- a/plugins/linux-v4l2/data/locale/en-US.ini +++ b/plugins/linux-v4l2/data/locale/en-US.ini @@ -4,3 +4,4 @@ Input="Input" ImageFormat="Video Format" Resolution="Resolution" FrameRate="Frame Rate" +LeaveUnchanged="Leave Unchanged" diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index d82b5ed68..42d1a8714 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -205,11 +205,10 @@ static const char* v4l2_getname(void) static void v4l2_defaults(obs_data_t settings) { - obs_data_set_default_int(settings, "input", 0); - obs_data_set_default_int(settings, "pixelformat", V4L2_PIX_FMT_YUYV); - obs_data_set_default_int(settings, "resolution", - pack_tuple(640, 480)); - obs_data_set_default_int(settings, "framerate", pack_tuple(1, 30)); + obs_data_set_default_int(settings, "input", -1); + obs_data_set_default_int(settings, "pixelformat", -1); + obs_data_set_default_int(settings, "resolution", -1); + obs_data_set_default_int(settings, "framerate", -1); } /* @@ -287,6 +286,8 @@ static void v4l2_input_list(int_fast32_t dev, obs_property_t prop) obs_property_list_clear(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, @@ -311,6 +312,8 @@ static void v4l2_format_list(int dev, obs_property_t prop) obs_property_list_clear(prop); + obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); + while (v4l2_ioctl(dev, VIDIOC_ENUM_FMT, &fmt) == 0) { dstr_copy(&buffer, (char *) fmt.description); if (fmt.flags & V4L2_FMT_FLAG_EMULATED) @@ -346,6 +349,8 @@ static void v4l2_resolution_list(int dev, uint_fast32_t pixelformat, obs_property_list_clear(prop); + obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); + v4l2_ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &frmsize); switch(frmsize.type) { @@ -392,6 +397,8 @@ static void v4l2_framerate_list(int dev, uint_fast32_t pixelformat, obs_property_list_clear(prop); + obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); + v4l2_ioctl(dev, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); switch(frmival.type) { From 632642010d82d1d2b03c6d3d1425692bcff0e595 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 16 Sep 2014 20:35:09 +0200 Subject: [PATCH 07/10] Minor refactoring in v4l2 input. We don't really need to use a dstr here, simply using the right format specifier for blog will suffice. --- plugins/linux-v4l2/v4l2-input.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 42d1a8714..05b08918e 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -589,7 +589,6 @@ static void v4l2_destroy(void *vptr) */ static void v4l2_init(struct v4l2_data *data) { - struct dstr fps; int fps_num, fps_denom; blog(LOG_INFO, "Start capture from %s", data->set_device); @@ -625,10 +624,7 @@ static void v4l2_init(struct v4l2_data *data) goto fail; } v4l2_unpack_tuple(&fps_num, &fps_denom, data->set_fps); - dstr_init(&fps); - dstr_printf(&fps, "%.2f", (float) fps_denom / fps_num); - blog(LOG_INFO, "Framerate: %s fps", fps.array); - dstr_free(&fps); + blog(LOG_INFO, "Framerate: %.2f fps", (float) fps_denom / fps_num); /* map buffers */ if (v4l2_create_mmap(data->dev, &data->buffers) < 0) { From 875398e9ddc87ee38867b2dbc50fe18f4e661795 Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 16 Sep 2014 20:46:04 +0200 Subject: [PATCH 08/10] Refactor update function for v4l2 input. The old implementation of this function assumed that there would be some settings that could be changed on the fly without restarting the capture. That was actually never used for any setting. --- plugins/linux-v4l2/v4l2-input.c | 48 ++++++++++++--------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 05b08918e..991bd9057 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -643,44 +643,30 @@ fail: v4l2_terminate(data); } +/** + * Update the settings for the v4l2 source + * + * Since there are very few settings that can be changed without restarting the + * stream we don't bother to even try. Whenever this is called the currently + * active stream (if exists) is stopped, the settings are updated and finally + * the new stream is started. + */ static void v4l2_update(void *vptr, obs_data_t settings) { V4L2_DATA(vptr); - bool restart = false; - const char *new_device; - new_device = obs_data_get_string(settings, "device_id"); - if (!data->set_device || strcmp(data->set_device, new_device) != 0) { - if (data->set_device) - bfree(data->set_device); - data->set_device = bstrdup(new_device); - restart = true; - } + v4l2_terminate(data); - if (data->set_input != obs_data_get_int(settings, "input")) { - data->set_input = obs_data_get_int(settings, "input"); - restart = true; - } + if (data->set_device) + bfree(data->set_device); - if (data->set_pixfmt != obs_data_get_int(settings, "pixelformat")) { - data->set_pixfmt = obs_data_get_int(settings, "pixelformat"); - restart = true; - } + data->set_device = bstrdup(obs_data_get_string(settings, "device_id")); + data->set_input = obs_data_get_int(settings, "input"); + data->set_pixfmt = obs_data_get_int(settings, "pixelformat"); + data->set_res = obs_data_get_int(settings, "resolution"); + data->set_fps = obs_data_get_int(settings, "framerate"); - if (data->set_res != obs_data_get_int(settings, "resolution")) { - data->set_res = obs_data_get_int(settings, "resolution"); - restart = true; - } - - if (data->set_fps != obs_data_get_int(settings, "framerate")) { - data->set_fps = obs_data_get_int(settings, "framerate"); - restart = true; - } - - if (restart) { - v4l2_terminate(data); - v4l2_init(data); - } + v4l2_init(data); } static void *v4l2_create(obs_data_t settings, obs_source_t source) From 91fc59facaf0196ff1a59f5c6c9ed792da083b2a Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 16 Sep 2014 21:23:26 +0200 Subject: [PATCH 09/10] Refactor source structure in v4l2 input. Due to the refactoring of the update function the separation of data members only to be accessed from inside/outside the capture thread is no longer needed. --- plugins/linux-v4l2/v4l2-input.c | 71 ++++++++++++++------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index 991bd9057..a80f8d974 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -45,37 +45,28 @@ along with this program. If not, see . /** * Data structure for the v4l2 source - * - * The data is divided into two sections, data being used inside and outside - * the capture thread. Data used by the capture thread must not be modified - * from the outside while the thread is running. - * - * Data members prefixed with "set_" are settings from the source properties - * and may be used from outside the capture thread. */ struct v4l2_data { - /* data used outside of the capture thread */ - obs_source_t source; + /* settings */ + char *device_id; + int input; + int pixfmt; + int resolution; + int framerate; + /* internal data */ + obs_source_t source; pthread_t thread; os_event_t event; - char *set_device; - int set_input; - int set_pixfmt; - int set_res; - int set_fps; - - /* data used within the capture thread */ int_fast32_t dev; - - uint64_t frames; int width; int height; - int_fast32_t pixfmt; int linesize; - struct v4l2_buffer_data buffers; + + /* statistics */ + uint64_t frames; }; /** @@ -572,8 +563,8 @@ static void v4l2_destroy(void *vptr) v4l2_terminate(data); - if (data->set_device) - bfree(data->set_device); + if (data->device_id) + bfree(data->device_id); bfree(data); } @@ -591,39 +582,37 @@ static void v4l2_init(struct v4l2_data *data) { int fps_num, fps_denom; - blog(LOG_INFO, "Start capture from %s", data->set_device); - data->dev = v4l2_open(data->set_device, O_RDWR | O_NONBLOCK); + blog(LOG_INFO, "Start capture from %s", data->device_id); + data->dev = v4l2_open(data->device_id, O_RDWR | O_NONBLOCK); if (data->dev == -1) { blog(LOG_ERROR, "Unable to open device"); goto fail; } /* set input */ - if (v4l2_set_input(data->dev, &data->set_input) < 0) { - blog(LOG_ERROR, "Unable to set input %d", - data->set_input); + if (v4l2_set_input(data->dev, &data->input) < 0) { + blog(LOG_ERROR, "Unable to set input %d", data->input); goto fail; } - blog(LOG_INFO, "Input: %d", data->set_input); + blog(LOG_INFO, "Input: %d", data->input); /* set pixel format and resolution */ - if (v4l2_set_format(data->dev, &data->set_res, &data->set_pixfmt, + if (v4l2_set_format(data->dev, &data->resolution, &data->pixfmt, &data->linesize) < 0) { blog(LOG_ERROR, "Unable to set format"); goto fail; } - v4l2_unpack_tuple(&data->width, &data->height, data->set_res); - data->pixfmt = data->set_pixfmt; + v4l2_unpack_tuple(&data->width, &data->height, data->resolution); blog(LOG_INFO, "Resolution: %dx%d", data->width, data->height); - blog(LOG_INFO, "Pixelformat: %d", data->set_pixfmt); + blog(LOG_INFO, "Pixelformat: %d", data->pixfmt); blog(LOG_INFO, "Linesize: %d Bytes", data->linesize); /* set framerate */ - if (v4l2_set_framerate(data->dev, &data->set_fps) < 0) { + if (v4l2_set_framerate(data->dev, &data->framerate) < 0) { blog(LOG_ERROR, "Unable to set framerate"); goto fail; } - v4l2_unpack_tuple(&fps_num, &fps_denom, data->set_fps); + v4l2_unpack_tuple(&fps_num, &fps_denom, data->framerate); blog(LOG_INFO, "Framerate: %.2f fps", (float) fps_denom / fps_num); /* map buffers */ @@ -657,14 +646,14 @@ static void v4l2_update(void *vptr, obs_data_t settings) v4l2_terminate(data); - if (data->set_device) - bfree(data->set_device); + if (data->device_id) + bfree(data->device_id); - data->set_device = bstrdup(obs_data_get_string(settings, "device_id")); - data->set_input = obs_data_get_int(settings, "input"); - data->set_pixfmt = obs_data_get_int(settings, "pixelformat"); - data->set_res = obs_data_get_int(settings, "resolution"); - data->set_fps = obs_data_get_int(settings, "framerate"); + 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->resolution = obs_data_get_int(settings, "resolution"); + data->framerate = obs_data_get_int(settings, "framerate"); v4l2_init(data); } From 0132453d235a7eb51ab7f183d8c789f31fa0674b Mon Sep 17 00:00:00 2001 From: fryshorts Date: Tue, 16 Sep 2014 21:45:23 +0200 Subject: [PATCH 10/10] Add video format check in v4l2 input. This adds a check whether the video format from the device is compatible with obs. This could either happen if the "Leave unchanged" option is selected for the video format, or if the driver simply overwrites the requested video format. --- plugins/linux-v4l2/v4l2-input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index a80f8d974..d46b62b3f 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -602,6 +602,10 @@ static void v4l2_init(struct v4l2_data *data) blog(LOG_ERROR, "Unable to set format"); goto fail; } + if (v4l2_to_obs_video_format(data->pixfmt) == VIDEO_FORMAT_NONE) { + blog(LOG_ERROR, "Selected video format not supported"); + goto fail; + } v4l2_unpack_tuple(&data->width, &data->height, data->resolution); blog(LOG_INFO, "Resolution: %dx%d", data->width, data->height); blog(LOG_INFO, "Pixelformat: %d", data->pixfmt);