Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright (c) 2013 Paul B Mahol
  3.  *
  4.  * This file is part of FFmpeg.
  5.  *
  6.  * FFmpeg is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * FFmpeg is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with FFmpeg; if not, write to the Free Software
  18.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19.  */
  20.  
  21. #include "libavutil/avassert.h"
  22. #include "libavutil/avstring.h"
  23. #include "libavutil/imgutils.h"
  24. #include "libavutil/opt.h"
  25. #include "libavutil/pixdesc.h"
  26. #include "avfilter.h"
  27. #include "internal.h"
  28. #include "framesync.h"
  29.  
  30. typedef struct InputParam {
  31.     int depth[4];
  32.     int nb_planes;
  33.     int planewidth[4];
  34.     int planeheight[4];
  35. } InputParam;
  36.  
  37. typedef struct MergePlanesContext {
  38.     const AVClass *class;
  39.     int64_t mapping;
  40.     const enum AVPixelFormat out_fmt;
  41.     int nb_inputs;
  42.     int nb_planes;
  43.     int planewidth[4];
  44.     int planeheight[4];
  45.     int map[4][2];
  46.     const AVPixFmtDescriptor *outdesc;
  47.  
  48.     FFFrameSync fs;
  49.     FFFrameSyncIn fsin[3]; /* must be immediately after fs */
  50. } MergePlanesContext;
  51.  
  52. #define OFFSET(x) offsetof(MergePlanesContext, x)
  53. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  54. static const AVOption mergeplanes_options[] = {
  55.     { "mapping", "set input to output plane mapping", OFFSET(mapping), AV_OPT_TYPE_INT, {.i64=0}, 0, 0x33333333, FLAGS },
  56.     { "format", "set output pixel format", OFFSET(out_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64=AV_PIX_FMT_YUVA444P}, .flags=FLAGS },
  57.     { NULL }
  58. };
  59.  
  60. AVFILTER_DEFINE_CLASS(mergeplanes);
  61.  
  62. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  63. {
  64.     MergePlanesContext *s = inlink->dst->priv;
  65.     return ff_framesync_filter_frame(&s->fs, inlink, in);
  66. }
  67.  
  68. static av_cold int init(AVFilterContext *ctx)
  69. {
  70.     MergePlanesContext *s = ctx->priv;
  71.     int64_t m = s->mapping;
  72.     int i, ret;
  73.  
  74.     s->outdesc = av_pix_fmt_desc_get(s->out_fmt);
  75.     if (!(s->outdesc->flags & AV_PIX_FMT_FLAG_PLANAR) ||
  76.         s->outdesc->nb_components < 2) {
  77.         av_log(ctx, AV_LOG_ERROR, "Only planar formats with more than one component are supported.\n");
  78.         return AVERROR(EINVAL);
  79.     }
  80.     s->nb_planes = av_pix_fmt_count_planes(s->out_fmt);
  81.  
  82.     for (i = s->nb_planes - 1; i >= 0; i--) {
  83.         s->map[i][0] = m & 0xf;
  84.         m >>= 4;
  85.         s->map[i][1] = m & 0xf;
  86.         m >>= 4;
  87.  
  88.         if (s->map[i][0] > 3 || s->map[i][1] > 3) {
  89.             av_log(ctx, AV_LOG_ERROR, "Mapping with out of range input and/or plane number.\n");
  90.             return AVERROR(EINVAL);
  91.         }
  92.  
  93.         s->nb_inputs = FFMAX(s->nb_inputs, s->map[i][1] + 1);
  94.     }
  95.  
  96.     av_assert0(s->nb_inputs && s->nb_inputs <= 4);
  97.  
  98.     for (i = 0; i < s->nb_inputs; i++) {
  99.         AVFilterPad pad = { 0 };
  100.  
  101.         pad.type = AVMEDIA_TYPE_VIDEO;
  102.         pad.name = av_asprintf("in%d", i);
  103.         if (!pad.name)
  104.             return AVERROR(ENOMEM);
  105.         pad.filter_frame = filter_frame;
  106.  
  107.         if ((ret = ff_insert_inpad(ctx, i, &pad)) < 0){
  108.             av_freep(&pad.name);
  109.             return ret;
  110.         }
  111.     }
  112.  
  113.     return 0;
  114. }
  115.  
  116. static int query_formats(AVFilterContext *ctx)
  117. {
  118.     MergePlanesContext *s = ctx->priv;
  119.     AVFilterFormats *formats = NULL;
  120.     int i;
  121.  
  122.     s->outdesc = av_pix_fmt_desc_get(s->out_fmt);
  123.     for (i = 0; i < AV_PIX_FMT_NB; i++) {
  124.         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i);
  125.         if (desc->comp[0].depth_minus1 == s->outdesc->comp[0].depth_minus1 &&
  126.             av_pix_fmt_count_planes(i) == desc->nb_components)
  127.             ff_add_format(&formats, i);
  128.     }
  129.  
  130.     for (i = 0; i < s->nb_inputs; i++)
  131.         ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
  132.  
  133.     formats = NULL;
  134.     ff_add_format(&formats, s->out_fmt);
  135.     ff_formats_ref(formats, &ctx->outputs[0]->in_formats);
  136.  
  137.     return 0;
  138. }
  139.  
  140. static int process_frame(FFFrameSync *fs)
  141. {
  142.     AVFilterContext *ctx = fs->parent;
  143.     AVFilterLink *outlink = ctx->outputs[0];
  144.     MergePlanesContext *s = fs->opaque;
  145.     AVFrame *in[4] = { NULL };
  146.     AVFrame *out;
  147.     int i, ret;
  148.  
  149.     for (i = 0; i < s->nb_inputs; i++) {
  150.         if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0)
  151.             return ret;
  152.     }
  153.  
  154.     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  155.     if (!out)
  156.         return AVERROR(ENOMEM);
  157.     out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
  158.  
  159.     for (i = 0; i < s->nb_planes; i++) {
  160.         const int input = s->map[i][1];
  161.         const int plane = s->map[i][0];
  162.  
  163.         av_image_copy_plane(out->data[i], out->linesize[i],
  164.                             in[input]->data[plane], in[input]->linesize[plane],
  165.                             s->planewidth[i], s->planeheight[i]);
  166.     }
  167.  
  168.     return ff_filter_frame(outlink, out);
  169. }
  170.  
  171. static int config_output(AVFilterLink *outlink)
  172. {
  173.     AVFilterContext *ctx = outlink->src;
  174.     MergePlanesContext *s = ctx->priv;
  175.     InputParam inputsp[4];
  176.     FFFrameSyncIn *in;
  177.     int i;
  178.  
  179.     ff_framesync_init(&s->fs, ctx, s->nb_inputs);
  180.     in = s->fs.in;
  181.     s->fs.opaque = s;
  182.     s->fs.on_event = process_frame;
  183.  
  184.     outlink->w = ctx->inputs[0]->w;
  185.     outlink->h = ctx->inputs[0]->h;
  186.     outlink->time_base = ctx->inputs[0]->time_base;
  187.     outlink->frame_rate = ctx->inputs[0]->frame_rate;
  188.     outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
  189.  
  190.     s->planewidth[1]  =
  191.     s->planewidth[2]  = FF_CEIL_RSHIFT(outlink->w, s->outdesc->log2_chroma_w);
  192.     s->planewidth[0]  =
  193.     s->planewidth[3]  = outlink->w;
  194.     s->planeheight[1] =
  195.     s->planeheight[2] = FF_CEIL_RSHIFT(outlink->h, s->outdesc->log2_chroma_h);
  196.     s->planeheight[0] =
  197.     s->planeheight[3] = outlink->h;
  198.  
  199.     for (i = 0; i < s->nb_inputs; i++) {
  200.         InputParam *inputp = &inputsp[i];
  201.         AVFilterLink *inlink = ctx->inputs[i];
  202.         const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(inlink->format);
  203.         int j;
  204.  
  205.         if (outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num ||
  206.             outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
  207.             av_log(ctx, AV_LOG_ERROR, "input #%d link %s SAR %d:%d "
  208.                                       "does not match output link %s SAR %d:%d\n",
  209.                                       i, ctx->input_pads[i].name,
  210.                                       inlink->sample_aspect_ratio.num,
  211.                                       inlink->sample_aspect_ratio.den,
  212.                                       ctx->output_pads[0].name,
  213.                                       outlink->sample_aspect_ratio.num,
  214.                                       outlink->sample_aspect_ratio.den);
  215.             return AVERROR(EINVAL);
  216.         }
  217.  
  218.         inputp->planewidth[1]  =
  219.         inputp->planewidth[2]  = FF_CEIL_RSHIFT(inlink->w, indesc->log2_chroma_w);
  220.         inputp->planewidth[0]  =
  221.         inputp->planewidth[3]  = inlink->w;
  222.         inputp->planeheight[1] =
  223.         inputp->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, indesc->log2_chroma_h);
  224.         inputp->planeheight[0] =
  225.         inputp->planeheight[3] = inlink->h;
  226.         inputp->nb_planes = av_pix_fmt_count_planes(inlink->format);
  227.  
  228.         for (j = 0; j < inputp->nb_planes; j++)
  229.             inputp->depth[j] = indesc->comp[j].depth_minus1 + 1;
  230.  
  231.         in[i].time_base = inlink->time_base;
  232.         in[i].sync   = 1;
  233.         in[i].before = EXT_STOP;
  234.         in[i].after  = EXT_STOP;
  235.     }
  236.  
  237.     for (i = 0; i < s->nb_planes; i++) {
  238.         const int input = s->map[i][1];
  239.         const int plane = s->map[i][0];
  240.         InputParam *inputp = &inputsp[input];
  241.  
  242.         if (plane + 1 > inputp->nb_planes) {
  243.             av_log(ctx, AV_LOG_ERROR, "input %d does not have %d plane\n",
  244.                                       input, plane);
  245.             goto fail;
  246.         }
  247.         if (s->outdesc->comp[i].depth_minus1 + 1 != inputp->depth[plane]) {
  248.             av_log(ctx, AV_LOG_ERROR, "output plane %d depth %d does not "
  249.                                       "match input %d plane %d depth %d\n",
  250.                                       i, s->outdesc->comp[i].depth_minus1 + 1,
  251.                                       input, plane, inputp->depth[plane]);
  252.             goto fail;
  253.         }
  254.         if (s->planewidth[i] != inputp->planewidth[plane]) {
  255.             av_log(ctx, AV_LOG_ERROR, "output plane %d width %d does not "
  256.                                       "match input %d plane %d width %d\n",
  257.                                       i, s->planewidth[i],
  258.                                       input, plane, inputp->planewidth[plane]);
  259.             goto fail;
  260.         }
  261.         if (s->planeheight[i] != inputp->planeheight[plane]) {
  262.             av_log(ctx, AV_LOG_ERROR, "output plane %d height %d does not "
  263.                                       "match input %d plane %d height %d\n",
  264.                                       i, s->planeheight[i],
  265.                                       input, plane, inputp->planeheight[plane]);
  266.             goto fail;
  267.         }
  268.     }
  269.  
  270.     return ff_framesync_configure(&s->fs);
  271. fail:
  272.     return AVERROR(EINVAL);
  273. }
  274.  
  275. static int request_frame(AVFilterLink *outlink)
  276. {
  277.     MergePlanesContext *s = outlink->src->priv;
  278.     return ff_framesync_request_frame(&s->fs, outlink);
  279. }
  280.  
  281. static av_cold void uninit(AVFilterContext *ctx)
  282. {
  283.     MergePlanesContext *s = ctx->priv;
  284.     int i;
  285.  
  286.     ff_framesync_uninit(&s->fs);
  287.  
  288.     for (i = 0; i < ctx->nb_inputs; i++)
  289.         av_freep(&ctx->input_pads[i].name);
  290. }
  291.  
  292. static const AVFilterPad mergeplanes_outputs[] = {
  293.     {
  294.         .name          = "default",
  295.         .type          = AVMEDIA_TYPE_VIDEO,
  296.         .config_props  = config_output,
  297.         .request_frame = request_frame,
  298.     },
  299.     { NULL }
  300. };
  301.  
  302. AVFilter avfilter_vf_mergeplanes = {
  303.     .name          = "mergeplanes",
  304.     .description   = NULL_IF_CONFIG_SMALL("Merge planes."),
  305.     .priv_size     = sizeof(MergePlanesContext),
  306.     .priv_class    = &mergeplanes_class,
  307.     .init          = init,
  308.     .uninit        = uninit,
  309.     .query_formats = query_formats,
  310.     .inputs        = NULL,
  311.     .outputs       = mergeplanes_outputs,
  312.     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
  313. };
  314.