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