From c48b390971a935e374c498c460de0fcdea7fc197 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Fri, 26 Apr 2024 13:15:13 +0200 Subject: [PATCH 1/3] include: video: Add frame interval APIs Add set/get/enum frame intervals APIs to support frame rate changing. Signed-off-by: Phi Bang Nguyen --- include/zephyr/drivers/video.h | 175 +++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) diff --git a/include/zephyr/drivers/video.h b/include/zephyr/drivers/video.h index 42f270084a5e29..b148f19a4a3c1b 100644 --- a/include/zephyr/drivers/video.h +++ b/include/zephyr/drivers/video.h @@ -118,6 +118,66 @@ struct video_buffer { uint32_t timestamp; }; +/** + * @brief video_frmival_type enum + * + * Supported frame interval type of a video device. + */ +enum video_frmival_type { + /** discrete frame interval type */ + VIDEO_FRMIVAL_TYPE_DISCRETE = 1, + /** stepwise frame interval type */ + VIDEO_FRMIVAL_TYPE_STEPWISE = 2, +}; + +/** + * @struct video_frmival + * @brief Video frame interval structure + * + * Used to describe a video frame interval. + */ +struct video_frmival { + /** numerator of the frame interval */ + uint32_t numerator; + /** denominator of the frame interval */ + uint32_t denominator; +}; + +/** + * @struct video_frmival_stepwise + * @brief Video frame interval stepwise structure + * + * Used to describe the video frame interval stepwise type. + */ +struct video_frmival_stepwise { + /** minimum frame interval in seconds */ + struct video_frmival min; + /** maximum frame interval in seconds */ + struct video_frmival max; + /** frame interval step size in seconds */ + struct video_frmival step; +}; + +/** + * @struct video_frmival_enum + * @brief Video frame interval enumeration structure + * + * Used to describe the supported video frame intervals of a given video format. + */ +struct video_frmival_enum { + /** frame interval index during enumeration */ + uint32_t index; + /** video format for which the query is made */ + const struct video_format *format; + /** frame interval type the device supports */ + enum video_frmival_type type; + /** the actual frame interval */ + union { + struct video_frmival discrete; + struct video_frmival_stepwise stepwise; + }; +}; + /** * @brief video_endpoint_id enum * @@ -165,6 +225,33 @@ typedef int (*video_api_get_format_t)(const struct device *dev, enum video_endpoint_id ep, struct video_format *fmt); +/** + * @typedef video_api_set_frmival_t + * @brief Set video frame interval + * + * See video_set_frmival() for argument descriptions. + */ +typedef int (*video_api_set_frmival_t)(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival); + +/** + * @typedef video_api_get_frmival_t + * @brief Get current video frame interval + * + * See video_get_frmival() for argument descriptions. + */ +typedef int (*video_api_get_frmival_t)(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival); + +/** + * @typedef video_api_enum_frmival_t + * @brief List all supported frame intervals of a given format + * + * See video_enum_frmival() for argument descriptions. + */ +typedef int (*video_api_enum_frmival_t)(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival_enum *fie); + /** * @typedef video_api_enqueue_t * @brief Enqueue a buffer in the driver’s incoming queue. @@ -267,6 +354,9 @@ __subsystem struct video_driver_api { video_api_set_ctrl_t set_ctrl; video_api_set_ctrl_t get_ctrl; video_api_set_signal_t set_signal; + video_api_set_frmival_t set_frmival; + video_api_get_frmival_t get_frmival; + video_api_enum_frmival_t enum_frmival; }; /** @@ -322,6 +412,91 @@ static inline int video_get_format(const struct device *dev, return api->get_format(dev, ep, fmt); } +/** + * @brief Set video frame interval. + * + * Configure video device with a specific frame interval. + * + * Drivers must not return an error solely because the requested interval doesn’t match the device + * capabilities. They must instead modify the interval to match what the hardware can provide. + * + * @param dev Pointer to the device structure for the driver instance. + * @param ep Endpoint ID. + * @param frmival Pointer to a video frame interval struct. + * + * @retval 0 If successful. + * @retval -ENOSYS If API is not implemented. + * @retval -EINVAL If parameters are invalid. + * @retval -EIO General input / output error. + */ +static inline int video_set_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + const struct video_driver_api *api = (const struct video_driver_api *)dev->api; + + if (api->set_frmival == NULL) { + return -ENOSYS; + } + + return api->set_frmival(dev, ep, frmival); +} + +/** + * @brief Get video frame interval. + * + * Get current frame interval of the video device. + * + * @param dev Pointer to the device structure for the driver instance. + * @param ep Endpoint ID. + * @param frmival Pointer to a video frame interval struct. + * + * @retval 0 If successful. + * @retval -ENOSYS If API is not implemented. + * @retval -EINVAL If parameters are invalid. + * @retval -EIO General input / output error. + */ +static inline int video_get_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + const struct video_driver_api *api = (const struct video_driver_api *)dev->api; + + if (api->get_frmival == NULL) { + return -ENOSYS; + } + + return api->get_frmival(dev, ep, frmival); +} + +/** + * @brief List video frame intervals. + * + * List all supported video frame intervals of a given format. + * + * Applications should fill the pixelformat, width and height fields of the + * video_frmival_enum struct first to form a query. Then, the index field is + * used to iterate through the supported frame intervals list. + * + * @param dev Pointer to the device structure for the driver instance. + * @param ep Endpoint ID. + * @param fie Pointer to a video frame interval enumeration struct. + * + * @retval 0 If successful. + * @retval -ENOSYS If API is not implemented. + * @retval -EINVAL If parameters are invalid. + * @retval -EIO General input / output error. + */ +static inline int video_enum_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival_enum *fie) +{ + const struct video_driver_api *api = (const struct video_driver_api *)dev->api; + + if (api->enum_frmival == NULL) { + return -ENOSYS; + } + + return api->enum_frmival(dev, ep, fie); +} + /** * @brief Enqueue a video buffer. * From e0197b62bc96afcb61f5e2a5ccdf8c95046a1c3a Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Thu, 2 May 2024 13:38:12 +0200 Subject: [PATCH 2/3] drivers: video_sw_generator: Add support to change frame rate Add code to to set / get / enumerate frame rates (intervals). Signed-off-by: Phi Bang Nguyen --- drivers/video/video_sw_generator.c | 80 ++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/drivers/video/video_sw_generator.c b/drivers/video/video_sw_generator.c index 54abceb28fb646..988811db1c325f 100644 --- a/drivers/video/video_sw_generator.c +++ b/drivers/video/video_sw_generator.c @@ -13,7 +13,15 @@ LOG_MODULE_REGISTER(video_sw_generator, CONFIG_VIDEO_LOG_LEVEL); #define VIDEO_PATTERN_COLOR_BAR 0 -#define VIDEO_PATTERN_FPS 30 +#define DEFAULT_FRAME_RATE 30 +/* + * The pattern generator needs about 1.5 ms to fill out a 320x160 RGB565 + * buffer and 25 ms for a 720p XRGB32 buffer (tested on i.MX RT1064). So, + * the max frame rate actually varies between 40 and 666 fps depending on + * the buffer format. There is no way to determine this value for each + * format. 60 fps is therefore chosen as a common value in practice. + */ +#define MAX_FRAME_RATE 60 struct video_sw_generator_data { const struct device *dev; @@ -26,6 +34,7 @@ struct video_sw_generator_data { bool ctrl_hflip; bool ctrl_vflip; struct k_poll_signal *signal; + uint32_t frame_rate; }; static const struct video_format_cap fmts[] = {{ @@ -93,7 +102,7 @@ static int video_sw_generator_stream_start(const struct device *dev) { struct video_sw_generator_data *data = dev->data; - k_work_schedule(&data->buf_work, K_MSEC(1000 / VIDEO_PATTERN_FPS)); + k_work_schedule(&data->buf_work, K_MSEC(1000 / data->frame_rate)); return 0; } @@ -145,7 +154,7 @@ static void __buffer_work(struct k_work *work) data = CONTAINER_OF(dwork, struct video_sw_generator_data, buf_work); - k_work_reschedule(&data->buf_work, K_MSEC(1000 / VIDEO_PATTERN_FPS)); + k_work_reschedule(&data->buf_work, K_MSEC(1000 / data->frame_rate)); vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT); if (vbuf == NULL) { @@ -262,6 +271,67 @@ static inline int video_sw_generator_set_ctrl(const struct device *dev, unsigned return 0; } +static int video_sw_generator_set_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + struct video_sw_generator_data *data = dev->data; + + if (frmival->denominator && frmival->numerator) { + data->frame_rate = MIN(DIV_ROUND_CLOSEST(frmival->denominator, frmival->numerator), + MAX_FRAME_RATE); + } else { + return -EINVAL; + } + + frmival->numerator = 1; + frmival->denominator = data->frame_rate; + + return 0; +} + +static int video_sw_generator_get_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival *frmival) +{ + struct video_sw_generator_data *data = dev->data; + + frmival->numerator = 1; + frmival->denominator = data->frame_rate; + + return 0; +} + +static int video_sw_generator_enum_frmival(const struct device *dev, enum video_endpoint_id ep, + struct video_frmival_enum *fie) +{ + int i = 0; + + if (ep != VIDEO_EP_OUT || fie->index) { + return -EINVAL; + } + + while (fmts[i].pixelformat && (fmts[i].pixelformat != fie->format->pixelformat)) { + i++; + } + + if ((i == ARRAY_SIZE(fmts)) || (fie->format->width > fmts[i].width_max) || + (fie->format->width < fmts[i].width_min) || + (fie->format->height > fmts[i].height_max) || + (fie->format->height < fmts[i].height_min)) { + return -EINVAL; + } + + fie->type = VIDEO_FRMIVAL_TYPE_STEPWISE; + fie->stepwise.min.numerator = 1; + fie->stepwise.min.denominator = MAX_FRAME_RATE; + fie->stepwise.max.numerator = UINT32_MAX; + fie->stepwise.max.denominator = 1; + /* The frame interval step size is the minimum resolution of K_MSEC(), which is 1ms */ + fie->stepwise.step.numerator = 1; + fie->stepwise.step.denominator = 1000; + + return 0; +} + static const struct video_driver_api video_sw_generator_driver_api = { .set_format = video_sw_generator_set_fmt, .get_format = video_sw_generator_get_fmt, @@ -272,6 +342,9 @@ static const struct video_driver_api video_sw_generator_driver_api = { .dequeue = video_sw_generator_dequeue, .get_caps = video_sw_generator_get_caps, .set_ctrl = video_sw_generator_set_ctrl, + .set_frmival = video_sw_generator_set_frmival, + .get_frmival = video_sw_generator_get_frmival, + .enum_frmival = video_sw_generator_enum_frmival, #ifdef CONFIG_POLL .set_signal = video_sw_generator_set_signal, #endif @@ -282,6 +355,7 @@ static struct video_sw_generator_data video_sw_generator_data_0 = { .fmt.height = 160, .fmt.pitch = 320 * 2, .fmt.pixelformat = VIDEO_PIX_FMT_RGB565, + .frame_rate = DEFAULT_FRAME_RATE, }; static int video_sw_generator_init(const struct device *dev) From 230538e66e21d06f338658cbf0e32fcf46faa926 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Thu, 2 May 2024 13:39:41 +0200 Subject: [PATCH 3/3] samples: video: capture: Get and enumerate frame rate Add code to get the current frame rate and enumerate all supported frame rates (frame intervals) of the current format. Signed-off-by: Phi Bang Nguyen --- samples/drivers/video/capture/src/main.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/samples/drivers/video/capture/src/main.c b/samples/drivers/video/capture/src/main.c index d04d0de0954d62..9eed2a3924fb27 100644 --- a/samples/drivers/video/capture/src/main.c +++ b/samples/drivers/video/capture/src/main.c @@ -78,6 +78,8 @@ int main(void) struct video_buffer *buffers[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX], *vbuf; struct video_format fmt; struct video_caps caps; + struct video_frmival frmival; + struct video_frmival_enum fie; unsigned int frame = 0; size_t bsize; int i = 0; @@ -129,6 +131,26 @@ int main(void) (char)(fmt.pixelformat >> 8), (char)(fmt.pixelformat >> 16), (char)(fmt.pixelformat >> 24), fmt.width, fmt.height); + if (!video_get_frmival(video_dev, VIDEO_EP_OUT, &frmival)) { + printk("- Default frame rate : %f fps\n", + 1.0 * frmival.denominator / frmival.numerator); + } + + printk("- Supported frame intervals for the default format:\n"); + memset(&fie, 0, sizeof(fie)); + fie.format = &fmt; + while (video_enum_frmival(video_dev, VIDEO_EP_OUT, &fie) == 0) { + if (fie.type == VIDEO_FRMIVAL_TYPE_DISCRETE) { + printk(" %u/%u ", fie.discrete.numerator, fie.discrete.denominator); + } else { + printk(" [min = %u/%u; max = %u/%u; step = %u/%u]\n", + fie.stepwise.min.numerator, fie.stepwise.min.denominator, + fie.stepwise.max.numerator, fie.stepwise.max.denominator, + fie.stepwise.step.numerator, fie.stepwise.step.denominator); + } + fie.index++; + } + #if DT_HAS_CHOSEN(zephyr_display) const struct device *const display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));