blob: 157686b7b3b81f9b14988b9f83e0158be9b62f95
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/hwcontext_internal.h" |
22 | #include "libavutil/log.h" |
23 | #include "libavutil/pixdesc.h" |
24 | #include "libavutil/opt.h" |
25 | |
26 | #include "avfilter.h" |
27 | #include "formats.h" |
28 | #include "internal.h" |
29 | #include "video.h" |
30 | |
31 | typedef struct HWUploadContext { |
32 | const AVClass *class; |
33 | |
34 | AVBufferRef *hwdevice_ref; |
35 | AVHWDeviceContext *hwdevice; |
36 | |
37 | AVBufferRef *hwframes_ref; |
38 | AVHWFramesContext *hwframes; |
39 | } HWUploadContext; |
40 | |
41 | static int hwupload_query_formats(AVFilterContext *avctx) |
42 | { |
43 | HWUploadContext *ctx = avctx->priv; |
44 | AVHWFramesConstraints *constraints = NULL; |
45 | const enum AVPixelFormat *input_pix_fmts, *output_pix_fmts; |
46 | AVFilterFormats *input_formats = NULL; |
47 | int err, i; |
48 | |
49 | if (!avctx->hw_device_ctx) { |
50 | av_log(ctx, AV_LOG_ERROR, "A hardware device reference is required " |
51 | "to upload frames to.\n"); |
52 | return AVERROR(EINVAL); |
53 | } |
54 | |
55 | ctx->hwdevice_ref = av_buffer_ref(avctx->hw_device_ctx); |
56 | if (!ctx->hwdevice_ref) |
57 | return AVERROR(ENOMEM); |
58 | ctx->hwdevice = (AVHWDeviceContext*)ctx->hwdevice_ref->data; |
59 | |
60 | constraints = av_hwdevice_get_hwframe_constraints(ctx->hwdevice_ref, NULL); |
61 | if (!constraints) { |
62 | err = AVERROR(EINVAL); |
63 | goto fail; |
64 | } |
65 | |
66 | input_pix_fmts = constraints->valid_sw_formats; |
67 | output_pix_fmts = constraints->valid_hw_formats; |
68 | |
69 | input_formats = ff_make_format_list(output_pix_fmts); |
70 | if (!input_formats) { |
71 | err = AVERROR(ENOMEM); |
72 | goto fail; |
73 | } |
74 | if (input_pix_fmts) { |
75 | for (i = 0; input_pix_fmts[i] != AV_PIX_FMT_NONE; i++) { |
76 | err = ff_add_format(&input_formats, input_pix_fmts[i]); |
77 | if (err < 0) |
78 | goto fail; |
79 | } |
80 | } |
81 | |
82 | if ((err = ff_formats_ref(input_formats, &avctx->inputs[0]->out_formats)) < 0 || |
83 | (err = ff_formats_ref(ff_make_format_list(output_pix_fmts), |
84 | &avctx->outputs[0]->in_formats)) < 0) |
85 | goto fail; |
86 | |
87 | av_hwframe_constraints_free(&constraints); |
88 | return 0; |
89 | |
90 | fail: |
91 | av_buffer_unref(&ctx->hwdevice_ref); |
92 | av_hwframe_constraints_free(&constraints); |
93 | return err; |
94 | } |
95 | |
96 | static int hwupload_config_output(AVFilterLink *outlink) |
97 | { |
98 | AVFilterContext *avctx = outlink->src; |
99 | AVFilterLink *inlink = avctx->inputs[0]; |
100 | HWUploadContext *ctx = avctx->priv; |
101 | int err; |
102 | |
103 | av_buffer_unref(&ctx->hwframes_ref); |
104 | |
105 | if (inlink->format == outlink->format) { |
106 | // The input is already a hardware format, so we just want to |
107 | // pass through the input frames in their own hardware context. |
108 | if (!inlink->hw_frames_ctx) { |
109 | av_log(ctx, AV_LOG_ERROR, "No input hwframe context.\n"); |
110 | return AVERROR(EINVAL); |
111 | } |
112 | |
113 | outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); |
114 | if (!outlink->hw_frames_ctx) |
115 | return AVERROR(ENOMEM); |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | ctx->hwframes_ref = av_hwframe_ctx_alloc(ctx->hwdevice_ref); |
121 | if (!ctx->hwframes_ref) |
122 | return AVERROR(ENOMEM); |
123 | |
124 | ctx->hwframes = (AVHWFramesContext*)ctx->hwframes_ref->data; |
125 | |
126 | av_log(ctx, AV_LOG_DEBUG, "Surface format is %s.\n", |
127 | av_get_pix_fmt_name(inlink->format)); |
128 | |
129 | ctx->hwframes->format = outlink->format; |
130 | ctx->hwframes->sw_format = inlink->format; |
131 | ctx->hwframes->width = inlink->w; |
132 | ctx->hwframes->height = inlink->h; |
133 | |
134 | err = av_hwframe_ctx_init(ctx->hwframes_ref); |
135 | if (err < 0) |
136 | goto fail; |
137 | |
138 | outlink->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref); |
139 | if (!outlink->hw_frames_ctx) { |
140 | err = AVERROR(ENOMEM); |
141 | goto fail; |
142 | } |
143 | |
144 | return 0; |
145 | |
146 | fail: |
147 | av_buffer_unref(&ctx->hwframes_ref); |
148 | return err; |
149 | } |
150 | |
151 | static int hwupload_filter_frame(AVFilterLink *link, AVFrame *input) |
152 | { |
153 | AVFilterContext *avctx = link->dst; |
154 | AVFilterLink *outlink = avctx->outputs[0]; |
155 | HWUploadContext *ctx = avctx->priv; |
156 | AVFrame *output = NULL; |
157 | int err; |
158 | |
159 | if (input->format == outlink->format) |
160 | return ff_filter_frame(outlink, input); |
161 | |
162 | output = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
163 | if (!output) { |
164 | av_log(ctx, AV_LOG_ERROR, "Failed to allocate frame to upload to.\n"); |
165 | err = AVERROR(ENOMEM); |
166 | goto fail; |
167 | } |
168 | |
169 | output->width = input->width; |
170 | output->height = input->height; |
171 | |
172 | err = av_hwframe_transfer_data(output, input, 0); |
173 | if (err < 0) { |
174 | av_log(ctx, AV_LOG_ERROR, "Failed to upload frame: %d.\n", err); |
175 | goto fail; |
176 | } |
177 | |
178 | err = av_frame_copy_props(output, input); |
179 | if (err < 0) |
180 | goto fail; |
181 | |
182 | av_frame_free(&input); |
183 | |
184 | return ff_filter_frame(outlink, output); |
185 | |
186 | fail: |
187 | av_frame_free(&input); |
188 | av_frame_free(&output); |
189 | return err; |
190 | } |
191 | |
192 | static av_cold void hwupload_uninit(AVFilterContext *avctx) |
193 | { |
194 | HWUploadContext *ctx = avctx->priv; |
195 | |
196 | av_buffer_unref(&ctx->hwframes_ref); |
197 | av_buffer_unref(&ctx->hwdevice_ref); |
198 | } |
199 | |
200 | static const AVClass hwupload_class = { |
201 | .class_name = "hwupload", |
202 | .item_name = av_default_item_name, |
203 | .option = NULL, |
204 | .version = LIBAVUTIL_VERSION_INT, |
205 | }; |
206 | |
207 | static const AVFilterPad hwupload_inputs[] = { |
208 | { |
209 | .name = "default", |
210 | .type = AVMEDIA_TYPE_VIDEO, |
211 | .filter_frame = hwupload_filter_frame, |
212 | }, |
213 | { NULL } |
214 | }; |
215 | |
216 | static const AVFilterPad hwupload_outputs[] = { |
217 | { |
218 | .name = "default", |
219 | .type = AVMEDIA_TYPE_VIDEO, |
220 | .config_props = hwupload_config_output, |
221 | }, |
222 | { NULL } |
223 | }; |
224 | |
225 | AVFilter ff_vf_hwupload = { |
226 | .name = "hwupload", |
227 | .description = NULL_IF_CONFIG_SMALL("Upload a normal frame to a hardware frame"), |
228 | .uninit = hwupload_uninit, |
229 | .query_formats = hwupload_query_formats, |
230 | .priv_size = sizeof(HWUploadContext), |
231 | .priv_class = &hwupload_class, |
232 | .inputs = hwupload_inputs, |
233 | .outputs = hwupload_outputs, |
234 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, |
235 | }; |
236 |