| Line | Branch | Exec | Source |
|---|---|---|---|
| 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 "filters.h" | ||
| 28 | #include "formats.h" | ||
| 29 | #include "video.h" | ||
| 30 | |||
| 31 | typedef struct HWUploadContext { | ||
| 32 | const AVClass *class; | ||
| 33 | |||
| 34 | AVBufferRef *hwdevice_ref; | ||
| 35 | |||
| 36 | AVBufferRef *hwframes_ref; | ||
| 37 | AVHWFramesContext *hwframes; | ||
| 38 | |||
| 39 | char *device_type; | ||
| 40 | } HWUploadContext; | ||
| 41 | |||
| 42 | ✗ | static int hwupload_init(AVFilterContext *avctx) | |
| 43 | { | ||
| 44 | ✗ | HWUploadContext *ctx = avctx->priv; | |
| 45 | int err; | ||
| 46 | |||
| 47 | ✗ | if (!avctx->hw_device_ctx) { | |
| 48 | ✗ | av_log(ctx, AV_LOG_ERROR, "A hardware device reference is required " | |
| 49 | "to upload frames to.\n"); | ||
| 50 | ✗ | return AVERROR(EINVAL); | |
| 51 | } | ||
| 52 | |||
| 53 | ✗ | if (ctx->device_type) { | |
| 54 | ✗ | err = av_hwdevice_ctx_create_derived( | |
| 55 | &ctx->hwdevice_ref, | ||
| 56 | ✗ | av_hwdevice_find_type_by_name(ctx->device_type), | |
| 57 | avctx->hw_device_ctx, 0); | ||
| 58 | ✗ | if (err < 0) | |
| 59 | ✗ | return err; | |
| 60 | } else { | ||
| 61 | ✗ | ctx->hwdevice_ref = av_buffer_ref(avctx->hw_device_ctx); | |
| 62 | ✗ | if (!ctx->hwdevice_ref) | |
| 63 | ✗ | return AVERROR(ENOMEM); | |
| 64 | } | ||
| 65 | |||
| 66 | ✗ | return 0; | |
| 67 | } | ||
| 68 | |||
| 69 | ✗ | static int hwupload_query_formats(const AVFilterContext *avctx, | |
| 70 | AVFilterFormatsConfig **cfg_in, | ||
| 71 | AVFilterFormatsConfig **cfg_out) | ||
| 72 | { | ||
| 73 | ✗ | const HWUploadContext *ctx = avctx->priv; | |
| 74 | ✗ | AVHWFramesConstraints *constraints = NULL; | |
| 75 | const enum AVPixelFormat *input_pix_fmts, *output_pix_fmts; | ||
| 76 | ✗ | AVFilterFormats *input_formats = NULL; | |
| 77 | int err, i; | ||
| 78 | |||
| 79 | ✗ | constraints = av_hwdevice_get_hwframe_constraints(ctx->hwdevice_ref, NULL); | |
| 80 | ✗ | if (!constraints) { | |
| 81 | ✗ | err = AVERROR(EINVAL); | |
| 82 | ✗ | goto fail; | |
| 83 | } | ||
| 84 | |||
| 85 | ✗ | input_pix_fmts = constraints->valid_sw_formats; | |
| 86 | ✗ | output_pix_fmts = constraints->valid_hw_formats; | |
| 87 | |||
| 88 | ✗ | input_formats = ff_make_pixel_format_list(output_pix_fmts); | |
| 89 | ✗ | if (!input_formats) { | |
| 90 | ✗ | err = AVERROR(ENOMEM); | |
| 91 | ✗ | goto fail; | |
| 92 | } | ||
| 93 | ✗ | if (input_pix_fmts) { | |
| 94 | ✗ | for (i = 0; input_pix_fmts[i] != AV_PIX_FMT_NONE; i++) { | |
| 95 | ✗ | err = ff_add_format(&input_formats, input_pix_fmts[i]); | |
| 96 | ✗ | if (err < 0) | |
| 97 | ✗ | goto fail; | |
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | ✗ | if ((err = ff_formats_ref(input_formats, &cfg_in[0]->formats)) < 0 || | |
| 102 | ✗ | (err = ff_formats_ref(ff_make_pixel_format_list(output_pix_fmts), | |
| 103 | ✗ | &cfg_out[0]->formats)) < 0) | |
| 104 | ✗ | goto fail; | |
| 105 | |||
| 106 | ✗ | av_hwframe_constraints_free(&constraints); | |
| 107 | ✗ | return 0; | |
| 108 | |||
| 109 | ✗ | fail: | |
| 110 | ✗ | av_hwframe_constraints_free(&constraints); | |
| 111 | ✗ | return err; | |
| 112 | } | ||
| 113 | |||
| 114 | ✗ | static int hwupload_config_output(AVFilterLink *outlink) | |
| 115 | { | ||
| 116 | ✗ | FilterLink *outl = ff_filter_link(outlink); | |
| 117 | ✗ | AVFilterContext *avctx = outlink->src; | |
| 118 | ✗ | AVFilterLink *inlink = avctx->inputs[0]; | |
| 119 | ✗ | FilterLink *inl = ff_filter_link(inlink); | |
| 120 | ✗ | HWUploadContext *ctx = avctx->priv; | |
| 121 | int err; | ||
| 122 | |||
| 123 | ✗ | av_buffer_unref(&ctx->hwframes_ref); | |
| 124 | |||
| 125 | ✗ | if (inlink->format == outlink->format) { | |
| 126 | // The input is already a hardware format, so we just want to | ||
| 127 | // pass through the input frames in their own hardware context. | ||
| 128 | ✗ | if (!inl->hw_frames_ctx) { | |
| 129 | ✗ | av_log(ctx, AV_LOG_ERROR, "No input hwframe context.\n"); | |
| 130 | ✗ | return AVERROR(EINVAL); | |
| 131 | } | ||
| 132 | |||
| 133 | ✗ | outl->hw_frames_ctx = av_buffer_ref(inl->hw_frames_ctx); | |
| 134 | ✗ | if (!outl->hw_frames_ctx) | |
| 135 | ✗ | return AVERROR(ENOMEM); | |
| 136 | |||
| 137 | ✗ | return 0; | |
| 138 | } | ||
| 139 | |||
| 140 | ✗ | ctx->hwframes_ref = av_hwframe_ctx_alloc(ctx->hwdevice_ref); | |
| 141 | ✗ | if (!ctx->hwframes_ref) | |
| 142 | ✗ | return AVERROR(ENOMEM); | |
| 143 | |||
| 144 | ✗ | ctx->hwframes = (AVHWFramesContext*)ctx->hwframes_ref->data; | |
| 145 | |||
| 146 | ✗ | av_log(ctx, AV_LOG_DEBUG, "Surface format is %s.\n", | |
| 147 | ✗ | av_get_pix_fmt_name(inlink->format)); | |
| 148 | |||
| 149 | ✗ | ctx->hwframes->format = outlink->format; | |
| 150 | ✗ | if (inl->hw_frames_ctx) { | |
| 151 | ✗ | AVHWFramesContext *in_hwframe_ctx = | |
| 152 | ✗ | (AVHWFramesContext*)inl->hw_frames_ctx->data; | |
| 153 | ✗ | ctx->hwframes->sw_format = in_hwframe_ctx->sw_format; | |
| 154 | } else { | ||
| 155 | ✗ | ctx->hwframes->sw_format = inlink->format; | |
| 156 | } | ||
| 157 | ✗ | ctx->hwframes->width = inlink->w; | |
| 158 | ✗ | ctx->hwframes->height = inlink->h; | |
| 159 | |||
| 160 | ✗ | if (avctx->extra_hw_frames >= 0) | |
| 161 | ✗ | ctx->hwframes->initial_pool_size = 2 + avctx->extra_hw_frames; | |
| 162 | |||
| 163 | ✗ | err = av_hwframe_ctx_init(ctx->hwframes_ref); | |
| 164 | ✗ | if (err < 0) | |
| 165 | ✗ | goto fail; | |
| 166 | |||
| 167 | ✗ | outl->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref); | |
| 168 | ✗ | if (!outl->hw_frames_ctx) { | |
| 169 | ✗ | err = AVERROR(ENOMEM); | |
| 170 | ✗ | goto fail; | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | return 0; | |
| 174 | |||
| 175 | ✗ | fail: | |
| 176 | ✗ | av_buffer_unref(&ctx->hwframes_ref); | |
| 177 | ✗ | return err; | |
| 178 | } | ||
| 179 | |||
| 180 | ✗ | static int hwupload_filter_frame(AVFilterLink *link, AVFrame *input) | |
| 181 | { | ||
| 182 | ✗ | AVFilterContext *avctx = link->dst; | |
| 183 | ✗ | AVFilterLink *outlink = avctx->outputs[0]; | |
| 184 | ✗ | HWUploadContext *ctx = avctx->priv; | |
| 185 | ✗ | AVFrame *output = NULL; | |
| 186 | int err; | ||
| 187 | |||
| 188 | ✗ | if (input->format == outlink->format) | |
| 189 | ✗ | return ff_filter_frame(outlink, input); | |
| 190 | |||
| 191 | ✗ | output = ff_get_video_buffer(outlink, outlink->w, outlink->h); | |
| 192 | ✗ | if (!output) { | |
| 193 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failed to allocate frame to upload to.\n"); | |
| 194 | ✗ | err = AVERROR(ENOMEM); | |
| 195 | ✗ | goto fail; | |
| 196 | } | ||
| 197 | |||
| 198 | ✗ | output->width = input->width; | |
| 199 | ✗ | output->height = input->height; | |
| 200 | |||
| 201 | ✗ | err = av_hwframe_transfer_data(output, input, 0); | |
| 202 | ✗ | if (err < 0) { | |
| 203 | ✗ | av_log(ctx, AV_LOG_ERROR, "Failed to upload frame: %d.\n", err); | |
| 204 | ✗ | goto fail; | |
| 205 | } | ||
| 206 | |||
| 207 | ✗ | err = av_frame_copy_props(output, input); | |
| 208 | ✗ | if (err < 0) | |
| 209 | ✗ | goto fail; | |
| 210 | |||
| 211 | ✗ | av_frame_free(&input); | |
| 212 | |||
| 213 | ✗ | return ff_filter_frame(outlink, output); | |
| 214 | |||
| 215 | ✗ | fail: | |
| 216 | ✗ | av_frame_free(&input); | |
| 217 | ✗ | av_frame_free(&output); | |
| 218 | ✗ | return err; | |
| 219 | } | ||
| 220 | |||
| 221 | ✗ | static av_cold void hwupload_uninit(AVFilterContext *avctx) | |
| 222 | { | ||
| 223 | ✗ | HWUploadContext *ctx = avctx->priv; | |
| 224 | |||
| 225 | ✗ | av_buffer_unref(&ctx->hwframes_ref); | |
| 226 | ✗ | av_buffer_unref(&ctx->hwdevice_ref); | |
| 227 | ✗ | } | |
| 228 | |||
| 229 | #define OFFSET(x) offsetof(HWUploadContext, x) | ||
| 230 | #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) | ||
| 231 | static const AVOption hwupload_options[] = { | ||
| 232 | { | ||
| 233 | "derive_device", "Derive a new device of this type", | ||
| 234 | OFFSET(device_type), AV_OPT_TYPE_STRING, | ||
| 235 | { .str = NULL }, 0, 0, FLAGS | ||
| 236 | }, | ||
| 237 | { | ||
| 238 | NULL | ||
| 239 | } | ||
| 240 | }; | ||
| 241 | |||
| 242 | AVFILTER_DEFINE_CLASS(hwupload); | ||
| 243 | |||
| 244 | static const AVFilterPad hwupload_inputs[] = { | ||
| 245 | { | ||
| 246 | .name = "default", | ||
| 247 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 248 | .filter_frame = hwupload_filter_frame, | ||
| 249 | }, | ||
| 250 | }; | ||
| 251 | |||
| 252 | static const AVFilterPad hwupload_outputs[] = { | ||
| 253 | { | ||
| 254 | .name = "default", | ||
| 255 | .type = AVMEDIA_TYPE_VIDEO, | ||
| 256 | .config_props = hwupload_config_output, | ||
| 257 | }, | ||
| 258 | }; | ||
| 259 | |||
| 260 | const FFFilter ff_vf_hwupload = { | ||
| 261 | .p.name = "hwupload", | ||
| 262 | .p.description = NULL_IF_CONFIG_SMALL("Upload a normal frame to a hardware frame"), | ||
| 263 | .p.priv_class = &hwupload_class, | ||
| 264 | .p.flags = AVFILTER_FLAG_HWDEVICE, | ||
| 265 | .init = hwupload_init, | ||
| 266 | .uninit = hwupload_uninit, | ||
| 267 | .priv_size = sizeof(HWUploadContext), | ||
| 268 | FILTER_INPUTS(hwupload_inputs), | ||
| 269 | FILTER_OUTPUTS(hwupload_outputs), | ||
| 270 | FILTER_QUERY_FUNC2(hwupload_query_formats), | ||
| 271 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, | ||
| 272 | }; | ||
| 273 |