当前位置:   article > 正文

RK3588处理器平台使用MPP实现H264硬件编码_rk3588 mpp

rk3588 mpp
  1. #include <string.h>
  2. #include <sys/time.h>
  3. #include "utils.h"
  4. #include "rk_mpi.h"
  5. #include "mpp_env.h"
  6. #include "mpp_mem.h"
  7. #include "mpp_log.h"
  8. #include "mpp_time.h"
  9. #include "mpp_common.h"
  10. #include <opencv2/core/core.hpp>
  11. #include <opencv2/highgui/highgui.hpp>
  12. #include <opencv2/opencv.hpp>
  13. #include <signal.h>
  14. using namespace std;
  15. using namespace cv;
  16. typedef struct
  17. {
  18. MppCodingType type;
  19. RK_U32 width;
  20. RK_U32 height;
  21. MppFrameFormat format;
  22. RK_U32 num_frames;
  23. } MpiEncTestCmd;
  24. typedef struct
  25. {
  26. //global flow control flag
  27. RK_U32 frm_eos;
  28. RK_U32 pkt_eos;
  29. RK_U32 frame_count;
  30. RK_U64 stream_size;
  31. //input ang output file
  32. FILE *fp_input;
  33. FILE *fp_output;
  34. //input and output
  35. MppBuffer frm_buf;
  36. MppEncSeiMode sei_mode;
  37. //base flow context
  38. MppCtx ctx;
  39. MppApi *mpi;
  40. MppEncPrepCfg prep_cfg;
  41. MppEncRcCfg rc_cfg;
  42. MppEncCodecCfg codec_cfg;
  43. //paramter for resource malloc
  44. RK_U32 width;
  45. RK_U32 height;
  46. RK_U32 hor_stride; //horizontal stride
  47. RK_U32 ver_stride; //vertical stride
  48. MppFrameFormat fmt;
  49. MppCodingType type;
  50. RK_U32 num_frames;
  51. //resources
  52. size_t frame_size;
  53. //NOTE: packet buffer may overflow
  54. size_t packet_size;
  55. //rate control runtime parameter
  56. RK_S32 gop;
  57. RK_S32 fps;
  58. RK_S32 bps;
  59. } MpiEncTestData;
  60. //------------------------------------------------------------------------------
  61. MpiEncTestData encoder_params;
  62. MpiEncTestData *encoder_params_ptr = &encoder_params;
  63. //
  64. MppApi *mpi;
  65. MppCtx ctx;
  66. //
  67. bool first_frame_flg = true;
  68. //------------------------------------------------------------------------------
  69. //功能:MPP上下文初始化
  70. //说明:根据MpiEncTestCmd参数设置MpiEncTestData参数
  71. //------------------------------------------------------------------------------
  72. MPP_RET test_ctx_init(MpiEncTestData **data, MpiEncTestCmd *cmd)
  73. {
  74. MpiEncTestData *p = NULL;
  75. MPP_RET ret = MPP_OK;
  76. if (!data || !cmd)
  77. {
  78. mpp_err_f("invalid input data %p cmd %p\n", data, cmd);
  79. return MPP_ERR_NULL_PTR;
  80. }
  81. p = mpp_calloc(MpiEncTestData, 1);
  82. if (!p)
  83. {
  84. mpp_err_f("create MpiEncTestData failed\n");
  85. ret = MPP_ERR_MALLOC;
  86. goto RET;
  87. }
  88. //get paramter from cmd
  89. p->width = cmd->width;
  90. p->height = cmd->height;
  91. p->hor_stride = MPP_ALIGN(cmd->width, 16);
  92. p->ver_stride = MPP_ALIGN(cmd->height, 16);
  93. p->fmt = cmd->format;
  94. p->type = cmd->type;
  95. p->num_frames = cmd->num_frames;
  96. p->frame_size = p->hor_stride * p->ver_stride * 3 / 2;
  97. p->packet_size = p->width * p->height;
  98. RET:
  99. *data = p;
  100. return ret;
  101. }
  102. //------------------------------------------------------------------------------
  103. //功能:设置MPP编码器参数
  104. //说明:1-输入控制配置;2-码率控制配置;3-协议控制配置;4-SEI模式配置
  105. //------------------------------------------------------------------------------
  106. MPP_RET test_mpp_setup(MpiEncTestData *p)
  107. {
  108. MPP_RET ret;
  109. MppApi *mpi;
  110. MppCtx ctx;
  111. MppEncCodecCfg *codec_cfg;
  112. MppEncPrepCfg *prep_cfg;
  113. MppEncRcCfg *rc_cfg;
  114. if (NULL == p)
  115. {
  116. return MPP_ERR_NULL_PTR;
  117. }
  118. mpi = p->mpi;
  119. ctx = p->ctx;
  120. codec_cfg = &p->codec_cfg;
  121. prep_cfg = &p->prep_cfg;
  122. rc_cfg = &p->rc_cfg;
  123. p->fps = 30;
  124. p->gop = 60;
  125. //p->bps = p->width * p->height / 5 * p->fps;
  126. p->bps = 4096*1024;
  127. //1--输入控制配置
  128. prep_cfg->change = MPP_ENC_PREP_CFG_CHANGE_INPUT | MPP_ENC_PREP_CFG_CHANGE_ROTATION | MPP_ENC_PREP_CFG_CHANGE_FORMAT;
  129. prep_cfg->width = p->width;
  130. prep_cfg->height = p->height;
  131. prep_cfg->hor_stride = p->hor_stride;
  132. prep_cfg->ver_stride = p->ver_stride;
  133. prep_cfg->format = p->fmt;
  134. prep_cfg->rotation = MPP_ENC_ROT_0;
  135. ret = mpi->control(ctx, MPP_ENC_SET_PREP_CFG, prep_cfg);
  136. if (ret)
  137. {
  138. mpp_err("mpi control enc set prep cfg failed ret %d\n", ret);
  139. goto RET;
  140. }
  141. //2--码率控制配置
  142. rc_cfg->change = MPP_ENC_RC_CFG_CHANGE_ALL;
  143. rc_cfg->rc_mode = MPP_ENC_RC_MODE_VBR;
  144. rc_cfg->quality = MPP_ENC_RC_QUALITY_CQP;
  145. if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_CBR)
  146. {
  147. //constant bitrate has very small bps range of 1/16 bps
  148. rc_cfg->bps_target = p->bps;
  149. rc_cfg->bps_max = p->bps * 17 / 16;
  150. rc_cfg->bps_min = p->bps * 15 / 16;
  151. }
  152. else if (rc_cfg->rc_mode == MPP_ENC_RC_MODE_VBR)
  153. {
  154. if (rc_cfg->quality == MPP_ENC_RC_QUALITY_CQP)
  155. {
  156. //constant QP does not have bps
  157. //rc_cfg->bps_target = -1;
  158. //rc_cfg->bps_max = -1;
  159. //rc_cfg->bps_min = -1;
  160. rc_cfg->bps_target = p->bps;
  161. rc_cfg->bps_max = p->bps * 17 / 16;
  162. rc_cfg->bps_min = p->bps * 1 / 16;
  163. }
  164. else
  165. {
  166. //variable bitrate has large bps range
  167. rc_cfg->bps_target = p->bps;
  168. rc_cfg->bps_max = p->bps * 17 / 16;
  169. rc_cfg->bps_min = p->bps * 1 / 16;
  170. }
  171. }
  172. //fix input / output frame rate
  173. rc_cfg->fps_in_flex = 0;
  174. rc_cfg->fps_in_num = p->fps;
  175. rc_cfg->fps_in_denorm = 1;
  176. rc_cfg->fps_out_flex = 0;
  177. rc_cfg->fps_out_num = p->fps;
  178. rc_cfg->fps_out_denorm = 1;
  179. rc_cfg->gop = p->gop;
  180. rc_cfg->skip_cnt = 0;
  181. mpp_log("mpi_enc_test bps %d fps %d gop %d\n", rc_cfg->bps_target, rc_cfg->fps_out_num, rc_cfg->gop);
  182. ret = mpi->control(ctx, MPP_ENC_SET_RC_CFG, rc_cfg);
  183. if (ret)
  184. {
  185. mpp_err("mpi control enc set rc cfg failed ret %d\n", ret);
  186. goto RET;
  187. }
  188. //3--协议控制配置
  189. codec_cfg->coding = p->type;
  190. codec_cfg->h264.change = MPP_ENC_H264_CFG_CHANGE_PROFILE | MPP_ENC_H264_CFG_CHANGE_ENTROPY | MPP_ENC_H264_CFG_CHANGE_TRANS_8x8;
  191. //66 - Baseline profile
  192. //77 - Main profile
  193. //100 - High profile
  194. codec_cfg->h264.profile = 77;
  195. /*
  196. * H.264 level_idc parameter
  197. * 10 / 11 / 12 / 13 - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
  198. * 20 / 21 / 22 - cif@30fps / half-D1@@25fps / D1@12.5fps
  199. * 30 / 31 / 32 - D1@25fps / 720p@30fps / 720p@60fps
  200. * 40 / 41 / 42 - 1080p@30fps / 1080p@30fps / 1080p@60fps
  201. * 50 / 51 / 52 - 4K@30fps
  202. */
  203. codec_cfg->h264.level = 41;
  204. codec_cfg->h264.entropy_coding_mode = 1;
  205. codec_cfg->h264.cabac_init_idc = 0;
  206. //codec_cfg->h264.qp_min = 0;
  207. //codec_cfg->h264.qp_max = 50;
  208. //codec_cfg->h264.transform8x8_mode = 0;
  209. ret = mpi->control(ctx, MPP_ENC_SET_CODEC_CFG, codec_cfg);
  210. if (ret)
  211. {
  212. mpp_err("mpi control enc set codec cfg failed ret %d\n", ret);
  213. goto RET;
  214. }
  215. //4--SEI模式配置
  216. p->sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME;
  217. ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p->sei_mode);
  218. if (ret)
  219. {
  220. mpp_err("mpi control enc set sei cfg failed ret %d\n", ret);
  221. goto RET;
  222. }
  223. RET:
  224. return ret;
  225. }
  226. //------------------------------------------------------------------------------
  227. //功能:将YUV420视频帧数据填充到MPP buffer
  228. //说明:使用16字节对齐,MPP可以实现零拷贝,提高效率
  229. //------------------------------------------------------------------------------
  230. void read_yuv_buffer(RK_U8 *buf, Mat &yuvImg, RK_U32 width, RK_U32 height)
  231. {
  232. RK_U8 *buf_y = buf;
  233. RK_U8 *buf_u = buf + MPP_ALIGN(width, 16) * MPP_ALIGN(height, 16);
  234. RK_U8 *buf_v = buf_u + MPP_ALIGN(width, 16) * MPP_ALIGN(height, 16) / 4;
  235. //
  236. RK_U8 *yuvImg_y = yuvImg.data;
  237. RK_U8 *yuvImg_u = yuvImg_y + width * height;
  238. RK_U8 *yuvImg_v = yuvImg_u + width * height / 4;
  239. //
  240. memcpy(buf_y, yuvImg_y, width * height);
  241. memcpy(buf_u, yuvImg_u, width * height / 4);
  242. memcpy(buf_v, yuvImg_v, width * height / 4);
  243. }
  244. //------------------------------------------------------------------------------
  245. //功能:MPP执行编码
  246. //------------------------------------------------------------------------------
  247. MPP_RET test_mpp_run_yuv(Mat yuvImg, MppApi *mpi, MppCtx &ctx, unsigned char * &H264_buf, int &length)
  248. {
  249. MpiEncTestData *p = encoder_params_ptr;
  250. MPP_RET ret;
  251. MppFrame frame = NULL;
  252. MppPacket packet = NULL;
  253. void *buf = mpp_buffer_get_ptr(p->frm_buf);
  254. read_yuv_buffer((RK_U8*)buf, yuvImg, p->width, p->height);
  255. ret = mpp_frame_init(&frame);
  256. if (ret)
  257. {
  258. mpp_err_f("mpp_frame_init failed\n");
  259. goto RET;
  260. }
  261. //
  262. mpp_frame_set_width(frame, p->width);
  263. mpp_frame_set_height(frame, p->height);
  264. mpp_frame_set_hor_stride(frame, p->hor_stride);
  265. mpp_frame_set_ver_stride(frame, p->ver_stride);
  266. mpp_frame_set_fmt(frame, p->fmt);
  267. mpp_frame_set_buffer(frame, p->frm_buf);
  268. mpp_frame_set_eos(frame, p->frm_eos);
  269. ret = mpi->encode_put_frame(ctx, frame);
  270. if (ret)
  271. {
  272. mpp_err("mpp encode put frame failed\n");
  273. goto RET;
  274. }
  275. ret = mpi->encode_get_packet(ctx, &packet);
  276. if (ret)
  277. {
  278. mpp_err("mpp encode get packet failed\n");
  279. goto RET;
  280. }
  281. if (packet)
  282. {
  283. void *ptr = mpp_packet_get_pos(packet);
  284. size_t len = mpp_packet_get_length(packet);
  285. p->pkt_eos = mpp_packet_get_eos(packet);
  286. //
  287. H264_buf = new unsigned char[len];
  288. memcpy(H264_buf, ptr, len);
  289. length = len;
  290. mpp_packet_deinit(&packet);
  291. p->stream_size += len;
  292. p->frame_count++;
  293. if (p->pkt_eos)
  294. {
  295. mpp_log("found last packet\n");
  296. mpp_assert(p->frm_eos);
  297. }
  298. }
  299. RET:
  300. return ret;
  301. }
  302. //------------------------------------------------------------------------------
  303. //功能:初始化MPP编码器
  304. //------------------------------------------------------------------------------
  305. MpiEncTestData *test_mpp_run_yuv_init(MpiEncTestData *p, int width , int height, unsigned char * &SPS_buf, int &SPS_length)
  306. {
  307. MPP_RET ret;
  308. //
  309. MpiEncTestCmd cmd;
  310. cmd.width = width;
  311. cmd.height = height;
  312. cmd.type = MPP_VIDEO_CodingAVC;
  313. cmd.format = MPP_FMT_YUV420P;
  314. cmd.num_frames = 0;
  315. ret = test_ctx_init(&p, &cmd);
  316. if (ret)
  317. {
  318. mpp_err_f("test data init failed ret %d\n", ret);
  319. goto MPP_TEST_OUT;
  320. }
  321. //
  322. mpp_log("p->frame_size = %d----------------\n", p->frame_size);
  323. ret = mpp_buffer_get(NULL, &p->frm_buf, p->frame_size);
  324. if (ret)
  325. {
  326. mpp_err_f("failed to get buffer for input frame ret %d\n", ret);
  327. goto MPP_TEST_OUT;
  328. }
  329. //
  330. mpp_log("mpi_enc_test encoder test start w %d h %d type %d\n",p->width, p->height, p->type);
  331. //encoder demo
  332. ret = mpp_create(&p->ctx, &p->mpi);
  333. if (ret)
  334. {
  335. mpp_err("mpp_create failed ret %d\n", ret);
  336. goto MPP_TEST_OUT;
  337. }
  338. //
  339. ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type);
  340. if (ret)
  341. {
  342. mpp_err("mpp_init failed ret %d\n", ret);
  343. goto MPP_TEST_OUT;
  344. }
  345. //
  346. ret = test_mpp_setup(p);
  347. if (ret)
  348. {
  349. mpp_err_f("test mpp setup failed ret %d\n", ret);
  350. goto MPP_TEST_OUT;
  351. }
  352. mpi = p->mpi;
  353. ctx = p->ctx;
  354. //
  355. if (p->type == MPP_VIDEO_CodingHEVC)
  356. {
  357. MppPacket packet = NULL;
  358. ret = mpi->control(ctx, MPP_ENC_GET_EXTRA_INFO, &packet);
  359. if (ret)
  360. {
  361. mpp_err("mpi control enc get extra info failed\n");
  362. }
  363. //get and write sps/pps for H.264
  364. if (packet)
  365. {
  366. void *ptr = mpp_packet_get_pos(packet);
  367. size_t len = mpp_packet_get_length(packet);
  368. SPS_buf = new unsigned char[len];
  369. memcpy(SPS_buf, ptr, len);
  370. SPS_length = len;
  371. packet = NULL;
  372. }
  373. }
  374. return p;
  375. MPP_TEST_OUT:
  376. return p;
  377. }
  378. //------------------------------------------------------------------------------
  379. //功能:将YUV420格式图像帧编码为H264数据包
  380. //------------------------------------------------------------------------------
  381. void YuvtoH264(int width, int height, Mat yuv_frame, unsigned char* (&encode_buf), int &encode_length)
  382. {
  383. unsigned char *H264_buf = NULL;
  384. int H264_buf_length = 0;
  385. unsigned char *SPS_buf = NULL;
  386. int SPS_length = 0;
  387. //
  388. if(first_frame_flg == true)
  389. {
  390. encoder_params_ptr = test_mpp_run_yuv_init(encoder_params_ptr, width, height, SPS_buf, SPS_length);
  391. //SPS数据,可以不使用
  392. // test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, length);
  393. // encode_buf = new unsigned char[SPS_length + length];
  394. // memcpy(encode_buf, SPS_buf, SPS_length);
  395. // memcpy(encode_buf + SPS_length, H264_buf, length);
  396. // encode_length = length + SPS_length;
  397. test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, H264_buf_length);
  398. encode_buf = new unsigned char[H264_buf_length];
  399. memcpy(encode_buf, H264_buf, H264_buf_length);
  400. encode_length = H264_buf_length;
  401. first_frame_flg = false;
  402. delete H264_buf;
  403. delete SPS_buf;
  404. printf("first_frame! \n");
  405. printf("SPS length: %d! \n", SPS_length);
  406. printf("frame length: %d! \n", H264_buf_length);
  407. }
  408. else
  409. {
  410. test_mpp_run_yuv(yuv_frame, mpi, ctx, H264_buf, H264_buf_length);
  411. encode_buf = new unsigned char[H264_buf_length];
  412. memcpy(encode_buf, H264_buf, H264_buf_length);
  413. encode_length = H264_buf_length;
  414. printf("frame length: %d! \n", H264_buf_length);
  415. delete H264_buf;
  416. }
  417. }
  418. int main(int argc, char **argv)
  419. {
  420. signal(SIGPIPE, SIG_IGN);
  421. FILE* fp_output = NULL;
  422. //opencv读取视频帧
  423. cv::Mat frame;
  424. frame = cv::imread("../mpp_enc_test_rtsp_push/img_01.jpg");
  425. cv::resize(frame,frame,cv::Size(1920,1080));
  426. cv::cvtColor(frame, frame, cv::COLOR_BGR2YUV_I420);
  427. //创建Linux FIFO文件
  428. ::unlink("/home/monster/live/testProgs/test.264");
  429. ::mkfifo("/home/monster/live/testProgs/test.264", O_CREAT | O_EXCL | 777);
  430. fp_output = fopen("/home/monster/live/testProgs/test.264", "w+b");
  431. //创建本地文件
  432. // fp_output = fopen("test.264", "w+b");
  433. int i = 0;
  434. while(1)
  435. {
  436. i++;
  437. // if(i == 30 * 60)
  438. // {
  439. // break;
  440. // }
  441. printf("frame:%d\n", i);
  442. //在图像上绘制帧计数
  443. cv::Mat frame_yuv = frame.clone();
  444. std::string str = to_string(i);
  445. cv::putText(frame_yuv, str,cv::Point(100,100), cv::FONT_HERSHEY_PLAIN, 2.0f,cv::Scalar(0,0,255));
  446. //H264编码
  447. uchar *h264_buf = nullptr;
  448. int buf_len = 0;
  449. YuvtoH264(1920, 1080, frame_yuv, h264_buf, buf_len);
  450. fwrite(h264_buf, 1, buf_len, fp_output);
  451. delete h264_buf;
  452. h264_buf = nullptr;
  453. }
  454. fclose(fp_output);
  455. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/841804
推荐阅读
相关标签
  

闽ICP备14008679号