diff --git a/plugins/linux-v4l2/v4l2-helpers.c b/plugins/linux-v4l2/v4l2-helpers.c index a1cb9934dcab7c4956f5b0733c9fc18aaac812ae..88551b89c339f195dc3e97a39dc885f1385df486 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 7d2fce511637a53a00101e87ac7d18ee2b132f62..f4a835f86158747f90ac4bec33c3b067ddb22519 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 53210543d2c52facb2b4920ccc9e27c653d74d2b..15aa6923781de73da5a75bfd292ee5b6c6db9a39 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;