Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video : Add frame interval APIs #72254

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 77 additions & 3 deletions drivers/video/video_sw_generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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[] = {{
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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)
Expand Down
175 changes: 175 additions & 0 deletions include/zephyr/drivers/video.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
ngphibang marked this conversation as resolved.
Show resolved Hide resolved
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
*
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
};

/**
Expand Down Expand Up @@ -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.
ngphibang marked this conversation as resolved.
Show resolved Hide resolved
*
* 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.
*
Expand Down
22 changes: 22 additions & 0 deletions samples/drivers/video/capture/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));

Expand Down
Loading