00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026 #include "avfilter.h"
00027 #include "libavutil/avstring.h"
00028 #include "libavutil/eval.h"
00029 #include "libavutil/mathematics.h"
00030 #include "libavutil/opt.h"
00031 #include "libavutil/pixdesc.h"
00032 #include "libavutil/imgutils.h"
00033 #include "libavutil/avassert.h"
00034 #include "libswscale/swscale.h"
00035
00036 static const char *const var_names[] = {
00037 "in_w", "iw",
00038 "in_h", "ih",
00039 "out_w", "ow",
00040 "out_h", "oh",
00041 "a",
00042 "sar",
00043 "dar",
00044 "hsub",
00045 "vsub",
00046 NULL
00047 };
00048
00049 enum var_name {
00050 VAR_IN_W, VAR_IW,
00051 VAR_IN_H, VAR_IH,
00052 VAR_OUT_W, VAR_OW,
00053 VAR_OUT_H, VAR_OH,
00054 VAR_A,
00055 VAR_SAR,
00056 VAR_DAR,
00057 VAR_HSUB,
00058 VAR_VSUB,
00059 VARS_NB
00060 };
00061
00062 typedef struct {
00063 struct SwsContext *sws;
00064 struct SwsContext *isws[2];
00065
00071 int w, h;
00072 unsigned int flags;
00073
00074 int hsub, vsub;
00075 int slice_y;
00076 int input_is_pal;
00077 int output_is_pal;
00078 int interlaced;
00079
00080 char w_expr[256];
00081 char h_expr[256];
00082 } ScaleContext;
00083
00084 static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
00085 {
00086 ScaleContext *scale = ctx->priv;
00087 const char *p;
00088
00089 av_strlcpy(scale->w_expr, "iw", sizeof(scale->w_expr));
00090 av_strlcpy(scale->h_expr, "ih", sizeof(scale->h_expr));
00091
00092 scale->flags = SWS_BILINEAR;
00093 if (args) {
00094 sscanf(args, "%255[^:]:%255[^:]", scale->w_expr, scale->h_expr);
00095 p = strstr(args,"flags=");
00096 if (p) {
00097 const AVClass *class = sws_get_class();
00098 const AVOption *o = av_opt_find(&class, "sws_flags", NULL, 0,
00099 AV_OPT_SEARCH_FAKE_OBJ);
00100 int ret = av_opt_eval_flags(&class, o, p + 6, &scale->flags);
00101
00102 if (ret < 0)
00103 return ret;
00104 }
00105 if(strstr(args,"interl=1")){
00106 scale->interlaced=1;
00107 }else if(strstr(args,"interl=-1"))
00108 scale->interlaced=-1;
00109 }
00110
00111 return 0;
00112 }
00113
00114 static av_cold void uninit(AVFilterContext *ctx)
00115 {
00116 ScaleContext *scale = ctx->priv;
00117 sws_freeContext(scale->sws);
00118 sws_freeContext(scale->isws[0]);
00119 sws_freeContext(scale->isws[1]);
00120 scale->sws = NULL;
00121 }
00122
00123 static int query_formats(AVFilterContext *ctx)
00124 {
00125 AVFilterFormats *formats;
00126 enum PixelFormat pix_fmt;
00127 int ret;
00128
00129 if (ctx->inputs[0]) {
00130 formats = NULL;
00131 for (pix_fmt = 0; pix_fmt < PIX_FMT_NB; pix_fmt++)
00132 if ( sws_isSupportedInput(pix_fmt)
00133 && (ret = avfilter_add_format(&formats, pix_fmt)) < 0) {
00134 avfilter_formats_unref(&formats);
00135 return ret;
00136 }
00137 avfilter_formats_ref(formats, &ctx->inputs[0]->out_formats);
00138 }
00139 if (ctx->outputs[0]) {
00140 formats = NULL;
00141 for (pix_fmt = 0; pix_fmt < PIX_FMT_NB; pix_fmt++)
00142 if ( (sws_isSupportedOutput(pix_fmt) || pix_fmt == PIX_FMT_PAL8)
00143 && (ret = avfilter_add_format(&formats, pix_fmt)) < 0) {
00144 avfilter_formats_unref(&formats);
00145 return ret;
00146 }
00147 avfilter_formats_ref(formats, &ctx->outputs[0]->in_formats);
00148 }
00149
00150 return 0;
00151 }
00152
00153 static int config_props(AVFilterLink *outlink)
00154 {
00155 AVFilterContext *ctx = outlink->src;
00156 AVFilterLink *inlink = outlink->src->inputs[0];
00157 enum PixelFormat outfmt = outlink->format;
00158 ScaleContext *scale = ctx->priv;
00159 int64_t w, h;
00160 double var_values[VARS_NB], res;
00161 char *expr;
00162 int ret;
00163
00164 var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w;
00165 var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h;
00166 var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN;
00167 var_values[VAR_OUT_H] = var_values[VAR_OH] = NAN;
00168 var_values[VAR_A] = (float) inlink->w / inlink->h;
00169 var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ?
00170 (float) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1;
00171 var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR];
00172 var_values[VAR_HSUB] = 1<<av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
00173 var_values[VAR_VSUB] = 1<<av_pix_fmt_descriptors[inlink->format].log2_chroma_h;
00174
00175
00176 av_expr_parse_and_eval(&res, (expr = scale->w_expr),
00177 var_names, var_values,
00178 NULL, NULL, NULL, NULL, NULL, 0, ctx);
00179 scale->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res;
00180 if ((ret = av_expr_parse_and_eval(&res, (expr = scale->h_expr),
00181 var_names, var_values,
00182 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00183 goto fail;
00184 scale->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res;
00185
00186 if ((ret = av_expr_parse_and_eval(&res, (expr = scale->w_expr),
00187 var_names, var_values,
00188 NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0)
00189 goto fail;
00190 scale->w = res;
00191
00192 w = scale->w;
00193 h = scale->h;
00194
00195
00196 if (w < -1 || h < -1) {
00197 av_log(ctx, AV_LOG_ERROR, "Size values less than -1 are not acceptable.\n");
00198 return AVERROR(EINVAL);
00199 }
00200 if (w == -1 && h == -1)
00201 scale->w = scale->h = 0;
00202
00203 if (!(w = scale->w))
00204 w = inlink->w;
00205 if (!(h = scale->h))
00206 h = inlink->h;
00207 if (w == -1)
00208 w = av_rescale(h, inlink->w, inlink->h);
00209 if (h == -1)
00210 h = av_rescale(w, inlink->h, inlink->w);
00211
00212 if (w > INT_MAX || h > INT_MAX ||
00213 (h * inlink->w) > INT_MAX ||
00214 (w * inlink->h) > INT_MAX)
00215 av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n");
00216
00217 outlink->w = w;
00218 outlink->h = h;
00219
00220
00221
00222 scale->input_is_pal = av_pix_fmt_descriptors[inlink->format].flags & PIX_FMT_PAL ||
00223 av_pix_fmt_descriptors[inlink->format].flags & PIX_FMT_PSEUDOPAL;
00224 if (outfmt == PIX_FMT_PAL8) outfmt = PIX_FMT_BGR8;
00225 scale->output_is_pal = av_pix_fmt_descriptors[outfmt].flags & PIX_FMT_PAL ||
00226 av_pix_fmt_descriptors[outfmt].flags & PIX_FMT_PSEUDOPAL;
00227
00228 if (scale->sws)
00229 sws_freeContext(scale->sws);
00230 if (inlink->w == outlink->w && inlink->h == outlink->h &&
00231 inlink->format == outlink->format)
00232 scale->sws = NULL;
00233 else {
00234 scale->sws = sws_getContext(inlink ->w, inlink ->h, inlink ->format,
00235 outlink->w, outlink->h, outfmt,
00236 scale->flags, NULL, NULL, NULL);
00237 if (scale->isws[0])
00238 sws_freeContext(scale->isws[0]);
00239 scale->isws[0] = sws_getContext(inlink ->w, inlink ->h/2, inlink ->format,
00240 outlink->w, outlink->h/2, outfmt,
00241 scale->flags, NULL, NULL, NULL);
00242 if (scale->isws[1])
00243 sws_freeContext(scale->isws[1]);
00244 scale->isws[1] = sws_getContext(inlink ->w, inlink ->h/2, inlink ->format,
00245 outlink->w, outlink->h/2, outfmt,
00246 scale->flags, NULL, NULL, NULL);
00247 if (!scale->sws || !scale->isws[0] || !scale->isws[1])
00248 return AVERROR(EINVAL);
00249 }
00250
00251 if (inlink->sample_aspect_ratio.num){
00252 outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio);
00253 } else
00254 outlink->sample_aspect_ratio = inlink->sample_aspect_ratio;
00255
00256 av_log(ctx, AV_LOG_INFO, "w:%d h:%d fmt:%s sar:%d/%d -> w:%d h:%d fmt:%s sar:%d/%d flags:0x%0x\n",
00257 inlink ->w, inlink ->h, av_pix_fmt_descriptors[ inlink->format].name,
00258 inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den,
00259 outlink->w, outlink->h, av_pix_fmt_descriptors[outlink->format].name,
00260 outlink->sample_aspect_ratio.num, outlink->sample_aspect_ratio.den,
00261 scale->flags);
00262 return 0;
00263
00264 fail:
00265 av_log(NULL, AV_LOG_ERROR,
00266 "Error when evaluating the expression '%s'.\n"
00267 "Maybe the expression for out_w:'%s' or for out_h:'%s' is self-referencing.\n",
00268 expr, scale->w_expr, scale->h_expr);
00269 return ret;
00270 }
00271
00272 static void start_frame(AVFilterLink *link, AVFilterBufferRef *picref)
00273 {
00274 ScaleContext *scale = link->dst->priv;
00275 AVFilterLink *outlink = link->dst->outputs[0];
00276 AVFilterBufferRef *outpicref;
00277
00278 if( picref->video->w != link->w
00279 || picref->video->h != link->h
00280 || picref->format != link->format) {
00281 AVFilterLink *out_link;
00282 int ret;
00283 snprintf(scale->w_expr, sizeof(scale->w_expr)-1, "%d", outlink->w);
00284 snprintf(scale->h_expr, sizeof(scale->h_expr)-1, "%d", outlink->h);
00285
00286 link->dst->inputs[0]->format = picref->format;
00287 link->dst->inputs[0]->w = picref->video->w;
00288 link->dst->inputs[0]->h = picref->video->h;
00289
00290 if ((ret = config_props(outlink)) < 0)
00291 av_assert0(0);
00292 }
00293
00294
00295 if (!scale->sws) {
00296 avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
00297 return;
00298 }
00299
00300 scale->hsub = av_pix_fmt_descriptors[link->format].log2_chroma_w;
00301 scale->vsub = av_pix_fmt_descriptors[link->format].log2_chroma_h;
00302
00303 outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE|AV_PERM_ALIGN, outlink->w, outlink->h);
00304 avfilter_copy_buffer_ref_props(outpicref, picref);
00305 outpicref->video->w = outlink->w;
00306 outpicref->video->h = outlink->h;
00307
00308 outlink->out_buf = outpicref;
00309 if(scale->output_is_pal)
00310 ff_set_systematic_pal2(outpicref->data[1], outlink->format == PIX_FMT_PAL8 ? PIX_FMT_BGR8 : outlink->format);
00311
00312 av_reduce(&outpicref->video->sample_aspect_ratio.num, &outpicref->video->sample_aspect_ratio.den,
00313 (int64_t)picref->video->sample_aspect_ratio.num * outlink->h * link->w,
00314 (int64_t)picref->video->sample_aspect_ratio.den * outlink->w * link->h,
00315 INT_MAX);
00316
00317 scale->slice_y = 0;
00318 avfilter_start_frame(outlink, avfilter_ref_buffer(outpicref, ~0));
00319 }
00320
00321 static int scale_slice(AVFilterLink *link, struct SwsContext *sws, int y, int h, int mul, int field)
00322 {
00323 ScaleContext *scale = link->dst->priv;
00324 AVFilterBufferRef *cur_pic = link->cur_buf;
00325 AVFilterBufferRef *out_buf = link->dst->outputs[0]->out_buf;
00326 const uint8_t *in[4];
00327 uint8_t *out[4];
00328 int in_stride[4],out_stride[4];
00329 int i;
00330
00331 for(i=0; i<4; i++){
00332 int vsub= ((i+1)&2) ? scale->vsub : 0;
00333 in_stride[i] = cur_pic->linesize[i] * mul;
00334 out_stride[i] = out_buf->linesize[i] * mul;
00335 in[i] = cur_pic->data[i] + ((y>>vsub)+field) * cur_pic->linesize[i];
00336 out[i] = out_buf->data[i] + field * out_buf->linesize[i];
00337 }
00338 if(scale->input_is_pal)
00339 in[1] = cur_pic->data[1];
00340 if(scale->output_is_pal)
00341 out[1] = out_buf->data[1];
00342
00343 return sws_scale(sws, in, in_stride, y/mul, h,
00344 out,out_stride);
00345 }
00346
00347 static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
00348 {
00349 ScaleContext *scale = link->dst->priv;
00350 int out_h;
00351
00352 if (!scale->sws) {
00353 avfilter_draw_slice(link->dst->outputs[0], y, h, slice_dir);
00354 return;
00355 }
00356
00357 if (scale->slice_y == 0 && slice_dir == -1)
00358 scale->slice_y = link->dst->outputs[0]->h;
00359
00360 if(scale->interlaced>0 || (scale->interlaced<0 && link->cur_buf->video->interlaced)){
00361 av_assert0(y%(2<<scale->vsub) == 0);
00362 out_h = scale_slice(link, scale->isws[0], y, (h+1)/2, 2, 0);
00363 out_h+= scale_slice(link, scale->isws[1], y, h /2, 2, 1);
00364 }else{
00365 out_h = scale_slice(link, scale->sws, y, h, 1, 0);
00366 }
00367
00368 if (slice_dir == -1)
00369 scale->slice_y -= out_h;
00370 avfilter_draw_slice(link->dst->outputs[0], scale->slice_y, out_h, slice_dir);
00371 if (slice_dir == 1)
00372 scale->slice_y += out_h;
00373 }
00374
00375 AVFilter avfilter_vf_scale = {
00376 .name = "scale",
00377 .description = NULL_IF_CONFIG_SMALL("Scale the input video to width:height size and/or convert the image format."),
00378
00379 .init = init,
00380 .uninit = uninit,
00381
00382 .query_formats = query_formats,
00383
00384 .priv_size = sizeof(ScaleContext),
00385
00386 .inputs = (const AVFilterPad[]) {{ .name = "default",
00387 .type = AVMEDIA_TYPE_VIDEO,
00388 .start_frame = start_frame,
00389 .draw_slice = draw_slice,
00390 .min_perms = AV_PERM_READ, },
00391 { .name = NULL}},
00392 .outputs = (const AVFilterPad[]) {{ .name = "default",
00393 .type = AVMEDIA_TYPE_VIDEO,
00394 .config_props = config_props, },
00395 { .name = NULL}},
00396 };