Skip to content

drivers: video: sw_generator: add more formats and try to simplify #85968

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

Merged
merged 12 commits into from
May 21, 2025

Conversation

josuah
Copy link
Contributor

@josuah josuah commented Feb 18, 2025

This includes several incremental, small changes to the software generator.

  • drivers: video: sw_generator: modify video_sw_generator_fill_colorbar()
  • drivers: video: sw_generator: refactor fmt/frmival selection
  • drivers: video: sw_generator: refactor: flatten arrays
  • drivers: video: sw_generator: preserve full prefix for internal functions

It was not broken on ideal conditions: this only help it be more robust during misuse.

@zephyrbot zephyrbot added the area: Video Video subsystem label Feb 18, 2025
@josuah josuah force-pushed the pr-video-sw-generator branch from 948ae06 to d803873 Compare February 19, 2025 00:24
@josuah josuah marked this pull request as draft February 19, 2025 23:13
@josuah
Copy link
Contributor Author

josuah commented Feb 19, 2025

force-push:

  • Added 4 different bayer formats
  • Added YUYV format
  • Refactored the pattern generator loop

This is a draft until I can properly test every format, i.e. with UVC or other ways.

Ideally this would be having unit tests like done by the capture sample, but this would need to come in another PR.

@josuah josuah force-pushed the pr-video-sw-generator branch 8 times, most recently from 584c08c to ff7d7f6 Compare February 23, 2025 12:47
@josuah
Copy link
Contributor Author

josuah commented Feb 23, 2025

Force-push:

  • The following formats could be tested with external tooling, not part of upstream Zephyr: YUYV, RGB24, RGGB8
  • Simplification of how struct k_work_sync is used

There is a challenge though: the YUYV is actually YUV BT.709 (used by sRGB and HDTV), not BT.601 (used by JPEG).
So using the pattern generator and piping it into a JPEG library gives a test pattern with wrong contrast.

EDIT: Linux has a separate format field that every driver can state to tell "If you see YUV value coming from me, mind that these were obtained from RGB using this tranfer function: we would need extra fields on struct video_format to handle this...

I suppose a Kconfig option could act as temporary workaround. Until there is a system using both BT.601 and BT.709, this might work.

@josuah
Copy link
Contributor Author

josuah commented Mar 8, 2025

force-push:

  • Test the formats using the lib/pixel, and bugfix them.
  • A bit more input validation

@josuah josuah marked this pull request as ready for review March 8, 2025 22:04
@josuah josuah force-pushed the pr-video-sw-generator branch from 334c03f to 80be4fd Compare March 13, 2025 12:26
Comment on lines 135 to 164
vbuf->bytesused = 0;

if (vbuf->size < data->fmt.pitch) {
LOG_WRN("Buffer %p has only %u bytes, shorter than pitch %u",
vbuf, vbuf->size, data->fmt.pitch);
return;
}

/* Generate the first line of data */
for (int w = 0, i = 0; w < data->fmt.width; w++) {
int color_idx = data->ctrl_vflip ? 7 - w / bw : w / bw;

if (data->fmt.pixelformat == VIDEO_PIX_FMT_RGB565) {
uint16_t *pixel = (uint16_t *)&vbuf->buffer[i];
*pixel = sys_cpu_to_le16(rgb565_colorbar_value[color_idx]);
} else if (data->fmt.pixelformat == VIDEO_PIX_FMT_XRGB32) {
uint32_t *pixel = (uint32_t *)&vbuf->buffer[i];
*pixel = sys_cpu_to_le32(xrgb32_colorbar_value[color_idx]);
}
i += video_bits_per_pixel(data->fmt.pixelformat) / BITS_PER_BYTE;
}

/* Duplicate the first line all over the buffer */
for (int h = 1; h < data->fmt.height; h++) {
if (vbuf->size < vbuf->bytesused + data->fmt.pitch) {
break;
}

memcpy(vbuf->buffer + h * data->fmt.pitch, vbuf->buffer, data->fmt.pitch);
vbuf->bytesused += data->fmt.pitch;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the old code is OK. I think we don't need to check the buffer against the format's pitch because

  • In the current v4z framework, when application allocates the video buffer, it is supposed to allocate sufficient memory for the requested format, if not, it will simply not work.
  • Otherwise, there is a problem in the current framework which will be pointed out in my upcomming buffer management PR:
  1. the application should not specify the format->pitch as it has no knowdlege about this (this should be set by the driver, e.g. driver can add padding to the width line, etc.)
  2. the application also should not specify the buffer size, it just set the format, the number of buffers and request the framework / driver to alloc the buffers on the video buffer heap.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced data->fmt.pitch by a local pitch variable that is computed locally: the driver knows what it generates, let me know if that does not solve the concerns fully.

(2.) takes the entire buffer management out of the application developer way! 😯

Comment on lines 88 to 82
if (fmt->pitch != fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE) {
LOG_ERR("The pitch is not consistent with the other parameters");
return -EINVAL;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here. This reveals the weak point of the current framework : application should not set the format's pitch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

Comment on lines 320 to 420
while (fmts[i].pixelformat && (fmts[i].pixelformat != fie->format->pixelformat)) {
i++;
for (i = 0; fmts[i].pixelformat != 0; ++i) {
if (fie->format->pixelformat == fmts[i].pixelformat &&
IN_RANGE(fie->format->width, fmts[i].width_min, fmts[i].width_max) &&
IN_RANGE(fie->format->height, fmts[i].height_min, fmts[i].height_max)) {
break;
}
}

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)) {
if (fmts[i].pixelformat == 0) {
LOG_ERR("Nothing matching the requested format was found");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the change is the same as the old code except the use of IN_RANGE(). But well, both are fine to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an attempt at using idioms.
I reverted fmts[i].pixelformat == 0 back to i == ARRAY_SIZE(fmts) to avoid too many distracting changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems unchanged since last time, but it is ok to me

Copy link
Contributor Author

@josuah josuah Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I forgot to change it there... I notice that maybe ARRAY_SIZE(fmts) needs to be ARRAY_SIZE(fmts) - 1 to exclude the {0} terminator from the comparisons. Same as here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this reason along with avoiding memory allocation, it could be convenient to switch to video_enum_format() like video_enum_frmival(). For another RFC/PR maybe!

@josuah josuah force-pushed the pr-video-sw-generator branch from 80be4fd to 3cfe8af Compare March 17, 2025 21:34
@josuah josuah changed the title drivers: video: sw_generator: add more checks and try to simplify drivers: video: sw_generator: add more formats and try to simplify Mar 17, 2025
@josuah josuah requested a review from ngphibang April 17, 2025 17:08
ngphibang
ngphibang previously approved these changes Apr 18, 2025
Comment on lines 320 to 420
while (fmts[i].pixelformat && (fmts[i].pixelformat != fie->format->pixelformat)) {
i++;
for (i = 0; fmts[i].pixelformat != 0; ++i) {
if (fie->format->pixelformat == fmts[i].pixelformat &&
IN_RANGE(fie->format->width, fmts[i].width_min, fmts[i].width_max) &&
IN_RANGE(fie->format->height, fmts[i].height_min, fmts[i].height_max)) {
break;
}
}

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)) {
if (fmts[i].pixelformat == 0) {
LOG_ERR("Nothing matching the requested format was found");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems unchanged since last time, but it is ok to me

@kartben kartben requested a review from Copilot April 18, 2025 14:59
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR enhances the video software generator by adding support for additional pixel formats and simplifying several functions. Key changes include updating header comments and macros to support the new VIDEO_PIX_FMT_RGB24 format, refactoring the format capabilities array using a macro, and splitting the fill_colorbar logic into multiple dedicated fill functions.

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
include/zephyr/drivers/video.h Updated comments and added definitions for the new RGB24 pixel format.
drivers/video/video_sw_generator.c Refactored work handling, format selection, and added new fill routines for various pixel formats.

@josuah josuah marked this pull request as ready for review May 19, 2025 15:26
@josuah
Copy link
Contributor Author

josuah commented May 19, 2025

Force-push:

  • CI fixes
  • Typo fixes
  • Fixes fixes (fix bugs introduced by previous fixes 😅)

Copy link

@avolmat-st avolmat-st left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor points, appart from that LGTM

@@ -274,6 +274,10 @@ static int video_sw_generator_enum_frmival(const struct device *dev, struct vide
int idx;
int ret;

if (fie->index >= 1) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very minor point, should match this line with the commit log which mentions fie.index > 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

return 1;
}

static uint16_t video_sw_generator_fill_bayer(uint8_t *buffer, uint16_t width, bool hflip,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this should be named fill_bayer8 or something like that since it produce 8bit unpacked bayer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the 8 from pattern_rgbr8_idx as these are bit-depth agnostic, and added it to fill_bayer.
Thank you!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum, I am not sure about that (bit-depth agnostic) but maybe I misunderstood what is being generated.
For a 8bit Bayer, indeed, that would generate B G G R for example, each letter being here 8bit. But for other bayers (10 - 12 - 14- 16 etc), each letter should be on 2 bytes, as described within the BGGR10 etc format description in video.h.
That is the reason why I mentioned that this is creating a 8bit bayer pattern, not 10-12-14-16 etc. Right ?

Copy link
Contributor Author

@josuah josuah May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can be reused for 10-bit, 12-bit, 14-bit, 16-bit patterns too?

static const uint8_t pattern_rggb_idx[] = {0, 1, 1, 2};
static const uint8_t pattern_bggr_idx[] = {2, 1, 1, 0};
static const uint8_t pattern_gbrg_idx[] = {1, 2, 0, 1};
static const uint8_t pattern_grbg_idx[] = {1, 0, 2, 1};

static const uint8_t pattern_8bars_yuv_rgb24[8][3] = {...};
static const uint16_t pattern_8bars_yuv_rgb48[8][3] = {...};

// 8-bit
row0[w + 0] = pattern_8bars_yuv_rgb24[color_idx][pattern_rggb_idx[0]];
row0[w + 1] = pattern_8bars_yuv_rgb24[color_idx][pattern_rggb_idx[1]];
row1[w + 0] = pattern_8bars_yuv_rgb24[color_idx][pattern_rggb_idx[2]];
row1[w + 1] = pattern_8bars_yuv_rgb24[color_idx][pattern_rggb_idx[3]];

// 16-bit
row0[w + 0] = pattern_8bars_yuv_rgb48[color_idx][pattern_rggb_idx[0]];
row0[w + 1] = pattern_8bars_yuv_rgb48[color_idx][pattern_rggb_idx[1]];
row1[w + 0] = pattern_8bars_yuv_rgb48[color_idx][pattern_rggb_idx[2]];
row1[w + 1] = pattern_8bars_yuv_rgb48[color_idx][pattern_rggb_idx[3]];

Side question: did you need 16-bit test pattern generators? We might need that too at some point at tinyVision.ai so I can add it in another PR. for 12-bit and 14-bit, would that be the MIPI packed formats or the padded formats (16-bit per pixel, only 12/14-bit populated with data).

That also made me spot a small issue that I'm fixing now.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that if addition is done, this is to be done within another PR. Here my point was only to mentions that it was handling 8 bit bayer hence need to clarify that.
I think usually that is unpacked formats (aka 10-12-14 bayers are all stored into 16bits with 0 padding). I don't know if someone has a need to get the still packed version.

Use the video_format_caps_index() function to check if a format matches
any entry of the format caps.

Signed-off-by: Josuah Demangeon <me@josuah.net>
@josuah josuah force-pushed the pr-video-sw-generator branch from 8cbe90c to 9080391 Compare May 19, 2025 20:16
@kartben kartben removed their assignment May 20, 2025
josuah added 9 commits May 20, 2025 10:33
Return an error on fie.index >= 1 as there is only one framerate entry
per pixelformat, this prevents an endless loop.

Signed-off-by: Josuah Demangeon <me@josuah.net>
Add a check for the array size to avoid overwriting unrelated memory when
the buffer is too small for the full format. It first check if there is
enough buffer for one line, and fill it programmatically. Then, it will
try to duplicate that line over the entire buffer, in the limit of the
room available.

Signed-off-by: Josuah Demangeon <me@josuah.net>
This refactors the pattern generator functions to also offer bayer formats
as input. 4 variant of bayer formats are proposed. The pixel packing is
also now split from the color selection: only a single RGB and single YUV
array used by all the pattern generators.

Signed-off-by: Josuah Demangeon <me@josuah.net>
This complements the 32-bit RGB (XRGB32) test pattern with an equivalent
24-bit RGB (RGB24) implementation.

Signed-off-by: Josuah Demangeon <me@josuah.net>
The documentation of k_work_cancel_delayable_sync() states that the input
k_work_sync parameter needs to be valid until the function call returns,
so there is no need to preserve the state across successive calls.
Now that there is a single work-related field in the struct, rename it
to simply "work".

Signed-off-by: Josuah Demangeon <me@josuah.net>
When the FPS value stored in data->frame_rate is zero, a division by zero
occurs. Fix it by clamping the frame rate between min and max values.

Signed-off-by: Josuah Demangeon <me@josuah.net>
Sort alphabetically the header added in the previous few commits
as well as the original implementation.

Signed-off-by: Josuah Demangeon <me@josuah.net>
Provide an initial video buffer in the first call to video_dequeue().
It is used by the driver for choosing between the input or output queue.
Applied to capture, capture_to_lvgl and tcpserversink samples.

Signed-off-by: Josuah Demangeon <me@josuah.net>
Remove trailing spaces at the end of log string.
Move arguments on the next line for readability.
Align arguments to first line.

Signed-off-by: Josuah Demangeon <me@josuah.net>
@josuah josuah force-pushed the pr-video-sw-generator branch from 9080391 to 0850ee8 Compare May 20, 2025 11:33
@josuah
Copy link
Contributor Author

josuah commented May 20, 2025

Force-push:

  • adjust the numbers: _fill_bayer() -> _fill_bayer8(), pattern_rggb8_idx -> pattern_rggb_idx, rgb/yuv -> rgb24/yuv24
  • Only tested with RGB565 for the capture and capture_to_lgl samples. I wanted to test everything with UVC (which supports bayer8 formats too) but need to rebase it first...

@josuah josuah requested a review from avolmat-st May 20, 2025 11:37
Copy link

@ngphibang
Copy link
Contributor

This PR contains the commit 55eb71f which could be considered as a hot fix. I tried to assigned @avolmat-st with hope that it can be merged quickly. Correct me if I did it wrong.

@josuah josuah added the bug The issue is a bug, or the PR is fixing a bug label May 21, 2025
@josuah
Copy link
Contributor Author

josuah commented May 21, 2025

@ngphibang apparently, just 3h left to show-up as "ready" in the merge-list:

@ngphibang
Copy link
Contributor

@josuah That's right, I forgot to check this. Thanks !

@kartben kartben merged commit b95d7e2 into zephyrproject-rtos:main May 21, 2025
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Samples Samples area: Video Video subsystem bug The issue is a bug, or the PR is fixing a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants