FFmpeg
dnn_backend_tf.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018 Sergey Lavrushkin
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 tensorflow backend implementation.
24  */
25 
26 #include "libavformat/avio.h"
27 #include "libavutil/avassert.h"
28 #include "libavutil/avstring.h"
29 #include "libavutil/cpu.h"
30 #include "libavutil/mem.h"
31 #include "libavutil/opt.h"
32 #include "libavcodec/defs.h"
33 #include "dnn_io_proc.h"
34 #include "dnn_backend_common.h"
35 #include "safe_queue.h"
36 #include <tensorflow/c/c_api.h>
37 
38 typedef struct TFModel {
41  TF_Graph *graph;
42  TF_Session *session;
43  TF_Status *status;
47 } TFModel;
48 
49 /**
50  * Stores execution parameters for single
51  * call to the TensorFlow C API
52  */
53 typedef struct TFInferRequest {
54  TF_Output *tf_outputs;
55  TF_Tensor **output_tensors;
56  TF_Output *tf_input;
57  TF_Tensor *input_tensor;
59 
60 typedef struct TFRequestItem {
63  TF_Status *status;
66 
67 #define OFFSET(x) offsetof(TFOptions, x)
68 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
69 static const AVOption dnn_tensorflow_options[] = {
70  { "sess_config", "config for SessionOptions", OFFSET(sess_config), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
71  { NULL }
72 };
73 
74 
75 static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue);
76 static void infer_completion_callback(void *args);
77 static inline void destroy_request_item(TFRequestItem **arg);
78 
79 static void free_buffer(void *data, size_t length)
80 {
81  av_freep(&data);
82 }
83 
84 /**
85  * Free the contents of TensorFlow inference request.
86  * It does not free the TFInferRequest instance.
87  *
88  * @param request pointer to TFInferRequest instance.
89  * NULL pointer is allowed.
90  */
91 static void tf_free_request(TFInferRequest *request)
92 {
93  if (!request)
94  return;
95  if (request->input_tensor) {
96  TF_DeleteTensor(request->input_tensor);
97  request->input_tensor = NULL;
98  }
99  av_freep(&request->tf_input);
100  av_freep(&request->tf_outputs);
101  if (request->output_tensors) {
102  int nb_output = sizeof(*request->output_tensors)/sizeof(request->output_tensors[0]);
103  for (uint32_t i = 0; i < nb_output; ++i) {
104  if (request->output_tensors[i]) {
105  TF_DeleteTensor(request->output_tensors[i]);
106  request->output_tensors[i] = NULL;
107  }
108  }
109  av_freep(&request->output_tensors);
110  }
111 }
112 
113 /**
114  * Create a TensorFlow inference request. All properties
115  * are initially unallocated and set as NULL.
116  *
117  * @return pointer to the allocated TFInferRequest instance.
118  */
120 {
121  TFInferRequest *infer_request = av_malloc(sizeof(TFInferRequest));
122  if (!infer_request) {
123  return NULL;
124  }
125  infer_request->tf_outputs = NULL;
126  infer_request->tf_input = NULL;
127  infer_request->input_tensor = NULL;
128  infer_request->output_tensors = NULL;
129  return infer_request;
130 }
131 
132 /**
133  * Start synchronous inference for the TensorFlow model.
134  *
135  * @param request pointer to the TFRequestItem for inference
136  * @retval 0 if execution is successful
137  * @retval AVERROR(EINVAL) if request is NULL
138  * @retval DNN_GENERIC_ERROR if execution fails
139  */
140 static int tf_start_inference(void *args)
141 {
142  TFRequestItem *request = args;
143  TFInferRequest *infer_request = request->infer_request;
144  LastLevelTaskItem *lltask = request->lltask;
145  TaskItem *task = lltask->task;
146  TFModel *tf_model = task->model;
147 
148  if (!request) {
149  av_log(tf_model->ctx, AV_LOG_ERROR, "TFRequestItem is NULL\n");
150  return AVERROR(EINVAL);
151  }
152 
153  TF_SessionRun(tf_model->session, NULL,
154  infer_request->tf_input, &infer_request->input_tensor, 1,
155  infer_request->tf_outputs, infer_request->output_tensors,
156  task->nb_output, NULL, 0, NULL,
157  request->status);
158  if (TF_GetCode(request->status) != TF_OK) {
159  av_log(tf_model->ctx, AV_LOG_ERROR, "%s", TF_Message(request->status));
160  return DNN_GENERIC_ERROR;
161  }
162  return 0;
163 }
164 
165 /**
166  * Free the TFRequestItem completely.
167  *
168  * @param arg Address of the TFInferRequest instance.
169  */
170 static inline void destroy_request_item(TFRequestItem **arg) {
171  TFRequestItem *request;
172  if (!arg) {
173  return;
174  }
175  request = *arg;
176  tf_free_request(request->infer_request);
177  av_freep(&request->infer_request);
178  av_freep(&request->lltask);
179  TF_DeleteStatus(request->status);
181  av_freep(arg);
182 }
183 
184 static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
185 {
186  TFModel *tf_model = task->model;
187  DnnContext *ctx = tf_model->ctx;
188  LastLevelTaskItem *lltask = av_malloc(sizeof(*lltask));
189  if (!lltask) {
190  av_log(ctx, AV_LOG_ERROR, "Unable to allocate space for LastLevelTaskItem\n");
191  return AVERROR(ENOMEM);
192  }
193  task->inference_todo = 1;
194  task->inference_done = 0;
195  lltask->task = task;
196  if (ff_queue_push_back(lltask_queue, lltask) < 0) {
197  av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n");
198  av_freep(&lltask);
199  return AVERROR(ENOMEM);
200  }
201  return 0;
202 }
203 
204 static TF_Buffer *read_graph(const char *model_filename)
205 {
206  TF_Buffer *graph_buf;
207  unsigned char *graph_data = NULL;
208  AVIOContext *model_file_context;
209  long size, bytes_read;
210 
211  if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){
212  return NULL;
213  }
214 
215  size = avio_size(model_file_context);
216 
217  graph_data = av_malloc(size);
218  if (!graph_data){
219  avio_closep(&model_file_context);
220  return NULL;
221  }
222  bytes_read = avio_read(model_file_context, graph_data, size);
223  avio_closep(&model_file_context);
224  if (bytes_read != size){
225  av_freep(&graph_data);
226  return NULL;
227  }
228 
229  graph_buf = TF_NewBuffer();
230  graph_buf->data = graph_data;
231  graph_buf->length = size;
232  graph_buf->data_deallocator = free_buffer;
233 
234  return graph_buf;
235 }
236 
237 static TF_Tensor *allocate_input_tensor(const DNNData *input)
238 {
239  TF_DataType dt;
240  size_t size;
241  int64_t input_dims[4] = { 0 };
242 
243  input_dims[0] = 1;
244  input_dims[1] = input->dims[dnn_get_height_idx_by_layout(input->layout)];
245  input_dims[2] = input->dims[dnn_get_width_idx_by_layout(input->layout)];
246  input_dims[3] = input->dims[dnn_get_channel_idx_by_layout(input->layout)];
247  switch (input->dt) {
248  case DNN_FLOAT:
249  dt = TF_FLOAT;
250  size = sizeof(float);
251  break;
252  case DNN_UINT8:
253  dt = TF_UINT8;
254  size = 1;
255  break;
256  default:
257  av_assert0(!"should not reach here");
258  }
259 
260  return TF_AllocateTensor(dt, input_dims, 4,
261  input_dims[1] * input_dims[2] * input_dims[3] * size);
262 }
263 
264 static int get_input_tf(DNNModel *model, DNNData *input, const char *input_name)
265 {
266  TFModel *tf_model = (TFModel *)model;
267  DnnContext *ctx = tf_model->ctx;
268  TF_Status *status;
269  TF_DataType dt;
270  int64_t dims[4];
271 
272  TF_Output tf_output;
273  tf_output.oper = TF_GraphOperationByName(tf_model->graph, input_name);
274  if (!tf_output.oper) {
275  av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
276  return AVERROR(EINVAL);
277  }
278 
279  tf_output.index = 0;
280  dt = TF_OperationOutputType(tf_output);
281  switch (dt) {
282  case TF_FLOAT:
283  input->dt = DNN_FLOAT;
284  break;
285  case TF_UINT8:
286  input->dt = DNN_UINT8;
287  break;
288  default:
289  av_log(ctx, AV_LOG_ERROR, "Unsupported output type %d in model\n", dt);
290  return AVERROR(EINVAL);
291  }
292  input->order = DCO_RGB;
293 
294  status = TF_NewStatus();
295  TF_GraphGetTensorShape(tf_model->graph, tf_output, dims, 4, status);
296  if (TF_GetCode(status) != TF_OK){
297  TF_DeleteStatus(status);
298  av_log(ctx, AV_LOG_ERROR, "Failed to get input tensor shape: number of dimension incorrect\n");
299  return DNN_GENERIC_ERROR;
300  }
301  TF_DeleteStatus(status);
302 
303  // currently only NHWC is supported
304  av_assert0(dims[0] == 1 || dims[0] == -1);
305  for (int i = 0; i < 4; i++)
306  input->dims[i] = dims[i];
307  input->layout = DL_NHWC;
308 
309  return 0;
310 }
311 
312 static int get_output_tf(DNNModel *model, const char *input_name, int input_width, int input_height,
313  const char *output_name, int *output_width, int *output_height)
314 {
315  int ret;
316  TFModel *tf_model = (TFModel *)model;
317  DnnContext *ctx = tf_model->ctx;
318  TaskItem task;
319  TFRequestItem *request;
320  DNNExecBaseParams exec_params = {
321  .input_name = input_name,
322  .output_names = &output_name,
323  .nb_output = 1,
324  .in_frame = NULL,
325  .out_frame = NULL,
326  };
327 
328  ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, tf_model, input_height, input_width, ctx);
329  if (ret != 0) {
330  goto err;
331  }
332 
333  ret = extract_lltask_from_task(&task, tf_model->lltask_queue);
334  if (ret != 0) {
335  av_log(ctx, AV_LOG_ERROR, "unable to extract inference from task.\n");
336  goto err;
337  }
338 
339  request = ff_safe_queue_pop_front(tf_model->request_queue);
340  if (!request) {
341  av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
342  ret = AVERROR(EINVAL);
343  goto err;
344  }
345 
346  ret = execute_model_tf(request, tf_model->lltask_queue);
347  *output_width = task.out_frame->width;
348  *output_height = task.out_frame->height;
349 
350 err:
351  av_frame_free(&task.out_frame);
352  av_frame_free(&task.in_frame);
353  return ret;
354 }
355 
356 #define SPACE_CHARS " \t\r\n"
357 static int hex_to_data(uint8_t *data, const char *p)
358 {
359  int c, len, v;
360 
361  len = 0;
362  v = 1;
363  for (;;) {
364  p += strspn(p, SPACE_CHARS);
365  if (*p == '\0')
366  break;
367  c = av_toupper((unsigned char) *p++);
368  if (c >= '0' && c <= '9')
369  c = c - '0';
370  else if (c >= 'A' && c <= 'F')
371  c = c - 'A' + 10;
372  else
373  break;
374  v = (v << 4) | c;
375  if (v & 0x100) {
376  if (data) {
377  data[len] = v;
378  }
379  len++;
380  v = 1;
381  }
382  }
383  return len;
384 }
385 
386 static int load_tf_model(TFModel *tf_model, const char *model_filename)
387 {
388  DnnContext *ctx = tf_model->ctx;
389  TF_Buffer *graph_def;
390  TF_ImportGraphDefOptions *graph_opts;
391  TF_SessionOptions *sess_opts;
392  const TF_Operation *init_op;
393  uint8_t *sess_config = NULL;
394  int sess_config_length = 0;
395 
396  // prepare the sess config data
397  if (ctx->tf_option.sess_config != NULL) {
398  const char *config;
399  /*
400  tf_model->ctx.options.sess_config is hex to present the serialized proto
401  required by TF_SetConfig below, so we need to first generate the serialized
402  proto in a python script, tools/python/tf_sess_config.py is a script example
403  to generate the configs of sess_config.
404  */
405  if (strncmp(ctx->tf_option.sess_config, "0x", 2) != 0) {
406  av_log(ctx, AV_LOG_ERROR, "sess_config should start with '0x'\n");
407  return AVERROR(EINVAL);
408  }
409  config = ctx->tf_option.sess_config + 2;
410  sess_config_length = hex_to_data(NULL, config);
411 
412  sess_config = av_mallocz(sess_config_length + AV_INPUT_BUFFER_PADDING_SIZE);
413  if (!sess_config) {
414  av_log(ctx, AV_LOG_ERROR, "failed to allocate memory\n");
415  return AVERROR(ENOMEM);
416  }
417  if (hex_to_data(sess_config, config) < 0) {
418  av_log(ctx, AV_LOG_ERROR, "failed to convert hex to data\n");
419  return AVERROR(EINVAL);
420  }
421  }
422 
423  graph_def = read_graph(model_filename);
424  if (!graph_def){
425  av_log(ctx, AV_LOG_ERROR, "Failed to read model \"%s\" graph\n", model_filename);
426  av_freep(&sess_config);
427  return AVERROR(EINVAL);
428  }
429  tf_model->graph = TF_NewGraph();
430  tf_model->status = TF_NewStatus();
431  graph_opts = TF_NewImportGraphDefOptions();
432  TF_GraphImportGraphDef(tf_model->graph, graph_def, graph_opts, tf_model->status);
433  TF_DeleteImportGraphDefOptions(graph_opts);
434  TF_DeleteBuffer(graph_def);
435  if (TF_GetCode(tf_model->status) != TF_OK){
436  av_log(ctx, AV_LOG_ERROR, "Failed to import serialized graph to model graph\n");
437  av_freep(&sess_config);
438  return DNN_GENERIC_ERROR;
439  }
440 
441  init_op = TF_GraphOperationByName(tf_model->graph, "init");
442  sess_opts = TF_NewSessionOptions();
443 
444  if (sess_config) {
445  TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->status);
446  av_freep(&sess_config);
447  if (TF_GetCode(tf_model->status) != TF_OK) {
448  TF_DeleteSessionOptions(sess_opts);
449  av_log(ctx, AV_LOG_ERROR, "Failed to set config for sess options with %s\n",
450  ctx->tf_option.sess_config);
451  return DNN_GENERIC_ERROR;
452  }
453  }
454 
455  tf_model->session = TF_NewSession(tf_model->graph, sess_opts, tf_model->status);
456  TF_DeleteSessionOptions(sess_opts);
457  if (TF_GetCode(tf_model->status) != TF_OK)
458  {
459  av_freep(&sess_config);
460  av_log(ctx, AV_LOG_ERROR, "Failed to create new session with model graph\n");
461  return DNN_GENERIC_ERROR;
462  }
463 
464  // Run initialization operation with name "init" if it is present in graph
465  if (init_op){
466  TF_SessionRun(tf_model->session, NULL,
467  NULL, NULL, 0,
468  NULL, NULL, 0,
469  &init_op, 1, NULL, tf_model->status);
470  if (TF_GetCode(tf_model->status) != TF_OK)
471  {
472  av_freep(&sess_config);
473  av_log(ctx, AV_LOG_ERROR, "Failed to run session when initializing\n");
474  return DNN_GENERIC_ERROR;
475  }
476  }
477 
478  return 0;
479 }
480 
481 static void dnn_free_model_tf(DNNModel **model)
482 {
483  TFModel *tf_model;
484 
485  if (!model || !*model)
486  return;
487 
488  tf_model = (TFModel *)(*model);
489  ff_dnn_wait_requests(tf_model->request_queue, tf_model->ctx->nireq);
490  while (ff_safe_queue_size(tf_model->request_queue) != 0) {
492  destroy_request_item(&item);
493  }
495 
496  while (ff_queue_size(tf_model->lltask_queue) != 0) {
498  av_freep(&item);
499  }
500  ff_queue_destroy(tf_model->lltask_queue);
501 
502  while (ff_queue_size(tf_model->task_queue) != 0) {
503  TaskItem *item = ff_queue_pop_front(tf_model->task_queue);
504  av_frame_free(&item->in_frame);
505  av_frame_free(&item->out_frame);
506  av_freep(&item);
507  }
508  ff_queue_destroy(tf_model->task_queue);
509 
510  if (tf_model->graph){
511  TF_DeleteGraph(tf_model->graph);
512  }
513  if (tf_model->session){
514  TF_CloseSession(tf_model->session, tf_model->status);
515  TF_DeleteSession(tf_model->session, tf_model->status);
516  }
517  if (tf_model->status){
518  TF_DeleteStatus(tf_model->status);
519  }
520  av_freep(&tf_model);
521  *model = NULL;
522 }
523 
525 {
526  DNNModel *model = NULL;
527  TFModel *tf_model = NULL;
528 
529  tf_model = av_mallocz(sizeof(TFModel));
530  if (!tf_model)
531  return NULL;
532  model = &tf_model->model;
533  tf_model->ctx = ctx;
534 
535  if (load_tf_model(tf_model, ctx->model_filename) != 0){
536  av_log(ctx, AV_LOG_ERROR, "Failed to load TensorFlow model: \"%s\"\n", ctx->model_filename);
537  goto err;
538  }
539 
540  if (ctx->nireq <= 0) {
541  ctx->nireq = av_cpu_count() / 2 + 1;
542  }
543 
544 #if !HAVE_PTHREAD_CANCEL
545  if (ctx->async) {
546  ctx->async = 0;
547  av_log(filter_ctx, AV_LOG_WARNING, "pthread is not supported, roll back to sync.\n");
548  }
549 #endif
550 
551  tf_model->request_queue = ff_safe_queue_create();
552  if (!tf_model->request_queue) {
553  goto err;
554  }
555 
556  for (int i = 0; i < ctx->nireq; i++) {
557  TFRequestItem *item = av_mallocz(sizeof(*item));
558  if (!item) {
559  goto err;
560  }
561  item->lltask = NULL;
563  if (!item->infer_request) {
564  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for TensorFlow inference request\n");
565  av_freep(&item);
566  goto err;
567  }
568  item->status = TF_NewStatus();
571  item->exec_module.args = item;
572 
573  if (ff_safe_queue_push_back(tf_model->request_queue, item) < 0) {
574  destroy_request_item(&item);
575  goto err;
576  }
577  }
578 
579  tf_model->lltask_queue = ff_queue_create();
580  if (!tf_model->lltask_queue) {
581  goto err;
582  }
583 
584  tf_model->task_queue = ff_queue_create();
585  if (!tf_model->task_queue) {
586  goto err;
587  }
588 
589  model->get_input = &get_input_tf;
590  model->get_output = &get_output_tf;
591  model->filter_ctx = filter_ctx;
592  model->func_type = func_type;
593 
594  return model;
595 err:
596  dnn_free_model_tf(&model);
597  return NULL;
598 }
599 
600 static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request) {
601  DNNData input = { 0 };
602  LastLevelTaskItem *lltask;
603  TaskItem *task;
604  TFInferRequest *infer_request = NULL;
605  DnnContext *ctx = tf_model->ctx;
606  int ret = 0;
607 
608  lltask = ff_queue_pop_front(tf_model->lltask_queue);
609  av_assert0(lltask);
610  task = lltask->task;
611  request->lltask = lltask;
612 
613  ret = get_input_tf(&tf_model->model, &input, task->input_name);
614  if (ret != 0) {
615  goto err;
616  }
617 
618  infer_request = request->infer_request;
619  input.dims[1] = task->in_frame->height;
620  input.dims[2] = task->in_frame->width;
621 
622  infer_request->tf_input = av_malloc(sizeof(TF_Output));
623  if (!infer_request->tf_input) {
624  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
625  ret = AVERROR(ENOMEM);
626  goto err;
627  }
628 
629  infer_request->tf_input->oper = TF_GraphOperationByName(tf_model->graph, task->input_name);
630  if (!infer_request->tf_input->oper){
631  av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", task->input_name);
633  goto err;
634  }
635  infer_request->tf_input->index = 0;
636 
637  infer_request->input_tensor = allocate_input_tensor(&input);
638  if (!infer_request->input_tensor){
639  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
640  ret = AVERROR(ENOMEM);
641  goto err;
642  }
643  input.data = (float *)TF_TensorData(infer_request->input_tensor);
644 
645  switch (tf_model->model.func_type) {
646  case DFT_PROCESS_FRAME:
647  if (task->do_ioproc) {
648  if (tf_model->model.frame_pre_proc != NULL) {
649  tf_model->model.frame_pre_proc(task->in_frame, &input, tf_model->model.filter_ctx);
650  } else {
652  }
653  }
654  break;
657  break;
658  default:
659  avpriv_report_missing_feature(ctx, "model function type %d", tf_model->model.func_type);
660  break;
661  }
662 
663  infer_request->tf_outputs = av_malloc_array(task->nb_output, sizeof(TF_Output));
664  if (infer_request->tf_outputs == NULL) {
665  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *tf_outputs\n");
666  ret = AVERROR(ENOMEM);
667  goto err;
668  }
669 
670  infer_request->output_tensors = av_calloc(task->nb_output, sizeof(*infer_request->output_tensors));
671  if (!infer_request->output_tensors) {
672  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output tensor\n");
673  ret = AVERROR(ENOMEM);
674  goto err;
675  }
676 
677  for (int i = 0; i < task->nb_output; ++i) {
678  infer_request->output_tensors[i] = NULL;
679  infer_request->tf_outputs[i].oper = TF_GraphOperationByName(tf_model->graph, task->output_names[i]);
680  if (!infer_request->tf_outputs[i].oper) {
681  av_log(ctx, AV_LOG_ERROR, "Could not find output \"%s\" in model\n", task->output_names[i]);
683  goto err;
684  }
685  infer_request->tf_outputs[i].index = 0;
686  }
687 
688  return 0;
689 err:
690  tf_free_request(infer_request);
691  return ret;
692 }
693 
694 static void infer_completion_callback(void *args) {
695  TFRequestItem *request = args;
696  LastLevelTaskItem *lltask = request->lltask;
697  TaskItem *task = lltask->task;
698  DNNData *outputs;
699  TFInferRequest *infer_request = request->infer_request;
700  TFModel *tf_model = task->model;
701  DnnContext *ctx = tf_model->ctx;
702 
703  outputs = av_calloc(task->nb_output, sizeof(*outputs));
704  if (!outputs) {
705  av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *outputs\n");
706  goto err;
707  }
708 
709  for (uint32_t i = 0; i < task->nb_output; ++i) {
711  TF_Dim(infer_request->output_tensors[i], 1);
713  TF_Dim(infer_request->output_tensors[i], 2);
715  TF_Dim(infer_request->output_tensors[i], 3);
716  outputs[i].data = TF_TensorData(infer_request->output_tensors[i]);
717  outputs[i].dt = (DNNDataType)TF_TensorType(infer_request->output_tensors[i]);
718  }
719  switch (tf_model->model.func_type) {
720  case DFT_PROCESS_FRAME:
721  //it only support 1 output if it's frame in & frame out
722  if (task->do_ioproc) {
723  if (tf_model->model.frame_post_proc != NULL) {
724  tf_model->model.frame_post_proc(task->out_frame, outputs, tf_model->model.filter_ctx);
725  } else {
727  }
728  } else {
729  task->out_frame->width =
731  task->out_frame->height =
733  }
734  break;
736  if (!tf_model->model.detect_post_proc) {
737  av_log(ctx, AV_LOG_ERROR, "Detect filter needs provide post proc\n");
738  return;
739  }
740  tf_model->model.detect_post_proc(task->in_frame, outputs, task->nb_output, tf_model->model.filter_ctx);
741  break;
742  default:
743  av_log(ctx, AV_LOG_ERROR, "Tensorflow backend does not support this kind of dnn filter now\n");
744  goto err;
745  }
746  task->inference_done++;
747 err:
748  tf_free_request(infer_request);
749  av_freep(&outputs);
750 
751  if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
752  destroy_request_item(&request);
753  av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
754  }
755 }
756 
757 static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue)
758 {
759  TFModel *tf_model;
760  DnnContext *ctx;
761  LastLevelTaskItem *lltask;
762  TaskItem *task;
763  int ret = 0;
764 
765  if (ff_queue_size(lltask_queue) == 0) {
766  destroy_request_item(&request);
767  return 0;
768  }
769 
770  lltask = ff_queue_peek_front(lltask_queue);
771  task = lltask->task;
772  tf_model = task->model;
773  ctx = tf_model->ctx;
774 
775  ret = fill_model_input_tf(tf_model, request);
776  if (ret != 0) {
777  goto err;
778  }
779 
780  if (task->async) {
781  if (ff_dnn_start_inference_async(ctx, &request->exec_module) != 0) {
782  goto err;
783  }
784  return 0;
785  }
786  else {
787  ret = tf_start_inference(request);
788  if (ret != 0) {
789  goto err;
790  }
791  infer_completion_callback(request);
792  return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
793  }
794 err:
795  tf_free_request(request->infer_request);
796  if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
797  destroy_request_item(&request);
798  }
799 
800  return ret;
801 }
802 
803 static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params)
804 {
805  TFModel *tf_model = (TFModel *)model;
806  DnnContext *ctx = tf_model->ctx;
807  TaskItem *task;
808  TFRequestItem *request;
809  int ret = 0;
810 
811  ret = ff_check_exec_params(ctx, DNN_TF, model->func_type, exec_params);
812  if (ret != 0) {
813  return ret;
814  }
815 
816  task = av_malloc(sizeof(*task));
817  if (!task) {
818  av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n");
819  return AVERROR(ENOMEM);
820  }
821 
822  ret = ff_dnn_fill_task(task, exec_params, tf_model, ctx->async, 1);
823  if (ret != 0) {
824  av_log(ctx, AV_LOG_ERROR, "Fill task with invalid parameter(s).\n");
825  av_freep(&task);
826  return ret;
827  }
828 
829  if (ff_queue_push_back(tf_model->task_queue, task) < 0) {
830  av_freep(&task);
831  av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n");
832  return AVERROR(ENOMEM);
833  }
834 
835  ret = extract_lltask_from_task(task, tf_model->lltask_queue);
836  if (ret != 0) {
837  av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n");
838  return ret;
839  }
840 
841  request = ff_safe_queue_pop_front(tf_model->request_queue);
842  if (!request) {
843  av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
844  return AVERROR(EINVAL);
845  }
846  return execute_model_tf(request, tf_model->lltask_queue);
847 }
848 
850 {
851  TFModel *tf_model = (TFModel *)model;
852  return ff_dnn_get_result_common(tf_model->task_queue, in, out);
853 }
854 
855 static int dnn_flush_tf(const DNNModel *model)
856 {
857  TFModel *tf_model = (TFModel *)model;
858  DnnContext *ctx = tf_model->ctx;
859  TFRequestItem *request;
860  int ret;
861 
862  if (ff_queue_size(tf_model->lltask_queue) == 0) {
863  // no pending task need to flush
864  return 0;
865  }
866 
867  request = ff_safe_queue_pop_front(tf_model->request_queue);
868  if (!request) {
869  av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
870  return AVERROR(EINVAL);
871  }
872 
873  ret = fill_model_input_tf(tf_model, request);
874  if (ret != 0) {
875  av_log(ctx, AV_LOG_ERROR, "Failed to fill model input.\n");
876  if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
877  destroy_request_item(&request);
878  }
879  return ret;
880  }
881 
882  return ff_dnn_start_inference_async(ctx, &request->exec_module);
883 }
884 
886  .clazz = DNN_DEFINE_CLASS(dnn_tensorflow),
887  .type = DNN_TF,
888  .load_model = dnn_load_model_tf,
889  .execute_model = dnn_execute_model_tf,
890  .get_result = dnn_get_result_tf,
891  .flush = dnn_flush_tf,
892  .free_model = dnn_free_model_tf,
893 };
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
TFInferRequest
Stores execution parameters for single call to the TensorFlow C API.
Definition: dnn_backend_tf.c:53
TFInferRequest::tf_outputs
TF_Output * tf_outputs
Definition: dnn_backend_tf.c:54
execute_model_tf
static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue)
Definition: dnn_backend_tf.c:757
FLAGS
#define FLAGS
Definition: dnn_backend_tf.c:68
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
TFModel::graph
TF_Graph * graph
Definition: dnn_backend_tf.c:41
ff_safe_queue_pop_front
void * ff_safe_queue_pop_front(SafeQueue *sq)
Remove and free first element from the queue in SafeQueue.
Definition: safe_queue.c:105
out
static FILE * out
Definition: movenc.c:55
DNNAsyncExecModule
Common Async Execution Mechanism for the DNN Backends.
Definition: dnn_backend_common.h:66
DNNFunctionType
DNNFunctionType
Definition: dnn_interface.h:57
extract_lltask_from_task
static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
Definition: dnn_backend_tf.c:184
int64_t
long long int64_t
Definition: coverity.c:34
ff_queue_pop_front
void * ff_queue_pop_front(Queue *q)
Remove and free first element from the Queue.
Definition: queue.c:151
ff_check_exec_params
int ff_check_exec_params(void *ctx, DNNBackendType backend, DNNFunctionType func_type, DNNExecBaseParams *exec_params)
Definition: dnn_backend_common.c:31
ff_queue_size
size_t ff_queue_size(Queue *q)
Return the length of the Queue.
Definition: queue.c:88
DNN_GENERIC_ERROR
#define DNN_GENERIC_ERROR
Definition: dnn_interface.h:33
av_frame_free
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition: frame.c:64
LastLevelTaskItem
Definition: dnn_backend_common.h:58
AVFrame
This structure describes decoded (raw) audio or video data.
Definition: frame.h:466
AVFrame::width
int width
Definition: frame.h:538
SafeQueue
Double-ended queue with mutex locks ensuring data consistency while multithreading.
Definition: safe_queue.c:46
AVOption
AVOption.
Definition: opt.h:428
DNNModel::frame_pre_proc
FramePrePostProc frame_pre_proc
Definition: dnn_interface.h:111
av_cpu_count
int av_cpu_count(void)
Definition: cpu.c:228
TFInferRequest::input_tensor
TF_Tensor * input_tensor
Definition: dnn_backend_tf.c:57
data
const char data[16]
Definition: mxf.c:149
avio_open
int avio_open(AVIOContext **s, const char *filename, int flags)
Create and initialize a AVIOContext for accessing the resource indicated by url.
Definition: avio.c:503
TFModel::model
DNNModel model
Definition: dnn_backend_tf.c:39
DNNExecBaseParams::input_name
const char * input_name
Definition: dnn_interface.h:82
load_tf_model
static int load_tf_model(TFModel *tf_model, const char *model_filename)
Definition: dnn_backend_tf.c:386
dnn_io_proc.h
TFModel::request_queue
SafeQueue * request_queue
Definition: dnn_backend_tf.c:44
TaskItem
Definition: dnn_backend_common.h:44
DNNAsyncExecModule::callback
void(* callback)(void *args)
Completion Callback for the backend.
Definition: dnn_backend_common.h:78
avio_size
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition: aviobuf.c:326
tf_sess_config.config
config
Definition: tf_sess_config.py:33
OFFSET
#define OFFSET(x)
Definition: dnn_backend_tf.c:67
cpu.h
destroy_request_item
static void destroy_request_item(TFRequestItem **arg)
Free the TFRequestItem completely.
Definition: dnn_backend_tf.c:170
DNNModel::filter_ctx
AVFilterContext * filter_ctx
Definition: dnn_interface.h:100
ff_queue_create
Queue * ff_queue_create(void)
Create a Queue instance.
Definition: queue.c:47
dnn_get_width_idx_by_layout
static int dnn_get_width_idx_by_layout(DNNLayout layout)
Definition: dnn_interface.h:209
TaskItem::model
void * model
Definition: dnn_backend_common.h:45
DnnContext
Definition: dnn_interface.h:151
get_input_tf
static int get_input_tf(DNNModel *model, DNNData *input, const char *input_name)
Definition: dnn_backend_tf.c:264
filter_ctx
static FilteringContext * filter_ctx
Definition: transcode.c:52
dnn_load_model_tf
static DNNModel * dnn_load_model_tf(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx)
Definition: dnn_backend_tf.c:524
DL_NHWC
@ DL_NHWC
Definition: dnn_interface.h:67
ff_dnn_wait_requests
void ff_dnn_wait_requests(SafeQueue *request_queue, int nireq)
Wait for all inference requests to complete before teardown.
Definition: dnn_backend_common.c:106
SPACE_CHARS
#define SPACE_CHARS
Definition: dnn_backend_tf.c:356
Queue
Linear double-ended data structure.
Definition: executor.c:51
ff_queue_push_back
int ff_queue_push_back(Queue *q, void *v)
Add data to the tail of the queue.
Definition: queue.c:130
avassert.h
DNN_TF
@ DNN_TF
Definition: dnn_interface.h:36
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
fill_model_input_tf
static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request)
Definition: dnn_backend_tf.c:600
TFRequestItem::exec_module
DNNAsyncExecModule exec_module
Definition: dnn_backend_tf.c:64
float
float
Definition: af_crystalizer.c:122
LastLevelTaskItem::task
TaskItem * task
Definition: dnn_backend_common.h:59
TFModel::ctx
DnnContext * ctx
Definition: dnn_backend_tf.c:40
read_graph
static TF_Buffer * read_graph(const char *model_filename)
Definition: dnn_backend_tf.c:204
ff_queue_destroy
void ff_queue_destroy(Queue *q)
Destroy the Queue instance.
Definition: queue.c:72
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
DNNData
Definition: dnn_interface.h:70
DNNModule::clazz
const AVClass clazz
Definition: dnn_interface.h:188
dnn_tensorflow_options
static const AVOption dnn_tensorflow_options[]
Definition: dnn_backend_tf.c:69
ff_dnn_fill_gettingoutput_task
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.
Definition: dnn_backend_common.c:165
DNNModel::get_output
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)
Definition: dnn_interface.h:107
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
TaskItem::inference_todo
uint32_t inference_todo
Definition: dnn_backend_common.h:53
av_mallocz
#define av_mallocz(s)
Definition: tableprint_vlc.h:31
arg
const char * arg
Definition: jacosubdec.c:65
ff_safe_queue_size
size_t ff_safe_queue_size(SafeQueue *sq)
Return the length of the SafeQueue.
Definition: safe_queue.c:80
ff_proc_from_frame_to_dnn
int ff_proc_from_frame_to_dnn(AVFrame *frame, DNNData *input, void *log_ctx)
Definition: dnn_io_proc.c:182
ff_frame_to_dnn_detect
int ff_frame_to_dnn_detect(AVFrame *frame, DNNData *input, void *log_ctx)
Definition: dnn_io_proc.c:423
NULL
#define NULL
Definition: coverity.c:32
ff_safe_queue_create
SafeQueue * ff_safe_queue_create(void)
Create and initialize a SafeQueue instance.
Definition: safe_queue.c:52
DNNModel::frame_post_proc
FramePrePostProc frame_post_proc
Definition: dnn_interface.h:114
tf_create_inference_request
static TFInferRequest * tf_create_inference_request(void)
Create a TensorFlow inference request.
Definition: dnn_backend_tf.c:119
ff_dnn_async_module_cleanup
int ff_dnn_async_module_cleanup(DNNAsyncExecModule *async_module)
Join the Async Execution thread and set module pointers to NULL.
Definition: dnn_backend_common.c:87
TFModel::task_queue
Queue * task_queue
Definition: dnn_backend_tf.c:46
infer_completion_callback
static void infer_completion_callback(void *args)
Definition: dnn_backend_tf.c:694
TaskItem::in_frame
AVFrame * in_frame
Definition: dnn_backend_common.h:46
TFModel::status
TF_Status * status
Definition: dnn_backend_tf.c:43
tf_free_request
static void tf_free_request(TFInferRequest *request)
Free the contents of TensorFlow inference request.
Definition: dnn_backend_tf.c:91
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
DnnContext::nireq
int nireq
Definition: dnn_interface.h:167
AVIOContext
Bytestream IO Context.
Definition: avio.h:160
TaskItem::async
uint8_t async
Definition: dnn_backend_common.h:50
TaskItem::inference_done
uint32_t inference_done
Definition: dnn_backend_common.h:54
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
DNNModel::detect_post_proc
DetectPostProc detect_post_proc
Definition: dnn_interface.h:116
size
int size
Definition: twinvq_data.h:10344
avio.h
DNNModel::func_type
DNNFunctionType func_type
Definition: dnn_interface.h:102
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.
dnn_flush_tf
static int dnn_flush_tf(const DNNModel *model)
Definition: dnn_backend_tf.c:855
ff_safe_queue_destroy
void ff_safe_queue_destroy(SafeQueue *sq)
Destroy the SafeQueue instance.
Definition: safe_queue.c:69
DNNDataType
DNNDataType
Definition: dnn_interface.h:42
hex_to_data
static int hex_to_data(uint8_t *data, const char *p)
Definition: dnn_backend_tf.c:357
DNN_FLOAT
@ DNN_FLOAT
Definition: dnn_interface.h:42
get_output_tf
static int get_output_tf(DNNModel *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
Definition: dnn_backend_tf.c:312
tf_start_inference
static int tf_start_inference(void *args)
Start synchronous inference for the TensorFlow model.
Definition: dnn_backend_tf.c:140
ff_dnn_fill_task
int ff_dnn_fill_task(TaskItem *task, DNNExecBaseParams *exec_params, void *backend_model, int async, int do_ioproc)
Fill the Task for Backend Execution.
Definition: dnn_backend_common.c:51
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
DNN_DEFINE_CLASS
#define DNN_DEFINE_CLASS(fname)
Definition: dnn_backend_common.h:40
ff_safe_queue_push_back
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:95
layout
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel layout
Definition: filter_design.txt:18
ff_dnn_backend_tf
const DNNModule ff_dnn_backend_tf
Definition: dnn_backend_tf.c:885
dnn_execute_model_tf
static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params)
Definition: dnn_backend_tf.c:803
av_malloc
#define av_malloc(s)
Definition: ops_asmgen.c:44
DFT_ANALYTICS_DETECT
@ DFT_ANALYTICS_DETECT
Definition: dnn_interface.h:60
TFRequestItem::status
TF_Status * status
Definition: dnn_backend_tf.c:63
TFInferRequest::output_tensors
TF_Tensor ** output_tensors
Definition: dnn_backend_tf.c:55
TFModel::session
TF_Session * session
Definition: dnn_backend_tf.c:42
TFRequestItem::infer_request
TFInferRequest * infer_request
Definition: dnn_backend_tf.c:61
av_malloc_array
#define av_malloc_array(a, b)
Definition: tableprint_vlc.h:32
DNNAsyncExecModule::start_inference
int(* start_inference)(void *request)
Synchronous inference function for the backend with corresponding request item as the argument.
Definition: dnn_backend_common.h:71
DNNAsyncExecModule::args
void * args
Argument for the execution functions.
Definition: dnn_backend_common.h:84
av_toupper
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition: avstring.h:227
safe_queue.h
TaskItem::output_names
const char ** output_names
Definition: dnn_backend_common.h:49
len
int len
Definition: vorbis_enc_data.h:426
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:264
outputs
static const AVFilterPad outputs[]
Definition: af_aap.c:310
TFInferRequest::tf_input
TF_Output * tf_input
Definition: dnn_backend_tf.c:56
ret
ret
Definition: filter_design.txt:187
DNN_UINT8
@ DNN_UINT8
Definition: dnn_interface.h:42
TFModel
Definition: dnn_backend_tf.c:38
AV_INPUT_BUFFER_PADDING_SIZE
#define AV_INPUT_BUFFER_PADDING_SIZE
Definition: defs.h:40
dnn_get_result_tf
static DNNAsyncStatusType dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out)
Definition: dnn_backend_tf.c:849
TaskItem::out_frame
AVFrame * out_frame
Definition: dnn_backend_common.h:47
AVFrame::height
int height
Definition: frame.h:538
status
ov_status_e status
Definition: dnn_backend_openvino.c:100
allocate_input_tensor
static TF_Tensor * allocate_input_tensor(const DNNData *input)
Definition: dnn_backend_tf.c:237
dnn_backend_common.h
TFRequestItem::lltask
LastLevelTaskItem * lltask
Definition: dnn_backend_tf.c:62
defs.h
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:615
ff_dnn_get_result_common
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.
Definition: dnn_backend_common.c:145
ff_queue_peek_front
void * ff_queue_peek_front(Queue *q)
Return a pointer to the data at the head of the queue.
Definition: queue.c:93
DCO_RGB
@ DCO_RGB
Definition: dnn_interface.h:47
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
AVFilterContext
An instance of a filter.
Definition: avfilter.h:273
ff_dnn_start_inference_async
int ff_dnn_start_inference_async(void *ctx, DNNAsyncExecModule *async_module)
Start asynchronous inference routine for the TensorFlow model on a detached thread.
Definition: dnn_backend_common.c:114
DNNModel
Definition: dnn_interface.h:98
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:617
mem.h
dnn_get_height_idx_by_layout
static int dnn_get_height_idx_by_layout(DNNLayout layout)
Definition: dnn_interface.h:214
dnn_free_model_tf
static void dnn_free_model_tf(DNNModel **model)
Definition: dnn_backend_tf.c:481
TaskItem::input_name
const char * input_name
Definition: dnn_backend_common.h:48
dnn_get_channel_idx_by_layout
static int dnn_get_channel_idx_by_layout(DNNLayout layout)
Definition: dnn_interface.h:219
avio_closep
int avio_closep(AVIOContext **s)
Close the resource accessed by the AVIOContext *s, free it and set the pointer pointing to it to NULL...
Definition: avio.c:655
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
free_buffer
static void free_buffer(void *data, size_t length)
Definition: dnn_backend_tf.c:79
DNNExecBaseParams
Definition: dnn_interface.h:81
DNNModel::get_input
int(* get_input)(struct DNNModel *model, DNNData *input, const char *input_name)
Definition: dnn_interface.h:105
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
TaskItem::do_ioproc
uint8_t do_ioproc
Definition: dnn_backend_common.h:51
avstring.h
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition: opt.h:275
TFRequestItem
Definition: dnn_backend_tf.c:60
DNNAsyncStatusType
DNNAsyncStatusType
Definition: dnn_interface.h:50
DFT_PROCESS_FRAME
@ DFT_PROCESS_FRAME
Definition: dnn_interface.h:59
TFModel::lltask_queue
Queue * lltask_queue
Definition: dnn_backend_tf.c:45
TaskItem::nb_output
uint32_t nb_output
Definition: dnn_backend_common.h:52
DNNModule
Definition: dnn_interface.h:187
ff_proc_from_dnn_to_frame
int ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx)
Definition: dnn_io_proc.c:42