Skip to content

video: introduction of STM32 VENC driver for H264 hardware video compression #92884

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

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

hfruchet-st
Copy link
Contributor

This PR introduces H264 hardware video compression thanks to VENC peripheral of STM32N6.

A new stm32 video encoder driver has been introduced which relies on vc8000nanoe software stack [1].
This is a first basic porting to enable H264 encoding use-case.
RAM memory resources required by VENC encoding process are allocated in external PSRAM, camera frames
captured by DCMIPP and encoded by VENC are also allocated in external PSRAM.
The video encoder driver currently lack of controls to configure the encoding process, default configuration
targets H264 1080p 2Mb/s constant bitrate.

In order to test, the tcpserversink video sample has been enhanced to support video compression in order to stream small H264 compressed video chunks instead of big raw uncompressed images.

To build sample: (flash using ST-Link and boot with FSBL [2])

zephyr$> west build -p -b stm32n6570_dk//fsbl --shield st_b_cams_imx_mb1854 samples/drivers/video/tcpserversink

The default configuration allows to capture and stream 1080p camera content from STM32N6 which can be received, decoded and displayed by an host PC using a GStreamer command line:

host$> gst-launch-1.0 tcpclientsrc host=192.0.2.1 port=5000 ! queue ! decodebin ! queue ! fpsdisplaysink sync=false

Tests have been made using ethernet over USB between STM32N6 and host PC thanks to the temporary last 2 commits.
When using ethernet over USB, set the ethernet interface through USB before starting the GStreamer command line:

host$> sudo ifconfig enx00005e005301 192.0.2.2

To find the right interface, search for "enx" in kernel log

host$> dmesg | grep enx
host$> [1316045.847337] cdc_ether 3-6.3.2:1.0 **enx00005e005301**: renamed from eth0

[1] zephyrproject-rtos/hal_stm32#295
[2] https://docs.zephyrproject.org/latest/boards/st/stm32n6570_dk/doc/index.html

hfruchet-st and others added 17 commits July 9, 2025 10:58
Video buffers being often quite big, it might not be possible to
allocate them from the default heap. For that reason, rely on the
mem sw attr heap allocator in order to design from where to
allocate buffers using which attributes.

Signed-off-by: Alain Volmat <[email protected]>
Add MEM_SW_ATTR on the PSRAM memory in order to have it
become accessible via the mem_attr_heap framework.
On this device since we need to allocate a rather large
framebuffer, we can do that on the PSRAM memory.

Signed-off-by: Alain Volmat <[email protected]>
Video encoder requires non-cacheable memory.

Signed-off-by: Hugues Fruchet <[email protected]>
Add YUV420 semi-planar pixel format (NV12).

Signed-off-by: Hugues Fruchet <[email protected]>
Add YUV420 semi-planar pixel format (NV12).

Signed-off-by: Hugues Fruchet <[email protected]>
Addition of description for the STM32 Video encoder (VENC).

Signed-off-by: Hugues Fruchet <[email protected]>
The STM32 video encoder (VENC) peripheral is a hardware
accelerator allowing to compress RGB/YUV frames into
H264 video bitstream chunks.

Signed-off-by: Hugues Fruchet <[email protected]>
Add node describing the venc in stm32n6.dtsi

Signed-off-by: Hugues Fruchet <[email protected]>
Enable video encoder on stm32n6570_dk discovery board.

Signed-off-by: Hugues Fruchet <[email protected]>
Sync with video capture sample.

Signed-off-by: Hugues Fruchet <[email protected]>
Add video compression support to lowerize network bandwidth.

To visualise camera content on host PC, use GStreamer command line:
$> gst-launch-1.0 tcpclientsrc host=<board ip address> port=5000 ! decodebin ! autovideosink sync=false

Signed-off-by: Hugues Fruchet <[email protected]>
Add YUV420 semi-planar support (NV12).
This is the video encoder prefered pixel format.

Signed-off-by: Hugues Fruchet <[email protected]>
Allow to configure the number of allocated capture frames.
This allows to make tradeof between framerate versus memory usage.
2 buffers allows to capture while sending data (optimal framerate).
1 buffer allows to reduce memory usage but capture framerate is lower.

Signed-off-by: Hugues Fruchet <[email protected]>
Add configuration files for the stm32n6570_dk board.

This enables streaming over ethernet of the images captured by
MB1854 camera module compressed in 1920x1088 H264 video bitstream.

Signed-off-by: Hugues Fruchet <[email protected]>
Ethernet over USB support.

Signed-off-by: Hugues Fruchet <[email protected]>
Disable ethernet phy support to use ethernet over USB.

Signed-off-by: Hugues Fruchet <[email protected]>
Copy link

github-actions bot commented Jul 9, 2025

The following west manifest projects have changed revision in this Pull Request:

Name Old Revision New Revision Diff
zephyr-isp 🆕 None (Added) master N/A

DNM label due to: 1 added project and 1 unreachable repo

Note: This message is automatically posted and updated by the Manifest GitHub Action.

Copy link

sonarqubecloud bot commented Jul 9, 2025

@avolmat-st avolmat-st self-requested a review July 9, 2025 11:12
@@ -58,6 +58,20 @@ config VIDEO_I2C_RETRY_NUM
The default is to not retry. Board configuration files or user project can then
use the number of retries that matches their situation.

config VIDEO_BUFFER_USE_MEM_ATTR_HEAP
bool "Use mem attr api for video buffer"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
bool "Use mem attr api for video buffer"
bool "Allocate video buffer from mem_attr heap"

+ maybe this should depends on MEM_ATTR_HEAP

default n

config VIDEO_BUFFER_MEM_SW_ATTRIBUTE
int "Mem SW attribute for video buffer"
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
int "Mem SW attribute for video buffer"
int "Heap allocations attribute"

Comment on lines +70 to +73
Mem SW attribute for video buffer:
1: ATTR_SW_ALLOC_CACHE
2: ATTR_SW_ALLOC_NON_CACHE
4: ATTR_SW_ALLOC_DMA
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be best to avoid duplicating information from the header.
Proposal:

Suggested change
Mem SW attribute for video buffer:
1: ATTR_SW_ALLOC_CACHE
2: ATTR_SW_ALLOC_NON_CACHE
4: ATTR_SW_ALLOC_DMA
Attribute to request when performing allocations from mem_attr heap.
Refer to include/zephyr/dt-bindings/memory-attr/memory-attr-sw.h for
the list of allowed values (one of ATTR_SW_*).
For example, if you want to use ATTR_SW_ALLOC_CACHE defined as:
#define ATTR_SW_ALLOC_CACHE BIT(0)
This symbol should be set equal to 1 (equal to (1 << 0)).

Comment on lines +33 to +35
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
mem_attr_heap_aligned_alloc((CONFIG_VIDEO_BUFFER_MEM_SW_ATTRIBUTE << \
DT_MEM_SW_ATTR_SHIFT), align, size)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
mem_attr_heap_aligned_alloc((CONFIG_VIDEO_BUFFER_MEM_SW_ATTRIBUTE << \
DT_MEM_SW_ATTR_SHIFT), align, size)
#define VIDEO_COMMON_HEAP_ALLOC(align, size, timeout) \
mem_attr_heap_aligned_alloc(DT_MEM_SW(CONFIG_VIDEO_BUFFER_MEM_SW_ATTRIBUTE), \
align, size)

Comment on lines +58 to +60
#if defined(CONFIG_VIDEO_BUFFER_USE_MEM_ATTR_HEAP)
mem_attr_heap_pool_init();
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: should check that return value is 0 or -EALREADY

{
/* VENC HW REset */
__HAL_RCC_VENC_FORCE_RESET();
k_sleep(K_MSEC(1));
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe k_busy_wait with a lower delay (if upper bound for tRESET is known and <<< 1ms).

Although polling a "is IP under reset" bit would be better. (Ideally, we would even call reset_line_toggle with info from DT, but driver doesn't perform polling...)

Comment on lines +710 to +711
if (ret)
return -1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (ret)
return -1;
if (ret) {
return -1;
}

or even ret < 0 and propagate ret

}
LOG_DBG("output=%p\n", output);

if (!(data->frame_nb % 30) || data->resync) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If this 30 is framerate dependent, it should come from a macro.

static int video_stm32_venc_set_stream(const struct device *dev, bool enable,
enum video_buf_type type)
{
struct video_stm32_venc_data *data = dev->data;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
struct video_stm32_venc_data *data = dev->data;
ARG_UNUSED(type);
struct video_stm32_venc_data *data = dev->data;

ISR_DIRECT_DECLARE(stm32_venc_isr)
{
VENC_EWL_TypeDef *enc = &ewl_instance;
u32 hw_handshake_status = READ_BIT(VENC_REG(BASE_HEncInstantInput >> 2U), (1U << 29U));
Copy link
Contributor

Choose a reason for hiding this comment

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

No HW define for (1 << 29)? (Nit: should be 1U << 29)

@josuah josuah added the area: Video Video subsystem label Jul 19, 2025
Copy link
Contributor

@josuah josuah left a comment

Choose a reason for hiding this comment

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

Thank you for preparing VENC!

This will really make video over network on Zephyr interesting.

It seems like Zephyr lacks a few APIs from Linux (no H.264 format definition, no planar API for NV12...), so if anything feels like missing, let us know so that the missing API can be provided in parallel.

Comment on lines +721 to +724
input = k_fifo_get(&data->fifo_input, K_NO_WAIT);
if (!input)
return 0;
LOG_DBG("input=%p\n", input);
Copy link
Contributor

Choose a reason for hiding this comment

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

In the meantime that a better buffering model is worked on, the way to video_buffer_free() a buffer is by giving it back to the application which video_dequeue() it and reuse it in the next cycle, for instance via data->fifo_input_out (in addition to a data->fifo_input_in).

If encountering errors where running out of buffers hopefully this can help.

Comment on lines +1066 to +1072
ret = HAL_DCMIPP_CSI_PIPE_SemiPlanarStart(&dcmipp->hdcmipp,
pipe->id, DCMIPP_VIRTUAL_CHANNEL0,
&spaddr, DCMIPP_MODE_CONTINUOUS);
} else {
ret = HAL_DCMIPP_CSI_PIPE_Start(&dcmipp->hdcmipp, pipe->id,
DCMIPP_VIRTUAL_CHANNEL0,
addr, DCMIPP_MODE_CONTINUOUS);
Copy link
Contributor

@josuah josuah Jul 19, 2025

Choose a reason for hiding this comment

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

Is this manageable without Linux-style planar API? Otherwise we can introduce planar APIs to help with this.

}

data->pixelformat = fmt->pixelformat;
data->pitch = fmt->pitch;
Copy link
Contributor

@josuah josuah Jul 19, 2025

Choose a reason for hiding this comment

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

Following these changes, when the application sets the format, the driver sets the ->pitch field.

But for H.264 the pitch might as well be 0 as a convention for variable data size...

case VIDEO_PIX_FMT_ARGB32:
case VIDEO_PIX_FMT_ABGR32:
case VIDEO_PIX_FMT_RGBA32:
case VIDEO_PIX_FMT_BGRA32:
return 32;
default:
/* Variable number of bits per pixel or unknown format */
return 0;
}
}

i32 irq_cnt;
} VENC_EWL_TypeDef;

#define ALIGNED_SIZE(x) ((((x) + ALIGNMENT_INCR - 1UL) / ALIGNMENT_INCR) * ALIGNMENT_INCR)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should it be ROUND_DOWN to avoid overflowing of a few bytes due to the rounding?


output = k_fifo_get(&data->fifo_output_in, K_NO_WAIT);
if (!output) {
return 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

At this line, the input buffer is dequeued and dropped by the driver.

There would need some scheme to synchronize both queue to make sure to have both an input and an output buffer poped from the FIFO at the same time.

  • One method is to turn it into a thread with K_FOREVER instead of K_NO_WAIT, and therefre, never return 0 as they are guaranteed to return something with K_FOREVER.

  • One method is to 'peek' at the FIFO without popping the buffer. If there is a buffer ready on both FIFO, then go ahead and pop them on both in confidence that none of them would be NULL (as it was checked beforehand)...

Maybe this does not cause an issue in the way the tcpserversink uses this driver though!

@josuah josuah added the platform: STM32 ST Micro STM32 label Jul 19, 2025
Comment on lines +373 to +377
- name: zephyr-isp
url: [email protected]:AIS/mg-zephyr-isp.git
revision: master
path: Lib/zephyr-isp
submodules: true
Copy link
Member

Choose a reason for hiding this comment

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

Impacts the whole project, should be removed.

Copy link
Member

Choose a reason for hiding this comment

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

Dependency should be documented at board or sample level

@@ -1597,6 +1599,7 @@ static inline unsigned int video_bits_per_pixel(uint32_t pixfmt)
case VIDEO_PIX_FMT_SGRBG12P:
case VIDEO_PIX_FMT_SRGGB12P:
case VIDEO_PIX_FMT_Y12P:
case VIDEO_PIX_FMT_NV12:

Choose a reason for hiding this comment

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

I've added the whole multi buffer support of DCMIPP as well as description in video.h in the following PR which is still currently in draft so that I can fully test it:

#94081

ret = HAL_DCMIPP_PIPE_SetMemoryAddress(&dcmipp->hdcmipp, Pipe, DCMIPP_MEMORY_ADDRESS_0,
(uint32_t)pipe->next->buffer);
if (ret != HAL_OK) {
LOG_ERR("Failed to update memory address");
return;
}

Choose a reason for hiding this comment

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

I've added the whole multi buffer support of DCMIPP as well as description in video.h in the following PR which is still currently in draft so that I can fully test it:

#94081

hfruchet-st referenced this pull request in avolmat-st/zephyr-stm32-mw-isp Aug 13, 2025
No specific processing is necessary currently as part of the
stm32_dcmipp_isp_start function hence remove the assert(0)
currently preventing stop of the isp.

Signed-off-by: Alain Volmat <[email protected]>
hfruchet-st referenced this pull request in avolmat-st/hal_stm32 Aug 13, 2025
Allow to call HAL_DCMIPP_PIPE_SetConfig when the pipe state
is READY. Currently it is only possible to use this function
if the pipe state is RESET and ERROR states.
Indeed, it is possible to reconfigure the PIPE as soon as the
pipe is not currently being used, aka in the READY state.
With that done, it becomes possible to change the PIPE
configuration between two use-cases without having to
DeInit / Init the HAL_DCMIPP or use the unitary pixel packer
functions.

Signed-off-by: Alain Volmat <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Video Video subsystem manifest platform: STM32 ST Micro STM32
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants