Introduce a video_get_format_index() utility to help finding a caps entry out of a given format. Introduce several utilities to seek and apply frame intervals. Signed-off-by: Josuah Demangeon <me@josuah.net>
166 lines
4.7 KiB
C
166 lines
4.7 KiB
C
/*
|
|
* Copyright (c) 2019, Linaro Limited
|
|
* Copyright (c) 2024, tinyVision.ai Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/drivers/video.h>
|
|
|
|
#if defined(CONFIG_VIDEO_BUFFER_USE_SHARED_MULTI_HEAP)
|
|
#include <zephyr/multi_heap/shared_multi_heap.h>
|
|
|
|
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
|
|
shared_multi_heap_aligned_alloc(CONFIG_VIDEO_BUFFER_SMH_ATTRIBUTE, align, size)
|
|
#define VIDEO_COMMON_FREE(block) shared_multi_heap_free(block)
|
|
#else
|
|
K_HEAP_DEFINE(video_buffer_pool, CONFIG_VIDEO_BUFFER_POOL_SZ_MAX*CONFIG_VIDEO_BUFFER_POOL_NUM_MAX);
|
|
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
|
|
k_heap_aligned_alloc(&video_buffer_pool, align, size, timeout);
|
|
#define VIDEO_COMMON_FREE(block) k_heap_free(&video_buffer_pool, block)
|
|
#endif
|
|
|
|
static struct video_buffer video_buf[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX];
|
|
|
|
struct mem_block {
|
|
void *data;
|
|
};
|
|
|
|
static struct mem_block video_block[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX];
|
|
|
|
struct video_buffer *video_buffer_aligned_alloc(size_t size, size_t align)
|
|
{
|
|
struct video_buffer *vbuf = NULL;
|
|
struct mem_block *block;
|
|
int i;
|
|
|
|
/* find available video buffer */
|
|
for (i = 0; i < ARRAY_SIZE(video_buf); i++) {
|
|
if (video_buf[i].buffer == NULL) {
|
|
vbuf = &video_buf[i];
|
|
block = &video_block[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vbuf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Alloc buffer memory */
|
|
block->data = VIDEO_COMMON_HEAP_ALLOC(align, size, K_FOREVER);
|
|
if (block->data == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
vbuf->buffer = block->data;
|
|
vbuf->size = size;
|
|
vbuf->bytesused = 0;
|
|
|
|
return vbuf;
|
|
}
|
|
|
|
struct video_buffer *video_buffer_alloc(size_t size)
|
|
{
|
|
return video_buffer_aligned_alloc(size, sizeof(void *));
|
|
}
|
|
|
|
void video_buffer_release(struct video_buffer *vbuf)
|
|
{
|
|
struct mem_block *block = NULL;
|
|
int i;
|
|
|
|
/* vbuf to block */
|
|
for (i = 0; i < ARRAY_SIZE(video_block); i++) {
|
|
if (video_block[i].data == vbuf->buffer) {
|
|
block = &video_block[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
vbuf->buffer = NULL;
|
|
if (block) {
|
|
VIDEO_COMMON_FREE(block->data);
|
|
}
|
|
}
|
|
|
|
int video_format_caps_index(const struct video_format_cap *fmts, const struct video_format *fmt,
|
|
size_t *idx)
|
|
{
|
|
for (int i = 0; fmts[i].pixelformat != 0; i++) {
|
|
if (fmts[i].pixelformat == fmt->pixelformat &&
|
|
IN_RANGE(fmt->width, fmts[i].width_min, fmts[i].width_max) &&
|
|
IN_RANGE(fmt->height, fmts[i].height_min, fmts[i].height_max)) {
|
|
*idx = i;
|
|
return 0;
|
|
}
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
void video_closest_frmival_stepwise(const struct video_frmival_stepwise *stepwise,
|
|
const struct video_frmival *desired,
|
|
struct video_frmival *match)
|
|
{
|
|
uint64_t min = stepwise->min.numerator;
|
|
uint64_t max = stepwise->max.numerator;
|
|
uint64_t step = stepwise->step.numerator;
|
|
uint64_t goal = desired->numerator;
|
|
|
|
/* Set a common denominator to all values */
|
|
min *= stepwise->max.denominator * stepwise->step.denominator * desired->denominator;
|
|
max *= stepwise->min.denominator * stepwise->step.denominator * desired->denominator;
|
|
step *= stepwise->min.denominator * stepwise->max.denominator * desired->denominator;
|
|
goal *= stepwise->min.denominator * stepwise->max.denominator * stepwise->step.denominator;
|
|
|
|
/* Saturate the desired value to the min/max supported */
|
|
goal = CLAMP(goal, min, max);
|
|
|
|
/* Compute a numerator and denominator */
|
|
match->numerator = min + DIV_ROUND_CLOSEST(goal - min, step) * step;
|
|
match->denominator = stepwise->min.denominator * stepwise->max.denominator *
|
|
stepwise->step.denominator * desired->denominator;
|
|
}
|
|
|
|
void video_closest_frmival(const struct device *dev, enum video_endpoint_id ep,
|
|
struct video_frmival_enum *match)
|
|
{
|
|
uint64_t best_diff_nsec = INT32_MAX;
|
|
struct video_frmival desired = match->discrete;
|
|
struct video_frmival_enum fie = {.format = match->format};
|
|
|
|
__ASSERT(match->type != VIDEO_FRMIVAL_TYPE_STEPWISE,
|
|
"cannot find range matching the range, only a value matching the range");
|
|
|
|
while (video_enum_frmival(dev, ep, &fie) == 0) {
|
|
struct video_frmival tmp = {0};
|
|
uint64_t diff_nsec = 0, a, b;
|
|
|
|
switch (fie.type) {
|
|
case VIDEO_FRMIVAL_TYPE_DISCRETE:
|
|
tmp = fie.discrete;
|
|
break;
|
|
case VIDEO_FRMIVAL_TYPE_STEPWISE:
|
|
video_closest_frmival_stepwise(&fie.stepwise, &desired, &tmp);
|
|
break;
|
|
default:
|
|
__ASSERT(false, "invalid answer from the queried video device");
|
|
}
|
|
|
|
a = video_frmival_nsec(&desired);
|
|
b = video_frmival_nsec(&tmp);
|
|
diff_nsec = a > b ? a - b : b - a;
|
|
if (diff_nsec < best_diff_nsec) {
|
|
best_diff_nsec = diff_nsec;
|
|
memcpy(&match->discrete, &tmp, sizeof(tmp));
|
|
|
|
/* The video_enum_frmival() function will increment fie.index every time.
|
|
* Compensate for it to get the current index, not the next index.
|
|
*/
|
|
match->index = fie.index - 1;
|
|
}
|
|
}
|
|
}
|