rkmpp 解码 精简mpi_dec_test.c例程
rkmpp 解码流程(除 MPP_VIDEO_CodingMJPEG
之外)
源码
输入h264码流 输出nv12文件
/*
* Copyright 2015 Rockchip Electronics Co. LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(_WIN32)
#include "vld.h"
#endif
#define MODULE_TAG "mpi_dec_test"
#include <string.h>
#include "mpi_dec_utils.h"
#include "mpp_common.h"
#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_time.h"
#include "rk_mpi.h"
typedef struct {
MpiDecTestCmd *cmd;
MppCtx ctx;
MppApi *mpi;
RK_U32 quiet;
/* end of stream flag when set quit the loop */
RK_U32 loop_end;
/* input and output */
DecBufMgr buf_mgr;
MppBufferGroup frm_grp;
MppPacket packet;
MppFrame frame;
FILE *fp_output;
RK_S32 frame_count;
RK_S32 frame_num;
RK_S64 first_pkt;
RK_S64 first_frm;
size_t max_usage;
float frame_rate;
RK_S64 elapsed_time;
RK_S64 delay;
FILE *fp_verify;
FrmCrc checkcrc;
} MpiDecLoopData;
int main(int argc, char **argv) {
char *video_path = "/home/ido/chenFan/mpp/test/video/test.h264";
// RK_S32 max_dec_frame_num = 100; //最大解码帧数
RK_S32 max_dec_frame_num = 0; //解码完整个文件
char *file_output = "/home/ido/chenFan/mpp/test/video/out.yuv";
size_t max_usage; //记录解码器最大使用内存
MppCtx ctx = NULL;
MppApi *mpi = NULL;
// 1. 创建解码器
int ret = mpp_create(&ctx, &mpi);
if (ret) {
mpp_err("mpp_create failed\n");
goto MPP_TEST_OUT;
}
// mpp_log("")
// 2. 初始化解码器
MppCodingType video_type = MPP_VIDEO_CodingAVC;
ret = mpp_init(ctx, MPP_CTX_DEC, video_type);
if (ret) {
mpp_err("%p mpp_init failed\n", ctx);
goto MPP_TEST_OUT;
}
MppParam cfg = NULL;
mpp_dec_cfg_init(&cfg);
// 2.1 获取默认配置
ret = mpi->control(ctx, MPP_DEC_GET_CFG, cfg);
// 2.2 设置内部分帧模式
uint32_t need_split = 1;
ret = mpp_dec_cfg_set_u32(cfg, "base:split_parse", need_split);
if (ret) {
mpp_err("%p failed to set split_parse ret %d\n", ctx, ret);
goto MPP_TEST_OUT;
}
ret = mpi->control(ctx, MPP_DEC_SET_CFG, cfg);
if (ret) {
mpp_err("%p failed to set cfg %p ret %d\n", ctx, cfg, ret);
goto MPP_TEST_OUT;
}
//解码码流保存到文件
FILE *fp_output = NULL;
#if 0
fp_output = fopen(file_output, "w+b");
if (NULL == fp_output) {
mpp_err("failed to open output file %s\n", file_output);
goto MPP_TEST_OUT;
}
#endif
//半内部模式配置
MppDecBufMode buf_mode = MPP_DEC_BUF_HALF_INT;
MppBufferGroup grp = NULL;
DecBufMgr buf_mgr;
ret = dec_buf_mgr_init(&buf_mgr);
if (ret) {
mpp_err("dec_buf_mgr_init failed\n");
goto MPP_TEST_OUT;
}
// 配置输入pkt
MppPacket packet = NULL;
ret = mpp_packet_init(&packet, NULL, 0);
if (ret) {
mpp_err("mpp_packet_init failed\n");
goto MPP_TEST_OUT;
}
// 3. 解码
/* end of stream flag when set quit the loop */
RK_U32 loop_end = 0;
RK_S32 frame_count = 0;
RK_S64 first_pkt = 0;
RK_S64 first_frm = 0;
// 3.1 打开输入码流
FileReader reader = NULL;
reader_init(&reader, video_path, video_type);
if (!reader) {
mpp_err("input file %s reader_init failed\n");
return -1;
}
mpp_log("input file %s size %ld\n", video_path, reader_size(reader));
RK_S64 t_s, t_e;
t_s = mpp_time();
// 3.2 解码循环
while (!loop_end) {
// 3.2.1 读取输入码流
FileBufSlot *slot = NULL;
ret = reader_read(reader, &slot);
if (ret < 0) {
mpp_err("input file %s reader_read failed\n");
return -1;
}
RK_U32 pkt_eos = 0;
pkt_eos = slot->eos;
if (pkt_eos) {
//如果指定最大帧数小于0即一直解码 或 码流自身帧数小于指定最大解码帧数,
//则重载码流继续解码
if (max_dec_frame_num < 0 || max_dec_frame_num > frame_count) {
mpp_log("%p loop again\n", ctx);
reader_rewind(reader);
pkt_eos = 0;
} else {
mpp_log("%p found last packet\n", ctx);
loop_end = 1;
}
}
// 3.2.2 配置输入pkt
mpp_packet_set_data(packet, slot->data);
mpp_packet_set_size(packet, slot->size);
mpp_packet_set_pos(packet, slot->data);
mpp_packet_set_length(packet, slot->size);
if (pkt_eos) mpp_packet_set_eos(packet);
RK_U32 pkt_done = 0;
do {
RK_S32 dec_frame_timeout = 30;
RK_U32 frm_eos = 0;
if (!pkt_done) {
// 3.2.3 packet传入解码器
ret = mpi->decode_put_packet(ctx, packet);
if (ret == MPP_OK) {
pkt_done = 1;
if (!first_pkt) {
first_pkt = mpp_time(); // 记录第一个成功送入解码器的时间戳
}
}
}
do {
RK_S32 get_frm = 0;
MppFrame frame = NULL;
try_again:
// 3.2.4 从解码器获取解码后的帧
ret = mpi->decode_get_frame(ctx, &frame);
//超时等待
if (ret == MPP_ERR_TIMEOUT) {
if (dec_frame_timeout > 0) {
dec_frame_timeout--;
msleep(1);
goto try_again;
}
mpp_err("%p decode_get_frame failed too much time\n", ctx);
}
if (ret) {
mpp_err("%p decode_get_frame failed ret %d\n", ret, ctx);
break;
}
if (frame) {
// 3.2.5 配置MppBufferGroup 并 通知解码器
// MPP_DEC_SET_INFO_CHANGE_READY
//半内部分配模式
//可通过mpp_buffer_group_limit_config限制解码器的内存使用量
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("%p decode_get_frame get info changed found\n", ctx);
mpp_log(
"%p decoder require buffer w:h [%d:%d] stride [%d:%d] "
"buf_size %d",
ctx, width, height, hor_stride, ver_stride, buf_size);
grp = dec_buf_mgr_setup(buf_mgr, buf_size, 24, buf_mode);
/* Set buffer to mpp decoder */
ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, grp);
if (ret) {
mpp_err("%p set buffer group failed ret %d\n", ctx, ret);
break;
}
ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
if (ret) {
mpp_err("%p info change ready failed ret %d\n", ctx, ret);
break;
}
} else {
// 3.2.6 解码所得帧
char log_buf[256];
RK_S32 log_size = sizeof(log_buf) - 1;
RK_S32 log_len = 0;
RK_U32 err_info = mpp_frame_get_errinfo(frame);
RK_U32 discard = mpp_frame_get_discard(frame);
if (!first_frm) first_frm = mpp_time();
log_len += snprintf(log_buf + log_len, log_size - log_len,
"decode get frame %d", frame_count);
if (err_info || discard) {
log_len += snprintf(log_buf + log_len, log_size - log_len,
" err %x discard %x", err_info, discard);
}
mpp_log("%p %s\n", ctx, log_buf);
if (fp_output && !err_info)
dump_mpp_frame_to_file(frame, fp_output);
frame_count++;
}
frm_eos = mpp_frame_get_eos(frame);
mpp_frame_deinit(&frame);
get_frm = 1;
}
// try get runtime frame memory usage
if (grp) {
size_t usage = mpp_buffer_group_usage(grp);
if (usage > max_usage) max_usage = usage;
}
// if last packet is send but last frame is not found continue
if (pkt_eos && pkt_done && !frm_eos) {
msleep(1);
continue;
}
if (frm_eos) {
mpp_log("%p found last packet\n", ctx);
break;
}
//解码指定帧数 或 解码完一帧
//注意:当前是内部分帧模式,一个 packet 不一定正好包含一个 frame
if ((max_dec_frame_num > 0 && (frame_count >= max_dec_frame_num)) ||
((max_dec_frame_num == 0) && frm_eos))
break;
//内部分帧模式
if (get_frm) continue;
break;
} while (1);
//解码指定帧数 或 解码完整个码流
if ((max_dec_frame_num > 0 && (frame_count >= max_dec_frame_num)) ||
((max_dec_frame_num == 0) && frm_eos)) {
loop_end = 1;
break;
}
if (pkt_done) break;
//休眠等待,避免传入过快占满解码器内部队列
msleep(1);
} while (1);
}
t_e = mpp_time();
RK_S64 elapsed_time = t_e - t_s;
float frame_rate = (float)frame_count * 1000000 / elapsed_time;
RK_S64 delay = first_frm - first_pkt;
mpp_log("decode %d frames time %lld ms delay %3d ms fps %3.2f\n", frame_count,
(RK_S64)(elapsed_time / 1000), (RK_S32)(delay / 1000), frame_rate);
mpp_log("test success max memory %.2f MB\n", max_usage / (float)(1 << 20));
// 4. 重置解码器,恢复为正常初始化后的状态, 用于循环解码,
// 解码器在接收到eos之后不再接收码流,需要reset重置
ret = mpi->reset(ctx);
if (ret) {
mpp_err("%p mpi->reset failed\n", ctx);
goto MPP_TEST_OUT;
}
MPP_TEST_OUT:
// 5. 销毁解码器
if (ctx) {
mpp_destroy(ctx);
ctx = NULL;
}
if (cfg) {
mpp_dec_cfg_deinit(cfg);
cfg = NULL;
}
if (packet) {
mpp_packet_deinit(&packet);
packet = NULL;
}
grp = NULL;
if (buf_mgr) {
dec_buf_mgr_deinit(buf_mgr);
buf_mgr = NULL;
}
if (fp_output) {
fclose(fp_output);
fp_output = NULL;
}
return 0;
}