Rockchip 平台多媒体概览
MPP:媒体处理平台是 Rockchip 平台的视频编解码器解析器和硬件抽象层库。
您可以从 git 获取 mpp 源代码。
git clone -b release https://github.com/rockchip-linux/mpp.git
如果您使用的是 Debian 关系发行版,源代码中有一个 debian 构建规则。 您可以查看 debian 目录以获取将来的信息。
您可以使用以下命令构建 deb 包,支持的架构有:armhf、arm64。
DEB_BUILD_OPTIONS="parallel=4 nocheck" dpkg-buildpackage -a
请先安装所有要求的交叉编译工具和 Debian 软件包工具。
您可以使用以下命令在板中构建它:
cmake -DRKPLATFORM=ON -DHAVE_DRM=ON && 制作
构建Android需要android ndk包,通常我们使用android-ndk-r10d。
您可以从 google 网站下载 ndk 并在 make-Android.bash 中修改 ndk 路径。
自动路径是 /home/pub/ndk/android-ndk-r10d。
然后,执行 build/andorid/xx/make-Android.bash。
第一次执行可能会出现一些错误,重新执行make-Android.bash的problem就可以解决了。
执行 build/xx-x86_64/build-all.bat 和 make-solutions.bat
+---------------------------------------+
| |
| OpenMax / libva |
| |
+---------------------------------------+
+-------------------- MPP ----------------------+
| |
| +-------------------------+ +--------+ |
| | | | | |
| | MPI / MPP | | | |
| | buffer queue manage | | | |
| | | | | |
| +-------------------------+ | | |
| | | |
| +-------------------------+ | | |
| | | | | |
| | codec | | OSAL | |
| | decoder / encoder | | | |
| | | | | |
| +-------------------------+ | | |
| | | |
| +-----------+ +-----------+ | | |
| | | | | | | |
| | parser | | HAL | | | |
| | control | | reg_gen | | | |
| | | | | | | |
| +-----------+ +-----------+ +--------| |
| |
+-------------------- MPP ----------------------+
+---------------------------------------+
| |
| kernel |
| RK vcodec_service / v4l2 |
| |
+---------------------------------------+
mpp 是 Rockchip SoC 跨平台媒体处理的中间件库。
mpp 的主要目的是提供非常高性能、高灵活性 以及多媒体(主要是视频和图像)过程的可扩展性。
mpp的设计目标是连接不同的Rockchip硬件内核驱动和不同的用户空间应用程序。
Rockchip 有两套硬件内核驱动。
第一个是 vcodec_service/vpu_service/mpp_service ,这是一个高 性能无状态框架基础硬件内核驱动程序。 该驱动支持 硬件可以提供的所有可用编解码器。 此驱动程序用于 Android/ Linux。
第二个是为 ChromeOS 开发的 v4l2 驱动程序。 它目前支持 H.264/H.265/vp8/vp9。 该驱动程序用于 ChomeOS/Linux。
Mpp 计划支持多种用户空间应用程序,包括 OpenMax、libva。
1. 跨平台
目标操作系统平台包括Android、Linux、ChromeOS和windows。 Mpp用途 cmake 在不同的平台上编译。
2. 高性能
Mpp支持sync/async接口,减少接口阻塞的时间。 和 mpp 在内部使硬件和软件并行运行。 当硬件 运行软件将同时准备下一个硬件任务。
3. 高灵活性
mpi(媒体处理接口)很容易通过不同的控制功能进行扩展。 输入/输出元素 packet/frame/buffer 易于扩展不同 组件。
该模块遮蔽了不同操作系统之间的差异,并提供基本组件,包括内存、时间、线程、日志和硬件内存分配器。
该模块负责与外部用户的交互。 Mpi层有两个用户的方式。 简单的方法 - 用户可以使用 put/get packet/frame 函数集。 高级方式——用户必须配置 MppTask 并使用 dequeue/enqueue 功能 设置为与 mpp 通信。 MppTask可以携带不同的元数据并完成复杂的工作。
该模块实现了高效的内部工作流程。 编解码器模块提供不同视频格式的通用呼叫流程。 软件过程将 与硬件指定的进程分开。 该软件将与具有通用任务接口的硬件,它结合了缓冲区信息和编解码器指定的信息。
该层提供不同视频格式的实现函数调用和 不同的硬件。 为解码器解析器提供视频流解析功能 并将格式相关的语法结构输出到hal。 hal 将转换在不同硬件上注册集的语法结构。 当前 hal 支持 vcodec_service 内核驱动并计划稍后支持 v4l2 驱动。
瑞芯微有两套硬件内核驱动程序。
第一个是 vcodec_service/vpu_service/mpp_service,它是一个高性能的无状态帧基础硬件内核驱动程序。此驱动程序支持硬件可以提供的所有可用编解码器。此驱动程序在 Android/Linux 上使用。
下面是vcodec_service内核驱动程序框架图。
+-------------+ +-------------+ +-------------+
| client A | | client B | | client C |
+-------------+ +-------------+ +-------------+
userspace
+------------------------------------------------------------------------------+
kernel
+-------------+ +-------------+ +-------------+
| session A | | session B | | session C |
+-------------+ +-------------+ +-------------+
| | | | | |
waiting done waiting done waiting done
| | | | | |
+---+---+ +---+---+ +---+---+ +---+---+ +---+---+
| task3 | | task0 | | task1 | | task0 | | task0 |
+---+---+ +-------+ +-------+ +-------+ +---+---+
| |
+---+---+ +---+---+
| task2 | | task1 |
+-------+ +-------+
+-----------+
| service |
+---------+-----------+---------+
| | |
waiting running done
| | |
+-----+-----+ +-----+-----+ +-----+-----+
| task A2 | | task A1 | | task C0 |
+-----+-----+ +-----------+ +-----+-----+
| |
+-----+-----+ +-----+-----+
| task B1 | | task C1 |
+-----+-----+ +-----+-----+
| |
+-----+-----+ +-----+-----+
| task A3 | | task A0 |
+-----------+ +-----+-----+
|
+-----+-----+
| task B0 |
+-----------+
此设计的原则是将用户任务处理和硬件资源管理分开,并最大限度地减少两个硬件进程操作之间的内核串行处理时间。
驱动程序使用会话作为通信通道。每个用户空间客户端(客户端)都有一个内核会话。客户端会将任务提交到会话。然后,硬件由服务 (vpu_service/vcodec_service) 管理。服务将提供在会话中处理任务的能力。
当客户端将任务提交到内核时,该任务将设置为等待状态,并链接到会话等待列表和服务等待列表。然后服务将任务从等待列表获取到运行列表并运行。当硬件完成任务时,任务将被移动到完成列表,并放入服务完成列表和会话完成列表。最后,客户端将从会话中获取完成的任务。
Mpp 缓冲器是硬件使用的缓冲区的扭曲器。硬件通常不能通过 CPU 使用缓冲区 malloc。然后,我们为不同平台上的不同内存分配器设计MppBuffer。目前,它是为Android上的离子缓冲区和Linux上的drm缓冲区设计的。稍后可能会支持 v4l2 设备中的vb2_buffer。
为了管理不同用户mpp缓冲区模块中的缓冲区使用情况,引入了MppBufferGroup作为bufffer管理器。所有 MppBuffer 都将连接到其管理器。MppBufferGroup 提供分配器服务和缓冲区重用功能。
不同的 MppBufferGroup 具有不同的分配器。除了正常的malloc/free函数外,分配器还可以接受来自外部文件描述符的缓冲区。
MppBufferGroup 有两个列表,未使用的缓冲区列表和已使用的缓冲区列表。当缓冲区空闲时,缓冲区不会立即释放。缓冲区将移动到未使用的列表以供以后重用。这样做是有充分理由的。当视频分辨率达到4K时,缓冲区大小将高于12M。分配缓冲区并为其生成映射表需要很长时间。因此,重用缓冲区将节省分配/空闲时间。
下面是 Mpp 缓冲区状态事务的示意图。
+----------+ +---------+
| create | | commit |
+-----+----+ +----+----+
| |
| |
| +----v----+
+----------+ unused <-----------+
| +---------+ |
| | | |
| | | |
+-----v----+ | | +-----+----+
| malloc | | | | free |
+----------+ | | +----------+
| inc_ref | +---------+ | dec_ref |
+-----+----+ +-----^----+
| |
| |
| +---------+ |
+----------> used +-----------+
+---------+
| |
| |
| |
| |
| |
+----^----+
|
|
+----+----+
| import |
+---------+
Mpp 任务是在高级模式下与外部用户进行事务的包含组件。高级模式的目标是为扩展提供灵活的多输入/输出内容。
Mpp任务mpp_meta作为丰富的内容载体。Mpp 元使用键和值对进行扩展。一个任务可以将多个数据传入或传出 mpp。
典型的情况是具有OSD和运动检测功能的编码器。一个任务可能包含 OSD 缓冲区、运动检测缓冲区、帧缓冲区和流缓冲区作为输入和输出流缓冲区以及带有数据的运动检测缓冲区。如果解码器想要输出一些端信息,这种情况也可以在解码器上使用。
1. MPP 任务队列
Mpp 任务队列是任务的管理器。由于用户可能错误地使用任务,我们选择将所有任务保存在mpp内的设计。任务队列将创建和释放任务。但任务队列不会直接与用户交互。我们使用端口和任务状态来控制事务。
2. MPP 端口
Mpp 端口是任务队列的事务接口。外部用户和内部工作线程将使用mpp_port_poll/mpp_port_dequeue/mpp_port_enqueue接口轮询/取消排队/排队任务任务队列。Mpp 高级模式使用端口连接外部用户、接口存储和内部进程线程。每个任务队列有两个端口:输入端口和输出端口。从全局视图来看,任务将始终从一个输入端口流向另一个输出端口。
3. MPP 任务状态
一个任务有四种状态。Mpp 使用list_head来表示状态。
INPUT_PORT:输入端口用户取消排队的初始状态。或者,当输出端口成功将任务排队时,任务将处于此状态。
INPUT_HOLD:当输入端口用户成功取消任务排队时,任务将处于此状态。
OUTPUT_PORT:当输入端口用户成功将任务排队时,任务将处于OUTPUT_PORT状态。此任务已准备好从输出端口取消排队。
OUTPUT_HOLD:当输出端口用户成功取消任务排队时,任务将处于此状态。
4. MPP 任务/端口事务
端口上有三个事务函数:轮询/取消排队/排队。当端口用户调用事务功能时,任务将从一种状态转移到另一种状态。状态转换流从输入端口到输出端口是单向的。
任务/端口/状态的整体关系图如下所示。
1. task queue
+------------------------------+
| |
+----+----+ +--------------+ +----+----+
| 4 | | 3 | | 2.1 |
+--------+ dequeue <--+ status <--+ enqueue <---------+
| | | | INPUT_PORT | | | |
| +---------+ | | +---------+ |
+------v-----+ | | +--------------+ | | +------+------+
| 3 | | 2 | | 2 | | 3 |
| status | | input | | output | | status |
| INPUT_HOLD | | port | | port | | OUTPUT_HOLD |
| | | | | | | |
+------+-----+ | | +--------------+ | | +------^------+
| +---------+ | 3 | +---------+ |
| | 4 | | status | | 2.1 | |
+--------> enqueue +--> INPUT_PORT +--> dequeue +---------+
| | | | | |
+----+----+ +--------------+ -----+----+
| |
+------------------------------+
在高级模式下,mpp 使用两个任务队列:输入任务队列和输出任务队列。
输入任务队列将输入端外部用户连接到内部工作线程。输出任务队列将内部工作线程连接到输出端外部用户。然后,将有三个线程来并行化内部进程和外部转换。这将最大限度地提高 mpp 效率。
工作流程如下图所示。
+-------------------+ +-------------------+
| input side user | | output side user |
+----^---------+----+ +----^---------+----+
| | | |
+----+----+----v----+ +----+----+----v----+
| dequeue | enqueue | | dequeue | enqueue |
+----^----+----+----+ +----^----+----+----+
| | | |
+----+---------+----+ MPP +----+---------v----+
+---+ input port +-----------+ output port +---+
| +-------------------+ +-------------------+ |
| | input task queue | | output task queue | |
| +-------------------+ +-------------------+ |
| | output port | | input port | |
| +----+---------^----+ +----+---------^----+ |
| | | | | |
| +----v----+----+----+ +----v----+----+----+ |
| | dequeue | enqueue | | dequeue | enqueue | |
| +----+----+----^----+ +----+----+----^----+ |
| | | | | |
| +----v---------+---------------------v---------+----+ |
| | internal work thread | |
| +---------------------------------------------------+ |
| |
+-----------------------------------------------------------+
Mpp 不直接提供线程 API,因此您应该使用相应的操作系统接口。
关于多线程编码/解码,必须做一些事情。
1.资源应申请多份而不是一份,避免资源竞争
2.资源释放的时间不同,释放资源应在一个frmae或数据包结束使用后完成。
有关多线程处理的特定详细信息,请参阅测试/mpi_enc_test和测试/mpi_dec_test。
所有cmd都在Inc/rk_mpi_cmd.h中定义
通常cmd在mpp_create和mpp_init之后设置,但工作模式应在mpp_init之前设置。您需要指示工作模式(单或高级)以帮助MPP选择线程。
typedef enum {
MPP_OSAL_CMD_BASE = CMD_MODULE_OSAL,
MPP_OSAL_CMD_END,
MPP_CMD_BASE = CMD_MODULE_MPP,
MPP_ENABLE_DEINTERLACE,
MPP_SET_INPUT_BLOCK,
MPP_SET_OUTPUT_BLOCK,
MPP_CMD_END,
MPP_CODEC_CMD_BASE = CMD_MODULE_CODEC,
MPP_CODEC_GET_FRAME_INFO,
MPP_CODEC_CMD_END,
MPP_DEC_CMD_BASE = CMD_MODULE_CODEC | CMD_CTX_ID_DEC,
MPP_DEC_SET_FRAME_INFO, /* vpu api legacy control for buffer slot dimension init */
MPP_DEC_SET_EXT_BUF_GROUP, /* IMPORTANT: set external buffer group to mpp decoder */
MPP_DEC_SET_INFO_CHANGE_READY,
MPP_DEC_SET_INTERNAL_PTS_ENABLE,
MPP_DEC_SET_PARSER_SPLIT_MODE, /* Need to setup before init */
MPP_DEC_SET_PARSER_FAST_MODE, /* Need to setup before init */
MPP_DEC_GET_STREAM_COUNT,
MPP_DEC_GET_VPUMEM_USED_COUNT,
MPP_DEC_SET_VC1_EXTRA_DATA,
MPP_DEC_SET_OUTPUT_FORMAT,
MPP_DEC_CMD_END,
MPP_ENC_CMD_BASE = CMD_MODULE_CODEC | CMD_CTX_ID_ENC,
/* basic encoder setup control */
MPP_ENC_SET_ALL_CFG, /* set MppEncCfgSet structure */
MPP_ENC_GET_ALL_CFG, /* get MppEncCfgSet structure */
MPP_ENC_SET_PREP_CFG, /* set MppEncPrepCfg structure */
MPP_ENC_GET_PREP_CFG, /* get MppEncPrepCfg structure */
MPP_ENC_SET_RC_CFG, /* set MppEncRcCfg structure */
MPP_ENC_GET_RC_CFG, /* get MppEncRcCfg structure */
MPP_ENC_SET_CODEC_CFG, /* set MppEncCodecCfg structure */
MPP_ENC_GET_CODEC_CFG, /* get MppEncCodecCfg structure */
/* runtime encoder setup control */
MPP_ENC_SET_IDR_FRAME, /* next frame will be encoded as intra frame */
MPP_ENC_SET_OSD_PLT_CFG, /* set OSD palette, parameter should be pointer to
MppEncOSDPlt */
MPP_ENC_SET_OSD_DATA_CFG, /* set OSD data with at most 8 regions, parameter
should be pointer to MppEncOSDData */
MPP_ENC_GET_OSD_CFG,
MPP_ENC_SET_EXTRA_INFO,
MPP_ENC_GET_EXTRA_INFO, /* get vps / sps / pps from hal */
MPP_ENC_SET_SEI_CFG, /* SEI: Supplement Enhancemant Information, parameter
is MppSeiMode */
MPP_ENC_GET_SEI_DATA, /* SEI: Supplement Enhancemant Information, parameter
is MppPacket */
MPP_ENC_CMD_END,
MPP_ISP_CMD_BASE = CMD_MODULE_CODEC | CMD_CTX_ID_ISP,
MPP_ISP_CMD_END,
MPP_HAL_CMD_BASE = CMD_MODULE_HAL,
MPP_HAL_CMD_END,
MPI_CMD_BUTT,
} MpiCmd;
you can call like this.
ret = mpi->control(ctx, xx_xx_cmd, &data);
Mpp支持三种速率控制模式,CBR(恒定比特率),VBR(可变比特率)和CQP(固定QP)。 Mpp控制速率根据设置的三个参数,包括MppEncCodecCfg、MppEncPrepCfg和MppEncRcCfg。
MppEncPrepCfg
+-----------+----------------------------------------+
| param | effect |
| | |
+----------------------------------------------------+
| | |
| format | input pix format, such as NV12, RGB24|
| | |
+----------------------------------------------------+
| | |
| width | wdith of picture |
| | |
+----------------------------------------------------+
| | |
| height | height of picture |
| | |
+----------------------------------------------------+
| | |
| stride | span of a line in picture, must align|
| | in 8 |
+-----------+----------------------------------------+
MppEncRcCfg
+-----------+----------------------------------------+
| param | effect |
| | |
+----------------------------------------------------+
| | |
| rc_mode | 0:CBR |
| | 1:VBR |
+----------------------------------------------------+
| | |
| bps_target| target bitrate |
| | |
+----------------------------------------------------+
| | |
|bps_max/min max/min value of target bitrate |
| | |
+----------------------------------------------------+
| | |
|fps_in_num | input frame rate / denorm |
| /denorm | |
+----------------------------------------------------+
| | |
|fps_out_num output frame rate / denorm |
| /denorm | |
+----------------------------------------------------+
| gop | group of picture, usually set as 4 * |
| | fps |
+-----------+----------------------------------------+
| | |
| uality | profile of VBR mode, worst,worse, |
| | medium,better,best and CQP,note that |
| | it will let qp_max/qp_min lose effect. |
| | |
| | {31, 51}, // worst |
| | {28, 46}, // worse |
| | {24, 42}, // medium |
| | {20, 39}, // better |
| | {16, 35}, // best |
| | {0, 0}, // cqp |
| | cqp means use qp_init,qp will be const |
| | in each frame. |
+-----------+----------------------------------------+
MppEncCodecCfg
+-----------+----------------------------------------+
| param | effect |
| | |
+----------------------------------------------------+
| | |
| qp_init | usually set as 33, if set at 0, mpp |
| | will estimate it by width,height... |
+----------------------------------------------------+
| | |
| qp_min/qp_| min/max value of qp |
| max | |
+----------------------------------------------------+
| | |
| qp_max_step The maximum range of QP differences |
| | between two frames |
+----------------------------------------------------+
| profile | 44 CAVLC 4:4:4 |
| | 66 Baseline |
| | 77 Main |
| | 88 Extended |
| | 100 High (suggest) |
+----------------------------------------------------+
| level | 10 Level 1.0 |
| | 99 Level 1.b |
| | 11 Level 1.1 |
| | 12 Level 1.2 |
| | 13 Level 1.3 |
| | 20 Level 2.0 |
| | 21 Level 2.1 |
| | 22 Level 2.2 |
| | 30 Level 3.0 |
| | 31 Level 3.1 |
| | 32 Level 3.2 |
| | 40 Level 4.0 |
| | 41 Level 4.1 |
| | 42 Level 4.2 |
| | 50 Level 5.0 |
| | 51 Level 5.1 |
+-----------+----------------------------------------+
| entroy_cod| entroy code mode 1:CAVLC |
| ing_mode | 0:CABAC |
+----------------------------------------------------+
| cabac_init| effect when entroy mode is CABAC |
| _idc | |
+-----------+----------------------------------------+
在 CBR 模式下,通过配置的最大比特率和最小比特率范围控制编码数据的比特率。
suggest param
+-----------+----------------------------------------+
| param | effect |
| | |
+----------------------------------------------------+
| | |
| rc_mode | MPP_ENC_RC_MODE_CBR |
+----------------------------------------------------+
| qp_init | 0(suggest, mpp internal will set it) |
| | 24(high bitrate) |
| | 32(medium bitrate) |
| | 40(low bitrate) |
+----------------------------------------------------+
| qp_min | suggest 0, mpp internal will set at 16 |
| | |
+----------------------------------------------------+
| qp_max | suggest 0, mpp internal will set at 48 |
| | |
+-----------+----------------------------------------+
| qp_max_step 16 |
+----------------------------------------------------+
在VBR模式下,mpp会尽量保持视频质量稳定,使编码文件的码率在给定的最大和最小比特率范围内尽可能波动。
suggest param
+-----------+----------------------------------------+
| param | effect |
| | |
+----------------------------------------------------+
| rc_mode | MPP_ENC_RC_MODE_VBR |
+----------------------------------------------------+
| quality | worst(very low bitrate) |
| | worse(low bitrate) |
| | medium(medium bitrate) |
| | better(high bitrate) |
| | best(very high bitrate) |
| | CQP(const qp,rate control lose effect|
+----------------------------------------------------+
| qp_init | 0(suggest, mpp internal estimate) |
| | 24(high bitrate) |
| | 32(medium bitrate) |
| | 40(low bitrate) |
+----------------------------------------------------+
| qp_min | 0 |
+----------------------------------------------------+
| qp_max | 0 |
+-----------+----------------------------------------+
| qp_max_step 8, set at 0 when CQP |
+----------------------------------------------------+
1. MPP_RET Mpp::put_packet(MppPacket packet)
打包为 mpi->put_packet,并与解码器中的内部数据包列表进行交互。
2. MPP_RET Mpp::get_frame(MppFrame *frame)
打包为 mpi->get_frame,并与解码器中的内部帧列表进行交互。解码时可以这样调用。
mpi->put_packet
|
mpi->get_frame
3. MPP_RET Mpp::put_frame(MppFrame frame)
打包为 mpi->put_frame,并与编码器中的内部帧列表进行交互。
4. MPP_RET Mpp::get_packet(MppPacket *packet)
打包为 mpi->get_packet,并在编码器中用于交互内部数据包列表。编码时可以这样调用。
mpi->put_frame
|
mpi->get_packet
5. MPP_RET Mpp::poll(MppPortType type, MppPollType timeout)
打包为 mpi->poll,轮询通常用于任务模式(高级模式)。轮询将检查内部任务列表,如果 MPP_INPUT_PORT/MPP_OUTPUT_PORT 为空,轮询将阻止实用程序任务列表获取节点,否则轮询将立即返回。
关于任务模式的工作原理,请参阅doc/design/4.mpp_task.txt
6. MPP_RET Mpp::dequeue(MppPortType type, MppTask *task)
被打包成 mpi->dequeue,将获得一个包含多个数据的任务。当端口MPP_INPUT_PORT时,获取要设置的任务。当端口MPP_OUTPUT_PORT时,获取包含编码数据的任务。
7. MPP_RET Mpp::enqueue(MppPortType type, MppTask task)
被打包成mpi->enqueue,会向内部任务列表发送一个任务,这样列表就会得到一个节点。编码时可以这样调用。
mpi->poll(input, xx)
|
mpi->dequeue(input, xx)
|
mpi->enqueue(input, xx)
|
mpi->poll(output, xx)
|
mpi->dequeue(output, xx)
|
mpi->enqueue(output, xx)
8. MPP_RET Mpp::control(MpiCmd cmd, MppParam param)
被打包为mpi->control,将改变mpp参数。
关于如何使用mpp编码/解码,请test/mpi_encode_test.c和test/mpi_decode_test.c
http://linux-rockchip.info/mw/index.php?title=VideoAcceleratMion
File:MPP Development Reference.pdf - Rockchip open source Document
File:MPP 开发参考 v0.3.pdf - Rockchip open source Document