FFmpeg
Loading...
Searching...
No Matches
dnn_backend_openvino.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020
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 OpenVINO backend implementation.
24 */
25
26#include "dnn_io_proc.h"
27#include "libavformat/avio.h"
28#include "libavutil/avassert.h"
29#include "libavutil/cpu.h"
30#include "libavutil/mem.h"
31#include "libavutil/opt.h"
32#include "libavutil/avstring.h"
34#include "safe_queue.h"
35#if HAVE_OPENVINO2
36#include <openvino/c/openvino.h>
37#else
38#include <c_api/ie_c_api.h>
39#endif
40#include "dnn_backend_common.h"
41
42typedef struct OVModel{
45#if HAVE_OPENVINO2
46 ov_core_t *core;
47 ov_model_t *ov_model;
48 ov_compiled_model_t *compiled_model;
49 ov_output_const_port_t* input_port;
50 ov_preprocess_input_info_t* input_info;
51 ov_output_const_port_t** output_ports;
52 ov_preprocess_output_info_t* output_info;
53 ov_preprocess_prepostprocessor_t* preprocess;
54#else
55 ie_core_t *core;
56 ie_network_t *network;
57 ie_executable_network_t *exe_network;
58 const char *all_input_names;
59 const char *all_output_names;
60#endif
61 SafeQueue *request_queue; // holds OVRequestItem
62 Queue *task_queue; // holds TaskItem
63 Queue *lltask_queue; // holds LastLevelTaskItem
65} OVModel;
66
67// one request for one call to openvino
68typedef struct OVRequestItem {
70 uint32_t lltask_count;
71#if HAVE_OPENVINO2
72 ov_infer_request_t *infer_request;
73 ov_callback_t callback;
74#else
75 ie_complete_call_back_t callback;
76 ie_infer_request_t *infer_request;
77#endif
79
80#define APPEND_STRING(generated_string, iterate_string) \
81 generated_string = generated_string ? av_asprintf("%s %s", generated_string, iterate_string) : \
82 av_asprintf("%s", iterate_string);
83
84#define OFFSET(x) offsetof(OVOptions, x)
85#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
87 { "input_resizable", "can input be resizable or not", OFFSET(input_resizable), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
88 { "layout", "input layout of model", OFFSET(layout), AV_OPT_TYPE_INT, { .i64 = DL_NONE}, DL_NONE, DL_NHWC, FLAGS, .unit = "layout" },
89 { "none", "none", 0, AV_OPT_TYPE_CONST, { .i64 = DL_NONE }, 0, 0, FLAGS, .unit = "layout"},
90 { "nchw", "nchw", 0, AV_OPT_TYPE_CONST, { .i64 = DL_NCHW }, 0, 0, FLAGS, .unit = "layout"},
91 { "nhwc", "nhwc", 0, AV_OPT_TYPE_CONST, { .i64 = DL_NHWC }, 0, 0, FLAGS, .unit = "layout"},
92 { "scale", "Add scale preprocess operation. Divide each element of input by specified value.", OFFSET(scale), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, INT_MIN, INT_MAX, FLAGS},
93 { "mean", "Add mean preprocess operation. Subtract specified value from each element of input.", OFFSET(mean), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, INT_MIN, INT_MAX, FLAGS},
94 { NULL }
95};
96
97#if HAVE_OPENVINO2
98static const struct {
99 ov_status_e status;
100 int av_err;
101 const char *desc;
102} ov2_errors[] = {
103 { OK, 0, "success" },
104 { GENERAL_ERROR, AVERROR_EXTERNAL, "general error" },
105 { NOT_IMPLEMENTED, AVERROR(ENOSYS), "not implemented" },
106 { NETWORK_NOT_LOADED, AVERROR_EXTERNAL, "network not loaded" },
107 { PARAMETER_MISMATCH, AVERROR(EINVAL), "parameter mismatch" },
108 { NOT_FOUND, AVERROR_EXTERNAL, "not found" },
109 { OUT_OF_BOUNDS, AVERROR(EOVERFLOW), "out of bounds" },
110 { UNEXPECTED, AVERROR_EXTERNAL, "unexpected" },
111 { REQUEST_BUSY, AVERROR(EBUSY), "request busy" },
112 { RESULT_NOT_READY, AVERROR(EBUSY), "result not ready" },
113 { NOT_ALLOCATED, AVERROR(ENODATA), "not allocated" },
114 { INFER_NOT_STARTED, AVERROR_EXTERNAL, "infer not started" },
115 { NETWORK_NOT_READ, AVERROR_EXTERNAL, "network not read" },
116 { INFER_CANCELLED, AVERROR(ECANCELED), "infer cancelled" },
117 { INVALID_C_PARAM, AVERROR(EINVAL), "invalid C parameter" },
118 { UNKNOWN_C_ERROR, AVERROR_UNKNOWN, "unknown C error" },
119 { NOT_IMPLEMENT_C_METHOD, AVERROR(ENOSYS), "not implement C method" },
120 { UNKNOW_EXCEPTION, AVERROR_UNKNOWN, "unknown exception" },
121};
122
123static int ov2_map_error(ov_status_e status, const char **desc)
124{
125 int i;
126 for (i = 0; i < FF_ARRAY_ELEMS(ov2_errors); i++) {
127 if (ov2_errors[i].status == status) {
128 if (desc)
129 *desc = ov2_errors[i].desc;
130 return ov2_errors[i].av_err;
131 }
132 }
133 if (desc)
134 *desc = "unknown error";
135 return AVERROR_UNKNOWN;
136}
137#endif
138
139#if HAVE_OPENVINO2
140static DNNDataType precision_to_datatype(ov_element_type_e precision)
141#else
142static DNNDataType precision_to_datatype(precision_e precision)
143#endif
144{
145 switch (precision)
146 {
147#if HAVE_OPENVINO2
148 case F32:
149#else
150 case FP32:
151#endif
152 return DNN_FLOAT;
153 case U8:
154 return DNN_UINT8;
155 default:
156 av_assert0(!"not supported yet.");
157 return DNN_FLOAT;
158 }
159}
160
162{
163 switch (dt)
164 {
165 case DNN_FLOAT:
166 return sizeof(float);
167 case DNN_UINT8:
168 return sizeof(uint8_t);
169 default:
170 av_assert0(!"not supported yet.");
171 return 1;
172 }
173}
174
175static int fill_model_input_ov(OVModel *ov_model, OVRequestItem *request)
176{
177 DNNData input;
178 LastLevelTaskItem *lltask;
179 TaskItem *task;
180 DnnContext *ctx = ov_model->ctx;
181#if HAVE_OPENVINO2
182 int64_t* dims;
183 ov_status_e status;
184 ov_tensor_t* tensor = NULL;
185 ov_shape_t input_shape = {0};
186 ov_element_type_e precision;
187 char *port_name;
188#else
189 dimensions_t dims;
190 precision_e precision;
191 ie_blob_buffer_t blob_buffer;
192 IEStatusCode status;
193 ie_blob_t *input_blob = NULL;
194#endif
195
196 memset(&input, 0, sizeof(input));
197 lltask = ff_queue_peek_front(ov_model->lltask_queue);
198 av_assert0(lltask);
199 task = lltask->task;
200
201#if HAVE_OPENVINO2
202 if (ov_model->input_port) {
203 ov_output_const_port_free(ov_model->input_port);
204 ov_model->input_port = NULL;
205 }
206 if (task->input_name)
207 status = ov_model_const_input_by_name(ov_model->ov_model, task->input_name, &ov_model->input_port);
208 else
209 status = ov_model_const_input(ov_model->ov_model, &ov_model->input_port);
210 if (status != OK) {
211 av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n");
212 return ov2_map_error(status, NULL);
213 }
214 status = ov_port_get_any_name(ov_model->input_port, &port_name);
215 if (status != OK) {
216 av_log(ctx, AV_LOG_ERROR, "Failed to get input port name.\n");
217 return ov2_map_error(status, NULL);
218 }
219 av_log(ctx, AV_LOG_VERBOSE, "OpenVINO model input: %s\n", port_name);
220 ov_free(port_name);
221 port_name = NULL;
222
223 status = ov_const_port_get_shape(ov_model->input_port, &input_shape);
224 if (status != OK) {
225 av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n");
226 return ov2_map_error(status, NULL);
227 }
228 dims = input_shape.dims;
229 status = ov_port_get_element_type(ov_model->input_port, &precision);
230 if (status != OK) {
231 av_log(ctx, AV_LOG_ERROR, "Failed to get input port data type.\n");
232 ov_shape_free(&input_shape);
233 return ov2_map_error(status, NULL);
234 }
235 for (int i = 0; i < input_shape.rank; i++)
236 input.dims[i] = dims[i];
237 input.layout = DL_NHWC;
238 input.dt = precision_to_datatype(precision);
239#else
240 status = ie_infer_request_get_blob(request->infer_request, task->input_name, &input_blob);
241 if (status != OK) {
242 av_log(ctx, AV_LOG_ERROR, "Failed to get input blob with name %s\n", task->input_name);
243 return DNN_GENERIC_ERROR;
244 }
245
246 status |= ie_blob_get_dims(input_blob, &dims);
247 status |= ie_blob_get_precision(input_blob, &precision);
248 if (status != OK) {
249 ie_blob_free(&input_blob);
250 av_log(ctx, AV_LOG_ERROR, "Failed to get input blob dims/precision\n");
251 return DNN_GENERIC_ERROR;
252 }
253
254 status = ie_blob_get_buffer(input_blob, &blob_buffer);
255 if (status != OK) {
256 ie_blob_free(&input_blob);
257 av_log(ctx, AV_LOG_ERROR, "Failed to get input blob buffer\n");
258 return DNN_GENERIC_ERROR;
259 }
260 for (int i = 0; i < input_shape.rank; i++)
261 input.dims[i] = dims[i];
262 input.layout = DL_NCHW;
263 input.data = blob_buffer.buffer;
264 input.dt = precision_to_datatype(precision);
265#endif
266 // all models in openvino open model zoo use BGR as input,
267 // change to be an option when necessary.
268 input.order = DCO_BGR;
269 // We use preprocess_steps to scale input data, so disable scale and mean here.
270 input.scale = 1;
271 input.mean = 0;
272
273 for (int i = 0; i < ctx->batch_size; ++i) {
274 lltask = ff_queue_pop_front(ov_model->lltask_queue);
275 if (!lltask) {
276 break;
277 }
278 request->lltasks[i] = lltask;
279 request->lltask_count = i + 1;
280 task = lltask->task;
281#if HAVE_OPENVINO2
282 if (tensor)
283 ov_tensor_free(tensor);
284 status = ov_tensor_create(precision, input_shape, &tensor);
285 ov_shape_free(&input_shape);
286 if (status != OK) {
287 av_log(ctx, AV_LOG_ERROR, "Failed to create tensor from host prt.\n");
288 return ov2_map_error(status, NULL);
289 }
290 status = ov_tensor_data(tensor, &input.data);
291 if (status != OK) {
292 av_log(ctx, AV_LOG_ERROR, "Failed to get input data.\n");
293 return ov2_map_error(status, NULL);
294 }
295 status = ov_infer_request_set_input_tensor(request->infer_request, tensor);
296 if (status != OK) {
297 av_log(ctx, AV_LOG_ERROR, "Failed to Set an input tensor for the model.\n");
298 return ov2_map_error(status, NULL);
299 }
300#endif
301 switch (ov_model->model.func_type) {
303 if (task->do_ioproc) {
304 if (ov_model->model.frame_pre_proc != NULL) {
305 ov_model->model.frame_pre_proc(task->in_frame, &input, ov_model->model.filter_ctx);
306 } else {
307 ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
308 }
309 }
310 break;
312 ff_frame_to_dnn_detect(task->in_frame, &input, ctx);
313 break;
315 ff_frame_to_dnn_classify(task->in_frame, &input, lltask->bbox_index, ctx);
316 break;
317 default:
318 av_assert0(!"should not reach here");
319 break;
320 }
321 input.data = (uint8_t *)input.data +
322 input.dims[1] * input.dims[2] * input.dims[3] * get_datatype_size(input.dt);
323 }
324#if HAVE_OPENVINO2
325 ov_tensor_free(tensor);
326#else
327 ie_blob_free(&input_blob);
328#endif
329
330 return 0;
331}
332
333static void infer_completion_callback(void *args)
334{
335 OVRequestItem *request = args;
336 LastLevelTaskItem *lltask = request->lltasks[0];
337 TaskItem *task = lltask->task;
338 OVModel *ov_model = task->model;
339 SafeQueue *requestq = ov_model->request_queue;
341 DnnContext *ctx = ov_model->ctx;
342#if HAVE_OPENVINO2
343 size_t* dims;
344 ov_status_e status;
345 ov_tensor_t *output_tensor;
346 ov_shape_t output_shape = {0};
347 ov_element_type_e precision;
348
349 outputs = av_calloc(ov_model->nb_outputs, sizeof(*outputs));
350 if (!outputs) {
351 av_log(ctx, AV_LOG_ERROR, "Failed to alloc outputs.");
352 return;
353 }
354
355 for (int i = 0; i < ov_model->nb_outputs; i++) {
356 status = ov_infer_request_get_tensor_by_const_port(request->infer_request,
357 ov_model->output_ports[i],
358 &output_tensor);
359 if (status != OK) {
361 "Failed to get output tensor.");
362 goto end;
363 }
364
365 status = ov_tensor_data(output_tensor, &outputs[i].data);
366 if (status != OK) {
368 "Failed to get output data.");
369 goto end;
370 }
371
372 status = ov_tensor_get_shape(output_tensor, &output_shape);
373 if (status != OK) {
374 av_log(ctx, AV_LOG_ERROR, "Failed to get output port shape.\n");
375 goto end;
376 }
377 dims = output_shape.dims;
378
379 status = ov_port_get_element_type(ov_model->output_ports[i], &precision);
380 if (status != OK) {
381 av_log(ctx, AV_LOG_ERROR, "Failed to get output port data type.\n");
382 goto end;
383 }
384 outputs[i].dt = precision_to_datatype(precision);
385 outputs[i].layout = DL_NCHW;
386 outputs[i].dims[0] = 1;
387 outputs[i].dims[1] = output_shape.rank > 2 ? dims[output_shape.rank - 3] : 1;
388 outputs[i].dims[2] = output_shape.rank > 1 ? dims[output_shape.rank - 2] : 1;
389 outputs[i].dims[3] = output_shape.rank > 0 ? dims[output_shape.rank - 1] : 1;
390 av_assert0(request->lltask_count <= dims[0]);
391 outputs[i].layout = ctx->ov_option.layout;
392 outputs[i].scale = ctx->ov_option.scale;
393 outputs[i].mean = ctx->ov_option.mean;
394 ov_shape_free(&output_shape);
395 ov_tensor_free(output_tensor);
396 output_tensor = NULL;
397 }
398#else
399 IEStatusCode status;
400 dimensions_t dims;
401 ie_blob_t *output_blob = NULL;
402 ie_blob_buffer_t blob_buffer;
403 precision_e precision;
404 DNNData output;
405 status = ie_infer_request_get_blob(request->infer_request, task->output_names[0], &output_blob);
406 if (status != OK) {
408 "output \"%s\" may not correct, all output(s) are: \"%s\"\n",
409 task->output_names[0], ov_model->all_output_names);
410 return;
411 }
412
413 status = ie_blob_get_buffer(output_blob, &blob_buffer);
414 if (status != OK) {
415 ie_blob_free(&output_blob);
416 av_log(ctx, AV_LOG_ERROR, "Failed to access output memory\n");
417 return;
418 }
419
420 status |= ie_blob_get_dims(output_blob, &dims);
421 status |= ie_blob_get_precision(output_blob, &precision);
422 if (status != OK) {
423 ie_blob_free(&output_blob);
424 av_log(ctx, AV_LOG_ERROR, "Failed to get dims or precision of output\n");
425 return;
426 }
427 output.data = blob_buffer.buffer;
428 output.layout = DL_NCHW;
429 for (int i = 0; i < 4; i++)
430 output.dims[i] = dims.dims[i];
431 av_assert0(request->lltask_count <= dims.dims[0]);
432 output.dt = precision_to_datatype(precision);
433 output.layout = ctx->ov_option.layout;
434 output.scale = ctx->ov_option.scale;
435 output.mean = ctx->ov_option.mean;
436 outputs = &output;
437#endif
438
439 av_assert0(request->lltask_count >= 1);
440 for (int i = 0; i < request->lltask_count; ++i) {
441 task = request->lltasks[i]->task;
442
443 switch (ov_model->model.func_type) {
445 if (task->do_ioproc) {
446 if (ov_model->model.frame_post_proc != NULL) {
447 ov_model->model.frame_post_proc(task->out_frame, outputs, ov_model->model.filter_ctx);
448 } else {
450 }
451 } else {
452 task->out_frame->width =
454 task->out_frame->height =
456 }
457 break;
459 if (!ov_model->model.detect_post_proc) {
460 av_log(ctx, AV_LOG_ERROR, "detect filter needs to provide post proc\n");
461 goto end;
462 }
463 ov_model->model.detect_post_proc(task->in_frame, outputs,
464 ov_model->nb_outputs,
465 ov_model->model.filter_ctx);
466 break;
468 if (!ov_model->model.classify_post_proc) {
469 av_log(ctx, AV_LOG_ERROR, "classify filter needs to provide post proc\n");
470 goto end;
471 }
472 for (int output_i = 0; output_i < ov_model->nb_outputs; output_i++)
473 ov_model->model.classify_post_proc(task->in_frame, outputs,
474 request->lltasks[i]->bbox_index,
475 ov_model->model.filter_ctx);
476 break;
477 default:
478 av_assert0(!"should not reach here");
479 break;
480 }
481
482 task->inference_done++;
483 av_freep(&request->lltasks[i]);
484 for (int i = 0; i < ov_model->nb_outputs; i++)
485 outputs[i].data = (uint8_t *)outputs[i].data +
486 outputs[i].dims[1] * outputs[i].dims[2] * outputs[i].dims[3] *
488 }
489end:
490#if HAVE_OPENVINO2
492 ov_shape_free(&output_shape);
493 if (output_tensor)
494 ov_tensor_free(output_tensor);
495#else
496 ie_blob_free(&output_blob);
497#endif
498 request->lltask_count = 0;
499 if (ff_safe_queue_push_back(requestq, request) < 0) {
500#if HAVE_OPENVINO2
501 ov_infer_request_free(request->infer_request);
502#else
503 ie_infer_request_free(&request->infer_request);
504#endif
505 av_freep(&request);
506 av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
507 return;
508 }
509}
510
511static void dnn_free_model_ov(DNNModel **model)
512{
513 OVModel *ov_model;
514
515 if (!model || !*model)
516 return;
517
518 ov_model = (OVModel *)(*model);
519 ff_dnn_wait_requests(ov_model->request_queue, ov_model->ctx->nireq);
520 while (ff_safe_queue_size(ov_model->request_queue) != 0) {
522 if (item && item->infer_request) {
523#if HAVE_OPENVINO2
524 ov_infer_request_free(item->infer_request);
525#else
526 ie_infer_request_free(&item->infer_request);
527#endif
528 }
529 av_freep(&item->lltasks);
530 av_freep(&item);
531 }
533
534 while (ff_queue_size(ov_model->lltask_queue) != 0) {
536 av_freep(&item);
537 }
539
540 while (ff_queue_size(ov_model->task_queue) != 0) {
541 TaskItem *item = ff_queue_pop_front(ov_model->task_queue);
542 av_frame_free(&item->in_frame);
543 av_frame_free(&item->out_frame);
544 av_freep(&item);
545 }
546 ff_queue_destroy(ov_model->task_queue);
547#if HAVE_OPENVINO2
548 if (ov_model->input_port)
549 ov_output_const_port_free(ov_model->input_port);
550 for (int i = 0; i < ov_model->nb_outputs; i++)
551 if (ov_model->output_ports[i])
552 ov_output_const_port_free(ov_model->output_ports[i]);
553 av_freep(&ov_model->output_ports);
554 if (ov_model->preprocess)
555 ov_preprocess_prepostprocessor_free(ov_model->preprocess);
556 if (ov_model->compiled_model)
557 ov_compiled_model_free(ov_model->compiled_model);
558 if (ov_model->ov_model)
559 ov_model_free(ov_model->ov_model);
560 if (ov_model->core)
561 ov_core_free(ov_model->core);
562#else
563 if (ov_model->exe_network)
564 ie_exec_network_free(&ov_model->exe_network);
565 if (ov_model->network)
566 ie_network_free(&ov_model->network);
567 if (ov_model->core)
568 ie_core_free(&ov_model->core);
569 av_free(ov_model->all_output_names);
570 av_free(ov_model->all_input_names);
571#endif
572 av_freep(&ov_model);
573 *model = NULL;
574}
575
576
577static int init_model_ov(OVModel *ov_model, const char *input_name, const char **output_names, int nb_outputs)
578{
579 int ret = 0;
580 DnnContext *ctx = ov_model->ctx;
581#if HAVE_OPENVINO2
582 ov_status_e status;
583 ov_preprocess_input_tensor_info_t* input_tensor_info = NULL;
584 ov_preprocess_output_tensor_info_t* output_tensor_info = NULL;
585 ov_preprocess_input_model_info_t* input_model_info = NULL;
586 ov_model_t *tmp_ov_model;
587 ov_layout_t* NHWC_layout = NULL;
588 ov_layout_t* NCHW_layout = NULL;
589 const char* NHWC_desc = "NHWC";
590 const char* NCHW_desc = "NCHW";
591 const char* device = ctx->device ? ctx->device : "CPU";
592#else
593 IEStatusCode status;
594 ie_available_devices_t a_dev;
595 ie_config_t config = {NULL, NULL, NULL};
596 char *all_dev_names = NULL;
597#endif
598 // We scale pixel by default when do frame processing.
599 if (fabsf(ctx->ov_option.scale) < 1e-6f)
600 ctx->ov_option.scale = ov_model->model.func_type == DFT_PROCESS_FRAME ? 255 : 1;
601 // batch size
602 if (ctx->batch_size <= 0) {
603 ctx->batch_size = 1;
604 }
605#if HAVE_OPENVINO2
606 if (ctx->batch_size > 1) {
607 avpriv_report_missing_feature(ctx, "Do not support batch_size > 1 for now,"
608 "change batch_size to 1.\n");
609 ctx->batch_size = 1;
610 }
611
612 status = ov_preprocess_prepostprocessor_create(ov_model->ov_model, &ov_model->preprocess);
613 if (status != OK) {
614 av_log(ctx, AV_LOG_ERROR, "Failed to create preprocess for ov_model.\n");
615 ret = ov2_map_error(status, NULL);
616 goto err;
617 }
618
619 if (input_name)
620 status = ov_preprocess_prepostprocessor_get_input_info_by_name(ov_model->preprocess, input_name, &ov_model->input_info);
621 else
622 status = ov_preprocess_prepostprocessor_get_input_info(ov_model->preprocess, &ov_model->input_info);
623 if (status != OK) {
624 av_log(ctx, AV_LOG_ERROR, "Failed to get input info from preprocess.\n");
625 ret = ov2_map_error(status, NULL);
626 goto err;
627 }
628
629 status = ov_preprocess_input_info_get_tensor_info(ov_model->input_info, &input_tensor_info);
630 if (status != OK) {
631 av_log(ctx, AV_LOG_ERROR, "Failed to get tensor info from input.\n");
632 ret = ov2_map_error(status, NULL);
633 goto err;
634 }
635
636 //set input layout
637 status = ov_layout_create(NHWC_desc, &NHWC_layout);
638 status |= ov_layout_create(NCHW_desc, &NCHW_layout);
639 if (status != OK) {
640 av_log(ctx, AV_LOG_ERROR, "Failed to create layout for input.\n");
641 ret = ov2_map_error(status, NULL);
642 goto err;
643 }
644
645 status = ov_preprocess_input_tensor_info_set_layout(input_tensor_info, NHWC_layout);
646 if (status != OK) {
647 av_log(ctx, AV_LOG_ERROR, "Failed to set input tensor layout\n");
648 ret = ov2_map_error(status, NULL);
649 goto err;
650 }
651
652 status = ov_preprocess_input_info_get_model_info(ov_model->input_info, &input_model_info);
653 if (status != OK) {
654 av_log(ctx, AV_LOG_ERROR, "Failed to get input model info\n");
655 ret = ov2_map_error(status, NULL);
656 goto err;
657 }
658 if (ctx->ov_option.layout == DL_NCHW)
659 status = ov_preprocess_input_model_info_set_layout(input_model_info, NCHW_layout);
660 else if (ctx->ov_option.layout == DL_NHWC)
661 status = ov_preprocess_input_model_info_set_layout(input_model_info, NHWC_layout);
662 if (status != OK) {
663 av_log(ctx, AV_LOG_ERROR, "Failed to get set input model layout\n");
664 ret = ov2_map_error(status, NULL);
665 goto err;
666 }
667
668 status = ov_preprocess_input_tensor_info_set_element_type(input_tensor_info, U8);
669 if (status != OK) {
670 av_log(ctx, AV_LOG_ERROR, "Failed to set input element type\n");
671 ret = ov2_map_error(status, NULL);
672 goto err;
673 }
674
675 if (!nb_outputs) {
676 size_t output_size;
677 status = ov_model_outputs_size(ov_model->ov_model, &output_size);
678 if (status != OK) {
679 av_log(ctx, AV_LOG_ERROR, "Failed to get output size.\n");
680 ret = ov2_map_error(status, NULL);
681 goto err;
682 }
683 nb_outputs = output_size;
684 }
685 ov_model->nb_outputs = nb_outputs;
686 for (int i = 0; i < nb_outputs; i++) {
687 if (output_names)
688 status = ov_preprocess_prepostprocessor_get_output_info_by_name(
689 ov_model->preprocess, output_names[i], &ov_model->output_info);
690 else
691 status = ov_preprocess_prepostprocessor_get_output_info_by_index(
692 ov_model->preprocess, i, &ov_model->output_info);
693 if (status != OK) {
694 av_log(ctx, AV_LOG_ERROR, "Failed to get output info from preprocess.\n");
695 ret = ov2_map_error(status, NULL);
696 goto err;
697 }
698 status |= ov_preprocess_output_info_get_tensor_info(ov_model->output_info, &output_tensor_info);
699 if (status != OK) {
700 av_log(ctx, AV_LOG_ERROR, "Failed to get tensor info from input/output.\n");
701 ret = ov2_map_error(status, NULL);
702 goto err;
703 }
704 if (ov_model->model.func_type != DFT_PROCESS_FRAME)
705 status |= ov_preprocess_output_set_element_type(output_tensor_info, F32);
706 else if (fabsf(ctx->ov_option.scale - 1) > 1e-6f || fabsf(ctx->ov_option.mean) > 1e-6f)
707 status |= ov_preprocess_output_set_element_type(output_tensor_info, F32);
708 else
709 status |= ov_preprocess_output_set_element_type(output_tensor_info, U8);
710 if (status != OK) {
711 av_log(ctx, AV_LOG_ERROR, "Failed to set output element type\n");
712 ret = ov2_map_error(status, NULL);
713 goto err;
714 }
715 ov_preprocess_output_tensor_info_free(output_tensor_info);
716 output_tensor_info = NULL;
717 ov_preprocess_output_info_free(ov_model->output_info);
718 ov_model->output_info = NULL;
719 }
720 // set preprocess steps.
721 if (fabsf(ctx->ov_option.scale - 1) > 1e-6f || fabsf(ctx->ov_option.mean) > 1e-6f) {
722 ov_preprocess_preprocess_steps_t* input_process_steps = NULL;
723 status = ov_preprocess_input_info_get_preprocess_steps(ov_model->input_info, &input_process_steps);
724 if (status != OK) {
725 av_log(ctx, AV_LOG_ERROR, "Failed to get preprocess steps\n");
726 ret = ov2_map_error(status, NULL);
727 goto err;
728 }
729 status = ov_preprocess_preprocess_steps_convert_element_type(input_process_steps, F32);
730 status |= ov_preprocess_preprocess_steps_mean(input_process_steps, ctx->ov_option.mean);
731 status |= ov_preprocess_preprocess_steps_scale(input_process_steps, ctx->ov_option.scale);
732 if (status != OK) {
733 av_log(ctx, AV_LOG_ERROR, "Failed to set preprocess steps\n");
734 ov_preprocess_preprocess_steps_free(input_process_steps);
735 input_process_steps = NULL;
736 ret = ov2_map_error(status, NULL);
737 goto err;
738 }
739 ov_preprocess_preprocess_steps_free(input_process_steps);
740 input_process_steps = NULL;
741 }
742 ov_preprocess_input_tensor_info_free(input_tensor_info);
743 input_tensor_info = NULL;
744 ov_preprocess_input_info_free(ov_model->input_info);
745 ov_model->input_info = NULL;
746
747 //update model
748 if(ov_model->ov_model)
749 tmp_ov_model = ov_model->ov_model;
750 status = ov_preprocess_prepostprocessor_build(ov_model->preprocess, &ov_model->ov_model);
751 if (status != OK) {
752 av_log(ctx, AV_LOG_ERROR, "Failed to update OV model\n");
753 ov_model_free(tmp_ov_model);
754 tmp_ov_model = NULL;
755 ret = ov2_map_error(status, NULL);
756 goto err;
757 }
758 ov_model_free(tmp_ov_model);
759
760 //update output_port
761 if (!ov_model->output_ports) {
762 ov_model->output_ports = av_calloc(nb_outputs, sizeof(*ov_model->output_ports));
763 if (!ov_model->output_ports) {
764 ret = AVERROR(ENOMEM);
765 goto err;
766 }
767 } else
768 for (int i = 0; i < nb_outputs; i++) {
769 ov_output_const_port_free(ov_model->output_ports[i]);
770 ov_model->output_ports[i] = NULL;
771 }
772
773 for (int i = 0; i < nb_outputs; i++) {
774 char *port_name;
775 if (output_names)
776 status = ov_model_const_output_by_name(ov_model->ov_model, output_names[i],
777 &ov_model->output_ports[i]);
778 else
779 status = ov_model_const_output_by_index(ov_model->ov_model, i,
780 &ov_model->output_ports[i]);
781 if (status != OK) {
782 av_log(ctx, AV_LOG_ERROR, "Failed to get output port %s.\n", output_names[i]);
783 goto err;
784 }
785 status = ov_port_get_any_name(ov_model->output_ports[i], &port_name);
786 if (status != OK) {
787 av_log(ctx, AV_LOG_ERROR, "Failed to get output port name.\n");
788 goto err;
789 }
790 av_log(ctx, AV_LOG_VERBOSE, "OpenVINO model outputs: %s\n", port_name);
791 ov_free(port_name);
792 port_name = NULL;
793 }
794 //compile network
795 status = ov_core_compile_model(ov_model->core, ov_model->ov_model, device, 0, &ov_model->compiled_model);
796 if (status != OK) {
797 ret = ov2_map_error(status, NULL);
798 goto err;
799 }
800 ov_preprocess_input_model_info_free(input_model_info);
801 input_model_info = NULL;
802 ov_layout_free(NCHW_layout);
803 ov_layout_free(NHWC_layout);
804#else
805 if (ctx->batch_size > 1) {
806 input_shapes_t input_shapes;
807 status = ie_network_get_input_shapes(ov_model->network, &input_shapes);
808 if (status != OK) {
809 ret = DNN_GENERIC_ERROR;
810 goto err;
811 }
812 for (int i = 0; i < input_shapes.shape_num; i++)
813 input_shapes.shapes[i].shape.dims[0] = ctx->batch_size;
814 status = ie_network_reshape(ov_model->network, input_shapes);
815 ie_network_input_shapes_free(&input_shapes);
816 if (status != OK) {
817 ret = DNN_GENERIC_ERROR;
818 goto err;
819 }
820 }
821
822 // The order of dims in the openvino is fixed and it is always NCHW for 4-D data.
823 // while we pass NHWC data from FFmpeg to openvino
824 status = ie_network_set_input_layout(ov_model->network, input_name, NHWC);
825 if (status != OK) {
826 if (status == NOT_FOUND) {
827 av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, failed to set input layout as NHWC, "\
828 "all input(s) are: \"%s\"\n", input_name, ov_model->all_input_names);
829 } else{
830 av_log(ctx, AV_LOG_ERROR, "Failed to set layout as NHWC for input %s\n", input_name);
831 }
832 ret = DNN_GENERIC_ERROR;
833 goto err;
834 }
835 status = ie_network_set_output_layout(ov_model->network, output_name, NHWC);
836 if (status != OK) {
837 if (status == NOT_FOUND) {
838 av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, failed to set output layout as NHWC, "\
839 "all output(s) are: \"%s\"\n", output_name, ov_model->all_output_names);
840 } else{
841 av_log(ctx, AV_LOG_ERROR, "Failed to set layout as NHWC for output %s\n", output_name);
842 }
843 ret = DNN_GENERIC_ERROR;
844 goto err;
845 }
846 ov_model->nb_outputs = 1;
847
848 // all models in openvino open model zoo use BGR with range [0.0f, 255.0f] as input,
849 // we don't have a AVPixelFormat to describe it, so we'll use AV_PIX_FMT_BGR24 and
850 // ask openvino to do the conversion internally.
851 // the current supported SR model (frame processing) is generated from tensorflow model,
852 // and its input is Y channel as float with range [0.0f, 1.0f], so do not set for this case.
853 // TODO: we need to get a final clear&general solution with all backends/formats considered.
854 if (ov_model->model->func_type != DFT_PROCESS_FRAME) {
855 status = ie_network_set_input_precision(ov_model->network, input_name, U8);
856 if (status != OK) {
857 av_log(ctx, AV_LOG_ERROR, "Failed to set input precision as U8 for %s\n", input_name);
858 ret = DNN_GENERIC_ERROR;
859 goto err;
860 }
861 }
862
863 status = ie_core_load_network(ov_model->core, ov_model->network, ctx->device, &config, &ov_model->exe_network);
864 if (status != OK) {
865 av_log(ctx, AV_LOG_ERROR, "Failed to load OpenVINO model network\n");
866 status = ie_core_get_available_devices(ov_model->core, &a_dev);
867 if (status != OK) {
868 av_log(ctx, AV_LOG_ERROR, "Failed to get available devices\n");
869 ret = DNN_GENERIC_ERROR;
870 goto err;
871 }
872 for (int i = 0; i < a_dev.num_devices; i++) {
873 APPEND_STRING(all_dev_names, a_dev.devices[i])
874 }
875 av_log(ctx, AV_LOG_ERROR,"device %s may not be supported, all available devices are: \"%s\"\n",
876 ctx->device, all_dev_names);
877 ret = AVERROR(ENODEV);
878 goto err;
879 }
880#endif
881 // create infer_requests for async execution
882 if (ctx->nireq <= 0) {
883 // the default value is a rough estimation
884 ctx->nireq = av_cpu_count() / 2 + 1;
885 }
886
888 if (!ov_model->request_queue) {
889 ret = AVERROR(ENOMEM);
890 goto err;
891 }
892
893 for (int i = 0; i < ctx->nireq; i++) {
894 OVRequestItem *item = av_mallocz(sizeof(*item));
895 if (!item) {
896 ret = AVERROR(ENOMEM);
897 goto err;
898 }
899
900#if HAVE_OPENVINO2
901 item->callback.callback_func = infer_completion_callback;
902#else
903 item->callback.completeCallBackFunc = infer_completion_callback;
904#endif
905 item->callback.args = item;
906 if (ff_safe_queue_push_back(ov_model->request_queue, item) < 0) {
907 av_freep(&item);
908 ret = AVERROR(ENOMEM);
909 goto err;
910 }
911
912#if HAVE_OPENVINO2
913 status = ov_compiled_model_create_infer_request(ov_model->compiled_model, &item->infer_request);
914 if (status != OK) {
915 av_log(ctx, AV_LOG_ERROR, "Failed to Creates an inference request object.\n");
916 goto err;
917 }
918#else
919 status = ie_exec_network_create_infer_request(ov_model->exe_network, &item->infer_request);
920 if (status != OK) {
921 ret = DNN_GENERIC_ERROR;
922 goto err;
923 }
924#endif
925
926 item->lltasks = av_malloc_array(ctx->batch_size, sizeof(*item->lltasks));
927 if (!item->lltasks) {
928 ret = AVERROR(ENOMEM);
929 goto err;
930 }
931 item->lltask_count = 0;
932 }
933
934 ov_model->task_queue = ff_queue_create();
935 if (!ov_model->task_queue) {
936 ret = AVERROR(ENOMEM);
937 goto err;
938 }
939
940 ov_model->lltask_queue = ff_queue_create();
941 if (!ov_model->lltask_queue) {
942 ret = AVERROR(ENOMEM);
943 goto err;
944 }
945
946 return 0;
947
948err:
949#if HAVE_OPENVINO2
950 if (output_tensor_info)
951 ov_preprocess_output_tensor_info_free(output_tensor_info);
952 if (ov_model->output_info)
953 ov_preprocess_output_info_free(ov_model->output_info);
954 if (NCHW_layout)
955 ov_layout_free(NCHW_layout);
956 if (NHWC_layout)
957 ov_layout_free(NHWC_layout);
958 if (input_model_info)
959 ov_preprocess_input_model_info_free(input_model_info);
960#endif
961 return ret;
962}
963
964static int execute_model_ov(OVRequestItem *request, Queue *inferenceq)
965{
966#if HAVE_OPENVINO2
967 ov_status_e status;
968#else
969 IEStatusCode status;
970#endif
971 LastLevelTaskItem *lltask;
972 int ret = 0;
973 TaskItem *task;
975 OVModel *ov_model;
976
977 if (ff_queue_size(inferenceq) == 0) {
978#if HAVE_OPENVINO2
979 ov_infer_request_free(request->infer_request);
980#else
981 ie_infer_request_free(&request->infer_request);
982#endif
983 av_freep(&request);
984 return 0;
985 }
986
987 lltask = ff_queue_peek_front(inferenceq);
988 task = lltask->task;
989 ov_model = task->model;
990 ctx = ov_model->ctx;
991
992 ret = fill_model_input_ov(ov_model, request);
993 if (ret != 0) {
994 goto err;
995 }
996
997#if HAVE_OPENVINO2
998 if (task->async) {
999 status = ov_infer_request_set_callback(request->infer_request, &request->callback);
1000 if (status != OK) {
1001 av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n");
1002 ret = ov2_map_error(status, NULL);
1003 goto err;
1004 }
1005
1006 status = ov_infer_request_start_async(request->infer_request);
1007 if (status != OK) {
1008 av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n");
1009 ret = ov2_map_error(status, NULL);
1010 goto err;
1011 }
1012 return 0;
1013 } else {
1014 status = ov_infer_request_infer(request->infer_request);
1015 if (status != OK) {
1016 av_log(NULL, AV_LOG_ERROR, "Failed to start synchronous model inference for OV2\n");
1017 ret = ov2_map_error(status, NULL);
1018 goto err;
1019 }
1021 return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
1022 }
1023#else
1024 if (task->async) {
1025 status = ie_infer_set_completion_callback(request->infer_request, &request->callback);
1026 if (status != OK) {
1027 av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n");
1028 ret = DNN_GENERIC_ERROR;
1029 goto err;
1030 }
1031 status = ie_infer_request_infer_async(request->infer_request);
1032 if (status != OK) {
1033 av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n");
1034 ret = DNN_GENERIC_ERROR;
1035 goto err;
1036 }
1037 return 0;
1038 } else {
1039 status = ie_infer_request_infer(request->infer_request);
1040 if (status != OK) {
1041 av_log(ctx, AV_LOG_ERROR, "Failed to start synchronous model inference\n");
1042 ret = DNN_GENERIC_ERROR;
1043 goto err;
1044 }
1046 return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
1047 }
1048#endif
1049err:
1050 if (ff_safe_queue_push_back(ov_model->request_queue, request) < 0) {
1051#if HAVE_OPENVINO2
1052 ov_infer_request_free(request->infer_request);
1053#else
1054 ie_infer_request_free(&request->infer_request);
1055#endif
1056 av_freep(&request);
1057 }
1058 return ret;
1059}
1060
1061static int get_input_ov(DNNModel *model, DNNData *input, const char *input_name)
1062{
1063 OVModel *ov_model = (OVModel *)model;
1064 DnnContext *ctx = ov_model->ctx;
1065 int input_resizable = ctx->ov_option.input_resizable;
1066
1067#if HAVE_OPENVINO2
1068 ov_shape_t input_shape = {0};
1069 ov_element_type_e precision;
1070 ov_status_e status;
1071 if (input_name)
1072 status = ov_model_const_input_by_name(ov_model->ov_model, input_name, &ov_model->input_port);
1073 else
1074 status = ov_model_const_input(ov_model->ov_model, &ov_model->input_port);
1075 if (status != OK) {
1076 av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n");
1077 return ov2_map_error(status, NULL);
1078 }
1079 status = ov_port_get_element_type(ov_model->input_port, &precision);
1080 if (status != OK) {
1081 av_log(ctx, AV_LOG_ERROR, "Failed to get input port data type.\n");
1082 return ov2_map_error(status, NULL);
1083 }
1084 status = ov_const_port_get_shape(ov_model->input_port, &input_shape);
1085 if (status != OK) {
1086 av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n");
1087 return ov2_map_error(status, NULL);
1088 }
1089 for (int i = 0; i < 4; i++)
1090 input->dims[i] = input_shape.dims[i];
1091
1092 if (ctx->ov_option.layout == DL_NONE) {
1093 if (input_shape.dims[1] <= 3)
1094 ctx->ov_option.layout = DL_NCHW;
1095 else
1096 ctx->ov_option.layout = DL_NHWC;
1097 }
1098 input->layout = ctx->ov_option.layout;
1099
1100 if (input_resizable) {
1101 input->dims[dnn_get_width_idx_by_layout(input->layout)] = -1;
1102 input->dims[dnn_get_height_idx_by_layout(input->layout)] = -1;
1103 }
1104
1105 input->dt = precision_to_datatype(precision);
1106 ov_shape_free(&input_shape);
1107 return 0;
1108#else
1109 char *model_input_name = NULL;
1110 IEStatusCode status;
1111 size_t model_input_count = 0;
1112 dimensions_t dims;
1113 precision_e precision;
1114 status = ie_network_get_inputs_number(ov_model->network, &model_input_count);
1115 if (status != OK) {
1116 av_log(ctx, AV_LOG_ERROR, "Failed to get input count\n");
1117 return DNN_GENERIC_ERROR;
1118 }
1119 for (size_t i = 0; i < model_input_count; i++) {
1120 status = ie_network_get_input_name(ov_model->network, i, &model_input_name);
1121 if (status != OK) {
1122 av_log(ctx, AV_LOG_ERROR, "Failed to get No.%d input's name\n", (int)i);
1123 return DNN_GENERIC_ERROR;
1124 }
1125 if (strcmp(model_input_name, input_name) == 0) {
1126 ie_network_name_free(&model_input_name);
1127 status |= ie_network_get_input_dims(ov_model->network, input_name, &dims);
1128 status |= ie_network_get_input_precision(ov_model->network, input_name, &precision);
1129 if (status != OK) {
1130 av_log(ctx, AV_LOG_ERROR, "Failed to get No.%d input's dims or precision\n", (int)i);
1131 return DNN_GENERIC_ERROR;
1132 }
1133
1134 for (int i = 0; i < 4; i++)
1135 input->dims[i] = input_shape.dims[i];
1136 if (input_resizable) {
1137 input->dims[dnn_get_width_idx_by_layout(input->layout)] = -1;
1138 input->dims[dnn_get_height_idx_by_layout(input->layout)] = -1;
1139 }
1140
1141 if (input_shape.dims[1] <= 3) // NCHW
1142 input->layout = DL_NCHW;
1143 else // NHWC
1144 input->layout = DL_NHWC;
1145
1146 input->dt = precision_to_datatype(precision);
1147 return 0;
1148 }
1149
1150 ie_network_name_free(&model_input_name);
1151 }
1152
1153 av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, all input(s) are: \"%s\"\n", input_name, ov_model->all_input_names);
1154 return AVERROR(EINVAL);
1155#endif
1156}
1157
1159{
1160 AVFrameSideData *sd;
1162 const AVDetectionBBox *bbox;
1163
1165 if (!sd) { // this frame has nothing detected
1166 return 0;
1167 }
1168
1169 if (!sd->size) {
1170 return 0;
1171 }
1172
1173 header = (const AVDetectionBBoxHeader *)sd->data;
1174 if (!header->nb_bboxes) {
1175 return 0;
1176 }
1177
1178 for (uint32_t i = 0; i < header->nb_bboxes; i++) {
1180 if (bbox->x < 0 || bbox->w < 0 || bbox->x + bbox->w >= frame->width) {
1181 return 0;
1182 }
1183 if (bbox->y < 0 || bbox->h < 0 || bbox->y + bbox->h >= frame->height) {
1184 return 0;
1185 }
1186
1188 return 0;
1189 }
1190 }
1191
1192 return 1;
1193}
1194
1195static int extract_lltask_from_task(DNNFunctionType func_type, TaskItem *task, Queue *lltask_queue, DNNExecBaseParams *exec_params)
1196{
1197 switch (func_type) {
1198 case DFT_PROCESS_FRAME:
1200 {
1201 LastLevelTaskItem *lltask = av_malloc(sizeof(*lltask));
1202 if (!lltask) {
1203 return AVERROR(ENOMEM);
1204 }
1205 task->inference_todo = 1;
1206 task->inference_done = 0;
1207 lltask->task = task;
1208 if (ff_queue_push_back(lltask_queue, lltask) < 0) {
1209 av_freep(&lltask);
1210 return AVERROR(ENOMEM);
1211 }
1212 return 0;
1213 }
1215 {
1217 AVFrame *frame = task->in_frame;
1218 AVFrameSideData *sd;
1220
1221 task->inference_todo = 0;
1222 task->inference_done = 0;
1223
1225 return 0;
1226 }
1227
1229 header = (const AVDetectionBBoxHeader *)sd->data;
1230
1231 for (uint32_t i = 0; i < header->nb_bboxes; i++) {
1232 LastLevelTaskItem *lltask;
1234
1235 if (params->target) {
1236 if (av_strncasecmp(bbox->detect_label, params->target, sizeof(bbox->detect_label)) != 0) {
1237 continue;
1238 }
1239 }
1240
1241 lltask = av_malloc(sizeof(*lltask));
1242 if (!lltask) {
1243 return AVERROR(ENOMEM);
1244 }
1245 task->inference_todo++;
1246 lltask->task = task;
1247 lltask->bbox_index = i;
1248 if (ff_queue_push_back(lltask_queue, lltask) < 0) {
1249 av_freep(&lltask);
1250 return AVERROR(ENOMEM);
1251 }
1252 }
1253 return 0;
1254 }
1255 default:
1256 av_assert0(!"should not reach here");
1257 return AVERROR(EINVAL);
1258 }
1259}
1260
1261static int get_output_ov(DNNModel *model, const char *input_name, int input_width, int input_height,
1262 const char *output_name, int *output_width, int *output_height)
1263{
1264#if HAVE_OPENVINO2
1265 ov_dimension_t dims[4] = {{1, 1}, {1, 1}, {input_height, input_height}, {input_width, input_width}};
1266 ov_status_e status;
1267 ov_shape_t input_shape = {0};
1268 ov_partial_shape_t partial_shape;
1269#else
1270 IEStatusCode status;
1271 input_shapes_t input_shapes;
1272#endif
1273 int ret;
1274 OVModel *ov_model = (OVModel *)model;
1275 DnnContext *ctx = ov_model->ctx;
1276 TaskItem task;
1277 OVRequestItem *request;
1278 DNNExecBaseParams exec_params = {
1279 .input_name = input_name,
1280 .output_names = output_name ? &output_name : NULL,
1281 .nb_output = 1,
1282 .in_frame = NULL,
1283 .out_frame = NULL,
1284 };
1285
1286 if (ov_model->model.func_type != DFT_PROCESS_FRAME) {
1287 av_log(ctx, AV_LOG_ERROR, "Get output dim only when processing frame.\n");
1288 return AVERROR(EINVAL);
1289 }
1290
1291#if HAVE_OPENVINO2
1292 if (ctx->ov_option.input_resizable) {
1293 status = ov_partial_shape_create(4, dims, &partial_shape);
1294 if (status != OK) {
1295 av_log(ctx, AV_LOG_ERROR, "Failed to create partial shape.\n");
1296 return ov2_map_error(status, NULL);
1297 }
1298 status = ov_const_port_get_shape(ov_model->input_port, &input_shape);
1299 if (status != OK) {
1300 av_log(ctx, AV_LOG_ERROR, "Failed to create shape for model input resize.\n");
1301 return ov2_map_error(status, NULL);
1302 }
1303 input_shape.dims[2] = input_height;
1304 input_shape.dims[3] = input_width;
1305
1306 status = ov_shape_to_partial_shape(input_shape, &partial_shape);
1307 ov_shape_free(&input_shape);
1308 if (status != OK) {
1309 av_log(ctx, AV_LOG_ERROR, "Failed to create partial shape for model input resize.\n");
1310 return ov2_map_error(status, NULL);
1311 }
1312
1313 status = ov_model_reshape_single_input(ov_model->ov_model, partial_shape);
1314 ov_partial_shape_free(&partial_shape);
1315 if (status != OK) {
1316 av_log(ctx, AV_LOG_ERROR, "Failed to reszie model input.\n");
1317 return ov2_map_error(status, NULL);
1318 }
1319 }
1320
1321 if (!ov_model->compiled_model) {
1322#else
1323 if (ctx->ov_option.input_resizable) {
1324 status = ie_network_get_input_shapes(ov_model->network, &input_shapes);
1325 input_shapes.shapes->shape.dims[2] = input_height;
1326 input_shapes.shapes->shape.dims[3] = input_width;
1327 status |= ie_network_reshape(ov_model->network, input_shapes);
1328 ie_network_input_shapes_free(&input_shapes);
1329 if (status != OK) {
1330 av_log(ctx, AV_LOG_ERROR, "Failed to reshape input size for %s\n", input_name);
1331 return DNN_GENERIC_ERROR;
1332 }
1333 }
1334 if (!ov_model->exe_network) {
1335#endif
1336 ret = init_model_ov(ov_model, input_name, output_name ? &output_name : NULL, 1);
1337 if (ret != 0) {
1338 av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO executable network or inference request\n");
1339 return ret;
1340 }
1341 }
1342
1343 ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, ov_model, input_height, input_width, ctx);
1344 if (ret != 0) {
1345 goto err;
1346 }
1347
1348 ret = extract_lltask_from_task(ov_model->model.func_type, &task, ov_model->lltask_queue, NULL);
1349 if (ret != 0) {
1350 av_log(ctx, AV_LOG_ERROR, "unable to extract inference from task.\n");
1351 goto err;
1352 }
1353
1354 request = ff_safe_queue_pop_front(ov_model->request_queue);
1355 if (!request) {
1356 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
1357 ret = AVERROR(EINVAL);
1358 goto err;
1359 }
1360
1361 ret = execute_model_ov(request, ov_model->lltask_queue);
1362 *output_width = task.out_frame->width;
1363 *output_height = task.out_frame->height;
1364err:
1365 av_frame_free(&task.out_frame);
1366 av_frame_free(&task.in_frame);
1367 return ret;
1368}
1369
1371{
1372 DNNModel *model = NULL;
1373 OVModel *ov_model = NULL;
1374#if HAVE_OPENVINO2
1375 ov_core_t* core = NULL;
1376 ov_model_t* ovmodel = NULL;
1377 ov_status_e status;
1378#else
1379 size_t node_count = 0;
1380 char *node_name = NULL;
1381 IEStatusCode status;
1382#endif
1383
1384 ov_model = av_mallocz(sizeof(OVModel));
1385 if (!ov_model)
1386 return NULL;
1387 ov_model->ctx = ctx;
1388 model = &ov_model->model;
1389
1390#if HAVE_OPENVINO2
1391 status = ov_core_create(&core);
1392 if (status != OK) {
1393 goto err;
1394 }
1395 ov_model->core = core;
1396
1397 status = ov_core_read_model(core, ctx->model_filename, NULL, &ovmodel);
1398 if (status != OK) {
1399 ov_version_t ver;
1400 status = ov_get_openvino_version(&ver);
1401 av_log(NULL, AV_LOG_ERROR, "Failed to read the network from model file %s,\n"
1402 "Please check if the model version matches the runtime OpenVINO Version:\n",
1403 ctx->model_filename);
1404 if (status == OK) {
1405 av_log(NULL, AV_LOG_ERROR, "BuildNumber: %s\n", ver.buildNumber);
1406 }
1407 ov_version_free(&ver);
1408 goto err;
1409 }
1410 ov_model->ov_model = ovmodel;
1411#else
1412 ov_model->all_input_names = NULL;
1413 ov_model->all_output_names = NULL;
1414
1415 status = ie_core_create("", &ov_model->core);
1416 if (status != OK)
1417 goto err;
1418
1419 status = ie_core_read_network(ov_model->core, ctx->model_filename, NULL, &ov_model->network);
1420 if (status != OK) {
1421 ie_version_t ver;
1422 ver = ie_c_api_version();
1423 av_log(ctx, AV_LOG_ERROR, "Failed to read the network from model file %s,\n"
1424 "Please check if the model version matches the runtime OpenVINO %s\n",
1425 ctx->model_filename, ver.api_version);
1426 ie_version_free(&ver);
1427 goto err;
1428 }
1429
1430 //get all the input and output names
1431 status = ie_network_get_inputs_number(ov_model->network, &node_count);
1432 if (status != OK) {
1433 av_log(ctx, AV_LOG_ERROR, "Failed to get input count\n");
1434 goto err;
1435 }
1436 for (size_t i = 0; i < node_count; i++) {
1437 status = ie_network_get_input_name(ov_model->network, i, &node_name);
1438 if (status != OK) {
1439 av_log(ctx, AV_LOG_ERROR, "Failed to get No.%d input's name\n", (int)i);
1440 goto err;
1441 }
1442 APPEND_STRING(ov_model->all_input_names, node_name)
1443 ie_network_name_free(&node_name);
1444 }
1445 status = ie_network_get_outputs_number(ov_model->network, &node_count);
1446 if (status != OK) {
1447 av_log(ctx, AV_LOG_ERROR, "Failed to get output count\n");
1448 goto err;
1449 }
1450 for (size_t i = 0; i < node_count; i++) {
1451 status = ie_network_get_output_name(ov_model->network, i, &node_name);
1452 if (status != OK) {
1453 av_log(ctx, AV_LOG_ERROR, "Failed to get No.%d output's name\n", (int)i);
1454 goto err;
1455 }
1456 APPEND_STRING(ov_model->all_output_names, node_name)
1457 ie_network_name_free(&node_name);
1458 }
1459#endif
1460
1461 model->get_input = &get_input_ov;
1462 model->get_output = &get_output_ov;
1463 model->filter_ctx = filter_ctx;
1464 model->func_type = func_type;
1465
1466 return model;
1467
1468err:
1469 dnn_free_model_ov(&model);
1470 return NULL;
1471}
1472
1473static int dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params)
1474{
1475 OVModel *ov_model = (OVModel *)model;
1476 DnnContext *ctx = ov_model->ctx;
1477 OVRequestItem *request;
1478 TaskItem *task;
1479 int ret;
1480
1481 ret = ff_check_exec_params(ctx, DNN_OV, model->func_type, exec_params);
1482 if (ret != 0) {
1483 return ret;
1484 }
1485
1486#if HAVE_OPENVINO2
1487 if (!ov_model->compiled_model) {
1488#else
1489 if (!ov_model->exe_network) {
1490#endif
1491 ret = init_model_ov(ov_model, exec_params->input_name,
1492 exec_params->output_names, exec_params->nb_output);
1493 if (ret != 0) {
1494 av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO executable network or inference request\n");
1495 return ret;
1496 }
1497 }
1498
1499 task = av_malloc(sizeof(*task));
1500 if (!task) {
1501 av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n");
1502 return AVERROR(ENOMEM);
1503 }
1504
1505 ret = ff_dnn_fill_task(task, exec_params, ov_model, ctx->async, 1);
1506 if (ret != 0) {
1507 av_freep(&task);
1508 return ret;
1509 }
1510
1511 if (ff_queue_push_back(ov_model->task_queue, task) < 0) {
1512 av_freep(&task);
1513 av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n");
1514 return AVERROR(ENOMEM);
1515 }
1516
1517 ret = extract_lltask_from_task(model->func_type, task, ov_model->lltask_queue, exec_params);
1518 if (ret != 0) {
1519 av_log(ctx, AV_LOG_ERROR, "unable to extract inference from task.\n");
1520 return ret;
1521 }
1522
1523 if (ctx->async) {
1524 while (ff_queue_size(ov_model->lltask_queue) >= ctx->batch_size) {
1525 request = ff_safe_queue_pop_front(ov_model->request_queue);
1526 if (!request) {
1527 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
1528 return AVERROR(EINVAL);
1529 }
1530
1531 ret = execute_model_ov(request, ov_model->lltask_queue);
1532 if (ret != 0) {
1533 return ret;
1534 }
1535 }
1536
1537 return 0;
1538 }
1539 else {
1540 if (model->func_type == DFT_ANALYTICS_CLASSIFY) {
1541 // Classification filter has not been completely
1542 // tested with the sync mode. So, do not support now.
1543 avpriv_report_missing_feature(ctx, "classify for sync execution");
1544 return AVERROR(ENOSYS);
1545 }
1546
1547 if (ctx->batch_size > 1) {
1548 avpriv_report_missing_feature(ctx, "batch mode for sync execution");
1549 return AVERROR(ENOSYS);
1550 }
1551
1552 request = ff_safe_queue_pop_front(ov_model->request_queue);
1553 if (!request) {
1554 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
1555 return AVERROR(EINVAL);
1556 }
1557 return execute_model_ov(request, ov_model->lltask_queue);
1558 }
1559}
1560
1562{
1563 OVModel *ov_model = (OVModel *)model;
1564 return ff_dnn_get_result_common(ov_model->task_queue, in, out);
1565}
1566
1567static int dnn_flush_ov(const DNNModel *model)
1568{
1569 OVModel *ov_model = (OVModel *)model;
1570 DnnContext *ctx = ov_model->ctx;
1571 OVRequestItem *request;
1572#if HAVE_OPENVINO2
1573 ov_status_e status;
1574#else
1575 IEStatusCode status;
1576#endif
1577 int ret;
1578
1579 if (ff_queue_size(ov_model->lltask_queue) == 0) {
1580 // no pending task need to flush
1581 return 0;
1582 }
1583
1584 request = ff_safe_queue_pop_front(ov_model->request_queue);
1585 if (!request) {
1586 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
1587 return AVERROR(EINVAL);
1588 }
1589
1590 ret = fill_model_input_ov(ov_model, request);
1591 if (ret != 0) {
1592 av_log(ctx, AV_LOG_ERROR, "Failed to fill model input.\n");
1593 return ret;
1594 }
1595#if HAVE_OPENVINO2
1596 status = ov_infer_request_infer(request->infer_request);
1597 if (status != OK) {
1598 av_log(ctx, AV_LOG_ERROR, "Failed to start sync inference for OV2\n");
1599 return ov2_map_error(status, NULL);
1600 }
1601#else
1602 status = ie_infer_set_completion_callback(request->infer_request, &request->callback);
1603 if (status != OK) {
1604 av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n");
1605 return DNN_GENERIC_ERROR;
1606 }
1607 status = ie_infer_request_infer_async(request->infer_request);
1608 if (status != OK) {
1609 av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n");
1610 return DNN_GENERIC_ERROR;
1611 }
1612#endif
1613
1614 return 0;
1615}
1616
1618 .clazz = DNN_DEFINE_CLASS(dnn_openvino),
1619 .type = DNN_OV,
1620 .load_model = dnn_load_model_ov,
1621 .execute_model = dnn_execute_model_ov,
1622 .get_result = dnn_get_result_ov,
1623 .flush = dnn_flush_ov,
1624 .free_model = dnn_free_model_ov,
1625};
SwsAArch64OpImplParams params
Definition ops.c:51
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 av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
Buffered I/O operations.
#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
static __device__ float fabsf(float a)
static AVFrame * frame
#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_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 void infer_completion_callback(void *args)
static DNNDataType precision_to_datatype(precision_e precision)
#define APPEND_STRING(generated_string, iterate_string)
static int fill_model_input_ov(OVModel *ov_model, OVRequestItem *request)
static int get_output_ov(DNNModel *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
static int get_datatype_size(DNNDataType dt)
static DNNAsyncStatusType dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out)
static int get_input_ov(DNNModel *model, DNNData *input, const char *input_name)
static int init_model_ov(OVModel *ov_model, const char *input_name, const char **output_names, int nb_outputs)
static int execute_model_ov(OVRequestItem *request, Queue *inferenceq)
static const AVOption dnn_openvino_options[]
static DNNModel * dnn_load_model_ov(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx)
static int dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params)
static int dnn_flush_ov(const DNNModel *model)
static int extract_lltask_from_task(DNNFunctionType func_type, TaskItem *task, Queue *lltask_queue, DNNExecBaseParams *exec_params)
#define OFFSET(x)
static int contain_valid_detection_bbox(AVFrame *frame)
const DNNModule ff_dnn_backend_openvino
static void dnn_free_model_ov(DNNModel **model)
static void infer_completion_callback(void *args)
static int dnn_get_height_idx_by_layout(DNNLayout layout)
DNNAsyncStatusType
@ DL_NCHW
@ DL_NHWC
@ DL_NONE
@ DNN_OV
DNNFunctionType
@ DFT_ANALYTICS_CLASSIFY
@ DFT_PROCESS_FRAME
@ DFT_ANALYTICS_DETECT
static int dnn_get_width_idx_by_layout(DNNLayout layout)
#define DNN_GENERIC_ERROR
DNNDataType
@ DNN_UINT8
@ DNN_FLOAT
@ DCO_BGR
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)
Definition dnn_io_proc.c:42
DNN input&output process between AVFrame and DNNData.
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition opt.h:270
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
#define AVERROR_UNKNOWN
Unknown error, typically from an external library.
Definition error.h:73
#define AVERROR_EXTERNAL
Generic error in an external library.
Definition error.h:59
#define AVERROR(e)
Definition error.h:45
AVFrameSideData * av_frame_get_side_data(const AVFrame *frame, enum AVFrameSideDataType type)
Definition frame.c:659
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
@ AV_FRAME_DATA_DETECTION_BBOXES
Bounding boxes for object detection and classification, as described by AVDetectionBBoxHeader.
Definition frame.h:194
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_strncasecmp(const char *a, const char *b, size_t n)
Locale-independent case-insensitive compare.
Definition avstring.c:218
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition intra.c:278
int av_cpu_count(void)
Definition cpu.c:228
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
const char * desc
Definition libsvtav1.c:83
int av_err
Definition libsvtav1.c:82
uint64_t layout
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
const char data[16]
Definition mxf.c:149
#define av_malloc(s)
Definition ops_static.c:52
AVOptions.
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
static const uint8_t header[24]
Definition sdr2.c:68
#define FF_ARRAY_ELEMS(a)
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,...
uint32_t classify_count
An instance of a filter.
Definition avfilter.h:273
Structure to hold side data for an AVFrame.
Definition frame.h:327
size_t size
Definition frame.h:330
uint8_t * data
Definition frame.h:329
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
float scale
DNNDataType dt
int dims[4]
DNNColorOrder order
void * data
DNNLayout layout
float mean
const char ** output_names
const char * input_name
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
SafeQueue * request_queue
ie_executable_network_t * exe_network
ie_core_t * core
ie_network_t * network
Queue * lltask_queue
const char * all_output_names
DnnContext * ctx
const char * all_input_names
ie_infer_request_t * infer_request
ie_complete_call_back_t callback
LastLevelTaskItem ** lltasks
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
#define av_free(p)
#define av_malloc_array(a, b)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static FilteringContext * filter_ctx
Definition transcode.c:52
static float mean(const float *input, int size)
Definition vf_nnedi.c:861