北京合众恒跃科技有限公司
直播中

h1654155781.3968

13年用户 81经验值
擅长:电源/新能源 控制/MCU RF/无线
私信 关注
[经验]

【HZ-RK3568开发板免费体验】合众HZ-RK3568 移植 ffmpeg 支持 mpp 库来加速

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",

};

更多回帖

发帖
×
20
完善资料,
赚取积分