]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_hwupload_cuda.c
avfilter: Constify all AVFilters
[ffmpeg] / libavfilter / vf_hwupload_cuda.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "libavutil/buffer.h"
20 #include "libavutil/hwcontext.h"
21 #include "libavutil/log.h"
22 #include "libavutil/opt.h"
23
24 #include "avfilter.h"
25 #include "formats.h"
26 #include "internal.h"
27 #include "video.h"
28
29 typedef struct CudaUploadContext {
30     const AVClass *class;
31     int device_idx;
32
33     AVBufferRef *hwdevice;
34     AVBufferRef *hwframe;
35 } CudaUploadContext;
36
37 static av_cold int cudaupload_init(AVFilterContext *ctx)
38 {
39     CudaUploadContext *s = ctx->priv;
40     char buf[64] = { 0 };
41
42     snprintf(buf, sizeof(buf), "%d", s->device_idx);
43
44     return av_hwdevice_ctx_create(&s->hwdevice, AV_HWDEVICE_TYPE_CUDA, buf, NULL, 0);
45 }
46
47 static av_cold void cudaupload_uninit(AVFilterContext *ctx)
48 {
49     CudaUploadContext *s = ctx->priv;
50
51     av_buffer_unref(&s->hwframe);
52     av_buffer_unref(&s->hwdevice);
53 }
54
55 static int cudaupload_query_formats(AVFilterContext *ctx)
56 {
57     int ret;
58
59     static const enum AVPixelFormat input_pix_fmts[] = {
60         AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV444P,
61         AV_PIX_FMT_P010, AV_PIX_FMT_P016, AV_PIX_FMT_YUV444P16,
62         AV_PIX_FMT_0RGB32, AV_PIX_FMT_0BGR32,
63 #if CONFIG_VULKAN
64         AV_PIX_FMT_VULKAN,
65 #endif
66         AV_PIX_FMT_NONE,
67     };
68     static const enum AVPixelFormat output_pix_fmts[] = {
69         AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE,
70     };
71     AVFilterFormats *in_fmts  = ff_make_format_list(input_pix_fmts);
72     AVFilterFormats *out_fmts;
73
74     ret = ff_formats_ref(in_fmts, &ctx->inputs[0]->outcfg.formats);
75     if (ret < 0)
76         return ret;
77
78     out_fmts = ff_make_format_list(output_pix_fmts);
79
80     ret = ff_formats_ref(out_fmts, &ctx->outputs[0]->incfg.formats);
81     if (ret < 0)
82         return ret;
83
84     return 0;
85 }
86
87 static int cudaupload_config_output(AVFilterLink *outlink)
88 {
89     AVFilterContext *ctx = outlink->src;
90     AVFilterLink *inlink = ctx->inputs[0];
91     CudaUploadContext *s = ctx->priv;
92
93     AVHWFramesContext *hwframe_ctx;
94     int ret;
95
96     av_buffer_unref(&s->hwframe);
97     s->hwframe = av_hwframe_ctx_alloc(s->hwdevice);
98     if (!s->hwframe)
99         return AVERROR(ENOMEM);
100
101     hwframe_ctx            = (AVHWFramesContext*)s->hwframe->data;
102     hwframe_ctx->format    = AV_PIX_FMT_CUDA;
103     if (inlink->hw_frames_ctx) {
104         AVHWFramesContext *in_hwframe_ctx = (AVHWFramesContext*)inlink->hw_frames_ctx->data;
105         hwframe_ctx->sw_format = in_hwframe_ctx->sw_format;
106     } else {
107         hwframe_ctx->sw_format = inlink->format;
108     }
109     hwframe_ctx->width     = inlink->w;
110     hwframe_ctx->height    = inlink->h;
111
112     ret = av_hwframe_ctx_init(s->hwframe);
113     if (ret < 0)
114         return ret;
115
116     outlink->hw_frames_ctx = av_buffer_ref(s->hwframe);
117     if (!outlink->hw_frames_ctx)
118         return AVERROR(ENOMEM);
119
120     return 0;
121 }
122
123 static int cudaupload_filter_frame(AVFilterLink *link, AVFrame *in)
124 {
125     AVFilterContext   *ctx = link->dst;
126     AVFilterLink  *outlink = ctx->outputs[0];
127
128     AVFrame *out = NULL;
129     int ret;
130
131     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
132     if (!out) {
133         ret = AVERROR(ENOMEM);
134         goto fail;
135     }
136
137     out->width  = in->width;
138     out->height = in->height;
139
140     ret = av_hwframe_transfer_data(out, in, 0);
141     if (ret < 0) {
142         av_log(ctx, AV_LOG_ERROR, "Error transferring data to the GPU\n");
143         goto fail;
144     }
145
146     ret = av_frame_copy_props(out, in);
147     if (ret < 0)
148         goto fail;
149
150     av_frame_free(&in);
151
152     return ff_filter_frame(ctx->outputs[0], out);
153 fail:
154     av_frame_free(&in);
155     av_frame_free(&out);
156     return ret;
157 }
158
159 #define OFFSET(x) offsetof(CudaUploadContext, x)
160 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
161 static const AVOption cudaupload_options[] = {
162     { "device", "Number of the device to use", OFFSET(device_idx), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },
163     { NULL },
164 };
165
166 AVFILTER_DEFINE_CLASS(cudaupload);
167
168 static const AVFilterPad cudaupload_inputs[] = {
169     {
170         .name         = "default",
171         .type         = AVMEDIA_TYPE_VIDEO,
172         .filter_frame = cudaupload_filter_frame,
173     },
174     { NULL }
175 };
176
177 static const AVFilterPad cudaupload_outputs[] = {
178     {
179         .name         = "default",
180         .type         = AVMEDIA_TYPE_VIDEO,
181         .config_props = cudaupload_config_output,
182     },
183     { NULL }
184 };
185
186 const AVFilter ff_vf_hwupload_cuda = {
187     .name        = "hwupload_cuda",
188     .description = NULL_IF_CONFIG_SMALL("Upload a system memory frame to a CUDA device."),
189
190     .init      = cudaupload_init,
191     .uninit    = cudaupload_uninit,
192
193     .query_formats = cudaupload_query_formats,
194
195     .priv_size  = sizeof(CudaUploadContext),
196     .priv_class = &cudaupload_class,
197
198     .inputs    = cudaupload_inputs,
199     .outputs   = cudaupload_outputs,
200
201     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
202 };