Subversion Repositories Kolibri OS

Rev

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. } MergePlanesContext;
  50.  
  51. #define OFFSET(x) offsetof(MergePlanesContext, x)
  52. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  53. static const AVOption mergeplanes_options[] = {
  54.     { "mapping", "set input to output plane mapping", OFFSET(mapping), AV_OPT_TYPE_INT, {.i64=0}, 0, 0x33333333, FLAGS },
  55.     { "format", "set output pixel format", OFFSET(out_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64=AV_PIX_FMT_YUVA444P}, 0, INT_MAX, .flags=FLAGS },
  56.     { NULL }
  57. };
  58.  
  59. AVFILTER_DEFINE_CLASS(mergeplanes);
  60.  
  61. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  62. {
  63.     MergePlanesContext *s = inlink->dst->priv;
  64.     return ff_framesync_filter_frame(&s->fs, inlink, in);
  65. }
  66.  
  67. static av_cold int init(AVFilterContext *ctx)
  68. {
  69.     MergePlanesContext *s = ctx->priv;
  70.     int64_t m = s->mapping;
  71.     int i, ret;
  72.  
  73.     s->outdesc = av_pix_fmt_desc_get(s->out_fmt);
  74.     if (!(s->outdesc->flags & AV_PIX_FMT_FLAG_PLANAR) ||
  75.         s->outdesc->nb_components < 2) {
  76.         av_log(ctx, AV_LOG_ERROR, "Only planar formats with more than one component are supported.\n");
  77.         return AVERROR(EINVAL);
  78.     }
  79.     s->nb_planes = av_pix_fmt_count_planes(s->out_fmt);
  80.  
  81.     for (i = s->nb_planes - 1; i >= 0; i--) {
  82.         s->map[i][0] = m & 0xf;
  83.         m >>= 4;
  84.         s->map[i][1] = m & 0xf;
  85.         m >>= 4;
  86.  
  87.         if (s->map[i][0] > 3 || s->map[i][1] > 3) {
  88.             av_log(ctx, AV_LOG_ERROR, "Mapping with out of range input and/or plane number.\n");
  89.             return AVERROR(EINVAL);
  90.         }
  91.  
  92.         s->nb_inputs = FFMAX(s->nb_inputs, s->map[i][1] + 1);
  93.     }
  94.  
  95.     av_assert0(s->nb_inputs && s->nb_inputs <= 4);
  96.  
  97.     for (i = 0; i < s->nb_inputs; i++) {
  98.         AVFilterPad pad = { 0 };
  99.  
  100.         pad.type = AVMEDIA_TYPE_VIDEO;
  101.         pad.name = av_asprintf("in%d", i);
  102.         if (!pad.name)
  103.             return AVERROR(ENOMEM);
  104.         pad.filter_frame = filter_frame;
  105.  
  106.         if ((ret = ff_insert_inpad(ctx, i, &pad)) < 0){
  107.             av_freep(&pad.name);
  108.             return ret;
  109.         }
  110.     }
  111.  
  112.     return 0;
  113. }
  114.  
  115. static int query_formats(AVFilterContext *ctx)
  116. {
  117.     MergePlanesContext *s = ctx->priv;
  118.     AVFilterFormats *formats = NULL;
  119.     int i;
  120.  
  121.     s->outdesc = av_pix_fmt_desc_get(s->out_fmt);
  122.     for (i = 0; av_pix_fmt_desc_get(i); i++) {
  123.         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i);
  124.         if (desc->comp[0].depth_minus1 == s->outdesc->comp[0].depth_minus1 &&
  125.             av_pix_fmt_count_planes(i) == desc->nb_components)
  126.             ff_add_format(&formats, i);
  127.     }
  128.  
  129.     for (i = 0; i < s->nb_inputs; i++)
  130.         ff_formats_ref(formats, &ctx->inputs[i]->out_formats);
  131.  
  132.     formats = NULL;
  133.     ff_add_format(&formats, s->out_fmt);
  134.     ff_formats_ref(formats, &ctx->outputs[0]->in_formats);
  135.  
  136.     return 0;
  137. }
  138.  
  139. static int process_frame(FFFrameSync *fs)
  140. {
  141.     AVFilterContext *ctx = fs->parent;
  142.     AVFilterLink *outlink = ctx->outputs[0];
  143.     MergePlanesContext *s = fs->opaque;
  144.     AVFrame *in[4] = { NULL };
  145.     AVFrame *out;
  146.     int i, ret;
  147.  
  148.     for (i = 0; i < s->nb_inputs; i++) {
  149.         if ((ret = ff_framesync_get_frame(&s->fs, i, &in[i], 0)) < 0)
  150.             return ret;
  151.     }
  152.  
  153.     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  154.     if (!out)
  155.         return AVERROR(ENOMEM);
  156.     out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base);
  157.  
  158.     for (i = 0; i < s->nb_planes; i++) {
  159.         const int input = s->map[i][1];
  160.         const int plane = s->map[i][0];
  161.  
  162.         av_image_copy_plane(out->data[i], out->linesize[i],
  163.                             in[input]->data[plane], in[input]->linesize[plane],
  164.                             s->planewidth[i], s->planeheight[i]);
  165.     }
  166.  
  167.     return ff_filter_frame(outlink, out);
  168. }
  169.  
  170. static int config_output(AVFilterLink *outlink)
  171. {
  172.     AVFilterContext *ctx = outlink->src;
  173.     MergePlanesContext *s = ctx->priv;
  174.     InputParam inputsp[4];
  175.     FFFrameSyncIn *in;
  176.     int i, ret;
  177.  
  178.     if ((ret = ff_framesync_init(&s->fs, ctx, s->nb_inputs)) < 0)
  179.         return ret;
  180.  
  181.     in = s->fs.in;
  182.     s->fs.opaque = s;
  183.     s->fs.on_event = process_frame;
  184.  
  185.     outlink->w = ctx->inputs[0]->w;
  186.     outlink->h = ctx->inputs[0]->h;
  187.     outlink->time_base = ctx->inputs[0]->time_base;
  188.     outlink->frame_rate = ctx->inputs[0]->frame_rate;
  189.     outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio;
  190.  
  191.     s->planewidth[1]  =
  192.     s->planewidth[2]  = FF_CEIL_RSHIFT(outlink->w, s->outdesc->log2_chroma_w);
  193.     s->planewidth[0]  =
  194.     s->planewidth[3]  = outlink->w;
  195.     s->planeheight[1] =
  196.     s->planeheight[2] = FF_CEIL_RSHIFT(outlink->h, s->outdesc->log2_chroma_h);
  197.     s->planeheight[0] =
  198.     s->planeheight[3] = outlink->h;
  199.  
  200.     for (i = 0; i < s->nb_inputs; i++) {
  201.         InputParam *inputp = &inputsp[i];
  202.         AVFilterLink *inlink = ctx->inputs[i];
  203.         const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(inlink->format);
  204.         int j;
  205.  
  206.         if (outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num ||
  207.             outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) {
  208.             av_log(ctx, AV_LOG_ERROR, "input #%d link %s SAR %d:%d "
  209.                                       "does not match output link %s SAR %d:%d\n",
  210.                                       i, ctx->input_pads[i].name,
  211.                                       inlink->sample_aspect_ratio.num,
  212.                                       inlink->sample_aspect_ratio.den,
  213.                                       ctx->output_pads[0].name,
  214.                                       outlink->sample_aspect_ratio.num,
  215.                                       outlink->sample_aspect_ratio.den);
  216.             return AVERROR(EINVAL);
  217.         }
  218.  
  219.         inputp->planewidth[1]  =
  220.         inputp->planewidth[2]  = FF_CEIL_RSHIFT(inlink->w, indesc->log2_chroma_w);
  221.         inputp->planewidth[0]  =
  222.         inputp->planewidth[3]  = inlink->w;
  223.         inputp->planeheight[1] =
  224.         inputp->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, indesc->log2_chroma_h);
  225.         inputp->planeheight[0] =
  226.         inputp->planeheight[3] = inlink->h;
  227.         inputp->nb_planes = av_pix_fmt_count_planes(inlink->format);
  228.  
  229.         for (j = 0; j < inputp->nb_planes; j++)
  230.             inputp->depth[j] = indesc->comp[j].depth_minus1 + 1;
  231.  
  232.         in[i].time_base = inlink->time_base;
  233.         in[i].sync   = 1;
  234.         in[i].before = EXT_STOP;
  235.         in[i].after  = EXT_STOP;
  236.     }
  237.  
  238.     for (i = 0; i < s->nb_planes; i++) {
  239.         const int input = s->map[i][1];
  240.         const int plane = s->map[i][0];
  241.         InputParam *inputp = &inputsp[input];
  242.  
  243.         if (plane + 1 > inputp->nb_planes) {
  244.             av_log(ctx, AV_LOG_ERROR, "input %d does not have %d plane\n",
  245.                                       input, plane);
  246.             goto fail;
  247.         }
  248.         if (s->outdesc->comp[i].depth_minus1 + 1 != inputp->depth[plane]) {
  249.             av_log(ctx, AV_LOG_ERROR, "output plane %d depth %d does not "
  250.                                       "match input %d plane %d depth %d\n",
  251.                                       i, s->outdesc->comp[i].depth_minus1 + 1,
  252.                                       input, plane, inputp->depth[plane]);
  253.             goto fail;
  254.         }
  255.         if (s->planewidth[i] != inputp->planewidth[plane]) {
  256.             av_log(ctx, AV_LOG_ERROR, "output plane %d width %d does not "
  257.                                       "match input %d plane %d width %d\n",
  258.                                       i, s->planewidth[i],
  259.                                       input, plane, inputp->planewidth[plane]);
  260.             goto fail;
  261.         }
  262.         if (s->planeheight[i] != inputp->planeheight[plane]) {
  263.             av_log(ctx, AV_LOG_ERROR, "output plane %d height %d does not "
  264.                                       "match input %d plane %d height %d\n",
  265.                                       i, s->planeheight[i],
  266.                                       input, plane, inputp->planeheight[plane]);
  267.             goto fail;
  268.         }
  269.     }
  270.  
  271.     return ff_framesync_configure(&s->fs);
  272. fail:
  273.     return AVERROR(EINVAL);
  274. }
  275.  
  276. static int request_frame(AVFilterLink *outlink)
  277. {
  278.     MergePlanesContext *s = outlink->src->priv;
  279.     return ff_framesync_request_frame(&s->fs, outlink);
  280. }
  281.  
  282. static av_cold void uninit(AVFilterContext *ctx)
  283. {
  284.     MergePlanesContext *s = ctx->priv;
  285.     int i;
  286.  
  287.     ff_framesync_uninit(&s->fs);
  288.  
  289.     for (i = 0; i < ctx->nb_inputs; i++)
  290.         av_freep(&ctx->input_pads[i].name);
  291. }
  292.  
  293. static const AVFilterPad mergeplanes_outputs[] = {
  294.     {
  295.         .name          = "default",
  296.         .type          = AVMEDIA_TYPE_VIDEO,
  297.         .config_props  = config_output,
  298.         .request_frame = request_frame,
  299.     },
  300.     { NULL }
  301. };
  302.  
  303. AVFilter ff_vf_mergeplanes = {
  304.     .name          = "mergeplanes",
  305.     .description   = NULL_IF_CONFIG_SMALL("Merge planes."),
  306.     .priv_size     = sizeof(MergePlanesContext),
  307.     .priv_class    = &mergeplanes_class,
  308.     .init          = init,
  309.     .uninit        = uninit,
  310.     .query_formats = query_formats,
  311.     .inputs        = NULL,
  312.     .outputs       = mergeplanes_outputs,
  313.     .flags         = AVFILTER_FLAG_DYNAMIC_INPUTS,
  314. };
  315.