#include <stdio.h>
#include "libavutil/intreadwrite.h"
+#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libavutil/imgutils.h"
#include "libavutil/internal.h"
#include "internal.h"
#include "video.h"
+typedef enum {
+ TRANSPOSE_PT_TYPE_NONE,
+ TRANSPOSE_PT_TYPE_LANDSCAPE,
+ TRANSPOSE_PT_TYPE_PORTRAIT,
+} PassthroughType;
+
typedef struct {
+ const AVClass *class;
int hsub, vsub;
int pixsteps[4];
/* 2 Rotate by 90 degrees counterclockwise. */
/* 3 Rotate by 90 degrees clockwise and vflip. */
int dir;
- int passthrough; ///< landscape passthrough mode enabled
+ PassthroughType passthrough; ///< landscape passthrough mode enabled
} TransContext;
+#define OFFSET(x) offsetof(TransContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+static const AVOption transpose_options[] = {
+ { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, {.i64=0}, 0, 7, FLAGS },
+
+ { "passthrough", "do not apply transposition if the input matches the specified geometry",
+ OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" },
+ { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
+ { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
+ { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
+
+ { NULL },
+};
+
+AVFILTER_DEFINE_CLASS(transpose);
+
static av_cold int init(AVFilterContext *ctx, const char *args)
{
TransContext *trans = ctx->priv;
- trans->dir = 0;
+ const char *shorthand[] = { "dir", "passthrough", NULL };
- if (args)
- sscanf(args, "%d", &trans->dir);
+ trans->class = &transpose_class;
+ av_opt_set_defaults(trans);
- if (trans->dir < 0 || trans->dir > 7) {
- av_log(ctx, AV_LOG_ERROR, "Invalid value %d not between 0 and 7.\n",
- trans->dir);
- return AVERROR(EINVAL);
- }
- return 0;
+ return av_opt_set_from_string(trans, args, shorthand, "=", ":");
}
static int query_formats(AVFilterContext *ctx)
{
- enum PixelFormat pix_fmts[] = {
- PIX_FMT_ARGB, PIX_FMT_RGBA,
- PIX_FMT_ABGR, PIX_FMT_BGRA,
- PIX_FMT_RGB24, PIX_FMT_BGR24,
- PIX_FMT_RGB565BE, PIX_FMT_RGB565LE,
- PIX_FMT_RGB555BE, PIX_FMT_RGB555LE,
- PIX_FMT_BGR565BE, PIX_FMT_BGR565LE,
- PIX_FMT_BGR555BE, PIX_FMT_BGR555LE,
- PIX_FMT_GRAY16BE, PIX_FMT_GRAY16LE,
- PIX_FMT_YUV420P16LE, PIX_FMT_YUV420P16BE,
- PIX_FMT_YUV444P16LE, PIX_FMT_YUV444P16BE,
- PIX_FMT_NV12, PIX_FMT_NV21,
- PIX_FMT_RGB8, PIX_FMT_BGR8,
- PIX_FMT_RGB4_BYTE, PIX_FMT_BGR4_BYTE,
- PIX_FMT_YUV444P, PIX_FMT_YUVJ444P,
- PIX_FMT_YUV420P, PIX_FMT_YUVJ420P,
- PIX_FMT_YUV410P,
- PIX_FMT_YUVA420P, PIX_FMT_GRAY8,
- PIX_FMT_NONE
+ enum AVPixelFormat pix_fmts[] = {
+ AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA,
+ AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
+ AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
+ AV_PIX_FMT_RGB565BE, AV_PIX_FMT_RGB565LE,
+ AV_PIX_FMT_RGB555BE, AV_PIX_FMT_RGB555LE,
+ AV_PIX_FMT_BGR565BE, AV_PIX_FMT_BGR565LE,
+ AV_PIX_FMT_BGR555BE, AV_PIX_FMT_BGR555LE,
+ AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16LE,
+ AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_YUV420P16BE,
+ AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUV444P16BE,
+ AV_PIX_FMT_NV12, AV_PIX_FMT_NV21,
+ AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8,
+ AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE,
+ AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
+ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
+ AV_PIX_FMT_YUV410P,
+ AV_PIX_FMT_YUVA420P, AV_PIX_FMT_GRAY8,
+ AV_PIX_FMT_NONE
};
ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
AVFilterContext *ctx = outlink->src;
TransContext *trans = ctx->priv;
AVFilterLink *inlink = ctx->inputs[0];
- const AVPixFmtDescriptor *pixdesc = &av_pix_fmt_descriptors[outlink->format];
+ const AVPixFmtDescriptor *desc_out = av_pix_fmt_desc_get(outlink->format);
+ const AVPixFmtDescriptor *desc_in = av_pix_fmt_desc_get(inlink->format);
if (trans->dir&4) {
+ av_log(ctx, AV_LOG_WARNING,
+ "dir values greater than 3 are deprecated, use the passthrough option instead\n");
trans->dir &= 3;
- if (inlink->w >= inlink->h) {
- trans->passthrough = 1;
+ trans->passthrough = TRANSPOSE_PT_TYPE_LANDSCAPE;
+ }
- av_log(ctx, AV_LOG_VERBOSE,
- "w:%d h:%d -> w:%d h:%d (landscape passthrough mode)\n",
- inlink->w, inlink->h, outlink->w, outlink->h);
- return 0;
- }
+ if ((inlink->w >= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
+ (inlink->w <= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
+ av_log(ctx, AV_LOG_VERBOSE,
+ "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
+ inlink->w, inlink->h, inlink->w, inlink->h);
+ return 0;
+ } else {
+ trans->passthrough = TRANSPOSE_PT_TYPE_NONE;
}
- trans->hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
- trans->vsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_h;
+ trans->hsub = desc_in->log2_chroma_w;
+ trans->vsub = desc_in->log2_chroma_h;
- av_image_fill_max_pixsteps(trans->pixsteps, NULL, pixdesc);
+ av_image_fill_max_pixsteps(trans->pixsteps, NULL, desc_out);
outlink->w = inlink->h;
outlink->h = inlink->w;
return trans->passthrough ? ff_null_draw_slice(inlink, y, h, slice_dir) : 0;
}
+static const AVFilterPad avfilter_vf_transpose_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .get_video_buffer= get_video_buffer,
+ .start_frame = start_frame,
+ .draw_slice = draw_slice,
+ .end_frame = end_frame,
+ .min_perms = AV_PERM_READ,
+ },
+ { NULL }
+};
+
+static const AVFilterPad avfilter_vf_transpose_outputs[] = {
+ {
+ .name = "default",
+ .config_props = config_props_output,
+ .type = AVMEDIA_TYPE_VIDEO,
+ },
+ { NULL }
+};
+
AVFilter avfilter_vf_transpose = {
.name = "transpose",
.description = NULL_IF_CONFIG_SMALL("Transpose input video."),
.query_formats = query_formats,
- .inputs = (const AVFilterPad[]) {{ .name = "default",
- .type = AVMEDIA_TYPE_VIDEO,
- .get_video_buffer= get_video_buffer,
- .start_frame = start_frame,
- .draw_slice = draw_slice,
- .end_frame = end_frame,
- .min_perms = AV_PERM_READ, },
- { .name = NULL}},
- .outputs = (const AVFilterPad[]) {{ .name = "default",
- .config_props = config_props_output,
- .type = AVMEDIA_TYPE_VIDEO, },
- { .name = NULL}},
+ .inputs = avfilter_vf_transpose_inputs,
+ .outputs = avfilter_vf_transpose_outputs,
+ .priv_class = &transpose_class,
};