Subversion Repositories Kolibri OS

Rev

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/imgutils.h"
  31. #include "libavutil/internal.h"
  32. #include "libavutil/intreadwrite.h"
  33. #include "libavutil/opt.h"
  34. #include "libavutil/pixdesc.h"
  35.  
  36. #include "avfilter.h"
  37. #include "formats.h"
  38. #include "internal.h"
  39. #include "video.h"
  40.  
  41. typedef enum {
  42.     TRANSPOSE_PT_TYPE_NONE,
  43.     TRANSPOSE_PT_TYPE_LANDSCAPE,
  44.     TRANSPOSE_PT_TYPE_PORTRAIT,
  45. } PassthroughType;
  46.  
  47. enum TransposeDir {
  48.     TRANSPOSE_CCLOCK_FLIP,
  49.     TRANSPOSE_CLOCK,
  50.     TRANSPOSE_CCLOCK,
  51.     TRANSPOSE_CLOCK_FLIP,
  52. };
  53.  
  54. typedef struct TransContext {
  55.     const AVClass *class;
  56.     int hsub, vsub;
  57.     int pixsteps[4];
  58.  
  59.     int passthrough;    ///< PassthroughType, landscape passthrough mode enabled
  60.     int dir;            ///< TransposeDir
  61. } TransContext;
  62.  
  63. static int query_formats(AVFilterContext *ctx)
  64. {
  65.     AVFilterFormats *pix_fmts = NULL;
  66.     int fmt;
  67.  
  68.     for (fmt = 0; av_pix_fmt_desc_get(fmt); fmt++) {
  69.         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
  70.         if (!(desc->flags & AV_PIX_FMT_FLAG_PAL ||
  71.               desc->flags & AV_PIX_FMT_FLAG_HWACCEL ||
  72.               desc->flags & AV_PIX_FMT_FLAG_BITSTREAM ||
  73.               desc->log2_chroma_w != desc->log2_chroma_h))
  74.             ff_add_format(&pix_fmts, fmt);
  75.     }
  76.  
  77.  
  78.     return ff_set_common_formats(ctx, pix_fmts);
  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 },
  116.                                                 inlink->sample_aspect_ratio);
  117.     else
  118.         outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
  119.  
  120.     av_log(ctx, AV_LOG_VERBOSE,
  121.            "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n",
  122.            inlink->w, inlink->h, trans->dir, outlink->w, outlink->h,
  123.            trans->dir == 1 || trans->dir == 3 ? "clockwise" : "counterclockwise",
  124.            trans->dir == 0 || trans->dir == 3);
  125.     return 0;
  126. }
  127.  
  128. static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h)
  129. {
  130.     TransContext *trans = inlink->dst->priv;
  131.  
  132.     return trans->passthrough ?
  133.         ff_null_get_video_buffer   (inlink, w, h) :
  134.         ff_default_get_video_buffer(inlink, w, h);
  135. }
  136.  
  137. typedef struct ThreadData {
  138.     AVFrame *in, *out;
  139. } ThreadData;
  140.  
  141. static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr,
  142.                         int nb_jobs)
  143. {
  144.     TransContext *trans = ctx->priv;
  145.     ThreadData *td = arg;
  146.     AVFrame *out = td->out;
  147.     AVFrame *in = td->in;
  148.     int plane;
  149.  
  150.     for (plane = 0; out->data[plane]; plane++) {
  151.         int hsub    = plane == 1 || plane == 2 ? trans->hsub : 0;
  152.         int vsub    = plane == 1 || plane == 2 ? trans->vsub : 0;
  153.         int pixstep = trans->pixsteps[plane];
  154.         int inh     = FF_CEIL_RSHIFT(in->height, vsub);
  155.         int outw    = FF_CEIL_RSHIFT(out->width,  hsub);
  156.         int outh    = FF_CEIL_RSHIFT(out->height, vsub);
  157.         int start   = (outh *  jobnr   ) / nb_jobs;
  158.         int end     = (outh * (jobnr+1)) / nb_jobs;
  159.         uint8_t *dst, *src;
  160.         int dstlinesize, srclinesize;
  161.         int x, y;
  162.  
  163.         dstlinesize = out->linesize[plane];
  164.         dst         = out->data[plane] + start * dstlinesize;
  165.         src         = in->data[plane];
  166.         srclinesize = in->linesize[plane];
  167.  
  168.         if (trans->dir & 1) {
  169.             src         += in->linesize[plane] * (inh - 1);
  170.             srclinesize *= -1;
  171.         }
  172.  
  173.         if (trans->dir & 2) {
  174.             dst          = out->data[plane] + dstlinesize * (outh - start - 1);
  175.             dstlinesize *= -1;
  176.         }
  177.  
  178.         switch (pixstep) {
  179.         case 1:
  180.             for (y = start; y < end; y++, dst += dstlinesize)
  181.                 for (x = 0; x < outw; x++)
  182.                     dst[x] = src[x * srclinesize + y];
  183.             break;
  184.         case 2:
  185.             for (y = start; y < end; y++, dst += dstlinesize) {
  186.                 for (x = 0; x < outw; x++)
  187.                     *((uint16_t *)(dst + 2 * x)) =
  188.                         *((uint16_t *)(src + x * srclinesize + y * 2));
  189.             }
  190.             break;
  191.         case 3:
  192.             for (y = start; y < end; y++, dst += dstlinesize) {
  193.                 for (x = 0; x < outw; x++) {
  194.                     int32_t v = AV_RB24(src + x * srclinesize + y * 3);
  195.                     AV_WB24(dst + 3 * x, v);
  196.                 }
  197.             }
  198.             break;
  199.         case 4:
  200.             for (y = start; y < end; y++, dst += dstlinesize) {
  201.                 for (x = 0; x < outw; x++)
  202.                     *((uint32_t *)(dst + 4 * x)) =
  203.                         *((uint32_t *)(src + x * srclinesize + y * 4));
  204.             }
  205.             break;
  206.         case 6:
  207.             for (y = start; y < end; y++, dst += dstlinesize) {
  208.                 for (x = 0; x < outw; x++) {
  209.                     int64_t v = AV_RB48(src + x * srclinesize + y*6);
  210.                     AV_WB48(dst + 6*x, v);
  211.                 }
  212.             }
  213.             break;
  214.         case 8:
  215.             for (y = start; y < end; y++, dst += dstlinesize) {
  216.                 for (x = 0; x < outw; x++)
  217.                     *((uint64_t *)(dst + 8*x)) = *((uint64_t *)(src + x * srclinesize + y*8));
  218.             }
  219.             break;
  220.         }
  221.     }
  222.  
  223.     return 0;
  224. }
  225.  
  226. static int filter_frame(AVFilterLink *inlink, AVFrame *in)
  227. {
  228.     AVFilterContext *ctx = inlink->dst;
  229.     TransContext *trans = ctx->priv;
  230.     AVFilterLink *outlink = ctx->outputs[0];
  231.     ThreadData td;
  232.     AVFrame *out;
  233.  
  234.     if (trans->passthrough)
  235.         return ff_filter_frame(outlink, in);
  236.  
  237.     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
  238.     if (!out) {
  239.         av_frame_free(&in);
  240.         return AVERROR(ENOMEM);
  241.     }
  242.     av_frame_copy_props(out, in);
  243.  
  244.     if (in->sample_aspect_ratio.num == 0) {
  245.         out->sample_aspect_ratio = in->sample_aspect_ratio;
  246.     } else {
  247.         out->sample_aspect_ratio.num = in->sample_aspect_ratio.den;
  248.         out->sample_aspect_ratio.den = in->sample_aspect_ratio.num;
  249.     }
  250.  
  251.     td.in = in, td.out = out;
  252.     ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outlink->h, ctx->graph->nb_threads));
  253.     av_frame_free(&in);
  254.     return ff_filter_frame(outlink, out);
  255. }
  256.  
  257. #define OFFSET(x) offsetof(TransContext, x)
  258. #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
  259.  
  260. static const AVOption transpose_options[] = {
  261.     { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, "dir" },
  262.         { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .unit = "dir" },
  263.         { "clock",       "rotate clockwise",                            0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK       }, .unit = "dir" },
  264.         { "cclock",      "rotate counter-clockwise",                    0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK      }, .unit = "dir" },
  265.         { "clock_flip",  "rotate clockwise with vertical flip",         0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP  }, .unit = "dir" },
  266.  
  267.     { "passthrough", "do not apply transposition if the input matches the specified geometry",
  268.       OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE},  0, INT_MAX, FLAGS, "passthrough" },
  269.         { "none",      "always apply transposition",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE},      INT_MIN, INT_MAX, FLAGS, "passthrough" },
  270.         { "portrait",  "preserve portrait geometry",   0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT},  INT_MIN, INT_MAX, FLAGS, "passthrough" },
  271.         { "landscape", "preserve landscape geometry",  0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
  272.  
  273.     { NULL }
  274. };
  275.  
  276. AVFILTER_DEFINE_CLASS(transpose);
  277.  
  278. static const AVFilterPad avfilter_vf_transpose_inputs[] = {
  279.     {
  280.         .name         = "default",
  281.         .type         = AVMEDIA_TYPE_VIDEO,
  282.         .get_video_buffer = get_video_buffer,
  283.         .filter_frame = filter_frame,
  284.     },
  285.     { NULL }
  286. };
  287.  
  288. static const AVFilterPad avfilter_vf_transpose_outputs[] = {
  289.     {
  290.         .name         = "default",
  291.         .config_props = config_props_output,
  292.         .type         = AVMEDIA_TYPE_VIDEO,
  293.     },
  294.     { NULL }
  295. };
  296.  
  297. AVFilter ff_vf_transpose = {
  298.     .name          = "transpose",
  299.     .description   = NULL_IF_CONFIG_SMALL("Transpose input video."),
  300.     .priv_size     = sizeof(TransContext),
  301.     .priv_class    = &transpose_class,
  302.     .query_formats = query_formats,
  303.     .inputs        = avfilter_vf_transpose_inputs,
  304.     .outputs       = avfilter_vf_transpose_outputs,
  305.     .flags         = AVFILTER_FLAG_SLICE_THREADS,
  306. };
  307.