提取出编码的代码后,现在提取解码例程,供以后需要的时候使用。
完整的解码代码如下,做了一些改动,指令参数全部去除,输入H264数据,解码后的数据保存在本地,官方示例解码后的数据是YUV_420sp和YUV_422sp格式,这里将其改为了YUV_420p格式。
#if defined(_WIN32)
#include "vld.h"
#endif #define MODULE_TAG "mpi_dec_test" #include <string.h> #include "utils.h"
#include "rk_mpi.h"
#include "mpp_log.h"
#include "mpp_mem.h"
#include "mpp_env.h"
#include "mpp_time.h"
#include "mpp_common.h" #include "mpp_frame.h"
#include "mpp_buffer_impl.h"
#include "mpp_frame_impl.h" #define MPI_DEC_STREAM_SIZE (SZ_4K)
#define MPI_DEC_LOOP_COUNT 4
#define MAX_FILE_NAME_LENGTH 256 typedef struct
{
MppCtx ctx;
MppApi *mpi;
RK_U32 eos;
char *buf; MppBufferGroup frm_grp;
MppBufferGroup pkt_grp;
MppPacket packet;
size_t packet_size;
MppFrame frame; FILE *fp_input;
FILE *fp_output;
RK_S32 frame_count;
RK_S32 frame_num;
size_t max_usage;
} MpiDecLoopData; typedef struct
{
char file_input[MAX_FILE_NAME_LENGTH];
char file_output[MAX_FILE_NAME_LENGTH];
MppCodingType type;
MppFrameFormat format;
RK_U32 width;
RK_U32 height;
RK_U32 debug; RK_U32 have_input;
RK_U32 have_output; RK_U32 simple;
RK_S32 timeout;
RK_S32 frame_num;
size_t max_usage;
} MpiDecTestCmd; size_t mpp_frame_get_buf_size(const MppFrame s)
{
check_is_mpp_frame((MppFrameImpl*)s);
return ((MppFrameImpl*)s)->buf_size;
} void dump_mpp_frame_to_file(MppFrame frame, FILE *fp)
{
RK_U32 width = 0;
RK_U32 height = 0;
RK_U32 h_stride = 0;
RK_U32 v_stride = 0; MppBuffer buffer = NULL;
RK_U8 *base = NULL; width = mpp_frame_get_width(frame);
height = mpp_frame_get_height(frame);
h_stride = mpp_frame_get_hor_stride(frame);
v_stride = mpp_frame_get_ver_stride(frame);
buffer = mpp_frame_get_buffer(frame); base = (RK_U8 *)mpp_buffer_get_ptr(buffer);
RK_U32 buf_size = mpp_frame_get_buf_size(frame);
size_t base_length = mpp_buffer_get_size(buffer);
mpp_log("base_length = %d\n",base_length); RK_U32 i;
RK_U8 *base_y = base;
RK_U8 *base_c = base + h_stride * v_stride; //保存为YUV420sp格式
/*for (i = 0; i < height; i++, base_y += h_stride)
{
fwrite(base_y, 1, width, fp);
}
for (i = 0; i < height / 2; i++, base_c += h_stride)
{
fwrite(base_c, 1, width, fp);
}*/ //保存为YUV420p格式
for(i = 0; i < height; i++, base_y += h_stride)
{
fwrite(base_y, 1, width, fp);
}
for(i = 0; i < height * width / 2; i+=2)
{
fwrite((base_c + i), 1, 1, fp);
}
for(i = 1; i < height * width / 2; i+=2)
{
fwrite((base_c + i), 1, 1, fp);
}
} size_t mpp_buffer_group_usage(MppBufferGroup group)
{
if (NULL == group)
{
mpp_err_f("input invalid group %p\n", group);
return MPP_BUFFER_MODE_BUTT;
} MppBufferGroupImpl *p = (MppBufferGroupImpl *)group;
return p->usage;
} static int decode_simple(MpiDecLoopData *data)
{
RK_U32 pkt_done = 0;
RK_U32 pkt_eos = 0;
RK_U32 err_info = 0; MPP_RET ret = MPP_OK;
MppCtx ctx = data->ctx;
MppApi *mpi = data->mpi;
char *buf = data->buf; MppPacket packet = data->packet;
MppFrame frame = NULL;
size_t read_size = fread(buf, 1, data->packet_size, data->fp_input); if (read_size != data->packet_size || feof(data->fp_input))
{
mpp_log("found last packet\n");
data->eos = pkt_eos = 1;
} mpp_packet_write(packet, 0, buf, read_size);
mpp_packet_set_pos(packet, buf);
mpp_packet_set_length(packet, read_size); if (pkt_eos)
{
mpp_packet_set_eos(packet);
} do {
if (!pkt_done)
{
ret = mpi->decode_put_packet(ctx, packet);
if (MPP_OK == ret)
{
pkt_done = 1;
}
} do {
RK_S32 get_frm = 0;
RK_U32 frm_eos = 0; ret = mpi->decode_get_frame(ctx, &frame); if (frame)
{
if (mpp_frame_get_info_change(frame))
{
RK_U32 width = mpp_frame_get_width(frame);
RK_U32 height = mpp_frame_get_height(frame);
RK_U32 hor_stride = mpp_frame_get_hor_stride(frame);
RK_U32 ver_stride = mpp_frame_get_ver_stride(frame);
RK_U32 buf_size = mpp_frame_get_buf_size(frame); mpp_log("decode_get_frame get info changed found\n");
mpp_log("decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d", width, height, hor_stride, ver_stride, buf_size); if (NULL == data->frm_grp)
{
ret = mpp_buffer_group_get_internal(&data->frm_grp, MPP_BUFFER_TYPE_ION);
if (ret)
{
mpp_err("get mpp buffer group failed ret %d\n", ret);
break;
}
ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, data->frm_grp);
if (ret)
{
mpp_err("set buffer group failed ret %d\n", ret);
break;
}
}
else
{
ret = mpp_buffer_group_clear(data->frm_grp);
if (ret)
{
mpp_err("clear buffer group failed ret %d\n", ret);
break;
}
} ret = mpp_buffer_group_limit_config(data->frm_grp, buf_size, 24);
if (ret)
{
mpp_err("limit buffer group failed ret %d\n", ret);
break;
}
ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
if (ret)
{
mpp_err("info change ready failed ret %d\n", ret);
break;
}
}
else
{
err_info = mpp_frame_get_errinfo(frame) | mpp_frame_get_discard(frame);
if (err_info)
{
mpp_log("decoder_get_frame get err info:%d discard:%d.\n", mpp_frame_get_errinfo(frame), mpp_frame_get_discard(frame));
}
data->frame_count++;
mpp_log("decode_get_frame get frame %d\n", data->frame_count);
if (data->fp_output && !err_info)
{
dump_mpp_frame_to_file(frame, data->fp_output);
}
}
frm_eos = mpp_frame_get_eos(frame);
mpp_frame_deinit(&frame);
frame = NULL;
get_frm = 1;
} if (data->frm_grp)
{
size_t usage = mpp_buffer_group_usage(data->frm_grp);
if (usage > data->max_usage)
{
data->max_usage = usage;
}
} if (pkt_eos && pkt_done && !frm_eos)
{
msleep(10);
continue;
}
if (frm_eos)
{
mpp_log("found last frame\n");
break;
}
if (data->frame_num && data->frame_count >= data->frame_num)
{
data->eos = 1;
break;
}
if (get_frm)
{
continue;
}
break;
} while (1); if (data->frame_num && data->frame_count >= data->frame_num)
{
data->eos = 1;
mpp_log("reach max frame number %d\n", data->frame_count);
break;
}
if (pkt_done)
{
break;
}
} while (1); return ret;
} int mpi_dec_test_decode(MpiDecTestCmd *cmd)
{
MPP_RET ret = MPP_OK;
size_t file_size = 0; MppCtx ctx = NULL;
MppApi *mpi = NULL; MppPacket packet = NULL;
MppFrame frame = NULL; MpiCmd mpi_cmd = MPP_CMD_BASE;
MppParam param = NULL;
RK_U32 need_split = 1; RK_U32 width = cmd->width;
RK_U32 height = cmd->height;
MppCodingType type = cmd->type; char *buf = NULL;
size_t packet_size = MPI_DEC_STREAM_SIZE;
MppBuffer pkt_buf = NULL;
MppBuffer frm_buf = NULL; MpiDecLoopData data; mpp_log("mpi_dec_test start\n");
memset(&data, 0, sizeof(data)); data.fp_input = fopen("test.h264", "rb");
if (NULL == data.fp_input)
{
mpp_err("failed to open input file %s\n", cmd->file_input);
goto MPP_TEST_OUT;
} fseek(data.fp_input, 0L, SEEK_END);
file_size = ftell(data.fp_input);
rewind(data.fp_input);
mpp_log("input file size %ld\n", file_size); data.fp_output = fopen("output.yuv", "w+b"); if (cmd->simple)
{
buf = mpp_malloc(char, packet_size); ret = mpp_packet_init(&packet, buf, packet_size);
}
mpp_log("mpi_dec_test decoder test start w %d h %d type %d\n", width, height, type); ret = mpp_create(&ctx, &mpi); mpi_cmd = MPP_DEC_SET_PARSER_SPLIT_MODE;
param = &need_split; ret = mpi->control(ctx, mpi_cmd, param);
if (MPP_OK != ret)
{
mpp_err("mpi->control failed\n");
goto MPP_TEST_OUT;
} ret = mpp_init(ctx, MPP_CTX_DEC, type);
if (MPP_OK != ret)
{
mpp_err("mpp_init failed\n");
goto MPP_TEST_OUT;
} mpp_log("packet_size = %d\n", packet_size); data.ctx = ctx;
data.mpi = mpi;
data.eos = 0;
data.buf = buf;
data.packet = packet;
data.packet_size = packet_size;
data.frame = frame;
data.frame_count = 0;
data.frame_num = cmd->frame_num;
mpp_log("data.packet_size = %d\n", data.packet_size); if (cmd->simple)
{
while (!data.eos)
{
decode_simple(&data);
mpp_log("data.eos = %d\n", data.eos);
}
} cmd->max_usage = data.max_usage;
ret = mpi->reset(ctx);
if (MPP_OK != ret)
{
mpp_err("mpi->reset failed\n");
goto MPP_TEST_OUT;
} MPP_TEST_OUT:
if (packet)
{
mpp_packet_deinit(&packet);
packet = NULL;
} if (frame)
{
mpp_frame_deinit(&frame);
frame = NULL;
} if (ctx)
{
mpp_destroy(ctx);
ctx = NULL;
} if (cmd->simple)
{
if (buf)
{
mpp_free(buf);
buf = NULL;
}
} if (data.pkt_grp)
{
mpp_buffer_group_put(data.pkt_grp);
data.pkt_grp = NULL;
} if (data.frm_grp)
{
mpp_buffer_group_put(data.frm_grp);
data.frm_grp = NULL;
} if (data.fp_output)
{
fclose(data.fp_output);
data.fp_output = NULL;
} if (data.fp_input)
{
fclose(data.fp_input);
data.fp_input = NULL;
} return ret;
} int main(int argc, char **argv)
{
RK_S32 ret = 0;
MpiDecTestCmd cmd_ctx;
MpiDecTestCmd* cmd = &cmd_ctx; cmd->simple = 1;
cmd->type = 7;
cmd->width = 640;
cmd->height = 480; ret = mpi_dec_test_decode(cmd); return ret;
}
具体分析:
1、MPI接口的结构和使用,可以参考上篇内容,解码与编码基本一致。
2、解码器接口
decode_put_packet:
MPP_RET decode_put_packet(MppCtx ctx,MppPacket packet)
ctx:MPP解码器实例;
packet:待输入的码流数据;
输入码流的方式:分帧与不分帧。裸码流输入有两种,一种是按帧分段的数据,每一个输入给decode_put_packet函数的数据包都包含完整的一帧,不多也不少。在这种情况下,MPP可以直接按包处理码流。另一种是按长度读取数据,无法判断一个包的数据是否为完整的一帧,需要MPP内部进行分帧处理。在进行这种形式的输入时,需要在mpp_init前,通过control接口的MPP_DEC_SET_PARSER_SPLIT_MODE命令,打开need_split标志。分帧方式效率高,但需要在输入码流前进行解析与分帧,不分帧方式使用简单,效率会受影响。官方解码示例采用的是不分帧方式,因此上述代码也是不分帧方式。
decode_get_frame:
MPP_RET decode_get_frame(MppCtx ctx,MppFrame *frame)
ctx:MPP解码器实例;
frame:用于MppFrame实例的指针;
完整的解码过程是上面两个函数的结合。