1 /*
2 * Copyright 2022 Rockchip Electronics Co. LTD
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #if defined(_WIN32)
18 #include "vld.h"
19 #endif
20
21 #define MODULE_TAG "mpi_dec_nt_test"
22
23 #include <string.h>
24 #include "rk_mpi.h"
25
26 #include "mpp_mem.h"
27 #include "mpp_env.h"
28 #include "mpp_time.h"
29 #include "mpp_common.h"
30 #include "mpi_dec_utils.h"
31
32 typedef struct {
33 MpiDecTestCmd *cmd;
34 MppCtx ctx;
35 MppApi *mpi;
36 RK_U32 quiet;
37
38 /* end of stream flag when set quit the loop */
39 RK_U32 loop_end;
40
41 /* input and output */
42 MppBufferGroup frm_grp;
43 MppPacket packet;
44 MppFrame frame;
45
46 FILE *fp_output;
47 RK_S32 frame_count;
48 RK_S32 frame_num;
49
50 RK_S64 first_pkt;
51 RK_S64 first_frm;
52
53 size_t max_usage;
54 float frame_rate;
55 RK_S64 elapsed_time;
56 RK_S64 delay;
57 FILE *fp_verify;
58 FrmCrc checkcrc;
59 } MpiDecLoopData;
60
dec_loop(MpiDecLoopData * data)61 static int dec_loop(MpiDecLoopData *data)
62 {
63 RK_U32 pkt_done = 0;
64 RK_U32 pkt_eos = 0;
65 MPP_RET ret = MPP_OK;
66 MpiDecTestCmd *cmd = data->cmd;
67 MppCtx ctx = data->ctx;
68 MppApi *mpi = data->mpi;
69 MppPacket packet = data->packet;
70 FileBufSlot *slot = NULL;
71 RK_U32 quiet = data->quiet;
72 FrmCrc *checkcrc = &data->checkcrc;
73
74 // when packet size is valid read the input binary file
75 ret = reader_read(cmd->reader, &slot);
76
77 mpp_assert(ret == MPP_OK);
78 mpp_assert(slot);
79
80 pkt_eos = slot->eos;
81
82 if (pkt_eos) {
83 if (data->frame_num < 0 || data->frame_num > data->frame_count) {
84 mpp_log_q(quiet, "%p loop again\n", ctx);
85 reader_rewind(cmd->reader);
86 pkt_eos = 0;
87 } else {
88 mpp_log_q(quiet, "%p found last packet\n", ctx);
89 data->loop_end = 1;
90 }
91 }
92
93 mpp_packet_set_data(packet, slot->data);
94 mpp_packet_set_size(packet, slot->size);
95 mpp_packet_set_pos(packet, slot->data);
96 mpp_packet_set_length(packet, slot->size);
97 // setup eos flag
98 if (pkt_eos)
99 mpp_packet_set_eos(packet);
100
101 do {
102 RK_U32 frm_eos = 0;
103 RK_S32 get_frm = 0;
104 MppFrame frame = NULL;
105
106 // send the packet first if packet is not done
107 ret = mpi->decode(ctx, packet, &frame);
108 if (ret)
109 mpp_err("decode failed ret %d\n", ret);
110
111 // then get all available frame and release
112 if (frame) {
113 if (mpp_frame_get_info_change(frame)) {
114 RK_U32 width = mpp_frame_get_width(frame);
115 RK_U32 height = mpp_frame_get_height(frame);
116 RK_U32 hor_stride = mpp_frame_get_hor_stride(frame);
117 RK_U32 ver_stride = mpp_frame_get_ver_stride(frame);
118 RK_U32 buf_size = mpp_frame_get_buf_size(frame);
119
120 mpp_log_q(quiet, "%p decode_get_frame get info changed found\n", ctx);
121 mpp_log_q(quiet, "%p decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d",
122 ctx, width, height, hor_stride, ver_stride, buf_size);
123
124 /*
125 * NOTE: We can choose decoder's buffer mode here.
126 * There are three mode that decoder can support:
127 *
128 * Mode 1: Pure internal mode
129 * In the mode user will NOT call MPP_DEC_SET_EXT_BUF_GROUP
130 * control to decoder. Only call MPP_DEC_SET_INFO_CHANGE_READY
131 * to let decoder go on. Then decoder will use create buffer
132 * internally and user need to release each frame they get.
133 *
134 * Advantage:
135 * Easy to use and get a demo quickly
136 * Disadvantage:
137 * 1. The buffer from decoder may not be return before
138 * decoder is close. So memroy leak or crash may happen.
139 * 2. The decoder memory usage can not be control. Decoder
140 * is on a free-to-run status and consume all memory it can
141 * get.
142 * 3. Difficult to implement zero-copy display path.
143 *
144 * Mode 2: Half internal mode
145 * This is the mode current test code using. User need to
146 * create MppBufferGroup according to the returned info
147 * change MppFrame. User can use mpp_buffer_group_limit_config
148 * function to limit decoder memory usage.
149 *
150 * Advantage:
151 * 1. Easy to use
152 * 2. User can release MppBufferGroup after decoder is closed.
153 * So memory can stay longer safely.
154 * 3. Can limit the memory usage by mpp_buffer_group_limit_config
155 * Disadvantage:
156 * 1. The buffer limitation is still not accurate. Memory usage
157 * is 100% fixed.
158 * 2. Also difficult to implement zero-copy display path.
159 *
160 * Mode 3: Pure external mode
161 * In this mode use need to create empty MppBufferGroup and
162 * import memory from external allocator by file handle.
163 * On Android surfaceflinger will create buffer. Then
164 * mediaserver get the file handle from surfaceflinger and
165 * commit to decoder's MppBufferGroup.
166 *
167 * Advantage:
168 * 1. Most efficient way for zero-copy display
169 * Disadvantage:
170 * 1. Difficult to learn and use.
171 * 2. Player work flow may limit this usage.
172 * 3. May need a external parser to get the correct buffer
173 * size for the external allocator.
174 *
175 * The required buffer size caculation:
176 * hor_stride * ver_stride * 3 / 2 for pixel data
177 * hor_stride * ver_stride / 2 for extra info
178 * Total hor_stride * ver_stride * 2 will be enough.
179 *
180 * For H.264/H.265 20+ buffers will be enough.
181 * For other codec 10 buffers will be enough.
182 */
183
184 if (NULL == data->frm_grp) {
185 /* If buffer group is not set create one and limit it */
186 ret = mpp_buffer_group_get_internal(&data->frm_grp, MPP_BUFFER_TYPE_ION);
187 if (ret) {
188 mpp_err("%p get mpp buffer group failed ret %d\n", ctx, ret);
189 break;
190 }
191
192 /* Set buffer to mpp decoder */
193 ret = mpi->control(ctx, MPP_DEC_SET_EXT_BUF_GROUP, data->frm_grp);
194 if (ret) {
195 mpp_err("%p set buffer group failed ret %d\n", ctx, ret);
196 break;
197 }
198 } else {
199 /* If old buffer group exist clear it */
200 ret = mpp_buffer_group_clear(data->frm_grp);
201 if (ret) {
202 mpp_err("%p clear buffer group failed ret %d\n", ctx, ret);
203 break;
204 }
205 }
206
207 /* Use limit config to limit buffer count to 24 with buf_size */
208 ret = mpp_buffer_group_limit_config(data->frm_grp, buf_size, 24);
209 if (ret) {
210 mpp_err("%p limit buffer group failed ret %d\n", ctx, ret);
211 break;
212 }
213
214 /*
215 * All buffer group config done. Set info change ready to let
216 * decoder continue decoding
217 */
218 ret = mpi->control(ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
219 if (ret) {
220 mpp_err("%p info change ready failed ret %d\n", ctx, ret);
221 break;
222 }
223 } else {
224 char log_buf[256];
225 RK_S32 log_size = sizeof(log_buf) - 1;
226 RK_S32 log_len = 0;
227 RK_U32 err_info = mpp_frame_get_errinfo(frame);
228 RK_U32 discard = mpp_frame_get_discard(frame);
229
230 if (!data->first_frm)
231 data->first_frm = mpp_time();
232
233 log_len += snprintf(log_buf + log_len, log_size - log_len,
234 "decode get frame %d", data->frame_count);
235
236 if (mpp_frame_has_meta(frame)) {
237 MppMeta meta = mpp_frame_get_meta(frame);
238 RK_S32 temporal_id = 0;
239
240 mpp_meta_get_s32(meta, KEY_TEMPORAL_ID, &temporal_id);
241
242 log_len += snprintf(log_buf + log_len, log_size - log_len,
243 " tid %d", temporal_id);
244 }
245
246 if (err_info || discard) {
247 log_len += snprintf(log_buf + log_len, log_size - log_len,
248 " err %x discard %x", err_info, discard);
249 }
250 mpp_log_q(quiet, "%p %s\n", ctx, log_buf);
251
252 data->frame_count++;
253 if (data->fp_output && !err_info)
254 dump_mpp_frame_to_file(frame, data->fp_output);
255
256 if (data->fp_verify) {
257 calc_frm_crc(frame, checkcrc);
258 write_frm_crc(data->fp_verify, checkcrc);
259 }
260
261 fps_calc_inc(cmd->fps);
262 }
263 frm_eos = mpp_frame_get_eos(frame);
264 mpp_frame_deinit(&frame);
265 get_frm = 1;
266 }
267
268 // try get runtime frame memory usage
269 if (data->frm_grp) {
270 size_t usage = mpp_buffer_group_usage(data->frm_grp);
271 if (usage > data->max_usage)
272 data->max_usage = usage;
273 }
274
275 // when get one output frame check the output frame count limit
276 if (get_frm) {
277 if (data->frame_num > 0) {
278 // when get enough frame quit
279 if (data->frame_count >= data->frame_num) {
280 data->loop_end = 1;
281 break;
282 }
283 } else {
284 // when get last frame quit
285 if (frm_eos) {
286 mpp_log_q(quiet, "%p found last packet\n", ctx);
287 data->loop_end = 1;
288 break;
289 }
290 }
291 }
292
293 if (packet) {
294 if (mpp_packet_get_length(packet)) {
295 msleep(1);
296 continue;
297 }
298
299 if (!data->first_pkt)
300 data->first_pkt = mpp_time();
301
302 packet = NULL;
303 pkt_done = 1;
304 }
305
306 mpp_assert(pkt_done);
307
308 // if last packet is send but last frame is not found continue
309 if (pkt_eos && !frm_eos) {
310 msleep(1);
311 continue;
312 }
313
314 if (pkt_done)
315 break;
316
317 /*
318 * why sleep here:
319 * mpi->decode_put_packet will failed when packet in internal queue is
320 * full,waiting the package is consumed .Usually hardware decode one
321 * frame which resolution is 1080p needs 2 ms,so here we sleep 1ms
322 * * is enough.
323 */
324 msleep(1);
325 } while (1);
326
327 return ret;
328 }
329
thread_decode(void * arg)330 void *thread_decode(void *arg)
331 {
332 MpiDecLoopData *data = (MpiDecLoopData *)arg;
333 RK_S64 t_s, t_e;
334
335 memset(&data->checkcrc, 0, sizeof(data->checkcrc));
336 data->checkcrc.luma.sum = mpp_malloc(RK_ULONG, 512);
337 data->checkcrc.chroma.sum = mpp_malloc(RK_ULONG, 512);
338
339 t_s = mpp_time();
340
341 while (!data->loop_end)
342 dec_loop(data);
343
344 t_e = mpp_time();
345 data->elapsed_time = t_e - t_s;
346 data->frame_count = data->frame_count;
347 data->frame_rate = (float)data->frame_count * 1000000 / data->elapsed_time;
348 data->delay = data->first_frm - data->first_pkt;
349
350 mpp_log("decode %d frames time %lld ms delay %3d ms fps %3.2f\n",
351 data->frame_count, (RK_S64)(data->elapsed_time / 1000),
352 (RK_S32)(data->delay / 1000), data->frame_rate);
353
354 MPP_FREE(data->checkcrc.luma.sum);
355 MPP_FREE(data->checkcrc.chroma.sum);
356
357 return NULL;
358 }
359
dec_nt_decode(MpiDecTestCmd * cmd)360 int dec_nt_decode(MpiDecTestCmd *cmd)
361 {
362 // base flow context
363 MppCtx ctx = NULL;
364 MppApi *mpi = NULL;
365
366 // input / output
367 MppPacket packet = NULL;
368 MppFrame frame = NULL;
369
370 // paramter for resource malloc
371 RK_U32 width = cmd->width;
372 RK_U32 height = cmd->height;
373 MppCodingType type = cmd->type;
374
375 // config for runtime mode
376 MppDecCfg cfg = NULL;
377 RK_U32 need_split = 1;
378
379 // resources
380 MppBuffer frm_buf = NULL;
381 pthread_t thd;
382 pthread_attr_t attr;
383 MpiDecLoopData data;
384 MPP_RET ret = MPP_OK;
385
386 mpp_log("mpi_dec_test start\n");
387 memset(&data, 0, sizeof(data));
388 pthread_attr_init(&attr);
389
390 cmd->simple = (cmd->type != MPP_VIDEO_CodingMJPEG) ? (1) : (0);
391
392 if (cmd->have_output) {
393 data.fp_output = fopen(cmd->file_output, "w+b");
394 if (NULL == data.fp_output) {
395 mpp_err("failed to open output file %s\n", cmd->file_output);
396 goto MPP_TEST_OUT;
397 }
398 }
399
400 if (cmd->file_slt) {
401 data.fp_verify = fopen(cmd->file_slt, "wt");
402 if (!data.fp_verify)
403 mpp_err("failed to open verify file %s\n", cmd->file_slt);
404 }
405
406 if (cmd->simple) {
407 ret = mpp_packet_init(&packet, NULL, 0);
408 mpp_err_f("mpp_packet_init get %p\n", packet);
409 if (ret) {
410 mpp_err("mpp_packet_init failed\n");
411 goto MPP_TEST_OUT;
412 }
413 } else {
414 RK_U32 hor_stride = MPP_ALIGN(width, 16);
415 RK_U32 ver_stride = MPP_ALIGN(height, 16);
416
417 ret = mpp_buffer_group_get_internal(&data.frm_grp, MPP_BUFFER_TYPE_ION);
418 if (ret) {
419 mpp_err("failed to get buffer group for input frame ret %d\n", ret);
420 goto MPP_TEST_OUT;
421 }
422
423 ret = mpp_frame_init(&frame); /* output frame */
424 if (ret) {
425 mpp_err("mpp_frame_init failed\n");
426 goto MPP_TEST_OUT;
427 }
428
429 /*
430 * NOTE: For jpeg could have YUV420 and YUV422 the buffer should be
431 * larger for output. And the buffer dimension should align to 16.
432 * YUV420 buffer is 3/2 times of w*h.
433 * YUV422 buffer is 2 times of w*h.
434 * So create larger buffer with 2 times w*h.
435 */
436 ret = mpp_buffer_get(data.frm_grp, &frm_buf, hor_stride * ver_stride * 4);
437 if (ret) {
438 mpp_err("failed to get buffer for input frame ret %d\n", ret);
439 goto MPP_TEST_OUT;
440 }
441
442 mpp_frame_set_buffer(frame, frm_buf);
443 }
444
445 // decoder demo
446 ret = mpp_create(&ctx, &mpi);
447 if (ret) {
448 mpp_err("mpp_create failed\n");
449 goto MPP_TEST_OUT;
450 }
451
452 mpp_log("%p mpi_dec_test decoder test start w %d h %d type %d\n",
453 ctx, width, height, type);
454
455 ret = mpi->control(ctx, MPP_SET_DISABLE_THREAD, NULL);
456
457 ret = mpp_init(ctx, MPP_CTX_DEC, type);
458 if (ret) {
459 mpp_err("%p mpp_init failed\n", ctx);
460 goto MPP_TEST_OUT;
461 }
462
463 mpp_dec_cfg_init(&cfg);
464
465 /* get default config from decoder context */
466 ret = mpi->control(ctx, MPP_DEC_GET_CFG, cfg);
467 if (ret) {
468 mpp_err("%p failed to get decoder cfg ret %d\n", ctx, ret);
469 goto MPP_TEST_OUT;
470 }
471
472 /*
473 * split_parse is to enable mpp internal frame spliter when the input
474 * packet is not aplited into frames.
475 */
476 ret = mpp_dec_cfg_set_u32(cfg, "base:split_parse", need_split);
477 if (ret) {
478 mpp_err("%p failed to set split_parse ret %d\n", ctx, ret);
479 goto MPP_TEST_OUT;
480 }
481
482 ret = mpi->control(ctx, MPP_DEC_SET_CFG, cfg);
483 if (ret) {
484 mpp_err("%p failed to set cfg %p ret %d\n", ctx, cfg, ret);
485 goto MPP_TEST_OUT;
486 }
487
488 data.cmd = cmd;
489 data.ctx = ctx;
490 data.mpi = mpi;
491 data.loop_end = 0;
492 data.packet = packet;
493 data.frame = frame;
494 data.frame_count = 0;
495 data.frame_num = cmd->frame_num;
496 data.quiet = cmd->quiet;
497
498 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
499
500 ret = pthread_create(&thd, &attr, thread_decode, &data);
501 if (ret) {
502 mpp_err("failed to create thread for input ret %d\n", ret);
503 goto MPP_TEST_OUT;
504 }
505
506 if (cmd->frame_num < 0) {
507 // wait for input then quit decoding
508 mpp_log("*******************************************\n");
509 mpp_log("**** Press Enter to stop loop decoding ****\n");
510 mpp_log("*******************************************\n");
511
512 getc(stdin);
513 data.loop_end = 1;
514 }
515
516 pthread_join(thd, NULL);
517
518 cmd->max_usage = data.max_usage;
519
520 ret = mpi->reset(ctx);
521 if (ret) {
522 mpp_err("%p mpi->reset failed\n", ctx);
523 goto MPP_TEST_OUT;
524 }
525
526 MPP_TEST_OUT:
527 if (data.packet) {
528 mpp_packet_deinit(&data.packet);
529 data.packet = NULL;
530 }
531
532 if (frame) {
533 mpp_frame_deinit(&frame);
534 frame = NULL;
535 }
536
537 if (ctx) {
538 mpp_destroy(ctx);
539 ctx = NULL;
540 }
541
542 if (!cmd->simple) {
543 if (frm_buf) {
544 mpp_buffer_put(frm_buf);
545 frm_buf = NULL;
546 }
547 }
548
549 if (data.frm_grp) {
550 mpp_buffer_group_put(data.frm_grp);
551 data.frm_grp = NULL;
552 }
553
554 if (data.fp_output) {
555 fclose(data.fp_output);
556 data.fp_output = NULL;
557 }
558
559 if (data.fp_verify) {
560 fclose(data.fp_verify);
561 data.fp_verify = NULL;
562 }
563
564 if (cfg) {
565 mpp_dec_cfg_deinit(cfg);
566 cfg = NULL;
567 }
568
569 pthread_attr_destroy(&attr);
570
571 return ret;
572 }
573
main(int argc,char ** argv)574 int main(int argc, char **argv)
575 {
576 RK_S32 ret = 0;
577 MpiDecTestCmd cmd_ctx;
578 MpiDecTestCmd* cmd = &cmd_ctx;
579
580 memset((void*)cmd, 0, sizeof(*cmd));
581 cmd->format = MPP_FMT_BUTT;
582 cmd->pkt_size = MPI_DEC_STREAM_SIZE;
583
584 // parse the cmd option
585 ret = mpi_dec_test_cmd_init(cmd, argc, argv);
586 if (ret)
587 goto RET;
588
589 mpi_dec_test_cmd_options(cmd);
590
591 ret = dec_nt_decode(cmd);
592 if (MPP_OK == ret)
593 mpp_log("test success max memory %.2f MB\n", cmd->max_usage / (float)(1 << 20));
594 else
595 mpp_err("test failed ret %d\n", ret);
596
597 RET:
598 mpi_dec_test_cmd_deinit(cmd);
599
600 return ret;
601 }
602
603