blob: 33af30cf4025e70de5d5a8b7a5c70acf1079ef8d
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/mem.h" |
23 | #include "libavutil/opt.h" |
24 | #include "libavutil/pixdesc.h" |
25 | |
26 | #include "avfilter.h" |
27 | #include "formats.h" |
28 | #include "internal.h" |
29 | #include "video.h" |
30 | |
31 | typedef struct HWDownloadContext { |
32 | const AVClass *class; |
33 | |
34 | AVBufferRef *hwframes_ref; |
35 | AVHWFramesContext *hwframes; |
36 | } HWDownloadContext; |
37 | |
38 | static int hwdownload_query_formats(AVFilterContext *avctx) |
39 | { |
40 | AVFilterFormats *infmts = NULL; |
41 | AVFilterFormats *outfmts = NULL; |
42 | const AVPixFmtDescriptor *desc; |
43 | int err; |
44 | |
45 | for (desc = av_pix_fmt_desc_next(NULL); desc; |
46 | desc = av_pix_fmt_desc_next(desc)) { |
47 | if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) |
48 | err = ff_add_format(&infmts, av_pix_fmt_desc_get_id(desc)); |
49 | else |
50 | err = ff_add_format(&outfmts, av_pix_fmt_desc_get_id(desc)); |
51 | if (err) { |
52 | ff_formats_unref(&infmts); |
53 | ff_formats_unref(&outfmts); |
54 | return err; |
55 | } |
56 | } |
57 | |
58 | if ((err = ff_formats_ref(infmts, &avctx->inputs[0]->out_formats)) < 0 || |
59 | (err = ff_formats_ref(outfmts, &avctx->outputs[0]->in_formats)) < 0) |
60 | return err; |
61 | |
62 | return 0; |
63 | } |
64 | |
65 | static int hwdownload_config_input(AVFilterLink *inlink) |
66 | { |
67 | AVFilterContext *avctx = inlink->dst; |
68 | HWDownloadContext *ctx = avctx->priv; |
69 | |
70 | av_buffer_unref(&ctx->hwframes_ref); |
71 | |
72 | if (!inlink->hw_frames_ctx) { |
73 | av_log(ctx, AV_LOG_ERROR, "The input must have a hardware frame " |
74 | "reference.\n"); |
75 | return AVERROR(EINVAL); |
76 | } |
77 | |
78 | ctx->hwframes_ref = av_buffer_ref(inlink->hw_frames_ctx); |
79 | if (!ctx->hwframes_ref) |
80 | return AVERROR(ENOMEM); |
81 | |
82 | ctx->hwframes = (AVHWFramesContext*)ctx->hwframes_ref->data; |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static int hwdownload_config_output(AVFilterLink *outlink) |
88 | { |
89 | AVFilterContext *avctx = outlink->src; |
90 | AVFilterLink *inlink = avctx->inputs[0]; |
91 | HWDownloadContext *ctx = avctx->priv; |
92 | enum AVPixelFormat *formats; |
93 | int err, i, found; |
94 | |
95 | if (!ctx->hwframes_ref) |
96 | return AVERROR(EINVAL); |
97 | |
98 | err = av_hwframe_transfer_get_formats(ctx->hwframes_ref, |
99 | AV_HWFRAME_TRANSFER_DIRECTION_FROM, |
100 | &formats, 0); |
101 | if (err < 0) |
102 | return err; |
103 | |
104 | found = 0; |
105 | for (i = 0; formats[i] != AV_PIX_FMT_NONE; i++) { |
106 | if (formats[i] == outlink->format) { |
107 | found = 1; |
108 | break; |
109 | } |
110 | } |
111 | av_freep(&formats); |
112 | |
113 | if (!found) { |
114 | av_log(ctx, AV_LOG_ERROR, "Invalid output format %s for hwframe " |
115 | "download.\n", av_get_pix_fmt_name(outlink->format)); |
116 | return AVERROR(EINVAL); |
117 | } |
118 | |
119 | outlink->w = inlink->w; |
120 | outlink->h = inlink->h; |
121 | |
122 | return 0; |
123 | } |
124 | |
125 | static int hwdownload_filter_frame(AVFilterLink *link, AVFrame *input) |
126 | { |
127 | AVFilterContext *avctx = link->dst; |
128 | AVFilterLink *outlink = avctx->outputs[0]; |
129 | HWDownloadContext *ctx = avctx->priv; |
130 | AVFrame *output = NULL; |
131 | int err; |
132 | |
133 | if (!ctx->hwframes_ref || !input->hw_frames_ctx) { |
134 | av_log(ctx, AV_LOG_ERROR, "Input frames must have hardware context.\n"); |
135 | err = AVERROR(EINVAL); |
136 | goto fail; |
137 | } |
138 | if ((void*)ctx->hwframes != input->hw_frames_ctx->data) { |
139 | av_log(ctx, AV_LOG_ERROR, "Input frame is not the in the configured " |
140 | "hwframe context.\n"); |
141 | err = AVERROR(EINVAL); |
142 | goto fail; |
143 | } |
144 | |
145 | output = ff_get_video_buffer(outlink, ctx->hwframes->width, |
146 | ctx->hwframes->height); |
147 | if (!output) { |
148 | err = AVERROR(ENOMEM); |
149 | goto fail; |
150 | } |
151 | |
152 | err = av_hwframe_transfer_data(output, input, 0); |
153 | if (err < 0) { |
154 | av_log(ctx, AV_LOG_ERROR, "Failed to download frame: %d.\n", err); |
155 | goto fail; |
156 | } |
157 | |
158 | output->width = outlink->w; |
159 | output->height = outlink->h; |
160 | |
161 | err = av_frame_copy_props(output, input); |
162 | if (err < 0) |
163 | goto fail; |
164 | |
165 | av_frame_free(&input); |
166 | |
167 | return ff_filter_frame(avctx->outputs[0], output); |
168 | |
169 | fail: |
170 | av_frame_free(&input); |
171 | av_frame_free(&output); |
172 | return err; |
173 | } |
174 | |
175 | static av_cold void hwdownload_uninit(AVFilterContext *avctx) |
176 | { |
177 | HWDownloadContext *ctx = avctx->priv; |
178 | |
179 | av_buffer_unref(&ctx->hwframes_ref); |
180 | } |
181 | |
182 | static const AVClass hwdownload_class = { |
183 | .class_name = "hwdownload", |
184 | .item_name = av_default_item_name, |
185 | .option = NULL, |
186 | .version = LIBAVUTIL_VERSION_INT, |
187 | }; |
188 | |
189 | static const AVFilterPad hwdownload_inputs[] = { |
190 | { |
191 | .name = "default", |
192 | .type = AVMEDIA_TYPE_VIDEO, |
193 | .config_props = hwdownload_config_input, |
194 | .filter_frame = hwdownload_filter_frame, |
195 | }, |
196 | { NULL } |
197 | }; |
198 | |
199 | static const AVFilterPad hwdownload_outputs[] = { |
200 | { |
201 | .name = "default", |
202 | .type = AVMEDIA_TYPE_VIDEO, |
203 | .config_props = hwdownload_config_output, |
204 | }, |
205 | { NULL } |
206 | }; |
207 | |
208 | AVFilter ff_vf_hwdownload = { |
209 | .name = "hwdownload", |
210 | .description = NULL_IF_CONFIG_SMALL("Download a hardware frame to a normal frame"), |
211 | .uninit = hwdownload_uninit, |
212 | .query_formats = hwdownload_query_formats, |
213 | .priv_size = sizeof(HWDownloadContext), |
214 | .priv_class = &hwdownload_class, |
215 | .inputs = hwdownload_inputs, |
216 | .outputs = hwdownload_outputs, |
217 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, |
218 | }; |
219 |