1 第一步 下载 ffmpeg
git clone https://github.com/FFmpeg/FFmpeg.git
2 在 ffmpeg 的编解码器上注册 mpp 硬件编解码器
libavcodec/allcodecs.c文件中添加 RKMPP 编解码器的注册代码
extern AVCodec ff_h264_rkmpp_decoder;
extern AVCodec ff_hevc_rkmpp_decoder;
extern AVCodec ff_h264_rkmpp_encoder;
extern AVCodec ff_hevc_rkmpp_encoder;
void avcodec_register_all(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
REGISTER_HWACCEL(H264_DXVA2, h264_dxva2);
REGISTER_HWACCEL(H264_VAAPI, h264_vaapi);
REGISTER_HWACCEL(H264_VDPAU, h264_vdpau);
REGISTER_DECODER(H264_RKMPP, h264_rkmpp);
REGISTER_DECODER(HEVC_RKMPP, hevc_rkmpp);
REGISTER_ENCODER(H264_RKMPP, h264_rkmpp);
REGISTER_ENCODER(HEVC_RKMPP, hevc_rkmpp);
}
实现完整的 rkmpp 解码器实现 (libavcodec/rkmppdec.c)`
#include <rockchip/rk_mpi.h>
#include <rockchip/mpp_buffer.h>
#include <rockchip/mpp_frame.h>
#include <rockchip/mpp_packet.h>
#include "libavutil/common.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_drm.h"
#include "libavutil/time.h"
#include "avcodec.h"
#include "internal.h"
#include "decode.h"
typedef struct RKMPPDecContext {
AVClass *avclass;
MppCtx ctx;
MppApi *mpi;
MppBufferGroup frame_group;
AVBufferRef *frames_ref;
AVBufferRef *device_ref;
int width;
int height;
AVCodecID codec_id;
int eos_reached;
int frames_output;
int debug;
} RKMPPDecContext;
#define OFFSET(x) offsetof(RKMPPDecContext, x)
#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
{ "debug", "debug flags", OFFSET(debug), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VD },
{ NULL },
};
static const AVClass rkmpp_decoder_class = {
.class_name = "rkmppdec",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
static int rkmpp_init_decoder(RKMPPDecContext *rkctx)
{
MppCtx ctx = NULL;
MppApi *mpi = NULL;
MppBufferGroup frame_group = NULL;
MppParam param = NULL;
RK_S32 ret = 0;
MppCodingType coding_type;
switch (rkctx->codec_id) {
case AV_CODEC_ID_H264:
coding_type = MPP_VIDEO_CodingAVC;
break;
case AV_CODEC_ID_HEVC:
coding_type = MPP_VIDEO_CodingHEVC;
break;
default:
av_log(NULL, AV_LOG_ERROR, "Unsupported codec for MPP\n");
return AVERROR(ENOSYS);
}
ret = mpp_create(&ctx, &mpi);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to create MPP context\n");
return AVERROR_UNKNOWN;
}
ret = mpp_init(ctx, MPP_CTX_DEC, coding_type);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to init MPP decoder\n");
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
ret = mpp_buffer_group_get_internal(&frame_group, MPP_BUFFER_TYPE_DRM);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to get MPP buffer group\n");
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
param = &frame_group;
ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, param);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to set external buffer group\n");
mpp_buffer_group_put(frame_group);
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
param = (MppParam)((RK_S32)1);
ret = mpi->control(ctx, MPP_DEC_SET_PARSER_FAST_MODE, param);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_WARNING, "Failed to set parser fast mode\n");
}
param = (MppParam)((RK_S32)1);
ret = mpi->control(ctx, MPP_DEC_SET_ENABLE_DEINTERLACE, param);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_WARNING, "Failed to set deinterlace mode\n");
}
rkctx->ctx = ctx;
rkctx->mpi = mpi;
rkctx->frame_group = frame_group;
return 0;
}
static int rkmpp_decode_init(AVCodecContext *avctx)
{
RKMPPDecContext *rkctx = avctx->priv_data;
int ret;
rkctx->codec_id = avctx->codec_id;
rkctx->width = avctx->width;
rkctx->height = avctx->height;
rkctx->frames_output = 0;
ret = rkmpp_init_decoder(rkctx);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to initialize MPP decoder\n");
return ret;
}
av_log(avctx, AV_LOG_VERBOSE, "RKMPP decoder initialized successfully\n");
return 0;
}
static int rkmpp_decode_frame(AVCodecContext *avctx, void *data,
int *got_frame, AVPacket *avpkt)
{
RKMPPDecContext *rkctx = avctx->priv_data;
MppApi *mpi = rkctx->mpi;
MppCtx ctx = rkctx->ctx;
MppPacket packet = NULL;
MppFrame frame = NULL;
MppBuffer buffer = NULL;
AVFrame *output_frame = data;
RK_S32 ret = 0;
int err = 0;
*got_frame = 0;
if (avpkt->size) {
ret = mpp_packet_init(&packet, avpkt->data, avpkt->size);
if (ret != MPP_OK) {
av_log(avctx, AV_LOG_ERROR, "Failed to init MPP packet\n");
return AVERROR_UNKNOWN;
}
mpp_packet_set_pts(packet, avpkt->pts);
} else {
mpp_packet_init(&packet, NULL, 0);
mpp_packet_set_eos(packet);
}
ret = mpi->decode_put_packet(ctx, packet);
if (ret != MPP_OK) {
av_log(avctx, AV_LOG_WARNING, "Failed to put packet to decoder\n");
if (packet) mpp_packet_deinit(&packet);
return AVERROR(EAGAIN);
}
if (packet) mpp_packet_deinit(&packet);
ret = mpi->decode_get_frame(ctx, &frame);
if (ret != MPP_OK) {
return AVERROR(EAGAIN);
}
if (frame) {
if (mpp_frame_get_info_change(frame)) {
avctx->width = mpp_frame_get_width(frame);
avctx->height = mpp_frame_get_height(frame);
av_log(avctx, AV_LOG_INFO, "Format changed to %dx%d\n",
avctx->width, avctx->height);
mpp_frame_deinit(&frame);
return AVERROR(EAGAIN);
}
if (mpp_frame_get_eos(frame)) {
rkctx->eos_reached = 1;
mpp_frame_deinit(&frame);
return 0;
}
buffer = mpp_frame_get_buffer(frame);
if (!buffer) {
av_log(avctx, AV_LOG_ERROR, "Failed to get frame buffer\n");
mpp_frame_deinit(&frame);
return AVERROR_UNKNOWN;
}
err = av_frame_copy_props(output_frame, avctx->coded_frame);
if (err < 0) {
mpp_frame_deinit(&frame);
return err;
}
output_frame->format = AV_PIX_FMT_DRM_PRIME;
output_frame->width = mpp_frame_get_width(frame);
output_frame->height = mpp_frame_get_height(frame);
output_frame->pts = mpp_frame_get_pts(frame);
output_frame->pkt_dts = avpkt->dts;
err = av_hwframe_map(output_frame, output_frame, AV_HWFRAME_MAP_READ);
if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to map HW frame: %s\n",
av_err2str(err));
mpp_frame_deinit(&frame);
return err;
}
*got_frame = 1;
rkctx->frames_output++;
if (rkctx->debug) {
av_log(avctx, AV_LOG_DEBUG, "Decoded frame %d, pts: %lld\n",
rkctx->frames_output, output_frame->pts);
}
mpp_frame_deinit(&frame);
}
return avpkt->size;
}
static int rkmpp_decode_end(AVCodecContext *avctx)
{
RKMPPDecContext *rkctx = avctx->priv_data;
if (rkctx->ctx) {
mpp_destroy(rkctx->ctx);
rkctx->ctx = NULL;
}
if (rkctx->frame_group) {
mpp_buffer_group_put(rkctx->frame_group);
rkctx->frame_group = NULL;
}
av_log(avctx, AV_LOG_VERBOSE, "RKMPP decoder cleaned up\n");
return 0;
}
static void rkmpp_decode_flush(AVCodecContext *avctx)
{
RKMPPDecContext *rkctx = avctx->priv_data;
if (rkctx->mpi && rkctx->ctx) {
rkctx->mpi->reset(rkctx->ctx);
}
rkctx->eos_reached = 0;
}
AVCodec ff_h264_rkmpp_decoder = {
.name = "h264_rkmpp",
.long_name = NULL_IF_CONFIG_SMALL("H.264 (RKMPP)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(RKMPPDecContext),
.init = rkmpp_decode_init,
.close = rkmpp_decode_end,
.decode = rkmpp_decode_frame,
.flush = rkmpp_decode_flush,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_DRM_PRIME, AV_PIX_FMT_NONE },
.priv_class = &rkmpp_decoder_class,
.wrapper_name = "rkmpp",
};
AVCodec ff_hevc_rkmpp_decoder = {
.name = "hevc_rkmpp",
.long_name = NULL_IF_CONFIG_SMALL("HEVC (RKMPP)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_HEVC,
.priv_data_size = sizeof(RKMPPDecContext),
.init = rkmpp_decode_init,
.close = rkmpp_decode_end,
.decode = rkmpp_decode_frame,
.flush = rkmpp_decode_flush,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE,
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_DRM_PRIME, AV_PIX_FMT_NONE },
.priv_class = &rkmpp_decoder_class,
.wrapper_name = "rkmpp",
};
完整的 rkmpp 编码器实现 (libavcodec/rkmppenc.c)
#include <rockchip/rk_mpi.h>
#include <rockchip/mpp_buffer.h>
#include <rockchip/mpp_frame.h>
#include <rockchip/mpp_packet.h>
#include "libavutil/common.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_drm.h"
#include "libavutil/time.h"
#include "avcodec.h"
#include "internal.h"
#include "encode.h"
typedef struct RKMPPEncContext {
AVClass *avclass;
MppCtx ctx;
MppApi *mpi;
MppBufferGroup frame_group;
int width;
int height;
AVCodecID codec_id;
int bit_rate;
int gop_size;
int max_b_frames;
int qp;
int frames_encoded;
int debug;
} RKMPPEncContext;
#define OFFSET(x) offsetof(RKMPPEncContext, x)
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "debug", "debug flags", OFFSET(debug), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },
{ "qp", "quantization parameter", OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 26 }, 0, 51, VE },
{ NULL },
};
static const AVClass rkmpp_encoder_class = {
.class_name = "rkmppenc",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
static int rkmpp_init_encoder(RKMPPEncContext *rkctx)
{
MppCtx ctx = NULL;
MppApi *mpi = NULL;
MppBufferGroup frame_group = NULL;
MppParam param = NULL;
RK_S32 ret = 0;
MppCodingType coding_type;
switch (rkctx->codec_id) {
case AV_CODEC_ID_H264:
coding_type = MPP_VIDEO_CodingAVC;
break;
case AV_CODEC_ID_HEVC:
coding_type = MPP_VIDEO_CodingHEVC;
break;
default:
av_log(NULL, AV_LOG_ERROR, "Unsupported codec for MPP\n");
return AVERROR(ENOSYS);
}
ret = mpp_create(&ctx, &mpi);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to create MPP context\n");
return AVERROR_UNKNOWN;
}
ret = mpp_init(ctx, MPP_CTX_ENC, coding_type);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to init MPP encoder\n");
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
MppEncPrepCfg prep_cfg;
memset(&prep_cfg, 0, sizeof(prep_cfg));
prep_cfg.width = rkctx->width;
prep_cfg.height = rkctx->height;
prep_cfg.format = MPP_FMT_YUV420P;
ret = mpi->control(ctx, MPP_ENC_SET_PREP_CFG, &prep_cfg);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to set prep config\n");
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
MppEncRcCfg rc_cfg;
memset(&rc_cfg, 0, sizeof(rc_cfg));
rc_cfg.rc_mode = MPP_ENC_RC_MODE_CBR;
rc_cfg.bps = rkctx->bit_rate;
rc_cfg.bps_min = rkctx->bit_rate * 3 / 4;
rc_cfg.bps_max = rkctx->bit_rate * 3 / 2;
ret = mpi->control(ctx, MPP_ENC_SET_RC_CFG, &rc_cfg);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to set rate control config\n");
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
MppEncCodecCfg codec_cfg;
memset(&codec_cfg, 0, sizeof(codec_cfg));
if (coding_type == MPP_VIDEO_CodingAVC) {
codec_cfg.h264.gop = rkctx->gop_size;
codec_cfg.h264.max_i_qp = 28;
codec_cfg.h264.min_i_qp = 10;
codec_cfg.h264.max_p_qp = 38;
codec_cfg.h264.min_p_qp = 20;
codec_cfg.h264.max_b_qp = 40;
codec_cfg.h264.min_b_qp = 22;
} else if (coding_type == MPP_VIDEO_CodingHEVC) {
codec_cfg.hevc.gop = rkctx->gop_size;
codec_cfg.hevc.max_i_qp = 28;
codec_cfg.hevc.min_i_qp = 10;
codec_cfg.hevc.max_p_qp = 38;
codec_cfg.hevc.min_p_qp = 20;
codec_cfg.hevc.max_b_qp = 40;
codec_cfg.hevc.min_b_qp = 22;
}
ret = mpi->control(ctx, MPP_ENC_SET_CODEC_CFG, &codec_cfg);
if (ret != MPP_OK) {
av_log(NULL, AV_LOG_ERROR, "Failed to set codec config\n");
mpp_destroy(ctx);
return AVERROR_UNKNOWN;
}
rkctx->ctx = ctx;
rkctx->mpi = mpi;
rkctx->frame_group = frame_group;
return 0;
}
static int rkmpp_encode_init(AVCodecContext *avctx)
{
RKMPPEncContext *rkctx = avctx->priv_data;
int ret;
rkctx->codec_id = avctx->codec_id;
rkctx->width = avctx->width;
rkctx->height = avctx->height;
rkctx->bit_rate = avctx->bit_rate;
rkctx->gop_size = avctx->gop_size;
rkctx->max_b_frames = avctx->max_b_frames;
rkctx->frames_encoded = 0;
ret = rkmpp_init_encoder(rkctx);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to initialize MPP encoder\n");
return ret;
}
av_log(avctx, AV_LOG_VERBOSE, "RKMPP encoder initialized successfully\n");
return 0;
}
static int rkmpp_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *frame, int *got_packet)
{
RKMPPEncContext *rkctx = avctx->priv_data;
MppApi *mpi = rkctx->mpi;
MppCtx ctx = rkctx->ctx;
MppFrame mframe = NULL;
MppPacket mpkt = NULL;
RK_S32 ret = 0;
int err = 0;
*got_packet = 0;
if (frame) {
ret = mpp_frame_init(&mframe);
if (ret != MPP_OK) {
av_log(avctx, AV_LOG_ERROR, "Failed to init MPP frame\n");
return AVERROR_UNKNOWN;
}
mpp_frame_set_width(mframe, frame->width);
mpp_frame_set_height(mframe, frame->height);
mpp_frame_set_pts(mframe, frame->pts);
mpp_frame_set_fmt(mframe, MPP_FMT_YUV420P);
if (frame->format == AV_PIX_FMT_DRM_PRIME) {
av_log(avctx, AV_LOG_ERROR, "DRM frame input not fully implemented yet\n");
mpp_frame_deinit(&mframe);
return AVERROR_PATCHWELCOME;
} else {
uint8_t *data[3] = { frame->data[0], frame->data[1], frame->data[2] };
int linesize[3] = { frame->linesize[0], frame->linesize[1], frame->linesize[2] };
mpp_frame_set_buffer(mframe, data, linesize);
}
ret = mpi->encode_put_frame(ctx, mframe);
if (ret != MPP_OK) {
av_log(avctx, AV_LOG_ERROR, "Failed to put frame to encoder\n");
mpp_frame_deinit(&mframe);
return AVERROR_UNKNOWN;
}
mpp_frame_deinit(&mframe);
} else {
ret = mpi->encode_put_frame(ctx, NULL);
if (ret != MPP_OK) {
av_log(avctx, AV_LOG_ERROR, "Failed to put flush frame to encoder\n");
return AVERROR_UNKNOWN;
}
}
ret = mpi->encode_get_packet(ctx, &mpkt);
if (ret != MPP_OK) {
return AVERROR(EAGAIN);
}
if (mpkt) {
size_t packet_size = mpp_packet_get_length(mpkt);
const void *packet_data = mpp_packet_get_data(mpkt);
if (packet_size > 0) {
err = ff_alloc_packet2(avctx, pkt, packet_size, packet_size);
if (err < 0) {
av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet\n");
mpp_packet_deinit(&mpkt);
return err;
}
memcpy(pkt->data, packet_data, packet_size);
pkt->pts = mpp_packet_get_pts(mpkt);
pkt->dts = mpp_packet_get_dts(mpkt);
if (mpp_packet_get_eos(mpkt)) {
pkt->flags |= AV_PKT_FLAG_EOF;
}
*got_packet = 1;
rkctx->frames_encoded++;
if (rkctx->debug) {
av_log(avctx, AV_LOG_DEBUG, "Encoded packet %d, size: %zu, pts: %lld\n",
rkctx->frames_encoded, packet_size, pkt->pts);
}
}
mpp_packet_deinit(&mpkt);
}
return 0;
}
static int rkmpp_encode_end(AVCodecContext *avctx)
{
RKMPPEncContext *rkctx = avctx->priv_data;
if (rkctx->ctx) {
mpp_destroy(rkctx->ctx);
rkctx->ctx = NULL;
}
if (rkctx->frame_group) {
mpp_buffer_group_put(rkctx->frame_group);
rkctx->frame_group = NULL;
}
av_log(avctx, AV_LOG_VERBOSE, "RKMPP encoder cleaned up\n");
return 0;
}
AVCodec ff_h264_rkmpp_encoder = {
.name = "h264_rkmpp",
.long_name = NULL_IF_CONFIG_SMALL("H.264 (RKMPP)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.priv_data_size = sizeof(RKMPPEncContext),
.init = rkmpp_encode_init,
.encode2 = rkmpp_encode_frame,
.close = rkmpp_encode_end,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE,
.pix_fmts = (const enum AVPixelFormat[]){
AV_PIX_FMT_DRM_PRIME,
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_NONE
},
.priv_class = &rkmpp_encoder_class,
.defaults = NULL,
.wrapper_name = "rkmpp",
};
AVCodec ff_hevc_rkmpp_encoder = {
.name = "hevc_rkmpp",
.long_name = NULL_IF_CONFIG_SMALL("HEVC (RKMPP)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_HEVC,
.priv_data_size = sizeof(RKMPPEncContext),
.init = rkmpp_encode_init,
.encode2 = rkmpp_encode_frame,
.close = rkmpp_encode_end,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE,
.pix_fmts = (const enum AVPixelFormat[]){
AV_PIX_FMT_DRM_PRIME,
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_NONE
},
.priv_class = &rkmpp_encoder_class,
.defaults = NULL,
.wrapper_name = "rkmpp",
};