FFmpeg
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_backend_openvino.h"
27 #include "dnn_io_proc.h"
28 #include "libavformat/avio.h"
29 #include "libavutil/avassert.h"
30 #include "libavutil/opt.h"
31 #include "libavutil/avstring.h"
32 #include "../internal.h"
33 #include "queue.h"
34 #include "safe_queue.h"
35 #include <c_api/ie_c_api.h>
36 
37 typedef struct OVOptions{
38  char *device_type;
39  int nireq;
42 } OVOptions;
43 
44 typedef struct OVContext {
45  const AVClass *class;
47 } OVContext;
48 
49 typedef struct OVModel{
52  ie_core_t *core;
53  ie_network_t *network;
54  ie_executable_network_t *exe_network;
55  ie_infer_request_t *infer_request;
56 
57  /* for async execution */
58  SafeQueue *request_queue; // holds RequestItem
59  Queue *task_queue; // holds TaskItem
60 } OVModel;
61 
62 typedef struct TaskItem {
64  const char *input_name;
66  const char *output_name;
68  int do_ioproc;
69  int async;
70  int done;
71 } TaskItem;
72 
73 typedef struct RequestItem {
74  ie_infer_request_t *infer_request;
77  ie_complete_call_back_t callback;
78 } RequestItem;
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(OVContext, x)
85 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
86 static const AVOption dnn_openvino_options[] = {
87  { "device", "device to run model", OFFSET(options.device_type), AV_OPT_TYPE_STRING, { .str = "CPU" }, 0, 0, FLAGS },
88  { "nireq", "number of request", OFFSET(options.nireq), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
89  { "batch_size", "batch size per request", OFFSET(options.batch_size), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 1000, FLAGS},
90  { "input_resizable", "can input be resizable or not", OFFSET(options.input_resizable), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },
91  { NULL }
92 };
93 
94 AVFILTER_DEFINE_CLASS(dnn_openvino);
95 
96 static DNNDataType precision_to_datatype(precision_e precision)
97 {
98  switch (precision)
99  {
100  case FP32:
101  return DNN_FLOAT;
102  case U8:
103  return DNN_UINT8;
104  default:
105  av_assert0(!"not supported yet.");
106  return DNN_FLOAT;
107  }
108 }
109 
111 {
112  switch (dt)
113  {
114  case DNN_FLOAT:
115  return sizeof(float);
116  case DNN_UINT8:
117  return sizeof(uint8_t);
118  default:
119  av_assert0(!"not supported yet.");
120  return 1;
121  }
122 }
123 
125 {
126  dimensions_t dims;
127  precision_e precision;
128  ie_blob_buffer_t blob_buffer;
129  OVContext *ctx = &ov_model->ctx;
130  IEStatusCode status;
131  DNNData input;
132  ie_blob_t *input_blob = NULL;
133  TaskItem *task = request->tasks[0];
134 
135  status = ie_infer_request_get_blob(request->infer_request, task->input_name, &input_blob);
136  if (status != OK) {
137  av_log(ctx, AV_LOG_ERROR, "Failed to get input blob with name %s\n", task->input_name);
138  return DNN_ERROR;
139  }
140 
141  status |= ie_blob_get_dims(input_blob, &dims);
142  status |= ie_blob_get_precision(input_blob, &precision);
143  if (status != OK) {
144  ie_blob_free(&input_blob);
145  av_log(ctx, AV_LOG_ERROR, "Failed to get input blob dims/precision\n");
146  return DNN_ERROR;
147  }
148 
149  status = ie_blob_get_buffer(input_blob, &blob_buffer);
150  if (status != OK) {
151  ie_blob_free(&input_blob);
152  av_log(ctx, AV_LOG_ERROR, "Failed to get input blob buffer\n");
153  return DNN_ERROR;
154  }
155 
156  input.height = dims.dims[2];
157  input.width = dims.dims[3];
158  input.channels = dims.dims[1];
159  input.data = blob_buffer.buffer;
160  input.dt = precision_to_datatype(precision);
161  // all models in openvino open model zoo use BGR as input,
162  // change to be an option when necessary.
163  input.order = DCO_BGR;
164 
165  av_assert0(request->task_count <= dims.dims[0]);
166  for (int i = 0; i < request->task_count; ++i) {
167  task = request->tasks[i];
168  if (task->do_ioproc) {
169  if (ov_model->model->pre_proc != NULL) {
170  ov_model->model->pre_proc(task->in_frame, &input, ov_model->model->filter_ctx);
171  } else {
173  }
174  }
175  input.data = (uint8_t *)input.data
176  + input.width * input.height * input.channels * get_datatype_size(input.dt);
177  }
178  ie_blob_free(&input_blob);
179 
180  return DNN_SUCCESS;
181 }
182 
183 static void infer_completion_callback(void *args)
184 {
185  dimensions_t dims;
186  precision_e precision;
187  IEStatusCode status;
188  RequestItem *request = args;
189  TaskItem *task = request->tasks[0];
190  SafeQueue *requestq = task->ov_model->request_queue;
191  ie_blob_t *output_blob = NULL;
192  ie_blob_buffer_t blob_buffer;
193  DNNData output;
194  OVContext *ctx = &task->ov_model->ctx;
195 
196  status = ie_infer_request_get_blob(request->infer_request, task->output_name, &output_blob);
197  if (status != OK) {
198  //incorrect output name
199  char *model_output_name = NULL;
200  char *all_output_names = NULL;
201  size_t model_output_count = 0;
202  av_log(ctx, AV_LOG_ERROR, "Failed to get model output data\n");
203  status = ie_network_get_outputs_number(task->ov_model->network, &model_output_count);
204  for (size_t i = 0; i < model_output_count; i++) {
205  status = ie_network_get_output_name(task->ov_model->network, i, &model_output_name);
206  APPEND_STRING(all_output_names, model_output_name)
207  }
209  "output \"%s\" may not correct, all output(s) are: \"%s\"\n",
210  task->output_name, all_output_names);
211  return;
212  }
213 
214  status = ie_blob_get_buffer(output_blob, &blob_buffer);
215  if (status != OK) {
216  ie_blob_free(&output_blob);
217  av_log(ctx, AV_LOG_ERROR, "Failed to access output memory\n");
218  return;
219  }
220 
221  status |= ie_blob_get_dims(output_blob, &dims);
222  status |= ie_blob_get_precision(output_blob, &precision);
223  if (status != OK) {
224  ie_blob_free(&output_blob);
225  av_log(ctx, AV_LOG_ERROR, "Failed to get dims or precision of output\n");
226  return;
227  }
228 
229  output.channels = dims.dims[1];
230  output.height = dims.dims[2];
231  output.width = dims.dims[3];
232  output.dt = precision_to_datatype(precision);
233  output.data = blob_buffer.buffer;
234 
235  av_assert0(request->task_count <= dims.dims[0]);
236  av_assert0(request->task_count >= 1);
237  for (int i = 0; i < request->task_count; ++i) {
238  task = request->tasks[i];
239  if (task->do_ioproc) {
240  if (task->ov_model->model->post_proc != NULL) {
241  task->ov_model->model->post_proc(task->out_frame, &output, task->ov_model->model->filter_ctx);
242  } else {
244  }
245  } else {
246  task->out_frame->width = output.width;
247  task->out_frame->height = output.height;
248  }
249  task->done = 1;
250  output.data = (uint8_t *)output.data
251  + output.width * output.height * output.channels * get_datatype_size(output.dt);
252  }
253  ie_blob_free(&output_blob);
254 
255  request->task_count = 0;
256 
257  if (task->async) {
258  if (ff_safe_queue_push_back(requestq, request) < 0) {
259  av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
260  return;
261  }
262  }
263 }
264 
265 static DNNReturnType init_model_ov(OVModel *ov_model, const char *input_name, const char *output_name)
266 {
267  OVContext *ctx = &ov_model->ctx;
268  IEStatusCode status;
269  ie_available_devices_t a_dev;
270  ie_config_t config = {NULL, NULL, NULL};
271  char *all_dev_names = NULL;
272 
273  // batch size
274  if (ctx->options.batch_size <= 0) {
275  ctx->options.batch_size = 1;
276  }
277 
278  if (ctx->options.batch_size > 1) {
279  input_shapes_t input_shapes;
280  status = ie_network_get_input_shapes(ov_model->network, &input_shapes);
281  if (status != OK)
282  goto err;
283  for (int i = 0; i < input_shapes.shape_num; i++)
284  input_shapes.shapes[i].shape.dims[0] = ctx->options.batch_size;
285  status = ie_network_reshape(ov_model->network, input_shapes);
286  ie_network_input_shapes_free(&input_shapes);
287  if (status != OK)
288  goto err;
289  }
290 
291  // The order of dims in the openvino is fixed and it is always NCHW for 4-D data.
292  // while we pass NHWC data from FFmpeg to openvino
293  status = ie_network_set_input_layout(ov_model->network, input_name, NHWC);
294  if (status != OK) {
295  av_log(ctx, AV_LOG_ERROR, "Failed to set layout as NHWC for input %s\n", input_name);
296  goto err;
297  }
298  status = ie_network_set_output_layout(ov_model->network, output_name, NHWC);
299  if (status != OK) {
300  av_log(ctx, AV_LOG_ERROR, "Failed to set layout as NHWC for output %s\n", output_name);
301  goto err;
302  }
303 
304  // all models in openvino open model zoo use BGR with range [0.0f, 255.0f] as input,
305  // we don't have a AVPixelFormat to descibe it, so we'll use AV_PIX_FMT_BGR24 and
306  // ask openvino to do the conversion internally.
307  // the current supported SR model (frame processing) is generated from tensorflow model,
308  // and its input is Y channel as float with range [0.0f, 1.0f], so do not set for this case.
309  // TODO: we need to get a final clear&general solution with all backends/formats considered.
310  if (ov_model->model->func_type != DFT_PROCESS_FRAME) {
311  status = ie_network_set_input_precision(ov_model->network, input_name, U8);
312  if (status != OK) {
313  av_log(ctx, AV_LOG_ERROR, "Failed to set input precision as U8 for %s\n", input_name);
314  goto err;
315  }
316  }
317 
318  status = ie_core_load_network(ov_model->core, ov_model->network, ctx->options.device_type, &config, &ov_model->exe_network);
319  if (status != OK) {
320  av_log(ctx, AV_LOG_ERROR, "Failed to load OpenVINO model network\n");
321  status = ie_core_get_available_devices(ov_model->core, &a_dev);
322  if (status != OK) {
323  av_log(ctx, AV_LOG_ERROR, "Failed to get available devices\n");
324  goto err;
325  }
326  for (int i = 0; i < a_dev.num_devices; i++) {
327  APPEND_STRING(all_dev_names, a_dev.devices[i])
328  }
329  av_log(ctx, AV_LOG_ERROR,"device %s may not be supported, all available devices are: \"%s\"\n",
330  ctx->options.device_type, all_dev_names);
331  goto err;
332  }
333 
334  // create infer_request for sync execution
335  status = ie_exec_network_create_infer_request(ov_model->exe_network, &ov_model->infer_request);
336  if (status != OK)
337  goto err;
338 
339  // create infer_requests for async execution
340  if (ctx->options.nireq <= 0) {
341  // the default value is a rough estimation
342  ctx->options.nireq = av_cpu_count() / 2 + 1;
343  }
344 
345  ov_model->request_queue = ff_safe_queue_create();
346  if (!ov_model->request_queue) {
347  goto err;
348  }
349 
350  for (int i = 0; i < ctx->options.nireq; i++) {
351  RequestItem *item = av_mallocz(sizeof(*item));
352  if (!item) {
353  goto err;
354  }
355 
356  item->callback.completeCallBackFunc = infer_completion_callback;
357  item->callback.args = item;
358  if (ff_safe_queue_push_back(ov_model->request_queue, item) < 0) {
359  av_freep(&item);
360  goto err;
361  }
362 
363  status = ie_exec_network_create_infer_request(ov_model->exe_network, &item->infer_request);
364  if (status != OK) {
365  goto err;
366  }
367 
368  item->tasks = av_malloc_array(ctx->options.batch_size, sizeof(*item->tasks));
369  if (!item->tasks) {
370  goto err;
371  }
372  item->task_count = 0;
373  }
374 
375  ov_model->task_queue = ff_queue_create();
376  if (!ov_model->task_queue) {
377  goto err;
378  }
379 
380  return DNN_SUCCESS;
381 
382 err:
383  ff_dnn_free_model_ov(&ov_model->model);
384  return DNN_ERROR;
385 }
386 
388 {
389  IEStatusCode status;
391  TaskItem *task = request->tasks[0];
392  OVContext *ctx = &task->ov_model->ctx;
393 
394  if (task->async) {
395  if (request->task_count < ctx->options.batch_size) {
396  if (ff_safe_queue_push_front(task->ov_model->request_queue, request) < 0) {
397  av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
398  return DNN_ERROR;
399  }
400  return DNN_SUCCESS;
401  }
402  ret = fill_model_input_ov(task->ov_model, request);
403  if (ret != DNN_SUCCESS) {
404  return ret;
405  }
406  status = ie_infer_set_completion_callback(request->infer_request, &request->callback);
407  if (status != OK) {
408  av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n");
409  return DNN_ERROR;
410  }
411  status = ie_infer_request_infer_async(request->infer_request);
412  if (status != OK) {
413  av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n");
414  return DNN_ERROR;
415  }
416  return DNN_SUCCESS;
417  } else {
418  ret = fill_model_input_ov(task->ov_model, request);
419  if (ret != DNN_SUCCESS) {
420  return ret;
421  }
422  status = ie_infer_request_infer(request->infer_request);
423  if (status != OK) {
424  av_log(ctx, AV_LOG_ERROR, "Failed to start synchronous model inference\n");
425  return DNN_ERROR;
426  }
427  infer_completion_callback(request);
428  return task->done ? DNN_SUCCESS : DNN_ERROR;
429  }
430 }
431 
432 static DNNReturnType get_input_ov(void *model, DNNData *input, const char *input_name)
433 {
434  OVModel *ov_model = model;
435  OVContext *ctx = &ov_model->ctx;
436  char *model_input_name = NULL;
437  char *all_input_names = NULL;
438  IEStatusCode status;
439  size_t model_input_count = 0;
440  dimensions_t dims;
441  precision_e precision;
442  int input_resizable = ctx->options.input_resizable;
443 
444  status = ie_network_get_inputs_number(ov_model->network, &model_input_count);
445  if (status != OK) {
446  av_log(ctx, AV_LOG_ERROR, "Failed to get input count\n");
447  return DNN_ERROR;
448  }
449 
450  for (size_t i = 0; i < model_input_count; i++) {
451  status = ie_network_get_input_name(ov_model->network, i, &model_input_name);
452  if (status != OK) {
453  av_log(ctx, AV_LOG_ERROR, "Failed to get No.%d input's name\n", (int)i);
454  return DNN_ERROR;
455  }
456  if (strcmp(model_input_name, input_name) == 0) {
457  ie_network_name_free(&model_input_name);
458  status |= ie_network_get_input_dims(ov_model->network, input_name, &dims);
459  status |= ie_network_get_input_precision(ov_model->network, input_name, &precision);
460  if (status != OK) {
461  av_log(ctx, AV_LOG_ERROR, "Failed to get No.%d input's dims or precision\n", (int)i);
462  return DNN_ERROR;
463  }
464 
465  input->channels = dims.dims[1];
466  input->height = input_resizable ? -1 : dims.dims[2];
467  input->width = input_resizable ? -1 : dims.dims[3];
468  input->dt = precision_to_datatype(precision);
469  return DNN_SUCCESS;
470  } else {
471  //incorrect input name
472  APPEND_STRING(all_input_names, model_input_name)
473  }
474 
475  ie_network_name_free(&model_input_name);
476  }
477 
478  av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, all input(s) are: \"%s\"\n", input_name, all_input_names);
479  return DNN_ERROR;
480 }
481 
482 static DNNReturnType get_output_ov(void *model, const char *input_name, int input_width, int input_height,
483  const char *output_name, int *output_width, int *output_height)
484 {
486  OVModel *ov_model = model;
487  OVContext *ctx = &ov_model->ctx;
488  TaskItem task;
489  RequestItem request;
490  AVFrame *in_frame = NULL;
491  AVFrame *out_frame = NULL;
492  TaskItem *ptask = &task;
493  IEStatusCode status;
494  input_shapes_t input_shapes;
495 
496  if (ctx->options.input_resizable) {
497  status = ie_network_get_input_shapes(ov_model->network, &input_shapes);
498  input_shapes.shapes->shape.dims[2] = input_height;
499  input_shapes.shapes->shape.dims[3] = input_width;
500  status |= ie_network_reshape(ov_model->network, input_shapes);
501  ie_network_input_shapes_free(&input_shapes);
502  if (status != OK) {
503  av_log(ctx, AV_LOG_ERROR, "Failed to reshape input size for %s\n", input_name);
504  return DNN_ERROR;
505  }
506  }
507 
508  if (!ov_model->exe_network) {
509  if (init_model_ov(ov_model, input_name, output_name) != DNN_SUCCESS) {
510  av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO exectuable network or inference request\n");
511  return DNN_ERROR;
512  }
513  }
514 
515  in_frame = av_frame_alloc();
516  if (!in_frame) {
517  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input frame\n");
518  return DNN_ERROR;
519  }
520  in_frame->width = input_width;
521  in_frame->height = input_height;
522 
523  out_frame = av_frame_alloc();
524  if (!out_frame) {
525  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output frame\n");
526  av_frame_free(&in_frame);
527  return DNN_ERROR;
528  }
529 
530  task.done = 0;
531  task.do_ioproc = 0;
532  task.async = 0;
533  task.input_name = input_name;
534  task.in_frame = in_frame;
535  task.output_name = output_name;
536  task.out_frame = out_frame;
537  task.ov_model = ov_model;
538 
539  request.infer_request = ov_model->infer_request;
540  request.task_count = 1;
541  request.tasks = &ptask;
542 
543  ret = execute_model_ov(&request);
544  *output_width = out_frame->width;
545  *output_height = out_frame->height;
546 
547  av_frame_free(&out_frame);
548  av_frame_free(&in_frame);
549  return ret;
550 }
551 
552 DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx)
553 {
554  DNNModel *model = NULL;
555  OVModel *ov_model = NULL;
556  OVContext *ctx = NULL;
557  IEStatusCode status;
558 
559  model = av_mallocz(sizeof(DNNModel));
560  if (!model){
561  return NULL;
562  }
563 
564  ov_model = av_mallocz(sizeof(OVModel));
565  if (!ov_model) {
566  av_freep(&model);
567  return NULL;
568  }
569  model->model = ov_model;
570  ov_model->model = model;
571  ov_model->ctx.class = &dnn_openvino_class;
572  ctx = &ov_model->ctx;
573 
574  //parse options
576  if (av_opt_set_from_string(ctx, options, NULL, "=", "&") < 0) {
577  av_log(ctx, AV_LOG_ERROR, "Failed to parse options \"%s\"\n", options);
578  goto err;
579  }
580 
581  status = ie_core_create("", &ov_model->core);
582  if (status != OK)
583  goto err;
584 
585  status = ie_core_read_network(ov_model->core, model_filename, NULL, &ov_model->network);
586  if (status != OK) {
587  ie_version_t ver;
588  ver = ie_c_api_version();
589  av_log(ctx, AV_LOG_ERROR, "Failed to read the network from model file %s,\n"
590  "Please check if the model version matches the runtime OpenVINO %s\n",
591  model_filename, ver.api_version);
592  ie_version_free(&ver);
593  goto err;
594  }
595 
596  model->get_input = &get_input_ov;
597  model->get_output = &get_output_ov;
598  model->options = options;
599  model->filter_ctx = filter_ctx;
600  model->func_type = func_type;
601 
602  return model;
603 
604 err:
605  ff_dnn_free_model_ov(&model);
606  return NULL;
607 }
608 
609 DNNReturnType ff_dnn_execute_model_ov(const DNNModel *model, const char *input_name, AVFrame *in_frame,
610  const char **output_names, uint32_t nb_output, AVFrame *out_frame)
611 {
612  OVModel *ov_model = model->model;
613  OVContext *ctx = &ov_model->ctx;
614  TaskItem task;
615  RequestItem request;
616  TaskItem *ptask = &task;
617 
618  if (!in_frame) {
619  av_log(ctx, AV_LOG_ERROR, "in frame is NULL when execute model.\n");
620  return DNN_ERROR;
621  }
622 
623  if (!out_frame && model->func_type == DFT_PROCESS_FRAME) {
624  av_log(ctx, AV_LOG_ERROR, "out frame is NULL when execute model.\n");
625  return DNN_ERROR;
626  }
627 
628  if (nb_output != 1) {
629  // currently, the filter does not need multiple outputs,
630  // so we just pending the support until we really need it.
631  avpriv_report_missing_feature(ctx, "multiple outputs");
632  return DNN_ERROR;
633  }
634 
635  if (ctx->options.batch_size > 1) {
636  avpriv_report_missing_feature(ctx, "batch mode for sync execution");
637  return DNN_ERROR;
638  }
639 
640  if (!ov_model->exe_network) {
641  if (init_model_ov(ov_model, input_name, output_names[0]) != DNN_SUCCESS) {
642  av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO exectuable network or inference request\n");
643  return DNN_ERROR;
644  }
645  }
646 
647  task.done = 0;
648  task.do_ioproc = 1;
649  task.async = 0;
650  task.input_name = input_name;
651  task.in_frame = in_frame;
652  task.output_name = output_names[0];
653  task.out_frame = out_frame;
654  task.ov_model = ov_model;
655 
656  request.infer_request = ov_model->infer_request;
657  request.task_count = 1;
658  request.tasks = &ptask;
659 
660  return execute_model_ov(&request);
661 }
662 
663 DNNReturnType ff_dnn_execute_model_async_ov(const DNNModel *model, const char *input_name, AVFrame *in_frame,
664  const char **output_names, uint32_t nb_output, AVFrame *out_frame)
665 {
666  OVModel *ov_model = model->model;
667  OVContext *ctx = &ov_model->ctx;
668  RequestItem *request;
669  TaskItem *task;
670 
671  if (!in_frame) {
672  av_log(ctx, AV_LOG_ERROR, "in frame is NULL when async execute model.\n");
673  return DNN_ERROR;
674  }
675 
676  if (!out_frame && model->func_type == DFT_PROCESS_FRAME) {
677  av_log(ctx, AV_LOG_ERROR, "out frame is NULL when async execute model.\n");
678  return DNN_ERROR;
679  }
680 
681  if (!ov_model->exe_network) {
682  if (init_model_ov(ov_model, input_name, output_names[0]) != DNN_SUCCESS) {
683  av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO exectuable network or inference request\n");
684  return DNN_ERROR;
685  }
686  }
687 
688  task = av_malloc(sizeof(*task));
689  if (!task) {
690  av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n");
691  return DNN_ERROR;
692  }
693 
694  task->done = 0;
695  task->do_ioproc = 1;
696  task->async = 1;
697  task->input_name = input_name;
698  task->in_frame = in_frame;
699  task->output_name = output_names[0];
700  task->out_frame = out_frame;
701  task->ov_model = ov_model;
702  if (ff_queue_push_back(ov_model->task_queue, task) < 0) {
703  av_freep(&task);
704  av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n");
705  return DNN_ERROR;
706  }
707 
708  request = ff_safe_queue_pop_front(ov_model->request_queue);
709  if (!request) {
710  av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
711  return DNN_ERROR;
712  }
713 
714  request->tasks[request->task_count++] = task;
715  return execute_model_ov(request);
716 }
717 
719 {
720  OVModel *ov_model = model->model;
721  TaskItem *task = ff_queue_peek_front(ov_model->task_queue);
722 
723  if (!task) {
724  return DAST_EMPTY_QUEUE;
725  }
726 
727  if (!task->done) {
728  return DAST_NOT_READY;
729  }
730 
731  *in = task->in_frame;
732  *out = task->out_frame;
733  ff_queue_pop_front(ov_model->task_queue);
734  av_freep(&task);
735 
736  return DAST_SUCCESS;
737 }
738 
740 {
741  OVModel *ov_model = model->model;
742  OVContext *ctx = &ov_model->ctx;
743  RequestItem *request;
744  IEStatusCode status;
746 
747  request = ff_safe_queue_pop_front(ov_model->request_queue);
748  if (!request) {
749  av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
750  return DNN_ERROR;
751  }
752 
753  if (request->task_count == 0) {
754  // no pending task need to flush
755  if (ff_safe_queue_push_back(ov_model->request_queue, request) < 0) {
756  av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
757  return DNN_ERROR;
758  }
759  return DNN_SUCCESS;
760  }
761 
762  ret = fill_model_input_ov(ov_model, request);
763  if (ret != DNN_SUCCESS) {
764  av_log(ctx, AV_LOG_ERROR, "Failed to fill model input.\n");
765  return ret;
766  }
767  status = ie_infer_set_completion_callback(request->infer_request, &request->callback);
768  if (status != OK) {
769  av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n");
770  return DNN_ERROR;
771  }
772  status = ie_infer_request_infer_async(request->infer_request);
773  if (status != OK) {
774  av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n");
775  return DNN_ERROR;
776  }
777 
778  return DNN_SUCCESS;
779 }
780 
782 {
783  if (*model){
784  OVModel *ov_model = (*model)->model;
785  while (ff_safe_queue_size(ov_model->request_queue) != 0) {
787  if (item && item->infer_request) {
788  ie_infer_request_free(&item->infer_request);
789  }
790  av_freep(&item->tasks);
791  av_freep(&item);
792  }
794 
795  while (ff_queue_size(ov_model->task_queue) != 0) {
796  TaskItem *item = ff_queue_pop_front(ov_model->task_queue);
797  av_frame_free(&item->in_frame);
798  av_frame_free(&item->out_frame);
799  av_freep(&item);
800  }
801  ff_queue_destroy(ov_model->task_queue);
802 
803  if (ov_model->infer_request)
804  ie_infer_request_free(&ov_model->infer_request);
805  if (ov_model->exe_network)
806  ie_exec_network_free(&ov_model->exe_network);
807  if (ov_model->network)
808  ie_network_free(&ov_model->network);
809  if (ov_model->core)
810  ie_core_free(&ov_model->core);
811  av_freep(&ov_model);
812  av_freep(model);
813  }
814 }
execute_model_ov
static DNNReturnType execute_model_ov(RequestItem *request)
Definition: dnn_backend_openvino.c:387
OFFSET
#define OFFSET(x)
Definition: dnn_backend_openvino.c:84
status
they must not be accessed directly The fifo field contains the frames that are queued in the input for processing by the filter The status_in and status_out fields contains the queued status(EOF or error) of the link
TaskItem::ov_model
OVModel * ov_model
Definition: dnn_backend_openvino.c:63
OVContext::class
const AVClass * class
Definition: dnn_backend_openvino.c:45
opt.h
filter_ctx
static FilteringContext * filter_ctx
Definition: transcoding.c:48
RequestItem::callback
ie_complete_call_back_t callback
Definition: dnn_backend_openvino.c:77
av_opt_set_defaults
void av_opt_set_defaults(void *s)
Set the values of all AVOption fields to their default values.
Definition: opt.c:1358
ff_safe_queue_pop_front
void * ff_safe_queue_pop_front(SafeQueue *sq)
Definition: safe_queue.c:105
out
FILE * out
Definition: movenc.c:54
OVModel::exe_network
ie_executable_network_t * exe_network
Definition: dnn_backend_openvino.c:54
DNNFunctionType
DNNFunctionType
Definition: dnn_interface.h:51
TaskItem::output_name
const char * output_name
Definition: dnn_backend_openvino.c:66
output
filter_frame For filters that do not use the this method is called when a frame is pushed to the filter s input It can be called at any time except in a reentrant way If the input frame is enough to produce output
Definition: filter_design.txt:225
ff_queue_pop_front
void * ff_queue_pop_front(Queue *q)
Definition: queue.c:152
ff_queue_size
size_t ff_queue_size(Queue *q)
Definition: queue.c:89
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:203
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:318
TaskItem::done
int done
Definition: dnn_backend_openvino.c:70
AVFrame::width
int width
Definition: frame.h:376
AVFILTER_DEFINE_CLASS
AVFILTER_DEFINE_CLASS(dnn_openvino)
SafeQueue
Definition: safe_queue.c:46
AVOption
AVOption.
Definition: opt.h:248
ff_dnn_load_model_ov
DNNModel * ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx)
Definition: dnn_backend_openvino.c:552
OVModel::core
ie_core_t * core
Definition: dnn_backend_openvino.c:52
dnn_io_proc.h
TaskItem
Definition: dnn_backend_openvino.c:62
ff_safe_queue_push_front
int ff_safe_queue_push_front(SafeQueue *sq, void *v)
Definition: safe_queue.c:85
OVModel
Definition: dnn_backend_openvino.c:49
get_input_ov
static DNNReturnType get_input_ov(void *model, DNNData *input, const char *input_name)
Definition: dnn_backend_openvino.c:432
OVOptions::batch_size
int batch_size
Definition: dnn_backend_openvino.c:40
av_malloc
#define av_malloc(s)
Definition: tableprint_vlc.h:31
DNNModel::filter_ctx
AVFilterContext * filter_ctx
Definition: dnn_interface.h:72
ff_queue_create
Queue * ff_queue_create(void)
Definition: queue.c:48
ff_proc_from_dnn_to_frame
DNNReturnType ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx)
Definition: dnn_io_proc.c:26
ff_dnn_execute_model_async_ov
DNNReturnType ff_dnn_execute_model_async_ov(const DNNModel *model, const char *input_name, AVFrame *in_frame, const char **output_names, uint32_t nb_output, AVFrame *out_frame)
Definition: dnn_backend_openvino.c:663
DNN_SUCCESS
@ DNN_SUCCESS
Definition: dnn_interface.h:33
OVOptions::device_type
char * device_type
Definition: dnn_backend_openvino.c:38
DNNModel::get_output
DNNReturnType(* get_output)(void *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
Definition: dnn_interface.h:79
init_model_ov
static DNNReturnType init_model_ov(OVModel *ov_model, const char *input_name, const char *output_name)
Definition: dnn_backend_openvino.c:265
Queue
Definition: queue.c:34
av_frame_alloc
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition: frame.c:190
ff_queue_push_back
int ff_queue_push_back(Queue *q, void *v)
Definition: queue.c:131
avassert.h
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
APPEND_STRING
#define APPEND_STRING(generated_string, iterate_string)
Definition: dnn_backend_openvino.c:80
ff_queue_destroy
void ff_queue_destroy(Queue *q)
Definition: queue.c:73
ff_dnn_free_model_ov
void ff_dnn_free_model_ov(DNNModel **model)
Definition: dnn_backend_openvino.c:781
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:37
DNNReturnType
DNNReturnType
Definition: dnn_interface.h:33
DNNData
Definition: dnn_interface.h:58
ctx
AVFormatContext * ctx
Definition: movenc.c:48
DNNModel::get_input
DNNReturnType(* get_input)(void *model, DNNData *input, const char *input_name)
Definition: dnn_interface.h:77
ff_safe_queue_size
size_t ff_safe_queue_size(SafeQueue *sq)
Definition: safe_queue.c:80
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:67
NULL
#define NULL
Definition: coverity.c:32
fill_model_input_ov
static DNNReturnType fill_model_input_ov(OVModel *ov_model, RequestItem *request)
Definition: dnn_backend_openvino.c:124
DNNModel::pre_proc
int(* pre_proc)(AVFrame *frame_in, DNNData *model_input, AVFilterContext *filter_ctx)
Definition: dnn_interface.h:83
OVModel::network
ie_network_t * network
Definition: dnn_backend_openvino.c:53
ff_safe_queue_create
SafeQueue * ff_safe_queue_create(void)
Definition: safe_queue.c:52
av_opt_set_from_string
int av_opt_set_from_string(void *ctx, const char *opts, const char *const *shorthand, const char *key_val_sep, const char *pairs_sep)
Parse the key-value pairs list in opts.
Definition: opt.c:1559
RequestItem::infer_request
ie_infer_request_t * infer_request
Definition: dnn_backend_openvino.c:74
TaskItem::in_frame
AVFrame * in_frame
Definition: dnn_backend_openvino.c:65
OVModel::ctx
OVContext ctx
Definition: dnn_backend_openvino.c:50
av_cpu_count
int av_cpu_count(void)
Definition: cpu.c:275
get_datatype_size
static int get_datatype_size(DNNDataType dt)
Definition: dnn_backend_openvino.c:110
options
const OptionDef options[]
DAST_SUCCESS
@ DAST_SUCCESS
Definition: dnn_interface.h:48
queue.h
DNNModel::post_proc
int(* post_proc)(AVFrame *frame_out, DNNData *model_output, AVFilterContext *filter_ctx)
Definition: dnn_interface.h:86
DAST_EMPTY_QUEUE
@ DAST_EMPTY_QUEUE
Definition: dnn_interface.h:46
TaskItem::async
int async
Definition: dnn_backend_openvino.c:69
OVModel::model
DNNModel * model
Definition: dnn_backend_openvino.c:51
avio.h
DNNModel::func_type
DNNFunctionType func_type
Definition: dnn_interface.h:74
avpriv_report_missing_feature
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
ff_safe_queue_destroy
void ff_safe_queue_destroy(SafeQueue *sq)
Definition: safe_queue.c:69
DNNDataType
DNNDataType
Definition: dnn_interface.h:37
RequestItem
Definition: dnn_backend_openvino.c:73
DNN_FLOAT
@ DNN_FLOAT
Definition: dnn_interface.h:37
get_output_ov
static DNNReturnType get_output_ov(void *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
Definition: dnn_backend_openvino.c:482
input
and forward the test the status of outputs and forward it to the corresponding return FFERROR_NOT_READY If the filters stores internally one or a few frame for some input
Definition: filter_design.txt:172
ff_dnn_flush_ov
DNNReturnType ff_dnn_flush_ov(const DNNModel *model)
Definition: dnn_backend_openvino.c:739
ff_proc_from_frame_to_dnn
DNNReturnType ff_proc_from_frame_to_dnn(AVFrame *frame, DNNData *input, DNNFunctionType func_type, void *log_ctx)
Definition: dnn_io_proc.c:207
ff_safe_queue_push_back
int ff_safe_queue_push_back(SafeQueue *sq, void *v)
Definition: safe_queue.c:95
OVOptions::input_resizable
int input_resizable
Definition: dnn_backend_openvino.c:41
in
uint8_t pi<< 24) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0f/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t,(*(const uint8_t *) pi - 0x80) *(1.0/(1<< 7))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t,(*(const int16_t *) pi >> 8)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0f/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *) pi *(1.0/(1<< 15))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t,(*(const int32_t *) pi >> 24)+0x80) CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0f/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *) pi *(1.0/(1U<< 31))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8(lrintf(*(const float *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16(lrintf(*(const float *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *) pi *(1U<< 31)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8(lrint(*(const double *) pi *(1<< 7))+0x80)) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16(lrint(*(const double *) pi *(1<< 15)))) CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *) pi *(1U<< 31)))) #define SET_CONV_FUNC_GROUP(ofmt, ifmt) static void set_generic_function(AudioConvert *ac) { } void ff_audio_convert_free(AudioConvert **ac) { if(! *ac) return;ff_dither_free(&(*ac) ->dc);av_freep(ac);} AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt, int channels, int sample_rate, int apply_map) { AudioConvert *ac;int in_planar, out_planar;ac=av_mallocz(sizeof(*ac));if(!ac) return NULL;ac->avr=avr;ac->out_fmt=out_fmt;ac->in_fmt=in_fmt;ac->channels=channels;ac->apply_map=apply_map;if(avr->dither_method !=AV_RESAMPLE_DITHER_NONE &&av_get_packed_sample_fmt(out_fmt)==AV_SAMPLE_FMT_S16 &&av_get_bytes_per_sample(in_fmt) > 2) { ac->dc=ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, apply_map);if(!ac->dc) { av_free(ac);return NULL;} return ac;} in_planar=ff_sample_fmt_is_planar(in_fmt, channels);out_planar=ff_sample_fmt_is_planar(out_fmt, channels);if(in_planar==out_planar) { ac->func_type=CONV_FUNC_TYPE_FLAT;ac->planes=in_planar ? ac->channels :1;} else if(in_planar) ac->func_type=CONV_FUNC_TYPE_INTERLEAVE;else ac->func_type=CONV_FUNC_TYPE_DEINTERLEAVE;set_generic_function(ac);if(ARCH_AARCH64) ff_audio_convert_init_aarch64(ac);if(ARCH_ARM) ff_audio_convert_init_arm(ac);if(ARCH_X86) ff_audio_convert_init_x86(ac);return ac;} int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) { int use_generic=1;int len=in->nb_samples;int p;if(ac->dc) { av_log(ac->avr, AV_LOG_TRACE, "%d samples - audio_convert: %s to %s (dithered)\n", len, av_get_sample_fmt_name(ac->in_fmt), av_get_sample_fmt_name(ac->out_fmt));return ff_convert_dither(ac-> in
Definition: audio_convert.c:326
i
int i
Definition: input.c:407
dnn_openvino_options
static const AVOption dnn_openvino_options[]
Definition: dnn_backend_openvino.c:86
infer_completion_callback
static void infer_completion_callback(void *args)
Definition: dnn_backend_openvino.c:183
ff_dnn_get_async_result_ov
DNNAsyncStatusType ff_dnn_get_async_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out)
Definition: dnn_backend_openvino.c:718
av_malloc_array
#define av_malloc_array(a, b)
Definition: tableprint_vlc.h:32
RequestItem::task_count
int task_count
Definition: dnn_backend_openvino.c:76
precision_to_datatype
static DNNDataType precision_to_datatype(precision_e precision)
Definition: dnn_backend_openvino.c:96
DNN_ERROR
@ DNN_ERROR
Definition: dnn_interface.h:33
uint8_t
uint8_t
Definition: audio_convert.c:194
av_mallocz
void * av_mallocz(size_t size)
Allocate a memory block with alignment suitable for all memory accesses (including vectors if availab...
Definition: mem.c:237
safe_queue.h
OVModel::request_queue
SafeQueue * request_queue
Definition: dnn_backend_openvino.c:58
OVContext
Definition: dnn_backend_openvino.c:44
dnn_backend_openvino.h
ret
ret
Definition: filter_design.txt:187
RequestItem::tasks
TaskItem ** tasks
Definition: dnn_backend_openvino.c:75
DNN_UINT8
@ DNN_UINT8
Definition: dnn_interface.h:37
TaskItem::out_frame
AVFrame * out_frame
Definition: dnn_backend_openvino.c:67
OVModel::task_queue
Queue * task_queue
Definition: dnn_backend_openvino.c:59
AVFrame::height
int height
Definition: frame.h:376
TaskItem::do_ioproc
int do_ioproc
Definition: dnn_backend_openvino.c:68
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Definition: opt.h:225
ff_queue_peek_front
void * ff_queue_peek_front(Queue *q)
Definition: queue.c:94
AVFilterContext
An instance of a filter.
Definition: avfilter.h:341
DNNModel
Definition: dnn_interface.h:66
TaskItem::input_name
const char * input_name
Definition: dnn_backend_openvino.c:64
DNNModel::options
const char * options
Definition: dnn_interface.h:70
OVOptions::nireq
int nireq
Definition: dnn_backend_openvino.c:39
AV_OPT_TYPE_BOOL
@ AV_OPT_TYPE_BOOL
Definition: opt.h:242
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
OVOptions
Definition: dnn_backend_openvino.c:37
ff_dnn_execute_model_ov
DNNReturnType ff_dnn_execute_model_ov(const DNNModel *model, const char *input_name, AVFrame *in_frame, const char **output_names, uint32_t nb_output, AVFrame *out_frame)
Definition: dnn_backend_openvino.c:609
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
OVModel::infer_request
ie_infer_request_t * infer_request
Definition: dnn_backend_openvino.c:55
avstring.h
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Definition: opt.h:229
DCO_BGR
@ DCO_BGR
Definition: dnn_interface.h:41
DAST_NOT_READY
@ DAST_NOT_READY
Definition: dnn_interface.h:47
DNNAsyncStatusType
DNNAsyncStatusType
Definition: dnn_interface.h:44
DFT_PROCESS_FRAME
@ DFT_PROCESS_FRAME
Definition: dnn_interface.h:53
FLAGS
#define FLAGS
Definition: dnn_backend_openvino.c:85
OVContext::options
OVOptions options
Definition: dnn_backend_openvino.c:46
DNNModel::model
void * model
Definition: dnn_interface.h:68