diff --git a/plugins/linux-v4l2/data/locale/en-US.ini b/plugins/linux-v4l2/data/locale/en-US.ini index df11f93dc..765989fcb 100644 --- a/plugins/linux-v4l2/data/locale/en-US.ini +++ b/plugins/linux-v4l2/data/locale/en-US.ini @@ -13,3 +13,5 @@ ColorRange.Default="Default" ColorRange.Partial="Partial" ColorRange.Full="Full" CameraCtrls="Camera Controls" +AutoresetOnTimeout="Autoreset on Timeout" +FramesUntilTimeout="Frames Until Timeout" diff --git a/plugins/linux-v4l2/v4l2-helpers.c b/plugins/linux-v4l2/v4l2-helpers.c index 0ae73397f..219ce284a 100644 --- a/plugins/linux-v4l2/v4l2-helpers.c +++ b/plugins/linux-v4l2/v4l2-helpers.c @@ -61,6 +61,17 @@ int_fast32_t v4l2_stop_capture(int_fast32_t dev) return 0; } +int_fast32_t v4l2_reset_capture(int_fast32_t dev, struct v4l2_buffer_data *buf) +{ + blog(LOG_DEBUG, "attempting to reset capture"); + if (v4l2_stop_capture(dev) < 0) + return -1; + if (v4l2_start_capture(dev, buf) < 0) + return -1; + + return 0; +} + #ifdef _DEBUG int_fast32_t v4l2_query_all_buffers(int_fast32_t dev, struct v4l2_buffer_data *buf_data) diff --git a/plugins/linux-v4l2/v4l2-helpers.h b/plugins/linux-v4l2/v4l2-helpers.h index 69b1c8d08..b14d0199f 100644 --- a/plugins/linux-v4l2/v4l2-helpers.h +++ b/plugins/linux-v4l2/v4l2-helpers.h @@ -184,6 +184,20 @@ int_fast32_t v4l2_start_capture(int_fast32_t dev, struct v4l2_buffer_data *buf); */ int_fast32_t v4l2_stop_capture(int_fast32_t dev); +/** + * Resets video capture on the device. + * + * This runs stop and start capture again. Stop dequeues the buffers and start + * enqueues the memory mapped buffers and instructs the device to start + * the video stream. + * + * @param dev handle for the v4l2 device + * @param buf buffer data + * + * @return negative on failure + */ +int_fast32_t v4l2_reset_capture(int_fast32_t dev, struct v4l2_buffer_data *buf); + #ifdef _DEBUG /** * Query the status of all buffers. diff --git a/plugins/linux-v4l2/v4l2-input.c b/plugins/linux-v4l2/v4l2-input.c index c1e31d9b5..8fdb465ed 100644 --- a/plugins/linux-v4l2/v4l2-input.c +++ b/plugins/linux-v4l2/v4l2-input.c @@ -88,6 +88,9 @@ struct v4l2_data { int height; int linesize; struct v4l2_buffer_data buffers; + + bool auto_reset; + int timeout_frames; }; /* forward declarations */ @@ -161,9 +164,21 @@ static void *v4l2_thread(void *vptr) struct v4l2_buffer buf; struct obs_source_frame out; size_t plane_offsets[MAX_AV_PLANES]; + int fps_num, fps_denom; + float ffps; + uint64_t timeout_usec; blog(LOG_DEBUG, "%s: new capture thread", data->device_id); + /* Get framerate and calculate appropriate select timeout value. */ + v4l2_unpack_tuple(&fps_num, &fps_denom, data->framerate); + ffps = (float)fps_denom / fps_num; + blog(LOG_DEBUG, "%s: framerate: %.2f fps", data->device_id, ffps); + /* Timeout set to 5 frame periods. */ + timeout_usec = (1000000 * data->timeout_frames) / ffps; + blog(LOG_INFO, "%s: select timeout set to %ldus (%dx frame periods)", + data->device_id, timeout_usec, data->timeout_frames); + if (v4l2_start_capture(data->dev, &data->buffers) < 0) goto exit; @@ -178,8 +193,10 @@ static void *v4l2_thread(void *vptr) while (os_event_try(data->event) == EAGAIN) { FD_ZERO(&fds); FD_SET(data->dev, &fds); - tv.tv_sec = 1; - tv.tv_usec = 0; + + /* Set timeout timevalue. */ + tv.tv_sec = 0; + tv.tv_usec = timeout_usec; r = select(data->dev + 1, &fds, NULL, NULL, &tv); if (r < 0) { @@ -199,6 +216,18 @@ static void *v4l2_thread(void *vptr) blog(LOG_ERROR, "%s: failed to log status", data->device_id); } + + if (data->auto_reset) { + if (v4l2_reset_capture(data->dev, + &data->buffers) == 0) + blog(LOG_INFO, + "%s: stream reset successful", + data->device_id); + else + blog(LOG_ERROR, "%s: failed to reset", + data->device_id); + } + continue; } @@ -264,6 +293,8 @@ static void v4l2_defaults(obs_data_t *settings) obs_data_set_default_int(settings, "framerate", -1); obs_data_set_default_int(settings, "color_range", VIDEO_RANGE_DEFAULT); obs_data_set_default_bool(settings, "buffering", true); + obs_data_set_default_bool(settings, "auto_reset", false); + obs_data_set_default_int(settings, "timeout_frames", 5); } /** @@ -823,6 +854,13 @@ static obs_properties_t *v4l2_properties(void *vptr) obs_properties_add_bool(props, "buffering", obs_module_text("UseBuffering")); + obs_properties_add_bool(props, "auto_reset", + obs_module_text("AutoresetOnTimeout")); + + obs_properties_add_int(props, "timeout_frames", + obs_module_text("FramesUntilTimeout"), 2, 120, + 1); + // a group to contain the camera control obs_properties_t *ctrl_props = obs_properties_create(); obs_properties_add_group(props, "controls", @@ -1067,6 +1105,8 @@ static void v4l2_update(void *vptr, obs_data_t *settings) data->resolution = obs_data_get_int(settings, "resolution"); data->framerate = obs_data_get_int(settings, "framerate"); data->color_range = obs_data_get_int(settings, "color_range"); + data->auto_reset = obs_data_get_bool(settings, "auto_reset"); + data->timeout_frames = obs_data_get_int(settings, "timeout_frames"); v4l2_update_source_flags(data, settings);