The hwmap filter takes a hardware frame as input and maps it to something else (hardware or software) for other processing.
The hwunmap filter undoes a hwmap - the frame which was mapped is put back into the filter chain. --- libavfilter/Makefile | 2 + libavfilter/allfilters.c | 2 + libavfilter/vf_hwmap.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++ libavfilter/vf_hwunmap.c | 153 +++++++++++++++++++++++++++++ 4 files changed, 400 insertions(+) create mode 100644 libavfilter/vf_hwmap.c create mode 100644 libavfilter/vf_hwunmap.c diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 21515fe..4ec244e 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -60,6 +60,8 @@ OBJS-$(CONFIG_GRADFUN_FILTER) += vf_gradfun.o OBJS-$(CONFIG_HFLIP_FILTER) += vf_hflip.o OBJS-$(CONFIG_HQDN3D_FILTER) += vf_hqdn3d.o OBJS-$(CONFIG_HWDOWNLOAD_FILTER) += vf_hwdownload.o +OBJS-$(CONFIG_HWMAP_FILTER) += vf_hwmap.o +OBJS-$(CONFIG_HWUNMAP_FILTER) += vf_hwunmap.o OBJS-$(CONFIG_HWUPLOAD_CUDA_FILTER) += vf_hwupload_cuda.o OBJS-$(CONFIG_HWUPLOAD_FILTER) += vf_hwupload.o OBJS-$(CONFIG_INTERLACE_FILTER) += vf_interlace.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index e3858d8..36a484c 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -83,6 +83,8 @@ void avfilter_register_all(void) REGISTER_FILTER(HFLIP, hflip, vf); REGISTER_FILTER(HQDN3D, hqdn3d, vf); REGISTER_FILTER(HWDOWNLOAD, hwdownload, vf); + REGISTER_FILTER(HWMAP, hwmap, vf); + REGISTER_FILTER(HWUNMAP, hwunmap, vf); REGISTER_FILTER(HWUPLOAD, hwupload, vf); REGISTER_FILTER(HWUPLOAD_CUDA, hwupload_cuda, vf); REGISTER_FILTER(INTERLACE, interlace, vf); diff --git a/libavfilter/vf_hwmap.c b/libavfilter/vf_hwmap.c new file mode 100644 index 0000000..c8d5a0e --- /dev/null +++ b/libavfilter/vf_hwmap.c @@ -0,0 +1,243 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/buffer.h" +#include "libavutil/hwcontext.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" + +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +typedef struct { + const AVClass *class; + + AVBufferRef *hwdevice_ref; + AVHWDeviceContext *hwdevice; + + AVBufferRef *hwframes_ref; + AVHWFramesContext *hwframes; + + int mode; +} HWMapContext; + +static int hwmap_query_formats(AVFilterContext *avctx) +{ + AVFilterFormats *infmts = NULL; + AVFilterFormats *outfmts = NULL; + const AVPixFmtDescriptor *desc; + int err; + + // Input is any hardware format, and output can be anything at all. + + for (desc = av_pix_fmt_desc_next(NULL); desc; + desc = av_pix_fmt_desc_next(desc)) { + if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + err = ff_add_format(&infmts, av_pix_fmt_desc_get_id(desc)); + if (err < 0) + goto fail; + } + err = ff_add_format(&outfmts, av_pix_fmt_desc_get_id(desc)); + if (err) + goto fail; + } + + ff_formats_ref(infmts, &avctx->inputs[0]->out_formats); + ff_formats_ref(outfmts, &avctx->outputs[0]->in_formats); + return 0; + +fail: + ff_formats_unref(&infmts); + ff_formats_unref(&outfmts); + return err; +} + +static int hwmap_config_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + HWMapContext *ctx = avctx->priv; + AVFilterLink *inlink = avctx->inputs[0]; + AVHWFramesContext *hwfc = + (AVHWFramesContext*)inlink->hw_frames_ctx->data; + const AVPixFmtDescriptor *desc; + int err; + + desc = av_pix_fmt_desc_get(outlink->format); + if (!desc) + return AVERROR(EINVAL); + + if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + // TODO: find these device references somewhere more suitable, + // ideally derived from hwfc or hwfc->device_ctx if possible. + + if (outlink->format == AV_PIX_FMT_OPENCL) { + extern AVBufferRef *opencl_device; + ctx->hwdevice_ref = av_buffer_ref(opencl_device); + } else if (outlink->format == AV_PIX_FMT_VAAPI) { + extern AVBufferRef *hw_device_ctx; + ctx->hwdevice_ref = av_buffer_ref(hw_device_ctx); + } else { + ctx->hwdevice_ref = av_buffer_ref(avctx->hw_device_ctx); + } + + if (!ctx->hwdevice_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + ctx->hwdevice = (AVHWDeviceContext*)ctx->hwdevice_ref->data; + + ctx->hwframes_ref = av_hwframe_ctx_alloc(ctx->hwdevice_ref); + if (!ctx->hwframes_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + ctx->hwframes = (AVHWFramesContext*)ctx->hwframes_ref->data; + + ctx->hwframes->format = outlink->format; + ctx->hwframes->width = inlink->w; + ctx->hwframes->height = inlink->h; + + // This should probably be configurable somehow too. + ctx->hwframes->sw_format = hwfc->sw_format; + + err = av_hwframe_ctx_init(ctx->hwframes_ref); + if (err < 0) + goto fail; + + outlink->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref); + if (!outlink->hw_frames_ctx) { + err = AVERROR(ENOMEM); + goto fail; + } + + } else { + ctx->hwframes_ref = NULL; + outlink->hw_frames_ctx = NULL; + } + + return 0; + +fail: + av_buffer_unref(&ctx->hwframes_ref); + av_buffer_unref(&ctx->hwdevice_ref); + return err; +} + +static int hwmap_filter_frame(AVFilterLink *link, AVFrame *input) +{ + AVFilterContext *avctx = link->dst; + AVFilterLink *outlink = avctx->outputs[0]; + HWMapContext *ctx = avctx->priv; + AVFrame *map = NULL; + int err; + + av_log(ctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n", + av_get_pix_fmt_name(input->format), + input->width, input->height, input->pts); + + map = av_frame_alloc(); + if (!map) + return AVERROR(ENOMEM); + + map->format = outlink->format; + + if (ctx->hwframes_ref) + map->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref); + + err = av_hwframe_map(map, input, ctx->mode); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to map frame: %d.\n", err); + goto fail; + } + + err = av_frame_copy_props(map, input); + if (err < 0) + goto fail; + + av_frame_unref(input); + + av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n", + av_get_pix_fmt_name(map->format), + map->width, map->height, map->pts); + + return ff_filter_frame(outlink, map); + +fail: + av_frame_free(&input); + av_frame_free(&map); + return err; +} + +#define OFFSET(x) offsetof(HWMapContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption hwmap_options[] = { + { "mode", "Frame mapping mode", + OFFSET(mode), AV_OPT_TYPE_INT, + { .i64 = AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE }, + INT_MIN, INT_MAX, FLAGS }, + + { "readonly", "Map frame read-only", + 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_READ }, + INT_MIN, INT_MAX, FLAGS, "mode" }, + { "writeonly", "Map frame write-only", + 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_WRITE }, + INT_MIN, INT_MAX, FLAGS, "mode" }, + { "readwrite", "Map frame read-write", + 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE }, + INT_MIN, INT_MAX, FLAGS, "mode" }, + + { NULL }, +}; + +static const AVClass hwmap_class = { + .class_name = "hwmap", + .item_name = av_default_item_name, + .option = hwmap_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVFilterPad hwmap_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &hwmap_filter_frame, + }, + { NULL } +}; + +static const AVFilterPad hwmap_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &hwmap_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_hwmap = { + .name = "hwmap", + .description = NULL_IF_CONFIG_SMALL("Map a hardware frame to a normal frame"), + .priv_size = sizeof(HWMapContext), + .priv_class = &hwmap_class, + .query_formats = &hwmap_query_formats, + .inputs = hwmap_inputs, + .outputs = hwmap_outputs, +}; diff --git a/libavfilter/vf_hwunmap.c b/libavfilter/vf_hwunmap.c new file mode 100644 index 0000000..f905dab --- /dev/null +++ b/libavfilter/vf_hwunmap.c @@ -0,0 +1,153 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> + +#include "libavutil/buffer.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_internal.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" + +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +static int hwunmap_query_formats(AVFilterContext *avctx) +{ + AVFilterFormats *infmts = NULL; + AVFilterFormats *outfmts = NULL; + const AVPixFmtDescriptor *desc; + int err; + + // Exactly the opposite of hwmap - output is any hardware format, + // and input can be anything at all. + + for (desc = av_pix_fmt_desc_next(NULL); desc; + desc = av_pix_fmt_desc_next(desc)) { + err = ff_add_format(&infmts, av_pix_fmt_desc_get_id(desc)); + if (err) + goto fail; + if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + err = ff_add_format(&outfmts, av_pix_fmt_desc_get_id(desc)); + if (err < 0) + goto fail; + } + } + + ff_formats_ref(infmts, &avctx->inputs[0]->out_formats); + ff_formats_ref(outfmts, &avctx->outputs[0]->in_formats); + return 0; + +fail: + ff_formats_unref(&infmts); + ff_formats_unref(&outfmts); + return err; +} + +static int hwunmap_config_output(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + AVFilterLink *link; + + // Hacky hack hack hack. + // We need to pick out the origin hw_frames_ctx in order to + // provide it on the output link. Therefore, walk the filter + // chain backwards until we find a hwmap instance with the + // same format as our output and extract the hw_frames_ctx from + // that. Anything tricky in the filter graph will break this. + + for (link = outlink; link;) { + if (link->src->nb_inputs < 1) + break; + link = link->src->inputs[0]; + if (!strcmp(link->dst->filter->name, "hwmap") && + link->format == outlink->format) { + outlink->hw_frames_ctx = + av_buffer_ref(link->hw_frames_ctx); + if (!outlink->hw_frames_ctx) + return AVERROR(ENOMEM); + break; + } + } + + return 0; +} + +static int hwunmap_filter_frame(AVFilterLink *link, AVFrame *map) +{ + AVFilterContext *avctx = link->dst; + AVFilterLink *outlink = avctx->outputs[0]; + FFHWMapDescriptor *hwmap; + AVFrame *output; + int err; + + hwmap = (FFHWMapDescriptor*)map->buf[0]->data; + if (!hwmap) { + av_log(avctx, AV_LOG_ERROR, "Unable to unmap frame: " + "did not come from hwmap filter?\n"); + return AVERROR(EINVAL); + } + + output = av_frame_alloc(); + if (!output) + return AVERROR(ENOMEM); + + err = av_frame_ref(output, hwmap->source); + if (err < 0) + return err; + + av_frame_free(&map); + + return ff_filter_frame(outlink, output); +} + +static const AVClass hwunmap_class = { + .class_name = "hwunmap", + .item_name = av_default_item_name, + .option = NULL, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const AVFilterPad hwunmap_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &hwunmap_filter_frame, + }, + { NULL } +}; + +static const AVFilterPad hwunmap_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &hwunmap_config_output, + }, + { NULL } +}; + +AVFilter ff_vf_hwunmap = { + .name = "hwunmap", + .description = NULL_IF_CONFIG_SMALL("Unmap a hardware frame"), + .query_formats = &hwunmap_query_formats, + .inputs = hwunmap_inputs, + .outputs = hwunmap_outputs, +}; -- 2.8.1 _______________________________________________ libav-devel mailing list libav-devel@libav.org https://lists.libav.org/mailman/listinfo/libav-devel