37#include <onnxruntime_c_api.h>
69#define OFFSET(x) offsetof(ONNXOptions, x)
70#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
72 {
"threads_per_operation",
"number of CPU threads per ORT operator (device=cpu only)",
84 g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
87#define ORT_ABORT_ON_ERROR(expr) \
89 OrtStatus *status = (expr); \
90 if (status != NULL) { \
91 const char *msg = g_ort->GetErrorMessage(status); \
92 av_log(ctx, AV_LOG_ERROR, "ONNX Runtime error: %s\n", msg); \
93 g_ort->ReleaseStatus(status); \
150 if (!model || !*model)
191 OrtTypeInfo *type_info =
NULL;
192 const OrtTensorTypeAndShapeInfo *tensor_info =
NULL;
194 size_t input_count = 0;
195 size_t input_index = 0;
198 ONNXTensorElementDataType tensor_type;
201 if (!input_name || !*input_name) {
211 status =
g_ort->SessionGetInputCount(onnx_model->
session, &input_count);
212 if (status !=
NULL) {
213 const char *msg =
g_ort->GetErrorMessage(status);
215 g_ort->ReleaseStatus(status);
219 for (
size_t i = 0;
i < input_count;
i++) {
223 if (status !=
NULL) {
224 g_ort->ReleaseStatus(status);
227 if (!strcmp(
name, input_name)) {
242 status =
g_ort->SessionGetInputTypeInfo(onnx_model->
session, input_index,
244 if (status !=
NULL) {
245 const char *msg =
g_ort->GetErrorMessage(status);
247 g_ort->ReleaseStatus(status);
251 status =
g_ort->CastTypeInfoToTensorInfo(type_info, &tensor_info);
252 if (status !=
NULL) {
253 g_ort->ReleaseTypeInfo(type_info);
254 g_ort->ReleaseStatus(status);
258 status =
g_ort->GetDimensionsCount(tensor_info, &num_dims);
259 if (status !=
NULL) {
260 g_ort->ReleaseTypeInfo(type_info);
261 g_ort->ReleaseStatus(status);
267 g_ort->ReleaseTypeInfo(type_info);
273 g_ort->ReleaseTypeInfo(type_info);
277 g_ort->GetDimensions(tensor_info, dims, num_dims);
278 g_ort->GetTensorElementType(tensor_info, &tensor_type);
282 "ONNX model has fixed batch size %"PRId64
", but the backend "
283 "only supports a batch size of 1\n", dims[0]);
285 g_ort->ReleaseTypeInfo(type_info);
294 input->
dims[0] = dims[0] > 0 ? dims[0] : 1;
295 input->
dims[1] = dims[1] > 0 ? dims[1] : 3;
296 input->
dims[2] = dims[2] > 0 ? dims[2] : -1;
297 input->
dims[3] = dims[3] > 0 ? dims[3] : -1;
299 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
304 g_ort->ReleaseTypeInfo(type_info);
316 g_ort->ReleaseTypeInfo(type_info);
330 int ret, width_idx, height_idx, channel_idx;
332 size_t input_tensor_size;
333 OrtMemoryInfo *memory_info;
357 input_shape[0] = input.
dims[0];
358 input_shape[1] = input.
dims[channel_idx];
359 input_shape[2] = input.
dims[height_idx];
360 input_shape[3] = input.
dims[width_idx];
362 input_tensor_size = input_shape[0] * input_shape[1] * input_shape[2] * input_shape[3];
363 input_tensor_size *=
sizeof(
float);
392 status =
g_ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info);
393 if (status !=
NULL) {
398 status =
g_ort->CreateTensorWithDataAsOrtValue(
399 memory_info, input.
data, input_tensor_size,
400 input_shape, 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,
403 g_ort->ReleaseMemoryInfo(memory_info);
405 if (status !=
NULL) {
406 const char *msg =
g_ort->GetErrorMessage(status);
408 g_ort->ReleaseStatus(status);
429 const char *input_names[1];
430 const char *output_names[1];
445 "Multiple output tensors (%u) for ONNX backend", task->
nb_output);
451 "ONNX backend: input/output tensor name was not resolved at load time\n");
461 size_t output_count = 0;
462 int found_output = 0;
464 status =
g_ort->SessionGetOutputCount(onnx_model->
session, &output_count);
465 if (status !=
NULL) {
466 const char *msg =
g_ort->GetErrorMessage(status);
468 g_ort->ReleaseStatus(status);
472 for (
size_t i = 0;
i < output_count;
i++) {
476 if (status !=
NULL) {
477 g_ort->ReleaseStatus(status);
489 "Output name '%s' not found in ONNX model\n",
501 input_names, (
const OrtValue *
const *)&infer_request->
input_tensor, 1,
504 if (status !=
NULL) {
505 const char *msg =
g_ort->GetErrorMessage(status);
507 g_ort->ReleaseStatus(status);
523 OrtTensorTypeAndShapeInfo *tensor_info;
524 ONNXTensorElementDataType tensor_type;
536 if (status !=
NULL) {
538 g_ort->ReleaseStatus(status);
542 g_ort->GetDimensionsCount(tensor_info, &num_dims);
546 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
549 g_ort->GetDimensions(tensor_info, dims, num_dims);
555 g_ort->GetTensorElementType(tensor_info, &tensor_type);
556 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
561 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
573 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
578 if (status !=
NULL) {
580 g_ort->ReleaseStatus(status);
582 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
605 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
610 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
635 if (lltask ==
NULL) {
671 const char *output_name,
int *output_width,
int *output_height)
679 .input_name = input_name,
680 .output_names = &output_name,
743 model = &onnx_model->
model;
746 status =
g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING,
"FFmpeg", &onnx_model->
env);
747 if (status !=
NULL) {
753 if (status !=
NULL) {
758 if (
options->num_threads > 0 &&
766 if (
g_ort->SessionOptionsAppendExecutionProvider_CUDA) {
767 OrtCUDAProviderOptions cuda_options;
768 memset(&cuda_options, 0,
sizeof(cuda_options));
769 cuda_options.device_id =
ctx->device_id;
771 status =
g_ort->SessionOptionsAppendExecutionProvider_CUDA(
773 if (status !=
NULL) {
774 const char *msg =
g_ort->GetErrorMessage(status);
776 ctx->device_id, msg);
777 g_ort->ReleaseStatus(status);
782 av_log(
ctx,
AV_LOG_WARNING,
"CUDA provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
786 const char* dml_options_keys[] = {
"device_id"};
787 const char* dml_options_values[] = {
NULL};
788 char device_id_str[32];
789 snprintf(device_id_str,
sizeof(device_id_str),
"%d",
ctx->device_id);
790 dml_options_values[0] = device_id_str;
796 g_ort->ReleaseStatus(status);
799 g_ort->ReleaseStatus(status);
801 if (
g_ort->SessionOptionsAppendExecutionProvider) {
802 status =
g_ort->SessionOptionsAppendExecutionProvider(
804 dml_options_keys, dml_options_values, 1);
805 if (status !=
NULL) {
806 const char *msg =
g_ort->GetErrorMessage(status);
808 ctx->device_id, msg);
809 g_ort->ReleaseStatus(status);
814 av_log(
ctx,
AV_LOG_WARNING,
"DirectML provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
820 if (
g_ort->SessionOptionsAppendExecutionProvider) {
821 status =
g_ort->SessionOptionsAppendExecutionProvider(
824 if (status !=
NULL) {
825 const char *msg =
g_ort->GetErrorMessage(status);
827 "Failed to enable VitisAI EP: %s. Falling back to CPU\n", msg);
828 g_ort->ReleaseStatus(status);
834 "VitisAI provider function not available in this ONNX Runtime API version. Falling back to CPU.\n");
839 "Unknown device '%s'. Supported: cpu, cuda, dml, vitisai. Using CPU\n",
843 "Unknown device '%s'. Supported: cpu, cuda, vitisai. Using CPU\n",
853 wchar_t *wfilename =
NULL;
854 if (utf8towchar(
ctx->model_filename, &wfilename)) {
863 status =
g_ort->CreateSession(onnx_model->
env, wfilename,
868 status =
g_ort->CreateSession(onnx_model->
env,
ctx->model_filename,
871 if (status !=
NULL) {
872 const char *msg =
g_ort->GetErrorMessage(status);
874 g_ort->ReleaseStatus(status);
878 status =
g_ort->GetAllocatorWithDefaultOptions(&onnx_model->
allocator);
879 if (status !=
NULL) {
889 size_t input_count = 0;
890 status =
g_ort->SessionGetInputCount(onnx_model->
session, &input_count);
891 if (status !=
NULL) {
892 const char *msg =
g_ort->GetErrorMessage(status);
894 g_ort->ReleaseStatus(status);
897 if (input_count == 0) {
901 if (input_count > 1) {
903 "ONNX model exposes %zu input tensors; the ONNX backend "
904 "supports single-input models only.\n",
911 if (!
ctx->model_inputname || !*
ctx->model_inputname) {
913 status =
g_ort->SessionGetInputName(onnx_model->
session, 0,
915 if (status !=
NULL) {
916 const char *msg =
g_ort->GetErrorMessage(status);
918 g_ort->ReleaseStatus(status);
924 if (!
ctx->model_inputname)
927 ctx->model_inputname);
931 if (!
ctx->model_outputnames) {
932 size_t output_count = 0;
934 status =
g_ort->SessionGetOutputCount(onnx_model->
session, &output_count);
935 if (status !=
NULL) {
936 const char *msg =
g_ort->GetErrorMessage(status);
938 g_ort->ReleaseStatus(status);
941 if (output_count == 0) {
945 status =
g_ort->SessionGetOutputName(onnx_model->
session, 0,
947 if (status !=
NULL) {
948 const char *msg =
g_ort->GetErrorMessage(status);
950 g_ort->ReleaseStatus(status);
953 ctx->model_outputnames =
av_calloc(1,
sizeof(*
ctx->model_outputnames));
954 if (!
ctx->model_outputnames) {
960 if (!
ctx->model_outputnames[0]) {
965 if (output_count == 1) {
967 ctx->model_outputnames[0]);
970 "ONNX model exposes %zu output tensors; auto-using index 0 ('%s'). "
971 "Specify output=NAME to choose a different one.\n",
972 output_count,
ctx->model_outputnames[0]);
static const AVFilterPad outputs[]
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_strcasecmp(const char *a, const char *b)
Locale-independent case-insensitive compare.
static int output_data(MLPDecodeContext *m, unsigned int substr, AVFrame *frame, int *got_frame_ptr)
Write the audio data into the output buffer.
#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
AVFilterContext * filter_ctx
DNNFunctionType func_type
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 AVFormatContext * ctx
static FilteringContext * filter_ctx