Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright (c) 2010 Stefano Sabatini
  3.  * Copyright (c) 2008 Vitor Sessak
  4.  *
  5.  * This file is part of FFmpeg.
  6.  *
  7.  * FFmpeg is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  *
  12.  * FFmpeg is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with FFmpeg; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20.  */
  21.  
  22. /**
  23.  * @file
  24.  * transposition filter
  25.  * Based on MPlayer libmpcodecs/vf_rotate.c.
  26.  */
  27.  
  28. #include <stdio.h>
  29.  
  30. #include "libavutil/intreadwrite.h"
  31. #include "libavutil/pixdesc.h"
  32. #include "libavutil/imgutils.h"
  33. #include "libavutil/internal.h"
  34. #include "libavutil/opt.h"
  35. #include "avfilter.h"
  36. #include "formats.h"
  37. #include "internal.h"
  38. #include "video.h"
  39.  
  40. typedef enum {
  41.     TRANSPOSE_PT_TYPE_NONE,
  42.     TRANSPOSE_PT_TYPE_LANDSCAPE,
  43.     TRANSPOSE_PT_TYPE_PORTRAIT,
  44. } PassthroughType;
  45.  
  46. enum TransposeDir {
  47.     TRANSPOSE_CCLOCK_FLIP,
  48.     TRANSPOSE_CLOCK,
  49.     TRANSPOSE_CCLOCK,
  50.     TRANSPOSE_CLOCK_FLIP,
  51. };
  52.  
  53. typedef struct {
  54.     const AVClass *class;
  55.     int hsub, vsub;
  56.     int pixsteps[4];
  57.  
  58.     PassthroughType passthrough; ///< landscape passthrough mode enabled
  59.     enum TransposeDir dir;
  60. } TransContext;
  61.  
  62. static int query_formats(AVFilterContext *ctx)
  63. {
  64.     AVFilterFormats *pix_fmts = NULL;
  65.     int fmt;
  66.  
  67.     for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) {
  68.         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
  69.         if (!(desc->flags & AV_PIX_FMT_FLAG_PAL ||
  70.               desc->flags & AV_PIX_FMT_FLAG_HWACCEL ||
  71.               desc->flags & AV_PIX_FMT_FLAG_BITSTREAM ||
  72.               desc->log2_chroma_w != desc->log2_chroma_h))
  73.             ff_add_format(&pix_fmts, fmt);
  74.     }
  75.  
  76.  
  77.     ff_set_common_formats(ctx, pix_fmts);
  78.     return 0;
  79. }
  80.  
  81. static int config_props_output(AVFilterLink *outlink)
  82. {
  83.     AVFilterContext *ctx = outlink->src;
  84.     TransContext *trans = ctx->priv;
  85.     AVFilterLink *inlink = ctx->inputs[0];
  86.     const AVPixFmtDescriptor *desc_out = av_pix_fmt_desc_get(outlink->format);
  87.     const AVPixFmtDescriptor *desc_in  = av_pix_fmt_desc_get(inlink->format);
  88.  
  89.     if (trans->dir&4) {
  90.         av_log(ctx, AV_LOG_WARNING,
  91.                "dir values greater than 3 are deprecated, use the passthrough option instead\n");
  92.         trans->dir &= 3;
  93.         trans->passthrough = TRANSPOSE_PT_TYPE_LANDSCAPE;
  94.     }
  95.  
  96.     if ((inlink->w >= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
  97.         (inlink->w <= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
  98.         av_log(ctx, AV_LOG_VERBOSE,
  99.                "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
  100.                inlink->w, inlink->h, inlink->w, inlink->h);
  101.         return 0;
  102.     } else {
  103.         trans->passthrough = TRANSPOSE_PT_TYPE_NONE;
  104.     }
  105.  
  106.     trans->hsub = desc_in->log2_chroma_w;
  107.     trans->vsub = desc_in->log2_chroma_h;
  108.  
  109.     av_image_fill_max_pixsteps(trans->pixsteps, NULL, desc_out);
  110.  
  111.     outlink->w = inlink->h;
  112.     outlink->h = inlink->w;
  113.  
  114.     if (inlink->sample_aspect_ratio.num){
  115.         outlink->sample_aspect_ratio = av_div_q((AVRational){1,1}, inlink->sample_aspect_ratio);
  116.     } else
  117.         outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
  118.  
  119.     av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n",
  120.            inlink->w, inlink->h, trans->dir, outlink->w, outlink->h,
  121.            trans->dir == 1 || trans->dir == 3 ? "clockwise" : "counterclockwise",
  122.            trans->dir == 0 || trans->dir == 3);
  123.     return 0;
  124. }
  125.  
  126. static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
  127. {
  128.     TransContext *trans = inlink->dst->priv;
  129.  
  130.     return trans->passthrough ?
  131.         ff_null_get_video_buffer   (inlink, w, h) :
  132.         ff_default_get_video_buffer(inlink, w, h);
  133. }
  134.  
  135. typedef struct ThreadData {
  136.     AVFrame *in, *out;
  137. } ThreadData;
  138.  
  139. static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
  140.                         int nb_jobs)
  141. {
  142.     TransContext *trans = ctx->priv;
  143.     ThreadData *td = arg;
  144.     AVFrame *out = td->out;
  145.     AVFrame *in = td->in;
  146.     int plane;
  147.  
  148.     for (plane = 0; out->data[plane]; plane++) {
  149.         int hsub = plane == 1 || plane == 2 ? trans->hsub : 0;
  150.         int vsub = plane == 1 || plane == 2 ? trans->vsub : 0;
  151.         int pixstep = trans->pixsteps[plane];
  152.         int inh  = in->height  >> vsub;
  153.         int outw = FF_CEIL_RSHIFT(out->width,  hsub);
  154.         int outh = FF_CEIL_RSHIFT(out->height, vsub);
  155.         int start = (outh *  jobnr   ) / nb_jobs;
  156.         int end   = (outh * (jobnr+1)) / nb_jobs;
  157.         uint8_t *dst, *src;
  158.         int dstlinesize, srclinesize;
  159.         int x, y;
  160.  
  161.         dstlinesize = out->linesize[plane];
  162.         dst = out->data[plane] + start * dstlinesize;
  163.         src = in->data[plane];
  164.         srclinesize = in->linesize[plane];
  165.  
  166.         if (trans->dir&1) {
  167.             src +=  in->linesize[plane] * (inh-1);
  168.             srclinesize *= -1;
  169.         }
  170.  
  171.         if (trans->dir&2) {
  172.             dst = out->data[plane] + dstlinesize * (outh-start-1);
  173.             dstlinesize *= -1;
  174.         }
  175.  
  176.         switch (pixstep) {
  177.         case 1:
  178.             for (y = start; y < end; y++, dst += dstlinesize)
  179.                 for (x = 0; x < outw; x++)
  180.                     dst[x] = src[x*srclinesize + y];
  181.             break;
  182.         case 2:
  183.             for (y = start; y < end; y++, dst += dstlinesize) {
  184.                 for (x = 0; x < outw; x++)
  185.                     *((uint16_t *)(dst + 2*x)) = *((uint16_t *)(src + x*srclinesize + y*2));
  186.             }
  187.             break;
  188.         case 3:
  189.             for (y = start; y < end; y++, dst += dstlinesize) {
  190.                 for (x = 0; x < outw; x++) {
  191.                     int32_t v = AV_RB24(src + x*srclinesize + y*3);
  192.                     AV_WB24(dst + 3*x, v);
  193.                 }
  194.             }
  195.             break;
  196.         case 4:
  197.             for (y = start; y < end; y++, dst += dstlinesize) {
  198.                 for (x = 0; x < outw; x++)
  199.                     *((uint32_t *)(dst + 4*x)) = *((uint32_t *)(src + x*srclinesize + y*4));
  200.             }
  201.             break;
  202.         case 6:
  203.             for (y = start; y < end; y++, dst += dstlinesize) {
  204.                 for (x = 0; x < outw; x++) {
  205.                     int64_t v = AV_RB48(src + x*srclinesize + y*6);
  206.                     AV_WB48(dst + 6*x, v);
  207.                 }
  208.             }
  209.             break;
  210.         case 8:
  211.             for (y = start; y < end; y++, dst += dstlinesize) {
  212.                 for (x = 0; x < outw; x++)
  213.                     *((uint64_t *)(dst + 8*x)) = *((uint64_t *)(src + x*srclinesize + y*8));
  214.             }
  215.             break;
  216.         }
  217.     }
  218.  
  219.     return 0;
  220. }
  221.  
  222. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  223. {
  224.     AVFilterContext *ctx = inlink->dst;
  225.     TransContext *trans = ctx->priv;
  226.     AVFilterLink *outlink = ctx->outputs[0];
  227.     ThreadData td;
  228.     AVFrame *out;
  229.  
  230.     if (trans->passthrough)
  231.         return ff_filter_frame(outlink, in);
  232.  
  233.     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  234.     if (!out) {
  235.         av_frame_free(&in);
  236.         return AVERROR(ENOMEM);
  237.     }
  238.     av_frame_copy_props(out, in);
  239.  
  240.     if (in->sample_aspect_ratio.num == 0) {
  241.         out->sample_aspect_ratio = in->sample_aspect_ratio;
  242.     } else {
  243.         out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
  244.         out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
  245.     }
  246.  
  247.     td.in = in, td.out = out;
  248.     ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ctx->graph->nb_threads));
  249.     av_frame_free(&in);
  250.     return ff_filter_frame(outlink, out);
  251. }
  252.  
  253. #define OFFSET(x) offsetof(TransContext, x)
  254. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  255.  
  256. static const AVOption transpose_options[] = {
  257.     { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, "dir" },
  258.         { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .unit = "dir" },
  259.         { "clock",       "rotate clockwise",                            0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK       }, .unit = "dir" },
  260.         { "cclock",      "rotate counter-clockwise",                    0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK      }, .unit = "dir" },
  261.         { "clock_flip",  "rotate clockwise with vertical flip",         0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP  }, .unit = "dir" },
  262.  
  263.     { "passthrough", "do not apply transposition if the input matches the specified geometry",
  264.       OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE},  0, INT_MAX, FLAGS, "passthrough" },
  265.         { "none",      "always apply transposition",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE},      INT_MIN, INT_MAX, FLAGS, "passthrough" },
  266.         { "portrait",  "preserve portrait geometry",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS, "passthrough" },
  267.         { "landscape", "preserve landscape geometry",  0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
  268.  
  269.     { NULL }
  270. };
  271.  
  272. AVFILTER_DEFINE_CLASS(transpose);
  273.  
  274. static const AVFilterPad avfilter_vf_transpose_inputs[] = {
  275.     {
  276.         .name             = "default",
  277.         .type             = AVMEDIA_TYPE_VIDEO,
  278.         .get_video_buffer = get_video_buffer,
  279.         .filter_frame     = filter_frame,
  280.     },
  281.     { NULL }
  282. };
  283.  
  284. static const AVFilterPad avfilter_vf_transpose_outputs[] = {
  285.     {
  286.         .name         = "default",
  287.         .config_props = config_props_output,
  288.         .type         = AVMEDIA_TYPE_VIDEO,
  289.     },
  290.     { NULL }
  291. };
  292.  
  293. AVFilter avfilter_vf_transpose = {
  294.     .name          = "transpose",
  295.     .description   = NULL_IF_CONFIG_SMALL("Transpose input video."),
  296.     .priv_size     = sizeof(TransContext),
  297.     .priv_class    = &transpose_class,
  298.     .query_formats = query_formats,
  299.     .inputs        = avfilter_vf_transpose_inputs,
  300.     .outputs       = avfilter_vf_transpose_outputs,
  301.     .flags         = AVFILTER_FLAG_SLICE_THREADS,
  302. };
  303.