diff --git a/libavfilter/vf_hwmap.c b/libavfilter/vf_hwmap.c index f19d10f2c2..2983528ea0 100644 --- a/libavfilter/vf_hwmap.c +++ b/libavfilter/vf_hwmap.c @@ -34,7 +34,7 @@ typedef struct HWMapContext { int mode; char *derive_device_type; - int map_backwards; + int reverse; } HWMapContext; static int hwmap_query_formats(AVFilterContext *avctx) @@ -92,7 +92,8 @@ static int hwmap_config_output(AVFilterLink *outlink) } if (inlink->format == hwfc->format && - (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) && + !ctx->reverse) { // Map between two hardware formats (including the case of // undoing an existing mapping). @@ -113,6 +114,56 @@ static int hwmap_config_output(AVFilterLink *outlink) goto fail; } + } else if (inlink->format == hwfc->format && + (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) && + ctx->reverse) { + // Map between two hardware formats, but do it in reverse. + // Make a new hwframe context for the target type, and then + // overwrite the input hwframe context with a derived context + // mapped from that back to the source type. + AVBufferRef *source; + AVHWFramesContext *frames; + + ctx->hwframes_ref = av_hwframe_ctx_alloc(device); + if (!ctx->hwframes_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + frames = (AVHWFramesContext*)ctx->hwframes_ref->data; + + frames->format = outlink->format; + frames->sw_format = hwfc->sw_format; + frames->width = hwfc->width; + frames->height = hwfc->height; + frames->initial_pool_size = 64; + + err = av_hwframe_ctx_init(ctx->hwframes_ref); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialise " + "target frames context: %d.\n", err); + goto fail; + } + + err = av_hwframe_ctx_create_derived(&source, + inlink->format, + hwfc->device_ref, + ctx->hwframes_ref, + ctx->mode); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to create " + "derived source frames context: %d.\n", err); + goto fail; + } + + // Here is the naughty bit. This overwriting changes what + // ff_get_video_buffer() in the previous filter returns - + // it will now give a frame allocated here mapped back to + // the format it expects. If there were any additional + // constraints on the output frames there then this may + // break nastily. + av_buffer_unref(&inlink->hw_frames_ctx); + inlink->hw_frames_ctx = source; + } else if ((outlink->format == hwfc->format && inlink->format == hwfc->sw_format) || inlink->format == hwfc->format) { @@ -144,13 +195,13 @@ static int hwmap_config_output(AVFilterLink *outlink) if (!device) { av_log(avctx, AV_LOG_ERROR, "A device reference is " - "required to create new frames with backwards " + "required to create new frames with reverse " "mapping.\n"); err = AVERROR(EINVAL); goto fail; } - ctx->map_backwards = 1; + ctx->reverse = 1; ctx->hwframes_ref = av_hwframe_ctx_alloc(device); if (!ctx->hwframes_ref) { @@ -167,7 +218,7 @@ static int hwmap_config_output(AVFilterLink *outlink) err = av_hwframe_ctx_init(ctx->hwframes_ref); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to create frame " - "context for backward mapping: %d.\n", err); + "context for reverse mapping: %d.\n", err); goto fail; } @@ -199,7 +250,7 @@ static AVFrame *hwmap_get_buffer(AVFilterLink *inlink, int w, int h) AVFilterLink *outlink = avctx->outputs[0]; HWMapContext *ctx = avctx->priv; - if (ctx->map_backwards) { + if (ctx->reverse && !inlink->hw_frames_ctx) { AVFrame *src, *dst; int err; @@ -257,7 +308,7 @@ static int hwmap_filter_frame(AVFilterLink *link, AVFrame *input) goto fail; } - if (ctx->map_backwards && !input->hw_frames_ctx) { + if (ctx->reverse && !input->hw_frames_ctx) { // If we mapped backwards from hardware to software, we need // to attach the hardware frame context to the input frame to // make the mapping visible to av_hwframe_map(). @@ -323,6 +374,9 @@ static const AVOption hwmap_options[] = { { "derive_device", "Derive a new device of this type", OFFSET(derive_device_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "reverse", "Map in reverse (create and allocate in the sink)", + OFFSET(reverse), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, 1, FLAGS }, { NULL }, };