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决定)。
数据流
典型播放路径如下(简化):
open 数据源(LSFS/SD 或 HTTP Range)
解析 AVI header,拿到帧率、音频格式等元信息
循环读取
movi中的 chunk:视频 chunk:封装为视频包(包含 PTS、codec、payload)并调用
av_render_push(...)音频 chunk:根据编码类型自动选择包类型并调用
av_render_push(...):PCM:
AV_RENDER_PKT_AUDIO_PCM_S16LEMP3:
AV_RENDER_PKT_AUDIO_MP3(需要av_render的 MP3 解码器:内置或运行期注册)
结束或出错后关闭数据源,通知 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_SET与AVI_IO_SEEK_CURclose:关闭
示例中提供了两个 opener:
avi_io_open_lsfs():本地文件(LSFS/FATFS)avi_io_open_http_range():HTTP Range(需启用 Kconfig)
启用 HTTP Range
需要在 menuconfig 打开:
LISA_MEDIA_PLAYERAVI_PLAYER_HTTP_RANGE
并配置:
AVI_PLAYER_HTTP_URLAVI_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 压力)