Skip to content

Commit 46a262f

Browse files
josuahnashif
authored andcommitted
drivers: video: common: Add utilities to seek frmival/caps structures
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 <[email protected]>
1 parent 498138e commit 46a262f

File tree

6 files changed

+314
-1
lines changed

6 files changed

+314
-1
lines changed

drivers/video/video_common.c

+81
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/*
22
* Copyright (c) 2019, Linaro Limited
3+
* Copyright (c) 2024, tinyVision.ai Inc.
34
*
45
* SPDX-License-Identifier: Apache-2.0
56
*/
67

8+
#include <string.h>
9+
710
#include <zephyr/kernel.h>
811
#include <zephyr/drivers/video.h>
912

@@ -83,3 +86,81 @@ void video_buffer_release(struct video_buffer *vbuf)
8386
VIDEO_COMMON_FREE(block->data);
8487
}
8588
}
89+
90+
int video_format_caps_index(const struct video_format_cap *fmts, const struct video_format *fmt,
91+
size_t *idx)
92+
{
93+
for (int i = 0; fmts[i].pixelformat != 0; i++) {
94+
if (fmts[i].pixelformat == fmt->pixelformat &&
95+
IN_RANGE(fmt->width, fmts[i].width_min, fmts[i].width_max) &&
96+
IN_RANGE(fmt->height, fmts[i].height_min, fmts[i].height_max)) {
97+
*idx = i;
98+
return 0;
99+
}
100+
}
101+
return -ENOENT;
102+
}
103+
104+
void video_closest_frmival_stepwise(const struct video_frmival_stepwise *stepwise,
105+
const struct video_frmival *desired,
106+
struct video_frmival *match)
107+
{
108+
uint64_t min = stepwise->min.numerator;
109+
uint64_t max = stepwise->max.numerator;
110+
uint64_t step = stepwise->step.numerator;
111+
uint64_t goal = desired->numerator;
112+
113+
/* Set a common denominator to all values */
114+
min *= stepwise->max.denominator * stepwise->step.denominator * desired->denominator;
115+
max *= stepwise->min.denominator * stepwise->step.denominator * desired->denominator;
116+
step *= stepwise->min.denominator * stepwise->max.denominator * desired->denominator;
117+
goal *= stepwise->min.denominator * stepwise->max.denominator * stepwise->step.denominator;
118+
119+
/* Saturate the desired value to the min/max supported */
120+
goal = CLAMP(goal, min, max);
121+
122+
/* Compute a numerator and denominator */
123+
match->numerator = min + DIV_ROUND_CLOSEST(goal - min, step) * step;
124+
match->denominator = stepwise->min.denominator * stepwise->max.denominator *
125+
stepwise->step.denominator * desired->denominator;
126+
}
127+
128+
void video_closest_frmival(const struct device *dev, enum video_endpoint_id ep,
129+
struct video_frmival_enum *match)
130+
{
131+
uint64_t best_diff_nsec = INT32_MAX;
132+
struct video_frmival desired = match->discrete;
133+
struct video_frmival_enum fie = {.format = match->format};
134+
135+
__ASSERT(match->type != VIDEO_FRMIVAL_TYPE_STEPWISE,
136+
"cannot find range matching the range, only a value matching the range");
137+
138+
while (video_enum_frmival(dev, ep, &fie) == 0) {
139+
struct video_frmival tmp = {0};
140+
uint64_t diff_nsec = 0, a, b;
141+
142+
switch (fie.type) {
143+
case VIDEO_FRMIVAL_TYPE_DISCRETE:
144+
tmp = fie.discrete;
145+
break;
146+
case VIDEO_FRMIVAL_TYPE_STEPWISE:
147+
video_closest_frmival_stepwise(&fie.stepwise, &desired, &tmp);
148+
break;
149+
default:
150+
__ASSERT(false, "invalid answer from the queried video device");
151+
}
152+
153+
a = video_frmival_nsec(&desired);
154+
b = video_frmival_nsec(&tmp);
155+
diff_nsec = a > b ? a - b : b - a;
156+
if (diff_nsec < best_diff_nsec) {
157+
best_diff_nsec = diff_nsec;
158+
memcpy(&match->discrete, &tmp, sizeof(tmp));
159+
160+
/* The video_enum_frmival() function will increment fie.index every time.
161+
* Compensate for it to get the current index, not the next index.
162+
*/
163+
match->index = fie.index - 1;
164+
}
165+
}
166+
}

include/zephyr/drivers/video.h

+58-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* @brief Video Interface
1717
* @defgroup video_interface Video Interface
1818
* @since 2.1
19-
* @version 1.0.0
19+
* @version 1.1.0
2020
* @ingroup io_interfaces
2121
* @{
2222
*/
@@ -753,6 +753,63 @@ struct video_buffer *video_buffer_alloc(size_t size);
753753
*/
754754
void video_buffer_release(struct video_buffer *buf);
755755

756+
/**
757+
* @brief Search for a format that matches in a list of capabilities
758+
*
759+
* @param fmts The format capability list to search.
760+
* @param fmt The format to find in the list.
761+
* @param idx The pointer to a number of the first format that matches.
762+
*
763+
* @return 0 when a format is found.
764+
* @return -ENOENT when no matching format is found.
765+
*/
766+
int video_format_caps_index(const struct video_format_cap *fmts, const struct video_format *fmt,
767+
size_t *idx);
768+
769+
/**
770+
* @brief Compute the difference between two frame intervals
771+
*
772+
* @param frmival Frame interval to turn into microseconds.
773+
*
774+
* @return The frame interval value in microseconds.
775+
*/
776+
static inline uint64_t video_frmival_nsec(const struct video_frmival *frmival)
777+
{
778+
return (uint64_t)NSEC_PER_SEC * frmival->numerator / frmival->denominator;
779+
}
780+
781+
/**
782+
* @brief Find the closest match to a frame interval value within a stepwise frame interval.
783+
*
784+
* @param stepwise The stepwise frame interval range to search
785+
* @param desired The frame interval for which find the closest match
786+
* @param match The resulting frame interval closest to @p desired
787+
*/
788+
void video_closest_frmival_stepwise(const struct video_frmival_stepwise *stepwise,
789+
const struct video_frmival *desired,
790+
struct video_frmival *match);
791+
792+
/**
793+
* @brief Find the closest match to a frame interval value within a video device.
794+
*
795+
* To compute the closest match, fill @p match with the following fields:
796+
*
797+
* - @c match->format to the @ref video_format of interest.
798+
* - @c match->type to @ref VIDEO_FRMIVAL_TYPE_DISCRETE.
799+
* - @c match->discrete to the desired frame interval.
800+
*
801+
* The result will be loaded into @p match, with the following fields set:
802+
*
803+
* - @c match->discrete to the value of the closest frame interval.
804+
* - @c match->index to the index of the closest frame interval.
805+
*
806+
* @param dev Video device to query.
807+
* @param ep Video endpoint ID to query.
808+
* @param match Frame interval enumerator with the query, and loaded with the result.
809+
*/
810+
void video_closest_frmival(const struct device *dev, enum video_endpoint_id ep,
811+
struct video_frmival_enum *match);
812+
756813
/* fourcc - four-character-code */
757814
#define video_fourcc(a, b, c, d) \
758815
((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(integration)
6+
7+
target_sources(app PRIVATE src/video_common.c)

tests/drivers/video/api/prj.conf

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_VIDEO=y
3+
CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=16384
4+
CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=1
5+
CONFIG_VIDEO_LOG_LEVEL_DBG=y
+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (c) 2024 tinyVision.ai Inc.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/ztest.h>
8+
#include <zephyr/drivers/video.h>
9+
10+
enum {
11+
RGB565,
12+
YUYV_A,
13+
YUYV_B,
14+
};
15+
16+
static const struct video_format_cap fmts[] = {
17+
[RGB565] = {.pixelformat = VIDEO_PIX_FMT_RGB565,
18+
.width_min = 1280, .width_max = 1280, .width_step = 50,
19+
.height_min = 720, .height_max = 720, .height_step = 50},
20+
[YUYV_A] = {.pixelformat = VIDEO_PIX_FMT_YUYV,
21+
.width_min = 100, .width_max = 1000, .width_step = 50,
22+
.height_min = 100, .height_max = 1000, .height_step = 50},
23+
[YUYV_B] = {.pixelformat = VIDEO_PIX_FMT_YUYV,
24+
.width_min = 1920, .width_max = 1920, .width_step = 0,
25+
.height_min = 1080, .height_max = 1080, .height_step = 0},
26+
{0},
27+
};
28+
29+
ZTEST(video_common, test_video_format_caps_index)
30+
{
31+
struct video_format fmt = {0};
32+
size_t idx;
33+
int ret;
34+
35+
fmt.pixelformat = VIDEO_PIX_FMT_YUYV;
36+
37+
fmt.width = 100;
38+
fmt.height = 100;
39+
fmt.pitch = 100 * 2;
40+
ret = video_format_caps_index(fmts, &fmt, &idx);
41+
zassert_ok(ret, "expecting minimum value to match");
42+
zassert_equal(idx, YUYV_A);
43+
44+
fmt.width = 1000;
45+
fmt.height = 1000;
46+
fmt.pitch = 1000 * 2;
47+
ret = video_format_caps_index(fmts, &fmt, &idx);
48+
zassert_ok(ret, "expecting maximum value to match");
49+
zassert_equal(idx, YUYV_A);
50+
51+
fmt.width = 1920;
52+
fmt.height = 1080;
53+
fmt.pitch = 1920 * 2;
54+
ret = video_format_caps_index(fmts, &fmt, &idx);
55+
zassert_ok(ret, "expecting exact match to work");
56+
zassert_equal(idx, YUYV_B);
57+
58+
fmt.width = 1001;
59+
fmt.height = 1000;
60+
fmt.pitch = 1001 * 2;
61+
ret = video_format_caps_index(fmts, &fmt, &idx);
62+
zassert_not_ok(ret, "expecting 1 above maximum width to mismatch");
63+
64+
fmt.width = 1000;
65+
fmt.height = 1001;
66+
fmt.pitch = 1000 * 2;
67+
ret = video_format_caps_index(fmts, &fmt, &idx);
68+
zassert_not_ok(ret, "expecting 1 above maximum height to mismatch");
69+
70+
fmt.width = 1280;
71+
fmt.height = 720;
72+
fmt.pitch = 1280 * 2;
73+
ret = video_format_caps_index(fmts, &fmt, &idx);
74+
zassert_not_ok(ret);
75+
zassert_not_ok(ret, "expecting wrong format to mismatch");
76+
77+
fmt.pixelformat = VIDEO_PIX_FMT_RGB565;
78+
79+
fmt.width = 1000;
80+
fmt.height = 1000;
81+
fmt.pitch = 1000 * 2;
82+
ret = video_format_caps_index(fmts, &fmt, &idx);
83+
zassert_not_ok(ret, "expecting wrong format to mismatch");
84+
85+
fmt.width = 1280;
86+
fmt.height = 720;
87+
fmt.pitch = 1280 * 2;
88+
ret = video_format_caps_index(fmts, &fmt, &idx);
89+
zassert_ok(ret, "expecting exact match to work");
90+
zassert_equal(idx, RGB565);
91+
}
92+
93+
ZTEST(video_common, test_video_frmival_nsec)
94+
{
95+
zassert_equal(
96+
video_frmival_nsec(&(struct video_frmival){.numerator = 1, .denominator = 15}),
97+
66666666);
98+
99+
zassert_equal(
100+
video_frmival_nsec(&(struct video_frmival){.numerator = 1, .denominator = 30}),
101+
33333333);
102+
103+
zassert_equal(
104+
video_frmival_nsec(&(struct video_frmival){.numerator = 5, .denominator = 1}),
105+
5000000000);
106+
107+
zassert_equal(
108+
video_frmival_nsec(&(struct video_frmival){.numerator = 1, .denominator = 1750000}),
109+
571);
110+
}
111+
112+
ZTEST(video_common, test_video_closest_frmival_stepwise)
113+
{
114+
struct video_frmival_stepwise stepwise;
115+
struct video_frmival desired;
116+
struct video_frmival expected;
117+
struct video_frmival match;
118+
119+
stepwise.min.numerator = 1;
120+
stepwise.min.denominator = 30;
121+
stepwise.max.numerator = 30;
122+
stepwise.max.denominator = 30;
123+
stepwise.step.numerator = 1;
124+
stepwise.step.denominator = 30;
125+
126+
desired.numerator = 1;
127+
desired.denominator = 1;
128+
video_closest_frmival_stepwise(&stepwise, &desired, &match);
129+
zassert_equal(video_frmival_nsec(&match), video_frmival_nsec(&desired), "1 / 1");
130+
131+
desired.numerator = 3;
132+
desired.denominator = 30;
133+
video_closest_frmival_stepwise(&stepwise, &desired, &match);
134+
zassert_equal(video_frmival_nsec(&match), video_frmival_nsec(&desired), "3 / 30");
135+
136+
desired.numerator = 7;
137+
desired.denominator = 80;
138+
expected.numerator = 3;
139+
expected.denominator = 30;
140+
video_closest_frmival_stepwise(&stepwise, &desired, &match);
141+
zassert_equal(video_frmival_nsec(&match), video_frmival_nsec(&expected), "7 / 80");
142+
143+
desired.numerator = 1;
144+
desired.denominator = 120;
145+
video_closest_frmival_stepwise(&stepwise, &desired, &match);
146+
zassert_equal(video_frmival_nsec(&match), video_frmival_nsec(&stepwise.min), "1 / 120");
147+
148+
desired.numerator = 100;
149+
desired.denominator = 1;
150+
video_closest_frmival_stepwise(&stepwise, &desired, &match);
151+
zassert_equal(video_frmival_nsec(&match), video_frmival_nsec(&stepwise.max), "100 / 1");
152+
}
153+
154+
ZTEST_SUITE(video_common, NULL, NULL, NULL, NULL, NULL);

tests/drivers/video/api/testcase.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright (c) 2024 tinyVision.ai Inc.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
tests:
5+
drivers.video.api:
6+
tags:
7+
- drivers
8+
- video
9+
platform_allow: native_sim

0 commit comments

Comments
 (0)