FFmpeg
Loading...
Searching...
No Matches
dnn_backend_torch.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024
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 Torch backend implementation.
24 */
25
26#include <torch/torch.h>
27#include <torch/script.h>
28
29extern "C" {
30#include "config.h"
31#include "dnn_io_proc.h"
32#include "dnn_backend_common.h"
33#include "libavutil/opt.h"
34#include "libavutil/mem.h"
35#include "libavutil/cpu.h"
36#if CONFIG_CUDA
37#include "libavutil/hwcontext.h"
40#include "libavutil/pixfmt.h"
41#endif
42#include "queue.h"
43#include "safe_queue.h"
44}
45
54
55typedef struct THInferRequest {
56 torch::Tensor *output;
57 torch::Tensor *input_tensor;
59
66
67
68#define OFFSET(x) offsetof(THOptions, x)
69#define FLAGS AV_OPT_FLAG_FILTERING_PARAM
70static const AVOption dnn_th_options[] = {
71 { "optimize", "turn on graph executor optimization", OFFSET(optimize), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS},
72 { NULL }
73};
74
75static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
76{
77 THModel *th_model = (THModel *)task->model;
78 DnnContext *ctx = th_model->ctx;
79 LastLevelTaskItem *lltask = (LastLevelTaskItem *)av_malloc(sizeof(*lltask));
80 if (!lltask) {
81 av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for LastLevelTaskItem\n");
82 return AVERROR(ENOMEM);
83 }
84 task->inference_todo = 1;
85 task->inference_done = 0;
86 lltask->task = task;
87 if (ff_queue_push_back(lltask_queue, lltask) < 0) {
88 av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n");
89 av_freep(&lltask);
90 return AVERROR(ENOMEM);
91 }
92 return 0;
93}
94
95static void th_free_request(THInferRequest *request)
96{
97 if (!request)
98 return;
99 if (request->output) {
100 delete(request->output);
101 request->output = NULL;
102 }
103 if (request->input_tensor) {
104 delete(request->input_tensor);
105 request->input_tensor = NULL;
106 }
107 return;
108}
109
111{
112 THRequestItem *item;
113 if (!arg || !*arg) {
114 return;
115 }
116 item = *arg;
118 av_freep(&item->infer_request);
119 av_freep(&item->lltasks);
121 av_freep(arg);
122}
123
124static void dnn_free_model_th(DNNModel **model)
125{
126 THModel *th_model;
127 if (!model || !*model)
128 return;
129
130 th_model = (THModel *)(*model);
131
132 if (th_model->request_queue) {
133 ff_dnn_wait_requests(th_model->request_queue, th_model->ctx->nireq);
134 while (ff_safe_queue_size(th_model->request_queue) != 0) {
137 }
139 }
140
141 if (th_model->lltask_queue)
143 if (th_model->task_queue)
144 ff_queue_destroy(th_model->task_queue);
145
146 if (th_model->jit_model)
147 delete th_model->jit_model;
148
149 av_freep(&th_model);
150 *model = NULL;
151}
152
153static int get_input_th(DNNModel *model, DNNData *input, const char *input_name)
154{
155 input->dt = DNN_FLOAT;
156 input->order = DCO_RGB;
157 input->layout = DL_NCHW;
158 input->dims[0] = 1;
159 input->dims[1] = 3;
160 input->dims[2] = -1;
161 input->dims[3] = -1;
162 return 0;
163}
164
165static void deleter(void *arg)
166{
167 av_freep(&arg);
168}
169
170#if CONFIG_CUDA
171static void cuda_tensor_deleter(void *arg)
172{
173 /* No-op: GPU memory is owned by FFmpeg AVBuffer ref-counting.
174 * LibTorch must not free it. */
175 (void)arg;
176}
177
178/**
179 * Map a CUDA frame's GPU pointer directly into a LibTorch tensor,
180 * bypassing any host-device memory copy.
181 *
182 * The resulting tensor is a zero-copy view over the frame's VRAM
183 * buffer; the AVBuffer reference keeps the memory alive.
184 */
185static int fill_model_input_th_cuda(THModel *th_model, THRequestItem *request)
186{
187 THInferRequest *infer_request = request->infer_request;
188 LastLevelTaskItem *lltask = request->lltasks[0];
189 TaskItem *task = lltask->task;
190 AVFrame *frame = task->in_frame;
191
192
193 int height = frame->height;
194 int width = frame->width;
195 /* linesize[0] is in bytes; for packed RGB/BGR it equals width * channels
196 * plus alignment padding. Use it as the stride so PyTorch respects the
197 * actual memory layout. */
198 int stride_bytes = frame->linesize[0];
199 int channels = stride_bytes / width; /* 3 for RGB24, 4 for RGB0/BGR0 */
200
201 /* Wrap the GPU device pointer in a LibTorch tensor (no copy). */
202 torch::Tensor byte_tensor = torch::from_blob(
203 frame->data[0],
204 {1, height, width, channels},
205 {(long)(height * stride_bytes), (long)stride_bytes,
206 (long)channels, 1L},
207 cuda_tensor_deleter,
208 torch::TensorOptions().dtype(torch::kUInt8).device(torch::kCUDA));
209
210 /* Convert NHWC uint8 → NCHW float32 in [0, 1] and keep on GPU. */
211 *infer_request->input_tensor =
212 byte_tensor.to(torch::kFloat32).div(255.0f)
213 .permute({0, 3, 1, 2}) /* NHWC → NCHW */
214 .slice(1, 0, 3) /* drop alpha if present */
215 .contiguous();
216
217 return 0;
218}
219
220static void fill_model_output_th_cuda(THModel *th_model, TaskItem *task, torch::Tensor &out_slice)
221{
222 AVHWFramesContext *hw_frames_ctx =
224
225 /* Determine channel layout from sw_format. */
226 int hw_channels = 3;
227 int rgb_start = 0;
228 bool needs_flip = false;
229 switch (hw_frames_ctx->sw_format) {
230 case AV_PIX_FMT_RGB24:
231 hw_channels = 3; rgb_start = 0; needs_flip = false;
232 break;
233 case AV_PIX_FMT_BGR24:
234 hw_channels = 3; rgb_start = 0; needs_flip = true;
235 break;
236 case AV_PIX_FMT_RGB0:
237 hw_channels = 4; rgb_start = 0; needs_flip = false;
238 break;
239 case AV_PIX_FMT_BGR0:
240 hw_channels = 4; rgb_start = 0; needs_flip = true;
241 break;
242 case AV_PIX_FMT_0RGB:
243 hw_channels = 4; rgb_start = 1; needs_flip = false;
244 break;
245 case AV_PIX_FMT_0BGR:
246 hw_channels = 4; rgb_start = 1; needs_flip = true;
247 break;
248 default:
249 av_log(th_model->ctx, AV_LOG_ERROR,
250 "Unsupported sw_format for CUDA zero-copy output\n");
251 hw_channels = 3;
252 break;
253 }
254
255 /* Convert model output: NCHW float [0,1] → NHWC uint8 [0,255]. */
256 torch::Tensor out_u8 =
257 out_slice.mul(255.0f)
258 .permute({0, 2, 3, 1})
259 .to(torch::kUInt8)
260 .contiguous();
261 if (needs_flip)
262 out_u8 = out_u8.flip({3});
263
264 int out_h = (int)out_u8.size(1);
265 int out_w = (int)out_u8.size(2);
266
267 /* Map the output frame's VRAM into a tensor with correct
268 * stride (linesize includes alignment padding). */
269 torch::Tensor out_frame_tensor = torch::from_blob(
270 task->out_frame->data[0],
271 {1, out_h, out_w, hw_channels},
272 {(long)(out_h * task->out_frame->linesize[0]),
273 (long)task->out_frame->linesize[0],
274 (long)hw_channels, 1L},
275 cuda_tensor_deleter,
276 torch::TensorOptions()
277 .dtype(torch::kUInt8)
278 .device(torch::kCUDA));
279
280 /* Device-to-Device copy into the correct channel slice. */
281 out_frame_tensor.slice(3, rgb_start, rgb_start + 3)
282 .copy_(out_u8);
283
284 /* Flush the CUDA stream before the encoder reads the frame. */
285 torch::cuda::synchronize();
286}
287#endif /* CONFIG_CUDA */
288
289static int fill_model_input_th(THModel *th_model, THRequestItem *request)
290{
291 LastLevelTaskItem *lltask = NULL;
292 TaskItem *task = NULL;
293 THInferRequest *infer_request = NULL;
294 DNNData input = { 0 };
295 DnnContext *ctx = th_model->ctx;
296 int ret, width_idx, height_idx, channel_idx;
297 int batch_size = ctx->batch_size;
298 float *batch_data = NULL;
299 int frame_size = 0;
300
301 infer_request = request->infer_request;
302
303 ret = get_input_th(&th_model->model, &input, NULL);
304 if (ret != 0) {
305 goto err;
306 }
307 width_idx = dnn_get_width_idx_by_layout(input.layout);
308 height_idx = dnn_get_height_idx_by_layout(input.layout);
309 channel_idx = dnn_get_channel_idx_by_layout(input.layout);
310
312 if (!lltask) {
313 ret = AVERROR(EINVAL);
314 goto err;
315 }
316 task = lltask->task;
317 input.dims[height_idx] = task->in_frame->height;
318 input.dims[width_idx] = task->in_frame->width;
319
320 frame_size = input.dims[height_idx] * input.dims[width_idx] * input.dims[channel_idx];
321 batch_data = (float *)av_malloc(batch_size * frame_size * sizeof(float));
322 if (!batch_data) {
323 ret = AVERROR(ENOMEM);
324 goto err;
325 }
326
327 for (int i = 0; i < batch_size; i++) {
329 if (!lltask)
330 break;
331
332 request->lltasks[i] = lltask;
333 request->lltask_count = i + 1;
334 task = lltask->task;
335
336 input.data = batch_data + i * frame_size;
337
338 switch (th_model->model.func_type) {
340 input.scale = 255;
341 if (task->do_ioproc) {
342 if (th_model->model.frame_pre_proc != NULL) {
343 th_model->model.frame_pre_proc(task->in_frame, &input, th_model->model.filter_ctx);
344 } else {
345 ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx);
346 }
347 }
348 break;
349 default:
350 avpriv_report_missing_feature(NULL, "model function type %d", th_model->model.func_type);
351 break;
352 }
353 }
354
355 infer_request->input_tensor = new torch::Tensor();
356 infer_request->output = new torch::Tensor();
357 *infer_request->input_tensor = torch::from_blob(batch_data,
358 {request->lltask_count, input.dims[channel_idx], input.dims[height_idx], input.dims[width_idx]},
359 deleter, torch::kFloat32);
360
361 return 0;
362
363err:
364 if (batch_data)
365 av_freep(&batch_data);
366 th_free_request(infer_request);
367 return ret;
368}
369
370static int th_start_inference(void *args)
371{
372 THRequestItem *request = (THRequestItem *)args;
373 THInferRequest *infer_request = NULL;
374 LastLevelTaskItem *lltask = NULL;
375 TaskItem *task = NULL;
376 THModel *th_model = NULL;
378 std::vector<torch::jit::IValue> inputs;
379 torch::NoGradGuard no_grad;
380
381 if (!request) {
382 av_log(NULL, AV_LOG_ERROR, "THRequestItem is NULL\n");
383 return AVERROR(EINVAL);
384 }
385 infer_request = request->infer_request;
386 lltask = request->lltasks[0];
387 task = lltask->task;
388 th_model = (THModel *)task->model;
389 ctx = th_model->ctx;
390
391 if (ctx->torch_option.optimize)
392 torch::jit::setGraphExecutorOptimize(true);
393 else
394 torch::jit::setGraphExecutorOptimize(false);
395
396 if (!infer_request->input_tensor || !infer_request->output) {
397 av_log(ctx, AV_LOG_ERROR, "input or output tensor is NULL\n");
398 return DNN_GENERIC_ERROR;
399 }
400 // Transfer tensor to the same device as model
401 const char *device_name = ctx->device ? ctx->device : "cpu";
402 c10::Device device(device_name);
403 if (infer_request->input_tensor->device() != device)
404 *infer_request->input_tensor = infer_request->input_tensor->to(device);
405 inputs.push_back(*infer_request->input_tensor);
406
407 *infer_request->output = th_model->jit_model->forward(inputs).toTensor();
408
409 return 0;
410}
411
412static void infer_completion_callback(void *args) {
413 THRequestItem *request = (THRequestItem*)args;
414 THInferRequest *infer_request = request->infer_request;
415 LastLevelTaskItem *lltask = request->lltasks[0];
416 THModel *th_model = (THModel *)lltask->task->model;
417 torch::Tensor *output = infer_request->output;
418 DNNData outputs = { 0 };
419
420 auto slices = torch::split(*output, /*split_size=*/1, /*dim=*/0);
421 for (uint32_t i = 0; i < request->lltask_count; i++) {
422 lltask = request->lltasks[i];
423 TaskItem *task = lltask->task;
424 torch::Tensor out_slice = slices[i];
425 c10::IntArrayRef sizes = out_slice.sizes();
426
427 outputs.order = DCO_RGB;
428 outputs.layout = DL_NCHW;
429 outputs.dt = DNN_FLOAT;
430
431 if (sizes.size() == 4) {
432 // 4 dimensions: [batch_size, channel, height, width]
433 // this format of data is normally used for video frame SR
434 outputs.dims[0] = sizes.at(0); // N
435 outputs.dims[1] = sizes.at(1); // C
436 outputs.dims[2] = sizes.at(2); // H
437 outputs.dims[3] = sizes.at(3); // W
438 } else {
439 avpriv_report_missing_feature(th_model->ctx, "Support of this kind of model");
440 goto err;
441 }
442
443 switch (th_model->model.func_type) {
445 if (task->do_ioproc) {
446#if CONFIG_CUDA
447 if (task->out_frame->format == AV_PIX_FMT_CUDA) {
448 fill_model_output_th_cuda(th_model, task, out_slice);
449 } else {
450#endif
451 if (out_slice.device() != torch::kCPU)
452 out_slice = out_slice.to(torch::kCPU);
453 outputs.scale = 255;
454 outputs.data = out_slice.data_ptr();
455 if (th_model->model.frame_post_proc != NULL) {
456 th_model->model.frame_post_proc(task->out_frame, &outputs,
457 th_model->model.filter_ctx);
458 } else {
460 th_model->ctx);
461 }
462#if CONFIG_CUDA
463 }
464#endif
465 } else {
468 }
469 break;
470 default:
471 avpriv_report_missing_feature(th_model->ctx, "model function type %d", th_model->model.func_type);
472 goto err;
473 }
474 task->inference_done++;
475 }
476
477err:
478 for (uint32_t i = 0; i < request->lltask_count; i++) {
479 av_freep(&request->lltasks[i]);
480 }
481 request->lltask_count = 0;
482
483 th_free_request(infer_request);
484
485 if (ff_safe_queue_push_back(th_model->request_queue, request) < 0) {
486 destroy_request_item(&request);
487 av_log(th_model->ctx, AV_LOG_ERROR, "Unable to push back request_queue when failed to start inference.\n");
488 }
489}
490
491static int execute_model_th(THRequestItem *request, Queue *lltask_queue)
492{
493 THModel *th_model = NULL;
494 LastLevelTaskItem *lltask;
495 TaskItem *task = NULL;
496 int ret = 0;
497
498 if (ff_queue_size(lltask_queue) == 0) {
499 destroy_request_item(&request);
500 return 0;
501 }
502
503 lltask = (LastLevelTaskItem *)ff_queue_peek_front(lltask_queue);
504 if (lltask == NULL) {
505 av_log(NULL, AV_LOG_ERROR, "Failed to get LastLevelTaskItem\n");
506 ret = AVERROR(EINVAL);
507 goto err;
508 }
509 task = lltask->task;
510 th_model = (THModel *)task->model;
511
512#if CONFIG_CUDA
513 if (task->in_frame->format == AV_PIX_FMT_CUDA) {
514 ret = fill_model_input_th_cuda(th_model, request);
515 } else {
516 ret = fill_model_input_th(th_model, request);
517 }
518#else
519 ret = fill_model_input_th(th_model, request);
520#endif
521 if (ret != 0) {
522 goto err;
523 }
524
525 if (task->async) {
526 ret = ff_dnn_start_inference_async(th_model->ctx, &request->exec_module);
527 if (ret != 0) {
528 goto err;
529 }
530 return 0;
531 } else {
532 // Synchronous execution path
533 ret = th_start_inference((void *)(request));
534 if (ret != 0) {
535 goto err;
536 }
538 return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR;
539 }
540
541err:
543 if (ff_safe_queue_push_back(th_model->request_queue, request) < 0) {
544 destroy_request_item(&request);
545 }
546 return ret;
547}
548
549static int get_output_th(DNNModel *model, const char *input_name, int input_width, int input_height,
550 const char *output_name, int *output_width, int *output_height)
551{
552 int ret = 0;
553 THModel *th_model = (THModel*) model;
554 DnnContext *ctx = th_model->ctx;
555 TaskItem task = { 0 };
556 THRequestItem *request = NULL;
557 DNNExecBaseParams exec_params = {
558 .input_name = input_name,
559 .output_names = &output_name,
560 .nb_output = 1,
561 .in_frame = NULL,
562 .out_frame = NULL,
563 };
564 ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, th_model, input_height, input_width, ctx);
565 if ( ret != 0) {
566 goto err;
567 }
568
569 ret = extract_lltask_from_task(&task, th_model->lltask_queue);
570 if ( ret != 0) {
571 av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n");
572 goto err;
573 }
574
576 if (!request) {
577 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
578 ret = AVERROR(EINVAL);
579 goto err;
580 }
581
582 ret = execute_model_th(request, th_model->lltask_queue);
583 *output_width = task.out_frame->width;
584 *output_height = task.out_frame->height;
585
586err:
588 av_frame_free(&task.in_frame);
589 return ret;
590}
591
593{
595 if (!request) {
596 return NULL;
597 }
598 request->input_tensor = NULL;
599 request->output = NULL;
600 return request;
601}
602
604{
605 DNNModel *model = NULL;
606 THModel *th_model = NULL;
607 THRequestItem *item = NULL;
608 const char *device_name = ctx->device ? ctx->device : "cpu";
609
610 th_model = (THModel *)av_mallocz(sizeof(THModel));
611 if (!th_model)
612 return NULL;
613 model = &th_model->model;
614 th_model->ctx = ctx;
615
616 c10::Device device = c10::Device(device_name);
617 if (device.is_xpu()) {
618 if (!at::hasXPU()) {
619 av_log(ctx, AV_LOG_ERROR, "No XPU device found\n");
620 goto fail;
621 }
622#if TORCH_VERSION_MAJOR > 2 || (TORCH_VERSION_MAJOR == 2 && TORCH_VERSION_MINOR >= 6)
623 at::detail::getXPUHooks().init();
624#else
625 at::detail::getXPUHooks().initXPU();
626#endif
627 } else if (device.is_cuda()) {
628 // CUDA device - works for both NVIDIA CUDA and AMD ROCm (which uses CUDA-compatible API)
629 if (!torch::cuda::is_available()) {
630 av_log(ctx, AV_LOG_ERROR, "CUDA/ROCm is not available\n");
631 goto fail;
632 }
633 av_log(ctx, AV_LOG_INFO, "Using CUDA/ROCm device: %s\n", device_name);
634 } else if (!device.is_cpu()) {
635 av_log(ctx, AV_LOG_ERROR, "Not supported device:\"%s\"\n", device_name);
636 goto fail;
637 }
638
639 try {
640 th_model->jit_model = new torch::jit::Module;
641 (*th_model->jit_model) = torch::jit::load(ctx->model_filename);
642 th_model->jit_model->to(device);
643 } catch (const c10::Error& e) {
644 av_log(ctx, AV_LOG_ERROR, "Failed to load torch model\n");
645 goto fail;
646 }
647
648 if (ctx->nireq <= 0) {
649 ctx->nireq = av_cpu_count() / 2 + 1;
650 }
651
653 if (!th_model->request_queue) {
654 goto fail;
655 }
656
657 for (int i = 0; i < ctx->nireq; i++) {
658 item = (THRequestItem *)av_mallocz(sizeof(THRequestItem));
659 if (!item) {
660 goto fail;
661 }
663 if (!item->infer_request) {
664 goto fail;
665 }
666 item->lltasks = (LastLevelTaskItem **)av_malloc_array(ctx->batch_size, sizeof(*item->lltasks));
667 if (!item->lltasks) {
668 goto fail;
669 }
670 item->lltask_count = 0;
671
674 item->exec_module.args = item;
675
676 if (ff_safe_queue_push_back(th_model->request_queue, item) < 0) {
677 goto fail;
678 }
679 item = NULL;
680 }
681
682 th_model->task_queue = ff_queue_create();
683 th_model->lltask_queue = ff_queue_create();
684
685 model->get_input = &get_input_th;
686 model->get_output = &get_output_th;
687 model->filter_ctx = filter_ctx;
688 model->func_type = func_type;
689 return model;
690
691fail:
692 if (item) {
694 }
695 dnn_free_model_th(&model);
696 return NULL;
697}
698
699static int dnn_execute_model_th(const DNNModel *model, DNNExecBaseParams *exec_params)
700{
701 THModel *th_model = (THModel *)model;
702 DnnContext *ctx = th_model->ctx;
703 TaskItem *task;
704 THRequestItem *request;
705 int ret = 0;
706
707 ret = ff_check_exec_params(ctx, DNN_TH, model->func_type, exec_params);
708 if (ret != 0) {
709 av_log(ctx, AV_LOG_ERROR, "exec parameter checking fail.\n");
710 return ret;
711 }
712
713 task = (TaskItem *)av_malloc(sizeof(TaskItem));
714 if (!task) {
715 av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n");
716 return AVERROR(ENOMEM);
717 }
718
719 ret = ff_dnn_fill_task(task, exec_params, th_model, ctx->async, 1);
720 if (ret != 0) {
721 av_freep(&task);
722 av_log(ctx, AV_LOG_ERROR, "unable to fill task.\n");
723 return ret;
724 }
725
726 ret = ff_queue_push_back(th_model->task_queue, task);
727 if (ret < 0) {
728 av_freep(&task);
729 av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n");
730 return ret;
731 }
732
733 ret = extract_lltask_from_task(task, th_model->lltask_queue);
734 if (ret != 0) {
735 av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n");
736 return ret;
737 }
738
739 while (ff_queue_size(th_model->lltask_queue) >= ctx->batch_size) {
741 if (!request) {
742 av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n");
743 return AVERROR(EINVAL);
744 }
745
746 ret = execute_model_th(request, th_model->lltask_queue);
747 if (ret != 0) {
748 return ret;
749 }
750 }
751
752 return 0;
753}
754
756{
757 THModel *th_model = (THModel *)model;
758 return ff_dnn_get_result_common(th_model->task_queue, in, out);
759}
760
761static int dnn_flush_th(const DNNModel *model)
762{
763 THModel *th_model = (THModel *)model;
764 THRequestItem *request;
765
766 if (ff_queue_size(th_model->lltask_queue) == 0)
767 // no pending task need to flush
768 return 0;
769
771 if (!request) {
772 av_log(th_model->ctx, AV_LOG_ERROR, "unable to get infer request.\n");
773 return AVERROR(EINVAL);
774 }
775
776 return execute_model_th(request, th_model->lltask_queue);
777}
778
779extern const DNNModule ff_dnn_backend_torch = {
780 .clazz = DNN_DEFINE_CLASS(dnn_th),
781 .type = DNN_TH,
782 .load_model = dnn_load_model_th,
783 .execute_model = dnn_execute_model_th,
784 .get_result = dnn_get_result_th,
785 .flush = dnn_flush_th,
786 .free_model = dnn_free_model_th,
787};
static const AVFilterPad inputs[]
Definition af_aap.c:299
static const AVFilterPad outputs[]
Definition af_aap.c:310
static FILE * out
static AVFormatContext * ctx
channels
Definition aptx.h:31
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define FLAGS
Definition cmdutils.c:598
#define NULL
Definition coverity.c:32
static AVFrame * frame
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)
const DNNModule ff_dnn_backend_torch
static int get_input_th(DNNModel *model, DNNData *input, const char *input_name)
static int fill_model_input_th(THModel *th_model, THRequestItem *request)
static DNNAsyncStatusType dnn_get_result_th(const DNNModel *model, AVFrame **in, AVFrame **out)
static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue)
static int th_start_inference(void *args)
static int execute_model_th(THRequestItem *request, Queue *lltask_queue)
static const AVOption dnn_th_options[]
static void th_free_request(THInferRequest *request)
static THInferRequest * th_create_inference_request(void)
static int dnn_flush_th(const DNNModel *model)
static int dnn_execute_model_th(const DNNModel *model, DNNExecBaseParams *exec_params)
static DNNModel * dnn_load_model_th(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx)
#define OFFSET(x)
static int get_output_th(DNNModel *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height)
static void deleter(void *arg)
static void destroy_request_item(THRequestItem **arg)
static void dnn_free_model_th(DNNModel **model)
static void infer_completion_callback(void *args)
static int dnn_get_height_idx_by_layout(DNNLayout layout)
DNNAsyncStatusType
@ DL_NCHW
@ DNN_TH
DNNFunctionType
@ DFT_PROCESS_FRAME
static int dnn_get_width_idx_by_layout(DNNLayout layout)
#define DNN_GENERIC_ERROR
@ 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_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.
static const uint8_t frame_size[4]
Definition g723_1.h:222
#define fail
Definition test.h:479
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
#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_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
An API-specific header for AV_HWDEVICE_TYPE_CUDA.
FFmpeg internal API for CUDA.
static const int sizes[][2]
Definition img2dec.c:62
const char * arg
Definition jacosubdec.c:65
const char * to
Definition webvttdec.c:36
int av_cpu_count(void)
Definition cpu.c:228
void avpriv_report_missing_feature(void *avc, const char *msg,...) av_printf_format(2
Log a generic warning message about a missing feature.
Memory handling functions.
#define av_malloc(s)
Definition ops_static.c:52
AVOptions.
pixel format definitions
@ AV_PIX_FMT_RGB24
packed RGB 8:8:8, 24bpp, RGBRGB...
Definition pixfmt.h:75
@ AV_PIX_FMT_BGR0
packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
Definition pixfmt.h:265
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
Definition pixfmt.h:260
@ AV_PIX_FMT_0BGR
packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined
Definition pixfmt.h:264
@ AV_PIX_FMT_RGB0
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition pixfmt.h:263
@ AV_PIX_FMT_BGR24
packed RGB 8:8:8, 24bpp, BGRBGR...
Definition pixfmt.h:76
@ AV_PIX_FMT_0RGB
packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
Definition pixfmt.h:262
void ff_queue_destroy(Queue *q)
Destroy the Queue instance.
Definition queue.c:72
void * ff_queue_pop_front(Queue *q)
Remove and free first element from the Queue.
Definition queue.c:151
int ff_queue_push_back(Queue *q, void *v)
Add data to the tail of the queue.
Definition queue.c:130
void * ff_queue_peek_front(Queue *q)
Return a pointer to the data at the head of the queue.
Definition queue.c:93
size_t ff_queue_size(Queue *q)
Return the length of the Queue.
Definition queue.c:88
Queue * ff_queue_create(void)
Create a Queue instance.
Definition queue.c:47
int ff_safe_queue_push_back(SafeQueue *sq, void *v)
Add data to the tail of queue in the SafeQueue after locking mutex.
Definition safe_queue.c:106
void * ff_safe_queue_pop_front(SafeQueue *sq)
Remove and free first element from the queue in SafeQueue.
Definition safe_queue.c:116
size_t ff_safe_queue_size(SafeQueue *sq)
Return the length of the SafeQueue.
Definition safe_queue.c:80
SafeQueue * ff_safe_queue_create(void)
Create and initialize a SafeQueue instance.
Definition safe_queue.c:52
void ff_safe_queue_destroy(SafeQueue *sq)
Destroy the SafeQueue instance.
Definition safe_queue.c:69
uint8_t * data
The data buffer.
Definition buffer.h:90
An instance of a filter.
Definition avfilter.h:273
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int width
Definition frame.h:544
AVBufferRef * hw_frames_ctx
For hwaccel-format frames, this should be a reference to the AVHWFramesContext describing the frame.
Definition frame.h:769
int height
Definition frame.h:544
int format
format of the frame, -1 if unknown or unset Values correspond to enum AVPixelFormat for video frames,...
Definition frame.h:559
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
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.
float scale
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
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:46
torch::Tensor * input_tensor
torch::Tensor * output
torch::jit::Module * jit_model
Queue * lltask_queue
Queue * task_queue
DnnContext * ctx
SafeQueue * request_queue
LastLevelTaskItem ** lltasks
THInferRequest * infer_request
DNNAsyncExecModule exec_module
uint32_t inference_done
AVFrame * in_frame
uint8_t do_ioproc
uint32_t inference_todo
AVFrame * out_frame
#define av_malloc_array(a, b)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
static FilteringContext * filter_ctx
Definition transcode.c:52