38#include <onnxruntime_c_api.h>
71#define OFFSET(x) offsetof(ONNXOptions, x)
72#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
74 {
"threads_per_operation",
"number of CPU threads per ORT operator (device=cpu only)",
86 g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
89#define ORT_ABORT_ON_ERROR(expr) \
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); \
158 if (!model || !*model)
199 OrtTypeInfo *type_info =
NULL;
200 const OrtTensorTypeAndShapeInfo *tensor_info =
NULL;
202 size_t input_count = 0;
203 size_t input_index = 0;
206 ONNXTensorElementDataType tensor_type;
209 if (!input_name || !*input_name) {
219 status =
g_ort->SessionGetInputCount(onnx_model->
session, &input_count);
220 if (status !=
NULL) {
221 const char *msg =
g_ort->GetErrorMessage(status);
223 g_ort->ReleaseStatus(status);
227 for (
size_t i = 0;
i < input_count;
i++) {
231 if (status !=
NULL) {
232 g_ort->ReleaseStatus(status);
235 if (!strcmp(
name, input_name)) {
250 status =
g_ort->SessionGetInputTypeInfo(onnx_model->
session, input_index,
252 if (status !=
NULL) {
253 const char *msg =
g_ort->GetErrorMessage(status);
255 g_ort->ReleaseStatus(status);
259 status =
g_ort->CastTypeInfoToTensorInfo(type_info, &tensor_info);
260 if (status !=
NULL) {
261 g_ort->ReleaseTypeInfo(type_info);
262 g_ort->ReleaseStatus(status);
266 status =
g_ort->GetDimensionsCount(tensor_info, &num_dims);
267 if (status !=
NULL) {
268 g_ort->ReleaseTypeInfo(type_info);
269 g_ort->ReleaseStatus(status);
275 g_ort->ReleaseTypeInfo(type_info);
281 g_ort->ReleaseTypeInfo(type_info);
285 g_ort->GetDimensions(tensor_info, dims, num_dims);
286 g_ort->GetTensorElementType(tensor_info, &tensor_type);
290 "ONNX model has fixed batch size %"PRId64
", but the backend "
291 "only supports a batch size of 1\n", dims[0]);
293 g_ort->ReleaseTypeInfo(type_info);
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",
303 g_ort->ReleaseTypeInfo(type_info);
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;
318 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
323 g_ort->ReleaseTypeInfo(type_info);
335 g_ort->ReleaseTypeInfo(type_info);
349 int ret, width_idx, height_idx, channel_idx;
351 size_t input_tensor_size;
352 OrtMemoryInfo *memory_info;
373 if (input.
dims[height_idx] < 0)
375 if (input.
dims[width_idx] < 0)
378 if (input.
dims[0] <= 0 || input.
dims[channel_idx] <= 0 ||
379 input.
dims[height_idx] <= 0 || input.
dims[width_idx] <= 0) {
386 (
unsigned)input.
dims[height_idx], 0,
ctx);
389 input.
dims[width_idx], input.
dims[height_idx]);
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];
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) {
444 status =
g_ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info);
445 if (status !=
NULL) {
450 status =
g_ort->CreateTensorWithDataAsOrtValue(
451 memory_info, input.
data, input_tensor_size,
452 input_shape, 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,
455 g_ort->ReleaseMemoryInfo(memory_info);
457 if (status !=
NULL) {
458 const char *msg =
g_ort->GetErrorMessage(status);
460 g_ort->ReleaseStatus(status);
481 const char *input_names[1];
497 "ONNX backend: input/output tensor name was not resolved at load time\n");
507 size_t output_count = 0;
508 int found_output = 0;
510 status =
g_ort->SessionGetOutputCount(onnx_model->
session, &output_count);
511 if (status !=
NULL) {
512 const char *msg =
g_ort->GetErrorMessage(status);
514 g_ort->ReleaseStatus(status);
518 for (uint32_t req = 0; req < task->
nb_output; req++) {
520 for (
size_t i = 0;
i < output_count;
i++) {
524 if (status !=
NULL) {
525 g_ort->ReleaseStatus(status);
536 "Output name '%s' not found in ONNX model\n",
559 input_names, (
const OrtValue *
const *)&infer_request->
input_tensor, 1,
562 if (status !=
NULL) {
563 const char *msg =
g_ort->GetErrorMessage(status);
565 g_ort->ReleaseStatus(status);
586 OrtTensorTypeAndShapeInfo *tensor_info;
587 ONNXTensorElementDataType tensor_type;
602 if (status !=
NULL) {
604 g_ort->ReleaseStatus(status);
608 status =
g_ort->GetDimensionsCount(tensor_info, &num_dims);
609 if (status !=
NULL) {
611 g_ort->ReleaseStatus(status);
612 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
619 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
623 status =
g_ort->GetDimensions(tensor_info, dims, num_dims);
624 if (status !=
NULL) {
626 g_ort->ReleaseStatus(status);
628 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
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",
638 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
643 status =
g_ort->GetTensorElementType(tensor_info, &tensor_type);
644 if (status !=
NULL) {
646 g_ort->ReleaseStatus(status);
648 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
651 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
655 "Unsupported output tensor[%u] data type, only float supported\n",
i);
657 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
670 }
else if (num_dims == 3) {
678 "Support for %zu-dimensional output (tensor[%u])", num_dims,
i);
680 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
685 if (status !=
NULL) {
687 g_ort->ReleaseStatus(status);
689 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
694 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
748 if (lltask ==
NULL) {
784 const char *output_name,
int *output_width,
int *output_height)
792 .input_name = input_name,
793 .output_names = &output_name,
853 model = &onnx_model->
model;
856 status =
g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING,
"FFmpeg", &onnx_model->
env);
857 if (status !=
NULL) {
863 if (status !=
NULL) {
868 if (
options->num_threads > 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;
881 status =
g_ort->SessionOptionsAppendExecutionProvider_CUDA(
883 if (status !=
NULL) {
884 const char *msg =
g_ort->GetErrorMessage(status);
886 ctx->device_id, msg);
887 g_ort->ReleaseStatus(status);
892 av_log(
ctx,
AV_LOG_WARNING,
"CUDA provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
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;
906 g_ort->ReleaseStatus(status);
909 g_ort->ReleaseStatus(status);
911 if (
g_ort->SessionOptionsAppendExecutionProvider) {
912 status =
g_ort->SessionOptionsAppendExecutionProvider(
914 dml_options_keys, dml_options_values, 1);
915 if (status !=
NULL) {
916 const char *msg =
g_ort->GetErrorMessage(status);
918 ctx->device_id, msg);
919 g_ort->ReleaseStatus(status);
924 av_log(
ctx,
AV_LOG_WARNING,
"DirectML provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
930 if (
g_ort->SessionOptionsAppendExecutionProvider) {
931 status =
g_ort->SessionOptionsAppendExecutionProvider(
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);
944 "VitisAI provider function not available in this ONNX Runtime API version. Falling back to CPU.\n");
949 "Unknown device '%s'. Supported: cpu, cuda, dml, vitisai. Using CPU\n",
953 "Unknown device '%s'. Supported: cpu, cuda, vitisai. Using CPU\n",
963 wchar_t *wfilename =
NULL;
964 if (utf8towchar(
ctx->model_filename, &wfilename)) {
973 status =
g_ort->CreateSession(onnx_model->
env, wfilename,
978 status =
g_ort->CreateSession(onnx_model->
env,
ctx->model_filename,
981 if (status !=
NULL) {
982 const char *msg =
g_ort->GetErrorMessage(status);
984 g_ort->ReleaseStatus(status);
988 status =
g_ort->GetAllocatorWithDefaultOptions(&onnx_model->
allocator);
989 if (status !=
NULL) {
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);
1004 g_ort->ReleaseStatus(status);
1007 if (input_count == 0) {
1011 if (input_count > 1) {
1013 "ONNX model exposes %zu input tensors; the ONNX backend "
1014 "supports single-input models only.\n",
1021 if (!
ctx->model_inputname || !*
ctx->model_inputname) {
1023 status =
g_ort->SessionGetInputName(onnx_model->
session, 0,
1025 if (status !=
NULL) {
1026 const char *msg =
g_ort->GetErrorMessage(status);
1028 g_ort->ReleaseStatus(status);
1034 if (!
ctx->model_inputname)
1037 ctx->model_inputname);
1041 if (!
ctx->model_outputnames) {
1042 size_t output_count = 0;
1044 status =
g_ort->SessionGetOutputCount(onnx_model->
session, &output_count);
1045 if (status !=
NULL) {
1046 const char *msg =
g_ort->GetErrorMessage(status);
1048 g_ort->ReleaseStatus(status);
1051 if (output_count == 0) {
1055 status =
g_ort->SessionGetOutputName(onnx_model->
session, 0,
1057 if (status !=
NULL) {
1058 const char *msg =
g_ort->GetErrorMessage(status);
1060 g_ort->ReleaseStatus(status);
1063 ctx->model_outputnames =
av_calloc(1,
sizeof(*
ctx->model_outputnames));
1064 if (!
ctx->model_outputnames) {
1070 if (!
ctx->model_outputnames[0]) {
1074 ctx->nb_outputs = 1;
1075 if (output_count == 1) {
1077 ctx->model_outputnames[0]);
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]);
static const AVFilterPad outputs[]
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define i(width, name, range_min, range_max)
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)
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)
static int dnn_get_width_idx_by_layout(DNNLayout layout)
#define DNN_GENERIC_ERROR
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)
DNN input&output process between AVFrame and DNNData.
@ AV_OPT_TYPE_INT
Underlying C type is int.
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
#define AV_LOG_WARNING
Something somehow does not look correct.
#define AV_LOG_INFO
Standard information.
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
int av_size_mult(size_t a, size_t b, size_t *r)
Multiply two size_t values checking for overflow.
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...
int av_strcasecmp(const char *a, const char *b)
Locale-independent case-insensitive compare.
#define AVFILTER_DEFINE_CLASS(fname)
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
static int ff_thread_once(char *control, void(*routine)(void))
void * av_calloc(size_t nmemb, size_t size)
Memory handling functions.
void ff_queue_destroy(Queue *q)
Destroy the Queue instance.
void * ff_queue_pop_front(Queue *q)
Remove and free first element from the Queue.
int ff_queue_push_back(Queue *q, void *v)
Add data to the tail of the queue.
void * ff_queue_peek_front(Queue *q)
Return a pointer to the data at the head of the queue.
size_t ff_queue_size(Queue *q)
Return the length of the Queue.
Queue * ff_queue_create(void)
Create a Queue instance.
int ff_safe_queue_push_back(SafeQueue *sq, void *v)
Add data to the tail of queue in the SafeQueue after locking mutex.
void * ff_safe_queue_pop_front(SafeQueue *sq)
Remove and free first element from the queue in SafeQueue.
size_t ff_safe_queue_size(SafeQueue *sq)
Return the length of the SafeQueue.
SafeQueue * ff_safe_queue_create(void)
Create and initialize a SafeQueue instance.
void ff_safe_queue_destroy(SafeQueue *sq)
Destroy the SafeQueue instance.
This structure describes decoded (raw) audio or video data.
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.
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 ** output_tensors
OrtSessionOptions * session_options
SafeQueue * request_queue
DNNAsyncExecModule exec_module
ONNXInferRequest * infer_request
LastLevelTaskItem * lltask
Linear double-ended data structure.
Double-ended queue with mutex locks ensuring data consistency while multithreading.
const char ** output_names
static FilteringContext * filter_ctx