avi_player

概览

avi_player 提供 AVI(RIFF)容器的 demux 能力:

  • 从数据源(本地文件或 HTTP Range)读取字节流。

  • 解析 header/idx1/movi chunk,识别音频/视频流。

  • 生成(或修正)音频/视频 PTS(毫秒)。

  • 以 push 方式把音频/视频包送入 av_render

当前支持的典型组合:

  • 视频:MJPEG

  • 音频:PCM s16le(WAVE_FORMAT_PCM / AVI_WAVE_FORMAT_PCM)或 MP3(WAVE_FORMAT_MPEGLAYER3 / AVI_WAVE_FORMAT_MPEGLAYER3,由 av_render 侧解码为 PCM)

职责与边界

avi_player 负责:

  • 解析 AVI/RIFF 结构(header/idx1/movi),识别音频/视频流。

  • 从数据源读取并 seek(本地文件或 HTTP Range)。

  • 为音频/视频 chunk 生成/修正 PTS(毫秒)。

  • 以 push 方式把数据包送入 av_render(不做解码/渲染)。

它不负责:

  • MJPEG 解码、RGB565 转换、LCD 输出。

  • 音视频同步策略(由 av_render 决定)。

数据流

典型播放路径如下(简化):

  1. open 数据源(LSFS/SD 或 HTTP Range)

  2. 解析 AVI header,拿到帧率、音频格式等元信息

  3. 循环读取 movi 中的 chunk:

    • 视频 chunk:封装为视频包(包含 PTS、codec、payload)并调用 av_render_push(...)

    • 音频 chunk:根据编码类型自动选择包类型并调用 av_render_push(...)

      • PCM:AV_RENDER_PKT_AUDIO_PCM_S16LE

      • MP3:AV_RENDER_PKT_AUDIO_MP3(需要 av_render 的 MP3 解码器:内置或运行期注册)

  4. 结束或出错后关闭数据源,通知 render 侧退出

流程图(demux → push)

+-----------------------+
|      avi_player       |
|  RIFF/AVI demux loop  |
+-----------+-----------+
      |
      | avi_io_read()/avi_io_seek()
      v
 +-------------------+
 |   AVI headers      |
 |  avih/strh/strf... |
 +-------------------+
      |
      v
 +-------------------+
 |   movi chunks      |
 |  00dc / 01wb ...   |
 +--------+----------+
      |
   +------+------+
   |             |
   v             v
video pkt       audio pkt
(MJPEG)         (PCM s16)
   |             |
   +------v------+
      |
   av_render_push()
      |
      v
    +--------+
    |av_render|
    +--------+

数据源(avi_io)

avi_player 通过 avi_io 抽象屏蔽底层来源差异:

  • 本地文件:通常走 LSFS/FATFS(如 /SD:/default.avi)。

  • HTTP Range:通过 Range: bytes=...- 重连实现 seek,适合“可随机访问”的远端 AVI。

avi_io 接口很小(见 avi_io.h):

  • read:顺序读取

  • seek:支持 AVI_IO_SEEK_SETAVI_IO_SEEK_CUR

  • close:关闭

示例中提供了两个 opener:

  • avi_io_open_lsfs():本地文件(LSFS/FATFS)

  • avi_io_open_http_range():HTTP Range(需启用 Kconfig)

启用 HTTP Range

需要在 menuconfig 打开:

  • LISA_MEDIA_PLAYER

  • AVI_PLAYER_HTTP_RANGE

并配置:

  • AVI_PLAYER_HTTP_URL

  • AVI_PLAYER_WIFI_SSID / AVI_PLAYER_WIFI_PWD

依赖:

  • httpclient 模块(Kconfig 中通过 select MODULE_HTTPCLIENT

  • WiFi 管理(Kconfig 中通过 select WIFI_MANAGER

注意:HTTP Range 仅影响数据源部分;demux/PTS/push 逻辑保持一致。

使用 FFmpeg 生成 MJPEG + MP3 音频的 AVI

下面命令示例与现有工程实践一致(320x240 + 顺时针旋转 90° + 20fps + MP3 64kbps):

ffmpeg -i Unit1.MyFamily.mp4 \
   -vf "scale=320:240:force_original_aspect_ratio=decrease,pad=320:240:(ow-iw)/2:(oh-ih)/2,transpose=1" \
   -r 20 \
   -c:v mjpeg -q:v 2 \
   -ar 16000 -ac 1 -c:a mp3 -b:a 64k \
   -y default.avi

提示:

  • 如果你希望继续用 PCM 音频,把 -c:a mp3 -b:a 64k 改为 -c:a pcm_s16le

  • 如果素材侧已预旋转为屏幕方向,则不需要 transpose,并建议在 av_render 里选择不旋转。

典型用法(示例工程)

示例工程在 samples/media/avi_player,通常会:

  • 初始化显示/音频设备

  • av_render_open(...) 创建渲染实例

  • avi_player_play_file("/SD:/default.avi", render, 0)avi_player_play_url(url, render, 0)

max_frames 参数:

  • 0:不限帧数(一直播放直到结束/出错)

  • >0:播放指定帧数后退出(用于压力测试/定位问题)

最小伪代码(以源码为准):

av_render_cfg_t cfg = {
   .display_dev = (void *)lcd,
   .audio_dev = (void *)audio,
   .sync_mode = AV_RENDER_SYNC_AUDIO,
};
av_render_t *render = NULL;
av_render_open(&cfg, &render);

/* Local file */
avi_player_play_file("/SD:/default.avi", render, 0);

av_render_close(render);

具体函数名以源码为准;若你已经把视频源预先旋转为屏幕方向,建议在 av_render 侧选择 AVI_PLAYER_ROTATE_NONE

播放控制(暂停/继续/停止)

如果你需要在播放过程中控制暂停/继续/停止,可以使用 avi_player_ctrl_t 并调用 *_ex 版本:

avi_player_ctrl_t *ctrl = avi_player_ctrl_create();

/* 通常在独立线程/任务里调用(该函数为阻塞式) */
int ret = avi_player_play_file_ex("/SD:/default.avi", render, 0, ctrl);
/* ret == -ECANCELED 表示外部请求了 stop */

/* 另一个线程里: */
avi_player_pause(ctrl);
avi_player_resume(ctrl);
avi_player_stop(ctrl);

avi_player_ctrl_destroy(ctrl);

说明:pause/stop 为“协作式”生效点,主要在 demux/push 的循环边界处检查。

配置项(Kconfig)

本模块配置在:

  • components/lisa_media_player/avi_player/Kconfig

常用选项:

  • AVI_PLAYER_HTTP_RANGE:启用 HTTP Range 数据源

  • AVI_PLAYER_MJPEG_POOL / AVI_PLAYER_AUDIO_POOL:启用包缓冲池(降低 malloc 压力)