API GUIDE / JPEGD / Middleware

G32S10 JPEGD Middleware 接口

Middleware 层接口:输入 JPEG 和输出 buffer 在 im_jpegd_decode() 返回前必须保持有效。

1. 公开数据类型

1.1 im_jpegd_frame_t

字段类型说明
jpeg_addrconst void *完整 JPEG 文件地址,包含 SOI 和所有头
jpeg_sizeuint32_t完整 JPEG 文件大小
dst_y_addrvoid *输出 Y 或 packed RAW 基地址
dst_uv_addrvoid *NV12 UV 地址;NULL 使用紧凑布局
dst_buf_sizeuint32_t输出 buffer 可写容量
widthuint32_t成功解码后填写的图像宽度
heightuint32_t成功解码后填写的图像高度
output_sizeuint32_t成功解码后填写的 RAW 字节数

输入 JPEG 和输出 buffer 在 im_jpegd_decode() 返回前必须保持有效。

2. 默认值和必填字段

2.1 im_jpegd_job_struct_init()

void im_jpegd_job_struct_init(im_jpegd_job_t *job);

该接口只初始化 job,不初始化 frame。主要默认值:

字段 默认值
out_format HAL_JPEGD_PIX_FMT_NV12
runtime.re.restart_markers_en false
runtime.timeout_en false
runtime.timeout_threshold 0x00ffffff
runtime.timeout_ms 1000
runtime.reset_cycle 10

2.2 frame 必填字段

调用 im_jpegd_decode() 前必须填写:

  • jpeg_addr

  • jpeg_size

  • dst_y_addr

  • dst_buf_size

NV12 时可以填写 dst_uv_addr,也可以留 NULL 使用紧凑布局。

3. 初始化和反初始化

3.1 im_jpegd_init()

error_code_t im_jpegd_init(uint32_t timeout_ms);

流程:

  1. 检查内部状态必须为 UNINITIALIZED。

  2. 申请并使能 IRQ_JPEG_DEC。

  3. 使用 HAL 默认配置初始化 JPEGD。

  4. 注册 MID frame-event callback。

  5. 保存 frame-end 等待超时,进入 INITIALIZED。

timeout_ms == 0 时使用内部默认值 1000 ms。

该超时只控制 MID 等待 frame-end 的时间,不使用 job.runtime.timeout_ms。

返回:

返回值原因
IM_OK初始化成功
IM_ERR_BUSY会话已存在
其他错误IRQ 或 HAL 初始化失败

IRQ 初始化成功但 HAL 初始化失败时,IRQ 会被关闭。

3.2 im_jpegd_deinit()

error_code_t im_jpegd_deinit(void);

注销 MID callback,调用 hal_jpegd_deinit(),关闭 IRQ 和 waiter,并清零 JPEGD 运行时上下文。

已经在 UNINITIALIZED 时直接返回 IM_OK。否则返回 hal_jpegd_deinit() 的结果。

4. 解码流程

error_code_t im_jpegd_decode(const im_jpegd_job_t *job,
                             im_jpegd_frame_t *frame);

4.1 输入检查

以下任一条件成立时返回 IM_ERR_INVALID_PARAM:

  • job == NULL

  • frame == NULL

  • frame->jpeg_addr == NULL

  • frame->jpeg_size == 0

  • frame->dst_y_addr == NULL

  • frame->dst_buf_size == 0

之后再检查会话状态:

  • UNINITIALIZED:IM_ERR_NOT_READY

  • RUNNING:IM_ERR_BUSY

5. 输出格式、尺寸和 buffer

5.1 对齐、stride 和输出大小

格式MCU 宽/高stride输出大小
Y8/8ALIGN_UP(width, 8)stride * ALIGN_UP(height, 8)
NV1216/16ALIGN_UP(width, 16)stride * ALIGN_UP(height, 16) * 3 / 2
YUYV16/8ALIGN_UP(width, 16) * 2stride * ALIGN_UP(height, 8)

当前 JPEGD HAL 的其他主要限制:

项当前限制
解码宽高8-1920 / 8-1440
dst_y_addr / UV 地址非空且至少 16-byte 对齐
out_formatNV12、YUYV 或单 Y
QT/Huffman 表指针全部非空
reset_cycle0-15
restart_mcu_num启用 restart 时必须大于 0 且小于等于 MCU 总数

dst_buf_size 必须覆盖 HAL 计算出的完整输出大小,否则 HAL 在启动前或取结果 时返回 IM_ERR_NO_SPACE。

6. 超时和 restart 配置

存在两个不同层次的超时:

配置 作用
im_jpegd_init(timeout_ms) MID 等待 frame-end 的软件超时
job.runtime.timeout_en / timeout_threshold JPEGD 硬件 timeout 检测

job.runtime.timeout_ms 不控制 MID 的 waiter,实际等待时间由 im_jpegd_init() 的参数决定。

restart marker 信息来自 JPEG DRI:

  • runtime.re.restart_markers_en
  • runtime.re.restart_mcu_num

每次 decode 时 MID 会覆盖调用方 job 中的这两个字段。

7. 错误处理和自动恢复

7.1 中断事件

MID callback 只在内部状态为 RUNNING 时记录事件并唤醒 waiter。

HAL 事件 MID 结果
HAL_JPEGD_IRQ_EVENT_FRAME_DONE 成功
HAL_JPEGD_IRQ_EVENT_TIMEOUT IM_ERR_TIMEOUT
其他事件 IM_ERR_IO

软件等待超时也返回 IM_ERR_TIMEOUT。

7.2 活跃操作失败

解析完成后,如果 sequence 配置、启动、等待或取结果失败,MID 会:

  1. 将内部状态置为 INITIALIZED。
  2. 调用 hal_jpegd_reset() 尝试复位 JPEGD。
  3. 返回触发失败的原始错误码。

如果 reset 也失败,MID 只记录日志,仍然返回原始错误;内部状态已经是 INITIALIZED。因此硬件复位失败这一附加错误不会覆盖第一次失败的原因。

JPEG 头解析失败发生在配置和启动之前,不会触发硬件 reset。

7.3 没有公开 reset 接口

JPEGD MID 当前没有 im_jpegd_reset()。活跃解码失败后的复位由 im_jpegd_decode() 内部自动完成。需要彻底重建时使用 im_jpegd_deinit() + im_jpegd_init()。

8. 时钟接口

error_code_t im_jpegd_set_clk_rate(uint32_t rate_hz);
uint32_t im_jpegd_get_clk_rate(void);

状态约束:

状态 im_jpegd_set_clk_rate()
UNINITIALIZED IM_ERR_NOT_READY
INITIALIZED / CONFIGURED 允许设置
RUNNING IM_ERR_BUSY

rate_hz == 0 选择不超过控制器上限的最高频率。父时钟固定为 MPLL,分频比 向上取整。CGU_JPEG 与 JPEGE 共用,两个 MID 同时修改时最后写入者生效。

im_jpegd_get_clk_rate() 不检查会话状态,与 decode 共用互斥锁;无法测量时 返回 0。每次 decode 都会重新配置 sequence,因此在不同解码之间调整时钟 不需要额外重新配置。

9. 典型用法

9.1 先解析头以计算输出容量

im_jpegd_header_parse_result_t parsed;
uint32_t stride;
uint32_t lines;
uint32_t output_size;

ret = im_jpegd_parse_header(jpeg_data, jpeg_size, &parsed);
if (ret != IM_OK) {
    return ret;
}

stride = ALIGN_UP(parsed.param.width, 16U);
lines = ALIGN_UP(parsed.param.height, 16U);
output_size = stride * lines * 3U / 2U;

im_jpegd_decode() 内部会再次解析头部,因此预解析只适合用于分配输出 buffer 或提前校验,不是完成解码的必要步骤。

9.2 设置时钟

ret = im_jpegd_init(0U);
if (ret != IM_OK) {
    return ret;
}

ret = im_jpegd_set_clk_rate(200000000U);
if (ret == IM_OK) {
    actual_hz = im_jpegd_get_clk_rate();
}