FFmpeg
Loading...
Searching...
No Matches
vf_scale_cuda.c
Go to the documentation of this file.
1/*
2* Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
3*
4* Permission is hereby granted, free of charge, to any person obtaining a
5* copy of this software and associated documentation files (the "Software"),
6* to deal in the Software without restriction, including without limitation
7* the rights to use, copy, modify, merge, publish, distribute, sublicense,
8* and/or sell copies of the Software, and to permit persons to whom the
9* Software is furnished to do so, subject to the following conditions:
10*
11* The above copyright notice and this permission notice shall be included in
12* all copies or substantial portions of the Software.
13*
14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20* DEALINGS IN THE SOFTWARE.
21*/
22
23#include <float.h>
24#include <stdio.h>
25#include <string.h>
26
27#include "libavutil/avassert.h"
28#include "libavutil/common.h"
29#include "libavutil/hwcontext.h"
32#include "libavutil/internal.h"
33#include "libavutil/mem.h"
34#include "libavutil/opt.h"
35#include "libavutil/pixdesc.h"
36#include "libavutil/refstruct.h"
37
38#include "libswscale/filters.h"
39
40#include "avfilter.h"
41#include "filters.h"
42#include "scale_eval.h"
43#include "video.h"
44
45#include "cuda/load_helper.h"
46#include "vf_scale_cuda.h"
47
50 char name[13];
51};
52
53static const struct format_entry supported_formats[] = {
54 {AV_PIX_FMT_YUV420P, "planar8"},
55 {AV_PIX_FMT_YUV422P, "planar8"},
56 {AV_PIX_FMT_YUV444P, "planar8"},
57 {AV_PIX_FMT_YUV420P10,"planar10"},
58 {AV_PIX_FMT_YUV422P10,"planar10"},
59 {AV_PIX_FMT_YUV444P10,"planar10"},
60 {AV_PIX_FMT_YUV444P10MSB,"planar16"},
61 {AV_PIX_FMT_YUV444P12MSB,"planar16"},
62 {AV_PIX_FMT_YUV444P16,"planar16"},
63 {AV_PIX_FMT_NV12, "semiplanar8"},
64 {AV_PIX_FMT_NV16, "semiplanar8"},
65 {AV_PIX_FMT_P010, "semiplanar10"},
66 {AV_PIX_FMT_P210, "semiplanar10"},
67 {AV_PIX_FMT_P012, "semiplanar16"},
68 {AV_PIX_FMT_P212, "semiplanar16"},
69 {AV_PIX_FMT_P016, "semiplanar16"},
70 {AV_PIX_FMT_P216, "semiplanar16"},
71 {AV_PIX_FMT_0RGB32, "bgr0"},
72 {AV_PIX_FMT_0BGR32, "rgb0"},
73 {AV_PIX_FMT_RGB32, "bgra"},
74 {AV_PIX_FMT_BGR32, "rgba"},
75};
76
77#define DIV_UP(a, b) ( ((a) + (b) - 1) / (b) )
78#define BLOCKX 32
79#define BLOCKY 16
80
81#define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, s->hwctx->internal->cuda_dl, x)
82
83enum {
85
90
92};
93
94enum {
98};
99
100enum {
104};
105
106enum {
109};
110
111typedef struct CUDAScalePassPlan {
113 /* Plane groups whose sample grids change along each axis. */
114 unsigned int x_planes;
115 unsigned int y_planes;
117
118typedef struct CUDAScaleFilter {
119 CUdeviceptr weights; ///< float[dst_size][filter_size]
120 CUdeviceptr offsets; ///< int[dst_size]
124
135
145
146typedef struct CUDAScaleContext {
147 const AVClass *class;
148
150
151 enum AVPixelFormat in_fmt, out_fmt;
156
159
162
163 /**
164 * Output sw format. AV_PIX_FMT_NONE for no conversion.
165 */
167
168 char *w_expr; ///< width expression string
169 char *h_expr; ///< height expression string
170
174
175 CUcontext cu_ctx;
176 CUmodule cu_module;
177 CUfunction cu_func_fixed;
179 CUfunction cu_func_out[2];
180 CUfunction cu_func_out_uv[2];
181 CUfunction cu_func_tmp;
182 CUfunction cu_func_tmp_uv;
183 CUstream cu_stream;
184
188
191 int use_filters_opt; /* -1 for auto */
192
193 float param;
195
196/*
197 * Compare the unrounded plane scale. AV_CEIL_RSHIFT() can hide a change
198 * in sample geometry for odd dimensions: 3px 4:4:4 -> 5px 4:2:0 has
199 * three stored chroma samples on each side, but the virtual plane size
200 * changes from 3 to 2.5 and still requires filtering.
201 */
202static int cudascale_plane_needs_scale(int in_size, int out_size,
203 int in_sub, int out_sub)
204{
205 return (int64_t)out_size * (1 << in_sub) !=
206 (int64_t)in_size * (1 << out_sub);
207}
208
209static int cudascale_plane_is_downscaled(int in_size, int out_size,
210 int in_sub, int out_sub)
211{
212 return (int64_t)out_size * (1 << in_sub) <
213 (int64_t)in_size * (1 << out_sub);
214}
215
216static double cudascale_plane_virtual_size(int src_size,
217 int in_size, int out_size,
218 int in_sub, int out_sub)
219{
220 return src_size * ((double) out_size / in_size) *
221 (1 << in_sub) / (1 << out_sub);
222}
223
225 int in_width, int in_height,
226 int out_width, int out_height,
227 int in_sub_x, int in_sub_y,
228 int out_sub_x, int out_sub_y,
229 int has_chroma)
230{
231 unsigned int x_planes = 0;
232 unsigned int y_planes = 0;
233
234 *plan = (CUDAScalePassPlan) {
236 };
237
238 if (cudascale_plane_needs_scale(in_width, out_width, 0, 0))
239 x_planes |= CUDA_SCALE_PLANE_PRIMARY;
240 if (cudascale_plane_needs_scale(in_height, out_height, 0, 0))
241 y_planes |= CUDA_SCALE_PLANE_PRIMARY;
242
243 if (has_chroma) {
244 if (cudascale_plane_needs_scale(in_width, out_width,
245 in_sub_x, out_sub_x))
246 x_planes |= CUDA_SCALE_PLANE_CHROMA;
247 if (cudascale_plane_needs_scale(in_height, out_height,
248 in_sub_y, out_sub_y))
249 y_planes |= CUDA_SCALE_PLANE_CHROMA;
250 }
251
252 plan->x_planes = x_planes;
253 plan->y_planes = y_planes;
254
255 if (x_planes && y_planes) {
258 } else if (x_planes) {
260 } else if (y_planes) {
262 }
263}
264
266{
267 CUDAScaleContext *s = ctx->priv;
268
269 s->frame = av_frame_alloc();
270 if (!s->frame)
271 return AVERROR(ENOMEM);
272
273 s->tmp_frame = av_frame_alloc();
274 if (!s->tmp_frame)
275 return AVERROR(ENOMEM);
276
277 return 0;
278}
279
280static void filter_uninit(CudaFunctions *cu, CUDAScaleFilter *filter)
281{
282 if (filter->weights)
283 cu->cuMemFree(filter->weights);
284 if (filter->offsets)
285 cu->cuMemFree(filter->offsets);
286 memset(filter, 0, sizeof(*filter));
287}
288
289static void cuda_tex_uninit(CudaFunctions *cu, CUDATex *t)
290{
291 for (int i = 0; i < FF_ARRAY_ELEMS(t->tex); i++) {
292 if (t->tex[i])
293 cu->cuTexObjectDestroy(t->tex[i]);
294 if (t->data[i] && !t->external_data)
295 cu->cuMemFree(t->data[i]);
296 }
297
298 memset(t, 0, sizeof(*t));
299}
300
301static void filter_set_uninit(CudaFunctions *cu, CUDAScaleFilterSet *set)
302{
303 cuda_tex_uninit(cu, &set->inter_tex);
304 for (int i = 0; i < FF_ARRAY_ELEMS(set->filters); i++) {
305 filter_uninit(cu, &set->filters[i]);
306 filter_uninit(cu, &set->filters_uv[i]);
307 }
308 memset(set, 0, sizeof(*set));
309}
310
312{
313 CUDAScaleContext *s = ctx->priv;
314
315 if (s->hwctx) {
316 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
317 CUcontext dummy;
318
319 CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx));
320
321 filter_set_uninit(cu, &s->filter_set);
322
323 if (s->cu_module) {
324 CHECK_CU(cu->cuModuleUnload(s->cu_module));
325 s->cu_module = NULL;
326 }
327
328 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
329 }
330
331 av_frame_free(&s->frame);
332 av_buffer_unref(&s->frames_ctx);
333 av_frame_free(&s->tmp_frame);
334}
335
337{
338 AVBufferRef *out_ref = NULL;
339 AVHWFramesContext *out_ctx;
340 int ret;
341
342 out_ref = av_hwframe_ctx_alloc(device_ctx);
343 if (!out_ref)
344 return AVERROR(ENOMEM);
345 out_ctx = (AVHWFramesContext*)out_ref->data;
346
347 out_ctx->format = AV_PIX_FMT_CUDA;
348 out_ctx->sw_format = s->out_fmt;
349 out_ctx->width = FFALIGN(width, 32);
350 out_ctx->height = FFALIGN(height, 32);
351
352 ret = av_hwframe_ctx_init(out_ref);
353 if (ret < 0)
354 goto fail;
355
356 av_frame_unref(s->frame);
357 ret = av_hwframe_get_buffer(out_ref, s->frame, 0);
358 if (ret < 0)
359 goto fail;
360
361 s->frame->width = width;
362 s->frame->height = height;
363
364 av_buffer_unref(&s->frames_ctx);
365 s->frames_ctx = out_ref;
366
367 return 0;
368fail:
369 av_buffer_unref(&out_ref);
370 return ret;
371}
372
374 int out_width, int in_height,
375 int use_float, unsigned int planes)
376{
377 CUDAScaleContext *s = ctx->priv;
378 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
379 int ret = 0;
380
381 *tex = (CUDATex) {
382 .width = out_width,
383 .height = in_height,
384 .crop_width = out_width,
385 .crop_height = in_height,
386 .log2_chroma_w = s->out_desc->log2_chroma_w,
387 .log2_chroma_h = s->in_desc->log2_chroma_h,
388 };
389
390 for (int i = 0; i < s->in_planes; i++) {
391 const int is_chroma = i == 1 || i == 2;
392 const unsigned int plane = is_chroma ? CUDA_SCALE_PLANE_CHROMA :
394 const int sub_x = is_chroma ? tex->log2_chroma_w : 0;
395 const int sub_y = is_chroma ? tex->log2_chroma_h : 0;
396 const int plane_w = AV_CEIL_RSHIFT(out_width, sub_x);
397 const int plane_h = AV_CEIL_RSHIFT(in_height, sub_y);
398 const int sizeof_pixel = (use_float ? sizeof(float) :
399 s->in_plane_depths[i] <= 8 ? 1 : 2) *
400 s->in_plane_channels[i];
401
402 if (!(planes & plane))
403 continue;
404
405 size_t pitch;
406 ret = CHECK_CU(cu->cuMemAllocPitch(&tex->data[i], &pitch,
407 (size_t) plane_w * sizeof_pixel,
408 plane_h, 16));
409 if (ret < 0)
410 goto fail;
411 tex->linesize[i] = pitch;
412
413 CUDA_TEXTURE_DESC tex_desc = {
414 /* inter tex is always read as float */
415 .filterMode = CU_TR_FILTER_MODE_POINT,
416 };
417
418 CUDA_RESOURCE_DESC res_desc = {
419 .resType = CU_RESOURCE_TYPE_PITCH2D,
420 .res.pitch2D.format = use_float ? CU_AD_FORMAT_FLOAT :
421 s->in_plane_depths[i] <= 8 ?
422 CU_AD_FORMAT_UNSIGNED_INT8 :
423 CU_AD_FORMAT_UNSIGNED_INT16,
424 .res.pitch2D.numChannels = s->in_plane_channels[i],
425 .res.pitch2D.devPtr = tex->data[i],
426 .res.pitch2D.pitchInBytes = pitch,
427 .res.pitch2D.width = plane_w,
428 .res.pitch2D.height = plane_h,
429 };
430
431 ret = CHECK_CU(cu->cuTexObjectCreate(&tex->tex[i], &res_desc,
432 &tex_desc, NULL));
433 if (ret < 0)
434 goto fail;
435 }
436
437 return 0;
438
439fail:
440 cuda_tex_uninit(cu, tex);
441 return ret;
442}
443
445{
446 for (int i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
447 if (supported_formats[i].format == fmt)
448 return 1;
449 return 0;
450}
451
452static const char* get_format_name(enum AVPixelFormat fmt)
453{
454 for (int i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
455 if (supported_formats[i].format == fmt)
456 return supported_formats[i].name;
457 return NULL;
458}
459
460static av_cold void set_format_info(AVFilterContext *ctx, enum AVPixelFormat in_format, enum AVPixelFormat out_format)
461{
462 CUDAScaleContext *s = ctx->priv;
463 int i, p, d;
464
465 s->in_fmt = in_format;
466 s->out_fmt = out_format;
467
468 s->in_desc = av_pix_fmt_desc_get(s->in_fmt);
469 s->out_desc = av_pix_fmt_desc_get(s->out_fmt);
470 s->in_planes = av_pix_fmt_count_planes(s->in_fmt);
471 s->out_planes = av_pix_fmt_count_planes(s->out_fmt);
472
473 // find maximum step of each component of each plane
474 // For our subset of formats, this should accurately tell us how many channels CUDA needs
475 // i.e. 1 for Y plane, 2 for UV plane of NV12, 4 for single plane of RGB0 formats
476
477 for (i = 0; i < s->in_desc->nb_components; i++) {
478 d = (s->in_desc->comp[i].depth + 7) / 8;
479 p = s->in_desc->comp[i].plane;
480 s->in_plane_channels[p] = FFMAX(s->in_plane_channels[p], s->in_desc->comp[i].step / d);
481
482 s->in_plane_depths[p] = s->in_desc->comp[i].depth;
483 }
484}
485
486static av_cold int init_processing_chain(AVFilterContext *ctx, int in_width, int in_height,
487 int out_width, int out_height)
488{
489 CUDAScaleContext *s = ctx->priv;
490 FilterLink *inl = ff_filter_link(ctx->inputs[0]);
491 FilterLink *outl = ff_filter_link(ctx->outputs[0]);
492
493 AVHWFramesContext *in_frames_ctx;
494
495 enum AVPixelFormat in_format;
496 enum AVPixelFormat out_format;
497 int ret;
498
499 /* check that we have a hw context */
500 if (!inl->hw_frames_ctx) {
501 av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n");
502 return AVERROR(EINVAL);
503 }
504 in_frames_ctx = (AVHWFramesContext*)inl->hw_frames_ctx->data;
505 in_format = in_frames_ctx->sw_format;
506 out_format = (s->format == AV_PIX_FMT_NONE) ? in_format : s->format;
507
508 if (!format_is_supported(in_format)) {
509 av_log(ctx, AV_LOG_ERROR, "Unsupported input format: %s\n",
510 av_get_pix_fmt_name(in_format));
511 return AVERROR(ENOSYS);
512 }
513 if (!format_is_supported(out_format)) {
514 av_log(ctx, AV_LOG_ERROR, "Unsupported output format: %s\n",
515 av_get_pix_fmt_name(out_format));
516 return AVERROR(ENOSYS);
517 }
518
519 set_format_info(ctx, in_format, out_format);
520 if (s->passthrough && in_width == out_width && in_height == out_height && in_format == out_format) {
521 s->frames_ctx = av_buffer_ref(inl->hw_frames_ctx);
522 if (!s->frames_ctx)
523 return AVERROR(ENOMEM);
524
525 s->use_filters = 0;
526 } else {
527 s->passthrough = 0;
528
529 ret = init_hwframe_ctx(s, in_frames_ctx->device_ref, out_width, out_height);
530 if (ret < 0)
531 return ret;
532
533 if (in_width == out_width && in_height == out_height &&
534 in_format == out_format && s->interp_algo == INTERP_ALGO_DEFAULT &&
535 s->use_filters_opt != 1)
536 s->interp_algo = INTERP_ALGO_NEAREST;
537
538 if (s->interp_algo == INTERP_ALGO_NEAREST) {
539 s->use_filters = 0;
540 } else if (s->use_filters_opt >= 0) {
541 s->use_filters = s->use_filters_opt;
542 } else {
543 /* Lanczos needs the generic path for its full windowed-sinc
544 * kernel. Other algorithms need it when downscaling for correct
545 * anti-aliasing. */
546 s->use_filters = s->interp_algo == INTERP_ALGO_LANCZOS ||
547 cudascale_plane_is_downscaled(in_width, out_width,
548 0, 0) ||
549 cudascale_plane_is_downscaled(in_height, out_height,
550 0, 0) ||
551 (s->in_planes > 1 && s->out_planes > 1 &&
553 in_width, out_width,
554 s->in_desc->log2_chroma_w,
555 s->out_desc->log2_chroma_w) ||
557 in_height, out_height,
558 s->in_desc->log2_chroma_h,
559 s->out_desc->log2_chroma_h)));
560 }
561 }
562
563 outl->hw_frames_ctx = av_buffer_ref(s->frames_ctx);
564 if (!outl->hw_frames_ctx)
565 return AVERROR(ENOMEM);
566
567 return 0;
568}
569
571{
572 CUDAScaleContext *s = ctx->priv;
573 CUcontext dummy, cuda_ctx = s->hwctx->cuda_ctx;
574 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
575 char buf[128];
576 int ret;
577
578 const char *in_fmt_name = get_format_name(s->in_fmt);
579 const char *out_fmt_name = get_format_name(s->out_fmt);
580
581 const char *fixed_infix;
582 int fixed_use_linear;
583 int fixed_as_integer;
584
585 extern const unsigned char ff_vf_scale_cuda_ptx_data[];
586 extern const unsigned int ff_vf_scale_cuda_ptx_len;
587
588 switch (s->interp_algo) {
590 fixed_infix = "Nearest";
591 fixed_use_linear = 0;
592 fixed_as_integer = 1;
593 break;
595 fixed_infix = "Bilinear";
596 fixed_use_linear = 1;
597 fixed_as_integer = 1;
598 break;
601 fixed_infix = "Bicubic";
602 fixed_use_linear = 0;
603 fixed_as_integer = 0;
604 break;
606 fixed_infix = "Lanczos";
607 fixed_use_linear = 0;
608 fixed_as_integer = 0;
609 break;
610 default:
611 av_log(ctx, AV_LOG_ERROR, "Unknown interpolation algorithm\n");
612 return AVERROR_BUG;
613 }
614
615 ret = CHECK_CU(cu->cuCtxPushCurrent(cuda_ctx));
616 if (ret < 0)
617 return ret;
618
619 ret = ff_cuda_load_module(ctx, s->hwctx, &s->cu_module,
620 ff_vf_scale_cuda_ptx_data, ff_vf_scale_cuda_ptx_len);
621 if (ret < 0)
622 goto fail;
623
624 if (s->use_filters) {
625 static const char *const infix[] = { "Generic_h", "Generic_v" };
626 const char *tmp_infix = s->interp_algo == INTERP_ALGO_LANCZOS ?
627 "Generic_float_h" : "Generic_h";
628
629 s->interp_use_linear = 0;
630 s->interp_as_integer = 0;
631
632 for (int dir = CUDA_SCALE_DIR_X; dir <= CUDA_SCALE_DIR_Y; dir++) {
633 snprintf(buf, sizeof(buf), "Subsample_%s_%s_%s",
634 infix[dir], in_fmt_name, out_fmt_name);
635 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_out[dir],
636 s->cu_module, buf));
637 if (ret < 0)
638 goto unsupported;
639
640 snprintf(buf, sizeof(buf), "Subsample_%s_%s_%s_uv",
641 infix[dir], in_fmt_name, out_fmt_name);
642 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_out_uv[dir],
643 s->cu_module, buf));
644 if (ret < 0)
645 goto unsupported;
646 }
647
648 snprintf(buf, sizeof(buf), "Subsample_%s_%s_%s", tmp_infix,
649 in_fmt_name, in_fmt_name);
650 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_tmp,
651 s->cu_module, buf));
652 if (ret < 0)
653 goto unsupported;
654
655 if (s->in_planes > 1) {
656 snprintf(buf, sizeof(buf), "Subsample_%s_%s_%s_uv", tmp_infix,
657 in_fmt_name, in_fmt_name);
658 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_tmp_uv,
659 s->cu_module, buf));
660 if (ret < 0)
661 goto unsupported;
662 }
663 } else {
664 s->interp_use_linear = fixed_use_linear;
665 s->interp_as_integer = fixed_as_integer;
666
667 snprintf(buf, sizeof(buf), "Subsample_%s_%s_%s", fixed_infix,
668 in_fmt_name, out_fmt_name);
669 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_fixed,
670 s->cu_module, buf));
671 if (ret < 0)
672 goto unsupported;
673
674 snprintf(buf, sizeof(buf), "Subsample_%s_%s_%s_uv", fixed_infix,
675 in_fmt_name, out_fmt_name);
676 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_fixed_uv,
677 s->cu_module, buf));
678 if (ret < 0)
679 goto unsupported;
680 }
681
682 goto fail;
683
685 av_log(ctx, AV_LOG_FATAL, "Unsupported conversion: %s -> %s\n",
686 in_fmt_name, out_fmt_name);
687 ret = AVERROR(ENOSYS);
688
689fail:
690 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
691
692 return ret;
693}
694
697 int src_size, int dst_size,
698 double virtual_size, int needs_scale)
699{
700 CUDAScaleContext *s = ctx->priv;
701 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
702
704 .scaler_params = { SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT },
705 .src_size = src_size,
706 .dst_size = dst_size,
707 .virtual_size = virtual_size,
708 };
709
710 if (!needs_scale) {
711 params.scaler = SWS_SCALE_POINT;
712 params.virtual_size = 0.0;
713 } else {
714 switch (s->interp_algo) {
715 case INTERP_ALGO_NEAREST: return 0; /* no weights needed */
716 case INTERP_ALGO_BILINEAR: params.scaler = SWS_SCALE_BILINEAR; break;
718 params.scaler = SWS_SCALE_LANCZOS;
719 if (s->param != SCALE_CUDA_PARAM_DEFAULT)
720 params.scaler_params[0] = s->param;
721 break;
724 params.scaler = SWS_SCALE_BICUBIC;
725 params.scaler_params[0] = params.scaler_params[1] = 0.0;
726 if (s->param != SCALE_CUDA_PARAM_DEFAULT)
727 params.scaler_params[1] = s->param;
728 break;
729 }
730 }
731
734 if (ret < 0) {
735 if (ret == AVERROR(ENOTSUP)) {
736 av_log(ctx, AV_LOG_ERROR, "Filter size exceeds the maximum "
737 "currently supported by the CUDA scaler (%d).\n",
739 }
740 return ret;
741 }
742
743 float *tmp = av_malloc_array(weights->num_weights, sizeof(*tmp));
744 if (!tmp) {
745 ret = AVERROR(ENOMEM);
746 goto fail;
747 }
748 for (size_t i = 0; i < weights->num_weights; i++)
749 tmp[i] = weights->weights[i] / (float) SWS_FILTER_SCALE;
750
751 f->filter_size = weights->filter_size;
752 f->dst_size = dst_size;
753
754 const size_t weights_size = weights->num_weights * sizeof(*tmp);
755 ret = CHECK_CU(cu->cuMemAlloc(&f->weights, weights_size));
756 if (ret < 0)
757 goto fail;
758 ret = CHECK_CU(cu->cuMemcpyHtoD(f->weights, tmp, weights_size));
759 if (ret < 0)
760 goto fail;
761
762 const size_t offsets_size = dst_size * sizeof(*weights->offsets);
763 ret = CHECK_CU(cu->cuMemAlloc(&f->offsets, offsets_size));
764 if (ret < 0)
765 goto fail;
766 ret = CHECK_CU(cu->cuMemcpyHtoD(f->offsets, weights->offsets, offsets_size));
767 if (ret < 0)
768 goto fail;
769
770 av_log(ctx, AV_LOG_VERBOSE, " using %d tap '%s' filter: %d -> %d\n",
771 f->filter_size, weights->name, src_size, dst_size);
772
773 ret = 0;
774
775fail:
776 av_free(tmp);
778 return ret;
779}
780
783 int in_width, int in_height,
784 int normalize_crop)
785{
786 CUDAScaleContext *s = ctx->priv;
787 AVFilterLink *outlink = ctx->outputs[0];
788 int ret;
789
790 const int in_sub_x = s->in_desc->log2_chroma_w;
791 const int in_sub_y = s->in_desc->log2_chroma_h;
792 const int out_sub_x = s->out_desc->log2_chroma_w;
793 const int out_sub_y = s->out_desc->log2_chroma_h;
794
795 const int has_chroma = s->in_planes > 1 && s->out_planes > 1;
796 const unsigned int all_planes = CUDA_SCALE_PLANE_PRIMARY |
797 (has_chroma ? CUDA_SCALE_PLANE_CHROMA : 0);
798 int pass_x, pass_y;
799
800 set->in_width = in_width;
801 set->in_height = in_height;
802 set->normalize_crop = normalize_crop;
803
804 cudascale_plan_passes(&set->pass_plan,
805 in_width, in_height, outlink->w, outlink->h,
806 in_sub_x, in_sub_y, out_sub_x, out_sub_y,
807 has_chroma);
808
809 /* The generic path also handles non-scaling copies and format
810 * conversions with a one-tap horizontal filter. */
811 if (set->pass_plan.dir[FILTER_OUT] == CUDA_SCALE_DIR_NONE)
812 set->pass_plan.dir[FILTER_OUT] = CUDA_SCALE_DIR_X;
813
814 pass_x = set->pass_plan.dir[FILTER_TMP] == CUDA_SCALE_DIR_X ?
815 FILTER_TMP :
816 set->pass_plan.dir[FILTER_OUT] == CUDA_SCALE_DIR_X ?
817 FILTER_OUT : -1;
818 pass_y = set->pass_plan.dir[FILTER_OUT] == CUDA_SCALE_DIR_Y ?
819 FILTER_OUT : -1;
820
821 if (pass_x >= 0) {
822 const unsigned int planes = pass_x == FILTER_TMP ?
823 (normalize_crop ? all_planes :
824 set->pass_plan.x_planes) :
825 all_planes;
826
827 if (pass_x == FILTER_TMP)
828 set->tmp_planes = planes;
829
831 ret = cudascale_filter_init(ctx, &set->filters[pass_x],
832 in_width, outlink->w, 0.0,
833 !!(set->pass_plan.x_planes &
835 if (ret < 0)
836 goto fail;
837 }
839 const int src_size = AV_CEIL_RSHIFT(in_width, in_sub_x);
840 const int dst_size = AV_CEIL_RSHIFT(outlink->w, out_sub_x);
841 const double virtual_size = cudascale_plane_virtual_size(
842 src_size, in_width, outlink->w, in_sub_x, out_sub_x);
843 ret = cudascale_filter_init(ctx, &set->filters_uv[pass_x],
844 src_size, dst_size, virtual_size,
845 !!(set->pass_plan.x_planes &
847 if (ret < 0)
848 goto fail;
849 }
850 }
851
852 if (pass_y >= 0) {
853 ret = cudascale_filter_init(ctx, &set->filters[pass_y],
854 in_height, outlink->h, 0.0,
855 !!(set->pass_plan.y_planes &
857 if (ret < 0)
858 goto fail;
859 if (all_planes & CUDA_SCALE_PLANE_CHROMA) {
860 const int src_size = AV_CEIL_RSHIFT(in_height, in_sub_y);
861 const int dst_size = AV_CEIL_RSHIFT(outlink->h, out_sub_y);
862 const double virtual_size = cudascale_plane_virtual_size(
863 src_size, in_height, outlink->h, in_sub_y, out_sub_y);
864 ret = cudascale_filter_init(ctx, &set->filters_uv[pass_y],
865 src_size, dst_size, virtual_size,
866 !!(set->pass_plan.y_planes &
868 if (ret < 0)
869 goto fail;
870 }
871 }
872
873 if (pass_x == FILTER_TMP) {
874 ret = inter_buf_init(ctx, &set->inter_tex,
875 outlink->w, in_height,
876 s->interp_algo == INTERP_ALGO_LANCZOS,
877 set->tmp_planes);
878 if (ret < 0)
879 goto fail;
880 }
881
882 ret = 0;
883
884fail:
885 return ret;
886}
887
889 int in_width, int in_height,
890 int normalize_crop)
891{
892 CUDAScaleContext *s = ctx->priv;
893 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
894 CUDAScaleFilterSet next = { 0 };
896 int ret;
897
898 if (s->filter_set.in_width == in_width &&
899 s->filter_set.in_height == in_height &&
900 s->filter_set.normalize_crop == normalize_crop)
901 return 0;
902
903 ret = cudascale_filter_set_init(ctx, &next, in_width, in_height,
904 normalize_crop);
905 if (ret < 0)
906 goto fail;
907
908 if (s->filter_set.in_width) {
909 /* Queued kernels may still reference the active LUTs and texture. */
910 ret = CHECK_CU(cu->cuStreamSynchronize(s->cu_stream));
911 if (ret < 0)
912 goto fail;
913 }
914
915 old = s->filter_set;
916 s->filter_set = next;
917 memset(&next, 0, sizeof(next));
918 filter_set_uninit(cu, &old);
919
921 "Prepared generic filters for visible input %dx%d%s\n",
922 in_width, in_height, normalize_crop ? " (cropped)" : "");
923 return 0;
924
925fail:
926 filter_set_uninit(cu, &next);
927 return ret;
928}
929
931 int in_width, int in_height)
932{
933 CUDAScaleContext *s = ctx->priv;
934 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
935 CUcontext dummy;
936 int ret;
937
938 ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx));
939 if (ret < 0)
940 return ret;
941
942 ret = cudascale_prepare_filter_set(ctx, in_width, in_height, 0);
943
944 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
945 return ret;
946}
947
949{
950 AVFilterContext *ctx = outlink->src;
951 AVFilterLink *inlink = outlink->src->inputs[0];
952 FilterLink *inl = ff_filter_link(inlink);
953 CUDAScaleContext *s = ctx->priv;
954 AVHWFramesContext *frames_ctx;
955 AVCUDADeviceContext *device_hwctx;
956 int w, h;
957 double w_adj = 1.0;
958 int ret;
959
960 if ((ret = ff_scale_eval_dimensions(s,
961 s->w_expr, s->h_expr,
962 inlink, outlink,
963 &w, &h)) < 0)
964 goto fail;
965
966 if (s->reset_sar)
967 w_adj = inlink->sample_aspect_ratio.num ?
969
970 ret = ff_scale_adjust_dimensions(inlink, &w, &h,
971 s->force_original_aspect_ratio,
972 s->force_divisible_by, w_adj);
973 if (ret < 0)
974 goto fail;
975
976 if (((int64_t)h * inlink->w) > INT_MAX ||
977 ((int64_t)w * inlink->h) > INT_MAX)
978 av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
979
980 outlink->w = w;
981 outlink->h = h;
982
983 ret = init_processing_chain(ctx, inlink->w, inlink->h, w, h);
984 if (ret < 0)
985 return ret;
986
987 frames_ctx = (AVHWFramesContext*)inl->hw_frames_ctx->data;
988 device_hwctx = frames_ctx->device_ctx->hwctx;
989
990 s->hwctx = device_hwctx;
991 s->cu_stream = s->hwctx->stream;
992
993 if (s->reset_sar)
994 outlink->sample_aspect_ratio = (AVRational){1, 1};
995 else if (inlink->sample_aspect_ratio.num) {
996 outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w,
997 outlink->w*inlink->h},
998 inlink->sample_aspect_ratio);
999 } else {
1000 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
1001 }
1002
1003 av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s -> w:%d h:%d fmt:%s%s\n",
1004 inlink->w, inlink->h, av_get_pix_fmt_name(s->in_fmt),
1005 outlink->w, outlink->h, av_get_pix_fmt_name(s->out_fmt),
1006 s->passthrough ? " (passthrough)" : "");
1007
1008 if (s->use_filters) {
1009 ret = cudascale_setup_filters(ctx, inlink->w, inlink->h);
1010 if (ret < 0)
1011 return ret;
1012 }
1013
1015 if (ret < 0)
1016 return ret;
1017
1018 return 0;
1019
1020fail:
1021 return ret;
1022}
1023
1025 int *width, int *height,
1026 int *normalize_crop)
1027{
1028 AVFilterLink *inlink = ctx->inputs[0];
1029 size_t frame_width, frame_height;
1030
1031 if (frame->width <= 0 || frame->height <= 0) {
1032 av_log(ctx, AV_LOG_ERROR, "Invalid crop rectangle\n");
1033 return AVERROR(EINVAL);
1034 }
1035
1036 frame_width = frame->width;
1037 frame_height = frame->height;
1038 if (frame->crop_left >= frame_width ||
1039 frame->crop_right >= frame_width - frame->crop_left ||
1040 frame->crop_top >= frame_height ||
1041 frame->crop_bottom >= frame_height - frame->crop_top) {
1042 av_log(ctx, AV_LOG_ERROR, "Invalid crop rectangle\n");
1043 return AVERROR(EINVAL);
1044 }
1045
1046 *width = (int)(frame_width - frame->crop_left - frame->crop_right);
1047 *height = (int)(frame_height - frame->crop_top - frame->crop_bottom);
1048 *normalize_crop = frame->crop_left || frame->crop_top ||
1049 *width != inlink->w || *height != inlink->h;
1050
1051 return 0;
1052}
1053
1054/* if depths/channels are NULL, only maps pointers without creating textures */
1056 const int depths[4], const int channels[4],
1057 CUDATex *tex, int use_linear, int as_integer)
1058{
1059 CUDAScaleContext *s = ctx->priv;
1060 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
1061
1062 const AVHWFramesContext *fctx = (const AVHWFramesContext*)frame->hw_frames_ctx->data;
1064 const int planes = av_pix_fmt_count_planes(fctx->sw_format);
1065
1066 *tex = (CUDATex) {
1067 .width = frame->width,
1068 .height = frame->height,
1069 .crop_left = frame->crop_left,
1070 .crop_top = frame->crop_top,
1071 .crop_width = (frame->width - frame->crop_right) - frame->crop_left,
1072 .crop_height = (frame->height - frame->crop_bottom) - frame->crop_top,
1073 .color_range = frame->color_range,
1074 .log2_chroma_w = desc->log2_chroma_w,
1075 .log2_chroma_h = desc->log2_chroma_h,
1076 .external_data = 1,
1077 };
1078
1079 for (int i = 0; i < planes; i++) {
1080 tex->data[i] = (CUdeviceptr)frame->data[i];
1081 tex->linesize[i] = frame->linesize[i];
1082 if (!depths || !channels)
1083 continue;
1084
1085 CUDA_TEXTURE_DESC tex_desc = {
1086 .filterMode = use_linear ?
1087 CU_TR_FILTER_MODE_LINEAR :
1088 CU_TR_FILTER_MODE_POINT,
1089 .flags = as_integer ? CU_TRSF_READ_AS_INTEGER : 0,
1090 };
1091
1092 const int is_chroma = i == 1 || i == 2;
1093 const int sub_x = is_chroma ? desc->log2_chroma_w : 0;
1094 const int sub_y = is_chroma ? desc->log2_chroma_h : 0;
1095 CUDA_RESOURCE_DESC res_desc = {
1096 .resType = CU_RESOURCE_TYPE_PITCH2D,
1097 .res.pitch2D.format = depths[i] <= 8 ?
1098 CU_AD_FORMAT_UNSIGNED_INT8 :
1099 CU_AD_FORMAT_UNSIGNED_INT16,
1100 .res.pitch2D.numChannels = channels[i],
1101 .res.pitch2D.pitchInBytes = tex->linesize[i],
1102 .res.pitch2D.devPtr = tex->data[i],
1103 .res.pitch2D.width = AV_CEIL_RSHIFT(frame->width, sub_x),
1104 .res.pitch2D.height = AV_CEIL_RSHIFT(frame->height, sub_y),
1105 };
1106
1107 int ret = CHECK_CU(cu->cuTexObjectCreate(&tex->tex[i], &res_desc, &tex_desc, NULL));
1108 if (ret < 0) {
1109 cuda_tex_uninit(cu, tex);
1110 return ret;
1111 }
1112 }
1113
1114 return 0;
1115}
1116
1118 const CUtexObject src_tex[4],
1119 int src_left, int src_top, int src_width, int src_height,
1120 const CUdeviceptr out_data[4],
1121 int dst_width, int dst_height, int dst_pitch, int mpeg_range,
1122 const CUDAScaleFilter *filter)
1123{
1124 CUDAScaleContext *s = ctx->priv;
1125 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
1126
1128 .src_tex = {src_tex[0], src_tex[1], src_tex[2], src_tex[3]},
1129 .dst = {
1130 out_data[0],
1131 out_data[1],
1132 out_data[2],
1133 out_data[3]
1134 },
1135 .dst_width = dst_width,
1136 .dst_height = dst_height,
1137 .dst_pitch = dst_pitch,
1138 .src_left = src_left,
1139 .src_top = src_top,
1140 .src_width = src_width,
1141 .src_height = src_height,
1142 .param = s->param,
1143 .mpeg_range = mpeg_range,
1144 /* Supported input formats use the same depth and shift for
1145 * every component. */
1146 .src_depth = s->in_desc->comp[0].depth,
1147 .src_storage_max = ((1U << s->in_desc->comp[0].depth) - 1) <<
1148 s->in_desc->comp[0].shift,
1149 };
1150
1151 if (filter) {
1152 params.weights = filter->weights;
1153 params.offsets = filter->offsets;
1154 params.filter_size = filter->filter_size;
1155 }
1156
1157 void *args[] = { &params };
1158
1159 return CHECK_CU(cu->cuLaunchKernel(func,
1160 DIV_UP(dst_width, BLOCKX), DIV_UP(dst_height, BLOCKY), 1,
1161 BLOCKX, BLOCKY, 1, 0, s->cu_stream, args, NULL));
1162}
1163
1165 const CUDAScaleFilterSet *set, int pass,
1166 const CUDATex *out, const CUDATex *in)
1167{
1168 CUDAScaleContext *s = ctx->priv;
1169 const CUDAScaleFilter *filter = NULL;
1170 const CUDAScaleFilter *filter_uv = NULL;
1171 CUfunction func, func_uv;
1172 int mpeg_range = in->color_range != AVCOL_RANGE_JPEG;
1173 int ret;
1174
1175 int out_planes = s->out_planes;
1176 if (pass == FILTER_TMP) {
1177 out_planes = s->in_planes;
1178 func = s->cu_func_tmp;
1179 func_uv = s->cu_func_tmp_uv;
1180 } else if (s->use_filters) {
1181 const int dir = set->pass_plan.dir[FILTER_OUT];
1182
1184 func = s->cu_func_out[dir];
1185 func_uv = s->cu_func_out_uv[dir];
1186 } else {
1187 func = s->cu_func_fixed;
1188 func_uv = s->cu_func_fixed_uv;
1189 }
1190
1191 if (s->use_filters) {
1192 filter = &set->filters[pass];
1193 filter_uv = &set->filters_uv[pass];
1194 }
1195
1196 if (pass != FILTER_TMP ||
1197 (set->tmp_planes & CUDA_SCALE_PLANE_PRIMARY)) {
1198 // scale primary plane(s). Usually Y (and A), or single plane of RGB frames.
1200 in->tex, in->crop_left, in->crop_top,
1201 in->crop_width, in->crop_height,
1202 out->data, out->width, out->height,
1203 out->linesize[0], mpeg_range, filter);
1204 if (ret < 0)
1205 return ret;
1206 }
1207
1208 if (out_planes > 1 &&
1209 (pass != FILTER_TMP ||
1210 (set->tmp_planes & CUDA_SCALE_PLANE_CHROMA))) {
1211 // scale UV plane. Scale function sets both U and V plane, or singular interleaved plane.
1212 /* Match av_frame_apply_cropping(): subsampled plane origins round
1213 * down, while visible plane extents round up. */
1214 ret = call_resize_kernel(ctx, func_uv, in->tex,
1215 in->crop_left >> in->log2_chroma_w,
1216 in->crop_top >> in->log2_chroma_h,
1219 out->data,
1220 AV_CEIL_RSHIFT(out->width, out->log2_chroma_w),
1221 AV_CEIL_RSHIFT(out->height, out->log2_chroma_h),
1222 out->linesize[1], mpeg_range, filter_uv);
1223 if (ret < 0)
1224 return ret;
1225 }
1226
1227 return 0;
1228}
1229
1231 int in_width, int in_height, int normalize_crop)
1232{
1233 CUDAScaleContext *s = ctx->priv;
1234 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
1235 AVFilterLink *outlink = ctx->outputs[0];
1236 int ret = 0;
1237
1238 if (s->use_filters) {
1239 ret = cudascale_prepare_filter_set(ctx, in_width, in_height,
1240 normalize_crop);
1241 if (ret < 0)
1242 return ret;
1243 }
1244
1245 CUDATex in_tex = {0}, out_tex = {0};
1246 ret = cuda_tex_map_frame(ctx, in, s->in_plane_depths,
1247 s->in_plane_channels, &in_tex,
1248 s->interp_use_linear, s->interp_as_integer);
1249 if (ret < 0)
1250 goto fail;
1251
1252 ret = cuda_tex_map_frame(ctx, s->frame, NULL, NULL, &out_tex, 0, 0);
1253 if (ret < 0)
1254 goto fail;
1255
1256 const CUDATex *src = &in_tex;
1257 CUDATex inter_tex;
1258 if (s->use_filters &&
1259 s->filter_set.pass_plan.dir[FILTER_TMP] != CUDA_SCALE_DIR_NONE) {
1260 /* Handle first pass separately */
1261 s->filter_set.inter_tex.color_range = in->color_range;
1262 inter_tex = s->filter_set.inter_tex;
1263 /* Reuse input textures for plane groups not touched by this pass. */
1264 for (int i = 0; i < s->in_planes; i++) {
1265 if (!inter_tex.tex[i])
1266 inter_tex.tex[i] = in_tex.tex[i];
1267 }
1268 ret = scalecuda_resize(ctx, &s->filter_set, FILTER_TMP,
1269 &s->filter_set.inter_tex, src);
1270 if (ret < 0)
1271 goto fail;
1272 src = &inter_tex;
1273 }
1274
1275 ret = scalecuda_resize(ctx, &s->filter_set, FILTER_OUT, &out_tex, src);
1276 if (ret < 0)
1277 goto fail;
1278
1279 ret = av_hwframe_get_buffer(s->frame->hw_frames_ctx, s->tmp_frame, 0);
1280 if (ret < 0)
1281 goto fail;
1282
1283 av_frame_move_ref(out, s->frame);
1284 av_frame_move_ref(s->frame, s->tmp_frame);
1285
1286 s->frame->width = outlink->w;
1287 s->frame->height = outlink->h;
1288
1289 ret = av_frame_copy_props(out, in);
1290 if (ret < 0)
1291 goto fail;
1292
1293 out->crop_top = 0;
1294 out->crop_bottom = 0;
1295 out->crop_left = 0;
1296 out->crop_right = 0;
1297
1298 if (in->crop_left || in->crop_right ||
1299 in->crop_top || in->crop_bottom ||
1300 out->width != in_width || out->height != in_height) {
1301 av_frame_side_data_remove_by_props(&out->side_data, &out->nb_side_data,
1303 }
1304
1305fail:
1306 cuda_tex_uninit(cu, &in_tex);
1307 cuda_tex_uninit(cu, &out_tex);
1308 return ret;
1309}
1310
1312{
1313 AVFilterContext *ctx = link->dst;
1314 CUDAScaleContext *s = ctx->priv;
1315 AVFilterLink *outlink = ctx->outputs[0];
1316 CudaFunctions *cu = s->hwctx->internal->cuda_dl;
1317
1318 AVFrame *out = NULL;
1319 CUcontext dummy;
1320 int in_width, in_height, normalize_crop;
1321 int ret = 0;
1322
1323 if (s->passthrough)
1324 return ff_filter_frame(outlink, in);
1325
1326 ret = cudascale_frame_geometry(ctx, in, &in_width, &in_height,
1327 &normalize_crop);
1328 if (ret < 0)
1329 goto fail;
1330
1331 out = av_frame_alloc();
1332 if (!out) {
1333 ret = AVERROR(ENOMEM);
1334 goto fail;
1335 }
1336
1337 ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx));
1338 if (ret < 0)
1339 goto fail;
1340
1341 ret = cudascale_scale(ctx, out, in, in_width, in_height,
1342 normalize_crop);
1343
1344 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
1345 if (ret < 0)
1346 goto fail;
1347
1348 if (s->reset_sar) {
1349 out->sample_aspect_ratio = (AVRational){1, 1};
1350 } else {
1351 av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den,
1352 (int64_t)in->sample_aspect_ratio.num * outlink->h * in_width,
1353 (int64_t)in->sample_aspect_ratio.den * outlink->w * in_height,
1354 INT_MAX);
1355 }
1356
1357 av_frame_free(&in);
1358 return ff_filter_frame(outlink, out);
1359fail:
1360 av_frame_free(&in);
1362 return ret;
1363}
1364
1366{
1367 CUDAScaleContext *s = inlink->dst->priv;
1368
1369 return s->passthrough ?
1370 ff_null_get_video_buffer (inlink, w, h) :
1372}
1373
1374#define OFFSET(x) offsetof(CUDAScaleContext, x)
1375#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
1376static const AVOption options[] = {
1377 { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS },
1378 { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS },
1379 { "interp_algo", "Interpolation algorithm used for resizing", OFFSET(interp_algo), AV_OPT_TYPE_INT, { .i64 = INTERP_ALGO_DEFAULT }, 0, INTERP_ALGO_COUNT - 1, FLAGS, .unit = "interp_algo" },
1380 { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_NEAREST }, 0, 0, FLAGS, .unit = "interp_algo" },
1381 { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_BILINEAR }, 0, 0, FLAGS, .unit = "interp_algo" },
1382 { "bicubic", "bicubic", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_BICUBIC }, 0, 0, FLAGS, .unit = "interp_algo" },
1383 { "lanczos", "lanczos", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_LANCZOS }, 0, 0, FLAGS, .unit = "interp_algo" },
1384 { "format", "Output video pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, { .i64 = AV_PIX_FMT_NONE }, INT_MIN, INT_MAX, .flags=FLAGS },
1385 { "passthrough", "Do not process frames at all if parameters match", OFFSET(passthrough), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS },
1386 { "use_filters", "Use generic filters instead of fixed function kernels", OFFSET(use_filters_opt), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, .unit = "use_filters" },
1387 { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "use_filters" },
1388 { "param", "Algorithm-Specific parameter", OFFSET(param), AV_OPT_TYPE_FLOAT, { .dbl = SCALE_CUDA_PARAM_DEFAULT }, -FLT_MAX, FLT_MAX, FLAGS },
1389 { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, SCALE_FORCE_OAR_NB-1, FLAGS, .unit = "force_oar" },
1390 { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_FORCE_OAR_DISABLE }, 0, 0, FLAGS, .unit = "force_oar" },
1391 { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_FORCE_OAR_DECREASE }, 0, 0, FLAGS, .unit = "force_oar" },
1392 { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_FORCE_OAR_INCREASE }, 0, 0, FLAGS, .unit = "force_oar" },
1393 { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 256, FLAGS },
1394 { "reset_sar", "reset SAR to 1 and scale to square pixels if scaling proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
1395 { NULL },
1396};
1397
1398static const AVClass cudascale_class = {
1399 .class_name = "cudascale",
1400 .item_name = av_default_item_name,
1401 .option = options,
1402 .version = LIBAVUTIL_VERSION_INT,
1403};
1404
1406 {
1407 .name = "default",
1408 .type = AVMEDIA_TYPE_VIDEO,
1409 .filter_frame = cudascale_filter_frame,
1410 .get_buffer.video = cudascale_get_video_buffer,
1411 },
1412};
1413
1415 {
1416 .name = "default",
1417 .type = AVMEDIA_TYPE_VIDEO,
1418 .config_props = cudascale_config_props,
1419 },
1420};
1421
1423 .p.name = "scale_cuda",
1424 .p.description = NULL_IF_CONFIG_SMALL("GPU accelerated video resizer"),
1425
1426 .p.priv_class = &cudascale_class,
1427
1428 .init = cudascale_init,
1429 .uninit = cudascale_uninit,
1430
1431 .priv_size = sizeof(CUDAScaleContext),
1432
1435
1437
1438 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
1439};
SwsAArch64OpImplParams params
Definition ops.c:51
static const char *const format[]
Definition af_aiir.c:445
const FFFilter ff_vf_scale_cuda
static FILE * out
static int out_size
static AVFormatContext * ctx
channels
Definition aptx.h:31
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
@ FILTER_NB
int ff_filter_frame(AVFilterLink *link, AVFrame *frame)
Send a frame of data to the next filter.
Definition avfilter.c:1068
Main libavfilter public API header.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define f(width, name)
Definition cbs_vp8.c:236
#define s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
common internal and external API header
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVFrame * frame
static int dummy
Definition ffplay.c:3754
int ff_sws_filter_generate(void *log, const SwsFilterParams *params, SwsFilterWeights **out)
Generate a filter kernel for the given parameters.
Definition filters.c:187
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_PIXEL_FMT
Underlying C type is enum AVPixelFormat.
Definition opt.h:306
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
@ AV_OPT_TYPE_FLOAT
Underlying C type is float.
Definition opt.h:270
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ 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
void av_buffer_unref(AVBufferRef **buf)
Free a given reference and automatically free the buffer if there are no more references to it.
Definition buffer.c:139
AVBufferRef * av_buffer_ref(const AVBufferRef *buf)
Create a new reference to an AVBuffer.
Definition buffer.c:103
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition error.h:52
#define AVERROR(e)
Definition error.h:45
void av_frame_unref(AVFrame *frame)
Unreference all the buffers referenced by frame and reset the frame fields.
Definition frame.c:496
void av_frame_move_ref(AVFrame *dst, AVFrame *src)
Move everything contained in src to dst and reset src.
Definition frame.c:523
void av_frame_side_data_remove_by_props(AVFrameSideData ***sd, int *nb_sd, int props)
Remove and free all side data instances that match any of the given side data properties.
Definition side_data.c:123
void av_frame_free(AVFrame **frame)
Free the frame and any dynamically allocated objects in it, e.g.
Definition frame.c:64
int av_frame_copy_props(AVFrame *dst, const AVFrame *src)
Copy only "metadata" fields from src to dst.
Definition frame.c:599
AVFrame * av_frame_alloc(void)
Allocate an AVFrame and set its fields to default values.
Definition frame.c:52
@ AV_SIDE_DATA_PROP_SIZE_DEPENDENT
Side data depends on the video dimensions.
Definition frame.h:354
#define AV_LOG_FATAL
Something went wrong and recovery is not possible.
Definition log.h:204
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
const char * av_default_item_name(void *ptr)
Return the context name.
Definition log.c:241
AVRational av_mul_q(AVRational b, AVRational c)
Multiply two rationals.
Definition rational.c:80
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max)
Reduce a fraction.
Definition rational.c:35
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
#define LIBAVUTIL_VERSION_INT
Definition version.h:85
#define SWS_PARAM_DEFAULT
Definition swscale.h:456
@ SWS_SCALE_POINT
nearest neighbor (point sampling)
Definition swscale.h:100
@ SWS_SCALE_LANCZOS
3-tap sinc/sinc
Definition swscale.h:104
@ SWS_SCALE_BILINEAR
bilinear filtering
Definition swscale.h:98
@ SWS_SCALE_BICUBIC
2-tap cubic BC-spline
Definition swscale.h:99
static const int weights[]
Definition hevc_pel.c:32
int av_hwframe_ctx_init(AVBufferRef *ref)
Finalize the context before use.
Definition hwcontext.c:337
AVBufferRef * av_hwframe_ctx_alloc(AVBufferRef *device_ref_in)
Allocate an AVHWFramesContext tied to a given device context.
Definition hwcontext.c:263
int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags)
Allocate a new frame attached to the given AVHWFramesContext.
Definition hwcontext.c:506
FFmpeg internal API for CUDA.
static int unsupported(AVCodecContext *avctx)
Definition iff.c:1461
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition jacosubdec.c:66
#define FILTER_INPUTS(array)
Definition filters.h:264
#define FILTER_OUTPUTS(array)
Definition filters.h:265
#define FF_FILTER_FLAG_HWFRAME_AWARE
The filter is aware of hardware frames, and any hardware frame context should not be automatically pr...
Definition filters.h:208
static FilterLink * ff_filter_link(AVFilterLink *link)
Definition filters.h:199
#define FILTER_SINGLE_PIXFMT(pix_fmt_)
Definition filters.h:254
#define av_cold
Definition attributes.h:117
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
const char * desc
Definition libsvtav1.c:83
@ SWS_FILTER_SIZE_MAX
Definition filters.h:41
@ SWS_FILTER_SCALE
14-bit coefficients are picked to fit comfortably within int16_t for efficient SIMD processing (e....
Definition filters.h:40
static const struct @257111027162314367033347246032313251342043035002 planes[]
uint8_t w
Definition llvidencdsp.c:39
int ff_cuda_load_module(void *avctx, AVCUDADeviceContext *hwctx, CUmodule *cu_module, const unsigned char *data, const unsigned int length)
Loads a CUDA module and applies any decompression, if necessary.
Definition load_helper.c:34
#define FFMAX(a, b)
Definition macros.h:47
#define FFALIGN(x, a)
Definition macros.h:78
Memory handling functions.
AVOptions.
int av_pix_fmt_count_planes(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3500
const char * av_get_pix_fmt_name(enum AVPixelFormat pix_fmt)
Return the short name for a pixel format, NULL in case pix_fmt is unknown.
Definition pixdesc.c:3380
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_0RGB32
Definition pixfmt.h:521
#define AV_PIX_FMT_P212
Definition pixfmt.h:624
#define AV_PIX_FMT_YUV420P10
Definition pixfmt.h:545
@ AVCOL_RANGE_JPEG
Full range content.
Definition pixfmt.h:783
#define AV_PIX_FMT_P210
Definition pixfmt.h:622
#define AV_PIX_FMT_P012
Definition pixfmt.h:609
#define AV_PIX_FMT_P216
Definition pixfmt.h:626
#define AV_PIX_FMT_P010
Definition pixfmt.h:608
#define AV_PIX_FMT_P016
Definition pixfmt.h:610
#define AV_PIX_FMT_YUV422P10
Definition pixfmt.h:546
#define AV_PIX_FMT_YUV444P12MSB
Definition pixfmt.h:561
#define AV_PIX_FMT_BGR32
Definition pixfmt.h:519
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NV12
planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition pixfmt.h:96
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
Definition pixfmt.h:260
@ AV_PIX_FMT_NV16
interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:198
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
#define AV_PIX_FMT_YUV444P16
Definition pixfmt.h:558
#define AV_PIX_FMT_YUV444P10MSB
Definition pixfmt.h:560
#define AV_PIX_FMT_0BGR32
Definition pixfmt.h:522
#define AV_PIX_FMT_RGB32
Definition pixfmt.h:517
#define AV_PIX_FMT_YUV444P10
Definition pixfmt.h:548
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition refstruct.c:120
int ff_scale_adjust_dimensions(AVFilterLink *inlink, int *ret_w, int *ret_h, int force_original_aspect_ratio, int force_divisible_by, double w_adj)
Transform evaluated width and height obtained from ff_scale_eval_dimensions into actual target width ...
Definition scale_eval.c:123
int ff_scale_eval_dimensions(void *log_ctx, const char *w_expr, const char *h_expr, AVFilterLink *inlink, AVFilterLink *outlink, int *ret_w, int *ret_h)
Parse and evaluate string expressions for width and height.
Definition scale_eval.c:58
@ SCALE_FORCE_OAR_DISABLE
Definition scale_eval.h:25
@ SCALE_FORCE_OAR_NB
Definition scale_eval.h:28
@ SCALE_FORCE_OAR_INCREASE
Definition scale_eval.h:27
@ SCALE_FORCE_OAR_DECREASE
Definition scale_eval.h:26
#define FF_ARRAY_ELEMS(a)
#define snprintf
Definition snprintf.h:34
A reference to a data buffer.
Definition buffer.h:82
uint8_t * data
The data buffer.
Definition buffer.h:90
This struct is allocated as AVHWDeviceContext.hwctx.
Describe the class of an AVClass context structure.
Definition log.h:76
An instance of a filter.
Definition avfilter.h:273
AVFilterLink ** inputs
array of pointers to input links
Definition avfilter.h:281
void * priv
private data for use by the filter
Definition avfilter.h:288
A filter pad used for either input or output.
Definition filters.h:40
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
size_t crop_right
Definition frame.h:798
AVRational sample_aspect_ratio
Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
Definition frame.h:569
size_t crop_top
Definition frame.h:795
enum AVColorRange color_range
MPEG vs JPEG YUV range.
Definition frame.h:723
size_t crop_left
Definition frame.h:797
size_t crop_bottom
Definition frame.h:796
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition hwcontext.h:88
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
enum AVPixelFormat format
The pixel format identifying the underlying HW surface type.
Definition hwcontext.h:200
AVBufferRef * device_ref
A reference to the parent AVHWDeviceContext.
Definition hwcontext.h:129
enum AVPixelFormat sw_format
The pixel format identifying the actual data layout of the hardware frames.
Definition hwcontext.h:213
int width
The allocated dimensions of the frames in this pool.
Definition hwcontext.h:220
AVHWDeviceContext * device_ctx
The parent AVHWDeviceContext.
Definition hwcontext.h:137
AVOption.
Definition opt.h:428
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
Rational number (pair of numerator and denominator).
Definition rational.h:58
int num
Numerator.
Definition rational.h:59
int den
Denominator.
Definition rational.h:60
enum AVPixelFormat format
Output sw format.
CUfunction cu_func_tmp
CUfunction cu_func_tmp_uv
const AVPixFmtDescriptor * in_desc
enum AVPixelFormat in_fmt out_fmt
AVCUDADeviceContext * hwctx
CUDAScaleFilterSet filter_set
char * w_expr
width expression string
const AVPixFmtDescriptor * out_desc
CUfunction cu_func_fixed_uv
char * h_expr
height expression string
CUfunction cu_func_out[2]
CUfunction cu_func_out_uv[2]
CUfunction cu_func_fixed
AVBufferRef * frames_ctx
unsigned int tmp_planes
CUDAScaleFilter filters_uv[FILTER_NB]
CUDAScalePassPlan pass_plan
CUDAScaleFilter filters[FILTER_NB]
CUdeviceptr weights
float[dst_size][filter_size]
CUdeviceptr offsets
int[dst_size]
unsigned int y_planes
unsigned int x_planes
int dir[FILTER_NB]
int external_data
CUtexObject tex[4]
CUdeviceptr data[4]
int color_range
int log2_chroma_h
int linesize[4]
int crop_height
int log2_chroma_w
Represents a computed filter kernel.
Definition filters.h:85
enum AVPixelFormat format
char name[13]
#define av_free(p)
#define av_malloc_array(a, b)
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
void(* filter)(uint8_t *src, ptrdiff_t stride, int qscale)
Definition h263dsp.c:29
#define src
Definition vp8dsp.c:248
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
static void set(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f, double v)
Definition swresample.c:55
static enum AVPixelFormat supported_formats[]
#define DIV_UP(a, b)
#define BLOCKX
#define BLOCKY
static int cudascale_filter_set_init(AVFilterContext *ctx, CUDAScaleFilterSet *set, int in_width, int in_height, int normalize_crop)
static double cudascale_plane_virtual_size(int src_size, int in_size, int out_size, int in_sub, int out_sub)
@ CUDA_SCALE_PLANE_PRIMARY
@ CUDA_SCALE_PLANE_CHROMA
static int cudascale_plane_needs_scale(int in_size, int out_size, int in_sub, int out_sub)
static av_cold int cudascale_setup_filters(AVFilterContext *ctx, int in_width, int in_height)
static int cudascale_filter_init(AVFilterContext *ctx, CUDAScaleFilter *f, int src_size, int dst_size, double virtual_size, int needs_scale)
static int cudascale_plane_is_downscaled(int in_size, int out_size, int in_sub, int out_sub)
static av_cold int init_hwframe_ctx(CUDAScaleContext *s, AVBufferRef *device_ctx, int width, int height)
static av_cold void cudascale_uninit(AVFilterContext *ctx)
static int cudascale_frame_geometry(AVFilterContext *ctx, const AVFrame *frame, int *width, int *height, int *normalize_crop)
static int cudascale_scale(AVFilterContext *ctx, AVFrame *out, AVFrame *in, int in_width, int in_height, int normalize_crop)
#define DIV_UP(a, b)
static av_cold int cudascale_config_props(AVFilterLink *outlink)
static int scalecuda_resize(AVFilterContext *ctx, const CUDAScaleFilterSet *set, int pass, const CUDATex *out, const CUDATex *in)
static av_cold int init_processing_chain(AVFilterContext *ctx, int in_width, int in_height, int out_width, int out_height)
static av_cold int cudascale_init(AVFilterContext *ctx)
static const AVClass cudascale_class
static av_cold int cudascale_load_functions(AVFilterContext *ctx)
static const char * get_format_name(enum AVPixelFormat fmt)
static void filter_set_uninit(CudaFunctions *cu, CUDAScaleFilterSet *set)
static int call_resize_kernel(AVFilterContext *ctx, CUfunction func, const CUtexObject src_tex[4], int src_left, int src_top, int src_width, int src_height, const CUdeviceptr out_data[4], int dst_width, int dst_height, int dst_pitch, int mpeg_range, const CUDAScaleFilter *filter)
@ INTERP_ALGO_LANCZOS
@ INTERP_ALGO_BICUBIC
@ INTERP_ALGO_DEFAULT
@ INTERP_ALGO_BILINEAR
@ INTERP_ALGO_COUNT
@ INTERP_ALGO_NEAREST
static void cuda_tex_uninit(CudaFunctions *cu, CUDATex *t)
static int format_is_supported(enum AVPixelFormat fmt)
#define CHECK_CU(x)
static av_cold void set_format_info(AVFilterContext *ctx, enum AVPixelFormat in_format, enum AVPixelFormat out_format)
@ CUDA_SCALE_DIR_Y
@ CUDA_SCALE_DIR_X
@ CUDA_SCALE_DIR_NONE
static AVFrame * cudascale_get_video_buffer(AVFilterLink *inlink, int w, int h)
static void cudascale_plan_passes(CUDAScalePassPlan *plan, int in_width, int in_height, int out_width, int out_height, int in_sub_x, int in_sub_y, int out_sub_x, int out_sub_y, int has_chroma)
static int cudascale_prepare_filter_set(AVFilterContext *ctx, int in_width, int in_height, int normalize_crop)
static int cuda_tex_map_frame(AVFilterContext *ctx, const AVFrame *frame, const int depths[4], const int channels[4], CUDATex *tex, int use_linear, int as_integer)
#define OFFSET(x)
static void filter_uninit(CudaFunctions *cu, CUDAScaleFilter *filter)
static int cudascale_filter_frame(AVFilterLink *link, AVFrame *in)
static const AVFilterPad cudascale_outputs[]
static int inter_buf_init(AVFilterContext *ctx, CUDATex *tex, int out_width, int in_height, int use_float, unsigned int planes)
static const AVFilterPad cudascale_inputs[]
@ FILTER_NB
@ FILTER_OUT
@ FILTER_TMP
#define SCALE_CUDA_PARAM_DEFAULT
AVFrame * ff_null_get_video_buffer(AVFilterLink *link, int w, int h)
Definition video.c:44
AVFrame * ff_default_get_video_buffer(AVFilterLink *link, int w, int h)
Definition video.c:84