yading@11: /* yading@11: * Copyright (c) 2010 Stefano Sabatini yading@11: * Copyright (c) 2008 Vitor Sessak yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * transposition filter yading@11: * Based on MPlayer libmpcodecs/vf_rotate.c. yading@11: */ yading@11: yading@11: #include yading@11: yading@11: #include "libavutil/intreadwrite.h" yading@11: #include "libavutil/opt.h" yading@11: #include "libavutil/pixdesc.h" yading@11: #include "libavutil/imgutils.h" yading@11: #include "libavutil/internal.h" yading@11: #include "libavutil/opt.h" yading@11: #include "avfilter.h" yading@11: #include "formats.h" yading@11: #include "internal.h" yading@11: #include "video.h" yading@11: yading@11: typedef enum { yading@11: TRANSPOSE_PT_TYPE_NONE, yading@11: TRANSPOSE_PT_TYPE_LANDSCAPE, yading@11: TRANSPOSE_PT_TYPE_PORTRAIT, yading@11: } PassthroughType; yading@11: yading@11: enum TransposeDir { yading@11: TRANSPOSE_CCLOCK_FLIP, yading@11: TRANSPOSE_CLOCK, yading@11: TRANSPOSE_CCLOCK, yading@11: TRANSPOSE_CLOCK_FLIP, yading@11: }; yading@11: yading@11: typedef struct { yading@11: const AVClass *class; yading@11: int hsub, vsub; yading@11: int pixsteps[4]; yading@11: yading@11: PassthroughType passthrough; ///< landscape passthrough mode enabled yading@11: enum TransposeDir dir; yading@11: } TransContext; yading@11: yading@11: static int query_formats(AVFilterContext *ctx) yading@11: { yading@11: AVFilterFormats *pix_fmts = NULL; yading@11: int fmt; yading@11: yading@11: for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { yading@11: const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); yading@11: if (!(desc->flags & PIX_FMT_PAL || yading@11: desc->flags & PIX_FMT_HWACCEL || yading@11: desc->flags & PIX_FMT_BITSTREAM || yading@11: desc->log2_chroma_w != desc->log2_chroma_h)) yading@11: ff_add_format(&pix_fmts, fmt); yading@11: } yading@11: yading@11: yading@11: ff_set_common_formats(ctx, pix_fmts); yading@11: return 0; yading@11: } yading@11: yading@11: static int config_props_output(AVFilterLink *outlink) yading@11: { yading@11: AVFilterContext *ctx = outlink->src; yading@11: TransContext *trans = ctx->priv; yading@11: AVFilterLink *inlink = ctx->inputs[0]; yading@11: const AVPixFmtDescriptor *desc_out = av_pix_fmt_desc_get(outlink->format); yading@11: const AVPixFmtDescriptor *desc_in = av_pix_fmt_desc_get(inlink->format); yading@11: yading@11: if (trans->dir&4) { yading@11: av_log(ctx, AV_LOG_WARNING, yading@11: "dir values greater than 3 are deprecated, use the passthrough option instead\n"); yading@11: trans->dir &= 3; yading@11: trans->passthrough = TRANSPOSE_PT_TYPE_LANDSCAPE; yading@11: } yading@11: yading@11: if ((inlink->w >= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) || yading@11: (inlink->w <= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) { yading@11: av_log(ctx, AV_LOG_VERBOSE, yading@11: "w:%d h:%d -> w:%d h:%d (passthrough mode)\n", yading@11: inlink->w, inlink->h, inlink->w, inlink->h); yading@11: return 0; yading@11: } else { yading@11: trans->passthrough = TRANSPOSE_PT_TYPE_NONE; yading@11: } yading@11: yading@11: trans->hsub = desc_in->log2_chroma_w; yading@11: trans->vsub = desc_in->log2_chroma_h; yading@11: yading@11: av_image_fill_max_pixsteps(trans->pixsteps, NULL, desc_out); yading@11: yading@11: outlink->w = inlink->h; yading@11: outlink->h = inlink->w; yading@11: yading@11: if (inlink->sample_aspect_ratio.num){ yading@11: outlink->sample_aspect_ratio = av_div_q((AVRational){1,1}, inlink->sample_aspect_ratio); yading@11: } else yading@11: outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; yading@11: yading@11: av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n", yading@11: inlink->w, inlink->h, trans->dir, outlink->w, outlink->h, yading@11: trans->dir == 1 || trans->dir == 3 ? "clockwise" : "counterclockwise", yading@11: trans->dir == 0 || trans->dir == 3); yading@11: return 0; yading@11: } yading@11: yading@11: static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) yading@11: { yading@11: TransContext *trans = inlink->dst->priv; yading@11: yading@11: return trans->passthrough ? yading@11: ff_null_get_video_buffer (inlink, w, h) : yading@11: ff_default_get_video_buffer(inlink, w, h); yading@11: } yading@11: yading@11: static int filter_frame(AVFilterLink *inlink, AVFrame *in) yading@11: { yading@11: TransContext *trans = inlink->dst->priv; yading@11: AVFilterLink *outlink = inlink->dst->outputs[0]; yading@11: AVFrame *out; yading@11: int plane; yading@11: yading@11: if (trans->passthrough) yading@11: return ff_filter_frame(outlink, in); yading@11: yading@11: out = ff_get_video_buffer(outlink, outlink->w, outlink->h); yading@11: if (!out) { yading@11: av_frame_free(&in); yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: yading@11: out->pts = in->pts; yading@11: yading@11: if (in->sample_aspect_ratio.num == 0) { yading@11: out->sample_aspect_ratio = in->sample_aspect_ratio; yading@11: } else { yading@11: out->sample_aspect_ratio.num = in->sample_aspect_ratio.den; yading@11: out->sample_aspect_ratio.den = in->sample_aspect_ratio.num; yading@11: } yading@11: yading@11: for (plane = 0; out->data[plane]; plane++) { yading@11: int hsub = plane == 1 || plane == 2 ? trans->hsub : 0; yading@11: int vsub = plane == 1 || plane == 2 ? trans->vsub : 0; yading@11: int pixstep = trans->pixsteps[plane]; yading@11: int inh = in->height >> vsub; yading@11: int outw = out->width >> hsub; yading@11: int outh = out->height >> vsub; yading@11: uint8_t *dst, *src; yading@11: int dstlinesize, srclinesize; yading@11: int x, y; yading@11: yading@11: dst = out->data[plane]; yading@11: dstlinesize = out->linesize[plane]; yading@11: src = in->data[plane]; yading@11: srclinesize = in->linesize[plane]; yading@11: yading@11: if (trans->dir&1) { yading@11: src += in->linesize[plane] * (inh-1); yading@11: srclinesize *= -1; yading@11: } yading@11: yading@11: if (trans->dir&2) { yading@11: dst += out->linesize[plane] * (outh-1); yading@11: dstlinesize *= -1; yading@11: } yading@11: yading@11: for (y = 0; y < outh; y++) { yading@11: switch (pixstep) { yading@11: case 1: yading@11: for (x = 0; x < outw; x++) yading@11: dst[x] = src[x*srclinesize + y]; yading@11: break; yading@11: case 2: yading@11: for (x = 0; x < outw; x++) yading@11: *((uint16_t *)(dst + 2*x)) = *((uint16_t *)(src + x*srclinesize + y*2)); yading@11: break; yading@11: case 3: yading@11: for (x = 0; x < outw; x++) { yading@11: int32_t v = AV_RB24(src + x*srclinesize + y*3); yading@11: AV_WB24(dst + 3*x, v); yading@11: } yading@11: break; yading@11: case 4: yading@11: for (x = 0; x < outw; x++) yading@11: *((uint32_t *)(dst + 4*x)) = *((uint32_t *)(src + x*srclinesize + y*4)); yading@11: break; yading@11: case 6: yading@11: for (x = 0; x < outw; x++) { yading@11: int64_t v = AV_RB48(src + x*srclinesize + y*6); yading@11: AV_WB48(dst + 6*x, v); yading@11: } yading@11: break; yading@11: case 8: yading@11: for (x = 0; x < outw; x++) yading@11: *((uint64_t *)(dst + 8*x)) = *((uint64_t *)(src + x*srclinesize + y*8)); yading@11: break; yading@11: } yading@11: dst += dstlinesize; yading@11: } yading@11: } yading@11: yading@11: av_frame_free(&in); yading@11: return ff_filter_frame(outlink, out); yading@11: } yading@11: yading@11: #define OFFSET(x) offsetof(TransContext, x) yading@11: #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM yading@11: yading@11: static const AVOption transpose_options[] = { yading@11: { "dir", "Transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, yading@11: TRANSPOSE_CCLOCK_FLIP, TRANSPOSE_CLOCK_FLIP, FLAGS, "dir" }, yading@11: { "cclock_flip", "counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .unit = "dir" }, yading@11: { "clock", "clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .unit = "dir" }, yading@11: { "cclock", "counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .unit = "dir" }, yading@11: { "clock_flip", "clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .unit = "dir" }, yading@11: yading@11: { "passthrough", "do not apply transposition if the input matches the specified geometry", yading@11: OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" }, yading@11: { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, yading@11: { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, yading@11: { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, yading@11: yading@11: { NULL }, yading@11: }; yading@11: yading@11: AVFILTER_DEFINE_CLASS(transpose); yading@11: yading@11: static const AVFilterPad avfilter_vf_transpose_inputs[] = { yading@11: { yading@11: .name = "default", yading@11: .type = AVMEDIA_TYPE_VIDEO, yading@11: .get_video_buffer= get_video_buffer, yading@11: .filter_frame = filter_frame, yading@11: }, yading@11: { NULL } yading@11: }; yading@11: yading@11: static const AVFilterPad avfilter_vf_transpose_outputs[] = { yading@11: { yading@11: .name = "default", yading@11: .config_props = config_props_output, yading@11: .type = AVMEDIA_TYPE_VIDEO, yading@11: }, yading@11: { NULL } yading@11: }; yading@11: yading@11: AVFilter avfilter_vf_transpose = { yading@11: .name = "transpose", yading@11: .description = NULL_IF_CONFIG_SMALL("Transpose input video."), yading@11: yading@11: .priv_size = sizeof(TransContext), yading@11: .priv_class = &transpose_class, yading@11: yading@11: .query_formats = query_formats, yading@11: yading@11: .inputs = avfilter_vf_transpose_inputs, yading@11: .outputs = avfilter_vf_transpose_outputs, yading@11: };