libxcoder 5.6.0
Loading...
Searching...
No Matches
ni_xcoder_encode.c
Go to the documentation of this file.
1/*******************************************************************************
2 *
3 * Copyright (C) 2022 NETINT Technologies
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 * SOFTWARE.
19 *
20 ******************************************************************************/
21
22/*!*****************************************************************************
23 * \file ni_xcoder_encode.c
24 *
25 * \brief Video encoding demo application directly using Netint Libxcoder API
26 ******************************************************************************/
27
28#include "ni_generic_utils.h"
29#include "ni_encode_utils.h"
30#include "ni_log.h"
31#include "ni_util.h"
32
33#ifdef _WIN32
34#include "ni_getopt.h"
35#elif __linux__ || __APPLE__
36#include <getopt.h>
37#include <unistd.h>
38#endif
39
40static void print_usage(void)
41{
43 "Video encoding demo application directly using Netint Libxcoder version %s\n"
44 "Usage: ni_xcoder_encode [options]\n"
45 "\n"
46 "options:\n"
47 "-h | --help Show this message.\n"
48 "-v | --version Print version info.\n"
49 "-i | --input (Required) Input file path.\n"
50 " Can be specified multiple (max %d) times\n"
51 " to concatenate inputs with different resolution together (sequence change)\n"
52 "-o | --output (Required) Output file path or name.\n"
53 " Can be specified multiple (max %d) times\n"
54 " to run multiple encoding instances simultaneously.\n"
55 "-m | --enc-codec (Required) Encoder codec format.\n"
56 " [a|avc, h|hevc, j|jpeg, x|av1, o|obu]\n"
57 " (x is in ivf container format, o is output raw AV1 OBU only)\n"
58 "-l | --loglevel Set loglevel of this application and libxcoder API.\n"
59 " [none, fatal, error, info, debug, trace]\n"
60 " (Default: info)\n"
61 "-c | --card Set card index to use.\n"
62 " See `ni_rsrc_mon` for info of cards on system.\n"
63 " (Default: 0)\n"
64 "-r | --repeat To loop input X times. Must be a positive integer\n"
65 " (Default: 1)\n"
66 "-k | --readframerate Read input at specified frame rate.\n"
67 "-p | --pix_fmt Indicate the pixel format of the input.\n"
68 " [yuv420p, yuv420p10le, nv12, p010le, rgba, bgra, argb, abgr, bgr0, yuv444p]\n"
69 " (Default: yuv420p)\n"
70 "-s | --size (Required) Resolution of input file in format WIDTHxHEIGHT.\n"
71 " (eg. '1920x1080')\n"
72 "-e | --encoder-params Encoding params. See \"Encoding Parameters\" chapter in\n"
73 " QuadraIntegration&ProgrammingGuide*.pdf for help.\n"
74 " Can be specified multiple (max %d) times,\n"
75 " must match the number of -o specified.\n"
76 " (Default: \"\")\n"
77 "-g | --encoder-gop Custom GOP for encoding. See \"Custom Gop Structure\" chapter in\n"
78 " QuadraIntegration&ProgrammingGuide*.pdf for help.\n"
79 " gopPresetIdx must be set to 0 to be in effect.\n"
80 " (Default: \"\")\n"
81 "-u | --hwupload (No argument) When enabled, upload raw frame to device first before encoding\n"
82 " Multiple input files and yuv444p format input are not supported in this mode\n"
84}
85
86int main(int argc, char *argv[])
87{
88 int i, ret = 0;
89 ni_demo_context_t ctx = {0};
90 char in_filename[MAX_INPUT_FILES][FILE_NAME_LEN] = {0};
91 char out_filename[MAX_OUTPUT_FILES][FILE_NAME_LEN] = {0};
92 char jpeg_filename[MAX_OUTPUT_FILES][FILE_NAME_LEN*2] = {0};
93 FILE* input_fp[MAX_INPUT_FILES] = {0};
94 FILE* output_fp[MAX_OUTPUT_FILES] = {0};
95 uint64_t prev_num_pkt[MAX_OUTPUT_FILES] = {0};
96 int i_index = 0, s_index = 0, o_index = 0, e_index = 0, g_index = 0;
97 int input_total = 0, output_total = 0;
98 int enc_codec_format = -1;
99 ni_log_level_t log_level;
100 int xcoderGUID = 0;
101 char *n;
104 int video_width[MAX_INPUT_FILES] = {0};
105 int video_height[MAX_INPUT_FILES] = {0};
106 char enc_conf_params[MAX_OUTPUT_FILES][2048] = {0};
107 char enc_gop_params[MAX_OUTPUT_FILES][2048] = {0};
108 ni_xcoder_params_t *p_enc_api_param = NULL;
109 int hwupload = 0;
110 int first_frame_uploaded = 0;
111 void *yuv_buf = NULL;
112 int send_rc = NI_TEST_RETCODE_SUCCESS, receive_rc = NI_TEST_RETCODE_SUCCESS;
114 ni_session_context_t upl_ctx = {0};
115 ni_session_context_t sca_ctx = {0};
116 ni_session_data_io_t in_frame = {0};
117 ni_session_data_io_t out_packet[MAX_OUTPUT_FILES] = {0};
118 ni_session_data_io_t sw_pix_frame[2] = {0};
119 ni_session_data_io_t scale_frame = {0};
120 ni_session_data_io_t swin_frame = {0};
121 ni_session_data_io_t *p_in_frame;
122 uint64_t current_time, previous_time;
123 int end_of_all_streams = 0, read_size = 0, eos = 0;
124 niFrameSurface1_t *p_hwframe = NULL;
125
126 int opt;
127 int opt_index;
128 const char *opt_string = "hvi:o:m:l:c:r:k:p:s:e:g:u";
129 static struct option long_options[] = {
130 {"help", no_argument, NULL, 'h'},
131 {"version", no_argument, NULL, 'v'},
132 {"input", required_argument, NULL, 'i'},
133 {"output", required_argument, NULL, 'o'},
134 {"enc-codec", required_argument, NULL, 'm'},
135 {"loglevel", required_argument, NULL, 'l'},
136 {"card", required_argument, NULL, 'c'},
137 {"repeat", required_argument, NULL, 'r'},
138 {"readframerate", required_argument, NULL, 'k'},
139 {"pix_fmt", required_argument, NULL, 'p'},
140 {"size", required_argument, NULL, 's'},
141 {"encoder-params", required_argument, NULL, 'e'},
142 {"encoder-gop", required_argument, NULL, 'g'},
143 {"hwupload", no_argument, NULL, 'u'},
144 {NULL, 0, NULL, 0},
145 };
146
147 while ((opt = getopt_long(argc, argv, opt_string, long_options, &opt_index)) != -1)
148 {
149 switch (opt)
150 {
151 case 'h':
152 print_usage();
153 ret = 0;
154 goto end;
155 case 'v':
157 ret = 0;
158 goto end;
159 case 'i':
160 if (i_index == MAX_INPUT_FILES)
161 {
162 ni_log(NI_LOG_ERROR, "Error: number of input files cannot exceed %d\n", MAX_INPUT_FILES);
163 ret = -1;
164 goto end;
165 }
166 ni_strcpy(in_filename[i_index], FILE_NAME_LEN, optarg);
167 i_index++;
168 break;
169 case 'o':
170 if (o_index == MAX_OUTPUT_FILES)
171 {
172 ni_log(NI_LOG_ERROR, "Error: number of output files cannot exceed %d\n", MAX_OUTPUT_FILES);
173 ret = -1;
174 goto end;
175 }
176
177 for (i = 0; i < o_index; i++)
178 {
179 if (0 == strcmp(out_filename[i], optarg))
180 {
181 ni_log(NI_LOG_ERROR, "Error: output file names must be unique: %s\n", optarg);
182 ret = -1;
183 goto end;
184 }
185 }
186
187 ni_strcpy(out_filename[o_index], FILE_NAME_LEN, optarg);
188 o_index++;
189 break;
190 case 'm':
191 // Accept both upper and lower case
192 for (i = 0; i < strlen(optarg); i++)
193 {
194 optarg[i] = (char)tolower((unsigned char)optarg[i]);
195 }
196 if (strcmp(optarg, "a") == 0 || strcmp(optarg, "avc") == 0)
197 {
198 enc_codec_format = NI_CODEC_FORMAT_H264;
199 }
200 else if (strcmp(optarg, "h") == 0 || strcmp(optarg, "hevc") == 0)
201 {
202 enc_codec_format = NI_CODEC_FORMAT_H265;
203 }
204 else if (strcmp(optarg, "j") == 0 || strcmp(optarg, "jpeg") == 0)
205 {
206 enc_codec_format = NI_CODEC_FORMAT_JPEG;
207 }
208 else if (strcmp(optarg, "x") == 0 || strcmp(optarg, "av1") == 0)
209 {
210 enc_codec_format = NI_CODEC_FORMAT_AV1;
211 }
212 else if (strcmp(optarg, "o") == 0 || strcmp(optarg, "obu") == 0)
213 {
214 enc_codec_format = NI_CODEC_FORMAT_AV1;
215 ctx.av1_output_obu = 1;
216 }
217 else
218 {
219 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -m | --dec-codec option\n"
220 "Must be one of [a|avc, h|hevc, x|av1, o|obu]\n", optarg);
221 ret = -1;
222 goto end;
223 }
224 break;
225 case 'l':
226 log_level = arg_to_ni_log_level(optarg);
227 if (log_level != NI_LOG_INVALID)
228 {
229 ni_log_set_level(log_level);
230 }
231 else
232 {
233 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -l | --loglevel option\n"
234 "Must be one of [none, fatal, error, info, debug, trace]\n", optarg);
235 ret = -1;
236 goto end;
237 }
238 break;
239 case 'c':
240 xcoderGUID = (int)strtol(optarg, &n, 10);
241 if (n == optarg || *n != '\0' || xcoderGUID < 0)
242 {
243 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -c | --card option\n"
244 "Must be a non-negative integer\n", optarg);
245 ret = -1;
246 goto end;
247 }
248 break;
249 case 'r':
250 ctx.loops_left = strtol(optarg, &n, 10);
251 if (n == optarg || *n != '\0' || !(ctx.loops_left >= 1))
252 {
253 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -r | --repeat option\n"
254 "Must be a positive integer\n", optarg);
255 ret = -1;
256 goto end;
257 }
258 break;
259 case 'k':
260 ctx.read_framerate = (int)strtol(optarg, &n, 10);
261 if (n == optarg || *n != '\0' || ctx.read_framerate < 0)
262 {
263 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -k | --readframerate option\n"
264 "Must be a non-negative integer\n", optarg);
265 ret = -1;
266 goto end;
267 }
268 break;
269 case 'p':
271 if (pix_fmt == NI_PIX_FMT_NONE)
272 {
273 if (!strcmp(optarg, "yuv444p"))
274 {
275 sw_pix_fmt = NI_SW_PIX_FMT_YUV444P;
276 }
277 else
278 {
279 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -p | --pix_fmt option\n"
280 "Must be one of [yuv420p, yuv420p10le, nv12, p010le, rgba, gbra, argb, abgr, bgr0, yuv444p]\n",
281 optarg);
282 ret = -1;
283 goto end;
284 }
285 }
286 break;
287 case 's':
288 if (s_index == MAX_INPUT_FILES)
289 {
290 ni_log(NI_LOG_ERROR, "Error: number of input resolutions cannot exceed %d\n", MAX_INPUT_FILES);
291 ret = -1;
292 goto end;
293 }
294 video_width[s_index] = (int)strtol(optarg, &n, 10);
295 video_height[s_index] = atoi(n + 1);
296 if ((*n != 'x') || (video_width[s_index] <= 0 || video_height[s_index] <= 0))
297 {
298 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -s | --size option\n"
299 "Must be in format [WIDTHxHEIGHT] (e.g. 1920x1080)\n", optarg);
300 ret = -1;
301 goto end;
302 }
303 s_index++;
304 break;
305 case 'e':
306 if (e_index == MAX_OUTPUT_FILES)
307 {
308 ni_log(NI_LOG_ERROR, "Error: number of encoder config cannot exceed %d\n", MAX_OUTPUT_FILES);
309 ret = -1;
310 goto end;
311 }
312 ni_strcpy(enc_conf_params[e_index], sizeof(enc_conf_params[e_index]), optarg);
313 e_index++;
314 break;
315 case 'g':
316 if (g_index == MAX_OUTPUT_FILES)
317 {
318 ni_log(NI_LOG_ERROR, "Error: number of encoder gop settings cannot exceed %d\n", MAX_OUTPUT_FILES);
319 ret = -1;
320 goto end;
321 }
322 ni_strcpy(enc_gop_params[g_index], sizeof(enc_gop_params[g_index]), optarg);
323 g_index++;
324 break;
325 case 'u':
326 hwupload = 1;
327 break;
328 default:
329 print_usage();
330 ret = -1;
331 goto end;
332 }
333 }
334
335 if (i_index == 0) {
336 ni_log(NI_LOG_ERROR, "Error: Missing input file argument (-i | --input)\n");
337 ret = -1;
338 goto end;
339 }
340
341 if (o_index == 0) {
342 ni_log(NI_LOG_ERROR, "Error: Missing output filepath (or filename) argument (-o | --output)\n");
343 ret = -1;
344 goto end;
345 }
346
347 if (s_index != i_index) {
348 ni_log(NI_LOG_ERROR, "Error: Number of input resolution specified does not match number of input files\n");
349 ret = -1;
350 goto end;
351 }
352
353 if (enc_codec_format == -1) {
354 ni_log(NI_LOG_ERROR, "Error: Missing encoder codec argument (-m | --enc-codec)\n");
355 ret = -1;
356 goto end;
357 }
358
359 input_total = i_index;
360 i_index = 0;
361 if (ctx.loops_left > 1 && input_total > 1)
362 {
363 ni_log(NI_LOG_ERROR, "Error: multiple input files not supported when loops %u greater than 1\n", ctx.loops_left);
364 ret = -1;
365 goto end;
366 }
367
368 output_total = o_index;
369 if (sw_pix_fmt == NI_SW_PIX_FMT_YUV444P && output_total != 2)
370 {
371 ni_log(NI_LOG_ERROR, "Error: Must indicate 2 output files for yuv444p encoding\n");
372 ret = -1;
373 goto end;
374 }
375
376 // Check for features not supported with hwupload
377 if (hwupload && ctx.read_framerate)
378 {
379 ni_log(NI_LOG_ERROR, "Error: -k | --readframerate option is not supported in hwupload mode\n");
380 ret = -1;
381 goto end;
382 }
383 if (hwupload && input_total > 1)
384 {
385 ni_log(NI_LOG_ERROR, "Error: multiple input (sequence change) is not supported in hwupload mode\n");
386 ret = -1;
387 goto end;
388 }
389 if (hwupload && sw_pix_fmt == NI_SW_PIX_FMT_YUV444P)
390 {
391 ni_log(NI_LOG_ERROR, "Error: yuv444p input is not supported in hwupload mode\n");
392 ret = -1;
393 goto end;
394 }
395
396 // Only in hwupload mode, the pixel formats not directly supported by encoder
397 // will be converted to yuv420p using 2D engine
398 if (!hwupload && !is_ni_enc_pix_fmt(pix_fmt) && sw_pix_fmt == NI_SW_PIX_FMT_NONE)
399 {
400 ni_log(NI_LOG_ERROR, "Error: pixel format %s is only supported in hwupload mode\n",
401 ni_pixel_format_name(pix_fmt));
402 ret = -1;
403 goto end;
404 }
405
406 if (ni_posix_memalign(&yuv_buf, sysconf(_SC_PAGESIZE), MAX_YUV_FRAME_SIZE))
407 {
408 ni_log(NI_LOG_ERROR, "Error: failed to allocate YUV data buffer\n");
409 ret = -1;
410 goto end;
411 }
412
413 for (i = 0; i < input_total; i++)
414 {
415 if (!in_filename[i][0])
416 {
417 ni_log(NI_LOG_ERROR, "Error: invalid input file %d\n", i);
418 ret = -1;
419 goto end;
420 }
421
422 input_fp[i] = NULL;
423 ni_fopen(&(input_fp[i]), in_filename[i], "rb");
424
425 if (!input_fp[i])
426 {
427 ni_log(NI_LOG_ERROR, "Error: Failed to open input %s\n", in_filename[i]);
428 ret = -1;
429 goto end;
430 }
431 ni_log(NI_LOG_INFO, "Opened input file: %s\n", in_filename[i]);
432 }
433
434 ctx.total_file_size = get_total_file_size(input_fp[0]);
435 ctx.curr_file_offset = 0;
436
437 for (i = 0; i < output_total; i++)
438 {
439 if (strcmp(out_filename[i], "null") != 0 &&
440 strcmp(out_filename[i], "/dev/null") != 0)
441 {
442 output_fp[i] = NULL;
443 if (enc_codec_format == NI_CODEC_FORMAT_JPEG)
444 {
445 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
446 out_filename[i], ctx.num_packets_received[i]);
447 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
448 } else
449 {
450 ni_fopen(&(output_fp[i]), out_filename[i], "wb");
451 }
452 if (!output_fp[i])
453 {
454 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", enc_codec_format == NI_CODEC_FORMAT_JPEG ? jpeg_filename[i] : out_filename[i]);
455 ret = -1;
456 goto end;
457 }
458 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
459 {
460 ni_log(NI_LOG_INFO, "Opened output file: %s\n", out_filename[i]);
461 }
462 } else
463 {
464 output_fp[i] = NULL;
465 ni_log(NI_LOG_INFO, "Note: Requested NULL output for index %d, no output file will be generated\n", i);
466 }
467 }
468
469 p_enc_api_param = calloc(output_total, sizeof(ni_xcoder_params_t));
470 if (!p_enc_api_param)
471 {
472 ni_log(NI_LOG_ERROR, "Error: Failed to allocate memory for p_enc_api_param\n");
473 ret = -1;
474 goto end;
475 }
476
477 for (i = 0; i < output_total; i++)
478 {
479 if (ni_device_session_context_init(&enc_ctx[i]) < 0)
480 {
481 ni_log(NI_LOG_ERROR, "Error: init encoder %d context error\n", i);
482 ret = -1;
483 goto end;
484 }
485 }
486
487 for (i = 0; i < output_total; i++)
488 {
489 enc_ctx[i].codec_format = enc_codec_format;
490 }
491
494
495 if (hwupload)
496 {
497 if (ni_device_session_context_init(&upl_ctx) < 0)
498 {
499 ni_log(NI_LOG_ERROR, "Error: init uploader context error\n");
500 ret = -1;
501 goto end;
502 }
503
504 if (ni_device_session_context_init(&sca_ctx) < 0)
505 {
506 ni_log(NI_LOG_ERROR, "Error: init scale context error\n");
507 ret = -1;
508 goto end;
509 }
510
511 ni_log(NI_LOG_INFO, "Starting hwupload + encoding mode: video resolution %dx%d\n",
512 video_width[0], video_height[0]);
513 // buffers by downstream entity like encoders
514 ret = uploader_open_session(&upl_ctx, xcoderGUID, video_width[0] < NI_MIN_WIDTH ? NI_MIN_WIDTH : video_width[0],
515 video_height[0] < NI_MIN_HEIGHT ? NI_MIN_HEIGHT : video_height[0], pix_fmt, 0, 3);
516 if (ret != 0)
517 {
518 goto end;
519 }
520
521 p_hwframe = hwupload_frame(&ctx, &upl_ctx, &sca_ctx, &swin_frame, &in_frame, &scale_frame, pix_fmt,
522 video_width[0], video_height[0], input_fp[0], yuv_buf, &eos);
523 if (p_hwframe == NULL)
524 {
525 ret = -1;
526 goto end;
527 }
528
529 ret = encoder_open(&ctx, &enc_ctx[0], p_enc_api_param, output_total, enc_conf_params, enc_gop_params,
530 NULL, video_width[0], video_height[0], 30, 1, 200000, enc_codec_format,
531 is_ni_enc_pix_fmt(pix_fmt) ? pix_fmt : NI_PIX_FMT_YUV420P,
532 0, xcoderGUID, p_hwframe, 0, false);
533 if (ret != 0)
534 {
535 goto end;
536 }
537
538 //unlike video codecs (1st packet is header), there is no header for Jpeg
539 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
540 {
541 do
542 {
543 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
544 video_width[0], video_height[0], output_total, output_fp);
545 }
546 while (receive_rc == NI_TEST_RETCODE_EAGAIN);
547
548 if (receive_rc == NI_TEST_RETCODE_SUCCESS)
549 {
550 ni_log(NI_LOG_INFO, "Got encoded sequence header packet\n");
551 }
552 else
553 {
554 ni_log(NI_LOG_ERROR, "Failed to get encoded sequence header packet, retcode %d\n", receive_rc);
555 ret = receive_rc;
556 goto end;
557 }
558 }
559 while (!end_of_all_streams &&
560 (send_rc == NI_TEST_RETCODE_SUCCESS || receive_rc== NI_TEST_RETCODE_SUCCESS ||
561 (send_rc == NI_TEST_RETCODE_EAGAIN && receive_rc == NI_TEST_RETCODE_EAGAIN)))
562 {
563 if (first_frame_uploaded && !eos)
564 {
565 p_hwframe = hwupload_frame(&ctx, &upl_ctx, &sca_ctx, &swin_frame, &in_frame, &scale_frame, pix_fmt,
566 video_width[0], video_height[0], input_fp[0], yuv_buf, &eos);
568 {
569 ni_log(NI_LOG_DEBUG, "No space to write to, try to read a packet\n");
570 p_in_frame = &in_frame;
571 goto receive_pkt;
572 } else if (p_hwframe == NULL)
573 {
575 break;
576 }
577 }
578
579 // Sending
580 p_in_frame = is_ni_enc_pix_fmt(pix_fmt) ? &in_frame : &scale_frame;
581 for (i = 0; i < output_total; i++)
582 {
583 ctx.curr_enc_index = i;
584 send_rc = encoder_send_data3(&ctx, &enc_ctx[i], p_in_frame, video_width[0], video_height[0], eos);
585 first_frame_uploaded = 1; //since first frame read before while-loop
586 if (send_rc < 0) //Error
587 {
588 ni_log(NI_LOG_ERROR, "enc %d send error, quit !\n", i);
589 ni_hw_frame_ref(p_hwframe);
590 end_of_all_streams = 1;
591 break;
592 }
593 //track in array with unique index, free when enc read finds
594 //this must be implemented in application space for complete
595 //tracking of hwframes
596 if (!ctx.enc_resend[i])
597 {
598 //successful read means there is recycle to check
599 ni_hw_frame_ref(p_hwframe);
600 } else
601 {
602 ni_log(NI_LOG_DEBUG, "enc %d need to re-send !\n", i);
603 ni_usleep(500);
604 i--;
605 continue;
606 }
607 }
608 if (end_of_all_streams)
609 break;
610
611receive_pkt:
612 for (i = 0; i < output_total; i++)
613 {
614 prev_num_pkt[i] = ctx.num_packets_received[i];
615 }
616
617 receive_rc = encoder_receive(&ctx, enc_ctx, p_in_frame, out_packet,
618 video_width[0], video_height[0], output_total, output_fp);
619 for (i = 0; receive_rc >= 0 && i < output_total; i++)
620 {
621 if (!ctx.enc_eos_received[i])
622 {
623 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
624 prev_num_pkt[i] < ctx.num_packets_received[i] &&
625 output_fp[i] != NULL)
626 {
627 fclose(output_fp[i]);
628 output_fp[i] = NULL;
629 ni_log(NI_LOG_DEBUG, "Save jpeg file : %s\n", jpeg_filename[i]);
630 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
631 out_filename[i], ctx.num_packets_received[i]);
632 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
633 if (!output_fp[i])
634 {
635 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", jpeg_filename[i]);
636 ret = -1;
637 break;
638 }
639 }
640
641 ni_log(NI_LOG_DEBUG, "enc %d continues to read!\n", i);
642 end_of_all_streams = 0;
643 break;
644 } else
645 {
646 ni_log(NI_LOG_DEBUG, "enc %d eos !\n", i);
647 end_of_all_streams = 1;
648 }
649 }
650
652 if (current_time - previous_time >= (uint64_t)1000000000) {
653 for (i = 0; i < output_total; i++)
654 {
655 ni_log(NI_LOG_INFO, "Encoder %d stats: received %u packets, fps %.2f, total bytes %u\n",
656 i, ctx.num_packets_received[i],
657 (float)enc_ctx[i].frame_num / (float)(current_time - ctx.start_time) * (float)1000000000,
659 }
661 }
662 }
663
665 if (!is_ni_enc_pix_fmt(pix_fmt))
666 { //Uploading rgba requires scaler conversion so close the session too
668 }
669 }
670 else
671 {
672 in_frame.data.frame.pixel_format = pix_fmt;
673 ret = encoder_open(&ctx, enc_ctx, p_enc_api_param, output_total,
674 enc_conf_params, enc_gop_params, NULL, video_width[0],
675 video_height[0], 30, 1, 200000,
676 enc_codec_format, pix_fmt, 0, xcoderGUID, NULL,
677 0, (sw_pix_fmt != NI_SW_PIX_FMT_NONE) ? false : true);
678 // zero copy is not supported for YUV444P
679 if (ret != 0)
680 {
681 goto end;
682 }
683
684 //unlike video codecs (1st packet is header), there is no header for Jpeg
685 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
686 {
687 do
688 {
689 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
690 video_width[0], video_height[0], output_total, output_fp);
691 }
692 while (receive_rc == NI_TEST_RETCODE_EAGAIN);
693
694 if (receive_rc == NI_TEST_RETCODE_SUCCESS)
695 {
696 ni_log(NI_LOG_INFO, "Got encoded sequence header packet\n");
697 }
698 else
699 {
700 ni_log(NI_LOG_ERROR, "Failed to get encoded sequence header packet, retcode %d\n", receive_rc);
701 ret = receive_rc;
702 goto end;
703 }
704 }
705
706 ni_log(NI_LOG_INFO, "Starting to encode: video resolution %dx%d\n", video_width[0], video_height[0]);
707
708 while (!end_of_all_streams &&
709 (send_rc == NI_TEST_RETCODE_SUCCESS || receive_rc == NI_TEST_RETCODE_SUCCESS ||
710 (send_rc == NI_TEST_RETCODE_EAGAIN && receive_rc == NI_TEST_RETCODE_EAGAIN)))
711 {
712 read_size = read_yuv_from_file(&ctx, input_fp[i_index], yuv_buf,
713 video_width[i_index], video_height[i_index],
714 pix_fmt, sw_pix_fmt, &eos,
715 enc_ctx[0].session_run_state);
716 if (read_size < 0)
717 {
718 break;
719 }
720
721 // YUV444P reading
722 if (sw_pix_fmt != NI_SW_PIX_FMT_NONE)
723 {
724 ret = convert_yuv_444p_to_420p(&sw_pix_frame[0], eos ? NULL : yuv_buf,
725 video_width[i_index], video_height[i_index],
726 sw_pix_fmt, 0, enc_codec_format);
727 if (ret < 0)
728 {
729 break;
730 }
731 }
732
733 for (i = 0; i < output_total; i++)
734 {
735 if (sw_pix_fmt != NI_SW_PIX_FMT_NONE)
736 {
737 ctx.curr_enc_index = i;
738 send_rc = encoder_send_data3(&ctx, &enc_ctx[i], &sw_pix_frame[i],
739 video_width[i_index], video_height[i_index], eos);
740 } else
741 {
742 ctx.curr_enc_index = i;
743 send_rc = encoder_send_data(&ctx, &enc_ctx[i],
744 &in_frame, eos ? NULL : yuv_buf,
745 video_width[i_index], video_height[i_index],
746 i_index == input_total - 1);
747 }
748
749 if (send_rc == NI_TEST_RETCODE_EAGAIN)
750 {
751 // retry send to same encoder session
752 i--;
753 continue;
754 } else if (send_rc == NI_TEST_RETCODE_NEXT_INPUT) // next input (will trigger sequence change)
755 {
756 i_index++;
757 ctx.total_file_size = get_total_file_size(input_fp[i_index]);
758 ctx.curr_file_offset = 0;
759 send_rc = NI_TEST_RETCODE_SUCCESS;
760 }
761 }
762
763 for (i = 0; i < output_total; i++)
764 {
765 prev_num_pkt[i] = ctx.num_packets_received[i];
766 }
767
768 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
769 video_width[0], video_height[0], output_total, output_fp);
770 for (i = 0; receive_rc >= 0 && i < output_total; i++)
771 {
772 if (!ctx.enc_eos_received[i])
773 {
774 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
775 prev_num_pkt[i] < ctx.num_packets_received[i] &&
776 output_fp[i] != NULL)
777 {
778 fclose(output_fp[i]);
779 output_fp[i] = NULL;
780 ni_log(NI_LOG_DEBUG, "Save jpeg file : %s\n", jpeg_filename[i]);
781 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
782 out_filename[i], ctx.num_packets_received[i]);
783 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
784 if (!output_fp[i])
785 {
786 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", jpeg_filename[i]);
787 ret = -1;
788 break;
789 }
790 }
791
792 ni_log(NI_LOG_DEBUG, "enc %d continues to read!\n", i);
793 end_of_all_streams = 0;
794 break;
795 } else
796 {
797 ni_log(NI_LOG_DEBUG, "enc %d eos !\n", i);
798 end_of_all_streams = 1;
799 }
800 }
801
803 if (current_time - previous_time >= (uint64_t)1000000000) {
804 for (i = 0; i < output_total; i++)
805 {
806 ni_log(NI_LOG_INFO, "Encoder %d stats: received %u packets, fps %.2f, total bytes %u\n",
807 i, ctx.num_packets_received[i],
808 (float)enc_ctx[i].frame_num / (float)(current_time - ctx.start_time) * (float)1000000000,
811 }
812 }
813 }
814 }
815
816 //delete jpeg file that are 0 bytes in size
817 for (i = 0; i < output_total; i++)
818 {
819 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
820 output_fp[i] != NULL)
821 {
822 fseek(output_fp[i], 0, SEEK_END);
823 if (ftell(output_fp[i]) == 0)
824 {
825 fclose(output_fp[i]);
826 output_fp[i] = NULL;
827 remove(jpeg_filename[i]);
828 }
829 }
830 }
831
832 encoder_stat_report_and_close(&ctx, enc_ctx, output_total);
833
834end:
836 ni_frame_buffer_free(&swin_frame.data.frame);
837 for (i = 0; i < output_total; i++)
838 {
839 ni_packet_buffer_free(&out_packet[i].data.packet);
840 }
841 for (i = 0; i < sizeof(sw_pix_frame)/sizeof(ni_session_data_io_t); i++)
842 {
843 ni_frame_buffer_free(&sw_pix_frame[i].data.frame);
844 }
845
848 for (i = 0; i < output_total; i++)
849 {
851 if (output_fp[i] != NULL)
852 {
853 fclose(output_fp[i]);
854 }
855 }
856
857 for (i = 0; i < input_total; i++)
858 {
859 if (input_fp[i])
860 {
861 fclose(input_fp[i]);
862 }
863 }
864
865 ni_aligned_free(yuv_buf);
866 free(p_enc_api_param);
867
868 for(i = 0; i < MAX_OUTPUT_FILES; ++i)
869 {
870 free(ctx.enc_pts_queue[i]);
871 ctx.enc_pts_queue[i] = NULL;
872 }
873
874 return ret;
875}
int main()
Definition client.cpp:51
#define NI_XCODER_REVISION
Definition ni_defs.h:98
@ NI_DEVICE_TYPE_SCALER
Definition ni_defs.h:362
@ NI_DEVICE_TYPE_UPLOAD
Definition ni_defs.h:367
@ NI_RETCODE_NVME_SC_WRITE_BUFFER_FULL
Definition ni_defs.h:550
ni_retcode_t ni_frame_buffer_free(ni_frame_t *p_frame)
Free frame buffer that was previously allocated with either ni_frame_buffer_alloc or ni_encoder_frame...
ni_retcode_t ni_device_session_close(ni_session_context_t *p_ctx, int eos_recieved, ni_device_type_t device_type)
Close device session that was previously opened by calling ni_device_session_open() If device_type is...
ni_retcode_t ni_packet_buffer_free(ni_packet_t *p_packet)
Free packet buffer that was previously allocated with ni_packet_buffer_alloc.
ni_retcode_t ni_device_session_context_init(ni_session_context_t *p_ctx)
Initialize already allocated session context to a known state.
#define atoi(p_str)
void ni_device_session_context_clear(ni_session_context_t *p_ctx)
Clear already allocated session context.
#define NI_MIN_HEIGHT
#define NI_MIN_WIDTH
@ NI_CODEC_FORMAT_H265
@ NI_CODEC_FORMAT_AV1
@ NI_CODEC_FORMAT_H264
@ NI_CODEC_FORMAT_JPEG
ni_pix_fmt_t
@ NI_PIX_FMT_YUV420P
@ NI_PIX_FMT_NONE
int encoder_receive(ni_demo_context_t *p_ctx, ni_session_context_t *enc_ctx_list, ni_session_data_io_t *in_frame, ni_session_data_io_t *pkt, int width, int height, int output_total, FILE **pfs_list)
int encoder_open(ni_demo_context_t *p_ctx, ni_session_context_t *enc_ctx_list, ni_xcoder_params_t *p_api_param_list, int output_total, char p_enc_conf_params[][2048], char p_enc_conf_gop[][2048], ni_frame_t *p_ni_frame, int width, int height, int fps_num, int fps_den, int bitrate, int codec_format, ni_pix_fmt_t pix_fmt, int aspect_ratio_idc, int xcoder_guid, niFrameSurface1_t *p_surface, int multi_thread, bool check_zerocopy)
int encoder_send_data(ni_demo_context_t *p_ctx, ni_session_context_t *p_enc_ctx, ni_session_data_io_t *p_in_data, void *yuv_buf, int input_video_width, int input_video_height, int is_last_input)
Send encoder input data, read from input file.
void encoder_stat_report_and_close(ni_demo_context_t *p_ctx, ni_session_context_t *p_enc_ctx_list, int output_total)
int encoder_send_data3(ni_demo_context_t *p_ctx, ni_session_context_t *p_enc_ctx, ni_session_data_io_t *p_in_data, int input_video_width, int input_video_height, int eos)
Send encoder input data, read from uploader instance hwframe.
const char * ni_pixel_format_name(ni_pix_fmt_t pix_fmt)
ni_pix_fmt_t ni_pixel_format_search(const char *name)
int read_yuv_from_file(ni_demo_context_t *p_ctx, FILE *pfs, void *yuv_buf, int width, int height, ni_pix_fmt_t pix_fmt, ni_sw_pix_fmt_t sw_pix_fmt, int *eos, ni_session_run_state_t run_state)
int is_ni_enc_pix_fmt(ni_pix_fmt_t pix_fmt)
niFrameSurface1_t * hwupload_frame(ni_demo_context_t *p_ctx, ni_session_context_t *p_upl_ctx, ni_session_context_t *p_sca_ctx, ni_session_data_io_t *p_sw_data, ni_session_data_io_t *p_hw_data, ni_session_data_io_t *p_scale_data, ni_pix_fmt_t pix_fmt, int width, int height, FILE *pfs, void *yuv_buf, int *eos)
uint64_t get_total_file_size(FILE *fp)
void print_version(void)
int uploader_open_session(ni_session_context_t *p_upl_ctx, int iXcoderGUID, int width, int height, ni_pix_fmt_t pix_fmt, int is_p2p, int pool_size)
Uploader session open.
void ni_hw_frame_ref(const niFrameSurface1_t *p_surface)
int convert_yuv_444p_to_420p(ni_session_data_io_t *p_frame, void *yuv_buf, int width, int height, ni_sw_pix_fmt_t sw_pix_fmt, int mode, ni_codec_format_t codec_format)
#define MAX_YUV_FRAME_SIZE
#define MAX_OUTPUT_FILES
#define NI_TEST_RETCODE_SUCCESS
#define MAX_INPUT_FILES
#define NI_TEST_RETCODE_EAGAIN
#define NI_TEST_RETCODE_FAILURE
ni_sw_pix_fmt_t
@ NI_SW_PIX_FMT_NONE
@ NI_SW_PIX_FMT_YUV444P
#define NI_TEST_RETCODE_NEXT_INPUT
#define FILE_NAME_LEN
char * optarg
Definition ni_getopt.c:33
int getopt_long(int argc, char *argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition ni_getopt.c:99
Implementation of getopt() and getopt_long() for Windows environment.
#define no_argument
Definition ni_getopt.h:85
#define required_argument
Definition ni_getopt.h:86
ni_log_level_t arg_to_ni_log_level(const char *arg_str)
Convert terminal arg string to ni_log_level_t.
Definition ni_log.c:262
void ni_log_set_level(ni_log_level_t level)
Set ni_log_level.
Definition ni_log.c:202
void ni_log(ni_log_level_t level, const char *fmt,...)
print log message using ni_log_callback
Definition ni_log.c:183
Logging definitions.
ni_log_level_t
Definition ni_log.h:58
@ NI_LOG_DEBUG
Definition ni_log.h:64
@ NI_LOG_ERROR
Definition ni_log.h:62
@ NI_LOG_INFO
Definition ni_log.h:63
@ NI_LOG_INVALID
Definition ni_log.h:59
struct timeval current_time
struct timeval previous_time
int ni_posix_memalign(void **memptr, size_t alignment, size_t size)
Allocate aligned memory.
Definition ni_util.c:202
ni_retcode_t ni_fopen(FILE **fp, const char *filename, const char *mode)
Definition ni_util.c:983
ni_retcode_t ni_strcpy(char *dest, size_t dmax, const char *src)
Definition ni_util.c:453
void ni_usleep(int64_t usec)
Definition ni_util.c:362
uint64_t ni_gettime_ns(void)
Definition ni_util.c:2622
Utility definitions.
#define ni_aligned_free(p_memptr)
Definition ni_util.h:400
int enc_resend[MAX_OUTPUT_FILES]
uint64_t num_packets_received[MAX_OUTPUT_FILES]
ni_pts_queue * enc_pts_queue[MAX_OUTPUT_FILES]
int enc_eos_received[MAX_OUTPUT_FILES]
uint64_t enc_total_bytes_received[MAX_OUTPUT_FILES]
union _ni_session_data_io::@19 data