zoukankan      html  css  js  c++  java
  • MPP-编码示例

    了解MPP的基本功能后,接下来具体分析编码的代码。首先把编码的代码提取出来,方便以后的使用。

    完整的编码代码如下,相比较给出的示例代码,做了一些改动,输入的指令全部去除,将函数入口改为利用OpenCV打开USB摄像头,调用编码函数编码,编码后的数据保存在本地。

      1 #if defined(_WIN32)
      2 #include "vld.h"
      3 #endif
      4 
      5 #define MODULE_TAG "mpi_enc_test"
      6 
      7 #include <string.h>
      8 #include <sys/time.h>
      9 
     10 #include "utils.h"
     11 #include "rk_mpi.h"
     12 #include "mpp_env.h"
     13 #include "mpp_mem.h"
     14 #include "mpp_log.h"
     15 #include "mpp_time.h"
     16 #include "mpp_common.h"
     17 
     18 #include <opencv2/core/core.hpp>
     19 #include <opencv2/highgui/highgui.hpp>
     20 #include <opencv2/opencv.hpp>
     21 
     22 using namespace std;
     23 using namespace cv;
     24 
     25 #define MAX_FILE_NAME_LENGTH 256
     26 #define calTimeCost(begin,end)(end.tv_sec*1000.-begin.tv_sec*1000.+end.tv_usec/1000.-begin.tv_usec/1000.)
     27 
     28 typedef struct 
     29 {
     30     MppCodingType   type;
     31     RK_U32          width;
     32     RK_U32          height;
     33     MppFrameFormat  format;
     34 
     35     RK_U32          num_frames;
     36 } MpiEncTestCmd;
     37 
     38 typedef struct 
     39 {
     40     //global flow control flag
     41     RK_U32 frm_eos;
     42     RK_U32 pkt_eos;
     43     RK_U32 frame_count;
     44     RK_U64 stream_size;
     45 
     46     //input ang output file
     47     FILE *fp_input;
     48     FILE *fp_output;
     49 
     50     //input and output
     51     MppBuffer frm_buf;
     52     MppEncSeiMode sei_mode;
     53 
     54     //base flow context
     55     MppCtx ctx;
     56     MppApi *mpi;
     57     MppEncPrepCfg prep_cfg;
     58     MppEncRcCfg rc_cfg;
     59     MppEncCodecCfg codec_cfg;   
     60 
     61     //paramter for resource malloc
     62     RK_U32 width;
     63     RK_U32 height;
     64     RK_U32 hor_stride; //horizontal stride  
     65     RK_U32 ver_stride; //vertical stride  
     66     MppFrameFormat fmt;
     67     MppCodingType type;
     68     RK_U32 num_frames;
     69 
     70     //resources
     71     size_t frame_size;
     72     //NOTE: packet buffer may overflow
     73     size_t packet_size;
     74 
     75     //rate control runtime parameter
     76     RK_S32 gop;
     77     RK_S32 fps;
     78     RK_S32 bps;
     79 } MpiEncTestData;
     80 
     81 MpiEncTestData data;
     82 MpiEncTestCmd  cmd_ctx;
     83 MpiEncTestData *ptr1;
     84 
     85 MPP_RET ret = MPP_OK;
     86 
     87 FILE *fp_out = fopen("result.h264", "w+b");
     88 
     89 MPP_RET test_ctx_init(MpiEncTestData **data, MpiEncTestCmd *cmd)
     90 {
     91     MpiEncTestData *p = NULL;
     92     MPP_RET ret = MPP_OK;
     93 
     94     if (!data || !cmd) 
     95        {
     96         mpp_err_f("invalid input data %p cmd %p
    ", data, cmd);
     97         return MPP_ERR_NULL_PTR;
     98     }
     99 
    100     p = mpp_calloc(MpiEncTestData, 1);
    101     if (!p) 
    102     {
    103         mpp_err_f("create MpiEncTestData failed
    ");
    104         ret = MPP_ERR_MALLOC;
    105         goto RET;
    106     }
    107 
    108     //get paramter from cmd
    109        p->width        = cmd->width;
    110     p->height       = cmd->height;
    111        p->hor_stride   = MPP_ALIGN(cmd->width, 16);
    112     p->ver_stride   = MPP_ALIGN(cmd->height, 16);
    113     p->fmt          = cmd->format;
    114     p->type         = cmd->type;
    115     p->num_frames   = cmd->num_frames;
    116 
    117     p->frame_size = p->hor_stride * p->ver_stride * 3 / 2;
    118     p->packet_size  = p->width * p->height;
    119 
    120 RET:
    121     *data = p;
    122     return ret;
    123 }
    124 
    125 MPP_RET test_mpp_setup(MpiEncTestData *p2)
    126 {
    127     MPP_RET ret;
    128     MppApi *mpi;
    129     MppCtx ctx;
    130     MppEncCodecCfg *codec_cfg;
    131     MppEncPrepCfg *prep_cfg;
    132     MppEncRcCfg *rc_cfg;
    133 
    134     if (NULL == p2)
    135     {
    136         return MPP_ERR_NULL_PTR;
    137     }
    138 
    139     mpi = p2->mpi;
    140     ctx = p2->ctx;
    141     codec_cfg = &p2->codec_cfg;
    142     prep_cfg = &p2->prep_cfg;
    143     rc_cfg = &p2->rc_cfg;
    144 
    145     p2->fps = 25;
    146     p2->gop = 50;
    147     //p2->bps = p2->width * p2->height / 5 * p2->fps;
    148     p2->bps = 4096*1024;
    149 
    150     prep_cfg->change        = MPP_ENC_PREP_CFG_CHANGE_INPUT | MPP_ENC_PREP_CFG_CHANGE_ROTATION | MPP_ENC_PREP_CFG_CHANGE_FORMAT;
    151     prep_cfg->width         = p2->width;
    152     prep_cfg->height        = p2->height;
    153     prep_cfg->hor_stride    = p2->hor_stride;
    154     prep_cfg->ver_stride    = p2->ver_stride;
    155     prep_cfg->format        = p2->fmt;
    156     prep_cfg->rotation      = MPP_ENC_ROT_0;
    157     ret = mpi->control(ctx, MPP_ENC_SET_PREP_CFG, prep_cfg);
    158     if (ret) 
    159     {
    160         mpp_err("mpi control enc set prep cfg failed ret %d
    ", ret);
    161         goto RET;
    162     }    
    163 
    164     rc_cfg->change  = MPP_ENC_RC_CFG_CHANGE_ALL;
    165     rc_cfg->rc_mode = MPP_ENC_RC_MODE_VBR;
    166     //rc_cfg->quality = MPP_ENC_RC_QUALITY_MEDIUM;
    167     rc_cfg->quality = MPP_ENC_RC_QUALITY_CQP;
    168 
    169     if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_CBR) 
    170     {    
    171         //constant bitrate has very small bps range of 1/16 bps
    172         rc_cfg->bps_target   = p2->bps;
    173         rc_cfg->bps_max      = p2->bps * 17 / 16;
    174         rc_cfg->bps_min      = p2->bps * 15 / 16;
    175     } 
    176     else if (rc_cfg->rc_mode ==  MPP_ENC_RC_MODE_VBR) 
    177     {
    178         if (rc_cfg->quality == MPP_ENC_RC_QUALITY_CQP) 
    179         {
    180             //constant QP does not have bps
    181             //rc_cfg->bps_target   = -1;
    182             //rc_cfg->bps_max      = -1;
    183             //rc_cfg->bps_min      = -1;
    184             
    185             rc_cfg->bps_target   = p2->bps;
    186             rc_cfg->bps_max      = p2->bps * 17 / 16;
    187             rc_cfg->bps_min      = p2->bps * 1 / 16;
    188         }     
    189         else 
    190         {
    191             //variable bitrate has large bps range 
    192             rc_cfg->bps_target   = p2->bps;
    193             rc_cfg->bps_max      = p2->bps * 17 / 16;
    194             rc_cfg->bps_min      = p2->bps * 1 / 16;
    195         }
    196     }
    197 
    198     //fix input / output frame rate
    199     rc_cfg->fps_in_flex      = 0;
    200     rc_cfg->fps_in_num       = p2->fps;
    201     rc_cfg->fps_in_denorm    = 1;
    202     rc_cfg->fps_out_flex     = 0;
    203     rc_cfg->fps_out_num      = p2->fps;
    204     rc_cfg->fps_out_denorm   = 1;
    205 
    206     rc_cfg->gop              = p2->gop;
    207     rc_cfg->skip_cnt         = 0;
    208 
    209     mpp_log("mpi_enc_test bps %d fps %d gop %d
    ", rc_cfg->bps_target, rc_cfg->fps_out_num, rc_cfg->gop);
    210     ret = mpi->control(ctx, MPP_ENC_SET_RC_CFG, rc_cfg);
    211     if (ret) 
    212     {
    213         mpp_err("mpi control enc set rc cfg failed ret %d
    ", ret);
    214         goto RET;
    215     }
    216 
    217     codec_cfg->coding = p2->type;
    218     codec_cfg->h264.change = MPP_ENC_H264_CFG_CHANGE_PROFILE | MPP_ENC_H264_CFG_CHANGE_ENTROPY | MPP_ENC_H264_CFG_CHANGE_TRANS_8x8;
    219     
    220      //66  - Baseline profile
    221      //77  - Main profile
    222      //100 - High profile
    223     codec_cfg->h264.profile = 77;
    224     
    225     /*
    226          * H.264 level_idc parameter
    227          * 10 / 11 / 12 / 13    - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
    228          * 20 / 21 / 22         - cif@30fps / half-D1@@25fps / D1@12.5fps
    229          * 30 / 31 / 32         - D1@25fps / 720p@30fps / 720p@60fps
    230          * 40 / 41 / 42         - 1080p@30fps / 1080p@30fps / 1080p@60fps
    231          * 50 / 51 / 52         - 4K@30fps
    232      */
    233      
    234     codec_cfg->h264.level    = 30;
    235     codec_cfg->h264.entropy_coding_mode  = 1;
    236     codec_cfg->h264.cabac_init_idc  = 0;
    237     //codec_cfg->h264.qp_min = 0;
    238     //codec_cfg->h264.qp_max = 50;
    239     
    240     //codec_cfg->h264.transform8x8_mode = 0;
    241 
    242     ret = mpi->control(ctx, MPP_ENC_SET_CODEC_CFG, codec_cfg);
    243     if (ret) 
    244     {
    245         mpp_err("mpi control enc set codec cfg failed ret %d
    ", ret);
    246         goto RET;
    247     }
    248 
    249     //optional
    250     p2->sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME;
    251     ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p2->sei_mode);
    252     if (ret) 
    253     {
    254         mpp_err("mpi control enc set sei cfg failed ret %d
    ", ret);
    255         goto RET;
    256     }
    257 
    258 RET:
    259     return ret;
    260 }
    261 
    262 int pic_num = 1;
    263 MPP_RET read_yuv_buffer(RK_U8 *buf, Mat &yuvImg, RK_U32 width, RK_U32 height)
    264 {
    265     MPP_RET ret = MPP_OK;
    266     RK_U32 read_size;
    267     RK_U32 row = 0;
    268     RK_U8 *buf_y = buf;
    269     RK_U8 *buf_u = buf_y + width * height; 
    270     RK_U8 *buf_v = buf_u + width * height / 4; 
    271 
    272     int yuv_size = width * height *3/2;
    273     
    274     memcpy(buf, yuvImg.data, yuv_size);
    275        
    276 err:
    277     return ret;
    278 }
    279 
    280 
    281 MPP_RET test_mpp_run_yuv(Mat yuvImg, MppApi *mpi1, MppCtx &ctx1, unsigned char * &H264_buf, int &length)
    282 {
    283     MpiEncTestData *p3 = ptr1;
    284     MPP_RET ret;
    285 
    286     MppFrame frame = NULL;
    287     MppPacket packet = NULL;
    288     void *buf = mpp_buffer_get_ptr(p3->frm_buf);
    289     
    290     ret = read_yuv_buffer(buf, yuvImg, p3->width, p3->height);
    291     
    292     ret = mpp_frame_init(&frame);
    293     if (ret) 
    294     {
    295         mpp_err_f("mpp_frame_init failed
    ");
    296         goto RET;
    297     }
    298 
    299     mpp_frame_set_width(frame, p3->width);
    300     mpp_frame_set_height(frame, p3->height);
    301     mpp_frame_set_hor_stride(frame, p3->hor_stride);
    302     mpp_frame_set_ver_stride(frame, p3->ver_stride);
    303     mpp_frame_set_fmt(frame, p3->fmt);
    304     mpp_frame_set_buffer(frame, p3->frm_buf);
    305     mpp_frame_set_eos(frame, p3->frm_eos);
    306     
    307     ret = mpi1->encode_put_frame(ctx1, frame);
    308 
    309     if (ret) 
    310     {
    311         mpp_err("mpp encode put frame failed
    ");
    312         goto RET;
    313     }
    314 
    315     ret = mpi1->encode_get_packet(ctx1, &packet);
    316     if (ret) 
    317     {
    318         mpp_err("mpp encode get packet failed
    ");
    319         goto RET;
    320     }
    321 
    322     if (packet) 
    323     {
    324         //write packet to file here
    325         void *ptr   = mpp_packet_get_pos(packet);
    326         size_t len  = mpp_packet_get_length(packet);
    327        
    328         p3->pkt_eos = mpp_packet_get_eos(packet);
    329         
    330         H264_buf = new unsigned char[len];
    331            
    332         memcpy(H264_buf, ptr, len);
    333         length = len;
    334         
    335         mpp_packet_deinit(&packet);
    336         p3->stream_size += len;
    337         p3->frame_count++;
    338 
    339         if (p3->pkt_eos) 
    340         {   
    341             mpp_log("found last packet
    ");
    342             mpp_assert(p3->frm_eos);
    343         }
    344     }
    345 RET:
    346     return ret;
    347 }
    348 
    349 MppApi *mpi;
    350 MppCtx ctx;
    351 
    352 MpiEncTestData *test_mpp_run_yuv_init(MpiEncTestData *p, MpiEncTestCmd *cmd, int width , int height, unsigned char * &SPS_buf, int &SPS_length)
    353 {    
    354     //MppApi *mpi;
    355     //MppCtx ctx;
    356     
    357     cmd->width = width;
    358     cmd->height = height;
    359     cmd->type = MPP_VIDEO_CodingAVC;
    360     cmd->format = MPP_FMT_YUV420P;
    361     cmd->num_frames = 0;
    362     
    363     ret = test_ctx_init(&p, cmd);
    364     if (ret) 
    365     {
    366         mpp_err_f("test data init failed ret %d
    ", ret);
    367         goto MPP_TEST_OUT;
    368     }
    369     mpp_log("p->frame_size = %d----------------
    ", p->frame_size);
    370 
    371     ret = mpp_buffer_get(NULL, &p->frm_buf, p->frame_size);
    372     if (ret) 
    373     {
    374         mpp_err_f("failed to get buffer for input frame ret %d
    ", ret);
    375         goto MPP_TEST_OUT;
    376     }
    377     mpp_log("mpi_enc_test encoder test start w %d h %d type %d
    ",p->width, p->height, p->type);
    378 
    379     //encoder demo
    380     ret = mpp_create(&p->ctx, &p->mpi);
    381     if (ret) 
    382     {
    383         mpp_err("mpp_create failed ret %d
    ", ret);
    384         goto MPP_TEST_OUT;
    385     }
    386 
    387     ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type);
    388     if (ret) 
    389     {
    390         mpp_err("mpp_init failed ret %d
    ", ret);
    391         goto MPP_TEST_OUT;
    392     }
    393 
    394     ret = test_mpp_setup(p);
    395     if (ret) 
    396     {
    397         mpp_err_f("test mpp setup failed ret %d
    ", ret);
    398         goto MPP_TEST_OUT;    
    399     }
    400     
    401     mpi = p->mpi;
    402     ctx = p->ctx;
    403     
    404     //p->fp_output = fopen("output.h264", "w+b");
    405 
    406     if (p->type == MPP_VIDEO_CodingAVC) 
    407     {
    408         MppPacket packet = NULL;
    409         
    410         ret = mpi->control(ctx, MPP_ENC_GET_EXTRA_INFO, &packet);
    411         if (ret) 
    412         {
    413             mpp_err("mpi control enc get extra info failed
    ");
    414         }
    415 
    416         //get and write sps/pps for H.264
    417         if (packet) 
    418         {
    419             void *ptr    = mpp_packet_get_pos(packet);
    420             size_t len    = mpp_packet_get_length(packet);
    421 
    422             SPS_buf = new unsigned char[len];
    423             
    424             //fwrite(ptr, 1, len, fp_out);
    425             memcpy(SPS_buf, ptr, len);
    426             SPS_length = len;
    427 
    428             packet = NULL;
    429         }
    430     }
    431 
    432     return p;
    433 MPP_TEST_OUT:
    434     return p;
    435 }
    436 
    437 MpiEncTestData *p = &data;
    438 MpiEncTestCmd *cmd = &cmd_ctx;
    439 
    440 unsigned char *H264_buf = NULL;
    441 int length = 0;
    442 unsigned char *SPS_buf = NULL;
    443 int SPS_length = 0;
    444 
    445 int frame_num = 1;
    446 
    447 int MyYuvtoH264(int width, int height, Mat yuv_frame, unsigned char* (&encode_buf), int &encode_length)
    448 {
    449     if(frame_num == 1)
    450     {    
    451         ptr1 = test_mpp_run_yuv_init(p, cmd, width, height, SPS_buf, SPS_length);    
    452         test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, length);
    453     
    454         encode_buf = new unsigned char[SPS_length + length];
    455         memcpy(encode_buf, SPS_buf, SPS_length);    
    456         memcpy(encode_buf + SPS_length, H264_buf, length);
    457         encode_length = length + SPS_length;        
    458         
    459         frame_num++;
    460         
    461         delete H264_buf;
    462         delete SPS_buf;
    463     }
    464     else
    465     {
    466         test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, length);
    467         
    468         encode_buf = new unsigned char[length];
    469         
    470         memcpy(encode_buf, H264_buf, length);
    471         encode_length = length;    
    472         
    473         delete H264_buf;
    474     }
    475 
    476     //fwrite(H264_buf, 1, length, fp_out);    
    477     return 0;
    478 }
    MPP_encode

    具体分析:

    1、MPI接口结构:

    在看代码前,整理下MPP设计的MPI接口,下面的图都来自于官方参考文档:

    MppMem:C库malloc内存的封装;

    MppBuffer:dmabuf内存的封装;

    MppPacket:一维缓存封装,可以从MppMem、MppBuffer生成,用来表示码流数据;

    MppFrame:二维帧数据封装,可以从MppMem、MppBuffer生成,用来表示图像数据;

    MppMeta、MppTask:输入输出用任务的高级组合接口,可以指定输入输出方式;

    使用MppPacket和MppFrame就可以完成一般的编解码工作。

    以视频编码为例,图像输入端把图像数据和大小交给MppFrame,通过encode_put_frame输入,在输出端通过encode_get_packet得到编码后的码流MppPacket,就完成了一帧数据的编码。 

    2、MPI接口使用:

    MPI是MPP提供给用户的接口,通过C结构里的函数指针方式提供给用户,用户可以通过MPP上下文结构MppCtx与MPI接口结构MppApi组合实现编解码器。

    如上图所示,mpp_create,mpp_init,mpp_destory操纵MppCtx接口,红框内是实现编码与解码的过程。红框内的函数调用分为编解码流程接口put/get_packet/frame和相关的control和reset接口。

    3、编码器接口

    control编码配置命令

    编码开始之前要进行一定的参数配置,参数设置通过control接口。一般需要配置三类信息:

    码率控制方式(MPPEncRcCfg),通过命令MPP_ENC_RC_CFG配置;

    输入控制配置(MppEncPrepCfg),通过命令MPP_ENC_SET_PREP_CFG配置;

    协议控制配置(MppEncCodecCfg),通过命令MPP_ENC_SET_CODEC_CFG配置;

    基本流程:

    三个配置中,码率控制配置和输入控制配置是必须的,协议控制配置时可选的高级选项。

    encode_put_frame:

    MPP_RET encode_put_frame(MppCtx ctx,MppFrame frame)

    ctx:MPP编码器实例;

    frame:带输入的图像数据;

    encode_get_packet:

    MPP_RET encode_get_packet(MppCtx ctx,MppPacket *packet)

    ctx:MPP编码器实例;

    packet:编码后的码流数据;

    H264编码器的输出数据分为头信息(SPS/PPS/SEI)和图像数据(I/P Slice)两部分,头信息通过control接口的MPP_ENC_GET_EXTRA_INFO命令获取,图像数据通过encode_get_packet接口获取。获取头信息时,要保证所有的参数更新完毕,这样生成的头信息才是最新的。目前encode_get_packet函数获取的码流都含有00 00 00 01起始码,如果想去掉起始码,可以从起始码之后的地址进行拷贝。

  • 相关阅读:
    python日志设置[logging]
    python异常处理
    python可迭代对象、迭代器、生成器
    python字典操作
    python列表和元组的操作
    python字符串操作
    python深拷贝和浅拷贝
    python时间和日期的处理
    ssh
    SSH安全外壳协议
  • 原文地址:https://www.cnblogs.com/xue0708/p/10113302.html
Copyright © 2011-2022 走看看