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

Reply via email to