MPP使用指南
1. 模块介绍
1.1. 术语定义
术语 | 定义 | 注释说明 |
---|---|---|
VE | Video Engine | 视频加速引擎 |
MPP | Media Process Platform | 通用多媒体处理软件平台 |
packet | video bitstream packet | 一帧视频或图片码流数据 |
frame | frame | 一帧解码后的视频或图片数据 |
1.2. 模块简介
MPP(Media Process Platform)是 Artinchip 自主研发的通用多媒体处理软件平台,适用于 Artinchip 芯片系列。支持在 Linux 平台上运行, 屏蔽了Artinchip不同芯片平台多种多媒体硬件模块(VE、GE 等)版本的差异,为使用者提供简单易用的多媒体处理 API,支持多种多媒体解决方案。
目前支持硬件模块包括:
- VE:视频、图片编解码功能
- GE:2D图形加速
MPP在系统架构的层次图如下图:
图 6.43 MPP系统框架层次图
-
-
硬件层 Hardware
硬件层是 Artinchip 系列芯片平台的多媒体硬件加速引擎 VE/GE。VE 模块硬件层接口请参考芯片用户手册 VE 模块GE 模块硬件层接口请参考芯片用户手册 GE 模块
-
-
-
MPP 用户空间层
MPP 用户态程序,包括 视频解码 mpp_decoder、视频编码 mpp_encoder、2D图像处理 mpp_ge 等功能模块。
-
-
-
应用层
分为两部分:提供简易播放器应用 mpp_player; 对接第三方开源库 openmax、gstreamer、lvgl等。
-
2. 参数配置
2.1. 内核配置
MPP 依赖 VE 驱动,MPP Heap 内存管理框架,请在 MPP 编译前进行配置。
2.1.1. MPP Heap
ArtInChip 平台使用 DMA-BUF 来实现多媒体模块间的 buffer 共享。对于 DMA-BUF,Kernel 提供 System Heap 和 CMA Heap 两个 exporter。 前者直接申请匿名页面,后者则在 CMA 内存中申请页面。
虽然通过这两个 exporter 都能拿到足够的物理连续内存,但它们在系统长时间运行后,都面临内存碎片化问题。特别是在 64 M 小内存方案中,碎片化问题尤为严重。
为了 解决内存碎片化 的问题,ArtInChip 基于 CMA Heap 进行封装,提供一个私有的 exporter ,即 MPP Heap 。
在 luban 根目录下执行 make kernel-menuconfig,进入 kernel 的功能配置,按如下选择:
Linux
Device Drivers --->
DMABUF options --->
[*] Explicit Synchronization Framework
[*] Sync File Validation Framework
[*] userspace dmabuf misc driver
[ ] Move notify between drivers (EXPERIMENTAL)
< > Selftests for the dma-buf interfaces
[*] DMA-BUF Userland Memory Heaps --->
[ ] DMA-BUF System Heap
[*] DMA-BUF CMA Heap
[*] DMA-BUF MPP Heap
MPP Heap 在 CMA 内存中申请一大块物理连续内存,使用 genpool 算法 进行管理,提供给 MPP 中间件专用,其大小配置:
Linux
Library routines --->
[*] DMA Contiguous Memory Allocator
[ ] Enable separate DMA Contiguous Memory Area for each NUMA Node
*** Default contiguous memory area size: ***
(16) Size in Mega Bytes
(8) Size in Mega Bytes for MPP Heap
在上述例子中,系统预留了 16M CMA 内存,MPP Heap 再从 16M CMA 内存中申请 8M。
因为 MPP Heap 的内存 是从 CMA 中申请的,前者是后者的子集,所以 MPP Heap 的 size 要小于 CMA 的 size。
注解
为了兼顾系统中其他需要物理连续内存的模块,不能将 CMA 内存全部分配给 MPP Heap,分配数值设置可参考 mpp heap 设置。
2.1.2. VE 驱动
VE 模块驱动配置请参考 VE 使用指南 内核配置
2.2. DTS 参数配置
VE 模块 DTS 配置请参考 VE 使用指南 内核配置
3. 调试指南
3.1. 调试开关
3.1.1. MPP调试
MPP调试log等级分为ERROR, WARNING, INFO, DEBUG, VERBOSE。通过LOGL_DEFAULT定义MPP全局的log等级。 默认log等级为INFO。
源文件路径:aic-mpp/base/log.h
enum log_level {
LOGL_ERROR = 0,
LOGL_WARNING,
LOGL_INFO,
LOGL_DEBUG,
LOGL_VERBOSE,
LOGL_COUNT,
LOGL_DEFAULT = LOGL_INFO,
LOGL_FORCE_DEBUG = 0x10,
};
3.1.2. 子模块调试
打开子模块调试log方式,在子模块中添加:
#define LOG_DEBUG
3.1.3. MPP Heap 调试
打开 MPP Heap 调试开关,可查看 MPP 中间件对预留内存的使用情况。
在 luban 根目录下执行 make kernel-menuconfig,进入 kernel 的功能配置,按如下选择:
Linux
Memory Management options --->
[*] MPP debugfs interface
系统启动后挂载 debugfs
mount -t debugfs none /sys/kernel/debug/
通过查 看 mpp 目录下的节点,即可获取 mpp heap 的内存使用情况
# cd /sys/kernel/dedbug/mpp/
# ls
bitmap count maxchunk used
# cat count
2048
# cat used
600
# cat maxchunk
1448
# cat bitmap
4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 4294967295
4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 4294967295 16777215 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
- count : MPP Heap 中包含的 page 总数
- used : MPP Heap 中已被申请的 page 总数
- maxchunk : MPP Heap 中还能申请到最大的连续 page 总数
- bitmap : MPP Heap 中记录 page 状态的 bitmap
一个 page 大小为 4K , bitmap 节点打印输出十进制数据。
4294967295 转换为十六进制为 0xFFFFFFFF
, 二进制为 0b11111111111111111111111111111111
。 一个 bit 表示一个 page 的状态,1 表示已被申请,0 表示空闲。4294967295 表示 32 个 page 全部被占用,总共 32 * 4K 大小。
小技巧
实际调试时要注意大小端问题。
4. 测试指南
4.1. 运行测试用例
4.1.1. mpp_test
mpp_test 的主要功能是测试 mpp_decoder 接口,解码视频或图片文件并通过 display 接口显示在屏幕上。
目前支持 h264, jpg,png 的解码和显示,使用方式如下:
[aic@] # mpp_test -h
Usage: mpp_test [options]:
-i input stream file name
-t directory of test files
-d display the picture
-c enable compare output data
-f output pixel format
-l loop time
-s save output data
-h help
Example1(test single file): mpp_test -i test.264
Example2(test some files) : mpp_test -t /usr/data/
4.1.2. pic_test
pic_test 的主要功能是测试 JPEG/PNG 图片解码和显示,相比于 mpp_test 代码更加精简。
[aic@] # pic_test -h
Usage: dec_test [options]
Options:
-i input stream file name
-h help
5. 设计说明
5.1. 源码说明
在 luban 的根目录下通过 make menuconfig 打开 aic-mpp,并进行编译。
Artinchip packages --->
[*] aic-mpp
源文件目录:
aic-mpp$ tree
.
├── base // 公共模块:包括内存分配和链表等基础功能
│ ├── memory
├── ge // 2D 图形加速模块
├── ve // 编解码器模块
| ├── include // ve 模块头文件
│ ├── common // 编解码器公共组件
| ├── decoder
│ ├── h264 // h.264 解码模块
│ ├── jpeg // jpg 解码模块
│ └── png // png 解码模块
├── include // mpp 对外头文件
├── mpp_test // mpp 测试用例
5.2. 模块架构
5.2.1. MPP 软件框架
mpp 软件框图如下所示:
图 6.44 mpp 软件框架¶
分为三个部分:
- mpp_decoder,实现h264、jpeg、png等解码功能
- mpp_encoder,实现jpeg编码(功能还未完成)。
- mpp_ge,实现2D图形加速功能
5.3. mpp_decoder 设计以及接口说明
mpp_decoder 由三个主要模块组成:
- 解码模块(H264、JPEG、PNG等):负责将码流数据解码成视频图像
- 输入码流数据管理模块(Packet manager):负责视频、图片码流数据和 buffer 的管理
- 显示帧管理模块(Frame manager):负责解码图像 buffer 的管理
5.3.1. packet 管理机制
Packet manager 负责管理码流数据和 buffer。初始化时,该模块申请一块物理连续的内存(buffer大小可由外部配置),用于存放视频/图片码流数据。
Packet manager 管理的数据单元为 packet,packet 表示一笔码流数据,它可以是完整的一帧数据,也支持不是完整的一帧数据。 每个 packet 与物理内存中的码流数据一一对应,它记录了每一笔码流的物理内存基地址、物理内存结束地址、物理内存偏移、虚拟内存地址、码流数据长度等信息。
图 6.45 packet管理
packet 通过 empty list 和 ready list 两个链表进行管理。 其中,empty list 用于存放空闲的 packet,ready list 用于存放待解码的 packet。
送码流数据时,从 empty list 获取一个空闲 packet,填充数据后,再把 packet 放入 ready list;
解码前,解码器从 ready list 获取一个填充数据的 packet,使用完后再把该 packet 放入 empty list。
图 6.46 packet manager 调用流程
5.3.2. frame 管理机制
Frame manager 负责管理图像 buffer。Frame manager 内部通过两个链表来管理图像 buffer:empty list 和 render list。 其中,empty list 存放可以给解码输出使用的图像 buffer,render list 存放解码完成但还未显示的图像 buffer。 在运行过程中,正在显示的图像 buffer 和用于参考的图像 buffer 可能不在这两个 list 中。
- frame 状态迁移
初始化时,该模块申请指定个数的图像 buffer(个数可由外部配置),每个图像 buffer 的信息存放在内部数组中。 每个图像 buffer 有4种状态:
- Decoding: 该帧正在被解码器使用(用于解码输出或作为参考帧)
- wait_render: 该帧在 render list 中,等待显示
- Rendering: 该帧正在被显示占用
- IDLE: 该帧处于空闲状态(既没有被显示占用,也没有被解码器用作参考帧)
其状态转移如下图所示:
- 初始化时,所有图像 buffer 都在 empty list 中,此时处于 IDLE 状态;
- 解码模块从 empty list 链表头部获取一个空图像 buffer,此时 buffer 被解码模块占用,从 IDLE 状态变为 Decoding 状态;
- 解码完成后,解码模块还图像数据。此时分两种情况:
- 1)如果当前帧还未被显示,该帧加入 render list 链表尾部,从 Decoding 状态变为 wait render 状态;
- 2)该帧不再用做参考帧且已显示完成,此时该帧加入 empty list 链表尾部,由 Decoding 状态进 入 IDLE 状态;
- 显示模块从 render list 链表头部取一帧图像,此时当前帧由 wait render 状态进入 Rendering 状态;
- 显示模块还图像 buffer,分两种情况:
- 1)如果当前帧不用于参考,此时由 Rendering 状态回到IDLE状态,该帧加入 empty list 链表尾部;
- 2)如果当前帧用于参考,此时由 Rendering 状态进入Decoding状态,该图像 buffer 不进入任何队列,等待解码器还参考帧;
图 6.47 frame状态迁移
- frame manager 调用流程
对于 JPEG、PNG 这类没有参考帧概念的编码格式,每一帧的状态是唯一的,解码后的数据帧可直接送 render list
图 6.48 frame manager 调用流程(JPEG/PNG))
但对于 H264 这类有参考帧的编码格式,解码后的视频帧可能既被显示占用也会被解码器用作参考帧,并且由于双向参考帧的存在, 视频帧需要重排序后才能送显示。 不同于JPG,H264 解码库内部存在一个 delay list 用于为显示帧重排序。
图 6.49 frame manager 调用流程(H264)
5.3.3. 物理连续内存使用 情况
H264 解码所需的物理连续内存如下所示:
内存占用模块 | 计算方式 | 说明 |
---|---|---|
输入码流 | 大小由应用层配置 | |
输出帧 | widthheight3/2*frame_num | frame_num至少需要(参考帧个数+1)个显示占用个数可由应用层通过struct decode_config结构体中的extra_frame_num 配置 |
帧内预测(需要上一行数据) | 帧格式:width2MBAFF:width4 | |
宏块信息 | 固定12K | |
dblk模块(上一个宏块行最后4行数据) | 帧格式:width8MBAFF:width16 | |
co-located信息 | 固定68K | |
每一帧co-located数据缓存 | (width/16)*(height/16)32frame_num |
注解
co-located 两个buffer,I、P帧解码时会往buffer里写数据,B 帧解码时从buffer读数据。 如果当前码流中没有 B 帧,这两块内存也需要申请。
5.3.4. mpp_decoder 调用流程
在调用 mpp_decoder 的解码函数时,解码模块从 Packet manager 取一笔码流,同时从 Frame maneger 取一个空闲图像 buffer,对码流进行解码 并输出图像到图像 buffer。
解码后,解码模块将码流 buffer 归还 Packet manager,将解码图像 buffer 归还 Frame maneger。
为保证解码效率,建议调用者创建3个线程实现解码功能 :
-
-
send data thread
通过 mpp_decoder_get_packet 和 mpp_decoder_put_packet 这两个接口把码流数据送到 packet 管理模块
-
-
-
decode thread
通过调用 mpp_decoder_decode 控制解码,解码库从 packet 管理模块取一笔码流数据,解码完成后,将视频帧送入 frame 管理模块
-
-
-
render thread
通过 mpp_decoder_get_frame 和 mpp_decoder_put_frame 两个接口从 frame 管理模块获取视频帧,并控制该帧显示时机
-
图 6.50 mpp_decoder 调用流程
5.3.5. mpp_decoder 数据结构
5.3.5.1. struct decode_config
struct decode_config {
enum mpp_pixel_format pix_fmt; // output pixel format
int bitstream_buffer_size; // bitstream buffer size in pm
int packet_count; // packet number in pm
int extra_frame_num; // extra frame number in fm
};
decode_config 结构体用于配置解码器初始化使用的参数。
- pix_fmt 表示解码输出的颜色格式
- bitstream_buffer_size 表示存放输入码流缓存的总长度
- packet_count 表示 packet manager 中 packet 的最大个数
- extra_frame_num 表示解码器额外分配的帧个数,主要用于缓存显示帧以保证显示平滑。
5.3.5.2. struct mpp_packet
struct mpp_packet {
void *data;
int size;
long long pts;
unsigned int flag;
};
mpp_packet 结构体用于表示输入码流信息。
- data 表示码流数据存放的起始地址
- size 表示该笔码流数据长度
- pts 表示该笔码流的时间戳
- flag 表示该笔码流的标记位,目前仅用于确定该码流是否为最后一笔码流(PACKET_FLAG_EOS)
5.3.5.3. struct mpp_frame
struct mpp_size {
int width;
int height;
};
struct mpp_rect {
int x;
int y;
int width;
int height;
};
enum mpp_buf_type {
MPP_DMA_BUF_FD,
MPP_PHY_ADDR,
};
struct mpp_buf {
enum mpp_buf_type buf_type;
union {
int fd[3];
unsigned int phy_addr[3];
};
unsigned int stride[3];
struct mpp_size size;
unsigned int crop_en;
struct mpp_rect crop;
enum mpp_pixel_format format;
unsigned int flags;
};
- buf_type:表示 mpp_buf 类型,以 fd 方式 MPP_DMA_BUF_FD 或 以物理地址方式 MPP_PHY_ADDR;
- fd[3] :表示 buffer 三个分量的 fd
- phy_addr[3]:表示 buffer 三个分量的物理地址
- stride[3]:表示 buffer 三个分量的 stride
- size:表示 buffer 的宽、高
- crop_en: 表示该 buffer 是否需要 crop
- crop:表示该 buffer 的 crop 信息
- format: 表示该 buffer 的颜色格式类型
struct mpp_frame {
struct mpp_buf buf;
long long pts;
unsigned int id;
unsigned int flags;
};
- buf:表示 mpp_frame 的 buffer 信息
- pts:表示 mpp_frame 的时间戳
- id:表示 mpp_frame 的唯一标识
- flags:表示 mpp_frame 的标志位
5.3.5.4. enum mpp_dec_errno
enum mpp_dec_errno {
DEC_ERR_NOT_SUPPORT = 0x90000001,
DEC_ERR_NO_EMPTY_PACKET = 0x90000002, // no packet in empty list
DEC_ERR_NO_READY_PACKET = 0x90000003, //
DEC_ERR_NO_EMPTY_FRAME = 0x90000004, //
DEC_ERR_NO_RENDER_FRAME = 0x90000005, //
DEC_ERR_NULL_PTR = 0x90000006,
DEC_ERR_FM_NOT_CREATE = 0x90000006,
};