Skip to content

Commit 513bff0

Browse files
committed
Merge branch 'feature/add_video_effects' into 'main'
Add video effects See merge request adf/multimedia/esp-gmf!48
2 parents 17d8a4e + b2a8dff commit 513bff0

18 files changed

+1726
-26
lines changed

elements/gmf_video/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
- Initial release of the `gmf_video` component
88
- Added elements for video processing: decoder, encoder, frame rate converter, and overlay mixer
99
- Added video pixel processing accelerator (PPA) element
10+
- Added software video effects elements: cropper, scaler, color converter and rotator

elements/gmf_video/README.md

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ The video decoder element use the `esp_video_codec` to convert compressed video
1818

1919
### Video PPA (Pixel Processing Accelerator)
2020
The Video PPA element is a compact element, support multiple functions. It is currently available on the ESP32P4 board and includes following functionalities:
21-
- **Color Conversion:**
21+
- **Color Conversion:**
2222
The ESP32P4 supports color conversion through two hardware modules:
2323
- **2D-DMA:** Automatically selected for better efficiency if supported.
2424
- **PPA:** Used as a fallback when 2D-DMA not supported
25-
- **Resizing:**
25+
- **Resizing:**
2626
Resizing functionality is provided by the PPA module.
27-
- **Cropping:**
27+
- **Cropping:**
2828
Cropping functionality is provided by the PPA module.
29-
- **Rotation:**
29+
- **Rotation:**
3030
Supports rotations at 0°, 90°, 180°, and 270°.
3131

3232
### Video FPS Converter
@@ -35,9 +35,24 @@ This module adjusts the frame rate of the video. It decreases the input frame ra
3535
### Video Overlay Mixer
3636
The Video Overlay Mixer module allows users to overlay additional graphics onto a video frame. By receiving overlay data via a user-defined port, it can blend elements such as timestamps, watermarks, or other images into a designated region of the original video frame.
3737

38+
### Video Pixel Processor Elements
39+
Following elements are wrapped for [Video Pixel Processor](https://github.com/espressif/esp-adf-libs/tree/master/esp_image_effects) which implemented software video processing.
40+
41+
#### Video Color Converter
42+
Element to do software color conversion for video image
43+
44+
#### Video Cropper
45+
Element to do software video cropper for video image
46+
47+
#### Video Scaler
48+
Element to do software video scaler for video image
49+
50+
#### Video Rotator
51+
Element to do software video rotator for video image
52+
3853
## ESP-GMF-Video Release and SoC Compatibility
3954

40-
The following table summarizes the support for ESP-GMF-Video elements across Espressif SoCs in the current release.
55+
The following table summarizes the support for ESP-GMF-Video elements across Espressif SoCs in the current release.
4156
A check mark (✔) indicates that the element is supported, while a cross mark (✖) indicates it is not supported.
4257

4358
| Element | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-P4 |
@@ -47,6 +62,7 @@ A check mark (✔) indicates that the element is supported, while a cross m
4762
| Overlay Mixer | ✔ | ✔ | ✔ | ✔ |
4863
| Video Decoder | MJPEG only | MJPEG only | ✔ | ✔ |
4964
| Video Encoder | MJPEG only | MJPEG only | ✔ | ✔ |
65+
| Video Pixel Processor | ✔ | ✔ | ✔ | ✔ |
5066

5167
## Notes
5268

elements/gmf_video/README_CN.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ ESP GMF Video 是一套专为视频编解码和视频转换等设计的视频处
3535
### 视频叠加混合器
3636
视频叠加混合器模块允许用户在视频帧上叠加额外的图像。它通过用户定义的端口接收叠加数据,并将诸如时间戳、水印或其他图像等元素混合到原始视频帧的指定区域。
3737

38+
### 视频像素处理器元素
39+
以下元素是为[视频像素处理器]https://github.com/espressif/esp-adf-libs/tree/master/esp_image_effects)包装的,它实现了软件视频处理。
40+
41+
#### 视频彩色转换器
42+
软件实现视频图像不同颜色转换
43+
44+
#### 视频剪辑器
45+
软件实现视频图像任意位置及大小的裁剪
46+
47+
#### 视频缩放器
48+
软件实现视频图像放大和缩小
49+
50+
#### 视频旋转器
51+
软件实现视频图像任意角度的旋转
52+
3853
# ESP-GMF-Video SoC 兼容性
3954

4055
下表总结了当前版本中 ESP-GMF-Video 元素在乐鑫各 SoC 上的支持情况。
@@ -47,6 +62,7 @@ ESP GMF Video 是一套专为视频编解码和视频转换等设计的视频处
4762
| 叠加混合器 | ✔ | ✔ | ✔ | ✔ |
4863
| 视频解码器 | 仅支持 MJPEG | 仅支持 MJPEG | ✔ | ✔ |
4964
| 视频编码器 | 仅支持 MJPEG | 仅支持 MJPEG | ✔ | ✔ |
65+
| 视频像素处理器 | ✔ | ✔ | ✔ | ✔ |
5066

5167
## 备注
5268

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO., LTD
3+
* SPDX-License-Identifier: LicenseRef-Espressif-Modified-MIT
4+
*
5+
* See LICENSE file for details.
6+
*/
7+
8+
#include <string.h>
9+
#include <inttypes.h>
10+
#include "esp_log.h"
11+
#include "esp_gmf_oal_mutex.h"
12+
#include "esp_gmf_oal_mem.h"
13+
#include "esp_gmf_err.h"
14+
#include "esp_gmf_node.h"
15+
#include "esp_gmf_video_element.h"
16+
#include "esp_gmf_video_color_convert.h"
17+
#include "esp_gmf_video_methods_def.h"
18+
#include "gmf_video_common.h"
19+
#include "esp_gmf_caps_def.h"
20+
21+
static const char *TAG = "IMGFX_CLRCVT_EL";
22+
23+
typedef struct _gmf_imgfx_color_convert_t {
24+
esp_gmf_video_element_t parent;
25+
esp_imgfx_color_convert_handle_t hd;
26+
bool need_recfg;
27+
} esp_gmf_color_convert_hd_t;
28+
29+
static esp_gmf_job_err_t video_cc_el_open(esp_gmf_element_handle_t self, void *para)
30+
{
31+
esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self;
32+
// Get and check configuration
33+
esp_imgfx_color_convert_cfg_t *cfg = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(self);
34+
ESP_GMF_MEM_CHECK(TAG, cfg, return ESP_GMF_ERR_INVALID_ARG);
35+
// Open color convert module
36+
esp_imgfx_color_convert_open(cfg, &video_el->hd);
37+
ESP_GMF_MEM_CHECK(TAG, video_el->hd, return ESP_GMF_JOB_ERR_FAIL);
38+
// Get video size
39+
esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size));
40+
esp_imgfx_get_image_size(cfg->out_pixel_fmt, &cfg->in_res, (uint32_t *)&(ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size));
41+
// Report information to the next element, the next element will use this information to configure
42+
gmf_video_update_info(self, cfg->in_res.width, cfg->in_res.height, cfg->out_pixel_fmt);
43+
// The video_el->hd has been opened using newest configuration, so it can set the need_recfg to false
44+
video_el->need_recfg = false;
45+
ESP_LOGD(TAG, "Open, %p", self);
46+
return ESP_GMF_JOB_ERR_OK;
47+
}
48+
49+
static esp_gmf_job_err_t video_cc_el_process(esp_gmf_element_handle_t self, void *para)
50+
{
51+
esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self;
52+
esp_imgfx_data_t in_image;
53+
esp_imgfx_data_t out_image;
54+
esp_imgfx_color_convert_cfg_t *cfg = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(self);
55+
bool bypass = cfg->in_pixel_fmt == cfg->out_pixel_fmt;
56+
if (video_el->need_recfg) {
57+
// reset color convert config
58+
esp_imgfx_err_t imgfx_ret = esp_imgfx_color_convert_set_cfg(video_el->hd, cfg);
59+
if (imgfx_ret != ESP_IMGFX_ERR_OK) {
60+
return ESP_GMF_JOB_ERR_FAIL;
61+
}
62+
// Get video size
63+
esp_imgfx_get_image_size(cfg->in_pixel_fmt, &cfg->in_res, (uint32_t *)&ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size);
64+
esp_imgfx_get_image_size(cfg->out_pixel_fmt, &cfg->in_res, (uint32_t *)&ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size);
65+
video_el->need_recfg = false;
66+
}
67+
esp_gmf_port_handle_t in_port = ESP_GMF_ELEMENT_GET(self)->in;
68+
esp_gmf_port_handle_t out_port = ESP_GMF_ELEMENT_GET(self)->out;
69+
esp_gmf_payload_t *in_load = NULL;
70+
esp_gmf_payload_t *out_load = NULL;
71+
esp_gmf_job_err_t ret = ESP_GMF_JOB_ERR_OK;
72+
esp_gmf_err_io_t load_ret = ESP_GMF_IO_OK;
73+
ret = video_el_acquire_payload(in_port, out_port, &in_load, &out_load, ESP_GMF_ELEMENT_GET(video_el)->in_attr.data_size,
74+
ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size, bypass);
75+
if (ret != ESP_GMF_JOB_ERR_OK) {
76+
goto __release;
77+
}
78+
/* It is for bypass */
79+
if (in_load == out_load) {
80+
goto __release;
81+
}
82+
/* finished */
83+
if (in_load->is_done) {
84+
out_load->is_done = in_load->is_done;
85+
out_load->pts = in_load->pts;
86+
ret = ESP_GMF_JOB_ERR_DONE;
87+
ESP_LOGD(TAG, "It's done, out: %d", in_load->valid_size);
88+
goto __release;
89+
}
90+
in_image.data = in_load->buf;
91+
in_image.data_len = in_load->valid_size;
92+
out_image.data = out_load->buf;
93+
out_image.data_len = out_load->buf_length;
94+
esp_imgfx_err_t imgfx_ret = esp_imgfx_color_convert_process(video_el->hd, &in_image, &out_image);
95+
if (imgfx_ret != ESP_IMGFX_ERR_OK) {
96+
ESP_LOGE(TAG, "Image effects color convert process failed, ret: %d-%p", imgfx_ret, video_el);
97+
ret = ESP_GMF_JOB_ERR_FAIL;
98+
goto __release;
99+
}
100+
// Copy the information to the output payload. The next element will use this information to process
101+
out_load->is_done = in_load->is_done;
102+
out_load->valid_size = ESP_GMF_ELEMENT_GET(video_el)->out_attr.data_size;
103+
out_load->pts = in_load->pts;
104+
__release:
105+
// Release in and out port
106+
if (out_load != NULL) {
107+
load_ret = esp_gmf_port_release_out(out_port, out_load, ESP_GMF_MAX_DELAY);
108+
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
109+
ESP_LOGE(TAG, "OUT port release error, ret:%d", load_ret);
110+
ret = ESP_GMF_JOB_ERR_FAIL;
111+
}
112+
}
113+
if (in_load != NULL) {
114+
load_ret = esp_gmf_port_release_in(in_port, in_load, ESP_GMF_MAX_DELAY);
115+
if ((load_ret < ESP_GMF_IO_OK) && (load_ret != ESP_GMF_IO_ABORT)) {
116+
ESP_LOGE(TAG, "IN port release error, ret:%d", load_ret);
117+
ret = ESP_GMF_JOB_ERR_FAIL;
118+
}
119+
}
120+
return ret;
121+
}
122+
123+
static esp_gmf_job_err_t video_cc_el_close(esp_gmf_element_handle_t self, void *para)
124+
{
125+
ESP_LOGD(TAG, "Closed, %p", self);
126+
esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self;
127+
if (video_el) {
128+
if (video_el->hd) {
129+
esp_imgfx_color_convert_close(video_el->hd);
130+
video_el->hd = NULL;
131+
}
132+
}
133+
return ESP_GMF_ERR_OK;
134+
}
135+
136+
static esp_gmf_err_t video_cc_el_delete(esp_gmf_obj_handle_t handle)
137+
{
138+
ESP_LOGD(TAG, "Deleted, %p", handle);
139+
void *cfg = OBJ_GET_CFG(handle);
140+
if (cfg) {
141+
esp_gmf_oal_free(cfg);
142+
}
143+
esp_gmf_video_el_deinit(handle);
144+
esp_gmf_oal_free(handle);
145+
return ESP_GMF_ERR_OK;
146+
}
147+
148+
static inline esp_gmf_err_t esp_gmf_video_color_convert_set_cfg(esp_gmf_element_handle_t self, esp_imgfx_color_convert_cfg_t *config)
149+
{
150+
ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL);
151+
ESP_GMF_NULL_CHECK(TAG, config, return ESP_GMF_JOB_ERR_FAIL);
152+
esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self;
153+
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el;
154+
// reset obj config
155+
if (obj->cfg) {
156+
memcpy(obj->cfg, config, sizeof(esp_imgfx_color_convert_cfg_t));
157+
video_el->need_recfg = true;
158+
return ESP_GMF_JOB_ERR_OK;
159+
}
160+
ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self);
161+
return ESP_GMF_JOB_ERR_FAIL;
162+
}
163+
164+
static inline esp_gmf_err_t esp_gmf_video_color_convert_get_cfg(esp_gmf_element_handle_t self, esp_imgfx_color_convert_cfg_t *config)
165+
{
166+
ESP_GMF_NULL_CHECK(TAG, self, return ESP_GMF_JOB_ERR_FAIL);
167+
esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self;
168+
// If IMGFX color converter opened, get it from it or-else get from object
169+
if (video_el->hd) {
170+
esp_imgfx_err_t imgfx_ret = esp_imgfx_color_convert_get_cfg(video_el->hd, config);
171+
if (imgfx_ret != ESP_IMGFX_ERR_OK) {
172+
ESP_LOGE(TAG, "Get video effects color convert cfg failed, hd:%p, ret: %d", self, imgfx_ret);
173+
return ESP_GMF_JOB_ERR_FAIL;
174+
}
175+
return ESP_GMF_JOB_ERR_OK;
176+
} else {
177+
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el;
178+
// Get obj config
179+
if (obj->cfg) {
180+
memcpy(config, obj->cfg, sizeof(esp_imgfx_color_convert_cfg_t));
181+
return ESP_GMF_JOB_ERR_OK;
182+
}
183+
}
184+
ESP_LOGE(TAG, " %s-%p is no configured yet", __func__, self);
185+
return ESP_GMF_JOB_ERR_FAIL;
186+
}
187+
188+
static esp_gmf_err_t video_set_dst_format(void *handle, esp_gmf_args_desc_t *arg_desc, uint8_t *buf, int buf_len)
189+
{
190+
ESP_GMF_NULL_CHECK(TAG, buf, return ESP_GMF_ERR_INVALID_ARG);
191+
return esp_gmf_video_color_convert_dst_format(handle, *(esp_imgfx_pixel_fmt_t *)buf);
192+
}
193+
194+
static esp_gmf_err_t video_cc_el_load_caps(esp_gmf_element_handle_t handle)
195+
{
196+
esp_gmf_cap_t *caps = NULL;
197+
esp_gmf_cap_t dec_caps = {0};
198+
dec_caps.cap_eightcc = ESP_GMF_CAPS_VIDEO_COLOR_CONVERT;
199+
dec_caps.attr_fun = NULL;
200+
int ret = esp_gmf_cap_append(&caps, &dec_caps);
201+
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to create capability");
202+
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
203+
el->caps = caps;
204+
return ESP_GMF_ERR_OK;
205+
}
206+
207+
static esp_gmf_err_t video_cc_el_load_methods(esp_gmf_element_handle_t handle)
208+
{
209+
esp_gmf_method_t *method = NULL;
210+
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
211+
esp_gmf_args_desc_t *set_args = NULL;
212+
ret = esp_gmf_args_desc_append(&set_args, VMETHOD_ARG(CLR_CVT, SET_DST_FMT, FMT), ESP_GMF_ARGS_TYPE_INT8, sizeof(esp_imgfx_pixel_fmt_t), 0);
213+
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to append color convert destination format");
214+
ret = esp_gmf_method_append(&method, VMETHOD(CLR_CVT, SET_DST_FMT), video_set_dst_format, set_args);
215+
ESP_GMF_RET_ON_ERROR(TAG, ret, return ret, "Failed to register %s method", VMETHOD(CLR_CVT, SET_DST_FMT));
216+
esp_gmf_element_t *el = (esp_gmf_element_t *)handle;
217+
el->method = method;
218+
return ret;
219+
}
220+
221+
static esp_gmf_err_t video_cc_el_received_event_handler(esp_gmf_event_pkt_t *evt, void *ctx)
222+
{
223+
ESP_GMF_NULL_CHECK(TAG, ctx, return ESP_GMF_ERR_INVALID_ARG);
224+
ESP_GMF_NULL_CHECK(TAG, evt, return ESP_GMF_ERR_INVALID_ARG);
225+
if ((evt->type != ESP_GMF_EVT_TYPE_REPORT_INFO)
226+
|| (evt->sub != ESP_GMF_INFO_VIDEO)
227+
|| (evt->payload == NULL)) {
228+
return ESP_GMF_ERR_OK;
229+
}
230+
esp_gmf_element_handle_t self = (esp_gmf_element_handle_t)ctx;
231+
esp_gmf_element_handle_t el = evt->from;
232+
esp_gmf_event_state_t state = ESP_GMF_EVENT_STATE_NONE;
233+
esp_gmf_element_get_state(self, &state);
234+
esp_gmf_info_video_t *info = (esp_gmf_info_video_t *)evt->payload;
235+
esp_imgfx_color_convert_cfg_t *config = (esp_imgfx_color_convert_cfg_t *)OBJ_GET_CFG(self);
236+
esp_gmf_color_convert_hd_t *video_el = (esp_gmf_color_convert_hd_t *)self;
237+
GMF_VIDEO_UPDATE_CONFIG(config, info, video_el->need_recfg);
238+
ESP_LOGD(TAG, "RECV element info, from: %s-%p, next: %p, self: %s-%p, type: %x, state: %s, width: %d, height: %d, pixel format: %lx",
239+
OBJ_GET_TAG(el), el, esp_gmf_node_for_next((esp_gmf_node_t *)el), OBJ_GET_TAG(self), self, evt->type,
240+
esp_gmf_event_get_state_str(state), info->width, info->height, info->format_id);
241+
// It is for the first time to receive event from previous element
242+
if (state == ESP_GMF_EVENT_STATE_NONE) {
243+
esp_gmf_element_set_state(self, ESP_GMF_EVENT_STATE_INITIALIZED);
244+
}
245+
return ESP_GMF_ERR_OK;
246+
}
247+
248+
static esp_gmf_err_t video_cc_el_new(void *config, esp_gmf_obj_handle_t *handle)
249+
{
250+
return esp_gmf_video_color_convert_init((esp_imgfx_color_convert_cfg_t *)config, handle);
251+
}
252+
253+
esp_gmf_err_t esp_gmf_video_color_convert_init(esp_imgfx_color_convert_cfg_t *config, esp_gmf_obj_handle_t *handle)
254+
{
255+
ESP_GMF_MEM_CHECK(TAG, handle, return ESP_GMF_ERR_INVALID_ARG);
256+
*handle = NULL;
257+
esp_gmf_color_convert_hd_t *video_el = esp_gmf_oal_calloc(1, sizeof(esp_gmf_color_convert_hd_t));
258+
ESP_GMF_MEM_CHECK(TAG, video_el, return ESP_GMF_ERR_MEMORY_LACK);
259+
esp_gmf_err_t ret = ESP_GMF_ERR_OK;
260+
esp_gmf_obj_t *obj = (esp_gmf_obj_t *)video_el;
261+
// Set element tag
262+
ret = esp_gmf_obj_set_tag(video_el, "imgfx_color_convert");
263+
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed set OBJ tag");
264+
// Configure object callbacks
265+
obj->new_obj = video_cc_el_new;
266+
obj->del_obj = video_cc_el_delete;
267+
// Set element config
268+
if (config != NULL) {
269+
esp_imgfx_color_convert_cfg_t *cfg = esp_gmf_oal_calloc(1, sizeof(esp_imgfx_color_convert_cfg_t));
270+
ESP_GMF_MEM_CHECK(TAG, cfg, ret = ESP_GMF_ERR_MEMORY_LACK; goto __init_exit);
271+
memcpy(cfg, config, sizeof(esp_imgfx_color_convert_cfg_t));
272+
obj->cfg = cfg;
273+
}
274+
// Configure element
275+
esp_gmf_element_cfg_t el_cfg = {
276+
.dependency = true,
277+
};
278+
ESP_GMF_ELEMENT_IN_PORT_ATTR_SET(el_cfg.in_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
279+
ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
280+
ESP_GMF_ELEMENT_OUT_PORT_ATTR_SET(el_cfg.out_attr, ESP_GMF_EL_PORT_CAP_SINGLE, 0, 0,
281+
ESP_GMF_PORT_TYPE_BLOCK, ESP_GMF_ELEMENT_PORT_DATA_SIZE_DEFAULT);
282+
// Initialize element
283+
ret = esp_gmf_video_el_init(video_el, &el_cfg);
284+
ESP_GMF_RET_ON_NOT_OK(TAG, ret, goto __init_exit, "Failed initialize video element");
285+
// Register element's callbacks
286+
esp_gmf_video_element_t *parent = (esp_gmf_video_element_t *)video_el;
287+
parent->base.ops.open = video_cc_el_open;
288+
parent->base.ops.process = video_cc_el_process;
289+
parent->base.ops.close = video_cc_el_close;
290+
parent->base.ops.event_receiver = video_cc_el_received_event_handler;
291+
parent->base.ops.load_caps = video_cc_el_load_caps;
292+
parent->base.ops.load_methods = video_cc_el_load_methods;
293+
*handle = (esp_gmf_obj_handle_t)video_el;
294+
ESP_LOGD(TAG, "Initialization, %s-%p", OBJ_GET_TAG(obj), obj);
295+
return ret;
296+
__init_exit:
297+
video_cc_el_delete(video_el);
298+
return ret;
299+
}
300+
301+
esp_gmf_err_t esp_gmf_video_color_convert_dst_format(esp_gmf_element_handle_t handle, uint32_t format)
302+
{
303+
esp_imgfx_color_convert_cfg_t config;
304+
esp_gmf_err_t ret = esp_gmf_video_color_convert_get_cfg(handle, &config);
305+
ESP_GMF_RET_ON_NOT_OK(TAG, ret, return ret, "Failed to set destination pixel format");
306+
config.out_pixel_fmt = format;
307+
return esp_gmf_video_color_convert_set_cfg(handle, &config);
308+
}

0 commit comments

Comments
 (0)