39#include <onnxruntime_c_api.h>
72#define OFFSET(x) offsetof(ONNXOptions, x)
73#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
75 {
"threads_per_operation",
"number of CPU threads per ORT operator (device=cpu only)",
87 g_ort = OrtGetApiBase()->GetApi(ORT_API_VERSION);
90#define ORT_ABORT_ON_ERROR(expr) \
92 OrtStatus *status = (expr); \
93 if (status != NULL) { \
94 const char *msg = g_ort->GetErrorMessage(status); \
95 av_log(ctx, AV_LOG_ERROR, "ONNX Runtime error: %s\n", msg); \
96 g_ort->ReleaseStatus(status); \
118 for (uint32_t
i = 0;
i <
header->nb_bboxes;
i++) {
120 if (bbox->
x < 0 || bbox->
w < 0 || bbox->
x + bbox->
w >
frame->width)
122 if (bbox->
y < 0 || bbox->
h < 0 || bbox->
y + bbox->
h >
frame->height)
172 for (uint32_t
i = 0;
i <
header->nb_bboxes;
i++) {
240 if (!model || !*model)
281 OrtTypeInfo *type_info =
NULL;
282 const OrtTensorTypeAndShapeInfo *tensor_info =
NULL;
284 size_t input_count = 0;
285 size_t input_index = 0;
288 ONNXTensorElementDataType tensor_type;
291 if (!input_name || !*input_name) {
301 status =
g_ort->SessionGetInputCount(onnx_model->
session, &input_count);
302 if (status !=
NULL) {
303 const char *msg =
g_ort->GetErrorMessage(status);
305 g_ort->ReleaseStatus(status);
309 for (
size_t i = 0;
i < input_count;
i++) {
313 if (status !=
NULL) {
314 g_ort->ReleaseStatus(status);
317 if (!strcmp(
name, input_name)) {
332 status =
g_ort->SessionGetInputTypeInfo(onnx_model->
session, input_index,
334 if (status !=
NULL) {
335 const char *msg =
g_ort->GetErrorMessage(status);
337 g_ort->ReleaseStatus(status);
341 status =
g_ort->CastTypeInfoToTensorInfo(type_info, &tensor_info);
342 if (status !=
NULL) {
343 g_ort->ReleaseTypeInfo(type_info);
344 g_ort->ReleaseStatus(status);
348 status =
g_ort->GetDimensionsCount(tensor_info, &num_dims);
349 if (status !=
NULL) {
350 g_ort->ReleaseTypeInfo(type_info);
351 g_ort->ReleaseStatus(status);
357 g_ort->ReleaseTypeInfo(type_info);
363 g_ort->ReleaseTypeInfo(type_info);
367 g_ort->GetDimensions(tensor_info, dims, num_dims);
368 g_ort->GetTensorElementType(tensor_info, &tensor_type);
372 "ONNX model has fixed batch size %"PRId64
", but the backend "
373 "only supports a batch size of 1\n", dims[0]);
375 g_ort->ReleaseTypeInfo(type_info);
379 for (
size_t i = 1;
i < num_dims;
i++) {
380 if (dims[
i] > INT_MAX) {
382 "ONNX model input dimension %zu (%"PRId64
") is too large to be represented\n",
385 g_ort->ReleaseTypeInfo(type_info);
395 input->
dims[0] = dims[0] > 0 ? dims[0] : 1;
396 input->
dims[1] = dims[1] > 0 ? dims[1] : 3;
397 input->
dims[2] = dims[2] > 0 ? dims[2] : -1;
398 input->
dims[3] = dims[3] > 0 ? dims[3] : -1;
400 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
405 g_ort->ReleaseTypeInfo(type_info);
417 g_ort->ReleaseTypeInfo(type_info);
431 int ret, width_idx, height_idx, channel_idx;
433 size_t input_tensor_size;
434 OrtMemoryInfo *memory_info;
455 if (input.
dims[height_idx] < 0)
457 if (input.
dims[width_idx] < 0)
460 if (input.
dims[0] <= 0 || input.
dims[channel_idx] <= 0 ||
461 input.
dims[height_idx] <= 0 || input.
dims[width_idx] <= 0) {
468 (
unsigned)input.
dims[height_idx], 0,
ctx);
471 input.
dims[width_idx], input.
dims[height_idx]);
475 input_shape[0] = input.
dims[0];
476 input_shape[1] = input.
dims[channel_idx];
477 input_shape[2] = input.
dims[height_idx];
478 input_shape[3] = input.
dims[width_idx];
484 input_tensor_size =
sizeof(
float);
485 if (
av_size_mult(input_tensor_size, (
size_t)input_shape[0], &input_tensor_size) < 0 ||
486 av_size_mult(input_tensor_size, (
size_t)input_shape[1], &input_tensor_size) < 0 ||
487 av_size_mult(input_tensor_size, (
size_t)input_shape[2], &input_tensor_size) < 0 ||
488 av_size_mult(input_tensor_size, (
size_t)input_shape[3], &input_tensor_size) < 0) {
531 status =
g_ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info);
532 if (status !=
NULL) {
537 status =
g_ort->CreateTensorWithDataAsOrtValue(
538 memory_info, input.
data, input_tensor_size,
539 input_shape, 4, ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT,
542 g_ort->ReleaseMemoryInfo(memory_info);
544 if (status !=
NULL) {
545 const char *msg =
g_ort->GetErrorMessage(status);
547 g_ort->ReleaseStatus(status);
568 const char *input_names[1];
584 "ONNX backend: input/output tensor name was not resolved at load time\n");
594 size_t output_count = 0;
595 int found_output = 0;
597 status =
g_ort->SessionGetOutputCount(onnx_model->
session, &output_count);
598 if (status !=
NULL) {
599 const char *msg =
g_ort->GetErrorMessage(status);
601 g_ort->ReleaseStatus(status);
605 for (uint32_t req = 0; req < task->
nb_output; req++) {
607 for (
size_t i = 0;
i < output_count;
i++) {
611 if (status !=
NULL) {
612 g_ort->ReleaseStatus(status);
623 "Output name '%s' not found in ONNX model\n",
646 input_names, (
const OrtValue *
const *)&infer_request->
input_tensor, 1,
649 if (status !=
NULL) {
650 const char *msg =
g_ort->GetErrorMessage(status);
652 g_ort->ReleaseStatus(status);
673 OrtTensorTypeAndShapeInfo *tensor_info;
674 ONNXTensorElementDataType tensor_type;
689 if (status !=
NULL) {
691 g_ort->ReleaseStatus(status);
695 status =
g_ort->GetDimensionsCount(tensor_info, &num_dims);
696 if (status !=
NULL) {
698 g_ort->ReleaseStatus(status);
699 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
706 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
710 status =
g_ort->GetDimensions(tensor_info, dims, num_dims);
711 if (status !=
NULL) {
713 g_ort->ReleaseStatus(status);
715 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
719 for (
size_t d = 0; d < num_dims; d++) {
720 if (dims[d] < 0 || dims[d] > INT_MAX) {
722 "Output tensor[%u] dimension %zu (%"PRId64
") is out of representable range\n",
725 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
730 status =
g_ort->GetTensorElementType(tensor_info, &tensor_type);
731 if (status !=
NULL) {
733 g_ort->ReleaseStatus(status);
735 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
738 if (tensor_type == ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT) {
742 "Unsupported output tensor[%u] data type, only float supported\n",
i);
744 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
757 }
else if (num_dims == 3) {
763 }
else if (num_dims == 2) {
769 }
else if (num_dims == 1) {
777 "Support for %zu-dimensional output (tensor[%u])", num_dims,
i);
779 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
785 "Output tensor[%u] batch size %d unsupported, must be 1\n",
788 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
793 if (status !=
NULL) {
795 g_ort->ReleaseStatus(status);
797 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
802 g_ort->ReleaseTensorTypeAndShapeInfo(tensor_info);
868 if (lltask ==
NULL) {
910 const char *output_name,
int *output_width,
int *output_height)
918 .input_name = input_name,
919 .output_names = &output_name,
979 model = &onnx_model->
model;
982 if (
ctx->nireq > 1) {
984 "nireq=%d is not supported by the ONNX Runtime backend, "
985 "which only allocates a single, synchronous inference "
986 "request. Rolling back to nireq=1.\n",
ctx->nireq);
990 status =
g_ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING,
"FFmpeg", &onnx_model->
env);
991 if (status !=
NULL) {
997 if (status !=
NULL) {
1002 if (
options->num_threads > 0 &&
1010 if (
g_ort->SessionOptionsAppendExecutionProvider_CUDA) {
1011 OrtCUDAProviderOptions cuda_options;
1012 memset(&cuda_options, 0,
sizeof(cuda_options));
1013 cuda_options.device_id =
ctx->device_id;
1015 status =
g_ort->SessionOptionsAppendExecutionProvider_CUDA(
1017 if (status !=
NULL) {
1018 const char *msg =
g_ort->GetErrorMessage(status);
1020 ctx->device_id, msg);
1021 g_ort->ReleaseStatus(status);
1026 av_log(
ctx,
AV_LOG_WARNING,
"CUDA provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
1030 const char* dml_options_keys[] = {
"device_id"};
1031 const char* dml_options_values[] = {
NULL};
1032 char device_id_str[32];
1033 snprintf(device_id_str,
sizeof(device_id_str),
"%d",
ctx->device_id);
1034 dml_options_values[0] = device_id_str;
1040 g_ort->ReleaseStatus(status);
1043 g_ort->ReleaseStatus(status);
1045 if (
g_ort->SessionOptionsAppendExecutionProvider) {
1046 status =
g_ort->SessionOptionsAppendExecutionProvider(
1048 dml_options_keys, dml_options_values, 1);
1049 if (status !=
NULL) {
1050 const char *msg =
g_ort->GetErrorMessage(status);
1052 ctx->device_id, msg);
1053 g_ort->ReleaseStatus(status);
1058 av_log(
ctx,
AV_LOG_WARNING,
"DirectML provider function not available in this ONNX Runtime API version. Falling back to CPU\n");
1064 if (
g_ort->SessionOptionsAppendExecutionProvider) {
1065 status =
g_ort->SessionOptionsAppendExecutionProvider(
1068 if (status !=
NULL) {
1069 const char *msg =
g_ort->GetErrorMessage(status);
1071 "Failed to enable VitisAI EP: %s. Falling back to CPU\n", msg);
1072 g_ort->ReleaseStatus(status);
1078 "VitisAI provider function not available in this ONNX Runtime API version. Falling back to CPU.\n");
1083 "Unknown device '%s'. Supported: cpu, cuda, dml, vitisai. Using CPU\n",
1087 "Unknown device '%s'. Supported: cpu, cuda, vitisai. Using CPU\n",
1097 wchar_t *wfilename =
NULL;
1098 if (utf8towchar(
ctx->model_filename, &wfilename)) {
1107 status =
g_ort->CreateSession(onnx_model->
env, wfilename,
1112 status =
g_ort->CreateSession(onnx_model->
env,
ctx->model_filename,
1115 if (status !=
NULL) {
1116 const char *msg =
g_ort->GetErrorMessage(status);
1118 g_ort->ReleaseStatus(status);
1122 status =
g_ort->GetAllocatorWithDefaultOptions(&onnx_model->
allocator);
1123 if (status !=
NULL) {
1133 size_t input_count = 0;
1134 status =
g_ort->SessionGetInputCount(onnx_model->
session, &input_count);
1135 if (status !=
NULL) {
1136 const char *msg =
g_ort->GetErrorMessage(status);
1138 g_ort->ReleaseStatus(status);
1141 if (input_count == 0) {
1145 if (input_count > 1) {
1147 "ONNX model exposes %zu input tensors; the ONNX backend "
1148 "supports single-input models only.\n",
1155 if (!
ctx->model_inputname || !*
ctx->model_inputname) {
1157 status =
g_ort->SessionGetInputName(onnx_model->
session, 0,
1159 if (status !=
NULL) {
1160 const char *msg =
g_ort->GetErrorMessage(status);
1162 g_ort->ReleaseStatus(status);
1168 if (!
ctx->model_inputname)
1171 ctx->model_inputname);
1175 if (!
ctx->model_outputnames) {
1176 size_t output_count = 0;
1178 status =
g_ort->SessionGetOutputCount(onnx_model->
session, &output_count);
1179 if (status !=
NULL) {
1180 const char *msg =
g_ort->GetErrorMessage(status);
1182 g_ort->ReleaseStatus(status);
1185 if (output_count == 0) {
1189 status =
g_ort->SessionGetOutputName(onnx_model->
session, 0,
1191 if (status !=
NULL) {
1192 const char *msg =
g_ort->GetErrorMessage(status);
1194 g_ort->ReleaseStatus(status);
1197 ctx->model_outputnames =
av_calloc(1,
sizeof(*
ctx->model_outputnames));
1198 if (!
ctx->model_outputnames) {
1204 if (!
ctx->model_outputnames[0]) {
1208 ctx->nb_outputs = 1;
1209 if (output_count == 1) {
1211 ctx->model_outputnames[0]);
1214 "ONNX model exposes %zu output tensors; auto-using index 0 ('%s'). "
1215 "Specify output=NAME to choose a different one.\n",
1216 output_count,
ctx->model_outputnames[0]);
SwsAArch64OpImplParams params
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)
#define AV_NUM_DETECTION_BBOX_CLASSIFY
At most 4 classifications based on the detected bounding box.
static av_always_inline AVDetectionBBox * av_get_detection_bbox(const AVDetectionBBoxHeader *header, unsigned int idx)
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)
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 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 int extract_lltask_from_task(DNNFunctionType func_type, TaskItem *task, Queue *lltask_queue, DNNExecBaseParams *exec_params)
static int contain_valid_detection_bbox(AVFrame *frame)
static void destroy_request_item(ONNXRequestItem **arg)
static int execute_model_onnx(ONNXModel *onnx_model, ONNXRequestItem *request, Queue *lltask_queue)
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_frame_to_dnn_classify(AVFrame *frame, DNNData *input, uint32_t bbox_index, 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.
AVFrameSideData * av_frame_get_side_data(const AVFrame *frame, enum AVFrameSideDataType type)
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
@ AV_FRAME_DATA_DETECTION_BBOXES
Bounding boxes for object detection and classification, as described by AVDetectionBBoxHeader.
#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.
int av_strncasecmp(const char *a, const char *b, size_t n)
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.
static const uint8_t header[24]
char detect_label[AV_DETECTION_BBOX_LABEL_NAME_MAX_SIZE]
Detect result with confidence.
int x
Distance in pixels from the left/top edge of the frame, together with width and height,...
Structure to hold side data for an AVFrame.
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
ClassifyPostProc classify_post_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