00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00028 #include <stdio.h>
00029
00030 #include "libavutil/intreadwrite.h"
00031 #include "libavutil/opt.h"
00032 #include "libavutil/pixdesc.h"
00033 #include "libavutil/imgutils.h"
00034 #include "libavutil/internal.h"
00035 #include "avfilter.h"
00036 #include "formats.h"
00037 #include "internal.h"
00038 #include "video.h"
00039
00040 typedef enum {
00041 TRANSPOSE_PT_TYPE_NONE,
00042 TRANSPOSE_PT_TYPE_LANDSCAPE,
00043 TRANSPOSE_PT_TYPE_PORTRAIT,
00044 } PassthroughType;
00045
00046 typedef struct {
00047 const AVClass *class;
00048 int hsub, vsub;
00049 int pixsteps[4];
00050
00051
00052
00053
00054
00055 int dir;
00056 PassthroughType passthrough;
00057 } TransContext;
00058
00059 #define OFFSET(x) offsetof(TransContext, x)
00060 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
00061
00062 static const AVOption transpose_options[] = {
00063 { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, {.i64=0}, 0, 7, FLAGS },
00064
00065 { "passthrough", "do not apply transposition if the input matches the specified geometry",
00066 OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" },
00067 { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
00068 { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
00069 { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" },
00070
00071 { NULL },
00072 };
00073
00074 AVFILTER_DEFINE_CLASS(transpose);
00075
00076 static av_cold int init(AVFilterContext *ctx, const char *args)
00077 {
00078 TransContext *trans = ctx->priv;
00079 const char *shorthand[] = { "dir", "passthrough", NULL };
00080
00081 trans->class = &transpose_class;
00082 av_opt_set_defaults(trans);
00083
00084 return av_opt_set_from_string(trans, args, shorthand, "=", ":");
00085 }
00086
00087 static int query_formats(AVFilterContext *ctx)
00088 {
00089 enum AVPixelFormat pix_fmts[] = {
00090 AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA,
00091 AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA,
00092 AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24,
00093 AV_PIX_FMT_RGB565BE, AV_PIX_FMT_RGB565LE,
00094 AV_PIX_FMT_RGB555BE, AV_PIX_FMT_RGB555LE,
00095 AV_PIX_FMT_BGR565BE, AV_PIX_FMT_BGR565LE,
00096 AV_PIX_FMT_BGR555BE, AV_PIX_FMT_BGR555LE,
00097 AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16LE,
00098 AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_YUV420P16BE,
00099 AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUV444P16BE,
00100 AV_PIX_FMT_NV12, AV_PIX_FMT_NV21,
00101 AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8,
00102 AV_PIX_FMT_RGB4_BYTE, AV_PIX_FMT_BGR4_BYTE,
00103 AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P,
00104 AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P,
00105 AV_PIX_FMT_YUV410P,
00106 AV_PIX_FMT_YUVA420P, AV_PIX_FMT_GRAY8,
00107 AV_PIX_FMT_NONE
00108 };
00109
00110 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00111 return 0;
00112 }
00113
00114 static int config_props_output(AVFilterLink *outlink)
00115 {
00116 AVFilterContext *ctx = outlink->src;
00117 TransContext *trans = ctx->priv;
00118 AVFilterLink *inlink = ctx->inputs[0];
00119 const AVPixFmtDescriptor *desc_out = av_pix_fmt_desc_get(outlink->format);
00120 const AVPixFmtDescriptor *desc_in = av_pix_fmt_desc_get(inlink->format);
00121
00122 if (trans->dir&4) {
00123 av_log(ctx, AV_LOG_WARNING,
00124 "dir values greater than 3 are deprecated, use the passthrough option instead\n");
00125 trans->dir &= 3;
00126 trans->passthrough = TRANSPOSE_PT_TYPE_LANDSCAPE;
00127 }
00128
00129 if ((inlink->w >= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) ||
00130 (inlink->w <= inlink->h && trans->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) {
00131 av_log(ctx, AV_LOG_VERBOSE,
00132 "w:%d h:%d -> w:%d h:%d (passthrough mode)\n",
00133 inlink->w, inlink->h, inlink->w, inlink->h);
00134 return 0;
00135 } else {
00136 trans->passthrough = TRANSPOSE_PT_TYPE_NONE;
00137 }
00138
00139 trans->hsub = desc_in->log2_chroma_w;
00140 trans->vsub = desc_in->log2_chroma_h;
00141
00142 av_image_fill_max_pixsteps(trans->pixsteps, NULL, desc_out);
00143
00144 outlink->w = inlink->h;
00145 outlink->h = inlink->w;
00146
00147 if (inlink->sample_aspect_ratio.num){
00148 outlink->sample_aspect_ratio = av_div_q((AVRational){1,1}, inlink->sample_aspect_ratio);
00149 } else
00150 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
00151
00152 av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d dir:%d -> w:%d h:%d rotation:%s vflip:%d\n",
00153 inlink->w, inlink->h, trans->dir, outlink->w, outlink->h,
00154 trans->dir == 1 || trans->dir == 3 ? "clockwise" : "counterclockwise",
00155 trans->dir == 0 || trans->dir == 3);
00156 return 0;
00157 }
00158
00159 static AVFilterBufferRef *get_video_buffer(AVFilterLink *inlink, int perms, int w, int h)
00160 {
00161 TransContext *trans = inlink->dst->priv;
00162
00163 return trans->passthrough ?
00164 ff_null_get_video_buffer (inlink, perms, w, h) :
00165 ff_default_get_video_buffer(inlink, perms, w, h);
00166 }
00167
00168 static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *in)
00169 {
00170 TransContext *trans = inlink->dst->priv;
00171 AVFilterLink *outlink = inlink->dst->outputs[0];
00172 AVFilterBufferRef *out;
00173 int plane;
00174
00175 if (trans->passthrough)
00176 return ff_filter_frame(outlink, in);
00177
00178 out = ff_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h);
00179 if (!out) {
00180 avfilter_unref_bufferp(&in);
00181 return AVERROR(ENOMEM);
00182 }
00183
00184 out->pts = in->pts;
00185
00186 if (in->video->sample_aspect_ratio.num == 0) {
00187 out->video->sample_aspect_ratio = in->video->sample_aspect_ratio;
00188 } else {
00189 out->video->sample_aspect_ratio.num = in->video->sample_aspect_ratio.den;
00190 out->video->sample_aspect_ratio.den = in->video->sample_aspect_ratio.num;
00191 }
00192
00193 for (plane = 0; out->data[plane]; plane++) {
00194 int hsub = plane == 1 || plane == 2 ? trans->hsub : 0;
00195 int vsub = plane == 1 || plane == 2 ? trans->vsub : 0;
00196 int pixstep = trans->pixsteps[plane];
00197 int inh = in->video->h>>vsub;
00198 int outw = out->video->w>>hsub;
00199 int outh = out->video->h>>vsub;
00200 uint8_t *dst, *src;
00201 int dstlinesize, srclinesize;
00202 int x, y;
00203
00204 dst = out->data[plane];
00205 dstlinesize = out->linesize[plane];
00206 src = in->data[plane];
00207 srclinesize = in->linesize[plane];
00208
00209 if (trans->dir&1) {
00210 src += in->linesize[plane] * (inh-1);
00211 srclinesize *= -1;
00212 }
00213
00214 if (trans->dir&2) {
00215 dst += out->linesize[plane] * (outh-1);
00216 dstlinesize *= -1;
00217 }
00218
00219 for (y = 0; y < outh; y++) {
00220 switch (pixstep) {
00221 case 1:
00222 for (x = 0; x < outw; x++)
00223 dst[x] = src[x*srclinesize + y];
00224 break;
00225 case 2:
00226 for (x = 0; x < outw; x++)
00227 *((uint16_t *)(dst + 2*x)) = *((uint16_t *)(src + x*srclinesize + y*2));
00228 break;
00229 case 3:
00230 for (x = 0; x < outw; x++) {
00231 int32_t v = AV_RB24(src + x*srclinesize + y*3);
00232 AV_WB24(dst + 3*x, v);
00233 }
00234 break;
00235 case 4:
00236 for (x = 0; x < outw; x++)
00237 *((uint32_t *)(dst + 4*x)) = *((uint32_t *)(src + x*srclinesize + y*4));
00238 break;
00239 }
00240 dst += dstlinesize;
00241 }
00242 }
00243
00244 avfilter_unref_bufferp(&in);
00245 return ff_filter_frame(outlink, out);
00246 }
00247
00248 static const AVFilterPad avfilter_vf_transpose_inputs[] = {
00249 {
00250 .name = "default",
00251 .type = AVMEDIA_TYPE_VIDEO,
00252 .get_video_buffer= get_video_buffer,
00253 .filter_frame = filter_frame,
00254 .min_perms = AV_PERM_READ,
00255 },
00256 { NULL }
00257 };
00258
00259 static const AVFilterPad avfilter_vf_transpose_outputs[] = {
00260 {
00261 .name = "default",
00262 .config_props = config_props_output,
00263 .type = AVMEDIA_TYPE_VIDEO,
00264 },
00265 { NULL }
00266 };
00267
00268 AVFilter avfilter_vf_transpose = {
00269 .name = "transpose",
00270 .description = NULL_IF_CONFIG_SMALL("Transpose input video."),
00271
00272 .init = init,
00273 .priv_size = sizeof(TransContext),
00274
00275 .query_formats = query_formats,
00276
00277 .inputs = avfilter_vf_transpose_inputs,
00278 .outputs = avfilter_vf_transpose_outputs,
00279 .priv_class = &transpose_class,
00280 };