当前位置: 首页 > news >正文

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;
}

http://www.dtcms.com/a/122891.html

相关文章:

  • LeetCode 热题 100 题解记录
  • Docker Hello World
  • 计算机网络 实验三:子网划分与组网
  • GaussDB性能调优:从根因分析到优化落地
  • 10. git switch
  • Java MCP SDK 开发笔记(一)
  • 深度学习疑问--Transformer【3】:transformer的encoder和decoder分别有什么用?encoder是可以单独使用的吗
  • WHAT - React 进一步学习推荐
  • Electron 应用太重?试试 PakePlus 轻装上阵
  • LVM 扩容详解
  • 0 std::process::Command 介绍
  • 中小型网络拓扑图静态路由方式
  • 监测fastapi服务并自动拉起(不依靠dockerfile)
  • 低代码开发平台:飞帆画 echarts 仪表盘
  • Redis最佳实践——用户会话管理详解
  • 金陵幻境录——六朝古都的科技诗篇-南京
  • go游戏后端开发29:实现游戏内聊天
  • 用 HTML 网页来管理 Markdown 标题序号
  • 【微服务架构】SpringCloud Alibaba(九):分布式事务Seata使用和源码分析(TCC模式、Saga模式)
  • 分布式锁阿
  • 软件功能性测试有多重要?功能性测试工具有哪些?
  • Cocos Creator新手学习
  • day25学习Pandas库
  • mysql的主从复制
  • 中文语义相似度 + 去除标签后的网页文本(爬虫数据)
  • 彩色路径 第32次CCF-CSP计算机软件能力认证
  • 服务器运维ACL访问控制列表如何配置
  • 【Leetcode-Hot100】字母异位词分组
  • echarts图表相关
  • 【智能体开发】智能体前后端开发方案