LCOV - code coverage report
Current view: top level - libavfilter - vf_hwmap.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 181 0.0 %
Date: 2017-12-12 03:56:30 Functions: 0 5 0.0 %

          Line data    Source code
       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/opt.h"
      23             : #include "libavutil/pixdesc.h"
      24             : 
      25             : #include "avfilter.h"
      26             : #include "formats.h"
      27             : #include "internal.h"
      28             : #include "video.h"
      29             : 
      30             : typedef struct HWMapContext {
      31             :     const AVClass *class;
      32             : 
      33             :     AVBufferRef   *hwframes_ref;
      34             : 
      35             :     int            mode;
      36             :     char          *derive_device_type;
      37             :     int            reverse;
      38             : } HWMapContext;
      39             : 
      40           0 : static int hwmap_query_formats(AVFilterContext *avctx)
      41             : {
      42             :     int ret;
      43             : 
      44           0 :     if ((ret = ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_VIDEO),
      45           0 :                               &avctx->inputs[0]->out_formats)) < 0 ||
      46           0 :         (ret = ff_formats_ref(ff_all_formats(AVMEDIA_TYPE_VIDEO),
      47           0 :                               &avctx->outputs[0]->in_formats)) < 0)
      48           0 :         return ret;
      49             : 
      50           0 :     return 0;
      51             : }
      52             : 
      53           0 : static int hwmap_config_output(AVFilterLink *outlink)
      54             : {
      55           0 :     AVFilterContext *avctx = outlink->src;
      56           0 :     HWMapContext      *ctx = avctx->priv;
      57           0 :     AVFilterLink   *inlink = avctx->inputs[0];
      58             :     AVHWFramesContext *hwfc;
      59             :     AVBufferRef *device;
      60             :     const AVPixFmtDescriptor *desc;
      61             :     int err, device_is_derived;
      62             : 
      63           0 :     av_log(avctx, AV_LOG_DEBUG, "Configure hwmap %s -> %s.\n",
      64           0 :            av_get_pix_fmt_name(inlink->format),
      65           0 :            av_get_pix_fmt_name(outlink->format));
      66             : 
      67           0 :     av_buffer_unref(&ctx->hwframes_ref);
      68             : 
      69           0 :     device = avctx->hw_device_ctx;
      70           0 :     device_is_derived = 0;
      71             : 
      72           0 :     if (inlink->hw_frames_ctx) {
      73           0 :         hwfc = (AVHWFramesContext*)inlink->hw_frames_ctx->data;
      74             : 
      75           0 :         if (ctx->derive_device_type) {
      76             :             enum AVHWDeviceType type;
      77             : 
      78           0 :             type = av_hwdevice_find_type_by_name(ctx->derive_device_type);
      79           0 :             if (type == AV_HWDEVICE_TYPE_NONE) {
      80           0 :                 av_log(avctx, AV_LOG_ERROR, "Invalid device type.\n");
      81           0 :                 err = AVERROR(EINVAL);
      82           0 :                 goto fail;
      83             :             }
      84             : 
      85           0 :             err = av_hwdevice_ctx_create_derived(&device, type,
      86             :                                                  hwfc->device_ref, 0);
      87           0 :             if (err < 0) {
      88           0 :                 av_log(avctx, AV_LOG_ERROR, "Failed to created derived "
      89             :                        "device context: %d.\n", err);
      90           0 :                 goto fail;
      91             :             }
      92           0 :             device_is_derived = 1;
      93             :         }
      94             : 
      95           0 :         desc = av_pix_fmt_desc_get(outlink->format);
      96           0 :         if (!desc) {
      97           0 :             err = AVERROR(EINVAL);
      98           0 :             goto fail;
      99             :         }
     100             : 
     101           0 :         if (inlink->format == hwfc->format &&
     102           0 :             (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) &&
     103           0 :             !ctx->reverse) {
     104             :             // Map between two hardware formats (including the case of
     105             :             // undoing an existing mapping).
     106             : 
     107           0 :             if (!device) {
     108           0 :                 av_log(avctx, AV_LOG_ERROR, "A device reference is "
     109             :                        "required to map to a hardware format.\n");
     110           0 :                 err = AVERROR(EINVAL);
     111           0 :                 goto fail;
     112             :             }
     113             : 
     114           0 :             err = av_hwframe_ctx_create_derived(&ctx->hwframes_ref,
     115           0 :                                                 outlink->format,
     116             :                                                 device,
     117             :                                                 inlink->hw_frames_ctx,
     118             :                                                 ctx->mode);
     119           0 :             if (err < 0) {
     120           0 :                 av_log(avctx, AV_LOG_ERROR, "Failed to create derived "
     121             :                        "frames context: %d.\n", err);
     122           0 :                 goto fail;
     123             :             }
     124             : 
     125           0 :         } else if (inlink->format == hwfc->format &&
     126           0 :                    (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) &&
     127           0 :                    ctx->reverse) {
     128             :             // Map between two hardware formats, but do it in reverse.
     129             :             // Make a new hwframe context for the target type, and then
     130             :             // overwrite the input hwframe context with a derived context
     131             :             // mapped from that back to the source type.
     132             :             AVBufferRef *source;
     133             :             AVHWFramesContext *frames;
     134             : 
     135           0 :             ctx->hwframes_ref = av_hwframe_ctx_alloc(device);
     136           0 :             if (!ctx->hwframes_ref) {
     137           0 :                 err = AVERROR(ENOMEM);
     138           0 :                 goto fail;
     139             :             }
     140           0 :             frames = (AVHWFramesContext*)ctx->hwframes_ref->data;
     141             : 
     142           0 :             frames->format    = outlink->format;
     143           0 :             frames->sw_format = hwfc->sw_format;
     144           0 :             frames->width     = hwfc->width;
     145           0 :             frames->height    = hwfc->height;
     146           0 :             frames->initial_pool_size = 64;
     147             : 
     148           0 :             err = av_hwframe_ctx_init(ctx->hwframes_ref);
     149           0 :             if (err < 0) {
     150           0 :                 av_log(avctx, AV_LOG_ERROR, "Failed to initialise "
     151             :                        "target frames context: %d.\n", err);
     152           0 :                 goto fail;
     153             :             }
     154             : 
     155           0 :             err = av_hwframe_ctx_create_derived(&source,
     156           0 :                                                 inlink->format,
     157             :                                                 hwfc->device_ref,
     158             :                                                 ctx->hwframes_ref,
     159             :                                                 ctx->mode);
     160           0 :             if (err < 0) {
     161           0 :                 av_log(avctx, AV_LOG_ERROR, "Failed to create "
     162             :                        "derived source frames context: %d.\n", err);
     163           0 :                 goto fail;
     164             :             }
     165             : 
     166             :             // Here is the naughty bit.  This overwriting changes what
     167             :             // ff_get_video_buffer() in the previous filter returns -
     168             :             // it will now give a frame allocated here mapped back to
     169             :             // the format it expects.  If there were any additional
     170             :             // constraints on the output frames there then this may
     171             :             // break nastily.
     172           0 :             av_buffer_unref(&inlink->hw_frames_ctx);
     173           0 :             inlink->hw_frames_ctx = source;
     174             : 
     175           0 :         } else if ((outlink->format == hwfc->format &&
     176           0 :                     inlink->format  == hwfc->sw_format) ||
     177           0 :                    inlink->format == hwfc->format) {
     178             :             // Map from a hardware format to a software format, or
     179             :             // undo an existing such mapping.
     180             : 
     181           0 :             ctx->hwframes_ref = av_buffer_ref(inlink->hw_frames_ctx);
     182           0 :             if (!ctx->hwframes_ref) {
     183           0 :                 err = AVERROR(ENOMEM);
     184           0 :                 goto fail;
     185             :             }
     186             : 
     187             :         } else {
     188             :             // Non-matching formats - not supported.
     189             : 
     190           0 :             av_log(avctx, AV_LOG_ERROR, "Unsupported formats for "
     191             :                    "hwmap: from %s (%s) to %s.\n",
     192           0 :                    av_get_pix_fmt_name(inlink->format),
     193             :                    av_get_pix_fmt_name(hwfc->format),
     194           0 :                    av_get_pix_fmt_name(outlink->format));
     195           0 :             err = AVERROR(EINVAL);
     196           0 :             goto fail;
     197             :         }
     198           0 :     } else if (avctx->hw_device_ctx) {
     199             :         // Map from a software format to a hardware format.  This
     200             :         // creates a new hwframe context like hwupload, but then
     201             :         // returns frames mapped from that to the previous link in
     202             :         // order to fill them without an additional copy.
     203             : 
     204           0 :         if (!device) {
     205           0 :             av_log(avctx, AV_LOG_ERROR, "A device reference is "
     206             :                    "required to create new frames with reverse "
     207             :                    "mapping.\n");
     208           0 :             err = AVERROR(EINVAL);
     209           0 :             goto fail;
     210             :         }
     211             : 
     212           0 :         ctx->reverse = 1;
     213             : 
     214           0 :         ctx->hwframes_ref = av_hwframe_ctx_alloc(device);
     215           0 :         if (!ctx->hwframes_ref) {
     216           0 :             err = AVERROR(ENOMEM);
     217           0 :             goto fail;
     218             :         }
     219           0 :         hwfc = (AVHWFramesContext*)ctx->hwframes_ref->data;
     220             : 
     221           0 :         hwfc->format    = outlink->format;
     222           0 :         hwfc->sw_format = inlink->format;
     223           0 :         hwfc->width     = inlink->w;
     224           0 :         hwfc->height    = inlink->h;
     225             : 
     226           0 :         err = av_hwframe_ctx_init(ctx->hwframes_ref);
     227           0 :         if (err < 0) {
     228           0 :             av_log(avctx, AV_LOG_ERROR, "Failed to create frame "
     229             :                    "context for reverse mapping: %d.\n", err);
     230           0 :             goto fail;
     231             :         }
     232             : 
     233             :     } else {
     234           0 :         av_log(avctx, AV_LOG_ERROR, "Mapping requires a hardware "
     235             :                "context (a device, or frames on input).\n");
     236           0 :         return AVERROR(EINVAL);
     237             :     }
     238             : 
     239           0 :     outlink->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref);
     240           0 :     if (!outlink->hw_frames_ctx) {
     241           0 :         err = AVERROR(ENOMEM);
     242           0 :         goto fail;
     243             :     }
     244             : 
     245           0 :     outlink->w = inlink->w;
     246           0 :     outlink->h = inlink->h;
     247             : 
     248           0 :     if (device_is_derived)
     249           0 :         av_buffer_unref(&device);
     250           0 :     return 0;
     251             : 
     252           0 : fail:
     253           0 :     if (device_is_derived)
     254           0 :         av_buffer_unref(&device);
     255           0 :     av_buffer_unref(&ctx->hwframes_ref);
     256           0 :     return err;
     257             : }
     258             : 
     259           0 : static AVFrame *hwmap_get_buffer(AVFilterLink *inlink, int w, int h)
     260             : {
     261           0 :     AVFilterContext *avctx = inlink->dst;
     262           0 :     AVFilterLink  *outlink = avctx->outputs[0];
     263           0 :     HWMapContext      *ctx = avctx->priv;
     264             : 
     265           0 :     if (ctx->reverse && !inlink->hw_frames_ctx) {
     266             :         AVFrame *src, *dst;
     267             :         int err;
     268             : 
     269           0 :         src = ff_get_video_buffer(outlink, w, h);
     270           0 :         if (!src) {
     271           0 :             av_log(avctx, AV_LOG_ERROR, "Failed to allocate source "
     272             :                    "frame for software mapping.\n");
     273           0 :             return NULL;
     274             :         }
     275             : 
     276           0 :         dst = av_frame_alloc();
     277           0 :         if (!dst) {
     278           0 :             av_frame_free(&src);
     279           0 :             return NULL;
     280             :         }
     281             : 
     282           0 :         err = av_hwframe_map(dst, src, ctx->mode);
     283           0 :         if (err) {
     284           0 :             av_log(avctx, AV_LOG_ERROR, "Failed to map frame to "
     285             :                    "software: %d.\n", err);
     286           0 :             av_frame_free(&src);
     287           0 :             av_frame_free(&dst);
     288           0 :             return NULL;
     289             :         }
     290             : 
     291           0 :         av_frame_free(&src);
     292           0 :         return dst;
     293             :     } else {
     294           0 :         return ff_default_get_video_buffer(inlink, w, h);
     295             :     }
     296             : }
     297             : 
     298           0 : static int hwmap_filter_frame(AVFilterLink *link, AVFrame *input)
     299             : {
     300           0 :     AVFilterContext *avctx = link->dst;
     301           0 :     AVFilterLink  *outlink = avctx->outputs[0];
     302           0 :     HWMapContext      *ctx = avctx->priv;
     303           0 :     AVFrame *map = NULL;
     304             :     int err;
     305             : 
     306           0 :     av_log(ctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
     307           0 :            av_get_pix_fmt_name(input->format),
     308           0 :            input->width, input->height, input->pts);
     309             : 
     310           0 :     map = av_frame_alloc();
     311           0 :     if (!map) {
     312           0 :         err = AVERROR(ENOMEM);
     313           0 :         goto fail;
     314             :     }
     315             : 
     316           0 :     map->format = outlink->format;
     317           0 :     map->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref);
     318           0 :     if (!map->hw_frames_ctx) {
     319           0 :         err = AVERROR(ENOMEM);
     320           0 :         goto fail;
     321             :     }
     322             : 
     323           0 :     if (ctx->reverse && !input->hw_frames_ctx) {
     324             :         // If we mapped backwards from hardware to software, we need
     325             :         // to attach the hardware frame context to the input frame to
     326             :         // make the mapping visible to av_hwframe_map().
     327           0 :         input->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref);
     328           0 :         if (!input->hw_frames_ctx) {
     329           0 :             err = AVERROR(ENOMEM);
     330           0 :             goto fail;
     331             :         }
     332             :     }
     333             : 
     334           0 :     err = av_hwframe_map(map, input, ctx->mode);
     335           0 :     if (err < 0) {
     336           0 :         av_log(avctx, AV_LOG_ERROR, "Failed to map frame: %d.\n", err);
     337           0 :         goto fail;
     338             :     }
     339             : 
     340           0 :     err = av_frame_copy_props(map, input);
     341           0 :     if (err < 0)
     342           0 :         goto fail;
     343             : 
     344           0 :     av_frame_free(&input);
     345             : 
     346           0 :     av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
     347           0 :            av_get_pix_fmt_name(map->format),
     348           0 :            map->width, map->height, map->pts);
     349             : 
     350           0 :     return ff_filter_frame(outlink, map);
     351             : 
     352           0 : fail:
     353           0 :     av_frame_free(&input);
     354           0 :     av_frame_free(&map);
     355           0 :     return err;
     356             : }
     357             : 
     358           0 : static av_cold void hwmap_uninit(AVFilterContext *avctx)
     359             : {
     360           0 :     HWMapContext *ctx = avctx->priv;
     361             : 
     362           0 :     av_buffer_unref(&ctx->hwframes_ref);
     363           0 : }
     364             : 
     365             : #define OFFSET(x) offsetof(HWMapContext, x)
     366             : #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
     367             : static const AVOption hwmap_options[] = {
     368             :     { "mode", "Frame mapping mode",
     369             :       OFFSET(mode), AV_OPT_TYPE_FLAGS,
     370             :       { .i64 = AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE },
     371             :       0, INT_MAX, FLAGS, "mode" },
     372             : 
     373             :     { "read", "Mapping should be readable",
     374             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_READ },
     375             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     376             :     { "write", "Mapping should be writeable",
     377             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_WRITE },
     378             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     379             :     { "overwrite", "Mapping will always overwrite the entire frame",
     380             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_OVERWRITE },
     381             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     382             :     { "direct", "Mapping should not involve any copying",
     383             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_DIRECT },
     384             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     385             : 
     386             :     { "derive_device", "Derive a new device of this type",
     387             :       OFFSET(derive_device_type), AV_OPT_TYPE_STRING,
     388             :       { .str = NULL }, 0, 0, FLAGS },
     389             :     { "reverse", "Map in reverse (create and allocate in the sink)",
     390             :       OFFSET(reverse), AV_OPT_TYPE_INT,
     391             :       { .i64 = 0 }, 0, 1, FLAGS },
     392             : 
     393             :     { NULL }
     394             : };
     395             : 
     396             : AVFILTER_DEFINE_CLASS(hwmap);
     397             : 
     398             : static const AVFilterPad hwmap_inputs[] = {
     399             :     {
     400             :         .name             = "default",
     401             :         .type             = AVMEDIA_TYPE_VIDEO,
     402             :         .get_video_buffer = hwmap_get_buffer,
     403             :         .filter_frame     = hwmap_filter_frame,
     404             :     },
     405             :     { NULL }
     406             : };
     407             : 
     408             : static const AVFilterPad hwmap_outputs[] = {
     409             :     {
     410             :         .name         = "default",
     411             :         .type         = AVMEDIA_TYPE_VIDEO,
     412             :         .config_props = hwmap_config_output,
     413             :     },
     414             :     { NULL }
     415             : };
     416             : 
     417             : AVFilter ff_vf_hwmap = {
     418             :     .name           = "hwmap",
     419             :     .description    = NULL_IF_CONFIG_SMALL("Map hardware frames"),
     420             :     .uninit         = hwmap_uninit,
     421             :     .priv_size      = sizeof(HWMapContext),
     422             :     .priv_class     = &hwmap_class,
     423             :     .query_formats  = hwmap_query_formats,
     424             :     .inputs         = hwmap_inputs,
     425             :     .outputs        = hwmap_outputs,
     426             :     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
     427             : };

Generated by: LCOV version 1.13