blob: ef98233d1260218e2e6e3d24ded41a0fd9e358d2
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_YUV444P, |
61 | AV_PIX_FMT_NONE, |
62 | }; |
63 | static const enum AVPixelFormat output_pix_fmts[] = { |
64 | AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE, |
65 | }; |
66 | AVFilterFormats *in_fmts = ff_make_format_list(input_pix_fmts); |
67 | AVFilterFormats *out_fmts; |
68 | |
69 | ret = ff_formats_ref(in_fmts, &ctx->inputs[0]->out_formats); |
70 | if (ret < 0) |
71 | return ret; |
72 | |
73 | out_fmts = ff_make_format_list(output_pix_fmts); |
74 | |
75 | ret = ff_formats_ref(out_fmts, &ctx->outputs[0]->in_formats); |
76 | if (ret < 0) |
77 | return ret; |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | static int cudaupload_config_output(AVFilterLink *outlink) |
83 | { |
84 | AVFilterContext *ctx = outlink->src; |
85 | AVFilterLink *inlink = ctx->inputs[0]; |
86 | CudaUploadContext *s = ctx->priv; |
87 | |
88 | AVHWFramesContext *hwframe_ctx; |
89 | int ret; |
90 | |
91 | av_buffer_unref(&s->hwframe); |
92 | s->hwframe = av_hwframe_ctx_alloc(s->hwdevice); |
93 | if (!s->hwframe) |
94 | return AVERROR(ENOMEM); |
95 | |
96 | hwframe_ctx = (AVHWFramesContext*)s->hwframe->data; |
97 | hwframe_ctx->format = AV_PIX_FMT_CUDA; |
98 | hwframe_ctx->sw_format = inlink->format; |
99 | hwframe_ctx->width = inlink->w; |
100 | hwframe_ctx->height = inlink->h; |
101 | |
102 | ret = av_hwframe_ctx_init(s->hwframe); |
103 | if (ret < 0) |
104 | return ret; |
105 | |
106 | outlink->hw_frames_ctx = av_buffer_ref(s->hwframe); |
107 | if (!outlink->hw_frames_ctx) |
108 | return AVERROR(ENOMEM); |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | static int cudaupload_filter_frame(AVFilterLink *link, AVFrame *in) |
114 | { |
115 | AVFilterContext *ctx = link->dst; |
116 | AVFilterLink *outlink = ctx->outputs[0]; |
117 | |
118 | AVFrame *out = NULL; |
119 | int ret; |
120 | |
121 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
122 | if (!out) { |
123 | ret = AVERROR(ENOMEM); |
124 | goto fail; |
125 | } |
126 | |
127 | out->width = in->width; |
128 | out->height = in->height; |
129 | |
130 | ret = av_hwframe_transfer_data(out, in, 0); |
131 | if (ret < 0) { |
132 | av_log(ctx, AV_LOG_ERROR, "Error transferring data to the GPU\n"); |
133 | goto fail; |
134 | } |
135 | |
136 | ret = av_frame_copy_props(out, in); |
137 | if (ret < 0) |
138 | goto fail; |
139 | |
140 | av_frame_free(&in); |
141 | |
142 | return ff_filter_frame(ctx->outputs[0], out); |
143 | fail: |
144 | av_frame_free(&in); |
145 | av_frame_free(&out); |
146 | return ret; |
147 | } |
148 | |
149 | #define OFFSET(x) offsetof(CudaUploadContext, x) |
150 | #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) |
151 | static const AVOption cudaupload_options[] = { |
152 | { "device", "Number of the device to use", OFFSET(device_idx), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, |
153 | { NULL }, |
154 | }; |
155 | |
156 | AVFILTER_DEFINE_CLASS(cudaupload); |
157 | |
158 | static const AVFilterPad cudaupload_inputs[] = { |
159 | { |
160 | .name = "default", |
161 | .type = AVMEDIA_TYPE_VIDEO, |
162 | .filter_frame = cudaupload_filter_frame, |
163 | }, |
164 | { NULL } |
165 | }; |
166 | |
167 | static const AVFilterPad cudaupload_outputs[] = { |
168 | { |
169 | .name = "default", |
170 | .type = AVMEDIA_TYPE_VIDEO, |
171 | .config_props = cudaupload_config_output, |
172 | }, |
173 | { NULL } |
174 | }; |
175 | |
176 | AVFilter ff_vf_hwupload_cuda = { |
177 | .name = "hwupload_cuda", |
178 | .description = NULL_IF_CONFIG_SMALL("Upload a system memory frame to a CUDA device."), |
179 | |
180 | .init = cudaupload_init, |
181 | .uninit = cudaupload_uninit, |
182 | |
183 | .query_formats = cudaupload_query_formats, |
184 | |
185 | .priv_size = sizeof(CudaUploadContext), |
186 | .priv_class = &cudaupload_class, |
187 | |
188 | .inputs = cudaupload_inputs, |
189 | .outputs = cudaupload_outputs, |
190 | |
191 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, |
192 | }; |
193 |