FFmpeg
Loading...
Searching...
No Matches
vf_colorspace_cuda.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022, 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 <string.h>
24
26#include "libavutil/common.h"
28#include "libavutil/hwcontext.h"
30#include "libavutil/internal.h"
31#include "libavutil/opt.h"
32#include "libavutil/pixdesc.h"
33
34#include "avfilter.h"
35#include "filters.h"
36
37#include "cuda/load_helper.h"
38
44
45#define DIV_UP(a, b) (((a) + (b)-1) / (b))
46#define BLOCKX 32
47#define BLOCKY 16
48
49#define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, s->hwctx->internal->cuda_dl, x)
50
70
72{
74
75 s->own_frame = av_frame_alloc();
76 if (!s->own_frame)
77 return AVERROR(ENOMEM);
78
79 s->tmp_frame = av_frame_alloc();
80 if (!s->tmp_frame)
81 return AVERROR(ENOMEM);
82
83 return 0;
84}
85
87{
89
90 if (s->hwctx && s->cu_module) {
91 CudaFunctions* cu = s->hwctx->internal->cuda_dl;
92 CUcontext dummy;
93
94 CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx));
95 CHECK_CU(cu->cuModuleUnload(s->cu_module));
96 s->cu_module = NULL;
97 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
98 }
99
100 av_frame_free(&s->own_frame);
101 av_buffer_unref(&s->frames_ctx);
102 av_frame_free(&s->tmp_frame);
103}
104
106 int width, int height)
107{
108 AVBufferRef* out_ref = NULL;
109 AVHWFramesContext* out_ctx;
110 int ret;
111
112 out_ref = av_hwframe_ctx_alloc(device_ctx);
113 if (!out_ref)
114 return AVERROR(ENOMEM);
115
116 out_ctx = (AVHWFramesContext*)out_ref->data;
117
118 out_ctx->format = AV_PIX_FMT_CUDA;
119 out_ctx->sw_format = s->pix_fmt;
120 out_ctx->width = FFALIGN(width, 32);
121 out_ctx->height = FFALIGN(height, 32);
122
123 ret = av_hwframe_ctx_init(out_ref);
124 if (ret < 0)
125 goto fail;
126
127 av_frame_unref(s->own_frame);
128 ret = av_hwframe_get_buffer(out_ref, s->own_frame, 0);
129 if (ret < 0)
130 goto fail;
131
132 s->own_frame->width = width;
133 s->own_frame->height = height;
134
135 av_buffer_unref(&s->frames_ctx);
136 s->frames_ctx = out_ref;
137
138 return 0;
139fail:
140 av_buffer_unref(&out_ref);
141 return ret;
142}
143
145{
146 for (int i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
147 if (fmt == supported_formats[i])
148 return 1;
149
150 return 0;
151}
152
154 int height)
155{
156 FilterLink *inl = ff_filter_link(ctx->inputs[0]);
157 FilterLink *outl = ff_filter_link(ctx->outputs[0]);
158 CUDAColorspaceContext* s = ctx->priv;
159 AVHWFramesContext* in_frames_ctx;
160
161 int ret;
162
163 if (!inl->hw_frames_ctx) {
164 av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n");
165 return AVERROR(EINVAL);
166 }
167
168 in_frames_ctx = (AVHWFramesContext*)inl->hw_frames_ctx->data;
169 s->pix_fmt = in_frames_ctx->sw_format;
170
171 if (!format_is_supported(s->pix_fmt)) {
172 av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n",
173 av_get_pix_fmt_name(s->pix_fmt));
174 return AVERROR(EINVAL);
175 }
176
177 if ((AVCOL_RANGE_MPEG != s->range) && (AVCOL_RANGE_JPEG != s->range)) {
178 av_log(ctx, AV_LOG_ERROR, "Unsupported color range\n");
179 return AVERROR(EINVAL);
180 }
181
182 s->num_planes = av_pix_fmt_count_planes(s->pix_fmt);
183
184 ret = init_hwframe_ctx(s, in_frames_ctx->device_ref, width, height);
185 if (ret < 0)
186 return ret;
187
188 outl->hw_frames_ctx = av_buffer_ref(s->frames_ctx);
189 if (!outl->hw_frames_ctx)
190 return AVERROR(ENOMEM);
191
192 return 0;
193}
194
196{
197 CUDAColorspaceContext* s = ctx->priv;
198 CUcontext dummy, cuda_ctx = s->hwctx->cuda_ctx;
199 CudaFunctions* cu = s->hwctx->internal->cuda_dl;
200 int ret;
201
202 extern const unsigned char ff_vf_colorspace_cuda_ptx_data[];
203 extern const unsigned int ff_vf_colorspace_cuda_ptx_len;
204
205 ret = CHECK_CU(cu->cuCtxPushCurrent(cuda_ctx));
206 if (ret < 0)
207 return ret;
208
209 ret = ff_cuda_load_module(ctx, s->hwctx, &s->cu_module,
210 ff_vf_colorspace_cuda_ptx_data,
211 ff_vf_colorspace_cuda_ptx_len);
212 if (ret < 0)
213 goto fail;
214
215 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_convert[AVCOL_RANGE_MPEG], s->cu_module, "to_mpeg_cuda"));
216 if (ret < 0)
217 goto fail;
218
219 ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_convert[AVCOL_RANGE_JPEG], s->cu_module, "to_jpeg_cuda"));
220 if (ret < 0)
221 goto fail;
222
223fail:
224 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
225 return ret;
226}
227
229{
230 AVFilterContext* ctx = outlink->src;
231 AVFilterLink* inlink = outlink->src->inputs[0];
232 FilterLink *inl = ff_filter_link(inlink);
233 CUDAColorspaceContext* s = ctx->priv;
234 AVHWFramesContext* frames_ctx;
235 AVCUDADeviceContext* device_hwctx;
236 int ret;
237
238 outlink->w = inlink->w;
239 outlink->h = inlink->h;
240
241 ret = init_processing_chain(ctx, inlink->w, inlink->h);
242 if (ret < 0)
243 return ret;
244
245 frames_ctx = (AVHWFramesContext*)inl->hw_frames_ctx->data;
246 device_hwctx = frames_ctx->device_ctx->hwctx;
247
248 s->hwctx = device_hwctx;
249 s->cu_stream = s->hwctx->stream;
250
251 if (inlink->sample_aspect_ratio.num) {
252 outlink->sample_aspect_ratio = av_mul_q(
253 (AVRational){outlink->h * inlink->w, outlink->w * inlink->h},
254 inlink->sample_aspect_ratio);
255 } else {
256 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
257 }
258
260 if (ret < 0)
261 return ret;
262
263 return ret;
264}
265
267{
268 CUDAColorspaceContext* s = ctx->priv;
269 CudaFunctions* cu = s->hwctx->internal->cuda_dl;
270 CUcontext dummy, cuda_ctx = s->hwctx->cuda_ctx;
271 int ret;
272
273 ret = CHECK_CU(cu->cuCtxPushCurrent(cuda_ctx));
274 if (ret < 0)
275 return ret;
276
277 out->color_range = s->range;
278
279 for (int i = 0; i < s->num_planes; i++) {
280 int width = in->width, height = in->height, comp_id = (i > 0);
281
282 switch (s->pix_fmt) {
284 break;
286 width = comp_id ? in->width / 2 : in->width;
288 case AV_PIX_FMT_NV12:
289 height = comp_id ? in->height / 2 : in->height;
290 break;
291 default:
292 av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n",
293 av_get_pix_fmt_name(s->pix_fmt));
294 return AVERROR(EINVAL);
295 }
296
297 if (!s->cu_convert[out->color_range]) {
298 av_log(ctx, AV_LOG_ERROR, "Unsupported color range\n");
299 return AVERROR(EINVAL);
300 }
301
302 if (in->color_range != out->color_range) {
303 void* args[] = {&in->data[i], &out->data[i], &in->linesize[i],
304 &comp_id};
305 ret = CHECK_CU(cu->cuLaunchKernel(
306 s->cu_convert[out->color_range], DIV_UP(width, BLOCKX),
307 DIV_UP(height, BLOCKY), 1, BLOCKX, BLOCKY, 1, 0, s->cu_stream,
308 args, NULL));
309 } else {
310 ret = av_hwframe_transfer_data(out, in, 0);
311 if (ret < 0)
312 return ret;
313 }
314 }
315
316 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
317 return ret;
318}
319
321{
322 CUDAColorspaceContext* s = ctx->priv;
323 AVFilterLink* outlink = ctx->outputs[0];
324 AVFrame* src = in;
325 int ret;
326
327 ret = conv_cuda_convert(ctx, s->own_frame, src);
328 if (ret < 0)
329 return ret;
330
331 src = s->own_frame;
332 ret = av_hwframe_get_buffer(src->hw_frames_ctx, s->tmp_frame, 0);
333 if (ret < 0)
334 return ret;
335
336 av_frame_move_ref(out, s->own_frame);
337 av_frame_move_ref(s->own_frame, s->tmp_frame);
338
339 s->own_frame->width = outlink->w;
340 s->own_frame->height = outlink->h;
341
342 ret = av_frame_copy_props(out, in);
343 if (ret < 0)
344 return ret;
345
346 return 0;
347}
348
350{
351 AVFilterContext* ctx = link->dst;
352 CUDAColorspaceContext* s = ctx->priv;
353 AVFilterLink* outlink = ctx->outputs[0];
354 CudaFunctions* cu = s->hwctx->internal->cuda_dl;
355
356 AVFrame* out = NULL;
357 CUcontext dummy;
358 int ret = 0;
359
361 if (!out) {
362 ret = AVERROR(ENOMEM);
363 goto fail;
364 }
365
366 ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx));
367 if (ret < 0)
368 goto fail;
369
370 ret = cudacolorspace_conv(ctx, out, in);
371
372 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
373 if (ret < 0)
374 goto fail;
375
376 av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den,
377 (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w,
378 (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h,
379 INT_MAX);
380
381 av_frame_free(&in);
382 return ff_filter_frame(outlink, out);
383fail:
384 av_frame_free(&in);
386 return ret;
387}
388
389#define OFFSET(x) offsetof(CUDAColorspaceContext, x)
390#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
391static const AVOption options[] = {
392 {"range", "Output video range", OFFSET(range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, .unit = "range"},
393 {"tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range"},
394 {"mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range"},
395 {"pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range"},
396 {"jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range"},
397 {NULL},
398};
399
401 .class_name = "colorspace_cuda",
402 .item_name = av_default_item_name,
403 .option = options,
404 .version = LIBAVUTIL_VERSION_INT,
405};
406
408 {
409 .name = "default",
410 .type = AVMEDIA_TYPE_VIDEO,
411 .filter_frame = cudacolorspace_filter_frame,
412 },
413};
414
416 {
417 .name = "default",
418 .type = AVMEDIA_TYPE_VIDEO,
419 .config_props = cudacolorspace_config_props,
420 },
421};
422
424 .p.name = "colorspace_cuda",
425 .p.description = NULL_IF_CONFIG_SMALL("CUDA accelerated video color converter"),
426
427 .p.priv_class = &cudacolorspace_class,
428
429 .init = cudacolorspace_init,
430 .uninit = cudacolorspace_uninit,
431
432 .priv_size = sizeof(CUDAColorspaceContext),
433
436
438
439 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
440};
const FFFilter ff_vf_colorspace_cuda
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 s(width, name)
Definition cbs_vp9.c:198
#define FLAGS
Definition cmdutils.c:598
common internal and external API header
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static int dummy
Definition ffplay.c:3754
#define fail
Definition test.h:479
@ AV_OPT_TYPE_CONST
Special option type for declaring named constants.
Definition opt.h:298
@ AV_OPT_TYPE_INT
Underlying C type is int.
Definition opt.h:258
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(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_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
#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
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_transfer_data(AVFrame *dst, const AVFrame *src, int flags)
Copy data to or from a hw surface.
Definition hwcontext.c:448
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.
#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
Macro definitions for various function/variable attributes.
#define av_fallthrough
Definition attributes.h:67
#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
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 FFALIGN(x, a)
Definition macros.h:78
enum AVColorRange range
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
@ AVCOL_RANGE_MPEG
Narrow or limited range content.
Definition pixfmt.h:766
@ AVCOL_RANGE_UNSPECIFIED
Definition pixfmt.h:749
@ AVCOL_RANGE_NB
Not part of ABI.
Definition pixfmt.h:784
@ AVCOL_RANGE_JPEG
Full range content.
Definition pixfmt.h:783
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_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
Definition pixfmt.h:260
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
#define FF_ARRAY_ELEMS(a)
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
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
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition frame.h:493
int width
Definition frame.h:544
int height
Definition frame.h:544
AVRational sample_aspect_ratio
Sample aspect ratio for the video frame, 0/1 if unknown/unspecified.
Definition frame.h:569
enum AVColorRange color_range
MPEG vs JPEG YUV range.
Definition frame.h:723
int linesize[AV_NUM_DATA_POINTERS]
For video, a positive or negative value, which is typically indicating the size in bytes of each pict...
Definition frame.h:517
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
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
AVCUDADeviceContext * hwctx
enum AVPixelFormat pix_fmt
CUfunction cu_convert[AVCOL_RANGE_NB]
#define av_log(a,...)
#define src
Definition vp8dsp.c:248
static FILE * out
Definition movenc.c:55
static AVFormatContext * ctx
Definition movenc.c:49
#define height
Definition dsp.h:89
#define width
Definition dsp.h:89
static enum AVPixelFormat supported_formats[]
#define DIV_UP(a, b)
#define BLOCKX
#define BLOCKY
static int conv_cuda_convert(AVFilterContext *ctx, AVFrame *out, AVFrame *in)
static av_cold int init_hwframe_ctx(CUDAColorspaceContext *s, AVBufferRef *device_ctx, int width, int height)
static av_cold int cudacolorspace_config_props(AVFilterLink *outlink)
#define DIV_UP(a, b)
static const AVFilterPad cudacolorspace_inputs[]
static const AVClass cudacolorspace_class
static av_cold int cudacolorspace_load_functions(AVFilterContext *ctx)
static const AVFilterPad cudacolorspace_outputs[]
static int format_is_supported(enum AVPixelFormat fmt)
static av_cold int cudacolorspace_init(AVFilterContext *ctx)
#define CHECK_CU(x)
static av_cold void cudacolorspace_uninit(AVFilterContext *ctx)
static int cudacolorspace_filter_frame(AVFilterLink *link, AVFrame *in)
#define OFFSET(x)
static int cudacolorspace_conv(AVFilterContext *ctx, AVFrame *out, AVFrame *in)
static av_cold int init_processing_chain(AVFilterContext *ctx, int width, int height)