• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "dnn_backend_tf.h"
27 #include "dnn_backend_native.h"
28 #include "dnn_backend_native_layer_conv2d.h"
29 #include "dnn_backend_native_layer_depth2space.h"
30 #include "libavformat/avio.h"
31 #include "libavutil/avassert.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/cpu.h"
34 #include "libavcodec/defs.h"
35 #include "../internal.h"
36 #include "dnn_backend_native_layer_pad.h"
37 #include "dnn_backend_native_layer_maximum.h"
38 #include "dnn_io_proc.h"
39 #include "dnn_backend_common.h"
40 #include "safe_queue.h"
41 #include <tensorflow/c/c_api.h>
42 
43 typedef struct TFOptions{
44     char *sess_config;
45     uint8_t async;
46     uint32_t nireq;
47 } TFOptions;
48 
49 typedef struct TFContext {
50     const AVClass *class;
51     TFOptions options;
52 } TFContext;
53 
54 typedef struct TFModel{
55     TFContext ctx;
56     DNNModel *model;
57     TF_Graph *graph;
58     TF_Session *session;
59     TF_Status *status;
60     SafeQueue *request_queue;
61     Queue *lltask_queue;
62     Queue *task_queue;
63 } TFModel;
64 
65 /**
66  * Stores execution parameters for single
67  * call to the TensorFlow C API
68  */
69 typedef struct TFInferRequest {
70     TF_Output *tf_outputs;
71     TF_Tensor **output_tensors;
72     TF_Output *tf_input;
73     TF_Tensor *input_tensor;
74 } TFInferRequest;
75 
76 typedef struct TFRequestItem {
77     TFInferRequest *infer_request;
78     LastLevelTaskItem *lltask;
79     TF_Status *status;
80     DNNAsyncExecModule exec_module;
81 } TFRequestItem;
82 
83 #define OFFSET(x) offsetof(TFContext, x)
84 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM
85 static const AVOption dnn_tensorflow_options[] = {
86     { "sess_config", "config for SessionOptions", OFFSET(options.sess_config), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
87     DNN_BACKEND_COMMON_OPTIONS
88     { NULL }
89 };
90 
91 AVFILTER_DEFINE_CLASS(dnn_tensorflow);
92 
93 static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue);
94 static void infer_completion_callback(void *args);
95 static inline void destroy_request_item(TFRequestItem **arg);
96 
free_buffer(void * data,size_t length)97 static void free_buffer(void *data, size_t length)
98 {
99     av_freep(&data);
100 }
101 
102 /**
103  * Free the contents of TensorFlow inference request.
104  * It does not free the TFInferRequest instance.
105  *
106  * @param request pointer to TFInferRequest instance.
107  * NULL pointer is allowed.
108  */
tf_free_request(TFInferRequest * request)109 static void tf_free_request(TFInferRequest *request)
110 {
111     if (!request)
112         return;
113     if (request->input_tensor) {
114         TF_DeleteTensor(request->input_tensor);
115         request->input_tensor = NULL;
116     }
117     av_freep(&request->tf_input);
118     av_freep(&request->tf_outputs);
119     if (request->output_tensors) {
120         int nb_output = sizeof(*request->output_tensors)/sizeof(request->output_tensors[0]);
121         for (uint32_t i = 0; i < nb_output; ++i) {
122             if (request->output_tensors[i]) {
123                 TF_DeleteTensor(request->output_tensors[i]);
124                 request->output_tensors[i] = NULL;
125             }
126         }
127         av_freep(&request->output_tensors);
128     }
129 }
130 
131 /**
132  * Create a TensorFlow inference request. All properties
133  * are initially unallocated and set as NULL.
134  *
135  * @return pointer to the allocated TFInferRequest instance.
136  */
tf_create_inference_request(void)137 static TFInferRequest *tf_create_inference_request(void)
138 {
139     TFInferRequest *infer_request = av_malloc(sizeof(TFInferRequest));
140     if (!infer_request) {
141         return NULL;
142     }
143     infer_request->tf_outputs = NULL;
144     infer_request->tf_input = NULL;
145     infer_request->input_tensor = NULL;
146     infer_request->output_tensors = NULL;
147     return infer_request;
148 }
149 
150 /**
151  * Start synchronous inference for the TensorFlow model.
152  *
153  * @param request pointer to the TFRequestItem for inference
154  * @retval 0 if execution is successful
155  * @retval AVERROR(EINVAL) if request is NULL
156  * @retval DNN_GENERIC_ERROR if execution fails
157  */
tf_start_inference(void * args)158 static int tf_start_inference(void *args)
159 {
160     TFRequestItem *request = args;
161     TFInferRequest *infer_request = request->infer_request;
162     LastLevelTaskItem *lltask = request->lltask;
163     TaskItem *task = lltask->task;
164     TFModel *tf_model = task->model;
165 
166     if (!request) {
167         av_log(&tf_model->ctx, AV_LOG_ERROR, "TFRequestItem is NULL\n");
168         return AVERROR(EINVAL);
169     }
170 
171     TF_SessionRun(tf_model->session, NULL,
172                   infer_request->tf_input, &infer_request->input_tensor, 1,
173                   infer_request->tf_outputs, infer_request->output_tensors,
174                   task->nb_output, NULL, 0, NULL,
175                   request->status);
176     if (TF_GetCode(request->status) != TF_OK) {
177         av_log(&tf_model->ctx, AV_LOG_ERROR, "%s", TF_Message(request->status));
178         tf_free_request(infer_request);
179         if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
180             destroy_request_item(&request);
181         }
182         return DNN_GENERIC_ERROR;
183     }
184     return 0;
185 }
186 
187 /**
188  * Free the TFRequestItem completely.
189  *
190  * @param arg Address of the TFInferRequest instance.
191  */
destroy_request_item(TFRequestItem ** arg)192 static inline void destroy_request_item(TFRequestItem **arg) {
193     TFRequestItem *request;
194     if (!arg) {
195         return;
196     }
197     request = *arg;
198     tf_free_request(request->infer_request);
199     av_freep(&request->infer_request);
200     av_freep(&request->lltask);
201     TF_DeleteStatus(request->status);
202     ff_dnn_async_module_cleanup(&request->exec_module);
203     av_freep(arg);
204 }
205 
extract_lltask_from_task(TaskItem * task,Queue * lltask_queue)206 static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
207 {
208     TFModel *tf_model = task->model;
209     TFContext *ctx = &tf_model->ctx;
210     LastLevelTaskItem *lltask = av_malloc(sizeof(*lltask));
211     if (!lltask) {
212         av_log(ctx, AV_LOG_ERROR, "Unable to allocate space for LastLevelTaskItem\n");
213         return AVERROR(ENOMEM);
214     }
215     task->inference_todo = 1;
216     task->inference_done = 0;
217     lltask->task = task;
218     if (ff_queue_push_back(lltask_queue, lltask) < 0) {
219         av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n");
220         av_freep(&lltask);
221         return AVERROR(ENOMEM);
222     }
223     return 0;
224 }
225 
read_graph(const char * model_filename)226 static TF_Buffer *read_graph(const char *model_filename)
227 {
228     TF_Buffer *graph_buf;
229     unsigned char *graph_data = NULL;
230     AVIOContext *model_file_context;
231     long size, bytes_read;
232 
233     if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){
234         return NULL;
235     }
236 
237     size = avio_size(model_file_context);
238 
239     graph_data = av_malloc(size);
240     if (!graph_data){
241         avio_closep(&model_file_context);
242         return NULL;
243     }
244     bytes_read = avio_read(model_file_context, graph_data, size);
245     avio_closep(&model_file_context);
246     if (bytes_read != size){
247         av_freep(&graph_data);
248         return NULL;
249     }
250 
251     graph_buf = TF_NewBuffer();
252     graph_buf->data = graph_data;
253     graph_buf->length = size;
254     graph_buf->data_deallocator = free_buffer;
255 
256     return graph_buf;
257 }
258 
allocate_input_tensor(const DNNData * input)259 static TF_Tensor *allocate_input_tensor(const DNNData *input)
260 {
261     TF_DataType dt;
262     size_t size;
263     int64_t input_dims[] = {1, input->height, input->width, input->channels};
264     switch (input->dt) {
265     case DNN_FLOAT:
266         dt = TF_FLOAT;
267         size = sizeof(float);
268         break;
269     case DNN_UINT8:
270         dt = TF_UINT8;
271         size = 1;
272         break;
273     default:
274         av_assert0(!"should not reach here");
275     }
276 
277     return TF_AllocateTensor(dt, input_dims, 4,
278                              input_dims[1] * input_dims[2] * input_dims[3] * size);
279 }
280 
get_input_tf(void * model,DNNData * input,const char * input_name)281 static int get_input_tf(void *model, DNNData *input, const char *input_name)
282 {
283     TFModel *tf_model = model;
284     TFContext *ctx = &tf_model->ctx;
285     TF_Status *status;
286     int64_t dims[4];
287 
288     TF_Output tf_output;
289     tf_output.oper = TF_GraphOperationByName(tf_model->graph, input_name);
290     if (!tf_output.oper) {
291         av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name);
292         return AVERROR(EINVAL);
293     }
294 
295     tf_output.index = 0;
296     input->dt = TF_OperationOutputType(tf_output);
297     input->order = DCO_RGB;
298 
299     status = TF_NewStatus();
300     TF_GraphGetTensorShape(tf_model->graph, tf_output, dims, 4, status);
301     if (TF_GetCode(status) != TF_OK){
302         TF_DeleteStatus(status);
303         av_log(ctx, AV_LOG_ERROR, "Failed to get input tensor shape: number of dimension incorrect\n");
304         return DNN_GENERIC_ERROR;
305     }
306     TF_DeleteStatus(status);
307 
308     // currently only NHWC is supported
309     av_assert0(dims[0] == 1 || dims[0] == -1);
310     input->height = dims[1];
311     input->width = dims[2];
312     input->channels = dims[3];
313 
314     return 0;
315 }
316 
get_output_tf(void * model,const char * input_name,int input_width,int input_height,const char * output_name,int * output_width,int * output_height)317 static int get_output_tf(void *model, const char *input_name, int input_width, int input_height,
318                                    const char *output_name, int *output_width, int *output_height)
319 {
320     int ret;
321     TFModel *tf_model = model;
322     TFContext *ctx = &tf_model->ctx;
323     TaskItem task;
324     TFRequestItem *request;
325     DNNExecBaseParams exec_params = {
326         .input_name     = input_name,
327         .output_names   = &output_name,
328         .nb_output      = 1,
329         .in_frame       = NULL,
330         .out_frame      = NULL,
331     };
332 
333     ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, tf_model, input_height, input_width, ctx);
334     if (ret != 0) {
335         goto err;
336     }
337 
338     ret = extract_lltask_from_task(&task, tf_model->lltask_queue);
339     if (ret != 0) {
340         av_log(ctx, AV_LOG_ERROR, "unable to extract inference from task.\n");
341         goto err;
342     }
343 
344     request = ff_safe_queue_pop_front(tf_model->request_queue);
345     if (!request) {
346         av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
347         ret = AVERROR(EINVAL);
348         goto err;
349     }
350 
351     ret = execute_model_tf(request, tf_model->lltask_queue);
352     *output_width = task.out_frame->width;
353     *output_height = task.out_frame->height;
354 
355 err:
356     av_frame_free(&task.out_frame);
357     av_frame_free(&task.in_frame);
358     return ret;
359 }
360 
361 #define SPACE_CHARS " \t\r\n"
hex_to_data(uint8_t * data,const char * p)362 static int hex_to_data(uint8_t *data, const char *p)
363 {
364     int c, len, v;
365 
366     len = 0;
367     v   = 1;
368     for (;;) {
369         p += strspn(p, SPACE_CHARS);
370         if (*p == '\0')
371             break;
372         c = av_toupper((unsigned char) *p++);
373         if (c >= '0' && c <= '9')
374             c = c - '0';
375         else if (c >= 'A' && c <= 'F')
376             c = c - 'A' + 10;
377         else
378             break;
379         v = (v << 4) | c;
380         if (v & 0x100) {
381             if (data) {
382                 data[len] = v;
383             }
384             len++;
385             v = 1;
386         }
387     }
388     return len;
389 }
390 
load_tf_model(TFModel * tf_model,const char * model_filename)391 static int load_tf_model(TFModel *tf_model, const char *model_filename)
392 {
393     TFContext *ctx = &tf_model->ctx;
394     TF_Buffer *graph_def;
395     TF_ImportGraphDefOptions *graph_opts;
396     TF_SessionOptions *sess_opts;
397     const TF_Operation *init_op;
398     uint8_t *sess_config = NULL;
399     int sess_config_length = 0;
400 
401     // prepare the sess config data
402     if (tf_model->ctx.options.sess_config != NULL) {
403         const char *config;
404         /*
405         tf_model->ctx.options.sess_config is hex to present the serialized proto
406         required by TF_SetConfig below, so we need to first generate the serialized
407         proto in a python script, tools/python/tf_sess_config.py is a script example
408         to generate the configs of sess_config.
409         */
410         if (strncmp(tf_model->ctx.options.sess_config, "0x", 2) != 0) {
411             av_log(ctx, AV_LOG_ERROR, "sess_config should start with '0x'\n");
412             return AVERROR(EINVAL);
413         }
414         config = tf_model->ctx.options.sess_config + 2;
415         sess_config_length = hex_to_data(NULL, config);
416 
417         sess_config = av_mallocz(sess_config_length + AV_INPUT_BUFFER_PADDING_SIZE);
418         if (!sess_config) {
419             av_log(ctx, AV_LOG_ERROR, "failed to allocate memory\n");
420             return AVERROR(ENOMEM);
421         }
422         if (hex_to_data(sess_config, config) < 0) {
423             av_log(ctx, AV_LOG_ERROR, "failed to convert hex to data\n");
424             return AVERROR(EINVAL);
425         }
426     }
427 
428     graph_def = read_graph(model_filename);
429     if (!graph_def){
430         av_log(ctx, AV_LOG_ERROR, "Failed to read model \"%s\" graph\n", model_filename);
431         av_freep(&sess_config);
432         return AVERROR(EINVAL);
433     }
434     tf_model->graph = TF_NewGraph();
435     tf_model->status = TF_NewStatus();
436     graph_opts = TF_NewImportGraphDefOptions();
437     TF_GraphImportGraphDef(tf_model->graph, graph_def, graph_opts, tf_model->status);
438     TF_DeleteImportGraphDefOptions(graph_opts);
439     TF_DeleteBuffer(graph_def);
440     if (TF_GetCode(tf_model->status) != TF_OK){
441         TF_DeleteGraph(tf_model->graph);
442         TF_DeleteStatus(tf_model->status);
443         av_log(ctx, AV_LOG_ERROR, "Failed to import serialized graph to model graph\n");
444         av_freep(&sess_config);
445         return DNN_GENERIC_ERROR;
446     }
447 
448     init_op = TF_GraphOperationByName(tf_model->graph, "init");
449     sess_opts = TF_NewSessionOptions();
450 
451     if (sess_config) {
452         TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->status);
453         av_freep(&sess_config);
454         if (TF_GetCode(tf_model->status) != TF_OK) {
455             TF_DeleteGraph(tf_model->graph);
456             TF_DeleteStatus(tf_model->status);
457             TF_DeleteSessionOptions(sess_opts);
458             av_log(ctx, AV_LOG_ERROR, "Failed to set config for sess options with %s\n",
459                                       tf_model->ctx.options.sess_config);
460             return DNN_GENERIC_ERROR;
461         }
462     }
463 
464     tf_model->session = TF_NewSession(tf_model->graph, sess_opts, tf_model->status);
465     TF_DeleteSessionOptions(sess_opts);
466     if (TF_GetCode(tf_model->status) != TF_OK)
467     {
468         TF_DeleteGraph(tf_model->graph);
469         TF_DeleteStatus(tf_model->status);
470         av_log(ctx, AV_LOG_ERROR, "Failed to create new session with model graph\n");
471         return DNN_GENERIC_ERROR;
472     }
473 
474     // Run initialization operation with name "init" if it is present in graph
475     if (init_op){
476         TF_SessionRun(tf_model->session, NULL,
477                       NULL, NULL, 0,
478                       NULL, NULL, 0,
479                       &init_op, 1, NULL, tf_model->status);
480         if (TF_GetCode(tf_model->status) != TF_OK)
481         {
482             TF_DeleteSession(tf_model->session, tf_model->status);
483             TF_DeleteGraph(tf_model->graph);
484             TF_DeleteStatus(tf_model->status);
485             av_log(ctx, AV_LOG_ERROR, "Failed to run session when initializing\n");
486             return DNN_GENERIC_ERROR;
487         }
488     }
489 
490     return 0;
491 }
492 
493 #define NAME_BUFFER_SIZE 256
494 
add_conv_layer(TFModel * tf_model,TF_Operation * transpose_op,TF_Operation ** cur_op,ConvolutionalParams * params,const int layer)495 static int add_conv_layer(TFModel *tf_model, TF_Operation *transpose_op, TF_Operation **cur_op,
496                                     ConvolutionalParams* params, const int layer)
497 {
498     TFContext *ctx = &tf_model->ctx;
499     TF_Operation *op;
500     TF_OperationDescription *op_desc;
501     TF_Output input;
502     int64_t strides[] = {1, 1, 1, 1};
503     TF_Tensor *kernel_tensor = NULL, *biases_tensor = NULL;
504     int64_t dims[4];
505     int dims_len;
506     char name_buffer[NAME_BUFFER_SIZE];
507     int32_t size;
508 
509     size = params->input_num * params->output_num * params->kernel_size * params->kernel_size;
510     input.index = 0;
511 
512     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_kernel%d", layer);
513     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
514     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
515     dims[0] = params->output_num;
516     dims[1] = params->kernel_size;
517     dims[2] = params->kernel_size;
518     dims[3] = params->input_num;
519     dims_len = 4;
520     kernel_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, size * sizeof(float));
521     memcpy(TF_TensorData(kernel_tensor), params->kernel, size * sizeof(float));
522     TF_SetAttrTensor(op_desc, "value", kernel_tensor, tf_model->status);
523     if (TF_GetCode(tf_model->status) != TF_OK){
524         goto err;
525     }
526     op = TF_FinishOperation(op_desc, tf_model->status);
527     if (TF_GetCode(tf_model->status) != TF_OK){
528         goto err;
529     }
530 
531     snprintf(name_buffer, NAME_BUFFER_SIZE, "transpose%d", layer);
532     op_desc = TF_NewOperation(tf_model->graph, "Transpose", name_buffer);
533     input.oper = op;
534     TF_AddInput(op_desc, input);
535     input.oper = transpose_op;
536     TF_AddInput(op_desc, input);
537     TF_SetAttrType(op_desc, "T", TF_FLOAT);
538     TF_SetAttrType(op_desc, "Tperm", TF_INT32);
539     op = TF_FinishOperation(op_desc, tf_model->status);
540     if (TF_GetCode(tf_model->status) != TF_OK){
541         goto err;
542     }
543 
544     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv2d%d", layer);
545     op_desc = TF_NewOperation(tf_model->graph, "Conv2D", name_buffer);
546     input.oper = *cur_op;
547     TF_AddInput(op_desc, input);
548     input.oper = op;
549     TF_AddInput(op_desc, input);
550     TF_SetAttrType(op_desc, "T", TF_FLOAT);
551     TF_SetAttrIntList(op_desc, "strides", strides, 4);
552     TF_SetAttrString(op_desc, "padding", "VALID", 5);
553     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
554     if (TF_GetCode(tf_model->status) != TF_OK){
555         goto err;
556     }
557 
558     snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_biases%d", layer);
559     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
560     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
561     dims[0] = params->output_num;
562     dims_len = 1;
563     biases_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, params->output_num * sizeof(float));
564     memcpy(TF_TensorData(biases_tensor), params->biases, params->output_num * sizeof(float));
565     TF_SetAttrTensor(op_desc, "value", biases_tensor, tf_model->status);
566     if (TF_GetCode(tf_model->status) != TF_OK){
567         goto err;
568     }
569     op = TF_FinishOperation(op_desc, tf_model->status);
570     if (TF_GetCode(tf_model->status) != TF_OK){
571         goto err;
572     }
573 
574     snprintf(name_buffer, NAME_BUFFER_SIZE, "bias_add%d", layer);
575     op_desc = TF_NewOperation(tf_model->graph, "BiasAdd", name_buffer);
576     input.oper = *cur_op;
577     TF_AddInput(op_desc, input);
578     input.oper = op;
579     TF_AddInput(op_desc, input);
580     TF_SetAttrType(op_desc, "T", TF_FLOAT);
581     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
582     if (TF_GetCode(tf_model->status) != TF_OK){
583         goto err;
584     }
585 
586     snprintf(name_buffer, NAME_BUFFER_SIZE, "activation%d", layer);
587     switch (params->activation){
588     case RELU:
589         op_desc = TF_NewOperation(tf_model->graph, "Relu", name_buffer);
590         break;
591     case TANH:
592         op_desc = TF_NewOperation(tf_model->graph, "Tanh", name_buffer);
593         break;
594     case SIGMOID:
595         op_desc = TF_NewOperation(tf_model->graph, "Sigmoid", name_buffer);
596         break;
597     default:
598         avpriv_report_missing_feature(ctx, "convolutional activation function %d", params->activation);
599         return AVERROR(ENOSYS);
600     }
601     input.oper = *cur_op;
602     TF_AddInput(op_desc, input);
603     TF_SetAttrType(op_desc, "T", TF_FLOAT);
604     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
605     if (TF_GetCode(tf_model->status) != TF_OK){
606         goto err;
607     }
608 
609     return 0;
610 err:
611     TF_DeleteTensor(kernel_tensor);
612     TF_DeleteTensor(biases_tensor);
613     av_log(ctx, AV_LOG_ERROR, "Failed to add conv layer %d\n", layer);
614     return DNN_GENERIC_ERROR;
615 }
616 
add_depth_to_space_layer(TFModel * tf_model,TF_Operation ** cur_op,DepthToSpaceParams * params,const int layer)617 static int add_depth_to_space_layer(TFModel *tf_model, TF_Operation **cur_op,
618                                               DepthToSpaceParams *params, const int layer)
619 {
620     TFContext *ctx = &tf_model->ctx;
621     TF_OperationDescription *op_desc;
622     TF_Output input;
623     char name_buffer[NAME_BUFFER_SIZE];
624 
625     snprintf(name_buffer, NAME_BUFFER_SIZE, "depth_to_space%d", layer);
626     op_desc = TF_NewOperation(tf_model->graph, "DepthToSpace", name_buffer);
627     input.oper = *cur_op;
628     input.index = 0;
629     TF_AddInput(op_desc, input);
630     TF_SetAttrType(op_desc, "T", TF_FLOAT);
631     TF_SetAttrInt(op_desc, "block_size", params->block_size);
632     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
633     if (TF_GetCode(tf_model->status) != TF_OK){
634         av_log(ctx, AV_LOG_ERROR, "Failed to add depth_to_space to layer %d\n", layer);
635         return DNN_GENERIC_ERROR;
636     }
637 
638     return 0;
639 }
640 
add_pad_layer(TFModel * tf_model,TF_Operation ** cur_op,LayerPadParams * params,const int layer)641 static int add_pad_layer(TFModel *tf_model, TF_Operation **cur_op,
642                                               LayerPadParams *params, const int layer)
643 {
644     TFContext *ctx = &tf_model->ctx;
645     TF_Operation *op;
646     TF_Tensor *tensor;
647     TF_OperationDescription *op_desc;
648     TF_Output input;
649     int32_t *pads;
650     int64_t pads_shape[] = {4, 2};
651 
652     char name_buffer[NAME_BUFFER_SIZE];
653     snprintf(name_buffer, NAME_BUFFER_SIZE, "pad%d", layer);
654 
655     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
656     TF_SetAttrType(op_desc, "dtype", TF_INT32);
657     tensor = TF_AllocateTensor(TF_INT32, pads_shape, 2, 4 * 2 * sizeof(int32_t));
658     pads = (int32_t *)TF_TensorData(tensor);
659     pads[0] = params->paddings[0][0];
660     pads[1] = params->paddings[0][1];
661     pads[2] = params->paddings[1][0];
662     pads[3] = params->paddings[1][1];
663     pads[4] = params->paddings[2][0];
664     pads[5] = params->paddings[2][1];
665     pads[6] = params->paddings[3][0];
666     pads[7] = params->paddings[3][1];
667     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
668     if (TF_GetCode(tf_model->status) != TF_OK){
669         TF_DeleteTensor(tensor);
670         av_log(ctx, AV_LOG_ERROR, "Failed to set value for pad of layer %d\n", layer);
671         return DNN_GENERIC_ERROR;
672     }
673     op = TF_FinishOperation(op_desc, tf_model->status);
674     if (TF_GetCode(tf_model->status) != TF_OK){
675         TF_DeleteTensor(tensor);
676         av_log(ctx, AV_LOG_ERROR, "Failed to add pad to layer %d\n", layer);
677         return DNN_GENERIC_ERROR;
678     }
679 
680     op_desc = TF_NewOperation(tf_model->graph, "MirrorPad", "mirror_pad");
681     input.oper = *cur_op;
682     input.index = 0;
683     TF_AddInput(op_desc, input);
684     input.oper = op;
685     TF_AddInput(op_desc, input);
686     TF_SetAttrType(op_desc, "T", TF_FLOAT);
687     TF_SetAttrType(op_desc, "Tpaddings", TF_INT32);
688     TF_SetAttrString(op_desc, "mode", "SYMMETRIC", 9);
689     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
690     if (TF_GetCode(tf_model->status) != TF_OK){
691         TF_DeleteTensor(tensor);
692         av_log(ctx, AV_LOG_ERROR, "Failed to add mirror_pad to layer %d\n", layer);
693         return DNN_GENERIC_ERROR;
694     }
695 
696     return 0;
697 }
698 
add_maximum_layer(TFModel * tf_model,TF_Operation ** cur_op,DnnLayerMaximumParams * params,const int layer)699 static int add_maximum_layer(TFModel *tf_model, TF_Operation **cur_op,
700                                        DnnLayerMaximumParams *params, const int layer)
701 {
702     TFContext *ctx = &tf_model->ctx;
703     TF_Operation *op;
704     TF_Tensor *tensor;
705     TF_OperationDescription *op_desc;
706     TF_Output input;
707     float *y;
708 
709     char name_buffer[NAME_BUFFER_SIZE];
710     snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum/y%d", layer);
711 
712     op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer);
713     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
714     tensor = TF_AllocateTensor(TF_FLOAT, NULL, 0, TF_DataTypeSize(TF_FLOAT));
715     y = (float *)TF_TensorData(tensor);
716     *y = params->val.y;
717     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
718     if (TF_GetCode(tf_model->status) != TF_OK){
719         TF_DeleteTensor(tensor);
720         av_log(ctx, AV_LOG_ERROR, "Failed to set value for maximum/y of layer %d", layer);
721         return DNN_GENERIC_ERROR;
722     }
723     op = TF_FinishOperation(op_desc, tf_model->status);
724     if (TF_GetCode(tf_model->status) != TF_OK){
725         TF_DeleteTensor(tensor);
726         av_log(ctx, AV_LOG_ERROR, "Failed to add maximum/y to layer %d\n", layer);
727         return DNN_GENERIC_ERROR;
728     }
729 
730     snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum%d", layer);
731     op_desc = TF_NewOperation(tf_model->graph, "Maximum", name_buffer);
732     input.oper = *cur_op;
733     input.index = 0;
734     TF_AddInput(op_desc, input);
735     input.oper = op;
736     TF_AddInput(op_desc, input);
737     TF_SetAttrType(op_desc, "T", TF_FLOAT);
738     *cur_op = TF_FinishOperation(op_desc, tf_model->status);
739     if (TF_GetCode(tf_model->status) != TF_OK){
740         TF_DeleteTensor(tensor);
741         av_log(ctx, AV_LOG_ERROR, "Failed to add maximum to layer %d\n", layer);
742         return DNN_GENERIC_ERROR;
743     }
744 
745     return 0;
746 }
747 
load_native_model(TFModel * tf_model,const char * model_filename)748 static int load_native_model(TFModel *tf_model, const char *model_filename)
749 {
750     TFContext *ctx = &tf_model->ctx;
751     int32_t layer;
752     TF_OperationDescription *op_desc;
753     TF_Operation *op;
754     TF_Operation *transpose_op;
755     TF_Tensor *tensor = NULL;
756     TF_Output input;
757     int32_t *transpose_perm;
758     int64_t transpose_perm_shape[] = {4};
759     int64_t input_shape[] = {1, -1, -1, -1};
760     int layer_add_res;
761     DNNModel *model = NULL;
762     NativeModel *native_model;
763 
764     model = ff_dnn_load_model_native(model_filename, DFT_PROCESS_FRAME, NULL, NULL);
765     if (!model){
766         av_log(ctx, AV_LOG_ERROR, "Failed to load native model\n");
767         return AVERROR(EINVAL);
768     }
769 
770     native_model = model->model;
771     tf_model->graph = TF_NewGraph();
772     tf_model->status = TF_NewStatus();
773 
774 #define CLEANUP_ON_ERROR(tf_model) \
775     { \
776         TF_DeleteTensor(tensor); \
777         TF_DeleteGraph(tf_model->graph); \
778         TF_DeleteStatus(tf_model->status); \
779         av_log(ctx, AV_LOG_ERROR, "Failed to set value or add operator to layer\n"); \
780         return DNN_GENERIC_ERROR; \
781     }
782 
783     op_desc = TF_NewOperation(tf_model->graph, "Placeholder", "x");
784     TF_SetAttrType(op_desc, "dtype", TF_FLOAT);
785     TF_SetAttrShape(op_desc, "shape", input_shape, 4);
786     op = TF_FinishOperation(op_desc, tf_model->status);
787     if (TF_GetCode(tf_model->status) != TF_OK){
788         CLEANUP_ON_ERROR(tf_model);
789     }
790 
791     op_desc = TF_NewOperation(tf_model->graph, "Const", "transpose_perm");
792     TF_SetAttrType(op_desc, "dtype", TF_INT32);
793     tensor = TF_AllocateTensor(TF_INT32, transpose_perm_shape, 1, 4 * sizeof(int32_t));
794     transpose_perm = (int32_t *)TF_TensorData(tensor);
795     transpose_perm[0] = 1;
796     transpose_perm[1] = 2;
797     transpose_perm[2] = 3;
798     transpose_perm[3] = 0;
799     TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status);
800     if (TF_GetCode(tf_model->status) != TF_OK){
801         CLEANUP_ON_ERROR(tf_model);
802     }
803     transpose_op = TF_FinishOperation(op_desc, tf_model->status);
804     if (TF_GetCode(tf_model->status) != TF_OK){
805         CLEANUP_ON_ERROR(tf_model);
806     }
807 
808     for (layer = 0; layer < native_model->layers_num; ++layer){
809         switch (native_model->layers[layer].type){
810         case DLT_INPUT:
811             layer_add_res = 0;
812             break;
813         case DLT_CONV2D:
814             layer_add_res = add_conv_layer(tf_model, transpose_op, &op,
815                                            (ConvolutionalParams *)native_model->layers[layer].params, layer);
816             break;
817         case DLT_DEPTH_TO_SPACE:
818             layer_add_res = add_depth_to_space_layer(tf_model, &op,
819                                                      (DepthToSpaceParams *)native_model->layers[layer].params, layer);
820             break;
821         case DLT_MIRROR_PAD:
822             layer_add_res = add_pad_layer(tf_model, &op,
823                                           (LayerPadParams *)native_model->layers[layer].params, layer);
824             break;
825         case DLT_MAXIMUM:
826             layer_add_res = add_maximum_layer(tf_model, &op,
827                                           (DnnLayerMaximumParams *)native_model->layers[layer].params, layer);
828             break;
829         default:
830             CLEANUP_ON_ERROR(tf_model);
831         }
832 
833         if (layer_add_res != 0){
834             CLEANUP_ON_ERROR(tf_model);
835         }
836     }
837 
838     op_desc = TF_NewOperation(tf_model->graph, "Identity", "y");
839     input.oper = op;
840     input.index = 0;
841     TF_AddInput(op_desc, input);
842     TF_FinishOperation(op_desc, tf_model->status);
843     if (TF_GetCode(tf_model->status) != TF_OK){
844         CLEANUP_ON_ERROR(tf_model);
845     }
846 
847     ff_dnn_free_model_native(&model);
848 
849     return 0;
850 }
851 
ff_dnn_load_model_tf(const char * model_filename,DNNFunctionType func_type,const char * options,AVFilterContext * filter_ctx)852 DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx)
853 {
854     DNNModel *model = NULL;
855     TFModel *tf_model = NULL;
856     TFContext *ctx = NULL;
857 
858     model = av_mallocz(sizeof(DNNModel));
859     if (!model){
860         return NULL;
861     }
862 
863     tf_model = av_mallocz(sizeof(TFModel));
864     if (!tf_model){
865         av_freep(&model);
866         return NULL;
867     }
868     tf_model->model = model;
869     ctx = &tf_model->ctx;
870     ctx->class = &dnn_tensorflow_class;
871 
872     //parse options
873     av_opt_set_defaults(ctx);
874     if (av_opt_set_from_string(ctx, options, NULL, "=", "&") < 0) {
875         av_log(ctx, AV_LOG_ERROR, "Failed to parse options \"%s\"\n", options);
876         goto err;
877     }
878 
879     if (load_tf_model(tf_model, model_filename) != 0){
880         if (load_native_model(tf_model, model_filename) != 0){
881             goto err;
882         }
883     }
884 
885     if (ctx->options.nireq <= 0) {
886         ctx->options.nireq = av_cpu_count() / 2 + 1;
887     }
888 
889 #if !HAVE_PTHREAD_CANCEL
890     if (ctx->options.async) {
891         ctx->options.async = 0;
892         av_log(filter_ctx, AV_LOG_WARNING, "pthread is not supported, roll back to sync.\n");
893     }
894 #endif
895 
896     tf_model->request_queue = ff_safe_queue_create();
897     if (!tf_model->request_queue) {
898         goto err;
899     }
900 
901     for (int i = 0; i < ctx->options.nireq; i++) {
902         TFRequestItem *item = av_mallocz(sizeof(*item));
903         if (!item) {
904             goto err;
905         }
906         item->lltask = NULL;
907         item->infer_request = tf_create_inference_request();
908         if (!item->infer_request) {
909             av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for TensorFlow inference request\n");
910             av_freep(&item);
911             goto err;
912         }
913         item->status = TF_NewStatus();
914         item->exec_module.start_inference = &tf_start_inference;
915         item->exec_module.callback = &infer_completion_callback;
916         item->exec_module.args = item;
917 
918         if (ff_safe_queue_push_back(tf_model->request_queue, item) < 0) {
919             destroy_request_item(&item);
920             goto err;
921         }
922     }
923 
924     tf_model->lltask_queue = ff_queue_create();
925     if (!tf_model->lltask_queue) {
926         goto err;
927     }
928 
929     tf_model->task_queue = ff_queue_create();
930     if (!tf_model->task_queue) {
931         goto err;
932     }
933 
934     model->model = tf_model;
935     model->get_input = &get_input_tf;
936     model->get_output = &get_output_tf;
937     model->options = options;
938     model->filter_ctx = filter_ctx;
939     model->func_type = func_type;
940 
941     return model;
942 err:
943     ff_dnn_free_model_tf(&model);
944     return NULL;
945 }
946 
fill_model_input_tf(TFModel * tf_model,TFRequestItem * request)947 static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request) {
948     DNNData input;
949     LastLevelTaskItem *lltask;
950     TaskItem *task;
951     TFInferRequest *infer_request;
952     TFContext *ctx = &tf_model->ctx;
953     int ret = 0;
954 
955     lltask = ff_queue_pop_front(tf_model->lltask_queue);
956     av_assert0(lltask);
957     task = lltask->task;
958     request->lltask = lltask;
959 
960     ret = get_input_tf(tf_model, &input, task->input_name);
961     if (ret != 0) {
962         goto err;
963     }
964 
965     infer_request = request->infer_request;
966     input.height = task->in_frame->height;
967     input.width = task->in_frame->width;
968 
969     infer_request->tf_input = av_malloc(sizeof(TF_Output));
970     if (!infer_request->tf_input) {
971         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
972         ret = AVERROR(ENOMEM);
973         goto err;
974     }
975 
976     infer_request->tf_input->oper = TF_GraphOperationByName(tf_model->graph, task->input_name);
977     if (!infer_request->tf_input->oper){
978         av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", task->input_name);
979         ret = DNN_GENERIC_ERROR;
980         goto err;
981     }
982     infer_request->tf_input->index = 0;
983 
984     infer_request->input_tensor = allocate_input_tensor(&input);
985     if (!infer_request->input_tensor){
986         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
987         ret = AVERROR(ENOMEM);
988         goto err;
989     }
990     input.data = (float *)TF_TensorData(infer_request->input_tensor);
991 
992     switch (tf_model->model->func_type) {
993     case DFT_PROCESS_FRAME:
994         if (task->do_ioproc) {
995             if (tf_model->model->frame_pre_proc != NULL) {
996                 tf_model->model->frame_pre_proc(task->in_frame, &input, tf_model->model->filter_ctx);
997             } else {
998                 ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
999             }
1000         }
1001         break;
1002     case DFT_ANALYTICS_DETECT:
1003         ff_frame_to_dnn_detect(task->in_frame, &input, ctx);
1004         break;
1005     default:
1006         avpriv_report_missing_feature(ctx, "model function type %d", tf_model->model->func_type);
1007         break;
1008     }
1009 
1010     infer_request->tf_outputs = av_malloc_array(task->nb_output, sizeof(TF_Output));
1011     if (infer_request->tf_outputs == NULL) {
1012         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *tf_outputs\n");
1013         ret = AVERROR(ENOMEM);
1014         goto err;
1015     }
1016 
1017     infer_request->output_tensors = av_calloc(task->nb_output, sizeof(*infer_request->output_tensors));
1018     if (!infer_request->output_tensors) {
1019         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output tensor\n");
1020         ret = AVERROR(ENOMEM);
1021         goto err;
1022     }
1023 
1024     for (int i = 0; i < task->nb_output; ++i) {
1025         infer_request->output_tensors[i] = NULL;
1026         infer_request->tf_outputs[i].oper = TF_GraphOperationByName(tf_model->graph, task->output_names[i]);
1027         if (!infer_request->tf_outputs[i].oper) {
1028             av_log(ctx, AV_LOG_ERROR, "Could not find output \"%s\" in model\n", task->output_names[i]);
1029             ret = DNN_GENERIC_ERROR;
1030             goto err;
1031         }
1032         infer_request->tf_outputs[i].index = 0;
1033     }
1034 
1035     return 0;
1036 err:
1037     tf_free_request(infer_request);
1038     return ret;
1039 }
1040 
infer_completion_callback(void * args)1041 static void infer_completion_callback(void *args) {
1042     TFRequestItem *request = args;
1043     LastLevelTaskItem *lltask = request->lltask;
1044     TaskItem *task = lltask->task;
1045     DNNData *outputs;
1046     TFInferRequest *infer_request = request->infer_request;
1047     TFModel *tf_model = task->model;
1048     TFContext *ctx = &tf_model->ctx;
1049 
1050     outputs = av_malloc_array(task->nb_output, sizeof(*outputs));
1051     if (!outputs) {
1052         av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *outputs\n");
1053         goto err;
1054     }
1055 
1056     for (uint32_t i = 0; i < task->nb_output; ++i) {
1057         outputs[i].height = TF_Dim(infer_request->output_tensors[i], 1);
1058         outputs[i].width = TF_Dim(infer_request->output_tensors[i], 2);
1059         outputs[i].channels = TF_Dim(infer_request->output_tensors[i], 3);
1060         outputs[i].data = TF_TensorData(infer_request->output_tensors[i]);
1061         outputs[i].dt = TF_TensorType(infer_request->output_tensors[i]);
1062     }
1063     switch (tf_model->model->func_type) {
1064     case DFT_PROCESS_FRAME:
1065         //it only support 1 output if it's frame in & frame out
1066         if (task->do_ioproc) {
1067             if (tf_model->model->frame_post_proc != NULL) {
1068                 tf_model->model->frame_post_proc(task->out_frame, outputs, tf_model->model->filter_ctx);
1069             } else {
1070                 ff_proc_from_dnn_to_frame(task->out_frame, outputs, ctx);
1071             }
1072         } else {
1073             task->out_frame->width = outputs[0].width;
1074             task->out_frame->height = outputs[0].height;
1075         }
1076         break;
1077     case DFT_ANALYTICS_DETECT:
1078         if (!tf_model->model->detect_post_proc) {
1079             av_log(ctx, AV_LOG_ERROR, "Detect filter needs provide post proc\n");
1080             return;
1081         }
1082         tf_model->model->detect_post_proc(task->in_frame, outputs, task->nb_output, tf_model->model->filter_ctx);
1083         break;
1084     default:
1085         av_log(ctx, AV_LOG_ERROR, "Tensorflow backend does not support this kind of dnn filter now\n");
1086         goto err;
1087     }
1088     task->inference_done++;
1089 err:
1090     tf_free_request(infer_request);
1091     av_freep(&outputs);
1092 
1093     if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
1094         destroy_request_item(&request);
1095         av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
1096     }
1097 }
1098 
execute_model_tf(TFRequestItem * request,Queue * lltask_queue)1099 static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue)
1100 {
1101     TFModel *tf_model;
1102     TFContext *ctx;
1103     LastLevelTaskItem *lltask;
1104     TaskItem *task;
1105     int ret = 0;
1106 
1107     if (ff_queue_size(lltask_queue) == 0) {
1108         destroy_request_item(&request);
1109         return 0;
1110     }
1111 
1112     lltask = ff_queue_peek_front(lltask_queue);
1113     task = lltask->task;
1114     tf_model = task->model;
1115     ctx = &tf_model->ctx;
1116 
1117     ret = fill_model_input_tf(tf_model, request);
1118     if (ret != 0) {
1119         goto err;
1120     }
1121 
1122     if (task->async) {
1123         if (ff_dnn_start_inference_async(ctx, &request->exec_module) != 0) {
1124             goto err;
1125         }
1126         return 0;
1127     }
1128     else {
1129         ret = tf_start_inference(request);
1130         if (ret != 0) {
1131             goto err;
1132         }
1133         infer_completion_callback(request);
1134         return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
1135     }
1136 err:
1137     tf_free_request(request->infer_request);
1138     if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
1139         destroy_request_item(&request);
1140     }
1141     return ret;
1142 }
1143 
ff_dnn_execute_model_tf(const DNNModel * model,DNNExecBaseParams * exec_params)1144 int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params)
1145 {
1146     TFModel *tf_model = model->model;
1147     TFContext *ctx = &tf_model->ctx;
1148     TaskItem *task;
1149     TFRequestItem *request;
1150     int ret = 0;
1151 
1152     ret = ff_check_exec_params(ctx, DNN_TF, model->func_type, exec_params);
1153     if (ret != 0) {
1154         return ret;
1155     }
1156 
1157     task = av_malloc(sizeof(*task));
1158     if (!task) {
1159         av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n");
1160         return AVERROR(ENOMEM);
1161     }
1162 
1163     ret = ff_dnn_fill_task(task, exec_params, tf_model, ctx->options.async, 1);
1164     if (ret != 0) {
1165         av_freep(&task);
1166         return ret;
1167     }
1168 
1169     if (ff_queue_push_back(tf_model->task_queue, task) < 0) {
1170         av_freep(&task);
1171         av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n");
1172         return AVERROR(ENOMEM);
1173     }
1174 
1175     ret = extract_lltask_from_task(task, tf_model->lltask_queue);
1176     if (ret != 0) {
1177         av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n");
1178         return ret;
1179     }
1180 
1181     request = ff_safe_queue_pop_front(tf_model->request_queue);
1182     if (!request) {
1183         av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
1184         return AVERROR(EINVAL);
1185     }
1186     return execute_model_tf(request, tf_model->lltask_queue);
1187 }
1188 
ff_dnn_get_result_tf(const DNNModel * model,AVFrame ** in,AVFrame ** out)1189 DNNAsyncStatusType ff_dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out)
1190 {
1191     TFModel *tf_model = model->model;
1192     return ff_dnn_get_result_common(tf_model->task_queue, in, out);
1193 }
1194 
ff_dnn_flush_tf(const DNNModel * model)1195 int ff_dnn_flush_tf(const DNNModel *model)
1196 {
1197     TFModel *tf_model = model->model;
1198     TFContext *ctx = &tf_model->ctx;
1199     TFRequestItem *request;
1200     int ret;
1201 
1202     if (ff_queue_size(tf_model->lltask_queue) == 0) {
1203         // no pending task need to flush
1204         return 0;
1205     }
1206 
1207     request = ff_safe_queue_pop_front(tf_model->request_queue);
1208     if (!request) {
1209         av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
1210         return AVERROR(EINVAL);
1211     }
1212 
1213     ret = fill_model_input_tf(tf_model, request);
1214     if (ret != 0) {
1215         av_log(ctx, AV_LOG_ERROR, "Failed to fill model input.\n");
1216         if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
1217             destroy_request_item(&request);
1218         }
1219         return ret;
1220     }
1221 
1222     return ff_dnn_start_inference_async(ctx, &request->exec_module);
1223 }
1224 
ff_dnn_free_model_tf(DNNModel ** model)1225 void ff_dnn_free_model_tf(DNNModel **model)
1226 {
1227     TFModel *tf_model;
1228 
1229     if (*model){
1230         tf_model = (*model)->model;
1231         while (ff_safe_queue_size(tf_model->request_queue) != 0) {
1232             TFRequestItem *item = ff_safe_queue_pop_front(tf_model->request_queue);
1233             destroy_request_item(&item);
1234         }
1235         ff_safe_queue_destroy(tf_model->request_queue);
1236 
1237         while (ff_queue_size(tf_model->lltask_queue) != 0) {
1238             LastLevelTaskItem *item = ff_queue_pop_front(tf_model->lltask_queue);
1239             av_freep(&item);
1240         }
1241         ff_queue_destroy(tf_model->lltask_queue);
1242 
1243         while (ff_queue_size(tf_model->task_queue) != 0) {
1244             TaskItem *item = ff_queue_pop_front(tf_model->task_queue);
1245             av_frame_free(&item->in_frame);
1246             av_frame_free(&item->out_frame);
1247             av_freep(&item);
1248         }
1249         ff_queue_destroy(tf_model->task_queue);
1250 
1251         if (tf_model->graph){
1252             TF_DeleteGraph(tf_model->graph);
1253         }
1254         if (tf_model->session){
1255             TF_CloseSession(tf_model->session, tf_model->status);
1256             TF_DeleteSession(tf_model->session, tf_model->status);
1257         }
1258         if (tf_model->status){
1259             TF_DeleteStatus(tf_model->status);
1260         }
1261         av_freep(&tf_model);
1262         av_freep(model);
1263     }
1264 }
1265