-
Notifications
You must be signed in to change notification settings - Fork 7.8k
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
base: main
Are you sure you want to change the base?
video: introduction of STM32 VENC driver for H264 hardware video compression #92884
Conversation
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]>
The following west manifest projects have changed revision in this Pull Request:
⛔ DNM label due to: 1 added project and 1 unreachable repo Note: This message is automatically posted and updated by the Manifest GitHub Action. |
|
@@ -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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int "Mem SW attribute for video buffer" | |
int "Heap allocations attribute" |
Mem SW attribute for video buffer: | ||
1: ATTR_SW_ALLOC_CACHE | ||
2: ATTR_SW_ALLOC_NON_CACHE | ||
4: ATTR_SW_ALLOC_DMA |
There was a problem hiding this comment.
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:
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)). |
#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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#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) |
#if defined(CONFIG_VIDEO_BUFFER_USE_MEM_ATTR_HEAP) | ||
mem_attr_heap_pool_init(); | ||
#endif |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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...)
if (ret) | ||
return -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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)); |
There was a problem hiding this comment.
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
)
There was a problem hiding this 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.
input = k_fifo_get(&data->fifo_input, K_NO_WAIT); | ||
if (!input) | ||
return 0; | ||
LOG_DBG("input=%p\n", input); |
There was a problem hiding this comment.
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.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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...
zephyr/include/zephyr/drivers/video.h
Lines 1638 to 1647 in 9219c81
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) |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 ofK_NO_WAIT
, and therefre, neverreturn 0
as they are guaranteed to return something withK_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!
- name: zephyr-isp | ||
url: [email protected]:AIS/mg-zephyr-isp.git | ||
revision: master | ||
path: Lib/zephyr-isp | ||
submodules: true |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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:
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; | ||
} | ||
|
There was a problem hiding this comment.
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:
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]>
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]>
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])
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:
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:
To find the right interface, search for "enx" in kernel log
[1] zephyrproject-rtos/hal_stm32#295
[2] https://docs.zephyrproject.org/latest/boards/st/stm32n6570_dk/doc/index.html