第五章 mpi_enc_mt_test.cpp

6 篇文章 4 订阅 ¥9.90 ¥99.00
本文档是关于MPI Enc MT Test的C++源代码解析,主要关注其在机器翻译和人工智能领域的应用。通过深入理解代码结构和功能,可以了解到并行计算在翻译模型中的作用。
摘要由CSDN通过智能技术生成

系列文章目录


文章目录


/*
 * Copyright 2022 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.
 */

#define MODULE_TAG "mpi_enc_mt_test"

#include <string.h>
#include "rk_mpi.h"

#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_time.h"
#include "mpp_list.h"
#include "mpp_lock.h"
#include "mpp_debug.h"
#include "mpp_common.h"

#include "utils.h"
#include "mpi_enc_utils.h"
#include "camera_source.h"
#include "mpp_enc_roi_utils.h"

#define BUF_COUNT   4

typedef struct {
    // base flow context
    MppCtx ctx;
    MppApi *mpi;
    RK_S32 chn;

    // global flow control flag
    RK_U32 frm_eos;
    RK_U32 pkt_eos;
    RK_U32 frm_pkt_cnt;
    RK_S32 frame_num;
    RK_S32 frm_cnt_in;
    RK_S32 frm_cnt_out;
    RK_U64 stream_size;
    /* end of encoding flag when set quit the loop */
    volatile RK_U32 loop_end;

    // src and dst
    FILE *fp_input;
    FILE *fp_output;
    FILE *fp_verify;

    /* encoder config set */
    MppEncCfg       cfg;
    MppEncPrepCfg   prep_cfg;
    MppEncRcCfg     rc_cfg;
    MppEncCodecCfg  codec_cfg;
    MppEncOSDPltCfg osd_plt_cfg;
    MppEncOSDPlt    osd_plt;
    MppEncOSDData   osd_data;
    RoiRegionCfg    roi_region;
    MppEncROICfg    roi_cfg;

    // input / output
    mpp_list        *list_buf;
    MppBufferGroup buf_grp;
    MppBuffer frm_buf[BUF_COUNT];
    MppBuffer pkt_buf[BUF_COUNT];
    RK_S32 buf_idx;
    MppEncSeiMode sei_mode;
    MppEncHeaderMode header_mode;

    // paramter for resource malloc
    RK_U32 width;
    RK_U32 height;
    RK_U32 hor_stride;
    RK_U32 ver_stride;
    MppFrameFormat fmt;
    MppCodingType type;
    RK_S32 loop_times;
    CamSource *cam_ctx;
    MppEncRoiCtx roi_ctx;

    // resources
    size_t header_size;
    size_t frame_size;
    /* NOTE: packet buffer may overflow */
    size_t packet_size;

    RK_U32 osd_enable;
    RK_U32 osd_mode;
    RK_U32 split_mode;
    RK_U32 split_arg;
    RK_U32 split_out;

    RK_U32 user_data_enable;
    RK_U32 roi_enable;

    // rate control runtime parameter
    RK_S32 fps_in_flex;
    RK_S32 fps_in_den;
    RK_S32 fps_in_num;
    RK_S32 fps_out_flex;
    RK_S32 fps_out_den;
    RK_S32 fps_out_num;
    RK_S32 bps;
    RK_S32 bps_max;
    RK_S32 bps_min;
    RK_S32 rc_mode;
    RK_S32 gop_mode;
    RK_S32 gop_len;
    RK_S32 vi_len;

    RK_S64 first_frm;
    RK_S64 first_pkt;
    RK_S64 last_pkt;
} MpiEncMtTestData;

/* For each instance thread return value */
typedef struct {
    float           frame_rate;
    RK_U64          bit_rate;

    RK_S64          time_start;
    RK_S64          time_delay;
    RK_S64          time_total;

    RK_S64          elapsed_time;
    RK_S32          frame_count;
    RK_S64          stream_size;
    RK_S64          delay;
} MpiEncMtCtxRet;

typedef struct {
    MpiEncTestArgs      *cmd;       // pointer to global command line info
    const char          *name;
    RK_S32              chn;

    pthread_t           thd_in;     // thread for for frame input
    pthread_t           thd_out;    // thread for for packet output

    struct list_head    frm_list;
    spinlock_t          frm_lock;

    MpiEncMtTestData    ctx;        // context of encoder
    MpiEncMtCtxRet      ret;        // return of encoder
} MpiEncMtCtxInfo;

/*
**函数流程:**

*   通过传入参数获取`MpiEncTestArgs`结构体,并将其赋值给局部变量`cmd`。
*   创建`MpiEncMtTestData`结构体并将其地址赋值给局部指针变量`p`。
*   将`test args`结构体中的数据复制到`context`结构体中的变量。包括格式、编码类型、比特率、帧数、GOP模式、等等。
*   如果存在输入文件,判断是否为摄像头设备。若是,则调用`camera_source_init()`函数初始化摄像头设备。否则,打开文件进行读取。
*   如果存在输出文件,则将其打开并执行写操作。
*   更新资源参数。
*   返回初始化状态。
*/
// 根据参数初始化测试环境并返回状态
MPP_RET mt_test_ctx_init(MpiEncMtCtxInfo *info)
{
    MpiEncTestArgs *cmd = info->cmd;
    // 获取test args结构体中的数据
    MpiEncMtTestData *p = &info->ctx;
    MPP_RET ret = MPP_OK;

    // 从命令行中获取变量值并赋值给context结构体中的变量
    p->width        = cmd->width;     // 宽度
    p->height       = cmd->height;    // 高度

    //如果 `hor_stride` 存在,则将其复制到 `hor_stride` 中。
    //否则,使用 `width` 换行符跳转至下一行并向左缩进并追加四个空格。
    //使用 `MPP_ALIGN ()` 对宽度进行四舍五入然后按照16字节对齐 。
    p->hor_stride   = (cmd->hor_stride) ? (cmd->hor_stride) :
                      (MPP_ALIGN(cmd->width, 16));   // 水平步长

    //如果 `ver_stride` 存在,则将其复制到 `ver_stride` 中。
    //否则,使用 `height` 换行符跳转至下一行并向左缩进并追加四个空格。
    //使用 `MPP_ALIGN()` 对高度进行四舍五入,然后按照16字节对齐。
    p->ver_stride   = (cmd->ver_stride) ? (cmd->ver_stride) :
                      (MPP_ALIGN(cmd->height, 16));  // 垂直步长
    p->fmt          = cmd->format;    // 格式
    p->type         = cmd->type;      // 编码类型
    p->bps          = cmd->bps_target;    // 目标比特率
    p->bps_min      = cmd->bps_min;       // 最小比特率
    p->bps_max      = cmd->bps_max;       // 最大比特率
    p->rc_mode      = cmd->rc_mode;       // 码率控制模式
    p->frame_num    = cmd->frame_num;     // 帧数
    if (cmd->type == MPP_VIDEO_CodingMJPEG && p->frame_num == 0) {     
        mpp_log("jpege default encode only one frame. Use -n [num] for rc case\n");
        p->frame_num = 1;
    }
    p->gop_mode     = cmd->gop_mode;      // GOP模式
    p->gop_len      = cmd->gop_len;       // GOP长度
    p->vi_len       = cmd->vi_len;        // 视频插入帧长度

    p->fps_in_flex  = cmd->fps_in_flex;   // 输入帧率分段
    p->fps_in_den   = cmd->fps_in_den;    // 输入帧率divider
    p->fps_in_num   = cmd->fps_in_num;    // 输入帧率dividend
    p->fps_out_flex = cmd->fps_out_flex;  // 输出帧率分段
    p->fps_out_den  = cmd->fps_out_den;   // 输出帧率divider
    p->fps_out_num  = cmd->fps_out_num;   // 输出帧率dividend

    // 如果文件输入存在,判断是否为摄像头设备,若不是则打开文件用于读取,否则用camera_source_init函数初始化摄像头
    if (cmd->file_input) {
        if (!strncmp(cmd->file_input, "/dev/video", 10)) {
            mpp_log("open camera device");
            // 第一个参数为stirng类型的摄像头设备名,第二个参数为int类型,表示设备可缓冲队列中的缓冲区数量,第三、四个参数为摄像头宽度和高度,第五个参数为格式。
            // 返回值为CameraContext指针
            p->cam_ctx = camera_source_init(cmd->file_input, 4, p->width, p->height, p->fmt);
            mpp_log("new framecap ok");
            if (p->cam_ctx == NULL)
                mpp_err("open %s fail", cmd->file_input);
        } else {
            // 打开文件进行读取
            p->fp_input = fopen(cmd->file_input, "rb");
            if (NULL == p->fp_input) {
                mpp_err("failed to open input file %s\n", cmd->file_input);
                mpp_err("create default yuv image for test\n");
            }
        }
    }

    // 如果文件输出存在,则将其打开并执行写操作
    if (cmd->file_output) {
        p->fp_output = fopen(cmd->file_output, "w+b");
        if (NULL == p->fp_output) {
            mpp_err("failed to open output file %s\n", cmd->file_output);
            ret = MPP_ERR_OPEN_FILE;
        }
    }

    // 


    if (cmd->file_slt) {
        p->fp_verify = fopen(cmd->file_slt, "wt");
        if (!p->fp_verify)
            mpp_err("failed to open verify file %s\n", cmd->file_slt);
    }

    // update resource parameter
    //通过`switch`语句根据上下文信息内容更新资源参数
    //由于编码所需数据格式不同,因此需要分别计算相应的帧大小(frame\_size)。
    switch (p->fmt & MPP_FRAME_FMT_MASK) {
    case MPP_FMT_YUV420SP:
    case MPP_FMT_YUV420P: {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 3 / 2;
    } break;

    case MPP_FMT_YUV422_YUYV :
    case MPP_FMT_YUV422_YVYU :
    case MPP_FMT_YUV422_UYVY :
    case MPP_FMT_YUV422_VYUY :
    case MPP_FMT_YUV422P :
    case MPP_FMT_YUV422SP : {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 2;
    } break;
    case MPP_FMT_RGB444 :
    case MPP_FMT_BGR444 :
    case MPP_FMT_RGB555 :
    case MPP_FMT_BGR555 :
    case MPP_FMT_RGB565 :
    case MPP_FMT_BGR565 :
    case MPP_FMT_RGB888 :
    case MPP_FMT_BGR888 :
    case MPP_FMT_RGB101010 :
    case MPP_FMT_BGR101010 :
    case MPP_FMT_ARGB8888 :
    case MPP_FMT_ABGR8888 :
    case MPP_FMT_BGRA8888 :
    case MPP_FMT_RGBA8888 : {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64);
    } break;

    default: {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 4;
    } break;
    }
    //根据参数初始化测试环境
    if (MPP_FRAME_FMT_IS_FBC(p->fmt))
        p->header_size = MPP_ALIGN(MPP_ALIGN(p->width, 16) * MPP_ALIGN(p->height, 16) / 16, SZ_4K);
    else
        p->header_size = 0;

    return ret;
}


MPP_RET mt_test_ctx_deinit(MpiEncMtCtxInfo *info)
{
    MpiEncMtTestData *p = NULL;  // 定义一个类型为`MpiEncMtTestData`的指针变量`p`, 初始值设为NULL。

    if (NULL == info)           // 如果传入的指针为空,直接返回通用返回码`MPP_OK`。
        return MPP_OK;

    p = &info->ctx;             // 把传入的结构体类型的成员`ctx`的地址赋值给`p`。

    if (p->cam_ctx) {           // 如果`p`的结构体成员`cam_ctx`不为空,则调用函数`camera_source_deinit`关闭摄像头并释放资源,然后把该成员置为空。
        camera_source_deinit(p->cam_ctx);
        p->cam_ctx = NULL;
    }
    if (p->fp_input) {          // 如果`p`的结构体成员`fp_input`不为空,则调用标准库函数`fclose`关闭输入文件流并释放资源,然后把该成员置为空。
        fclose(p->fp_input);
        p->fp_input = NULL;
    }
    if (p->fp_output) {         // 如果`p`的结构体成员`fp_output`不为空,则调用标准库函数`fclose`关闭输出文件流并释放资源,然后把该成员置为空。
        fclose(p->fp_output);
        p->fp_output = NULL;
    }
    if (p->fp_verify) {         // 如果`p`的结构体成员`fp_verify`不为空,则调用标准库函数`fclose`关闭验证文件流并释放资源,然后把该成员置为空。
        fclose(p->fp_verify);
        p->fp_verify = NULL;
    }

    return MPP_OK;              // 返回通用返回码`MPP_OK`
}


/*
首先通过传入的MpiEncMtCtxInfo结构体获取需要的一些信息,比如编码器参数MpiEncTestArgs,以及编码器上下文MpiEncMtTestData和MppApi对象等等。

然后,该函数设置了默认参数,主要是关于帧率和码率的设置。

之后,通过mpp_enc_cfg_set_s32和mpp_enc_cfg_set_u32函数设置了编码器的一些配置参数,例如输入输出帧率,GOP模式等等。

接下来,针对不同的码率控制模式,该函数设置了不同的码率参数,并对QP值进行了不同的设置。

最后,对于不同的编解码类型,该函数进行了不同的参数配置,例如在AVC和HEVC编码中,对于不同的码率控制模式,设置了不同的QP值。
*/
MPP_RET test_mt_cfg_setup(MpiEncMtCtxInfo *info)
{
    MpiEncTestArgs *cmd = info->cmd;
    MpiEncMtTestData *p = &info->ctx;
    MppApi *mpi = p->mpi;
    MppCtx ctx = p->ctx;
    MppEncCfg cfg = p->cfg;
    RK_U32 gop_mode = p->gop_mode;
    RK_U32 quiet = cmd->quiet;
    MPP_RET ret;

    /* setup default parameter */
    if (p->fps_in_den == 0)
        p->fps_in_den = 1;
    if (p->fps_in_num == 0)
        p->fps_in_num = 30;
    if (p->fps_out_den == 0)
        p->fps_out_den = 1;
    if (p->fps_out_num == 0)
        p->fps_out_num = 30;

    if (!p->bps)
        p->bps = p->width * p->height / 8 * (p->fps_out_num / p->fps_out_den);

    mpp_enc_cfg_set_s32(cfg, "prep:width", p->width);
    mpp_enc_cfg_set_s32(cfg, "prep:height", p->height);
    mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", p->hor_stride);
    mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", p->ver_stride);
    mpp_enc_cfg_set_s32(cfg, "prep:format", p->fmt);

    mpp_enc_cfg_set_s32(cfg, "rc:mode", p->rc_mode);

    /* fix input / output frame rate */
    mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", p->fps_in_flex);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", p->fps_in_num);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denorm", p->fps_in_den);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", p->fps_out_flex);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", p->fps_out_num);
    mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denorm", p->fps_out_den);
    mpp_enc_cfg_set_s32(cfg, "rc:gop", p->gop_len ? p->gop_len : p->fps_out_num * 2);

    /* drop frame or not when bitrate overflow */
    mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
    mpp_enc_cfg_set_u32(cfg, "rc:drop_thd", 20);        /* 20% of max bps */
    mpp_enc_cfg_set_u32(cfg, "rc:drop_gap", 1);         /* Do not continuous drop frame */

    /* setup bitrate for different rc_mode */
    mpp_enc_cfg_set_s32(cfg, "rc:bps_target", p->bps);
    switch (p->rc_mode) {
    case MPP_ENC_RC_MODE_FIXQP : {
        /* do not setup bitrate on FIXQP mode */
    } break;
    case MPP_ENC_RC_MODE_CBR : {
        /* CBR mode has narrow bound */
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16);
    } break;
    case MPP_ENC_RC_MODE_VBR :
    case MPP_ENC_RC_MODE_AVBR : {
        /* VBR mode has wide bound */
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 1 / 16);
    } break;
    default : {
        /* default use CBR mode */
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16);
    } break;
    }

    /* setup qp for different codec and rc_mode */
    switch (p->type) {
    case MPP_VIDEO_CodingAVC :
    case MPP_VIDEO_CodingHEVC : {
        switch (p->rc_mode) {
        case MPP_ENC_RC_MODE_FIXQP : {
            RK_S32 fix_qp = cmd->qp_init;

            mpp_enc_cfg_set_s32(cfg, "rc:qp_init", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 0);
        } break;
        case MPP_ENC_RC_MODE_CBR :
        case MPP_ENC_RC_MODE_VBR :
        case MPP_ENC_RC_MODE_AVBR : {
            mpp_enc_cfg_set_s32(cfg, "rc:qp_init", -1);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max", 51);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min", 10);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 51);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 10);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 2);
        } break;
        default : {
            mpp_err_f("unsupport encoder rc mode %d\n", p->rc_mode);
        } break;
        }
    } break;
    case MPP_VIDEO_CodingVP8 : {
        /* vp8 only setup base qp range */
        mpp_enc_cfg_set_s32(cfg, "rc:qp_init", 40);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_max",  127);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_min",  0);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 127);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 0);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 6);
    } break;
    case MPP_VIDEO_CodingMJPEG : {
        /* jpeg use special codec config to control qtable */
        mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", 80);
        mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", 99);
        mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", 1);
    } break;
    default : {
    } break;
    }

    /* setup codec  */
    mpp_enc_cfg_set_s32(cfg, "codec:type", p->type);
    switch (p->type) {
    case MPP_VIDEO_CodingAVC : {
        /*
         * H.264 profile_idc parameter
         * 66  - Baseline profile
         * 77  - Main profile
         * 100 - High profile
         */
        mpp_enc_cfg_set_s32(cfg, "h264:profile", 100);
        /*
         * H.264 level_idc parameter
         * 10 / 11 / 12 / 13    - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
         * 20 / 21 / 22         - cif@30fps / half-D1@@25fps / D1@12.5fps
         * 30 / 31 / 32         - D1@25fps / 720p@30fps / 720p@60fps
         * 40 / 41 / 42         - 1080p@30fps / 1080p@30fps / 1080p@60fps
         * 50 / 51 / 52         - 4K@30fps
         */
        mpp_enc_cfg_set_s32(cfg, "h264:level", 40);
        mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 1);
        mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0);
        mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 1);
    } break;
    case MPP_VIDEO_CodingHEVC :
    case MPP_VIDEO_CodingMJPEG :
    case MPP_VIDEO_CodingVP8 : {
    } break;
    default : {
        mpp_err_f("unsupport encoder coding type %d\n", p->type);
    } break;
    }

    p->split_mode = 0;
    p->split_arg = 0;
    p->split_out = 0;

    mpp_env_get_u32("split_mode", &p->split_mode, MPP_ENC_SPLIT_NONE);
    mpp_env_get_u32("split_arg", &p->split_arg, 0);
    mpp_env_get_u32("split_out", &p->split_out, 0);

    if (p->split_mode) {
        mpp_log_q(quiet, "%p split mode %d arg %d out %d\n", ctx,
                  p->split_mode, p->split_arg, p->split_out);
        mpp_enc_cfg_set_s32(cfg, "split:mode", p->split_mode);
        mpp_enc_cfg_set_s32(cfg, "split:arg", p->split_arg);
        mpp_enc_cfg_set_s32(cfg, "split:out", p->split_out);
    }

    ret = mpi->control(ctx, MPP_ENC_SET_CFG, cfg);
    if (ret) {
        mpp_err("mpi control enc set cfg failed ret %d\n", ret);
        goto RET;
    }

    /* optional */
    p->sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME;
    ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p->sei_mode);
    if (ret) {
        mpp_err("mpi control enc set sei cfg failed ret %d\n", ret);
        goto RET;
    }

    if (p->type == MPP_VIDEO_CodingAVC || p->type == MPP_VIDEO_CodingHEVC) {
        p->header_mode = MPP_ENC_HEADER_MODE_EACH_IDR;
        ret = mpi->control(ctx, MPP_ENC_SET_HEADER_MODE, &p->header_mode);
        if (ret) {
            mpp_err("mpi control enc set header mode failed ret %d\n", ret);
            goto RET;
        }
    }

    mpp_env_get_u32("gop_mode", &gop_mode, gop_mode);
    if (gop_mode) {
        MppEncRefCfg ref;

        mpp_enc_ref_cfg_init(&ref);

        if (p->gop_mode < 4)
            mpi_enc_gen_ref_cfg(ref, gop_mode);
        else
            mpi_enc_gen_smart_gop_ref_cfg(ref, p->gop_len, p->vi_len);

        ret = mpi->control(ctx, MPP_ENC_SET_REF_CFG, ref);
        if (ret) {
            mpp_err("mpi control enc set ref cfg failed ret %d\n", ret);
            goto RET;
        }
        mpp_enc_ref_cfg_deinit(&ref);
    }

    /* setup test mode by env */
    mpp_env_get_u32("osd_enable", &p->osd_enable, 0);
    mpp_env_get_u32("osd_mode", &p->osd_mode, MPP_ENC_OSD_PLT_TYPE_DEFAULT);
    mpp_env_get_u32("roi_enable", &p->roi_enable, 0);
    mpp_env_get_u32("user_data_enable", &p->user_data_enable, 0);

    if (p->roi_enable) {
        mpp_enc_roi_init(&p->roi_ctx, p->width, p->height, p->type, 4);
        mpp_assert(p->roi_ctx);
    }

RET:
    return ret;
}


// 初始化可编码上下文信息和测试参数,对应结构体为 MpiEncMtCtxInfo 和 MpiEncTestArgs
MPP_RET mt_test_res_init(MpiEncMtCtxInfo *info)
{
    // 获取测试参数
    MpiEncTestArgs *cmd = info->cmd;
    // 获取可编码上下文信息
    MpiEncMtTestData *p = &info->ctx;
    // 超时类型,初始设定为非阻塞
    MppPollType timeout = MPP_POLL_NON_BLOCK;
    // 是否安静模式
    RK_U32 quiet = cmd->quiet;
    // 返回值,默认为成功
    MPP_RET ret = MPP_OK;
    // 循环计数器
    RK_S32 i;

    // 打印开始信息
    mpp_log_q(quiet, "%s start\n", info->name);

    // 帧和包的列表,用于进出队列
    p->list_buf = new mpp_list(NULL);
    // 如果没有分配成功,则报错并返回错误代码
    if (NULL == p->list_buf) {
        mpp_err_f("failed to get mpp buffer list\n");
        return MPP_ERR_MALLOC;
    }

    // 获取缓冲区
    ret = mpp_buffer_group_get_internal(&p->buf_grp, MPP_BUFFER_TYPE_DRM);
    if (ret) {
        mpp_err_f("failed to get mpp buffer group ret %d\n", ret);
        return ret;
    }

    // 为每个缓冲区分配内存
    for (i = 0; i < BUF_COUNT; i++) {
        // 获取输入帧缓冲区
        ret = mpp_buffer_get(p->buf_grp, &p->frm_buf[i], p->frame_size + p->header_size);
        if (ret) {
            mpp_err_f("failed to get buffer for input frame ret %d\n", ret);
            return ret;
        }

        // 获取输出包缓冲区
        ret = mpp_buffer_get(p->buf_grp, &p->pkt_buf[i], p->frame_size);
        if (ret) {
            mpp_err_f("failed to get buffer for output packet ret %d\n", ret);
            return ret;
        }

        // 将帧缓冲区添加到列表中
        p->list_buf->add_at_tail(&p->frm_buf[i], sizeof(p->frm_buf[i]));
    }

    // 创建编码器示例
    ret = mpp_create(&p->ctx, &p->mpi);
    if (ret) {
        mpp_err("mpp_create failed ret %d\n", ret);
        return ret;
    }

    // 打印关于编码器开始测试的信息
    mpp_log_q(quiet, "%p encoder test start w %d h %d type %d\n",
              p->ctx, p->width, p->height, p->type);

    // 设定输入超时
    ret = p->mpi->control(p->ctx, MPP_SET_INPUT_TIMEOUT, &timeout);
    if (ret) {
        mpp_err("mpi control set input timeout %d ret %d\n", timeout, ret);
        return ret;
    }

    // 设定输出超时
    timeout = MPP_POLL_BLOCK;
    ret = p->mpi->control(p->ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout);
    if (ret) {
        mpp_err("mpi control set output timeout %d ret %d\n", timeout, ret);
        return ret;
    }

    // 初始化编码器上下文
    ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type);
    if (ret) {
        mpp_err("mpp_init failed ret %d\n", ret);
        return ret;
    }

    // 初始化编码器配置
    ret = mpp_enc_cfg_init(&p->cfg);
    if (ret) {
        mpp_err_f("mpp_enc_cfg_init failed ret %d\n", ret);
        return ret;
    }

    // 设置 MT 编码器的配置
    ret = test_mt_cfg_setup(info);
    if (ret) {
        mpp_err_f("test mpp setup failed ret %d\n", ret);
    }

    // 返回执行结果
    return ret;
}

// C++注释: mt_test_res_deinit 函数
// 接收一个指向 MpiEncMtCtxInfo 结构体的指针 info(修改指针所指向的值)
MPP_RET mt_test_res_deinit(MpiEncMtCtxInfo *info)
{
    // 取出结构体的指针
    MpiEncMtTestData *p = &info->ctx;
    // 定义 ret 并初始化为 MPP_OK
    MPP_RET ret = MPP_OK;
    RK_S32 i;

    // 对结构体中的 mpi 进行重置操作
    p->mpi->reset(p->ctx);
    if (ret) {
        // 输出错误信息
        mpp_err("mpi->reset failed\n");
        return ret;
    }

    // 释放并清空 ctx 指向的内存
    if (p->ctx) {
        mpp_destroy(p->ctx);
        p->ctx = NULL;
    }

    // 释放并清空 cfg 指向的内存
    if (p->cfg) {
        mpp_enc_cfg_deinit(p->cfg);
        p->cfg = NULL;
    }

    // 循环 BUF_COUNT 次,依次释放 frm_buf 和 pkt_buf 指向的内存
    for (i = 0; i < BUF_COUNT; i++) {
        if (p->frm_buf[i]) {
            mpp_buffer_put(p->frm_buf[i]);
            p->frm_buf[i] = NULL;
        }

        if (p->pkt_buf[i]) {
            mpp_buffer_put(p->pkt_buf[i]);
            p->pkt_buf[i] = NULL;
        }
    }

    // 释放并清空 osd_data.buf 指向的内存
    if (p->osd_data.buf) {
        mpp_buffer_put(p->osd_data.buf);
        p->osd_data.buf = NULL;
    }

    // 释放并清空 buf_grp 指向的内存
    if (p->buf_grp) {
        mpp_buffer_group_put(p->buf_grp);
        p->buf_grp = NULL;
    }

    // 释放并清空 list_buf 指向的内存
    if (p->list_buf) {
        delete p->list_buf;
        p->list_buf = NULL;
    }

    // 释放并清空 roi_ctx 指向的内存
    if (p->roi_ctx) {
        mpp_enc_roi_deinit(p->roi_ctx);
        p->roi_ctx = NULL;
    }

    // 函数返回 MPP_OK
    return MPP_OK;
}

void *enc_test_input(void *arg)
{
    MpiEncMtCtxInfo *info = (MpiEncMtCtxInfo *)arg;
    MpiEncTestArgs *cmd = info->cmd;
    MpiEncMtTestData *p = &info->ctx;
    RK_S32 chn = info->chn;
    MppApi *mpi = p->mpi;
    MppCtx ctx = p->ctx;
    mpp_list *list_buf = p->list_buf;
    RK_U32 cap_num = 0;
    RK_U32 quiet = cmd->quiet;
    MPP_RET ret = MPP_OK;

    mpp_log_q(quiet, "%s start\n", info->name);

    while (1) {
        MppMeta meta = NULL;
        MppFrame frame = NULL;
        MppBuffer buffer = NULL;
        void *buf = NULL;
        RK_S32 cam_frm_idx = -1;
        MppBuffer cam_buf = NULL;

        {
            AutoMutex autolock(list_buf->mutex());
            if (!list_buf->list_size())
                list_buf->wait();

            buffer = NULL;
            list_buf->del_at_head(&buffer, sizeof(buffer));
            if (NULL == buffer)
                continue;

            buf = mpp_buffer_get_ptr(buffer);
        }

        if (p->fp_input) {
            ret = read_image((RK_U8 *)buf, p->fp_input, p->width, p->height,
                             p->hor_stride, p->ver_stride, p->fmt);
            if (ret == MPP_NOK || feof(p->fp_input)) {
                p->frm_eos = 1;

                if (p->frame_num < 0 || p->frm_cnt_in < p->frame_num) {
                    clearerr(p->fp_input);
                    rewind(p->fp_input);
                    p->frm_eos = 0;
                    mpp_log_q(quiet, "chn %d loop times %d\n", chn, ++p->loop_times);
                    continue;
                }
                mpp_log_q(quiet, "chn %d found last frame. feof %d\n", chn, feof(p->fp_input));
            } else if (ret == MPP_ERR_VALUE)
                break;
        } else {
            if (p->cam_ctx == NULL) {
                ret = fill_image((RK_U8 *)buf, p->width, p->height, p->hor_stride,
                                 p->ver_stride, p->fmt, p->frm_cnt_in);
                if (ret)
                    break;
            } else {
                cam_frm_idx = camera_source_get_frame(p->cam_ctx);
                mpp_assert(cam_frm_idx >= 0);

                /* skip unstable frames */
                if (cap_num++ < 50) {
                    camera_source_put_frame(p->cam_ctx, cam_frm_idx);
                    continue;
                }

                cam_buf = camera_frame_to_buf(p->cam_ctx, cam_frm_idx);
                mpp_assert(cam_buf);
            }
        }

        ret = mpp_frame_init(&frame);
        if (ret) {
            mpp_err_f("mpp_frame_init failed\n");
            break;
        }

        mpp_frame_set_width(frame, p->width);
        mpp_frame_set_height(frame, p->height);
        mpp_frame_set_hor_stride(frame, p->hor_stride);
        mpp_frame_set_ver_stride(frame, p->ver_stride);
        mpp_frame_set_fmt(frame, p->fmt);
        mpp_frame_set_eos(frame, p->frm_eos);

        if (p->fp_input && feof(p->fp_input))
            mpp_frame_set_buffer(frame, NULL);
        else if (cam_buf)
            mpp_frame_set_buffer(frame, cam_buf);
        else
            mpp_frame_set_buffer(frame, buffer);

        meta = mpp_frame_get_meta(frame);

        if (p->osd_enable || p->user_data_enable || p->roi_enable) {
            if (p->user_data_enable) {
                MppEncUserData user_data;
                const char *str = "this is user data\n";

                if ((p->frm_cnt_in & 10) == 0) {
                    user_data.pdata = (void *)str;
                    user_data.len = strlen(str) + 1;
                    mpp_meta_set_ptr(meta, KEY_USER_DATA, &user_data);
                }
                static RK_U8 uuid_debug_info[16] = {
                    0x57, 0x68, 0x97, 0x80, 0xe7, 0x0c, 0x4b, 0x65,
                    0xa9, 0x06, 0xae, 0x29, 0x94, 0x11, 0xcd, 0x9a
                };

                MppEncUserDataSet data_group;
                MppEncUserDataFull datas[2];
                const char *str1 = "this is user data 1\n";
                const char *str2 = "this is user data 2\n";
                data_group.count = 2;
                datas[0].len = strlen(str1) + 1;
                datas[0].pdata = (void *)str1;
                datas[0].uuid = uuid_debug_info;

                datas[1].len = strlen(str2) + 1;
                datas[1].pdata = (void *)str2;
                datas[1].uuid = uuid_debug_info;

                data_group.datas = datas;

                mpp_meta_set_ptr(meta, KEY_USER_DATAS, &data_group);
            }

            if (p->osd_enable) {
                /* gen and cfg osd plt */
                mpi_enc_gen_osd_plt(&p->osd_plt, p->frm_cnt_in);

                p->osd_plt_cfg.change = MPP_ENC_OSD_PLT_CFG_CHANGE_ALL;
                p->osd_plt_cfg.type = MPP_ENC_OSD_PLT_TYPE_USERDEF;
                p->osd_plt_cfg.plt = &p->osd_plt;

                ret = mpi->control(ctx, MPP_ENC_SET_OSD_PLT_CFG, &p->osd_plt_cfg);
                if (ret) {
                    mpp_err("mpi control enc set osd plt failed ret %d\n", ret);
                    break;
                }

                /* gen and cfg osd plt */
                mpi_enc_gen_osd_data(&p->osd_data, p->buf_grp, p->width,
                                     p->height, p->frm_cnt_in);
                mpp_meta_set_ptr(meta, KEY_OSD_DATA, (void*)&p->osd_data);
            }

            if (p->roi_enable) {
                RoiRegionCfg *region = &p->roi_region;

                /* calculated in pixels */
                region->x = MPP_ALIGN(p->width / 8, 16);
                region->y = MPP_ALIGN(p->height / 8, 16);
                region->w = 128;
                region->h = 256;
                region->force_intra = 0;
                region->qp_mode = 1;
                region->qp_val = 24;

                mpp_enc_roi_add_region(p->roi_ctx, region);

                region->x = MPP_ALIGN(p->width / 2, 16);
                region->y = MPP_ALIGN(p->height / 4, 16);
                region->w = 256;
                region->h = 128;
                region->force_intra = 1;
                region->qp_mode = 1;
                region->qp_val = 10;

                mpp_enc_roi_add_region(p->roi_ctx, region);

                /* send roi info by metadata */
                mpp_enc_roi_setup_meta(p->roi_ctx, meta);
            }
        }

        if (!p->first_frm)
            p->first_frm = mpp_time();
        /*
         * NOTE: in non-block mode the frame can be resent.
         * The default input timeout mode is block.
         *
         * User should release the input frame to meet the requirements of
         * resource creator must be the resource destroyer.
         */
        p->frm_cnt_in++;
        do {
            ret = mpi->encode_put_frame(ctx, frame);
            if (ret)
                msleep(1);
        } while (ret);

        if (cam_frm_idx >= 0)
            camera_source_put_frame(p->cam_ctx, cam_frm_idx);

        if (p->frame_num > 0 && p->frm_cnt_in >= p->frame_num) {
            p->frm_eos = 1;
            break;
        }

        if (p->loop_end) {
            p->frm_eos = 1;
            break;
        }

        if (p->frm_eos)
            break;
    }

    return NULL;
}

void *enc_test_output(void *arg)
{
    MpiEncMtCtxInfo *info = (MpiEncMtCtxInfo *)arg;
    MpiEncTestArgs *cmd = info->cmd;
    MpiEncMtTestData *p = &info->ctx;
    MpiEncMtCtxRet *enc_ret = &info->ret;
    mpp_list *list_buf = p->list_buf;
    RK_S32 chn = info->chn;
    MppApi *mpi = p->mpi;
    MppCtx ctx = p->ctx;
    RK_U32 quiet = cmd->quiet;
    MPP_RET ret = MPP_OK;
    MppPacket packet = NULL;
    RK_U32 eoi = 1;

    void *ptr;
    size_t len;
    char log_buf[256];
    RK_S32 log_size = sizeof(log_buf) - 1;
    RK_S32 log_len = 0;

    while (1) {
        ret = mpi->encode_get_packet(ctx, &packet);
        if (ret || NULL == packet) {
            msleep(1);
            continue;
        }

        p->last_pkt = mpp_time();

        // write packet to file here
        ptr = mpp_packet_get_pos(packet);
        len = mpp_packet_get_length(packet);
        log_size = sizeof(log_buf) - 1;
        log_len = 0;

        if (!p->first_pkt)
            p->first_pkt = mpp_time();

        p->pkt_eos = mpp_packet_get_eos(packet);

        if (p->fp_output)
            fwrite(ptr, 1, len, p->fp_output);

        log_len += snprintf(log_buf + log_len, log_size - log_len,
                            "encoded frame %-4d", p->frm_cnt_out);

        /* for low delay partition encoding */
        if (mpp_packet_is_partition(packet)) {
            eoi = mpp_packet_is_eoi(packet);

            log_len += snprintf(log_buf + log_len, log_size - log_len,
                                " pkt %d", p->frm_pkt_cnt);
            p->frm_pkt_cnt = (eoi) ? (0) : (p->frm_pkt_cnt + 1);
        }

        log_len += snprintf(log_buf + log_len, log_size - log_len,
                            " size %-7zu", len);

        if (mpp_packet_has_meta(packet)) {
            MppMeta meta = mpp_packet_get_meta(packet);
            MppFrame frm = NULL;
            RK_S32 temporal_id = 0;
            RK_S32 lt_idx = -1;
            RK_S32 avg_qp = -1;

            if (MPP_OK == mpp_meta_get_s32(meta, KEY_TEMPORAL_ID, &temporal_id))
                log_len += snprintf(log_buf + log_len, log_size - log_len,
                                    " tid %d", temporal_id);

            if (MPP_OK == mpp_meta_get_s32(meta, KEY_LONG_REF_IDX, &lt_idx))
                log_len += snprintf(log_buf + log_len, log_size - log_len,
                                    " lt %d", lt_idx);

            if (MPP_OK == mpp_meta_get_s32(meta, KEY_ENC_AVERAGE_QP, &avg_qp))
                log_len += snprintf(log_buf + log_len, log_size - log_len,
                                    " qp %d", avg_qp);

            if (MPP_OK == mpp_meta_get_frame(meta, KEY_INPUT_FRAME, &frm)) {
                MppBuffer frm_buf = NULL;

                mpp_assert(frm);
                frm_buf = mpp_frame_get_buffer(frm);
                mpp_assert(frm_buf);

                {
                    AutoMutex autolock(list_buf->mutex());
                    list_buf->add_at_tail(&frm_buf, sizeof(frm_buf));
                    list_buf->signal();
                }

                mpp_frame_deinit(&frm);
            }
        }

        mpp_log_q(quiet, "chn %d %s\n", chn, log_buf);

        mpp_packet_deinit(&packet);
        fps_calc_inc(cmd->fps);

        p->stream_size += len;
        p->frm_cnt_out += eoi;

        if (p->frm_cnt_out != p->frm_cnt_in)
            continue;

        if (p->frame_num > 0 && p->frm_cnt_out >= p->frame_num) {
            p->pkt_eos = 1;
            break;
        }

        if (p->frm_eos) {
            p->pkt_eos = 1;
            break;
        }

        if (p->pkt_eos) {
            mpp_log_q(quiet, "chn %d found last packet\n", chn);
            mpp_assert(p->frm_eos);
            break;
        }
    } while (!eoi);

    enc_ret->elapsed_time = p->last_pkt - p->first_frm;
    enc_ret->frame_count = p->frm_cnt_out;
    enc_ret->stream_size = p->stream_size;
    enc_ret->frame_rate = (float)p->frm_cnt_out * 1000000 / enc_ret->elapsed_time;
    enc_ret->bit_rate = (p->stream_size * 8 * (p->fps_out_num / p->fps_out_den)) / p->frm_cnt_out;
    enc_ret->delay = p->first_pkt - p->first_frm;

    return NULL;
}

/*

该函数是一个多线程编码测试的功能实现。接受了两个参数,分别为 MpiEncTestArgs 和 const char *name。

在该函数中,首先定义一个指针变量 ctxs 用于存放多线程上下文信息,并将其初始化为NULL。紧接着定义总帧率 total_rate 变量并初始化为0.0,
返回值变量 ret 设置为 MPP_NOK(一种枚举类型),以及循环计数器变量 i 并初始化为0.

然后调用 mpp_calloc 函数根据传入的 MpiEncTestArgs 参数 nthreads 的值来为指针变量 ctxs 分配空间。若为 null,则输出错误信息并返回 -1。

随后利用一个 for 循环将 cmd、name、i 传递给多线程上下文信息结构体并逐个初始化。其中包括 mt_test_ctx_init 和 mt_test_res_init 
初始化多线程上下文信息和资源等操作,在每一次循环中都会创建多线程输入线程和多线程输出线程,使用 pthread_create 函数创建线程。

当 cmd->frame_num 小于0的时候,在控制台显示提示让用户停止循环编码。对每个进程执行 pthread_join 等待结束线程的执行,最后通过循环
遍历每个线程的返回值,统计各个线程的参数值,得出平均帧率并将相关信息输出到控制台中。

最后释放掉之前为 ctxs 分配的内存,输出测试用例的平均帧率,并将返回值 ret 作为函数返回。

*/
int enc_test_mt(MpiEncTestArgs* cmd, const char *name)
{
    // 定义变量和指针
    MpiEncMtCtxInfo *ctxs = NULL;
    float total_rate = 0.0;       
    RK_S32 ret = MPP_NOK;         
    RK_S32 i = 0;                 

    // 分配内存
    ctxs = mpp_calloc(MpiEncMtCtxInfo, cmd->nthreads);  
    if (NULL == ctxs) {          
        mpp_err("failed to alloc context for instances\n"); 
        return -1;
    }

    // 循环创建多线程并初始化,包括资源的init和线程的create
    for (i = 0; i < cmd->nthreads; i++) {
        ctxs[i].cmd = cmd;
        ctxs[i].name = name;
        ctxs[i].chn = i;

        ret = mt_test_ctx_init(&ctxs[i]);
        if (ret) {
            mpp_err_f("test ctx init failed ret %d\n", ret);
            return ret;
        }

        ret = mt_test_res_init(&ctxs[i]);
        if (ret) {
            mpp_err_f("test resource deinit failed ret %d\n", ret);
            return ret;
        }

        ret = pthread_create(&ctxs[i].thd_out, NULL, enc_test_output, &ctxs[i]);
        if (ret) {
            mpp_err("failed to create thread %d\n", i);
            return ret;
        }

        ret = pthread_create(&ctxs[i].thd_in, NULL, enc_test_input, &ctxs[i]);
        if (ret) {
            mpp_err("failed to create thread %d\n", i);
            return ret;
        }
    }

    // 如果命令行中frame_num为负数,则等待输入停止encode
    if (cmd->frame_num < 0) {
        mpp_log("*******************************************\n");
        mpp_log("**** Press Enter to stop loop encoding ****\n");
        mpp_log("*******************************************\n");

        getc(stdin);
        for (i = 0; i < cmd->nthreads; i++)
            ctxs[i].ctx.loop_end = 1;
    }

    // 等待线程结束, 并且执行资源的deinit
    for (i = 0; i < cmd->nthreads; i++) {
        pthread_join(ctxs[i].thd_in, NULL);
        pthread_join(ctxs[i].thd_out, NULL);

        ret = mt_test_res_deinit(&ctxs[i]);
        if (ret) {
            mpp_err_f("test resource deinit failed ret %d\n", ret);
            return ret;
        }

        ret = mt_test_ctx_deinit(&ctxs[i]);
        if (ret) {
            mpp_err_f("test ctx deinit failed ret %d\n", ret);
            return ret;
        }
    }

    // 计算每个通道的帧率,并累加所有通道的帧率求平均
    for (i = 0; i < cmd->nthreads; i++) {
        MpiEncMtCtxRet *enc_ret = &ctxs[i].ret;

        mpp_log("chn %d encode %d frames time %lld ms delay %3d ms fps %3.2f bps %lld\n",
                i, enc_ret->frame_count, (RK_S64)(enc_ret->elapsed_time / 1000),
                (RK_S32)(enc_ret->delay / 1000), enc_ret->frame_rate, enc_ret->bit_rate);

        total_rate += enc_ret->frame_rate;
    }

    // 释放内存,计算平均帧率输出
    MPP_FREE(ctxs);

    total_rate /= cmd->nthreads;
    mpp_log("%s average frame rate %.2f\n", name, total_rate);

    return ret;
}

// 主函数定义,参数为执行时的命令行参数数量和数组。
int main(int argc, char **argv)
{
    // 定义一个整型变量ret并赋值MPP_NOK。MPP_NOK为错误码常量。
    RK_S32 ret = MPP_NOK;
    // 分配存储命令行参数结构体的内存空间,并返回该结构体的指针。
    MpiEncTestArgs* cmd = mpi_enc_test_cmd_get();

    // 解析处理命令行选项/参数
    // 如果命令行更新到结构体表示的选项中出现错误,则跳转到DONE标签(goto语句)。
    ret = mpi_enc_test_cmd_update_by_args(cmd, argc, argv);
    if (ret)
        goto DONE;

    // 输出已解析到的命令行参数,便于调试。
    mpi_enc_test_cmd_show_opt(cmd);

    // 调用enc_test_mt方法并传入命令行参数结构体和程序名作为参数。
    ret = enc_test_mt(cmd, argv[0]);

DONE:
    // 释放分配的内存,并将指针设为空指针。这样做是为了避免悬空指针。
    mpi_enc_test_cmd_put(cmd);

    // 返回程序执行结果。
    return ret;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值