MPP-解码示例
提取出编码的代码后,现在提取解码例程,供以后需要的时候使用。
完整的解码代码如下,做了一些改动,指令参数全部去除,输入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实例的指针;
完整的解码过程是上面两个函数的结合。