函数说明
-
调用video_decoder_create_v2函数初始化解码器句柄,完成一系列初始化操作
-
video_decoder_decode_v2函数将送入的h264_data数据,转成期望的数据格式,并且保存到dst_buff缓冲区
-
video_decoder_destroy_v2销毁解码器句柄,释放内存
-
这里以格式BGRA8888为例,dst_buff缓冲区大小至少要dst_w * dst_h * 4,参考RK官方demo代码:mpi_dec_test.c
代码如下
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <rockchip/rk_mpi.h>
#include <rga/RgaApi.h>
#define VDEC_VO_TAG "vdec_vo.c"
typedef struct{
// MPP 和 RGA 全局上下文
MppCtx mpp_ctx;
MppApi *mpi;
MppPacket packet;
MppDecCfg cfg;
MppBufferGroup group;
struct _dst_info {
int w;
int h;
int format;
int bbp;
}dst_info;
int start;
}VDEC_VO_PARAMS;
int video_decoder_get_frame_v2(void* handle, uint8_t* dst_buff, int size)
{
VDEC_VO_PARAMS* vdec_vo_handle = (VDEC_VO_PARAMS*)handle;
if (vdec_vo_handle == NULL) return -1;
int ret = -1;
int retry_times = 2;
MppFrame mpp_frame;
try_again:
ret = vdec_vo_handle->mpi->decode_get_frame(vdec_vo_handle->mpp_ctx, &mpp_frame);
if (MPP_OK != ret || !mpp_frame) {
if (retry_times > 0) {
retry_times--;
msleep(2);
goto try_again;
}
log_Error(VDEC_VO_TAG, "%p decode_get_frame failed too much time\n", vdec_vo_handle->mpp_ctx);
}
if (ret) {
log_Error(VDEC_VO_TAG, "%p decode_get_frame failed ret %d\n", vdec_vo_handle->mpp_ctx, ret);
return -1;
}
//log_Debug(VDEC_VO_TAG, "%s, mpp_frame=%d, ret=%d\n", __func__, mpp_frame != NULL, ret);
if (mpp_frame != NULL) {
RK_U32 src_width = mpp_frame_get_width(mpp_frame);
RK_U32 src_height = mpp_frame_get_height(mpp_frame);
RK_U32 src_hor_stride = mpp_frame_get_hor_stride(mpp_frame);
RK_U32 src_ver_stride = mpp_frame_get_ver_stride(mpp_frame);
RK_U32 buf_size = mpp_frame_get_buf_size(mpp_frame);
MppFrameFormat fmt = mpp_frame_get_fmt(mpp_frame);
RK_U32 err_info = mpp_frame_get_errinfo(mpp_frame);
RK_U32 discard = mpp_frame_get_discard(mpp_frame);
RK_U32 frm_oes = mpp_frame_get_discard(mpp_frame);
//log_Debug(VDEC_VO_TAG, "%s, Frame format: %d, err_info=%d, discard=%d, frm_oes=%d\n", __func__, fmt, err_info, discard, frm_oes);
if (mpp_frame_get_info_change(mpp_frame)) {
log_Debug(VDEC_VO_TAG, "%p decode_get_frame get info changed found\n", vdec_vo_handle->mpp_ctx);
log_Debug(VDEC_VO_TAG, "%p decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d",
vdec_vo_handle->mpp_ctx, src_width, src_height, src_hor_stride, src_ver_stride, buf_size);
if (NULL == vdec_vo_handle->group) {
/* If buffer group is not set create one and limit it */
ret = mpp_buffer_group_get_internal(&vdec_vo_handle->group, MPP_BUFFER_TYPE_ION);
if (ret) {
log_Error(VDEC_VO_TAG, "%p get mpp buffer group failed ret %d\n", vdec_vo_handle->mpp_ctx, ret);
}
/* Set buffer to mpp decoder */
ret = vdec_vo_handle->mpi->control(vdec_vo_handle->mpp_ctx, MPP_DEC_SET_EXT_BUF_GROUP, vdec_vo_handle->group);
if (ret) {
log_Error(VDEC_VO_TAG, "%p set buffer group failed ret %d\n", vdec_vo_handle->mpp_ctx, ret);
}
} else {
/* If old buffer group exist clear it */
ret = mpp_buffer_group_clear(vdec_vo_handle->group);
if (ret) {
log_Error(VDEC_VO_TAG, "%p clear buffer group failed ret %d\n", vdec_vo_handle->mpp_ctx, ret);
}
}
/* Use limit config to limit buffer count to 24 with buf_size */
ret = mpp_buffer_group_limit_config(vdec_vo_handle->group, buf_size, 24);
if (ret) {
log_Error(VDEC_VO_TAG, "%p limit buffer group failed ret %d\n", vdec_vo_handle->mpp_ctx, ret);
}
/*
* All buffer group config done. Set info change ready to let
* decoder continue decoding
*/
ret = vdec_vo_handle->mpi->control(vdec_vo_handle->mpp_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
if (ret) {
log_Error(VDEC_VO_TAG, "%p info change ready failed ret %d\n", vdec_vo_handle->mpp_ctx, ret);
}
}else {
// 获取解码后的 YUV 数据
void *yuv_buffer = mpp_frame_get_buffer(mpp_frame);
int fd = mpp_buffer_get_fd(yuv_buffer);
// RGA 转换:NV12 → dst_format
rga_info_t src = {0,};
src.fd = fd;
src.mmuFlag = 1;
rga_set_rect(&src.rect, 0,0,src_width,src_height,src_hor_stride,src_ver_stride, RK_FORMAT_YCbCr_420_SP);
rga_info_t dst = {0,};
dst.fd = -1;
dst.mmuFlag = 1;
dst.virAddr = dst_buff,
rga_set_rect(&dst.rect, 0,0,vdec_vo_handle->dst_info.w,vdec_vo_handle->dst_info.h,vdec_vo_handle->dst_info.w,
vdec_vo_handle->dst_info.h, vdec_vo_handle->dst_info.format);
ret = c_RkRgaBlit(&src, &dst, NULL);
if (ret == 0) {
} else {
log_Error(VDEC_VO_TAG, "c_RkRgaBlit failed ret = %d\n", ret);
}
ret = 1;
}
mpp_frame_deinit(&mpp_frame);
return ret;
}
return 0;
}
void* video_decoder_create_v2(int disp_w, int disp_h, const char* dst_format)
{
VDEC_VO_PARAMS* vdec_vo_handle = (VDEC_VO_PARAMS*)calloc(1, sizeof(VDEC_VO_PARAMS));
if(vdec_vo_handle != NULL)
{
int ret = 0;
ret = mpp_create(&vdec_vo_handle->mpp_ctx, &vdec_vo_handle->mpi);
if(ret) {
log_Error(VDEC_VO_TAG, "mpp_create failed! ret=%d\n", ret);
free(vdec_vo_handle);
return NULL;
}
MppParam param = NULL;
RK_U32 need_split = 1;
MpiCmd mpi_cmd = MPP_DEC_SET_PARSER_SPLIT_MODE;
param = &need_split;
ret = vdec_vo_handle->mpi->control(vdec_vo_handle->mpp_ctx, mpi_cmd, param);
if (MPP_OK != ret) {
log_Error(VDEC_VO_TAG, "%p mpi->control failed\n", vdec_vo_handle->mpp_ctx);
mpp_destroy(vdec_vo_handle->mpp_ctx);
return NULL;
}
//ret = vdec_vo_handle->mpi->control(vdec_vo_handle->mpp_ctx, MPP_DEC_SET_ENABLE_DEINTERLACE, (MppParam)0);
ret = mpp_init(vdec_vo_handle->mpp_ctx, MPP_CTX_DEC, MPP_VIDEO_CodingAVC);
if (ret) {
log_Error(VDEC_VO_TAG, "mpp_init failed! ret=%d\n", ret);
mpp_destroy(vdec_vo_handle->mpp_ctx);
free(vdec_vo_handle);
return NULL;
}
mpp_dec_cfg_init(&vdec_vo_handle->cfg);
/*
* split_parse is to enable mpp internal frame spliter when the input
* packet is not aplited into frames.
*/
ret = mpp_dec_cfg_set_u32(vdec_vo_handle->cfg, "base:split_parse", need_split);
if (ret) {
log_Error(VDEC_VO_TAG, "%p failed to set split_parse ret %d\n", vdec_vo_handle->mpp_ctx, ret);
}
ret = vdec_vo_handle->mpi->control(vdec_vo_handle->mpp_ctx, MPP_DEC_SET_CFG, vdec_vo_handle->cfg);
if (ret) {
log_Error(VDEC_VO_TAG, "%p failed to set cfg %p ret %d\n", vdec_vo_handle->mpp_ctx, vdec_vo_handle->cfg, ret);
}
ret = mpp_packet_init(&vdec_vo_handle->packet, NULL, 0);
vdec_vo_handle->dst_info.w = disp_w;
vdec_vo_handle->dst_info.h = disp_h;
if (str_eq(dst_format, "BGRA")) {
vdec_vo_handle->dst_info.format = RK_FORMAT_BGRA_8888;
} else {
log_Error(VDEC_VO_TAG, "not support format:%s\n", dst_format);
video_decoder_destroy_v2();
return NULL;
}
}
log_Info(VDEC_VO_TAG, "%s, vdec_vo_handle:0x%p successfully!\n", __func__, vdec_vo_handle);
// 初始化 RGA (程序初始化全局只需要创建一次即可)
c_RkRgaInit();
return vdec_vo_handle;
}
int video_decoder_decode_v2(void* handle, uint8_t* h264_data, int h264_size, int is_key_frame, uint8_t* dst_buff, int dst_size)
{
VDEC_VO_PARAMS* vdec_vo_handle = (VDEC_VO_PARAMS*)handle;
if (vdec_vo_handle != NULL) {
// 这里保证先得到H264关键帧,其实也可以不需要,RKMPP内部自己会解析判断
if (is_key_frame) {vdec_vo_handle->start = 1;}
if (!vdec_vo_handle->start) {return -1;}
int ret = 0;
// 复用 packet,仅更新数据
mpp_packet_set_data(vdec_vo_handle->packet, h264_data);
mpp_packet_set_size(vdec_vo_handle->packet, h264_size);
mpp_packet_set_pos(vdec_vo_handle->packet, h264_data);
mpp_packet_set_length(vdec_vo_handle->packet, h264_size);
if (is_key_frame) {
//log_Debug(VDEC_VO_TAG, "%s, is_key_frame\n", __func__);
//mpp_packet_set_extra_data(vdec_vo_handle->packet);
}
/*MppDecQueryCfg query = {0};
query.query_flag = MPP_DEC_QUERY_ALL;
vdec_vo_handle->mpi->control(vdec_vo_handle->mpp_ctx, MPP_DEC_QUERY, &query);
log_Debug(VDEC_VO_TAG, "%s, dec_in_pkt_cnt=%d, dec_hw_run_cnt=%d, dec_out_frm_cnt=%d, rt_bps=%d, rt_fps=%d, rt_status=%d, rt_wait=%d\n",
__func__, query.dec_in_pkt_cnt, query.dec_hw_run_cnt, query.dec_out_frm_cnt,
query.rt_bps, query.rt_fps, query.rt_status, query.rt_wait);*/
// 发送给解码器
ret = vdec_vo_handle->mpi->decode_put_packet(vdec_vo_handle->mpp_ctx, vdec_vo_handle->packet);
if (ret) {
log_Error(VDEC_VO_TAG, "decode_put_packet failed! ret=%d\n", ret);
}
// 送入数据后就可以立即获取解码后的数据了
video_decoder_get_frame_v2(handle, dst_buff, size);
return ret;
}
return -1;
}
void video_decoder_destroy_v2(void* handle)
{
VDEC_VO_PARAMS* vdec_vo_handle = (VDEC_VO_PARAMS*)handle;
if(vdec_vo_handle != NULL){
int ret = 0;
// 释放资源(循环外!)
if (vdec_vo_handle->packet != NULL) {
mpp_packet_deinit(&vdec_vo_handle->packet);
vdec_vo_handle->packet = NULL;
}
if (vdec_vo_handle->group != NULL) {
mpp_buffer_group_put(vdec_vo_handle->group);
vdec_vo_handle->group = NULL;
}
if (vdec_vo_handle->cfg != NULL) {
mpp_dec_cfg_deinit(vdec_vo_handle->cfg);
vdec_vo_handle->cfg = NULL;
}
mpp_destroy(vdec_vo_handle->mpp_ctx);
free(vdec_vo_handle);
}
}
No pains, no gains.