av_render

概览

av_render 是一个面向嵌入式的轻量渲染/同步组件,采用 push-based 模型:

  • 上层(如 avi_player)把音频/视频包(带 PTS)推送进来。

  • av_render 负责排队、多线程处理、(可选)解码、同步与最终渲染输出。

架构(逻辑)

下图为播放器的模块架构:

Audio Data                Video Data
  |                         |
  v                         v
+----------+              +----------+
| A Decoder|              | V Decoder|
+----+-----+              +----+-----+
    |                         |
    v                         v
+----------+              +----------+
| A Render |              | V Render |
+----+-----+              +----+-----+
    \                       /
    \                     /
     v                   v
        +-----------+
        |   Sync    |
        +-----------+

职责与边界

av_render 负责:

  • 接收 avi_player push 的音频/视频包,做队列缓冲。

  • 多线程渲染:音频线程、视频线程(以及可选 MJPEG 解码线程)。

  • 音视频同步:以音频为主时钟(audio master),控制视频的等待/丢帧/回压。

  • 视频路径:MJPEG 解码 -> RGB565 ->(可选旋转)-> LCD 写屏。

线程模型(典型)

线程架构示意:

Audio packets (PCM)                 Video packets (MJPEG/RGB565)
    |                                     |
    v                                     v
  +-------------+                       +------------------+
  |   audio_q   |                       |  vdec_q / video_q |
  +------+------+                       +---------+--------+
    |                                        |
    |                                  +-----+-------------------+
    |                                  | MJPEG decode thread(*) |
    |                                  |  MJPEG -> RGB565       |
    |                                  +-----+-------------------+
    |                                        |
    v                                        v
+-------------------+                    +-------------------+
| Audio render thread|                    | Video render thread|
| - write PCM to dev |                    | - sync wait (audio)|
| - update audio clk |                    | - rotate (optional)|
+---------+----------+                    | - LCD write        |
     |                               +---------+----------+
     v                                         |
audio device                               display device

说明:上图中的 audio/display device 属于“具体输出实现”。

  • audio_dev / display_dev 在配置里是 void *(opaque handle),由具体 render_impl 解释。

  • 默认情况下:当创建 av_render 时配置了 audio_dev / display_dev,会使用内置的 LISA render_impl(位于 components/lisa_media_player/av_render/src/render_impl)把 PCM/LCD 写入对应设备。

  • 如果你注册了自定义 renderer(av_render_register_audio_renderer / av_render_register_video_renderer),则可以不依赖 audio_dev / display_dev(可置空),由自定义实现决定如何输出。

其中 (*) 表示可选:当推入 AV_RENDER_PKT_VIDEO_MJPEG 时启用 MJPEG 解码路径。

  • Audio thread:

    • 消费音频包并写入音频设备

    • 更新“当前音频播放时间戳”作为主时钟

  • Video thread:

    • 从视频队列取出解码后的帧

    • 根据音频主时钟做同步等待(小切片 sleep,避免长阻塞)

    • 执行 LCD 输出(可选固定 FPS)

  • MJPEG decode thread(可选/实现相关):

    • 从“待解码队列”取 MJPEG 包

    • 解码为 RGB565 帧并入视频队列

同步策略

  • 默认使用 AV_RENDER_SYNC_AUDIO

    • 以 audio PTS 作为主时钟基准

    • video 在显示前根据 PTS 做等待

    • 当 video 过晚时允许丢帧

  • AVI_PLAYER_MAX_VIDEO_AHEAD_MS

    • 限制 video PTS 领先 audio 的最大值

    • 触发 push 侧/渲染侧的回压,降低端到端延迟并避免队列无限堆积

旋转与屏幕适配

提供旋转选项(尤其适合 240x320 LCD 播放 320x240 视频):

  • AVI_PLAYER_ROTATE_NONE

  • AVI_PLAYER_ROTATE_90_CW

  • AVI_PLAYER_ROTATE_90_CCW

如果你在素材侧已经把视频预旋转成屏幕方向,推荐选择 AVI_PLAYER_ROTATE_NONE

固定帧率

  • AVI_PLAYER_FIXED_FPS / AVI_PLAYER_FIXED_FPS_VALUE

用于在 LCD 带宽成为瓶颈时限制渲染帧率,降低卡顿概率(仍可能因为过晚而丢帧)。

配置项(Kconfig)

本模块配置在:

  • components/lisa_media_player/av_render/Kconfig

常用选项:

  • AVI_PLAYER_PROFILE_LOG:开启 profiling 日志(用于定位卡顿/饿死/队列策略)

  • AVI_PLAYER_STATS_LOG:开启统计日志

  • AVI_PLAYER_MAX_VIDEO_AHEAD_MS:同步回压窗口

包模型与所有权

av_render 的统一入口是 av_render_push(),使用 av_render_pkt_t 描述一个音频/视频包:

  • type:例如 AV_RENDER_PKT_AUDIO_PCM_S16LEAV_RENDER_PKT_VIDEO_MJPEGAV_RENDER_PKT_VIDEO_RGB565

  • pts_ms:时间戳(毫秒),用于同步与丢帧决策。

  • buf:payload + 释放回调。

所有权规则(很重要):

  • av_render_push() 返回 0:payload 所有权转移给 av_render(由 render/decoder 线程最终释放)。

  • av_render_push() 返回非 0:不会接管 payload,函数内部会通过 free_fn(或默认释放函数)释放。

音频解码(MP3)

当前 av_render 的音频输出统一是 PCM s16le**(写入音频设备)。 当上游推入的是压缩音频(例如 MP3)时,需要由 ``av_render`` 的 **audio decoder 把压缩数据解码成 PCM。

  • 压缩音频包类型:AV_RENDER_PKT_AUDIO_MP3

  • 音频解码器接口:av_render_register_audio_decoder()(运行期注册)

运行期注册(自定义 decoder)

应用可以在推入第一包 MP3 之前注册 decoder:

static int my_mp3_push(av_render_t *r,
           uint8_t *encoded,
           size_t size,
           uint32_t pts_ms,
           av_render_free_fn_t free_fn,
           void *free_user,
           void *user)
{
  /* 1) 解析/缓存 encoded 码流
 * 2) 解码为 PCM s16le
 * 3) 通过 av_render_push() 或内部队列把 PCM 送入音频渲染线程
 * 4) 成功时接管 encoded 并负责释放;失败时不接管,由上层释放
 */
  return 0;
}

static void my_mp3_close(av_render_t *r, void *user)
{
  (void)r;
  (void)user;
}

static const av_render_audio_decoder_ops_t ops = {
  .push = my_mp3_push,
  .close = my_mp3_close,
};

av_render_register_audio_decoder(render, &ops, NULL);

说明:

  • 如果应用注册了 decoder,则优先使用应用 decoder。

  • 如果未注册,则会尝试使用内置 decoder(受 Kconfig 控制)。

内置 MP3 解码(modules/mp3dec)

本工程提供基于 modules/mp3dec 的内置 MP3 解码实现,特点:

  • 支持流式输入(AVI chunk 可能不是按 MP3 帧对齐)。

  • 对 VBR/CBR 均可工作:PCM 的播放节奏以“解码输出样本数 / sample_rate”推进(更接近真实时长)。

启用方式(menuconfig):

  • LISA_MEDIA_PLAYER -> av_render -> Enable MP3 audio decoder (mp3dec) - 对应配置:CONFIG_AVI_PLAYER_AUDIO_MP3=y

可调参数:

  • CONFIG_AVI_PLAYER_MP3_DECODE_QUEUE_DEPTH

  • CONFIG_AVI_PLAYER_MP3_DECODE_THREAD_STACK_SIZE

  • CONFIG_AVI_PLAYER_MP3_DECODE_THREAD_PRIORITY

关键 API(头文件)

  • av_render_open() / av_render_close()

  • av_render_add_audio_stream() / av_render_add_video_stream()

  • av_render_push()

  • 运行期注册: - av_render_register_video_renderer() - av_render_register_audio_renderer() - av_render_register_mjpeg_decoder()

提示:如果需要做 OSD/叠加,推荐用 av_render_register_video_renderer() 包一层,然后调用 av_render_default_video_write_rgb565() 复用内置 LCD 输出(包含 rotate_mode)。