FFmpeg
Loading...
Searching...
No Matches
dnn_backend_onnx.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026 Advanced Micro Devices, Inc.
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/**
22 * @file
23 * DNN ONNX Runtime backend implementation.
24 */
25
26#include "libavutil/opt.h"
27#include "libavutil/avassert.h"
28#include "libavutil/imgutils.h"
29#include "libavutil/mem.h"
30#include "libavutil/avstring.h"
31#include "libavutil/thread.h"
33#include "../filters.h"
34#include "dnn_io_proc.h"
35#include "dnn_backend_common.h"
36#include "queue.h"
37#include "safe_queue.h"
38#include <onnxruntime_c_api.h>
39#include <inttypes.h>
40#include <stdio.h>
41#include <string.h>
42
57
58typedef struct ONNXInferRequest {
59 OrtValue *input_tensor;
60 OrtValue **output_tensors;
61 uint32_t nb_outputs;
64
70
71#define OFFSET(x) offsetof(ONNXOptions, x)
72#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
73static const AVOption dnn_onnx_options[] = {
74 { "threads_per_operation", "number of CPU threads per ORT operator (device=cpu only)",
75 OFFSET(num_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
76 { NULL }
77};
78
80
81static const OrtApi *g_ort = NULL;
83
84static void init_ort_api(void)
85{
86 g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
87}
88
89#define ORT_ABORT_ON_ERROR(expr) \
90 do { \
91 OrtStatus *status = (expr); \
92 if (status != NULL) { \
93 const char *msg = g_ort->GetErrorMessage(status); \
94 av_log(ctx, AV_LOG_ERROR, "ONNX Runtime error: %s\n", msg); \
95 g_ort->ReleaseStatus(status); \
96 goto err; \
97 } \
98 } while (0)
99
100static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
101{
102 ONNXModel *onnx_model = (ONNXModel *)task->model;
103 DnnContext *ctx = onnx_model->ctx;
104 LastLevelTaskItem *lltask = av_malloc(sizeof(*lltask));
105
106 if (!lltask) {
107 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for LastLevelTaskItem\n");
108 return AVERROR(ENOMEM);
109 }
110 task->inference_todo = 1;
111 task->inference_done = 0;
112 lltask->task = task;
113 if (ff_queue_push_back(lltask_queue, lltask) < 0) {
114 av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n");
115 av_freep(&lltask);
116 return AVERROR(ENOMEM);
117 }
118 return 0;
119}
120
122{
123 if (!request)
124 return;
125 if (request->input_tensor) {
126 g_ort->ReleaseValue(request->input_tensor);
127 request->input_tensor = NULL;
128 }
129 av_freep(&request->input_data);
130 if (request->output_tensors) {
131 for (uint32_t i = 0; i < request->nb_outputs; i++) {
132 if (request->output_tensors[i]) {
133 g_ort->ReleaseValue(request->output_tensors[i]);
134 request->output_tensors[i] = NULL;
135 }
136 }
137 av_freep(&request->output_tensors);
138 }
139 request->nb_outputs = 0;
140}
141
143{
144 ONNXRequestItem *item;
145 if (!arg || !*arg)
146 return;
147 item = *arg;
149 av_freep(&item->infer_request);
150 av_freep(&item->lltask);
152 av_freep(arg);
153}
154
155static void dnn_free_model_onnx(DNNModel **model)
156{
157 ONNXModel *onnx_model;
158 if (!model || !*model)
159 return;
160
161 onnx_model = (ONNXModel *)(*model);
162
163 ff_dnn_wait_requests(onnx_model->request_queue, onnx_model->ctx->nireq);
164 while (ff_safe_queue_size(onnx_model->request_queue) != 0) {
167 }
169
170 while (ff_queue_size(onnx_model->lltask_queue) != 0) {
172 av_freep(&item);
173 }
174 ff_queue_destroy(onnx_model->lltask_queue);
175
176 while (ff_queue_size(onnx_model->task_queue) != 0) {
177 TaskItem *item = (TaskItem *)ff_queue_pop_front(onnx_model->task_queue);
178 av_frame_free(&item->in_frame);
179 av_frame_free(&item->out_frame);
180 av_freep(&item);
181 }
182 ff_queue_destroy(onnx_model->task_queue);
183
184 if (onnx_model->session)
185 g_ort->ReleaseSession(onnx_model->session);
186 if (onnx_model->session_options)
187 g_ort->ReleaseSessionOptions(onnx_model->session_options);
188 if (onnx_model->env)
189 g_ort->ReleaseEnv(onnx_model->env);
190
191 av_freep(&onnx_model);
192 *model = NULL;
193}
194
195static int get_input_onnx(DNNModel *model, DNNData *input, const char *input_name)
196{
197 ONNXModel *onnx_model = (ONNXModel *)model;
198 DnnContext *ctx = onnx_model->ctx;
199 OrtTypeInfo *type_info = NULL;
200 const OrtTensorTypeAndShapeInfo *tensor_info = NULL;
201 size_t num_dims;
202 size_t input_count = 0;
203 size_t input_index = 0;
204 int found_input = 0;
205 int64_t *dims;
206 ONNXTensorElementDataType tensor_type;
207 OrtStatus *status;
208
209 if (!input_name || !*input_name) {
210 av_log(ctx, AV_LOG_ERROR, "ONNX input name is not specified\n");
211 return AVERROR(EINVAL);
212 }
213
214 if (onnx_model->input_resolved) {
215 *input = onnx_model->input_info;
216 return 0;
217 }
218
219 status = g_ort->SessionGetInputCount(onnx_model->session, &input_count);
220 if (status != NULL) {
221 const char *msg = g_ort->GetErrorMessage(status);
222 av_log(ctx, AV_LOG_ERROR, "Failed to get input count: %s\n", msg);
223 g_ort->ReleaseStatus(status);
224 return AVERROR(EINVAL);
225 }
226
227 for (size_t i = 0; i < input_count; i++) {
228 char *name = NULL;
229 status = g_ort->SessionGetInputName(onnx_model->session, i,
230 onnx_model->allocator, &name);
231 if (status != NULL) {
232 g_ort->ReleaseStatus(status);
233 continue;
234 }
235 if (!strcmp(name, input_name)) {
236 input_index = i;
237 found_input = 1;
238 }
239 onnx_model->allocator->Free(onnx_model->allocator, name);
240 if (found_input)
241 break;
242 }
243
244 if (!found_input) {
245 av_log(ctx, AV_LOG_ERROR, "Input name '%s' not found in ONNX model\n",
246 input_name);
247 return AVERROR(EINVAL);
248 }
249
250 status = g_ort->SessionGetInputTypeInfo(onnx_model->session, input_index,
251 &type_info);
252 if (status != NULL) {
253 const char *msg = g_ort->GetErrorMessage(status);
254 av_log(ctx, AV_LOG_ERROR, "Failed to get input type info: %s\n", msg);
255 g_ort->ReleaseStatus(status);
256 return AVERROR(EINVAL);
257 }
258
259 status = g_ort->CastTypeInfoToTensorInfo(type_info, &tensor_info);
260 if (status != NULL) {
261 g_ort->ReleaseTypeInfo(type_info);
262 g_ort->ReleaseStatus(status);
263 return AVERROR(EINVAL);
264 }
265
266 status = g_ort->GetDimensionsCount(tensor_info, &num_dims);
267 if (status != NULL) {
268 g_ort->ReleaseTypeInfo(type_info);
269 g_ort->ReleaseStatus(status);
270 return AVERROR(EINVAL);
271 }
272
273 if (num_dims != 4) {
274 avpriv_report_missing_feature(ctx, "Support for %zu dimensional input", num_dims);
275 g_ort->ReleaseTypeInfo(type_info);
276 return AVERROR(ENOSYS);
277 }
278
279 dims = av_malloc(num_dims * sizeof(int64_t));
280 if (!dims) {
281 g_ort->ReleaseTypeInfo(type_info);
282 return AVERROR(ENOMEM);
283 }
284
285 g_ort->GetDimensions(tensor_info, dims, num_dims);
286 g_ort->GetTensorElementType(tensor_info, &tensor_type);
287
288 if (dims[0] > 1) {
290 "ONNX model has fixed batch size %"PRId64", but the backend "
291 "only supports a batch size of 1\n", dims[0]);
292 av_free(dims);
293 g_ort->ReleaseTypeInfo(type_info);
294 return AVERROR(ENOSYS);
295 }
296
297 for (size_t i = 1; i < num_dims; i++) {
298 if (dims[i] > INT_MAX) {
300 "ONNX model input dimension %zu (%"PRId64") is too large to be represented\n",
301 i, dims[i]);
302 av_free(dims);
303 g_ort->ReleaseTypeInfo(type_info);
304 return AVERROR(ENOSYS);
305 }
306 }
307
308 /*
309 * The ONNX backend assumes a 4-D NCHW input tensor (the rank check
310 * above already rejects anything else).
311 */
312 input->layout = DL_NCHW;
313 input->dims[0] = dims[0] > 0 ? dims[0] : 1;
314 input->dims[1] = dims[1] > 0 ? dims[1] : 3;
315 input->dims[2] = dims[2] > 0 ? dims[2] : -1;
316 input->dims[3] = dims[3] > 0 ? dims[3] : -1;
317
318 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
319 input->dt = DNN_FLOAT;
320 } else {
321 av_log(ctx, AV_LOG_ERROR, "Unsupported input tensor data type, only float is supported\n");
322 av_free(dims);
323 g_ort->ReleaseTypeInfo(type_info);
324 return AVERROR(ENOSYS);
325 }
326
327 /*
328 * The DCO_RGB setting below is only consulted by the dnn_detect and dnn_classify;
329 * the dnn_processing path lets the source AVFrame pixel format determine the
330 * tensor channel order, so both RGB24 and BGR24 inputs work transparently
331 * for that flow.
332 */
333 input->order = DCO_RGB;
334 av_free(dims);
335 g_ort->ReleaseTypeInfo(type_info);
336
337 onnx_model->input_info = *input;
338 onnx_model->input_resolved = 1;
339 return 0;
340}
341
342static int fill_model_input_onnx(ONNXModel *onnx_model, ONNXRequestItem *request)
343{
344 LastLevelTaskItem *lltask = NULL;
345 TaskItem *task = NULL;
346 ONNXInferRequest *infer_request = NULL;
347 DNNData input = { 0 };
348 DnnContext *ctx = onnx_model->ctx;
349 int ret, width_idx, height_idx, channel_idx;
350 int64_t input_shape[4];
351 size_t input_tensor_size;
352 OrtMemoryInfo *memory_info;
353 OrtStatus *status;
354
355 lltask = (LastLevelTaskItem *)ff_queue_pop_front(onnx_model->lltask_queue);
356 if (!lltask) {
357 ret = AVERROR(EINVAL);
358 goto err;
359 }
360 request->lltask = lltask;
361 task = lltask->task;
362 infer_request = request->infer_request;
363
364 ret = get_input_onnx(&onnx_model->model, &input, task->input_name);
365 if (ret != 0) {
366 goto err;
367 }
368
369 width_idx = dnn_get_width_idx_by_layout(input.layout);
370 height_idx = dnn_get_height_idx_by_layout(input.layout);
371 channel_idx = dnn_get_channel_idx_by_layout(input.layout);
372
373 if (input.dims[height_idx] < 0)
374 input.dims[height_idx] = task->in_frame->height;
375 if (input.dims[width_idx] < 0)
376 input.dims[width_idx] = task->in_frame->width;
377
378 if (input.dims[0] <= 0 || input.dims[channel_idx] <= 0 ||
379 input.dims[height_idx] <= 0 || input.dims[width_idx] <= 0) {
380 av_log(ctx, AV_LOG_ERROR, "ONNX input tensor has a non-positive dimension\n");
381 ret = AVERROR(EINVAL);
382 goto err;
383 }
384
385 ret = av_image_check_size((unsigned)input.dims[width_idx],
386 (unsigned)input.dims[height_idx], 0, ctx);
387 if (ret < 0) {
388 av_log(ctx, AV_LOG_ERROR, "ONNX input image dimensions %dx%d are not supported\n",
389 input.dims[width_idx], input.dims[height_idx]);
390 goto err;
391 }
392
393 input_shape[0] = input.dims[0];
394 input_shape[1] = input.dims[channel_idx];
395 input_shape[2] = input.dims[height_idx];
396 input_shape[3] = input.dims[width_idx];
397
398 /*
399 * Build the byte count with checked size_t multiplications instead of
400 * multiplying four int64_t shape values in one expression.
401 */
402 input_tensor_size = sizeof(float);
403 if (av_size_mult(input_tensor_size, (size_t)input_shape[0], &input_tensor_size) < 0 ||
404 av_size_mult(input_tensor_size, (size_t)input_shape[1], &input_tensor_size) < 0 ||
405 av_size_mult(input_tensor_size, (size_t)input_shape[2], &input_tensor_size) < 0 ||
406 av_size_mult(input_tensor_size, (size_t)input_shape[3], &input_tensor_size) < 0) {
407 av_log(ctx, AV_LOG_ERROR, "ONNX input tensor size overflows\n");
408 ret = AVERROR(EINVAL);
409 goto err;
410 }
411
412 input.data = av_malloc(input_tensor_size);
413 if (!input.data) {
414 ret = AVERROR(ENOMEM);
415 goto err;
416 }
417 infer_request->input_data = input.data;
418
419 switch (onnx_model->model.func_type) {
421 input.scale = 255;
422 if (task->do_ioproc) {
423 if (onnx_model->model.frame_pre_proc != NULL) {
424 ret = onnx_model->model.frame_pre_proc(task->in_frame, &input,
425 onnx_model->model.filter_ctx);
426 } else {
427 ret = ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
428 }
429 if (ret < 0)
430 goto err;
431 }
432 break;
434 ret = ff_frame_to_dnn_detect(task->in_frame, &input, ctx);
435 if (ret < 0)
436 goto err;
437 break;
438 default:
439 avpriv_report_missing_feature(ctx, "model function type %d", onnx_model->model.func_type);
440 ret = AVERROR(ENOSYS);
441 goto err;
442 }
443
444 status = g_ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info);
445 if (status != NULL) {
446 ret = AVERROR(ENOMEM);
447 goto err;
448 }
449
450 status = g_ort->CreateTensorWithDataAsOrtValue(
451 memory_info, input.data, input_tensor_size,
452 input_shape, 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,
453 &infer_request->input_tensor);
454
455 g_ort->ReleaseMemoryInfo(memory_info);
456
457 if (status != NULL) {
458 const char *msg = g_ort->GetErrorMessage(status);
459 av_log(ctx, AV_LOG_ERROR, "Failed to create input tensor: %s\n", msg);
460 g_ort->ReleaseStatus(status);
461 ret = AVERROR(ENOMEM);
462 goto err;
463 }
464
465 return 0;
466
467err:
468 onnx_free_request(infer_request);
469 return ret;
470}
471
472static int onnx_start_inference(void *args)
473{
474 ONNXRequestItem *request = (ONNXRequestItem *)args;
475 ONNXInferRequest *infer_request = NULL;
476 LastLevelTaskItem *lltask = NULL;
477 TaskItem *task = NULL;
478 ONNXModel *onnx_model = NULL;
480 OrtStatus *status;
481 const char *input_names[1];
482 int ret = DNN_GENERIC_ERROR;
483
484 if (!request) {
485 av_log(NULL, AV_LOG_ERROR, "ONNXRequestItem is NULL\n");
486 return AVERROR(EINVAL);
487 }
488
489 infer_request = request->infer_request;
490 lltask = request->lltask;
491 task = lltask->task;
492 onnx_model = (ONNXModel *)task->model;
493 ctx = onnx_model->ctx;
494
495 if (!task->input_name || !task->output_names || !task->output_names[0]) {
497 "ONNX backend: input/output tensor name was not resolved at load time\n");
498 return AVERROR(EINVAL);
499 }
500
501 if (!infer_request->input_tensor) {
502 av_log(ctx, AV_LOG_ERROR, "Input tensor is NULL\n");
503 return DNN_GENERIC_ERROR;
504 }
505
506 if (!onnx_model->output_resolved) {
507 size_t output_count = 0;
508 int found_output = 0;
509
510 status = g_ort->SessionGetOutputCount(onnx_model->session, &output_count);
511 if (status != NULL) {
512 const char *msg = g_ort->GetErrorMessage(status);
513 av_log(ctx, AV_LOG_ERROR, "Failed to get output count: %s\n", msg);
514 g_ort->ReleaseStatus(status);
515 return AVERROR(EINVAL);
516 }
517
518 for (uint32_t req = 0; req < task->nb_output; req++) {
519 found_output = 0;
520 for (size_t i = 0; i < output_count; i++) {
521 char *name = NULL;
522 status = g_ort->SessionGetOutputName(onnx_model->session, i,
523 onnx_model->allocator, &name);
524 if (status != NULL) {
525 g_ort->ReleaseStatus(status);
526 continue;
527 }
528 if (!strcmp(name, task->output_names[req]))
529 found_output = 1;
530 onnx_model->allocator->Free(onnx_model->allocator, name);
531 if (found_output)
532 break;
533 }
534 if (!found_output) {
536 "Output name '%s' not found in ONNX model\n",
537 task->output_names[req]);
538 return AVERROR(EINVAL);
539 }
540 }
541
542 onnx_model->output_resolved = 1;
543 }
544
545 input_names[0] = task->input_name;
546
547 /* ORT writes task->nb_output result handles into this array; it must be
548 * allocated (and NULL-initialised) before Run() so ORT owns each slot. */
549 av_freep(&infer_request->output_tensors);
550 infer_request->output_tensors = av_calloc(task->nb_output,
551 sizeof(*infer_request->output_tensors));
552 if (!infer_request->output_tensors) {
553 infer_request->nb_outputs = 0;
554 return AVERROR(ENOMEM);
555 }
556 infer_request->nb_outputs = task->nb_output;
557
558 status = g_ort->Run(onnx_model->session, NULL,
559 input_names, (const OrtValue *const *)&infer_request->input_tensor, 1,
560 task->output_names, task->nb_output, infer_request->output_tensors);
561
562 if (status != NULL) {
563 const char *msg = g_ort->GetErrorMessage(status);
564 av_log(ctx, AV_LOG_ERROR, "ONNX inference failed: %s\n", msg);
565 g_ort->ReleaseStatus(status);
566 goto err;
567 }
568
569 return 0;
570
571err:
572 av_freep(&infer_request->output_tensors);
573 infer_request->nb_outputs = 0;
574 return ret;
575}
576
577static void infer_completion_callback(void *args)
578{
579 ONNXRequestItem *request = (ONNXRequestItem *)args;
580 LastLevelTaskItem *lltask = request->lltask;
581 TaskItem *task = lltask->task;
583 ONNXInferRequest *infer_request = request->infer_request;
584 ONNXModel *onnx_model = (ONNXModel *)task->model;
585 DnnContext *ctx = onnx_model->ctx;
586 OrtTensorTypeAndShapeInfo *tensor_info;
587 ONNXTensorElementDataType tensor_type;
588 size_t num_dims;
589 int64_t *dims;
590 OrtStatus *status;
591 int ret;
592
593 outputs = av_calloc(infer_request->nb_outputs, sizeof(*outputs));
594 if (!outputs) {
595 av_log(ctx, AV_LOG_ERROR, "Failed to allocate output DNNData array\n");
596 goto err;
597 }
598
599 for (uint32_t i = 0; i < infer_request->nb_outputs; i++) {
600 status = g_ort->GetTensorTypeAndShape(infer_request->output_tensors[i],
601 &tensor_info);
602 if (status != NULL) {
603 av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] type/shape\n", i);
604 g_ort->ReleaseStatus(status);
605 goto err;
606 }
607
608 status = g_ort->GetDimensionsCount(tensor_info, &num_dims);
609 if (status != NULL) {
610 av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] dimension count\n", i);
611 g_ort->ReleaseStatus(status);
612 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
613 goto err;
614 }
615
616 dims = av_malloc(num_dims * sizeof(int64_t));
617 if (!dims) {
618 av_log(ctx, AV_LOG_ERROR, "Failed to allocate dims array\n");
619 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
620 goto err;
621 }
622
623 status = g_ort->GetDimensions(tensor_info, dims, num_dims);
624 if (status != NULL) {
625 av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] dimensions\n", i);
626 g_ort->ReleaseStatus(status);
627 av_free(dims);
628 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
629 goto err;
630 }
631
632 for (size_t d = 0; d < num_dims; d++) {
633 if (dims[d] < 0 || dims[d] > INT_MAX) {
635 "Output tensor[%u] dimension %zu (%"PRId64") is out of representable range\n",
636 i, d, dims[d]);
637 av_free(dims);
638 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
639 goto err;
640 }
641 }
642
643 status = g_ort->GetTensorElementType(tensor_info, &tensor_type);
644 if (status != NULL) {
645 av_log(ctx, AV_LOG_ERROR, "Failed to get output tensor[%u] element type\n", i);
646 g_ort->ReleaseStatus(status);
647 av_free(dims);
648 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
649 goto err;
650 }
651 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
652 outputs[i].dt = DNN_FLOAT;
653 } else {
655 "Unsupported output tensor[%u] data type, only float supported\n", i);
656 av_free(dims);
657 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
658 goto err;
659 }
660
661 /* Output is interpreted as NCHW, matching the input assumption. */
662 outputs[i].layout = DL_NCHW;
663 outputs[i].order = DCO_RGB;
664
665 if (num_dims == 4) {
666 outputs[i].dims[0] = dims[0];
667 outputs[i].dims[1] = dims[1];
668 outputs[i].dims[2] = dims[2];
669 outputs[i].dims[3] = dims[3];
670 } else if (num_dims == 3) {
671 /* Some detection models output [1, N, D]; promote it to [1, 1, N, D]. */
672 outputs[i].dims[0] = dims[0];
673 outputs[i].dims[1] = 1;
674 outputs[i].dims[2] = dims[1];
675 outputs[i].dims[3] = dims[2];
676 } else {
678 "Support for %zu-dimensional output (tensor[%u])", num_dims, i);
679 av_free(dims);
680 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
681 goto err;
682 }
683
684 status = g_ort->GetTensorMutableData(infer_request->output_tensors[i], &outputs[i].data);
685 if (status != NULL) {
686 av_log(ctx, AV_LOG_ERROR, "Failed to get tensor[%u] data pointer\n", i);
687 g_ort->ReleaseStatus(status);
688 av_free(dims);
689 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
690 goto err;
691 }
692
693 av_free(dims);
694 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
695 }
696
697 switch (onnx_model->model.func_type) {
699 if (task->do_ioproc) {
700 outputs[0].scale = 255;
701 if (onnx_model->model.frame_post_proc != NULL) {
702 onnx_model->model.frame_post_proc(task->out_frame, outputs, onnx_model->model.filter_ctx);
703 } else {
705 }
706 } else {
709 }
710 break;
712 ret = onnx_model->model.detect_post_proc(task->in_frame, outputs,
713 infer_request->nb_outputs,
714 onnx_model->model.filter_ctx);
715 if (ret < 0)
716 goto err;
717 break;
718 default:
719 avpriv_report_missing_feature(ctx, "model function type %d", onnx_model->model.func_type);
720 goto err;
721 }
722
723 task->inference_done++;
724
725err:
727 av_freep(&request->lltask);
728 onnx_free_request(infer_request);
729 if (ff_safe_queue_push_back(onnx_model->request_queue, request) < 0) {
730 destroy_request_item(&request);
731 av_log(ctx, AV_LOG_ERROR, "Unable to push back request_queue.\n");
732 }
733}
734
735static int execute_model_onnx(ONNXRequestItem *request, Queue *lltask_queue)
736{
737 ONNXModel *onnx_model = NULL;
738 LastLevelTaskItem *lltask;
739 TaskItem *task = NULL;
740 int ret = 0;
741
742 if (ff_queue_size(lltask_queue) == 0) {
743 destroy_request_item(&request);
744 return 0;
745 }
746
747 lltask = (LastLevelTaskItem *)ff_queue_peek_front(lltask_queue);
748 if (lltask == NULL) {
749 av_log(NULL, AV_LOG_ERROR, "Failed to get LastLevelTaskItem\n");
750 destroy_request_item(&request);
751 return AVERROR(EINVAL);
752 }
753 task = lltask->task;
754 onnx_model = (ONNXModel *)task->model;
755
756 ret = fill_model_input_onnx(onnx_model, request);
757 if (ret != 0) {
758 goto err;
759 }
760
761 if (task->async) {
762 avpriv_report_missing_feature(onnx_model->ctx, "ONNX async inference");
763 ret = AVERROR(ENOSYS);
764 goto err;
765 } else {
766 ret = onnx_start_inference((void *)request);
767 if (ret != 0) {
768 goto err;
769 }
771 return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
772 }
773
774err:
775 av_freep(&request->lltask);
777 if (ff_safe_queue_push_back(onnx_model->request_queue, request) < 0) {
778 destroy_request_item(&request);
779 }
780 return ret;
781}
782
783static int get_output_onnx(DNNModel *model, const char *input_name, int input_width, int input_height,
784 const char *output_name, int *output_width, int *output_height)
785{
786 int ret = 0;
787 ONNXModel *onnx_model = (ONNXModel *)model;
788 DnnContext *ctx = onnx_model->ctx;
789 TaskItem task = { 0 };
790 ONNXRequestItem *request = NULL;
791 DNNExecBaseParams exec_params = {
792 .input_name = input_name,
793 .output_names = &output_name,
794 .nb_output = 1,
795 .in_frame = NULL,
796 .out_frame = NULL,
797 };
798
799 ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, onnx_model, input_height, input_width, ctx);
800 if (ret != 0) {
801 goto err;
802 }
803
804 ret = extract_lltask_from_task(&task, onnx_model->lltask_queue);
805 if (ret != 0) {
806 av_log(ctx, AV_LOG_ERROR, "Unable to extract last level task from task.\n");
807 goto err;
808 }
809
810 request = (ONNXRequestItem *)ff_safe_queue_pop_front(onnx_model->request_queue);
811 if (!request) {
812 av_log(ctx, AV_LOG_ERROR, "Unable to get infer request.\n");
813 ret = AVERROR(EINVAL);
814 goto err;
815 }
816
817 ret = execute_model_onnx(request, onnx_model->lltask_queue);
818 *output_width = task.out_frame->width;
819 *output_height = task.out_frame->height;
820
821err:
823 av_frame_free(&task.in_frame);
824 return ret;
825}
826
828{
829 ONNXInferRequest *request = av_mallocz(sizeof(ONNXInferRequest));
830 if (!request)
831 return NULL;
832 return request;
833}
834
836{
837 DNNModel *model = NULL;
838 ONNXModel *onnx_model = NULL;
839 ONNXRequestItem *item = NULL;
840 ONNXOptions *options = &ctx->onnx_option;
841 OrtStatus *status;
842
844 if (!g_ort) {
845 av_log(ctx, AV_LOG_ERROR, "Failed to get ONNX Runtime API\n");
846 return NULL;
847 }
848
849 onnx_model = av_mallocz(sizeof(ONNXModel));
850 if (!onnx_model)
851 return NULL;
852
853 model = &onnx_model->model;
854 onnx_model->ctx = ctx;
855
856 status = g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "FFmpeg", &onnx_model->env);
857 if (status != NULL) {
858 av_log(ctx, AV_LOG_ERROR, "Failed to create ONNX Runtime environment\n");
859 goto fail;
860 }
861
862 status = g_ort->CreateSessionOptions(&onnx_model->session_options);
863 if (status != NULL) {
864 av_log(ctx, AV_LOG_ERROR, "Failed to create session options\n");
865 goto fail;
866 }
867
868 if (options->num_threads > 0 &&
869 (!ctx->device || av_strcasecmp(ctx->device, "cpu") == 0)) {
870 g_ort->SetIntraOpNumThreads(onnx_model->session_options, options->num_threads);
871 }
872 g_ort->SetSessionGraphOptimizationLevel(onnx_model->session_options, ORT_ENABLE_ALL);
873
874 if (ctx->device && av_strcasecmp(ctx->device, "cpu") != 0) {
875 if (av_strcasecmp(ctx->device, "cuda") == 0) {
876 if (g_ort->SessionOptionsAppendExecutionProvider_CUDA) {
877 OrtCUDAProviderOptions cuda_options;
878 memset(&cuda_options, 0, sizeof(cuda_options));
879 cuda_options.device_id = ctx->device_id;
880
881 status = g_ort->SessionOptionsAppendExecutionProvider_CUDA(
882 onnx_model->session_options, &cuda_options);
883 if (status != NULL) {
884 const char *msg = g_ort->GetErrorMessage(status);
885 av_log(ctx, AV_LOG_WARNING, "Failed to enable CUDA (device %d): %s. Falling back to CPU\n",
886 ctx->device_id, msg);
887 g_ort->ReleaseStatus(status);
888 } else {
889 av_log(ctx, AV_LOG_INFO, "Using CUDA execution provider on device %d\n", ctx->device_id);
890 }
891 } else {
892 av_log(ctx, AV_LOG_WARNING, "CUDA provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
893 }
894 } else if (av_strcasecmp(ctx->device, "dml") == 0) {
895#ifdef _WIN32
896 const char* dml_options_keys[] = {"device_id"};
897 const char* dml_options_values[] = {NULL};
898 char device_id_str[32];
899 snprintf(device_id_str, sizeof(device_id_str), "%d", ctx->device_id);
900 dml_options_values[0] = device_id_str;
901
902 /* DirectML cannot use ORT's memory-pattern optimizer and only
903 * supports sequential execution. */
904 status = g_ort->SetSessionExecutionMode(onnx_model->session_options, ORT_SEQUENTIAL);
905 if (status)
906 g_ort->ReleaseStatus(status);
907 status = g_ort->DisableMemPattern(onnx_model->session_options);
908 if (status)
909 g_ort->ReleaseStatus(status);
910
911 if (g_ort->SessionOptionsAppendExecutionProvider) {
912 status = g_ort->SessionOptionsAppendExecutionProvider(
913 onnx_model->session_options, "DML",
914 dml_options_keys, dml_options_values, 1);
915 if (status != NULL) {
916 const char *msg = g_ort->GetErrorMessage(status);
917 av_log(ctx, AV_LOG_WARNING, "Failed to enable DirectML (device %d): %s. Falling back to CPU\n",
918 ctx->device_id, msg);
919 g_ort->ReleaseStatus(status);
920 } else {
921 av_log(ctx, AV_LOG_INFO, "Using DirectML execution provider on device %d\n", ctx->device_id);
922 }
923 } else {
924 av_log(ctx, AV_LOG_WARNING, "DirectML provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
925 }
926#else
927 av_log(ctx, AV_LOG_WARNING, "DirectML is only available on Windows. Falling back to CPU\n");
928#endif
929 } else if (av_strcasecmp(ctx->device, "vitisai") == 0) {
930 if (g_ort->SessionOptionsAppendExecutionProvider) {
931 status = g_ort->SessionOptionsAppendExecutionProvider(
932 onnx_model->session_options, "VitisAI",
933 NULL, NULL, 0);
934 if (status != NULL) {
935 const char *msg = g_ort->GetErrorMessage(status);
937 "Failed to enable VitisAI EP: %s. Falling back to CPU\n", msg);
938 g_ort->ReleaseStatus(status);
939 } else {
940 av_log(ctx, AV_LOG_INFO, "Using VitisAI execution provider (AMD Ryzen AI NPU)\n");
941 }
942 } else {
944 "VitisAI provider function not available in this ONNX Runtime API version. Falling back to CPU.\n");
945 }
946 } else {
947#ifdef _WIN32
949 "Unknown device '%s'. Supported: cpu, cuda, dml, vitisai. Using CPU\n",
950 ctx->device);
951#else
953 "Unknown device '%s'. Supported: cpu, cuda, vitisai. Using CPU\n",
954 ctx->device);
955#endif
956 }
957 } else {
958 av_log(ctx, AV_LOG_INFO, "Using CPU execution provider\n");
959 }
960
961#ifdef _WIN32
962 {
963 wchar_t *wfilename = NULL;
964 if (utf8towchar(ctx->model_filename, &wfilename)) {
965 av_log(ctx, AV_LOG_ERROR, "Failed to convert model filename to UTF-16\n");
966 goto fail;
967 }
968 if (!wfilename) {
969 av_log(ctx, AV_LOG_ERROR, "Failed to convert model filename to UTF-16\n");
970 goto fail;
971 }
972
973 status = g_ort->CreateSession(onnx_model->env, wfilename,
974 onnx_model->session_options, &onnx_model->session);
975 av_free(wfilename);
976 }
977#else
978 status = g_ort->CreateSession(onnx_model->env, ctx->model_filename,
979 onnx_model->session_options, &onnx_model->session);
980#endif
981 if (status != NULL) {
982 const char *msg = g_ort->GetErrorMessage(status);
983 av_log(ctx, AV_LOG_ERROR, "Failed to create ONNX session: %s\n", msg);
984 g_ort->ReleaseStatus(status);
985 goto fail;
986 }
987
988 status = g_ort->GetAllocatorWithDefaultOptions(&onnx_model->allocator);
989 if (status != NULL) {
990 av_log(ctx, AV_LOG_ERROR, "Failed to get allocator\n");
991 goto fail;
992 }
993
994 /*
995 * The ONNX backend binds exactly one input tensor to Run(), so only
996 * single-input models are supported.
997 */
998 {
999 size_t input_count = 0;
1000 status = g_ort->SessionGetInputCount(onnx_model->session, &input_count);
1001 if (status != NULL) {
1002 const char *msg = g_ort->GetErrorMessage(status);
1003 av_log(ctx, AV_LOG_ERROR, "Failed to get model input count: %s\n", msg);
1004 g_ort->ReleaseStatus(status);
1005 goto fail;
1006 }
1007 if (input_count == 0) {
1008 av_log(ctx, AV_LOG_ERROR, "ONNX model exposes no input tensors\n");
1009 goto fail;
1010 }
1011 if (input_count > 1) {
1013 "ONNX model exposes %zu input tensors; the ONNX backend "
1014 "supports single-input models only.\n",
1015 input_count);
1016 goto fail;
1017 }
1018 }
1019
1020 /* Auto-detect the input tensor name when the user did not pass input=NAME. */
1021 if (!ctx->model_inputname || !*ctx->model_inputname) {
1022 char *name = NULL;
1023 status = g_ort->SessionGetInputName(onnx_model->session, 0,
1024 onnx_model->allocator, &name);
1025 if (status != NULL) {
1026 const char *msg = g_ort->GetErrorMessage(status);
1027 av_log(ctx, AV_LOG_ERROR, "Failed to get model input name: %s\n", msg);
1028 g_ort->ReleaseStatus(status);
1029 goto fail;
1030 }
1031 av_freep(&ctx->model_inputname);
1032 ctx->model_inputname = av_strdup(name);
1033 onnx_model->allocator->Free(onnx_model->allocator, name);
1034 if (!ctx->model_inputname)
1035 goto fail;
1036 av_log(ctx, AV_LOG_INFO, "Auto-detected ONNX input tensor '%s'\n",
1037 ctx->model_inputname);
1038 }
1039
1040 /* Auto-detect the output tensor name when the user did not pass output=NAME. */
1041 if (!ctx->model_outputnames) {
1042 size_t output_count = 0;
1043 char *name = NULL;
1044 status = g_ort->SessionGetOutputCount(onnx_model->session, &output_count);
1045 if (status != NULL) {
1046 const char *msg = g_ort->GetErrorMessage(status);
1047 av_log(ctx, AV_LOG_ERROR, "Failed to get model output count: %s\n", msg);
1048 g_ort->ReleaseStatus(status);
1049 goto fail;
1050 }
1051 if (output_count == 0) {
1052 av_log(ctx, AV_LOG_ERROR, "ONNX model exposes no output tensors\n");
1053 goto fail;
1054 }
1055 status = g_ort->SessionGetOutputName(onnx_model->session, 0,
1056 onnx_model->allocator, &name);
1057 if (status != NULL) {
1058 const char *msg = g_ort->GetErrorMessage(status);
1059 av_log(ctx, AV_LOG_ERROR, "Failed to get model output name: %s\n", msg);
1060 g_ort->ReleaseStatus(status);
1061 goto fail;
1062 }
1063 ctx->model_outputnames = av_calloc(1, sizeof(*ctx->model_outputnames));
1064 if (!ctx->model_outputnames) {
1065 onnx_model->allocator->Free(onnx_model->allocator, name);
1066 goto fail;
1067 }
1068 ctx->model_outputnames[0] = av_strdup(name);
1069 onnx_model->allocator->Free(onnx_model->allocator, name);
1070 if (!ctx->model_outputnames[0]) {
1071 av_freep(&ctx->model_outputnames);
1072 goto fail;
1073 }
1074 ctx->nb_outputs = 1;
1075 if (output_count == 1) {
1076 av_log(ctx, AV_LOG_INFO, "Auto-detected ONNX output tensor '%s'\n",
1077 ctx->model_outputnames[0]);
1078 } else {
1080 "ONNX model exposes %zu output tensors; auto-using index 0 ('%s'). "
1081 "Specify output=NAME to choose a different one.\n",
1082 output_count, ctx->model_outputnames[0]);
1083 }
1084 }
1085
1086 onnx_model->request_queue = ff_safe_queue_create();
1087 if (!onnx_model->request_queue) {
1088 goto fail;
1089 }
1090
1091 item = av_mallocz(sizeof(ONNXRequestItem));
1092 if (!item) {
1093 goto fail;
1094 }
1095 item->lltask = NULL;
1097 if (!item->infer_request) {
1098 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for ONNX inference request\n");
1099 goto fail;
1100 }
1103 item->exec_module.args = item;
1104
1105 if (ff_safe_queue_push_back(onnx_model->request_queue, item) < 0) {
1106 goto fail;
1107 }
1108 item = NULL;
1109
1110 onnx_model->task_queue = ff_queue_create();
1111 if (!onnx_model->task_queue) {
1112 goto fail;
1113 }
1114
1115 onnx_model->lltask_queue = ff_queue_create();
1116 if (!onnx_model->lltask_queue) {
1117 goto fail;
1118 }
1119
1120 model->get_input = &get_input_onnx;
1121 model->get_output = &get_output_onnx;
1122 model->filter_ctx = filter_ctx;
1123 model->func_type = func_type;
1124
1125 return model;
1126
1127fail:
1128 if (item) {
1129 destroy_request_item(&item);
1130 }
1131 dnn_free_model_onnx(&model);
1132 return NULL;
1133}
1134
1135static int dnn_execute_model_onnx(const DNNModel *model, DNNExecBaseParams *exec_params)
1136{
1137 ONNXModel *onnx_model = (ONNXModel *)model;
1138 DnnContext *ctx = onnx_model->ctx;
1139 TaskItem *task;
1140 ONNXRequestItem *request;
1141 int ret = 0;
1142
1143 ret = ff_check_exec_params(ctx, DNN_ONNX, model->func_type, exec_params);
1144 if (ret != 0) {
1145 av_log(ctx, AV_LOG_ERROR, "Exec parameter checking failed.\n");
1146 return ret;
1147 }
1148
1149 task = av_malloc(sizeof(TaskItem));
1150 if (!task) {
1151 av_log(ctx, AV_LOG_ERROR, "Unable to alloc memory for task item.\n");
1152 return AVERROR(ENOMEM);
1153 }
1154
1155 ret = ff_dnn_fill_task(task, exec_params, onnx_model, 0, 1);
1156 if (ret != 0) {
1157 av_freep(&task);
1158 av_log(ctx, AV_LOG_ERROR, "Unable to fill task.\n");
1159 return ret;
1160 }
1161
1162 ret = ff_queue_push_back(onnx_model->task_queue, task);
1163 if (ret < 0) {
1164 av_freep(&task);
1165 av_log(ctx, AV_LOG_ERROR, "Unable to push back task_queue.\n");
1166 return ret;
1167 }
1168
1169 ret = extract_lltask_from_task(task, onnx_model->lltask_queue);
1170 if (ret != 0) {
1171 av_log(ctx, AV_LOG_ERROR, "Unable to extract last level task from task.\n");
1172 return ret;
1173 }
1174
1175 request = (ONNXRequestItem *)ff_safe_queue_pop_front(onnx_model->request_queue);
1176 if (!request) {
1177 av_log(ctx, AV_LOG_ERROR, "Unable to get infer request.\n");
1178 return AVERROR(EINVAL);
1179 }
1180
1181 return execute_model_onnx(request, onnx_model->lltask_queue);
1182}
1183
1185{
1186 ONNXModel *onnx_model = (ONNXModel *)model;
1187 return ff_dnn_get_result_common(onnx_model->task_queue, in, out);
1188}
1189
1190static int dnn_flush_onnx(const DNNModel *model)
1191{
1192 ONNXModel *onnx_model = (ONNXModel *)model;
1193 ONNXRequestItem *request;
1194
1195 if (ff_queue_size(onnx_model->lltask_queue) == 0)
1196 return 0;
1197
1198 request = (ONNXRequestItem *)ff_safe_queue_pop_front(onnx_model->request_queue);
1199 if (!request) {
1200 av_log(onnx_model->ctx, AV_LOG_ERROR, "Unable to get infer request.\n");
1201 return AVERROR(EINVAL);
1202 }
1203
1204 return execute_model_onnx(request, onnx_model->lltask_queue);
1205}
1206
1208 .clazz = DNN_DEFINE_CLASS(dnn_onnx),
1209 .type = DNN_ONNX,
1210 .load_model = dnn_load_model_onnx,
1211 .execute_model = dnn_execute_model_onnx,
1212 .get_result = dnn_get_result_onnx,
1213 .flush = dnn_flush_onnx,
1214 .free_model = dnn_free_model_onnx,
1215};
static const AVFilterPad outputs[]
Definition af_aap.c:310
static FILE * out
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
int ff_check_exec_params(void *ctx, DNNBackendType backend, DNNFunctionType func_type, DNNExecBaseParams *exec_params)
void ff_dnn_wait_requests(SafeQueue *request_queue, int nireq)
Wait for all inference requests to complete before teardown.
DNNAsyncStatusType ff_dnn_get_result_common(Queue *task_queue, AVFrame **in, AVFrame **out)
Extract input and output frame from the Task Queue after asynchronous inference.
int ff_dnn_async_module_cleanup(DNNAsyncExecModule *async_module)
Join the Async Execution thread and set module pointers to NULL.
int ff_dnn_fill_task(TaskItem *task, DNNExecBaseParams *exec_params, void *backend_model, int async, int do_ioproc)
Fill the Task for Backend Execution.
int ff_dnn_fill_gettingoutput_task(TaskItem *task, DNNExecBaseParams *exec_params, void *backend_model, int input_height, int input_width, void *ctx)
Allocate input and output frames and fill the Task with execution parameters.
DNN common functions different backends.
#define DNN_DEFINE_CLASS(fname)
static int dnn_execute_model_onnx(const DNNModel *model, DNNExecBaseParams *exec_params)
static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
const DNNModule ff_dnn_backend_onnx
static const AVOption dnn_onnx_options[]
static int dnn_flush_onnx(const DNNModel *model)
static DNNModel * dnn_load_model_onnx(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx)
static int get_output_onnx(DNNModel *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
static int fill_model_input_onnx(ONNXModel *onnx_model, ONNXRequestItem *request)
static int execute_model_onnx(ONNXRequestItem *request, Queue *lltask_queue)
static ONNXInferRequest * onnx_create_inference_request(void)
static void init_ort_api(void)
static void onnx_free_request(ONNXInferRequest *request)
static const OrtApi * g_ort
static void dnn_free_model_onnx(DNNModel **model)
static DNNAsyncStatusType dnn_get_result_onnx(const DNNModel *model, AVFrame **in, AVFrame **out)
static AVOnce g_ort_init_once
static int onnx_start_inference(void *args)
#define OFFSET(x)
static void destroy_request_item(ONNXRequestItem **arg)
static void infer_completion_callback(void *args)
static int get_input_onnx(DNNModel *model, DNNData *input, const char *input_name)
static int dnn_get_height_idx_by_layout(DNNLayout layout)
DNNAsyncStatusType
@ DL_NCHW
@ DNN_ONNX
DNNFunctionType
@ DFT_PROCESS_FRAME
@ DFT_ANALYTICS_DETECT
static int dnn_get_width_idx_by_layout(DNNLayout layout)
#define DNN_GENERIC_ERROR
@ DNN_FLOAT
@ DCO_RGB
static int dnn_get_channel_idx_by_layout(DNNLayout layout)
int ff_proc_from_frame_to_dnn(AVFrame *frame, DNNData *input, void *log_ctx)
int ff_frame_to_dnn_detect(AVFrame *frame, DNNData *input, void *log_ctx)
int ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx)
Definition dnn_io_proc.c:42
DNN input&output process between AVFrame and DNNData.
#define fail
Definition test.h:479
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition log.h:216
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_size_mult(size_t a, size_t b, size_t *r)
Multiply two size_t values checking for overflow.
Definition mem.c:565
int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
Check if the given dimension of an image is valid, meaning that all bytes of the image can be address...
Definition imgutils.c:318
int av_strcasecmp(const char *a, const char *b)
Locale-independent case-insensitive compare.
Definition avstring.c:208
misc image utilities
const char * arg
Definition jacosubdec.c:65
#define AVFILTER_DEFINE_CLASS(fname)
Definition filters.h:478
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
#define AVOnce
Definition thread.h:202
static int ff_thread_once(char *control, void(*routine)(void))
Definition thread.h:205
#define AV_ONCE_INIT
Definition thread.h:203
uint64_t layout
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
#define av_strdup(s)
Definition ops_static.c:55
#define av_malloc(s)
Definition ops_static.c:52
AVOptions.
const char * name
Definition qsvenc.c:142
void ff_queue_destroy(Queue *q)
Destroy the Queue instance.
Definition queue.c:72
void * ff_queue_pop_front(Queue *q)
Remove and free first element from the Queue.
Definition queue.c:151
int ff_queue_push_back(Queue *q, void *v)
Add data to the tail of the queue.
Definition queue.c:130
void * ff_queue_peek_front(Queue *q)
Return a pointer to the data at the head of the queue.
Definition queue.c:93
size_t ff_queue_size(Queue *q)
Return the length of the Queue.
Definition queue.c:88
Queue * ff_queue_create(void)
Create a Queue instance.
Definition queue.c:47
int ff_safe_queue_push_back(SafeQueue *sq, void *v)
Add data to the tail of queue in the SafeQueue after locking mutex.
Definition safe_queue.c:106
void * ff_safe_queue_pop_front(SafeQueue *sq)
Remove and free first element from the queue in SafeQueue.
Definition safe_queue.c:116
size_t ff_safe_queue_size(SafeQueue *sq)
Return the length of the SafeQueue.
Definition safe_queue.c:80
SafeQueue * ff_safe_queue_create(void)
Create and initialize a SafeQueue instance.
Definition safe_queue.c:52
void ff_safe_queue_destroy(SafeQueue *sq)
Destroy the SafeQueue instance.
Definition safe_queue.c:69
#define snprintf
Definition snprintf.h:34
An instance of a filter.
Definition avfilter.h:273
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int width
Definition frame.h:544
int height
Definition frame.h:544
AVOption.
Definition opt.h:428
Common Async Execution Mechanism for the DNN Backends.
void * args
Argument for the execution functions.
int(* start_inference)(void *request)
Synchronous inference function for the backend with corresponding request item as the argument.
void(* callback)(void *args)
Completion Callback for the backend.
float scale
DNNDataType dt
int dims[4]
DNNColorOrder order
void * data
DNNLayout layout
int(* get_input)(struct DNNModel *model, DNNData *input, const char *input_name)
int(* get_output)(struct DNNModel *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
FramePrePostProc frame_pre_proc
FramePrePostProc frame_post_proc
DetectPostProc detect_post_proc
AVFilterContext * filter_ctx
DNNFunctionType func_type
OrtValue * input_tensor
OrtValue ** output_tensors
Queue * task_queue
DnnContext * ctx
DNNData input_info
OrtSessionOptions * session_options
SafeQueue * request_queue
OrtSession * session
DNNModel model
OrtAllocator * allocator
Queue * lltask_queue
DNNAsyncExecModule exec_module
ONNXInferRequest * infer_request
LastLevelTaskItem * lltask
Linear double-ended data structure.
Definition executor.c:51
Double-ended queue with mutex locks ensuring data consistency while multithreading.
Definition safe_queue.c:46
uint32_t inference_done
AVFrame * in_frame
const char ** output_names
uint8_t do_ioproc
uint32_t inference_todo
const char * input_name
AVFrame * out_frame
uint32_t nb_output
#define av_free(p)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static FilteringContext * filter_ctx
Definition transcode.c:52