libxcoder 5.7.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 video_height[s_index] = atoi(n + 1);
308 if ((*n != 'x') || (video_width[s_index] <= 0 || video_height[s_index] <= 0))
309 {
310 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -s | --size option\n"
311 "Must be in format [WIDTHxHEIGHT] (e.g. 1920x1080)\n", optarg);
312 ret = -1;
313 goto end;
314 }
315 s_index++;
316 break;
317 case 'e':
318 if (e_index == MAX_OUTPUT_FILES)
319 {
320 ni_log(NI_LOG_ERROR, "Error: number of encoder config cannot exceed %d\n", MAX_OUTPUT_FILES);
321 ret = -1;
322 goto end;
323 }
324 ni_strcpy(enc_conf_params[e_index], sizeof(enc_conf_params[e_index]), optarg);
325 e_index++;
326 break;
327 case 'g':
328 if (g_index == MAX_OUTPUT_FILES)
329 {
330 ni_log(NI_LOG_ERROR, "Error: number of encoder gop settings cannot exceed %d\n", MAX_OUTPUT_FILES);
331 ret = -1;
332 goto end;
333 }
334 ni_strcpy(enc_gop_params[g_index], sizeof(enc_gop_params[g_index]), optarg);
335 g_index++;
336 break;
337 case 'u':
338 hwupload = 1;
339 break;
340 case 'b':
341 if (f_index == MAX_OUTPUT_FILES)
342 {
343 ni_log(NI_LOG_ERROR, "Error: number of bitstream features log files cannot exceed %d\n", MAX_OUTPUT_FILES);
344 ret = -1;
345 goto end;
346 }
347 ni_strcpy(bitstream_features_log_file[f_index], FILE_NAME_LEN, optarg);
348 f_index++;
349 break;
350 default:
351 print_usage();
352 ret = -1;
353 goto end;
354 }
355 }
356
357 if (i_index == 0) {
358 ni_log(NI_LOG_ERROR, "Error: Missing input file argument (-i | --input)\n");
359 ret = -1;
360 goto end;
361 }
362
363 if (o_index == 0) {
364 ni_log(NI_LOG_ERROR, "Error: Missing output filepath (or filename) argument (-o | --output)\n");
365 ret = -1;
366 goto end;
367 }
368
369 if (s_index != i_index) {
370 ni_log(NI_LOG_ERROR, "Error: Number of input resolution specified does not match number of input files\n");
371 ret = -1;
372 goto end;
373 }
374
375 if (enc_codec_format == -1) {
376 ni_log(NI_LOG_ERROR, "Error: Missing encoder codec argument (-m | --enc-codec)\n");
377 ret = -1;
378 goto end;
379 }
380
381 // Validate that number of bitstream features log files matches number of output files
382 // Skip this check if bitstream-features option was not specified (optional feature)
383 if (f_index > 0)
384 {
385 if (f_index != o_index)
386 {
387 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);
388 ret = -1;
389 goto end;
390 }
391 }
392
393 input_total = i_index;
394 i_index = 0;
395 if (ctx.loops_left > 1 && input_total > 1)
396 {
397 ni_log(NI_LOG_ERROR, "Error: multiple input files not supported when loops %u greater than 1\n", ctx.loops_left);
398 ret = -1;
399 goto end;
400 }
401
402 output_total = o_index;
403 if (sw_pix_fmt == NI_SW_PIX_FMT_YUV444P && output_total != 2)
404 {
405 ni_log(NI_LOG_ERROR, "Error: Must indicate 2 output files for yuv444p encoding\n");
406 ret = -1;
407 goto end;
408 }
409
410 // Check for features not supported with hwupload
411 if (hwupload && ctx.read_framerate)
412 {
413 ni_log(NI_LOG_ERROR, "Error: -k | --readframerate option is not supported in hwupload mode\n");
414 ret = -1;
415 goto end;
416 }
417 if (hwupload && input_total > 1)
418 {
419 ni_log(NI_LOG_ERROR, "Error: multiple input (sequence change) is not supported in hwupload mode\n");
420 ret = -1;
421 goto end;
422 }
423 if (hwupload && sw_pix_fmt == NI_SW_PIX_FMT_YUV444P)
424 {
425 ni_log(NI_LOG_ERROR, "Error: yuv444p input is not supported in hwupload mode\n");
426 ret = -1;
427 goto end;
428 }
429
430 // Only in hwupload mode, the pixel formats not directly supported by encoder
431 // will be converted to yuv420p using 2D engine
432 if (!hwupload && !is_ni_enc_pix_fmt(pix_fmt) && sw_pix_fmt == NI_SW_PIX_FMT_NONE)
433 {
434 ni_log(NI_LOG_ERROR, "Error: pixel format %s is only supported in hwupload mode\n",
435 ni_pixel_format_name(pix_fmt));
436 ret = -1;
437 goto end;
438 }
439
440 if (ni_posix_memalign(&yuv_buf, sysconf(_SC_PAGESIZE), MAX_YUV_FRAME_SIZE))
441 {
442 ni_log(NI_LOG_ERROR, "Error: failed to allocate YUV data buffer\n");
443 ret = -1;
444 goto end;
445 }
446
447 for (i = 0; i < input_total; i++)
448 {
449 if (!in_filename[i][0])
450 {
451 ni_log(NI_LOG_ERROR, "Error: invalid input file %d\n", i);
452 ret = -1;
453 goto end;
454 }
455
456 input_fp[i] = NULL;
457 ni_fopen(&(input_fp[i]), in_filename[i], "rb");
458
459 if (!input_fp[i])
460 {
461 ni_log(NI_LOG_ERROR, "Error: Failed to open input %s\n", in_filename[i]);
462 ret = -1;
463 goto end;
464 }
465 ni_log(NI_LOG_INFO, "Opened input file: %s\n", in_filename[i]);
466 }
467
468 ctx.total_file_size = get_total_file_size(input_fp[0]);
469 ctx.curr_file_offset = 0;
470
471 for (i = 0; i < output_total; i++)
472 {
473 if (strcmp(out_filename[i], "null") != 0 &&
474 strcmp(out_filename[i], "/dev/null") != 0)
475 {
476 output_fp[i] = NULL;
477 if (enc_codec_format == NI_CODEC_FORMAT_JPEG)
478 {
479 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
480 out_filename[i], ctx.num_packets_received[i]);
481 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
482 } else
483 {
484 ni_fopen(&(output_fp[i]), out_filename[i], "wb");
485 }
486 if (!output_fp[i])
487 {
488 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", enc_codec_format == NI_CODEC_FORMAT_JPEG ? jpeg_filename[i] : out_filename[i]);
489 ret = -1;
490 goto end;
491 }
492 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
493 {
494 ni_log(NI_LOG_INFO, "Opened output file: %s\n", out_filename[i]);
495 }
496 } else
497 {
498 output_fp[i] = NULL;
499 ni_log(NI_LOG_INFO, "Note: Requested NULL output for index %d, no output file will be generated\n", i);
500 }
501 }
502
503 // Open bitstream features log files if specified
504 if (f_index > 0)
505 {
506 for (i = 0; i < output_total; i++)
507 {
508 if (bitstream_features_log_file[i][0] != 0)
509 {
510 bitstream_features_fp[i] = NULL;
511 ni_fopen(&bitstream_features_fp[i], bitstream_features_log_file[i], "w");
512 if (!bitstream_features_fp[i])
513 {
514 ni_log(NI_LOG_ERROR, "Error: Failed to open bitstream features log file %s\n", bitstream_features_log_file[i]);
515 ret = -1;
516 goto end;
517 }
518 ni_log(NI_LOG_INFO, "Opened bitstream features log file: %s (for output %d)\n", bitstream_features_log_file[i], i);
519 }
520 }
521 }
522
523 p_enc_api_param = calloc(output_total, sizeof(ni_xcoder_params_t));
524 if (!p_enc_api_param)
525 {
526 ni_log(NI_LOG_ERROR, "Error: Failed to allocate memory for p_enc_api_param\n");
527 ret = -1;
528 goto end;
529 }
530
531 for (i = 0; i < output_total; i++)
532 {
533 if (ni_device_session_context_init(&enc_ctx[i]) < 0)
534 {
535 ni_log(NI_LOG_ERROR, "Error: init encoder %d context error\n", i);
536 ret = -1;
537 goto end;
538 }
539 }
540
541 for (i = 0; i < output_total; i++)
542 {
543 enc_ctx[i].codec_format = enc_codec_format;
544 }
545
548
549 if (hwupload)
550 {
551 if (ni_device_session_context_init(&upl_ctx) < 0)
552 {
553 ni_log(NI_LOG_ERROR, "Error: init uploader context error\n");
554 ret = -1;
555 goto end;
556 }
557
558 if (ni_device_session_context_init(&sca_ctx) < 0)
559 {
560 ni_log(NI_LOG_ERROR, "Error: init scale context error\n");
561 ret = -1;
562 goto end;
563 }
564
565 ni_log(NI_LOG_INFO, "Starting hwupload + encoding mode: video resolution %dx%d\n",
566 video_width[0], video_height[0]);
567 // buffers by downstream entity like encoders
568 ret = uploader_open_session(&upl_ctx, xcoderGUID, video_width[0] < NI_MIN_WIDTH ? NI_MIN_WIDTH : video_width[0],
569 video_height[0] < NI_MIN_HEIGHT ? NI_MIN_HEIGHT : video_height[0], pix_fmt, 0, 3);
570 if (ret != 0)
571 {
572 goto end;
573 }
574
575 p_hwframe = hwupload_frame(&ctx, &upl_ctx, &sca_ctx, &swin_frame, &in_frame, &scale_frame, pix_fmt,
576 video_width[0], video_height[0], input_fp[0], yuv_buf, &eos);
577 if (p_hwframe == NULL)
578 {
579 ret = -1;
580 goto end;
581 }
582
583 ret = encoder_open(&ctx, &enc_ctx[0], p_enc_api_param, output_total, enc_conf_params, enc_gop_params,
584 NULL, video_width[0], video_height[0], 30, 1, 200000, enc_codec_format,
585 is_ni_enc_pix_fmt(pix_fmt) ? pix_fmt : NI_PIX_FMT_YUV420P,
586 0, xcoderGUID, p_hwframe, 0, false);
587 if (ret != 0)
588 {
589 goto end;
590 }
591
592 //unlike video codecs (1st packet is header), there is no header for Jpeg
593 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
594 {
595 do
596 {
597 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
598 video_width[0], video_height[0], output_total, output_fp);
599 }
600 while (receive_rc == NI_TEST_RETCODE_EAGAIN);
601
602 if (receive_rc == NI_TEST_RETCODE_SUCCESS)
603 {
604 ni_log(NI_LOG_INFO, "Got encoded sequence header packet\n");
605 }
606 else
607 {
608 ni_log(NI_LOG_ERROR, "Failed to get encoded sequence header packet, retcode %d\n", receive_rc);
609 ret = receive_rc;
610 goto end;
611 }
612 }
613 while (!end_of_all_streams &&
614 (send_rc == NI_TEST_RETCODE_SUCCESS || receive_rc== NI_TEST_RETCODE_SUCCESS ||
615 (send_rc == NI_TEST_RETCODE_EAGAIN && receive_rc == NI_TEST_RETCODE_EAGAIN)))
616 {
617 if (first_frame_uploaded && !eos)
618 {
619 p_hwframe = hwupload_frame(&ctx, &upl_ctx, &sca_ctx, &swin_frame, &in_frame, &scale_frame, pix_fmt,
620 video_width[0], video_height[0], input_fp[0], yuv_buf, &eos);
622 {
623 ni_log(NI_LOG_DEBUG, "No space to write to, try to read a packet\n");
624 p_in_frame = &in_frame;
625 goto receive_pkt;
626 } else if (p_hwframe == NULL)
627 {
629 break;
630 }
631 }
632
633 // Sending
634 p_in_frame = is_ni_enc_pix_fmt(pix_fmt) ? &in_frame : &scale_frame;
635 for (i = 0; i < output_total; i++)
636 {
637 ctx.curr_enc_index = i;
638 send_rc = encoder_send_data3(&ctx, &enc_ctx[i], p_in_frame, video_width[0], video_height[0], eos);
639 first_frame_uploaded = 1; //since first frame read before while-loop
640 if (send_rc < 0) //Error
641 {
642 ni_log(NI_LOG_ERROR, "enc %d send error, quit !\n", i);
643 ni_hw_frame_ref(p_hwframe);
644 end_of_all_streams = 1;
645 break;
646 }
647 //track in array with unique index, free when enc read finds
648 //this must be implemented in application space for complete
649 //tracking of hwframes
650 if (!ctx.enc_resend[i])
651 {
652 //successful read means there is recycle to check
653 ni_hw_frame_ref(p_hwframe);
654 } else
655 {
656 ni_log(NI_LOG_DEBUG, "enc %d need to re-send !\n", i);
657 ni_usleep(500);
658 i--;
659 continue;
660 }
661 }
662 if (end_of_all_streams)
663 break;
664
665receive_pkt:
666 for (i = 0; i < output_total; i++)
667 {
668 prev_num_pkt[i] = ctx.num_packets_received[i];
669 }
670
671 receive_rc = encoder_receive(&ctx, enc_ctx, p_in_frame, out_packet,
672 video_width[0], video_height[0], output_total, output_fp);
673 // Write bitstream features if log files are specified and packets were received
674 if (f_index > 0 && receive_rc == NI_TEST_RETCODE_SUCCESS)
675 {
676 for (i = 0; i < output_total; i++)
677 {
678 if (out_packet[i].data.packet.data_len > 0 && bitstream_features_fp[i] != NULL)
679 {
680 ni_write_bitstream_features(&enc_ctx[i], &out_packet[i].data.packet, bitstream_features_fp[i]);
681 }
682 }
683 }
684 for (i = 0; receive_rc >= 0 && i < output_total; i++)
685 {
686 if (!ctx.enc_eos_received[i])
687 {
688 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
689 prev_num_pkt[i] < ctx.num_packets_received[i] &&
690 output_fp[i] != NULL)
691 {
692 fclose(output_fp[i]);
693 output_fp[i] = NULL;
694 ni_log(NI_LOG_DEBUG, "Save jpeg file : %s\n", jpeg_filename[i]);
695 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
696 out_filename[i], ctx.num_packets_received[i]);
697 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
698 if (!output_fp[i])
699 {
700 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", jpeg_filename[i]);
701 ret = -1;
702 break;
703 }
704 }
705
706 ni_log(NI_LOG_DEBUG, "enc %d continues to read!\n", i);
707 end_of_all_streams = 0;
708 break;
709 } else
710 {
711 ni_log(NI_LOG_DEBUG, "enc %d eos !\n", i);
712 end_of_all_streams = 1;
713 }
714 }
715
717 if (current_time - previous_time >= (uint64_t)1000000000) {
718 for (i = 0; i < output_total; i++)
719 {
720 ni_log(NI_LOG_INFO, "Encoder %d stats: received %u packets, fps %.2f, total bytes %u\n",
721 i, ctx.num_packets_received[i],
722 (float)enc_ctx[i].frame_num / (float)(current_time - ctx.start_time) * (float)1000000000,
724 }
726 }
727 }
728
730 if (!is_ni_enc_pix_fmt(pix_fmt))
731 { //Uploading rgba requires scaler conversion so close the session too
733 }
734 }
735 else
736 {
737 in_frame.data.frame.pixel_format = pix_fmt;
738 ret = encoder_open(&ctx, enc_ctx, p_enc_api_param, output_total,
739 enc_conf_params, enc_gop_params, NULL, video_width[0],
740 video_height[0], 30, 1, 200000,
741 enc_codec_format, pix_fmt, 0, xcoderGUID, NULL,
742 0, (sw_pix_fmt != NI_SW_PIX_FMT_NONE) ? false : true);
743 // zero copy is not supported for YUV444P
744 if (ret != 0)
745 {
746 goto end;
747 }
748
749 //unlike video codecs (1st packet is header), there is no header for Jpeg
750 if (enc_codec_format != NI_CODEC_FORMAT_JPEG)
751 {
752 do
753 {
754 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
755 video_width[0], video_height[0], output_total, output_fp);
756 }
757 while (receive_rc == NI_TEST_RETCODE_EAGAIN);
758
759 if (receive_rc == NI_TEST_RETCODE_SUCCESS)
760 {
761 ni_log(NI_LOG_INFO, "Got encoded sequence header packet\n");
762 }
763 else
764 {
765 ni_log(NI_LOG_ERROR, "Failed to get encoded sequence header packet, retcode %d\n", receive_rc);
766 ret = receive_rc;
767 goto end;
768 }
769 }
770
771 ni_log(NI_LOG_INFO, "Starting to encode: video resolution %dx%d\n", video_width[0], video_height[0]);
772
773 while (!end_of_all_streams &&
774 (send_rc == NI_TEST_RETCODE_SUCCESS || receive_rc == NI_TEST_RETCODE_SUCCESS ||
775 (send_rc == NI_TEST_RETCODE_EAGAIN && receive_rc == NI_TEST_RETCODE_EAGAIN)))
776 {
777 read_size = read_yuv_from_file(&ctx, input_fp[i_index], yuv_buf,
778 video_width[i_index], video_height[i_index],
779 pix_fmt, sw_pix_fmt, &eos,
780 enc_ctx[0].session_run_state);
781 if (read_size < 0)
782 {
783 break;
784 }
785
786 // YUV444P reading
787 if (sw_pix_fmt != NI_SW_PIX_FMT_NONE)
788 {
789 ret = convert_yuv_444p_to_420p(&sw_pix_frame[0], eos ? NULL : yuv_buf,
790 video_width[i_index], video_height[i_index],
791 sw_pix_fmt, 0, enc_codec_format);
792 if (ret < 0)
793 {
794 break;
795 }
796 }
797
798 for (i = 0; i < output_total; i++)
799 {
800 if (sw_pix_fmt != NI_SW_PIX_FMT_NONE)
801 {
802 ctx.curr_enc_index = i;
803 send_rc = encoder_send_data3(&ctx, &enc_ctx[i], &sw_pix_frame[i],
804 video_width[i_index], video_height[i_index], eos);
805 } else
806 {
807 ctx.curr_enc_index = i;
808 send_rc = encoder_send_data(&ctx, &enc_ctx[i],
809 &in_frame, eos ? NULL : yuv_buf,
810 video_width[i_index], video_height[i_index],
811 i_index == input_total - 1);
812 }
813
814 if (send_rc == NI_TEST_RETCODE_EAGAIN)
815 {
816 // retry send to same encoder session
817 i--;
818 continue;
819 } else if (send_rc == NI_TEST_RETCODE_NEXT_INPUT) // next input (will trigger sequence change)
820 {
821 i_index++;
822 ctx.total_file_size = get_total_file_size(input_fp[i_index]);
823 ctx.curr_file_offset = 0;
824 send_rc = NI_TEST_RETCODE_SUCCESS;
825 }
826 }
827
828 for (i = 0; i < output_total; i++)
829 {
830 prev_num_pkt[i] = ctx.num_packets_received[i];
831 }
832
833 receive_rc = encoder_receive(&ctx, enc_ctx, &in_frame, out_packet,
834 video_width[0], video_height[0], output_total, output_fp);
835 // Write bitstream features if log files are specified and packets were received
836 if (f_index > 0 && receive_rc == NI_TEST_RETCODE_SUCCESS)
837 {
838 for (i = 0; i < output_total; i++)
839 {
840 if (out_packet[i].data.packet.data_len > 0 && bitstream_features_fp[i] != NULL)
841 {
842 ni_write_bitstream_features(&enc_ctx[i], &out_packet[i].data.packet, bitstream_features_fp[i]);
843 }
844 }
845 }
846 for (i = 0; receive_rc >= 0 && i < output_total; i++)
847 {
848 if (!ctx.enc_eos_received[i])
849 {
850 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
851 prev_num_pkt[i] < ctx.num_packets_received[i] &&
852 output_fp[i] != NULL)
853 {
854 fclose(output_fp[i]);
855 output_fp[i] = NULL;
856 ni_log(NI_LOG_DEBUG, "Save jpeg file : %s\n", jpeg_filename[i]);
857 snprintf(jpeg_filename[i], sizeof(jpeg_filename[i]), "%s/%" PRIu64 ".png",
858 out_filename[i], ctx.num_packets_received[i]);
859 ni_fopen(&(output_fp[i]), jpeg_filename[i], "wb");
860 if (!output_fp[i])
861 {
862 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", jpeg_filename[i]);
863 ret = -1;
864 break;
865 }
866 }
867
868 ni_log(NI_LOG_DEBUG, "enc %d continues to read!\n", i);
869 end_of_all_streams = 0;
870 break;
871 } else
872 {
873 ni_log(NI_LOG_DEBUG, "enc %d eos !\n", i);
874 end_of_all_streams = 1;
875 }
876 }
877
879 if (current_time - previous_time >= (uint64_t)1000000000) {
880 for (i = 0; i < output_total; i++)
881 {
882 ni_log(NI_LOG_INFO, "Encoder %d stats: received %u packets, fps %.2f, total bytes %u\n",
883 i, ctx.num_packets_received[i],
884 (float)enc_ctx[i].frame_num / (float)(current_time - ctx.start_time) * (float)1000000000,
887 }
888 }
889 }
890 }
891
892 //delete jpeg file that are 0 bytes in size
893 for (i = 0; i < output_total; i++)
894 {
895 if (enc_ctx[i].codec_format == NI_CODEC_FORMAT_JPEG &&
896 output_fp[i] != NULL)
897 {
898 fseek(output_fp[i], 0, SEEK_END);
899 if (ftell(output_fp[i]) == 0)
900 {
901 fclose(output_fp[i]);
902 output_fp[i] = NULL;
903 remove(jpeg_filename[i]);
904 }
905 }
906 }
907
908end:
909 encoder_stat_report_and_close(&ctx, enc_ctx, output_total);
910
911#ifdef _WIN32
912 for (i = 0; i < output_total; i++)
913 {
914 ni_device_close(enc_ctx[i].device_handle);
915 }
918#elif __linux__
919 for (i = 0; i < output_total; i++)
920 {
921 ni_device_close(enc_ctx[i].device_handle);
922 ni_device_close(enc_ctx[i].blk_io_handle);
923 }
928#endif
929
931 ni_frame_buffer_free(&swin_frame.data.frame);
932 for (i = 0; i < output_total; i++)
933 {
934 ni_packet_buffer_free(&out_packet[i].data.packet);
935 }
936 for (i = 0; i < sizeof(sw_pix_frame)/sizeof(ni_session_data_io_t); i++)
937 {
938 ni_frame_buffer_free(&sw_pix_frame[i].data.frame);
939 }
940
943 for (i = 0; i < output_total; i++)
944 {
946 if (output_fp[i] != NULL)
947 {
948 fclose(output_fp[i]);
949 }
950 }
951
952 for (i = 0; i < input_total; i++)
953 {
954 if (input_fp[i])
955 {
956 fclose(input_fp[i]);
957 }
958 }
959
960 // Close bitstream features log files if they were opened
961 if (f_index > 0)
962 {
963 for (i = 0; i < output_total; i++)
964 {
965 if (bitstream_features_fp[i] != NULL)
966 {
967 fclose(bitstream_features_fp[i]);
968 bitstream_features_fp[i] = NULL;
969 }
970 }
971 }
972
973 ni_aligned_free(yuv_buf);
974 free(p_enc_api_param);
975
976 for(i = 0; i < MAX_OUTPUT_FILES; ++i)
977 {
978 free(ctx.enc_pts_queue[i]);
979 ctx.enc_pts_queue[i] = NULL;
980 }
981
982 return ret;
983}
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:553
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_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: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]
uint32_t data_len
ni_device_handle_t device_handle
ni_device_handle_t blk_io_handle
union _ni_session_data_io::@19 data