1. 程式人生 > >MPP-編碼示例

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 - [email protected] / [email protected] / [email protected] / [email protected] 228 * 20 / 21 / 22 - [email protected] / [email protected]@25fps / [email protected] 229 * 30 / 31 / 32 - [email protected] / [email protected] / [email protected] 230 * 40 / 41 / 42 - [email protected] / [email protected] / [email protected] 231 * 50 / 51 / 52 - [email protected] 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 } 479 480 int main() 481 { 482 //VideoCapture cap("test.mp4"); 483 VideoCapture cap(0); 484 Mat frame; 485 486 unsigned char *encode_buf = NULL; 487 int encode_length = 0; 488 489 while(1) 490 { 491 cap >> frame; 492 493 Mat yuvImg; 494 cvtColor(frame, yuvImg, CV_BGR2YUV_I420); 495 496 MyYuvtoH264(frame.cols, frame.rows, yuvImg, encode_buf, encode_length); 497 fwrite(encode_buf, 1, encode_length, fp_out); 498 499 delete encode_buf; 500 } 501 502 return 0; 503 }
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起始碼,如果想去掉起始碼,可以從起始碼之後的地址進行拷貝。