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