FFmpeg
Loading...
Searching...
No Matches
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
48
49/**
50 * Stores execution parameters for single
51 * call to the TensorFlow C API
52 */
53typedef struct TFInferRequest {
54 TF_Output *tf_outputs;
55 TF_Tensor **output_tensors;
56 TF_Output *tf_input;
57 TF_Tensor *input_tensor;
59
66
67#define OFFSET(x) offsetof(TFOptions, x)
68#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
70 { "sess_config", "config for SessionOptions", OFFSET(sess_config), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
71 { NULL }
72};
73
74
75static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue);
76static void infer_completion_callback(void *args);
77static inline void destroy_request_item(TFRequestItem **arg);
78
79static 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 */
91static 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 */
140static 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 */
171 TFRequestItem *request;
172 if (!arg) {
173 return;
174 }
175 request = *arg;
177 av_freep(&request->infer_request);
178 av_freep(&request->lltask);
179 TF_DeleteStatus(request->status);
181 av_freep(arg);
182}
183
184static 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
204static 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
237static 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
264static 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
312static 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
350err:
352 av_frame_free(&task.in_frame);
353 return ret;
354}
355
356#define SPACE_CHARS " \t\r\n"
357static 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
386static 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
481static 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) {
493 }
495
496 while (ff_queue_size(tf_model->lltask_queue) != 0) {
498 av_freep(&item);
499 }
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
545 if (!tf_model->request_queue) {
546 goto err;
547 }
548
549 for (int i = 0; i < ctx->nireq; i++) {
550 TFRequestItem *item = av_mallocz(sizeof(*item));
551 if (!item) {
552 goto err;
553 }
554 item->lltask = NULL;
556 if (!item->infer_request) {
557 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for TensorFlow inference request\n");
558 av_freep(&item);
559 goto err;
560 }
561 item->status = TF_NewStatus();
564 item->exec_module.args = item;
565
566 if (ff_safe_queue_push_back(tf_model->request_queue, item) < 0) {
568 goto err;
569 }
570 }
571
572 tf_model->lltask_queue = ff_queue_create();
573 if (!tf_model->lltask_queue) {
574 goto err;
575 }
576
577 tf_model->task_queue = ff_queue_create();
578 if (!tf_model->task_queue) {
579 goto err;
580 }
581
582 model->get_input = &get_input_tf;
583 model->get_output = &get_output_tf;
584 model->filter_ctx = filter_ctx;
585 model->func_type = func_type;
586
587 return model;
588err:
589 dnn_free_model_tf(&model);
590 return NULL;
591}
592
593static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request) {
594 DNNData input = { 0 };
595 LastLevelTaskItem *lltask;
596 TaskItem *task;
597 TFInferRequest *infer_request = NULL;
598 DnnContext *ctx = tf_model->ctx;
599 int ret = 0;
600
601 lltask = ff_queue_pop_front(tf_model->lltask_queue);
602 av_assert0(lltask);
603 task = lltask->task;
604 request->lltask = lltask;
605
606 ret = get_input_tf(&tf_model->model, &input, task->input_name);
607 if (ret != 0) {
608 goto err;
609 }
610
611 infer_request = request->infer_request;
612 input.dims[1] = task->in_frame->height;
613 input.dims[2] = task->in_frame->width;
614
615 infer_request->tf_input = av_malloc(sizeof(TF_Output));
616 if (!infer_request->tf_input) {
617 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
618 ret = AVERROR(ENOMEM);
619 goto err;
620 }
621
622 infer_request->tf_input->oper = TF_GraphOperationByName(tf_model->graph, task->input_name);
623 if (!infer_request->tf_input->oper){
624 av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", task->input_name);
625 ret = DNN_GENERIC_ERROR;
626 goto err;
627 }
628 infer_request->tf_input->index = 0;
629
630 infer_request->input_tensor = allocate_input_tensor(&input);
631 if (!infer_request->input_tensor){
632 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for input tensor\n");
633 ret = AVERROR(ENOMEM);
634 goto err;
635 }
636 input.data = (float *)TF_TensorData(infer_request->input_tensor);
637
638 switch (tf_model->model.func_type) {
640 if (task->do_ioproc) {
641 if (tf_model->model.frame_pre_proc != NULL) {
642 tf_model->model.frame_pre_proc(task->in_frame, &input, tf_model->model.filter_ctx);
643 } else {
644 ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
645 }
646 }
647 break;
649 ff_frame_to_dnn_detect(task->in_frame, &input, ctx);
650 break;
651 default:
652 avpriv_report_missing_feature(ctx, "model function type %d", tf_model->model.func_type);
653 break;
654 }
655
656 infer_request->tf_outputs = av_malloc_array(task->nb_output, sizeof(TF_Output));
657 if (infer_request->tf_outputs == NULL) {
658 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *tf_outputs\n");
659 ret = AVERROR(ENOMEM);
660 goto err;
661 }
662
663 infer_request->output_tensors = av_calloc(task->nb_output, sizeof(*infer_request->output_tensors));
664 if (!infer_request->output_tensors) {
665 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for output tensor\n");
666 ret = AVERROR(ENOMEM);
667 goto err;
668 }
669
670 for (int i = 0; i < task->nb_output; ++i) {
671 infer_request->output_tensors[i] = NULL;
672 infer_request->tf_outputs[i].oper = TF_GraphOperationByName(tf_model->graph, task->output_names[i]);
673 if (!infer_request->tf_outputs[i].oper) {
674 av_log(ctx, AV_LOG_ERROR, "Could not find output \"%s\" in model\n", task->output_names[i]);
675 ret = DNN_GENERIC_ERROR;
676 goto err;
677 }
678 infer_request->tf_outputs[i].index = 0;
679 }
680
681 return 0;
682err:
683 tf_free_request(infer_request);
684 return ret;
685}
686
687static void infer_completion_callback(void *args) {
688 TFRequestItem *request = args;
689 LastLevelTaskItem *lltask = request->lltask;
690 TaskItem *task = lltask->task;
692 TFInferRequest *infer_request = request->infer_request;
693 TFModel *tf_model = task->model;
694 DnnContext *ctx = tf_model->ctx;
695
696 outputs = av_calloc(task->nb_output, sizeof(*outputs));
697 if (!outputs) {
698 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *outputs\n");
699 goto err;
700 }
701
702 for (uint32_t i = 0; i < task->nb_output; ++i) {
703 outputs[i].layout = DL_NHWC;
705 TF_Dim(infer_request->output_tensors[i], 1);
707 TF_Dim(infer_request->output_tensors[i], 2);
709 TF_Dim(infer_request->output_tensors[i], 3);
710 outputs[i].data = TF_TensorData(infer_request->output_tensors[i]);
711 outputs[i].dt = (DNNDataType)TF_TensorType(infer_request->output_tensors[i]);
712 }
713 switch (tf_model->model.func_type) {
715 //it only support 1 output if it's frame in & frame out
716 if (task->do_ioproc) {
717 if (tf_model->model.frame_post_proc != NULL) {
718 tf_model->model.frame_post_proc(task->out_frame, outputs, tf_model->model.filter_ctx);
719 } else {
721 }
722 } else {
723 task->out_frame->width =
725 task->out_frame->height =
727 }
728 break;
730 if (!tf_model->model.detect_post_proc) {
731 av_log(ctx, AV_LOG_ERROR, "Detect filter needs provide post proc\n");
732 return;
733 }
734 tf_model->model.detect_post_proc(task->in_frame, outputs, task->nb_output, tf_model->model.filter_ctx);
735 break;
736 default:
737 av_log(ctx, AV_LOG_ERROR, "Tensorflow backend does not support this kind of dnn filter now\n");
738 goto err;
739 }
740 task->inference_done++;
741err:
742 tf_free_request(infer_request);
744
745 if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
746 destroy_request_item(&request);
747 av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n");
748 }
749}
750
751static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue)
752{
753 TFModel *tf_model;
755 LastLevelTaskItem *lltask;
756 TaskItem *task;
757 int ret = 0;
758
759 if (ff_queue_size(lltask_queue) == 0) {
760 destroy_request_item(&request);
761 return 0;
762 }
763
764 lltask = ff_queue_peek_front(lltask_queue);
765 task = lltask->task;
766 tf_model = task->model;
767 ctx = tf_model->ctx;
768
769 ret = fill_model_input_tf(tf_model, request);
770 if (ret != 0) {
771 goto err;
772 }
773
774 if (task->async) {
775 if (ff_dnn_start_inference_async(ctx, &request->exec_module) != 0) {
776 goto err;
777 }
778 return 0;
779 }
780 else {
781 ret = tf_start_inference(request);
782 if (ret != 0) {
783 goto err;
784 }
786 return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
787 }
788err:
790 if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
791 destroy_request_item(&request);
792 }
793
794 return ret;
795}
796
797static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params)
798{
799 TFModel *tf_model = (TFModel *)model;
800 DnnContext *ctx = tf_model->ctx;
801 TaskItem *task;
802 TFRequestItem *request;
803 int ret = 0;
804
805 ret = ff_check_exec_params(ctx, DNN_TF, model->func_type, exec_params);
806 if (ret != 0) {
807 return ret;
808 }
809
810 task = av_malloc(sizeof(*task));
811 if (!task) {
812 av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n");
813 return AVERROR(ENOMEM);
814 }
815
816 ret = ff_dnn_fill_task(task, exec_params, tf_model, ctx->async, 1);
817 if (ret != 0) {
818 av_log(ctx, AV_LOG_ERROR, "Fill task with invalid parameter(s).\n");
819 av_freep(&task);
820 return ret;
821 }
822
823 if (ff_queue_push_back(tf_model->task_queue, task) < 0) {
824 av_freep(&task);
825 av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n");
826 return AVERROR(ENOMEM);
827 }
828
829 ret = extract_lltask_from_task(task, tf_model->lltask_queue);
830 if (ret != 0) {
831 av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n");
832 return ret;
833 }
834
835 request = ff_safe_queue_pop_front(tf_model->request_queue);
836 if (!request) {
837 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
838 return AVERROR(EINVAL);
839 }
840 return execute_model_tf(request, tf_model->lltask_queue);
841}
842
844{
845 TFModel *tf_model = (TFModel *)model;
846 return ff_dnn_get_result_common(tf_model->task_queue, in, out);
847}
848
849static int dnn_flush_tf(const DNNModel *model)
850{
851 TFModel *tf_model = (TFModel *)model;
852 DnnContext *ctx = tf_model->ctx;
853 TFRequestItem *request;
854 int ret;
855
856 if (ff_queue_size(tf_model->lltask_queue) == 0) {
857 // no pending task need to flush
858 return 0;
859 }
860
861 request = ff_safe_queue_pop_front(tf_model->request_queue);
862 if (!request) {
863 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
864 return AVERROR(EINVAL);
865 }
866
867 ret = fill_model_input_tf(tf_model, request);
868 if (ret != 0) {
869 av_log(ctx, AV_LOG_ERROR, "Failed to fill model input.\n");
870 if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) {
871 destroy_request_item(&request);
872 }
873 return ret;
874 }
875
877}
878
880 .clazz = DNN_DEFINE_CLASS(dnn_tensorflow),
881 .type = DNN_TF,
882 .load_model = dnn_load_model_tf,
883 .execute_model = dnn_execute_model_tf,
884 .get_result = dnn_get_result_tf,
885 .flush = dnn_flush_tf,
886 .free_model = dnn_free_model_tf,
887};
static const AVFilterPad outputs[]
Definition af_aap.c:310
static FILE * out
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
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:572
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:724
Buffered I/O operations.
#define AVIO_FLAG_READ
read-only
Definition avio.h:617
int64_t avio_size(AVIOContext *s)
Get the filesize.
Definition aviobuf.c:326
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition aviobuf.c:615
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define FLAGS
Definition cmdutils.c:596
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
Misc types and constants that do not belong anywhere else.
int ff_check_exec_params(void *ctx, DNNBackendType backend, DNNFunctionType func_type, DNNExecBaseParams *exec_params)
void ff_dnn_wait_requests(SafeQueue *request_queue, int nireq)
Wait for all inference requests to complete before teardown.
DNNAsyncStatusType ff_dnn_get_result_common(Queue *task_queue, AVFrame **in, AVFrame **out)
Extract input and output frame from the Task Queue after asynchronous inference.
int ff_dnn_async_module_cleanup(DNNAsyncExecModule *async_module)
Join the Async Execution thread and set module pointers to NULL.
int ff_dnn_fill_task(TaskItem *task, DNNExecBaseParams *exec_params, void *backend_model, int async, int do_ioproc)
Fill the Task for Backend Execution.
int ff_dnn_start_inference_async(void *ctx, DNNAsyncExecModule *async_module)
Start asynchronous inference routine for the TensorFlow model on a detached thread.
int ff_dnn_fill_gettingoutput_task(TaskItem *task, DNNExecBaseParams *exec_params, void *backend_model, int input_height, int input_width, void *ctx)
Allocate input and output frames and fill the Task with execution parameters.
DNN common functions different backends.
#define DNN_DEFINE_CLASS(fname)
static void infer_completion_callback(void *args)
static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue)
static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
static TFInferRequest * tf_create_inference_request(void)
Create a TensorFlow inference request.
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)
static void tf_free_request(TFInferRequest *request)
Free the contents of TensorFlow inference request.
static int get_input_tf(DNNModel *model, DNNData *input, const char *input_name)
static int load_tf_model(TFModel *tf_model, const char *model_filename)
static int hex_to_data(uint8_t *data, const char *p)
static DNNAsyncStatusType dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out)
static DNNModel * dnn_load_model_tf(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx)
static int tf_start_inference(void *args)
Start synchronous inference for the TensorFlow model.
static int dnn_flush_tf(const DNNModel *model)
#define SPACE_CHARS
static TF_Buffer * read_graph(const char *model_filename)
static const AVOption dnn_tensorflow_options[]
static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params)
static TF_Tensor * allocate_input_tensor(const DNNData *input)
const DNNModule ff_dnn_backend_tf
static void destroy_request_item(TFRequestItem **arg)
Free the TFRequestItem completely.
static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request)
#define OFFSET(x)
static void infer_completion_callback(void *args)
static void free_buffer(void *data, size_t length)
static void dnn_free_model_tf(DNNModel **model)
static int dnn_get_height_idx_by_layout(DNNLayout layout)
DNNAsyncStatusType
@ DL_NHWC
@ DNN_TF
DNNFunctionType
@ DFT_PROCESS_FRAME
@ DFT_ANALYTICS_DETECT
static int dnn_get_width_idx_by_layout(DNNLayout layout)
#define DNN_GENERIC_ERROR
DNNDataType
@ DNN_UINT8
@ DNN_FLOAT
@ DCO_RGB
static int dnn_get_channel_idx_by_layout(DNNLayout layout)
int ff_proc_from_frame_to_dnn(AVFrame *frame, DNNData *input, void *log_ctx)
int ff_frame_to_dnn_detect(AVFrame *frame, DNNData *input, void *log_ctx)
int ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx)
Definition dnn_io_proc.c:42
DNN input&output process between AVFrame and DNNData.
@ AV_OPT_TYPE_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
#define AV_INPUT_BUFFER_PADDING_SIZE
Required number of additionally allocated bytes at the end of the input bitstream for decoding.
Definition defs.h:40
#define AVERROR(e)
Definition error.h:45
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static av_const int av_toupper(int c)
Locale-independent conversion of ASCII characters to uppercase.
Definition avstring.h:227
const char * arg
Definition jacosubdec.c:65
int av_cpu_count(void)
Definition cpu.c:229
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
uint64_t layout
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:370
Memory handling functions.
const char data[16]
Definition mxf.c:149
#define av_malloc(s)
Definition ops_static.c:52
AVOptions.
void ff_queue_destroy(Queue *q)
Destroy the Queue instance.
Definition queue.c:72
void * ff_queue_pop_front(Queue *q)
Remove and free first element from the Queue.
Definition queue.c:151
int ff_queue_push_back(Queue *q, void *v)
Add data to the tail of the queue.
Definition queue.c:130
void * ff_queue_peek_front(Queue *q)
Return a pointer to the data at the head of the queue.
Definition queue.c:93
size_t ff_queue_size(Queue *q)
Return the length of the Queue.
Definition queue.c:88
Queue * ff_queue_create(void)
Create a Queue instance.
Definition queue.c:47
int ff_safe_queue_push_back(SafeQueue *sq, void *v)
Add data to the tail of queue in the SafeQueue after locking mutex.
Definition safe_queue.c:87
void * ff_safe_queue_pop_front(SafeQueue *sq)
Remove and free first element from the queue in SafeQueue.
Definition safe_queue.c:97
size_t ff_safe_queue_size(SafeQueue *sq)
Return the length of the SafeQueue.
Definition safe_queue.c:61
SafeQueue * ff_safe_queue_create(void)
Create and initialize a SafeQueue instance.
Definition safe_queue.c:33
void ff_safe_queue_destroy(SafeQueue *sq)
Destroy the SafeQueue instance.
Definition safe_queue.c:50
An instance of a filter.
Definition avfilter.h:273
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
int width
Definition frame.h:544
int height
Definition frame.h:544
Bytestream IO Context.
Definition avio.h:160
AVOption.
Definition opt.h:428
Common Async Execution Mechanism for the DNN Backends.
void * args
Argument for the execution functions.
int(* start_inference)(void *request)
Synchronous inference function for the backend with corresponding request item as the argument.
void(* callback)(void *args)
Completion Callback for the backend.
DNNDataType dt
int dims[4]
DNNColorOrder order
void * data
DNNLayout layout
int(* get_input)(struct DNNModel *model, DNNData *input, const char *input_name)
int(* get_output)(struct DNNModel *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
FramePrePostProc frame_pre_proc
FramePrePostProc frame_post_proc
DetectPostProc detect_post_proc
AVFilterContext * filter_ctx
DNNFunctionType func_type
Linear double-ended data structure.
Definition executor.c:51
Double-ended queue with mutex locks ensuring data consistency while multithreading.
Definition safe_queue.c:27
Stores execution parameters for single call to the TensorFlow C API.
TF_Output * tf_outputs
TF_Tensor ** output_tensors
TF_Tensor * input_tensor
TF_Output * tf_input
SafeQueue * request_queue
DNNModel model
TF_Graph * graph
Queue * task_queue
TF_Status * status
Queue * lltask_queue
TF_Session * session
DnnContext * ctx
LastLevelTaskItem * lltask
TFInferRequest * infer_request
DNNAsyncExecModule exec_module
TF_Status * status
uint32_t inference_done
AVFrame * in_frame
const char ** output_names
uint8_t do_ioproc
uint32_t inference_todo
const char * input_name
AVFrame * out_frame
uint32_t nb_output
#define av_malloc_array(a, b)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static FilteringContext * filter_ctx
Definition transcode.c:52
int size
int len
static double c[64]