libxcoder 5.6.0
Loading...
Searching...
No Matches
ni_xcoder_scale.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_scale.c
24 *
25 * \brief Video scaling demo application directly using Netint Libxcoder API
26 ******************************************************************************/
27
28#include "ni_generic_utils.h"
29#include "ni_filter_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 scaling demo application directly using Netint Libxcoder version %s\n"
44 "Usage: ni_xcoder_scale [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 "-o | --output (Required) Output file path.\n"
51 " Can be specified multiple (max %d) times\n"
52 " to run multiple scaling instances simultaneously.\n"
53 "-l | --loglevel Set loglevel of this application and libxcoder API.\n"
54 " [none, fatal, error, info, debug, trace]\n"
55 " (Default: info)\n"
56 "-c | --card Set card index to use.\n"
57 " See `ni_rsrc_mon` for info of cards on system.\n"
58 " (Default: 0)\n"
59 "-r | --repeat To loop input X times. Must be a positive integer\n"
60 " (Default: 1)\n"
61 "-p | --pix_fmt Indicate the pixel format of the input.\n"
62 " [yuv420p, yuv420p10le, nv12, p010le, rgba, bgra, argb, abgr, bgr0]\n"
63 " (Default: yuv420p)\n"
64 "-s | --size (Required) Resolution of input file in format WIDTHxHEIGHT.\n"
65 " (eg. '1920x1080')\n"
66 "-f | --vf Video filter params. The only supported filter in this demo is:\n"
67 " ni_quadra_scale - supported params [width, height, format]\n"
68 " e.g. ni_quadra_scale=width=1280:height=720:format=yuv420p\n"
69 " (Default: \"\")\n"
71}
72
73int main(int argc, char *argv[])
74{
75 int i, ret = 0;
76 ni_demo_context_t ctx = {0};
77 char in_filename[FILE_NAME_LEN] = {0};
78 char out_filename[MAX_OUTPUT_FILES][FILE_NAME_LEN] = {0};
79 FILE *input_fp = NULL;
80 FILE *output_fp[MAX_OUTPUT_FILES] = {0};
81 int o_index = 0, f_index = 0;
82 int output_total = 0;
83 int input_width = 0, input_height = 0;
84 int bit_depth_factor[MAX_OUTPUT_FILES] = {0};
85 int output_width[MAX_OUTPUT_FILES] = {0};
86 int output_height[MAX_OUTPUT_FILES] = {0};
87 int chunk_size = 0, eos = 0;
88 void *yuv_buf = NULL;
89 ni_log_level_t log_level;
90 int xcoderGUID = 0;
91 char *n;
92 char filter_conf_params[MAX_OUTPUT_FILES][2048] = {0};
93 ni_session_context_t upl_ctx = {0};
96 ni_session_data_io_t sw_in_frame = {0};
97 ni_session_data_io_t hw_in_frame = {0};
98 ni_session_data_io_t scaled_frame[MAX_OUTPUT_FILES] = {0};
99 ni_session_data_io_t download_frame[MAX_OUTPUT_FILES] = {0};
100 niFrameSurface1_t *p_hwframe = NULL;
101 ni_scale_params_t scale_params[MAX_OUTPUT_FILES] = {0};
102 ni_drawbox_params_t drawbox_params = {0};
104
105 int opt;
106 int opt_index;
107 const char *opt_string = "hvi:o:l:c:r:p:s:f:";
108 static struct option long_options[] = {
109 {"help", no_argument, NULL, 'h'},
110 {"version", no_argument, NULL, 'v'},
111 {"input", required_argument, NULL, 'i'},
112 {"output", required_argument, NULL, 'o'},
113 {"loglevel", required_argument, NULL, 'l'},
114 {"card", required_argument, NULL, 'c'},
115 {"repeat", required_argument, NULL, 'r'},
116 {"pix_fmt", required_argument, NULL, 'p'},
117 {"size", required_argument, NULL, 's'},
118 {"vf", required_argument, NULL, 'f'},
119 {NULL, 0, NULL, 0},
120 };
121
122 while ((opt = getopt_long(argc, argv, opt_string, long_options, &opt_index)) != -1)
123 {
124 switch (opt)
125 {
126 case 'h':
127 print_usage();
128 ret = 0;
129 goto end;
130 case 'v':
132 ret = 0;
133 goto end;
134 case 'i':
135 ni_strcpy(in_filename, FILE_NAME_LEN, optarg);
136 break;
137 case 'o':
138 if (o_index == MAX_OUTPUT_FILES)
139 {
140 ni_log(NI_LOG_ERROR, "Error: number of output files cannot exceed %d\n", MAX_OUTPUT_FILES);
141 ret = -1;
142 goto end;
143 }
144
145 for (i = 0; i < o_index; i++)
146 {
147 if (0 == strcmp(out_filename[i], optarg))
148 {
149 ni_log(NI_LOG_ERROR, "Error: output file names must be unique: %s\n", optarg);
150 ret = -1;
151 goto end;
152 }
153 }
154
155 ni_strcpy(out_filename[o_index], FILE_NAME_LEN, optarg);
156 o_index++;
157 break;
158 case 'l':
159 log_level = arg_to_ni_log_level(optarg);
160 if (log_level != NI_LOG_INVALID)
161 {
162 ni_log_set_level(log_level);
163 }
164 else
165 {
166 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -l | --loglevel option\n"
167 "Must be one of [none, fatal, error, info, debug, trace]\n", optarg);
168 ret = -1;
169 goto end;
170 }
171 break;
172 case 'c':
173 xcoderGUID = (int)strtol(optarg, &n, 10);
174 if (n == optarg || *n != '\0' || xcoderGUID < 0)
175 {
176 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -c | --card option\n"
177 "Must be a non-negative integer\n", optarg);
178 ret = -1;
179 goto end;
180 }
181 break;
182 case 'r':
183 ctx.loops_left = strtol(optarg, &n, 10);
184 if (n == optarg || *n != '\0' || !(ctx.loops_left >= 1))
185 {
186 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -r | --repeat option\n"
187 "Must be a positive integer\n", optarg);
188 ret = -1;
189 goto end;
190 }
191 break;
192 case 'p':
194 if (pix_fmt == NI_PIX_FMT_NONE)
195 {
196 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -p | --pix_fmt option\n"
197 "Must be one of [yuv420p, yuv420p10le, nv12, p010le, rgba, gbra, argb, abgr, bgr0, yuv444p]\n",
198 optarg);
199 ret = -1;
200 goto end;
201 }
202 break;
203 case 's':
204 input_width = (int)strtol(optarg, &n, 10);
205 input_height = atoi(n + 1);
206 if ((*n != 'x') || (input_width <= 0 || input_height <= 0))
207 {
208 ni_log(NI_LOG_ERROR, "Error: Invalid value \"%s\" for -s | --size option\n"
209 "Must be in format [WIDTHxHEIGHT] (e.g. 1920x1080)\n", optarg);
210 ret = -1;
211 goto end;
212 }
213 break;
214 case 'f':
215 if (f_index == MAX_OUTPUT_FILES)
216 {
217 ni_log(NI_LOG_ERROR, "Error: number of filter config cannot exceed %d\n", MAX_OUTPUT_FILES);
218 ret = -1;
219 goto end;
220 }
221 ni_strcpy(filter_conf_params[f_index], sizeof(filter_conf_params[f_index]), optarg);
222 f_index++;
223 break;
224 default:
225 print_usage();
226 ret = -1;
227 goto end;
228 }
229 }
230
231 if (!in_filename[0]) {
232 ni_log(NI_LOG_ERROR, "Error: Missing input file argument (-i | --input)\n");
233 ret = -1;
234 goto end;
235 }
236
237 if (o_index == 0) {
238 ni_log(NI_LOG_ERROR, "Error: Missing output file argument (-o | --output)\n");
239 ret = -1;
240 goto end;
241 }
242
243 if (ni_posix_memalign(&yuv_buf, sysconf(_SC_PAGESIZE), MAX_YUV_FRAME_SIZE))
244 {
245 ni_log(NI_LOG_ERROR, "Error: failed to allocate YUV data buffer\n");
246 ret = -1;
247 goto end;
248 }
249
250 ni_fopen(&(input_fp), in_filename, "rb");
251 if (!input_fp)
252 {
253 ni_log(NI_LOG_ERROR, "Error: Failed to open input %s\n", in_filename);
254 ret = -1;
255 goto end;
256 }
257 ni_log(NI_LOG_INFO, "Opened input file: %s\n", in_filename);
258
259 ctx.total_file_size = get_total_file_size(input_fp);
260 ctx.curr_file_offset = 0;
261
262 output_total = o_index;
263 for (i = 0; i < output_total; i++)
264 {
265 if (strcmp(out_filename[i], "null") != 0 &&
266 strcmp(out_filename[i], "/dev/null") != 0)
267 {
268 output_fp[i] = NULL;
269 ni_fopen(&(output_fp[i]), out_filename[i], "wb");
270 if (!output_fp[i])
271 {
272 ni_log(NI_LOG_ERROR, "Error: Failed to open %s\n", out_filename[i]);
273 ret = -1;
274 goto end;
275 }
276 ni_log(NI_LOG_INFO, "Opened output file: %s\n", out_filename[i]);
277 } else
278 {
279 output_fp[i] = NULL;
280 ni_log(NI_LOG_INFO, "Note: Requested NULL output for index %d, no output file will be generated\n", i);
281 }
282 }
283
284 for (i = 0; i < output_total; i++)
285 {
286 output_width[i] = input_width;
287 output_height[i] = input_height;
288 }
289
290 if (ni_device_session_context_init(&upl_ctx) < 0)
291 {
292 ni_log(NI_LOG_ERROR, "Error: init uploader context error\n");
293 ret = -1;
294 goto end;
295 }
296
297 for (i = 0; i < output_total; i++)
298 {
299 if (filter_conf_params[i][0])
300 {
301 ret = retrieve_filter_params(filter_conf_params[i], &scale_params[i], &drawbox_params);
302 if (ret)
303 {
304 ni_log(NI_LOG_ERROR, "Error: failed to parse filter parameters: %s\n", filter_conf_params[i]);
305 goto end;
306 }
307 }
308
309 if (!scale_params[i].enabled)
310 {
311 ni_log(NI_LOG_ERROR, "Error: scale parameters not set for output #%d\n", i);
312 ret = -1;
313 goto end;
314 }
315 else
316 {
317 ret = ni_device_session_context_init(&sca_ctx[i]);
318 if (ret)
319 {
320 ni_log(NI_LOG_ERROR, "Error: Failed to init scale context\n");
321 goto end;
322 }
323 output_width[i] = scale_params[i].width;
324 output_height[i] = scale_params[i].height;
325 bit_depth_factor[i] = 1;
326 if (scale_params[i].format == GC620_I010 || scale_params[i].format == GC620_P010_MSB)
327 {
328 bit_depth_factor[i] = 2;
329 }
330 }
331 }
332
333 ret = uploader_open_session(&upl_ctx, xcoderGUID, input_width < NI_MIN_WIDTH ? NI_MIN_WIDTH : input_width,
334 input_height < NI_MIN_HEIGHT ? NI_MIN_HEIGHT : input_height, pix_fmt, 0, 3);
335 if (ret != 0)
336 {
337 ni_log(NI_LOG_ERROR, "Error: Failed to open uploader session\n");
338 goto end;
339 }
340
341 for (i = 0; i < output_total; i++)
342 {
343 ni_log(NI_LOG_INFO, "(Task %d) Starting to scale: video resolution %dx%d -> %dx%d\n",
344 i, input_width, input_height, output_width[i], output_height[i]);
345 }
346
349
350 while (!eos)
351 {
352 chunk_size = read_yuv_from_file(&ctx, input_fp, yuv_buf, input_width, input_height, pix_fmt,
354 if (chunk_size < 0)
355 {
356 ni_log(NI_LOG_ERROR, "Error: read yuv file error\n");
357 ret = -1;
358 break;
359 }
360
361 ret = upload_send_data_get_desc(&ctx, &upl_ctx, &sw_in_frame, &hw_in_frame, input_width,
362 input_height, eos ? NULL : yuv_buf);
363 if (ret)
364 {
365 ni_log(NI_LOG_ERROR, "Error: upload frame error\n");
366 break;
367 }
368
369 p_hwframe = (niFrameSurface1_t *)hw_in_frame.data.frame.p_data[3];
370 ni_hw_frame_ref(p_hwframe);
371 for (i = 0; i < output_total; i++)
372 {
373 scale_filter(&sca_ctx[i], &hw_in_frame.data.frame, &scaled_frame[i], xcoderGUID,
374 scale_params[i].width, scale_params[i].height,
375 ni_to_gc620_pix_fmt(pix_fmt), scale_params[i].format);
376 if (ret)
377 {
378 ni_log(NI_LOG_ERROR, "Error: Failed to run scale filter\n");
379 goto end;
380 }
381
382 p_hwframe = (niFrameSurface1_t *)scaled_frame[i].data.frame.p_data[3];
383 p_hwframe->ui16width = scale_params[i].width;
384 p_hwframe->ui16height = scale_params[i].height;
385 p_hwframe->bit_depth = bit_depth_factor[i];
386 if (scale_params[i].format == GC620_I420 || scale_params[i].format == GC620_I010)
388 else
390 ni_hw_frame_ref(p_hwframe);
391 ret = hwdl_frame(&sca_ctx[i], &download_frame[i], &scaled_frame[i].data.frame,
392 gc620_to_ni_pix_fmt(scale_params[i].format));
393 if (ret <= 0)
394 {
395 ni_log(NI_LOG_ERROR, "Error: Failed to download output frame\n");
396 goto end;
397 }
399
400 ret = write_rawvideo_data(output_fp[i], (output_width[i] * bit_depth_factor[i] + 127) / 128 * 128,
401 (output_height[i] + 1) / 2 * 2, output_width[i], output_height[i],
402 gc620_to_ni_pix_fmt(scale_params[i].format), &download_frame[i].data.frame);
403 }
404 p_hwframe = (niFrameSurface1_t *)hw_in_frame.data.frame.p_data[3];
406
408 if (current_time - previous_time >= (uint64_t)1000000000)
409 {
410 ni_log(NI_LOG_INFO, "Scaler stats: processed %u frames, fps %.2f\n",
412 (float)ctx.num_frames_received / (float)(current_time - ctx.start_time) * (float)1000000000);
414 }
415 }
416
418 for (i = 0; i < output_total; i++)
419 {
421 }
422
423end:
424 ni_frame_buffer_free(&sw_in_frame.data.frame);
425 ni_frame_buffer_free(&hw_in_frame.data.frame);
426 for (i = 0; i < output_total; i++)
427 {
428 ni_frame_buffer_free(&scaled_frame[i].data.frame);
429 ni_frame_buffer_free(&download_frame[i].data.frame);
430 }
431
433 for (i = 0; i < output_total; i++)
434 {
436 if (output_fp[i] != NULL)
437 {
438 fclose(output_fp[i]);
439 }
440 }
441
442 if (input_fp)
443 {
444 fclose(input_fp);
445 }
446
447 ni_aligned_free(yuv_buf);
448
449 for(i = 0; i < MAX_OUTPUT_FILES; ++i)
450 {
451 free(ctx.enc_pts_queue[i]);
452 ctx.enc_pts_queue[i] = NULL;
453 }
454
455 return ret;
456}
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_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_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 GC620_I420
#define NI_MIN_WIDTH
@ NI_PIXEL_PLANAR_FORMAT_SEMIPLANAR
@ NI_PIXEL_PLANAR_FORMAT_PLANAR
#define GC620_P010_MSB
ni_pix_fmt_t
@ NI_PIX_FMT_YUV420P
@ NI_PIX_FMT_NONE
#define GC620_I010
int scale_filter(ni_session_context_t *p_ctx, ni_frame_t *p_frame_in, ni_session_data_io_t *p_data_out, int iXcoderGUID, int scale_width, int scale_height, int in_format, int out_format)
Do a scale and/or format-change operation.
int retrieve_filter_params(char filter_params[], ni_scale_params_t *scale_params, ni_drawbox_params_t *drawbox_params)
ni_pix_fmt_t gc620_to_ni_pix_fmt(int 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)
void ni_hw_frame_unref(uint16_t hwframe_index)
int ni_to_gc620_pix_fmt(ni_pix_fmt_t pix_fmt)
uint64_t get_total_file_size(FILE *fp)
int upload_send_data_get_desc(ni_demo_context_t *p_ctx, ni_session_context_t *p_upl_ctx, ni_session_data_io_t *p_swin_data, ni_session_data_io_t *p_in_data, int input_video_width, int input_video_height, void *yuv_buf)
Read from input file, upload to encoder, retrieve HW descriptor.
int hwdl_frame(ni_session_context_t *p_ctx, ni_session_data_io_t *p_session_data, ni_frame_t *p_src_frame, int output_format)
Download hw frames by HwDesc.
void print_version(void)
int write_rawvideo_data(FILE *p_file, int input_aligned_width, int input_aligned_height, int output_width, int output_height, int format, ni_frame_t *p_out_frame)
Write hwdl data to files.
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)
#define MAX_YUV_FRAME_SIZE
#define MAX_OUTPUT_FILES
@ NI_SW_PIX_FMT_NONE
#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_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
uint64_t ni_gettime_ns(void)
Definition ni_util.c:2622
Utility definitions.
#define ni_aligned_free(p_memptr)
Definition ni_util.h:400
ni_pts_queue * enc_pts_queue[MAX_OUTPUT_FILES]
uint8_t * p_data[NI_MAX_NUM_DATA_POINTERS]
ni_session_run_state_t session_run_state
union _ni_session_data_io::@19 data