libxcoder 5.8.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"
83 "-b | --bitstream-features Log filename for bitstream features output.\n"
84 " Can be specified multiple (max %d) times,\n"
85 " must match the number of -o outputs specified.\n"
86 " When specified, bitstream features will be logged after each packet receive.\n"
87 " Each log file corresponds to the output file in the same order.\n"
88 " Please note for this feature to take effects,\n"
89 " 'getBitstreamFeatures' parameter must be included in --encoder-params.\n"
90 " Currently this feature is only supported for h.264.\n"
91 " (Default: none)\n"
93}
94
95int main(int argc, char *argv[])
96{
97 int i, ret = 0;
98 ni_demo_context_t ctx = {0};
99 char in_filename[MAX_INPUT_FILES][FILE_NAME_LEN] = {0};
100 char out_filename[MAX_OUTPUT_FILES][FILE_NAME_LEN] = {0};
101 char jpeg_filename[MAX_OUTPUT_FILES][FILE_NAME_LEN*2] = {0};
102 FILE* input_fp[MAX_INPUT_FILES] = {0};
103 FILE* output_fp[MAX_OUTPUT_FILES] = {0};
104 uint64_t prev_num_pkt[MAX_OUTPUT_FILES] = {0};
105 int i_index = 0, s_index = 0, o_index = 0, e_index = 0, g_index = 0, f_index = 0;
106 int input_total = 0, output_total = 0;
107 int enc_codec_format = -1;
108 ni_log_level_t log_level;
109 int xcoderGUID = 0;
110 char *n;
113 int video_width[MAX_INPUT_FILES] = {0};
114 int video_height[MAX_INPUT_FILES] = {0};
115 char enc_conf_params[MAX_OUTPUT_FILES][2048] = {0};
116 char enc_gop_params[MAX_OUTPUT_FILES][2048] = {0};
117 ni_xcoder_params_t *p_enc_api_param = NULL;
118 int hwupload = 0;
119 int first_frame_uploaded = 0;
120 void *yuv_buf = NULL;
121 int send_rc = NI_TEST_RETCODE_SUCCESS, receive_rc = NI_TEST_RETCODE_SUCCESS;
123 ni_session_context_t upl_ctx = {0};
124 ni_session_context_t sca_ctx = {0};
125 ni_session_data_io_t in_frame = {0};
126 ni_session_data_io_t out_packet[MAX_OUTPUT_FILES] = {0};
127 ni_session_data_io_t sw_pix_frame[2] = {0};
128 ni_session_data_io_t scale_frame = {0};
129 ni_session_data_io_t swin_frame = {0};
130 ni_session_data_io_t *p_in_frame;
131 uint64_t current_time, previous_time;
132 int end_of_all_streams = 0, read_size = 0, eos = 0;
133 niFrameSurface1_t *p_hwframe = NULL;
134 char bitstream_features_log_file[MAX_OUTPUT_FILES][FILE_NAME_LEN] = {0};
135 FILE *bitstream_features_fp[MAX_OUTPUT_FILES] = {0};
136
137 int opt;
138 int opt_index;
139 const char *opt_string = "hvi:o:m:l:c:r:k:p:s:e:g:ub:";
140 static struct option long_options[] = {
141 {"help", no_argument, NULL, 'h'},
142 {"version", no_argument, NULL, 'v'},
143 {"input", required_argument, NULL, 'i'},
144 {"output", required_argument, NULL, 'o'},
145 {"enc-codec", required_argument, NULL, 'm'},
146 {"loglevel", required_argument, NULL, 'l'},
147 {"card", required_argument, NULL, 'c'},
148 {"repeat", required_argument, NULL, 'r'},
149 {"readframerate", required_argument, NULL, 'k'},
150 {"pix_fmt", required_argument, NULL, 'p'},
151 {"size", required_argument, NULL, 's'},
152 {"encoder-params", required_argument, NULL, 'e'},
153 {"encoder-gop", required_argument, NULL, 'g'},
154 {"hwupload", no_argument, NULL, 'u'},
155 {"bitstream-features", required_argument, NULL, 'b'},
156 {NULL, 0, NULL, 0},
157 };
158
159 while ((opt = getopt_long(argc, argv, opt_string, long_options, &opt_index)) != -1)
160 {
161 switch (opt)
162 {
163 case 'h':
164 print_usage();
165 ret = 0;
166 goto end;
167 case 'v':
169 ret = 0;
170 goto end;
171 case 'i':
172 if (i_index == MAX_INPUT_FILES)
173 {
174 ni_log(NI_LOG_ERROR, "Error: number of input files cannot exceed %d\n", MAX_INPUT_FILES);
175 ret = -1;
176 goto end;
177 }
178 ni_strcpy(in_filename[i_index], FILE_NAME_LEN, optarg);
179 i_index++;
180 break;
181 case 'o':
182 if (o_index == MAX_OUTPUT_FILES)
183 {
184 ni_log(NI_LOG_ERROR, "Error: number of output files cannot exceed %d\n", MAX_OUTPUT_FILES);
185 ret = -1;
186 goto end;
187 }
188
189 for (i = 0; i < o_index; i++)
190 {
191 if (0 == strcmp(out_filename[i], optarg))
192 {
193 ni_log(NI_LOG_ERROR, "Error: output file names must be unique: %s\n", optarg);
194 ret = -1;
195 goto end;
196 }
197 }
198
199 ni_strcpy(out_filename[o_index], FILE_NAME_LEN, optarg);
200 o_index++;
201 break;
202 case 'm':
203 // Accept both upper and lower case
204 for (i = 0; i < strlen(optarg); i++)
205 {
206 optarg[i] = (char)tolower((unsigned char)optarg[i]);
207 }
208 if (strcmp(optarg, "a") == 0 || strcmp(optarg, "avc") == 0)
209 {
210 enc_codec_format = NI_CODEC_FORMAT_H264;
211 }
212 else if (strcmp(optarg, "h") == 0 || strcmp(optarg, "hevc") == 0)
213 {
214 enc_codec_format = NI_CODEC_FORMAT_H265;
215 }
216 else if (strcmp(optarg, "j") == 0 || strcmp(optarg, "jpeg") == 0)
217 {
218 enc_codec_format = NI_CODEC_FORMAT_JPEG;
219 }
220 else if (strcmp(optarg, "x") == 0 || strcmp(optarg, "av1") == 0)
221 {
222 enc_codec_format = NI_CODEC_FORMAT_AV1;
223 }
224 else if (strcmp(optarg, "o") == 0 || strcmp(optarg, "obu") == 0)
225 {
226 enc_codec_format = NI_CODEC_FORMAT_AV1;
227 ctx.av1_output_obu = 1;
228 }
229 else
230 {
231 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -m | --dec-codec option\n"
232 "Must be one of [a|avc, h|hevc, x|av1, o|obu]\n", optarg);
233 ret = -1;
234 goto end;
235 }
236 break;
237 case 'l':
238 log_level = arg_to_ni_log_level(optarg);
239 if (log_level != NI_LOG_INVALID)
240 {
241 ni_log_set_level(log_level);
242 }
243 else
244 {
245 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -l | --loglevel option\n"
246 "Must be one of [none, fatal, error, info, debug, trace]\n", optarg);
247 ret = -1;
248 goto end;
249 }
250 break;
251 case 'c':
252 xcoderGUID = (int)strtol(optarg, &n, 10);
253 if (n == optarg || *n != '\0' || xcoderGUID < 0)
254 {
255 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -c | --card option\n"
256 "Must be a non-negative integer\n", optarg);
257 ret = -1;
258 goto end;
259 }
260 break;
261 case 'r':
262 ctx.loops_left = strtol(optarg, &n, 10);
263 if (n == optarg || *n != '\0' || !(ctx.loops_left >= 1))
264 {
265 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -r | --repeat option\n"
266 "Must be a positive integer\n", optarg);
267 ret = -1;
268 goto end;
269 }
270 break;
271 case 'k':
272 ctx.read_framerate = (int)strtol(optarg, &n, 10);
273 if (n == optarg || *n != '\0' || ctx.read_framerate < 0)
274 {
275 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -k | --readframerate option\n"
276 "Must be a non-negative integer\n", optarg);
277 ret = -1;
278 goto end;
279 }
280 break;
281 case 'p':
283 if (pix_fmt == NI_PIX_FMT_NONE)
284 {
285 if (!strcmp(optarg, "yuv444p"))
286 {
287 sw_pix_fmt = NI_SW_PIX_FMT_YUV444P;
288 }
289 else
290 {
291 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -p | --pix_fmt option\n"
292 "Must be one of [yuv420p, yuv420p10le, nv12, p010le, rgba, gbra, argb, abgr, bgr0, yuv444p]\n",
293 optarg);
294 ret = -1;
295 goto end;
296 }
297 }
298 break;
299 case 's':
300 if (s_index == MAX_INPUT_FILES)
301 {
302 ni_log(NI_LOG_ERROR, "Error: number of input resolutions cannot exceed %d\n", MAX_INPUT_FILES);
303 ret = -1;
304 goto end;
305 }
306 video_width[s_index] = (int)strtol(optarg, &n, 10);
307 {
308 int32_t tmp_height;
309 if (ni_strtoi(n + 1, &tmp_height) != NI_RETCODE_SUCCESS)
310 tmp_height = 0;
311 video_height[s_index] = tmp_height;
312 }
313 if ((*n != 'x') || (video_width[s_index] <= 0 || video_height[s_index] <= 0))
314 {
315 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -s | --size option\n"
316 "Must be in format [WIDTHxHEIGHT] (e.g. 1920x1080)\n", optarg);
317 ret = -1;
318 goto end;
319 }
320 s_index++;
321 break;
322 case 'e':
323 if (e_index == MAX_OUTPUT_FILES)
324 {
325 ni_log(NI_LOG_ERROR, "Error: number of encoder config cannot exceed %d\n", MAX_OUTPUT_FILES);
326 ret = -1;
327 goto end;
328 }
329 ni_strcpy(enc_conf_params[e_index], sizeof(enc_conf_params[e_index]), optarg);
330 e_index++;
331 break;
332 case 'g':
333 if (g_index == MAX_OUTPUT_FILES)
334 {
335 ni_log(NI_LOG_ERROR, "Error: number of encoder gop settings cannot exceed %d\n", MAX_OUTPUT_FILES);
336 ret = -1;
337 goto end;
338 }
339 ni_strcpy(enc_gop_params[g_index], sizeof(enc_gop_params[g_index]), optarg);
340 g_index++;
341 break;
342 case 'u':
343 hwupload = 1;
344 break;
345 case 'b':
346 if (f_index == MAX_OUTPUT_FILES)
347 {
348 ni_log(NI_LOG_ERROR, "Error: number of bitstream features log files cannot exceed %d\n", MAX_OUTPUT_FILES);
349 ret = -1;
350 goto end;
351 }
352 ni_strcpy(bitstream_features_log_file[f_index], FILE_NAME_LEN, optarg);
353 f_index++;
354 break;
355 default:
356 print_usage();
357 ret = -1;
358 goto end;
359 }
360 }
361
362 if (i_index == 0) {
363 ni_log(NI_LOG_ERROR, "Error: Missing input file argument (-i | --input)\n");
364 ret = -1;
365 goto end;
366 }
367
368 if (o_index == 0) {
369 ni_log(NI_LOG_ERROR, "Error: Missing output filepath (or filename) argument (-o | --output)\n");
370 ret = -1;
371 goto end;
372 }
373
374 if (s_index != i_index) {
375 ni_log(NI_LOG_ERROR, "Error: Number of input resolution specified does not match number of input files\n");
376 ret = -1;
377 goto end;
378 }
379
380 if (enc_codec_format == -1) {
381 ni_log(NI_LOG_ERROR, "Error: Missing encoder codec argument (-m | --enc-codec)\n");
382 ret = -1;
383 goto end;
384 }
385
386 // Validate that number of bitstream features log files matches number of output files
387 // Skip this check if bitstream-features option was not specified (optional feature)
388 if (f_index > 0)
389 {
390 if (f_index != o_index)
391 {
392 ni_log(NI_LOG_ERROR, "Error: Number of bitstream features log files (%d) must match number of output files (%d)\n", f_index, o_index);
393 ret = -1;
394 goto end;
395 }
396 }
397
398 input_total = i_index;
399 i_index = 0;
400 if (ctx.loops_left > 1 && input_total > 1)
401 {
402 ni_log(NI_LOG_ERROR, "Error: multiple input files not supported when loops %u greater than 1\n", ctx.loops_left);
403 ret = -1;
404 goto end;
405 }
406
407 output_total = o_index;
408 if (sw_pix_fmt == NI_SW_PIX_FMT_YUV444P && output_total != 2)
409 {
410 ni_log(NI_LOG_ERROR, "Error: Must indicate 2 output files for yuv444p encoding\n");
411 ret = -1;
412 goto end;
413 }
414
415 // Check for features not supported with hwupload
416 if (hwupload && ctx.read_framerate)
417 {
418 ni_log(NI_LOG_ERROR, "Error: -k | --readframerate option is not supported in hwupload mode\n");
419 ret = -1;
420 goto end;
421 }
422 if (hwupload && input_total > 1)
423 {
424 ni_log(NI_LOG_ERROR, "Error: multiple input (sequence change) is not supported in hwupload mode\n");
425 ret = -1;
426 goto end;
427 }
428 if (hwupload && sw_pix_fmt == NI_SW_PIX_FMT_YUV444P)
429 {
430 ni_log(NI_LOG_ERROR, "Error: yuv444p input is not supported in hwupload mode\n");
431 ret = -1;
432 goto end;
433 }
434
435 // Only in hwupload mode, the pixel formats not directly supported by encoder
436 // will be converted to yuv420p using 2D engine
437 if (!hwupload && !is_ni_enc_pix_fmt(pix_fmt) && sw_pix_fmt == NI_SW_PIX_FMT_NONE)
438 {
439 ni_log(NI_LOG_ERROR, "Error: pixel format %s is only supported in hwupload mode\n",
440 ni_pixel_format_name(pix_fmt));
441 ret = -1;
442 goto end;
443 }
444
445 if (ni_posix_memalign(&yuv_buf, sysconf(_SC_PAGESIZE), MAX_YUV_FRAME_SIZE))
446 {
447 ni_log(NI_LOG_ERROR, "Error: failed to allocate YUV data buffer\n");
448 ret = -1;
449 goto end;
450 }
451
452 for (i = 0; i < input_total; i++)
453 {
454 if (!in_filename[i][0])
455 {
456 ni_log(NI_LOG_ERROR, "Error: invalid input file %d\n", i);
457 ret = -1;
458 goto end;
459 }
460
461 input_fp[i] = NULL;
462 ni_fopen(&(input_fp[i]), in_filename[i], "rb");
463
464 if (!input_fp[i])
465 {
466 ni_log(NI_LOG_ERROR, "Error: Failed to open input %s\n", in_filename[i]);
467 ret = -1;
468 goto end;
469 }
470 ni_log(NI_LOG_INFO, "Opened input file: %s\n", in_filename[i]);
471 }
472
473 ctx.total_file_size = get_total_file_size(input_fp[0]);
474 ctx.curr_file_offset = 0;
475
476 for (i = 0; i < output_total; i++)
477 {
478 if (strcmp(out_filename[i], "null") != 0 &&
479 strcmp(out_filename[i], "/dev/null") != 0)
480 {
481 output_fp[i] = NULL;
482 if (enc_codec_format == NI_CODEC_FORMAT_JPEG)
483 {
484 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
485 out_filename[i], ctx.num_packets_received[i]);
486 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
487 } else
488 {
489 ni_fopen(&(output_fp[i]), out_filename[i], "wb");
490 }
491 if (!output_fp[i])
492 {
493 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", enc_codec_format == NI_CODEC_FORMAT_JPEG ? jpeg_filename[i] : out_filename[i]);
494 ret = -1;
495 goto end;
496 }
497 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
498 {
499 ni_log(NI_LOG_INFO, "Opened output file: %s\n", out_filename[i]);
500 }
501 } else
502 {
503 output_fp[i] = NULL;
504 ni_log(NI_LOG_INFO, "Note: Requested NULL output for index %d, no output file will be generated\n", i);
505 }
506 }
507
508 // Open bitstream features log files if specified
509 if (f_index > 0)
510 {
511 for (i = 0; i < output_total; i++)
512 {
513 if (bitstream_features_log_file[i][0] != 0)
514 {
515 bitstream_features_fp[i] = NULL;
516 ni_fopen(&bitstream_features_fp[i], bitstream_features_log_file[i], "w");
517 if (!bitstream_features_fp[i])
518 {
519 ni_log(NI_LOG_ERROR, "Error: Failed to open bitstream features log file %s\n", bitstream_features_log_file[i]);
520 ret = -1;
521 goto end;
522 }
523 ni_log(NI_LOG_INFO, "Opened bitstream features log file: %s (for output %d)\n", bitstream_features_log_file[i], i);
524 }
525 }
526 }
527
528 p_enc_api_param = calloc(output_total, sizeof(ni_xcoder_params_t));
529 if (!p_enc_api_param)
530 {
531 ni_log(NI_LOG_ERROR, "Error: Failed to allocate memory for p_enc_api_param\n");
532 ret = -1;
533 goto end;
534 }
535
536 for (i = 0; i < output_total; i++)
537 {
538 if (ni_device_session_context_init(&enc_ctx[i]) < 0)
539 {
540 ni_log(NI_LOG_ERROR, "Error: init encoder %d context error\n", i);
541 ret = -1;
542 goto end;
543 }
544 }
545
546 for (i = 0; i < output_total; i++)
547 {
548 enc_ctx[i].codec_format = enc_codec_format;
549 }
550
553
554 if (hwupload)
555 {
556 if (ni_device_session_context_init(&upl_ctx) < 0)
557 {
558 ni_log(NI_LOG_ERROR, "Error: init uploader context error\n");
559 ret = -1;
560 goto end;
561 }
562
563 if (ni_device_session_context_init(&sca_ctx) < 0)
564 {
565 ni_log(NI_LOG_ERROR, "Error: init scale context error\n");
566 ret = -1;
567 goto end;
568 }
569
570 ni_log(NI_LOG_INFO, "Starting hwupload + encoding mode: video resolution %dx%d\n",
571 video_width[0], video_height[0]);
572 // buffers by downstream entity like encoders
573 ret = uploader_open_session(&upl_ctx, xcoderGUID, video_width[0] < NI_MIN_WIDTH ? NI_MIN_WIDTH : video_width[0],
574 video_height[0] < NI_MIN_HEIGHT ? NI_MIN_HEIGHT : video_height[0], pix_fmt, 0, 3);
575 if (ret != 0)
576 {
577 goto end;
578 }
579
580 p_hwframe = hwupload_frame(&ctx, &upl_ctx, &sca_ctx, &swin_frame, &in_frame, &scale_frame, pix_fmt,
581 video_width[0], video_height[0], input_fp[0], yuv_buf, &eos);
582 if (p_hwframe == NULL)
583 {
584 ret = -1;
585 goto end;
586 }
587
588 ret = encoder_open(&ctx, &enc_ctx[0], p_enc_api_param, output_total, enc_conf_params, enc_gop_params,
589 NULL, video_width[0], video_height[0], 30, 1, 200000, enc_codec_format,
590 is_ni_enc_pix_fmt(pix_fmt) ? pix_fmt : NI_PIX_FMT_YUV420P,
591 0, xcoderGUID, p_hwframe, 0, false);
592 if (ret != 0)
593 {
594 goto end;
595 }
596
597 //unlike video codecs (1st packet is header), there is no header for Jpeg
598 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
599 {
600 do
601 {
602 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
603 video_width[0], video_height[0], output_total, output_fp);
604 }
605 while (receive_rc == NI_TEST_RETCODE_EAGAIN);
606
607 if (receive_rc == NI_TEST_RETCODE_SUCCESS)
608 {
609 ni_log(NI_LOG_INFO, "Got encoded sequence header packet\n");
610 }
611 else
612 {
613 ni_log(NI_LOG_ERROR, "Failed to get encoded sequence header packet, retcode %d\n", receive_rc);
614 ret = receive_rc;
615 goto end;
616 }
617 }
618 while (!end_of_all_streams &&
619 (send_rc == NI_TEST_RETCODE_SUCCESS || receive_rc== NI_TEST_RETCODE_SUCCESS ||
620 (send_rc == NI_TEST_RETCODE_EAGAIN && receive_rc == NI_TEST_RETCODE_EAGAIN)))
621 {
622 if (first_frame_uploaded && !eos)
623 {
624 p_hwframe = hwupload_frame(&ctx, &upl_ctx, &sca_ctx, &swin_frame, &in_frame, &scale_frame, pix_fmt,
625 video_width[0], video_height[0], input_fp[0], yuv_buf, &eos);
627 {
628 ni_log(NI_LOG_DEBUG, "No space to write to, try to read a packet\n");
629 p_in_frame = &in_frame;
630 goto receive_pkt;
631 } else if (p_hwframe == NULL)
632 {
634 break;
635 }
636 }
637
638 // Sending
639 p_in_frame = is_ni_enc_pix_fmt(pix_fmt) ? &in_frame : &scale_frame;
640 for (i = 0; i < output_total; i++)
641 {
642 ctx.curr_enc_index = i;
643 send_rc = encoder_send_data3(&ctx, &enc_ctx[i], p_in_frame, video_width[0], video_height[0], eos);
644 first_frame_uploaded = 1; //since first frame read before while-loop
645 if (send_rc < 0) //Error
646 {
647 ni_log(NI_LOG_ERROR, "enc %d send error, quit !\n", i);
648 ni_hw_frame_ref(p_hwframe);
649 end_of_all_streams = 1;
650 break;
651 }
652 //track in array with unique index, free when enc read finds
653 //this must be implemented in application space for complete
654 //tracking of hwframes
655 if (!ctx.enc_resend[i])
656 {
657 //successful read means there is recycle to check
658 ni_hw_frame_ref(p_hwframe);
659 } else
660 {
661 ni_log(NI_LOG_DEBUG, "enc %d need to re-send !\n", i);
662 ni_usleep(500);
663 i--;
664 continue;
665 }
666 }
667 if (end_of_all_streams)
668 break;
669
670receive_pkt:
671 for (i = 0; i < output_total; i++)
672 {
673 prev_num_pkt[i] = ctx.num_packets_received[i];
674 }
675
676 receive_rc = encoder_receive(&ctx, enc_ctx, p_in_frame, out_packet,
677 video_width[0], video_height[0], output_total, output_fp);
678 // Write bitstream features if log files are specified and packets were received
679 if (f_index > 0 && receive_rc == NI_TEST_RETCODE_SUCCESS)
680 {
681 for (i = 0; i < output_total; i++)
682 {
683 if (out_packet[i].data.packet.data_len > 0 && bitstream_features_fp[i] != NULL)
684 {
685 ni_write_bitstream_features(&enc_ctx[i], &out_packet[i].data.packet, bitstream_features_fp[i]);
686 }
687 }
688 }
689 for (i = 0; receive_rc >= 0 && i < output_total; i++)
690 {
691 if (!ctx.enc_eos_received[i])
692 {
693 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
694 prev_num_pkt[i] < ctx.num_packets_received[i] &&
695 output_fp[i] != NULL)
696 {
697 fclose(output_fp[i]);
698 output_fp[i] = NULL;
699 ni_log(NI_LOG_DEBUG, "Save jpeg file : %s\n", jpeg_filename[i]);
700 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
701 out_filename[i], ctx.num_packets_received[i]);
702 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
703 if (!output_fp[i])
704 {
705 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", jpeg_filename[i]);
706 ret = -1;
707 break;
708 }
709 }
710
711 ni_log(NI_LOG_DEBUG, "enc %d continues to read!\n", i);
712 end_of_all_streams = 0;
713 break;
714 } else
715 {
716 ni_log(NI_LOG_DEBUG, "enc %d eos !\n", i);
717 end_of_all_streams = 1;
718 }
719 }
720
722 if (current_time - previous_time >= (uint64_t)1000000000) {
723 for (i = 0; i < output_total; i++)
724 {
725 ni_log(NI_LOG_INFO, "Encoder %d stats: received %u packets, fps %.2f, total bytes %u\n",
726 i, ctx.num_packets_received[i],
727 (float)enc_ctx[i].frame_num / (float)(current_time - ctx.start_time) * (float)1000000000,
729 }
731 }
732 }
733
735 if (!is_ni_enc_pix_fmt(pix_fmt))
736 { //Uploading rgba requires scaler conversion so close the session too
738 }
739 }
740 else
741 {
742 in_frame.data.frame.pixel_format = pix_fmt;
743 ret = encoder_open(&ctx, enc_ctx, p_enc_api_param, output_total,
744 enc_conf_params, enc_gop_params, NULL, video_width[0],
745 video_height[0], 30, 1, 200000,
746 enc_codec_format, pix_fmt, 0, xcoderGUID, NULL,
747 0, (sw_pix_fmt != NI_SW_PIX_FMT_NONE) ? false : true);
748 // zero copy is not supported for YUV444P
749 if (ret != 0)
750 {
751 goto end;
752 }
753
754 //unlike video codecs (1st packet is header), there is no header for Jpeg
755 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
756 {
757 do
758 {
759 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
760 video_width[0], video_height[0], output_total, output_fp);
761 }
762 while (receive_rc == NI_TEST_RETCODE_EAGAIN);
763
764 if (receive_rc == NI_TEST_RETCODE_SUCCESS)
765 {
766 ni_log(NI_LOG_INFO, "Got encoded sequence header packet\n");
767 }
768 else
769 {
770 ni_log(NI_LOG_ERROR, "Failed to get encoded sequence header packet, retcode %d\n", receive_rc);
771 ret = receive_rc;
772 goto end;
773 }
774 }
775
776 ni_log(NI_LOG_INFO, "Starting to encode: video resolution %dx%d\n", video_width[0], video_height[0]);
777
778 while (!end_of_all_streams &&
779 (send_rc == NI_TEST_RETCODE_SUCCESS || receive_rc == NI_TEST_RETCODE_SUCCESS ||
780 (send_rc == NI_TEST_RETCODE_EAGAIN && receive_rc == NI_TEST_RETCODE_EAGAIN)))
781 {
782 read_size = read_yuv_from_file(&ctx, input_fp[i_index], yuv_buf,
783 video_width[i_index], video_height[i_index],
784 pix_fmt, sw_pix_fmt, &eos,
785 enc_ctx[0].session_run_state);
786 if (read_size < 0)
787 {
788 break;
789 }
790
791 // YUV444P reading
792 if (sw_pix_fmt != NI_SW_PIX_FMT_NONE)
793 {
794 ret = convert_yuv_444p_to_420p(&sw_pix_frame[0], eos ? NULL : yuv_buf,
795 video_width[i_index], video_height[i_index],
796 sw_pix_fmt, 0, enc_codec_format);
797 if (ret < 0)
798 {
799 break;
800 }
801 }
802
803 for (i = 0; i < output_total; i++)
804 {
805 if (sw_pix_fmt != NI_SW_PIX_FMT_NONE)
806 {
807 ctx.curr_enc_index = i;
808 send_rc = encoder_send_data3(&ctx, &enc_ctx[i], &sw_pix_frame[i],
809 video_width[i_index], video_height[i_index], eos);
810 } else
811 {
812 ctx.curr_enc_index = i;
813 send_rc = encoder_send_data(&ctx, &enc_ctx[i],
814 &in_frame, eos ? NULL : yuv_buf,
815 video_width[i_index], video_height[i_index],
816 i_index == input_total - 1);
817 }
818
819 if (send_rc == NI_TEST_RETCODE_EAGAIN)
820 {
821 // retry send to same encoder session
822 i--;
823 continue;
824 } else if (send_rc == NI_TEST_RETCODE_NEXT_INPUT) // next input (will trigger sequence change)
825 {
826 i_index++;
827 ctx.total_file_size = get_total_file_size(input_fp[i_index]);
828 ctx.curr_file_offset = 0;
829 send_rc = NI_TEST_RETCODE_SUCCESS;
830 }
831 }
832
833 for (i = 0; i < output_total; i++)
834 {
835 prev_num_pkt[i] = ctx.num_packets_received[i];
836 }
837
838 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
839 video_width[0], video_height[0], output_total, output_fp);
840 // Write bitstream features if log files are specified and packets were received
841 if (f_index > 0 && receive_rc == NI_TEST_RETCODE_SUCCESS)
842 {
843 for (i = 0; i < output_total; i++)
844 {
845 if (out_packet[i].data.packet.data_len > 0 && bitstream_features_fp[i] != NULL)
846 {
847 ni_write_bitstream_features(&enc_ctx[i], &out_packet[i].data.packet, bitstream_features_fp[i]);
848 }
849 }
850 }
851 for (i = 0; receive_rc >= 0 && i < output_total; i++)
852 {
853 if (!ctx.enc_eos_received[i])
854 {
855 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
856 prev_num_pkt[i] < ctx.num_packets_received[i] &&
857 output_fp[i] != NULL)
858 {
859 fclose(output_fp[i]);
860 output_fp[i] = NULL;
861 ni_log(NI_LOG_DEBUG, "Save jpeg file : %s\n", jpeg_filename[i]);
862 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
863 out_filename[i], ctx.num_packets_received[i]);
864 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
865 if (!output_fp[i])
866 {
867 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", jpeg_filename[i]);
868 ret = -1;
869 break;
870 }
871 }
872
873 ni_log(NI_LOG_DEBUG, "enc %d continues to read!\n", i);
874 end_of_all_streams = 0;
875 break;
876 } else
877 {
878 ni_log(NI_LOG_DEBUG, "enc %d eos !\n", i);
879 end_of_all_streams = 1;
880 }
881 }
882
884 if (current_time - previous_time >= (uint64_t)1000000000) {
885 for (i = 0; i < output_total; i++)
886 {
887 ni_log(NI_LOG_INFO, "Encoder %d stats: received %u packets, fps %.2f, total bytes %u\n",
888 i, ctx.num_packets_received[i],
889 (float)enc_ctx[i].frame_num / (float)(current_time - ctx.start_time) * (float)1000000000,
892 }
893 }
894 }
895 }
896
897 //delete jpeg file that are 0 bytes in size
898 for (i = 0; i < output_total; i++)
899 {
900 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
901 output_fp[i] != NULL)
902 {
903 fseek(output_fp[i], 0, SEEK_END);
904 if (ftell(output_fp[i]) == 0)
905 {
906 fclose(output_fp[i]);
907 output_fp[i] = NULL;
908 remove(jpeg_filename[i]);
909 }
910 }
911 }
912
913end:
914 encoder_stat_report_and_close(&ctx, enc_ctx, output_total);
915
916#ifdef _WIN32
917 for (i = 0; i < output_total; i++)
918 {
919 ni_device_close(enc_ctx[i].device_handle);
920 }
923#elif __linux__
924 for (i = 0; i < output_total; i++)
925 {
926 ni_device_close(enc_ctx[i].device_handle);
927 ni_device_close(enc_ctx[i].blk_io_handle);
928 }
933#endif
934
936 ni_frame_buffer_free(&swin_frame.data.frame);
937 for (i = 0; i < output_total; i++)
938 {
939 ni_packet_buffer_free(&out_packet[i].data.packet);
940 }
941 for (i = 0; i < sizeof(sw_pix_frame)/sizeof(ni_session_data_io_t); i++)
942 {
943 ni_frame_buffer_free(&sw_pix_frame[i].data.frame);
944 }
945
948 for (i = 0; i < output_total; i++)
949 {
951 if (output_fp[i] != NULL)
952 {
953 fclose(output_fp[i]);
954 }
955 }
956
957 for (i = 0; i < input_total; i++)
958 {
959 if (input_fp[i])
960 {
961 fclose(input_fp[i]);
962 }
963 }
964
965 // Close bitstream features log files if they were opened
966 if (f_index > 0)
967 {
968 for (i = 0; i < output_total; i++)
969 {
970 if (bitstream_features_fp[i] != NULL)
971 {
972 fclose(bitstream_features_fp[i]);
973 bitstream_features_fp[i] = NULL;
974 }
975 }
976 }
977
978 ni_aligned_free(yuv_buf);
979 free(p_enc_api_param);
980
981 for(i = 0; i < MAX_OUTPUT_FILES; ++i)
982 {
983 free(ctx.enc_pts_queue[i]);
984 ctx.enc_pts_queue[i] = NULL;
985 }
986
987 return ret;
988}
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:555
@ NI_RETCODE_SUCCESS
Definition ni_defs.h:448
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.
void ni_device_close(ni_device_handle_t device_handle)
Close device and release resources.
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
void ni_write_bitstream_features(ni_session_context_t *p_enc_ctx, ni_packet_t *p_out_pkt, FILE *p_file)
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:206
ni_retcode_t ni_fopen(FILE **fp, const char *filename, const char *mode)
Definition ni_util.c:998
ni_retcode_t ni_strcpy(char *dest, size_t dmax, const char *src)
Definition ni_util.c:460
void ni_usleep(int64_t usec)
Definition ni_util.c:366
uint64_t ni_gettime_ns(void)
Definition ni_util.c:2701
ni_retcode_t ni_strtoi(const char *str, int32_t *out_val)
Safely convert a decimal string to a 32-bit integer with full validation. Trailing whitespace is tole...
Definition ni_util.c:2577
Utility definitions.
#define ni_aligned_free(p_memptr)
Definition ni_util.h:402
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]
uint32_t data_len
ni_device_handle_t device_handle
ni_device_handle_t blk_io_handle
union _ni_session_data_io::@19 data