LCOV - code coverage report
Current view: top level - libavfilter - vf_hwmap.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 184 0.0 %
Date: 2018-02-17 15:39:01 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             : 
     147           0 :             if (avctx->extra_hw_frames >= 0)
     148           0 :                 frames->initial_pool_size = 2 + avctx->extra_hw_frames;
     149             : 
     150           0 :             err = av_hwframe_ctx_init(ctx->hwframes_ref);
     151           0 :             if (err < 0) {
     152           0 :                 av_log(avctx, AV_LOG_ERROR, "Failed to initialise "
     153             :                        "target frames context: %d.\n", err);
     154           0 :                 goto fail;
     155             :             }
     156             : 
     157           0 :             err = av_hwframe_ctx_create_derived(&source,
     158           0 :                                                 inlink->format,
     159             :                                                 hwfc->device_ref,
     160             :                                                 ctx->hwframes_ref,
     161             :                                                 ctx->mode);
     162           0 :             if (err < 0) {
     163           0 :                 av_log(avctx, AV_LOG_ERROR, "Failed to create "
     164             :                        "derived source frames context: %d.\n", err);
     165           0 :                 goto fail;
     166             :             }
     167             : 
     168             :             // Here is the naughty bit.  This overwriting changes what
     169             :             // ff_get_video_buffer() in the previous filter returns -
     170             :             // it will now give a frame allocated here mapped back to
     171             :             // the format it expects.  If there were any additional
     172             :             // constraints on the output frames there then this may
     173             :             // break nastily.
     174           0 :             av_buffer_unref(&inlink->hw_frames_ctx);
     175           0 :             inlink->hw_frames_ctx = source;
     176             : 
     177           0 :         } else if ((outlink->format == hwfc->format &&
     178           0 :                     inlink->format  == hwfc->sw_format) ||
     179           0 :                    inlink->format == hwfc->format) {
     180             :             // Map from a hardware format to a software format, or
     181             :             // undo an existing such mapping.
     182             : 
     183           0 :             ctx->hwframes_ref = av_buffer_ref(inlink->hw_frames_ctx);
     184           0 :             if (!ctx->hwframes_ref) {
     185           0 :                 err = AVERROR(ENOMEM);
     186           0 :                 goto fail;
     187             :             }
     188             : 
     189             :         } else {
     190             :             // Non-matching formats - not supported.
     191             : 
     192           0 :             av_log(avctx, AV_LOG_ERROR, "Unsupported formats for "
     193             :                    "hwmap: from %s (%s) to %s.\n",
     194           0 :                    av_get_pix_fmt_name(inlink->format),
     195             :                    av_get_pix_fmt_name(hwfc->format),
     196           0 :                    av_get_pix_fmt_name(outlink->format));
     197           0 :             err = AVERROR(EINVAL);
     198           0 :             goto fail;
     199             :         }
     200           0 :     } else if (avctx->hw_device_ctx) {
     201             :         // Map from a software format to a hardware format.  This
     202             :         // creates a new hwframe context like hwupload, but then
     203             :         // returns frames mapped from that to the previous link in
     204             :         // order to fill them without an additional copy.
     205             : 
     206           0 :         if (!device) {
     207           0 :             av_log(avctx, AV_LOG_ERROR, "A device reference is "
     208             :                    "required to create new frames with reverse "
     209             :                    "mapping.\n");
     210           0 :             err = AVERROR(EINVAL);
     211           0 :             goto fail;
     212             :         }
     213             : 
     214           0 :         ctx->reverse = 1;
     215             : 
     216           0 :         ctx->hwframes_ref = av_hwframe_ctx_alloc(device);
     217           0 :         if (!ctx->hwframes_ref) {
     218           0 :             err = AVERROR(ENOMEM);
     219           0 :             goto fail;
     220             :         }
     221           0 :         hwfc = (AVHWFramesContext*)ctx->hwframes_ref->data;
     222             : 
     223           0 :         hwfc->format    = outlink->format;
     224           0 :         hwfc->sw_format = inlink->format;
     225           0 :         hwfc->width     = inlink->w;
     226           0 :         hwfc->height    = inlink->h;
     227             : 
     228           0 :         if (avctx->extra_hw_frames >= 0)
     229           0 :             hwfc->initial_pool_size = 2 + avctx->extra_hw_frames;
     230             : 
     231           0 :         err = av_hwframe_ctx_init(ctx->hwframes_ref);
     232           0 :         if (err < 0) {
     233           0 :             av_log(avctx, AV_LOG_ERROR, "Failed to create frame "
     234             :                    "context for reverse mapping: %d.\n", err);
     235           0 :             goto fail;
     236             :         }
     237             : 
     238             :     } else {
     239           0 :         av_log(avctx, AV_LOG_ERROR, "Mapping requires a hardware "
     240             :                "context (a device, or frames on input).\n");
     241           0 :         return AVERROR(EINVAL);
     242             :     }
     243             : 
     244           0 :     outlink->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref);
     245           0 :     if (!outlink->hw_frames_ctx) {
     246           0 :         err = AVERROR(ENOMEM);
     247           0 :         goto fail;
     248             :     }
     249             : 
     250           0 :     outlink->w = inlink->w;
     251           0 :     outlink->h = inlink->h;
     252             : 
     253           0 :     if (device_is_derived)
     254           0 :         av_buffer_unref(&device);
     255           0 :     return 0;
     256             : 
     257           0 : fail:
     258           0 :     if (device_is_derived)
     259           0 :         av_buffer_unref(&device);
     260           0 :     av_buffer_unref(&ctx->hwframes_ref);
     261           0 :     return err;
     262             : }
     263             : 
     264           0 : static AVFrame *hwmap_get_buffer(AVFilterLink *inlink, int w, int h)
     265             : {
     266           0 :     AVFilterContext *avctx = inlink->dst;
     267           0 :     AVFilterLink  *outlink = avctx->outputs[0];
     268           0 :     HWMapContext      *ctx = avctx->priv;
     269             : 
     270           0 :     if (ctx->reverse && !inlink->hw_frames_ctx) {
     271             :         AVFrame *src, *dst;
     272             :         int err;
     273             : 
     274           0 :         src = ff_get_video_buffer(outlink, w, h);
     275           0 :         if (!src) {
     276           0 :             av_log(avctx, AV_LOG_ERROR, "Failed to allocate source "
     277             :                    "frame for software mapping.\n");
     278           0 :             return NULL;
     279             :         }
     280             : 
     281           0 :         dst = av_frame_alloc();
     282           0 :         if (!dst) {
     283           0 :             av_frame_free(&src);
     284           0 :             return NULL;
     285             :         }
     286             : 
     287           0 :         err = av_hwframe_map(dst, src, ctx->mode);
     288           0 :         if (err) {
     289           0 :             av_log(avctx, AV_LOG_ERROR, "Failed to map frame to "
     290             :                    "software: %d.\n", err);
     291           0 :             av_frame_free(&src);
     292           0 :             av_frame_free(&dst);
     293           0 :             return NULL;
     294             :         }
     295             : 
     296           0 :         av_frame_free(&src);
     297           0 :         return dst;
     298             :     } else {
     299           0 :         return ff_default_get_video_buffer(inlink, w, h);
     300             :     }
     301             : }
     302             : 
     303           0 : static int hwmap_filter_frame(AVFilterLink *link, AVFrame *input)
     304             : {
     305           0 :     AVFilterContext *avctx = link->dst;
     306           0 :     AVFilterLink  *outlink = avctx->outputs[0];
     307           0 :     HWMapContext      *ctx = avctx->priv;
     308           0 :     AVFrame *map = NULL;
     309             :     int err;
     310             : 
     311           0 :     av_log(ctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
     312           0 :            av_get_pix_fmt_name(input->format),
     313           0 :            input->width, input->height, input->pts);
     314             : 
     315           0 :     map = av_frame_alloc();
     316           0 :     if (!map) {
     317           0 :         err = AVERROR(ENOMEM);
     318           0 :         goto fail;
     319             :     }
     320             : 
     321           0 :     map->format = outlink->format;
     322           0 :     map->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref);
     323           0 :     if (!map->hw_frames_ctx) {
     324           0 :         err = AVERROR(ENOMEM);
     325           0 :         goto fail;
     326             :     }
     327             : 
     328           0 :     if (ctx->reverse && !input->hw_frames_ctx) {
     329             :         // If we mapped backwards from hardware to software, we need
     330             :         // to attach the hardware frame context to the input frame to
     331             :         // make the mapping visible to av_hwframe_map().
     332           0 :         input->hw_frames_ctx = av_buffer_ref(ctx->hwframes_ref);
     333           0 :         if (!input->hw_frames_ctx) {
     334           0 :             err = AVERROR(ENOMEM);
     335           0 :             goto fail;
     336             :         }
     337             :     }
     338             : 
     339           0 :     err = av_hwframe_map(map, input, ctx->mode);
     340           0 :     if (err < 0) {
     341           0 :         av_log(avctx, AV_LOG_ERROR, "Failed to map frame: %d.\n", err);
     342           0 :         goto fail;
     343             :     }
     344             : 
     345           0 :     err = av_frame_copy_props(map, input);
     346           0 :     if (err < 0)
     347           0 :         goto fail;
     348             : 
     349           0 :     av_frame_free(&input);
     350             : 
     351           0 :     av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
     352           0 :            av_get_pix_fmt_name(map->format),
     353           0 :            map->width, map->height, map->pts);
     354             : 
     355           0 :     return ff_filter_frame(outlink, map);
     356             : 
     357           0 : fail:
     358           0 :     av_frame_free(&input);
     359           0 :     av_frame_free(&map);
     360           0 :     return err;
     361             : }
     362             : 
     363           0 : static av_cold void hwmap_uninit(AVFilterContext *avctx)
     364             : {
     365           0 :     HWMapContext *ctx = avctx->priv;
     366             : 
     367           0 :     av_buffer_unref(&ctx->hwframes_ref);
     368           0 : }
     369             : 
     370             : #define OFFSET(x) offsetof(HWMapContext, x)
     371             : #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
     372             : static const AVOption hwmap_options[] = {
     373             :     { "mode", "Frame mapping mode",
     374             :       OFFSET(mode), AV_OPT_TYPE_FLAGS,
     375             :       { .i64 = AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE },
     376             :       0, INT_MAX, FLAGS, "mode" },
     377             : 
     378             :     { "read", "Mapping should be readable",
     379             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_READ },
     380             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     381             :     { "write", "Mapping should be writeable",
     382             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_WRITE },
     383             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     384             :     { "overwrite", "Mapping will always overwrite the entire frame",
     385             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_OVERWRITE },
     386             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     387             :     { "direct", "Mapping should not involve any copying",
     388             :       0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_DIRECT },
     389             :       INT_MIN, INT_MAX, FLAGS, "mode" },
     390             : 
     391             :     { "derive_device", "Derive a new device of this type",
     392             :       OFFSET(derive_device_type), AV_OPT_TYPE_STRING,
     393             :       { .str = NULL }, 0, 0, FLAGS },
     394             :     { "reverse", "Map in reverse (create and allocate in the sink)",
     395             :       OFFSET(reverse), AV_OPT_TYPE_INT,
     396             :       { .i64 = 0 }, 0, 1, FLAGS },
     397             : 
     398             :     { NULL }
     399             : };
     400             : 
     401             : AVFILTER_DEFINE_CLASS(hwmap);
     402             : 
     403             : static const AVFilterPad hwmap_inputs[] = {
     404             :     {
     405             :         .name             = "default",
     406             :         .type             = AVMEDIA_TYPE_VIDEO,
     407             :         .get_video_buffer = hwmap_get_buffer,
     408             :         .filter_frame     = hwmap_filter_frame,
     409             :     },
     410             :     { NULL }
     411             : };
     412             : 
     413             : static const AVFilterPad hwmap_outputs[] = {
     414             :     {
     415             :         .name         = "default",
     416             :         .type         = AVMEDIA_TYPE_VIDEO,
     417             :         .config_props = hwmap_config_output,
     418             :     },
     419             :     { NULL }
     420             : };
     421             : 
     422             : AVFilter ff_vf_hwmap = {
     423             :     .name           = "hwmap",
     424             :     .description    = NULL_IF_CONFIG_SMALL("Map hardware frames"),
     425             :     .uninit         = hwmap_uninit,
     426             :     .priv_size      = sizeof(HWMapContext),
     427             :     .priv_class     = &hwmap_class,
     428             :     .query_formats  = hwmap_query_formats,
     429             :     .inputs         = hwmap_inputs,
     430             :     .outputs        = hwmap_outputs,
     431             :     .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
     432             : };

Generated by: LCOV version 1.13