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\n", 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\n");
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\n", 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\n", 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\n", 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\n", 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\n", 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\n");
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\n");
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\n");
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\n");
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\n", ret);
367         goto MPP_TEST_OUT;
368     }
369     mpp_log("p->frame_size = %d----------------\n", 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\n", ret);
375         goto MPP_TEST_OUT;
376     }
377     mpp_log("mpi_enc_test encoder test start w %d h %d type %d\n",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\n", 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\n", 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\n", 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\n");
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起始码,如果想去掉起始码,可以从起始码之后的地址进行拷贝。

 

posted @ 2018-12-13 22:07  为取经而来  阅读(5958)  评论(0编辑  收藏  举报