blob: 2fe9a1fb522ca6253b9cafcd81e8ac9722e8b6a3
1 | /* |
2 | * Copyright (c) 2007 Bobby Bingham |
3 | * |
4 | * This file is part of FFmpeg. |
5 | * |
6 | * FFmpeg is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * FFmpeg is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with FFmpeg; if not, write to the Free Software |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
19 | */ |
20 | |
21 | /** |
22 | * @file |
23 | * scale video filter |
24 | */ |
25 | |
26 | #include <stdio.h> |
27 | #include <string.h> |
28 | |
29 | #include "avfilter.h" |
30 | #include "formats.h" |
31 | #include "internal.h" |
32 | #include "scale.h" |
33 | #include "video.h" |
34 | #include "libavutil/avstring.h" |
35 | #include "libavutil/internal.h" |
36 | #include "libavutil/mathematics.h" |
37 | #include "libavutil/opt.h" |
38 | #include "libavutil/parseutils.h" |
39 | #include "libavutil/pixdesc.h" |
40 | #include "libavutil/imgutils.h" |
41 | #include "libavutil/avassert.h" |
42 | #include "libswscale/swscale.h" |
43 | |
44 | enum EvalMode { |
45 | EVAL_MODE_INIT, |
46 | EVAL_MODE_FRAME, |
47 | EVAL_MODE_NB |
48 | }; |
49 | |
50 | typedef struct ScaleContext { |
51 | const AVClass *class; |
52 | struct SwsContext *sws; ///< software scaler context |
53 | struct SwsContext *isws[2]; ///< software scaler context for interlaced material |
54 | AVDictionary *opts; |
55 | |
56 | /** |
57 | * New dimensions. Special values are: |
58 | * 0 = original width/height |
59 | * -1 = keep original aspect |
60 | * -N = try to keep aspect but make sure it is divisible by N |
61 | */ |
62 | int w, h; |
63 | char *size_str; |
64 | unsigned int flags; ///sws flags |
65 | double param[2]; // sws params |
66 | |
67 | int hsub, vsub; ///< chroma subsampling |
68 | int slice_y; ///< top of current output slice |
69 | int input_is_pal; ///< set to 1 if the input format is paletted |
70 | int output_is_pal; ///< set to 1 if the output format is paletted |
71 | int interlaced; |
72 | |
73 | char *w_expr; ///< width expression string |
74 | char *h_expr; ///< height expression string |
75 | char *flags_str; |
76 | |
77 | char *in_color_matrix; |
78 | char *out_color_matrix; |
79 | |
80 | int in_range; |
81 | int out_range; |
82 | |
83 | int out_h_chr_pos; |
84 | int out_v_chr_pos; |
85 | int in_h_chr_pos; |
86 | int in_v_chr_pos; |
87 | |
88 | int force_original_aspect_ratio; |
89 | |
90 | int nb_slices; |
91 | |
92 | int eval_mode; ///< expression evaluation mode |
93 | |
94 | } ScaleContext; |
95 | |
96 | AVFilter ff_vf_scale2ref; |
97 | |
98 | static av_cold int init_dict(AVFilterContext *ctx, AVDictionary **opts) |
99 | { |
100 | ScaleContext *scale = ctx->priv; |
101 | int ret; |
102 | |
103 | if (scale->size_str && (scale->w_expr || scale->h_expr)) { |
104 | av_log(ctx, AV_LOG_ERROR, |
105 | "Size and width/height expressions cannot be set at the same time.\n"); |
106 | return AVERROR(EINVAL); |
107 | } |
108 | |
109 | if (scale->w_expr && !scale->h_expr) |
110 | FFSWAP(char *, scale->w_expr, scale->size_str); |
111 | |
112 | if (scale->size_str) { |
113 | char buf[32]; |
114 | if ((ret = av_parse_video_size(&scale->w, &scale->h, scale->size_str)) < 0) { |
115 | av_log(ctx, AV_LOG_ERROR, |
116 | "Invalid size '%s'\n", scale->size_str); |
117 | return ret; |
118 | } |
119 | snprintf(buf, sizeof(buf)-1, "%d", scale->w); |
120 | av_opt_set(scale, "w", buf, 0); |
121 | snprintf(buf, sizeof(buf)-1, "%d", scale->h); |
122 | av_opt_set(scale, "h", buf, 0); |
123 | } |
124 | if (!scale->w_expr) |
125 | av_opt_set(scale, "w", "iw", 0); |
126 | if (!scale->h_expr) |
127 | av_opt_set(scale, "h", "ih", 0); |
128 | |
129 | av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s flags:'%s' interl:%d\n", |
130 | scale->w_expr, scale->h_expr, (char *)av_x_if_null(scale->flags_str, ""), scale->interlaced); |
131 | |
132 | scale->flags = 0; |
133 | |
134 | if (scale->flags_str) { |
135 | const AVClass *class = sws_get_class(); |
136 | const AVOption *o = av_opt_find(&class, "sws_flags", NULL, 0, |
137 | AV_OPT_SEARCH_FAKE_OBJ); |
138 | int ret = av_opt_eval_flags(&class, o, scale->flags_str, &scale->flags); |
139 | if (ret < 0) |
140 | return ret; |
141 | } |
142 | scale->opts = *opts; |
143 | *opts = NULL; |
144 | |
145 | return 0; |
146 | } |
147 | |
148 | static av_cold void uninit(AVFilterContext *ctx) |
149 | { |
150 | ScaleContext *scale = ctx->priv; |
151 | sws_freeContext(scale->sws); |
152 | sws_freeContext(scale->isws[0]); |
153 | sws_freeContext(scale->isws[1]); |
154 | scale->sws = NULL; |
155 | av_dict_free(&scale->opts); |
156 | } |
157 | |
158 | static int query_formats(AVFilterContext *ctx) |
159 | { |
160 | AVFilterFormats *formats; |
161 | enum AVPixelFormat pix_fmt; |
162 | int ret; |
163 | |
164 | if (ctx->inputs[0]) { |
165 | const AVPixFmtDescriptor *desc = NULL; |
166 | formats = NULL; |
167 | while ((desc = av_pix_fmt_desc_next(desc))) { |
168 | pix_fmt = av_pix_fmt_desc_get_id(desc); |
169 | if ((sws_isSupportedInput(pix_fmt) || |
170 | sws_isSupportedEndiannessConversion(pix_fmt)) |
171 | && (ret = ff_add_format(&formats, pix_fmt)) < 0) { |
172 | return ret; |
173 | } |
174 | } |
175 | if ((ret = ff_formats_ref(formats, &ctx->inputs[0]->out_formats)) < 0) |
176 | return ret; |
177 | } |
178 | if (ctx->outputs[0]) { |
179 | const AVPixFmtDescriptor *desc = NULL; |
180 | formats = NULL; |
181 | while ((desc = av_pix_fmt_desc_next(desc))) { |
182 | pix_fmt = av_pix_fmt_desc_get_id(desc); |
183 | if ((sws_isSupportedOutput(pix_fmt) || pix_fmt == AV_PIX_FMT_PAL8 || |
184 | sws_isSupportedEndiannessConversion(pix_fmt)) |
185 | && (ret = ff_add_format(&formats, pix_fmt)) < 0) { |
186 | return ret; |
187 | } |
188 | } |
189 | if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->in_formats)) < 0) |
190 | return ret; |
191 | } |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static const int *parse_yuv_type(const char *s, enum AVColorSpace colorspace) |
197 | { |
198 | if (!s) |
199 | s = "bt601"; |
200 | |
201 | if (s && strstr(s, "bt709")) { |
202 | colorspace = AVCOL_SPC_BT709; |
203 | } else if (s && strstr(s, "fcc")) { |
204 | colorspace = AVCOL_SPC_FCC; |
205 | } else if (s && strstr(s, "smpte240m")) { |
206 | colorspace = AVCOL_SPC_SMPTE240M; |
207 | } else if (s && (strstr(s, "bt601") || strstr(s, "bt470") || strstr(s, "smpte170m"))) { |
208 | colorspace = AVCOL_SPC_BT470BG; |
209 | } else if (s && strstr(s, "bt2020")) { |
210 | colorspace = AVCOL_SPC_BT2020_NCL; |
211 | } |
212 | |
213 | if (colorspace < 1 || colorspace > 10 || colorspace == 8) { |
214 | colorspace = AVCOL_SPC_BT470BG; |
215 | } |
216 | |
217 | return sws_getCoefficients(colorspace); |
218 | } |
219 | |
220 | static int config_props(AVFilterLink *outlink) |
221 | { |
222 | AVFilterContext *ctx = outlink->src; |
223 | AVFilterLink *inlink0 = outlink->src->inputs[0]; |
224 | AVFilterLink *inlink = ctx->filter == &ff_vf_scale2ref ? |
225 | outlink->src->inputs[1] : |
226 | outlink->src->inputs[0]; |
227 | enum AVPixelFormat outfmt = outlink->format; |
228 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
229 | ScaleContext *scale = ctx->priv; |
230 | int w, h; |
231 | int ret; |
232 | |
233 | if ((ret = ff_scale_eval_dimensions(ctx, |
234 | scale->w_expr, scale->h_expr, |
235 | inlink, outlink, |
236 | &w, &h)) < 0) |
237 | goto fail; |
238 | |
239 | /* Note that force_original_aspect_ratio may overwrite the previous set |
240 | * dimensions so that it is not divisible by the set factors anymore. */ |
241 | if (scale->force_original_aspect_ratio) { |
242 | int tmp_w = av_rescale(h, inlink->w, inlink->h); |
243 | int tmp_h = av_rescale(w, inlink->h, inlink->w); |
244 | |
245 | if (scale->force_original_aspect_ratio == 1) { |
246 | w = FFMIN(tmp_w, w); |
247 | h = FFMIN(tmp_h, h); |
248 | } else { |
249 | w = FFMAX(tmp_w, w); |
250 | h = FFMAX(tmp_h, h); |
251 | } |
252 | } |
253 | |
254 | if (w > INT_MAX || h > INT_MAX || |
255 | (h * inlink->w) > INT_MAX || |
256 | (w * inlink->h) > INT_MAX) |
257 | av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); |
258 | |
259 | outlink->w = w; |
260 | outlink->h = h; |
261 | |
262 | /* TODO: make algorithm configurable */ |
263 | |
264 | scale->input_is_pal = desc->flags & AV_PIX_FMT_FLAG_PAL || |
265 | desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL; |
266 | if (outfmt == AV_PIX_FMT_PAL8) outfmt = AV_PIX_FMT_BGR8; |
267 | scale->output_is_pal = av_pix_fmt_desc_get(outfmt)->flags & AV_PIX_FMT_FLAG_PAL || |
268 | av_pix_fmt_desc_get(outfmt)->flags & AV_PIX_FMT_FLAG_PSEUDOPAL; |
269 | |
270 | if (scale->sws) |
271 | sws_freeContext(scale->sws); |
272 | if (scale->isws[0]) |
273 | sws_freeContext(scale->isws[0]); |
274 | if (scale->isws[1]) |
275 | sws_freeContext(scale->isws[1]); |
276 | scale->isws[0] = scale->isws[1] = scale->sws = NULL; |
277 | if (inlink0->w == outlink->w && |
278 | inlink0->h == outlink->h && |
279 | !scale->out_color_matrix && |
280 | scale->in_range == scale->out_range && |
281 | inlink0->format == outlink->format) |
282 | ; |
283 | else { |
284 | struct SwsContext **swscs[3] = {&scale->sws, &scale->isws[0], &scale->isws[1]}; |
285 | int i; |
286 | |
287 | for (i = 0; i < 3; i++) { |
288 | int in_v_chr_pos = scale->in_v_chr_pos, out_v_chr_pos = scale->out_v_chr_pos; |
289 | struct SwsContext **s = swscs[i]; |
290 | *s = sws_alloc_context(); |
291 | if (!*s) |
292 | return AVERROR(ENOMEM); |
293 | |
294 | av_opt_set_int(*s, "srcw", inlink0 ->w, 0); |
295 | av_opt_set_int(*s, "srch", inlink0 ->h >> !!i, 0); |
296 | av_opt_set_int(*s, "src_format", inlink0->format, 0); |
297 | av_opt_set_int(*s, "dstw", outlink->w, 0); |
298 | av_opt_set_int(*s, "dsth", outlink->h >> !!i, 0); |
299 | av_opt_set_int(*s, "dst_format", outfmt, 0); |
300 | av_opt_set_int(*s, "sws_flags", scale->flags, 0); |
301 | av_opt_set_int(*s, "param0", scale->param[0], 0); |
302 | av_opt_set_int(*s, "param1", scale->param[1], 0); |
303 | if (scale->in_range != AVCOL_RANGE_UNSPECIFIED) |
304 | av_opt_set_int(*s, "src_range", |
305 | scale->in_range == AVCOL_RANGE_JPEG, 0); |
306 | if (scale->out_range != AVCOL_RANGE_UNSPECIFIED) |
307 | av_opt_set_int(*s, "dst_range", |
308 | scale->out_range == AVCOL_RANGE_JPEG, 0); |
309 | |
310 | if (scale->opts) { |
311 | AVDictionaryEntry *e = NULL; |
312 | while ((e = av_dict_get(scale->opts, "", e, AV_DICT_IGNORE_SUFFIX))) { |
313 | if ((ret = av_opt_set(*s, e->key, e->value, 0)) < 0) |
314 | return ret; |
315 | } |
316 | } |
317 | /* Override YUV420P default settings to have the correct (MPEG-2) chroma positions |
318 | * MPEG-2 chroma positions are used by convention |
319 | * XXX: support other 4:2:0 pixel formats */ |
320 | if (inlink0->format == AV_PIX_FMT_YUV420P && scale->in_v_chr_pos == -513) { |
321 | in_v_chr_pos = (i == 0) ? 128 : (i == 1) ? 64 : 192; |
322 | } |
323 | |
324 | if (outlink->format == AV_PIX_FMT_YUV420P && scale->out_v_chr_pos == -513) { |
325 | out_v_chr_pos = (i == 0) ? 128 : (i == 1) ? 64 : 192; |
326 | } |
327 | |
328 | av_opt_set_int(*s, "src_h_chr_pos", scale->in_h_chr_pos, 0); |
329 | av_opt_set_int(*s, "src_v_chr_pos", in_v_chr_pos, 0); |
330 | av_opt_set_int(*s, "dst_h_chr_pos", scale->out_h_chr_pos, 0); |
331 | av_opt_set_int(*s, "dst_v_chr_pos", out_v_chr_pos, 0); |
332 | |
333 | if ((ret = sws_init_context(*s, NULL, NULL)) < 0) |
334 | return ret; |
335 | if (!scale->interlaced) |
336 | break; |
337 | } |
338 | } |
339 | |
340 | if (inlink->sample_aspect_ratio.num){ |
341 | outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio); |
342 | } else |
343 | outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; |
344 | |
345 | 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", |
346 | inlink ->w, inlink ->h, av_get_pix_fmt_name( inlink->format), |
347 | inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den, |
348 | outlink->w, outlink->h, av_get_pix_fmt_name(outlink->format), |
349 | outlink->sample_aspect_ratio.num, outlink->sample_aspect_ratio.den, |
350 | scale->flags); |
351 | return 0; |
352 | |
353 | fail: |
354 | return ret; |
355 | } |
356 | |
357 | static int config_props_ref(AVFilterLink *outlink) |
358 | { |
359 | AVFilterLink *inlink = outlink->src->inputs[1]; |
360 | |
361 | outlink->w = inlink->w; |
362 | outlink->h = inlink->h; |
363 | outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; |
364 | outlink->time_base = inlink->time_base; |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | static int request_frame(AVFilterLink *outlink) |
370 | { |
371 | return ff_request_frame(outlink->src->inputs[0]); |
372 | } |
373 | |
374 | static int request_frame_ref(AVFilterLink *outlink) |
375 | { |
376 | return ff_request_frame(outlink->src->inputs[1]); |
377 | } |
378 | |
379 | static int scale_slice(AVFilterLink *link, AVFrame *out_buf, AVFrame *cur_pic, struct SwsContext *sws, int y, int h, int mul, int field) |
380 | { |
381 | ScaleContext *scale = link->dst->priv; |
382 | const uint8_t *in[4]; |
383 | uint8_t *out[4]; |
384 | int in_stride[4],out_stride[4]; |
385 | int i; |
386 | |
387 | for(i=0; i<4; i++){ |
388 | int vsub= ((i+1)&2) ? scale->vsub : 0; |
389 | in_stride[i] = cur_pic->linesize[i] * mul; |
390 | out_stride[i] = out_buf->linesize[i] * mul; |
391 | in[i] = cur_pic->data[i] + ((y>>vsub)+field) * cur_pic->linesize[i]; |
392 | out[i] = out_buf->data[i] + field * out_buf->linesize[i]; |
393 | } |
394 | if(scale->input_is_pal) |
395 | in[1] = cur_pic->data[1]; |
396 | if(scale->output_is_pal) |
397 | out[1] = out_buf->data[1]; |
398 | |
399 | return sws_scale(sws, in, in_stride, y/mul, h, |
400 | out,out_stride); |
401 | } |
402 | |
403 | static int filter_frame(AVFilterLink *link, AVFrame *in) |
404 | { |
405 | ScaleContext *scale = link->dst->priv; |
406 | AVFilterLink *outlink = link->dst->outputs[0]; |
407 | AVFrame *out; |
408 | const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); |
409 | char buf[32]; |
410 | int in_range; |
411 | |
412 | if (av_frame_get_colorspace(in) == AVCOL_SPC_YCGCO) |
413 | av_log(link->dst, AV_LOG_WARNING, "Detected unsupported YCgCo colorspace.\n"); |
414 | |
415 | if( in->width != link->w |
416 | || in->height != link->h |
417 | || in->format != link->format |
418 | || in->sample_aspect_ratio.den != link->sample_aspect_ratio.den || in->sample_aspect_ratio.num != link->sample_aspect_ratio.num) { |
419 | int ret; |
420 | |
421 | if (scale->eval_mode == EVAL_MODE_INIT) { |
422 | snprintf(buf, sizeof(buf)-1, "%d", outlink->w); |
423 | av_opt_set(scale, "w", buf, 0); |
424 | snprintf(buf, sizeof(buf)-1, "%d", outlink->h); |
425 | av_opt_set(scale, "h", buf, 0); |
426 | } |
427 | |
428 | link->dst->inputs[0]->format = in->format; |
429 | link->dst->inputs[0]->w = in->width; |
430 | link->dst->inputs[0]->h = in->height; |
431 | |
432 | link->dst->inputs[0]->sample_aspect_ratio.den = in->sample_aspect_ratio.den; |
433 | link->dst->inputs[0]->sample_aspect_ratio.num = in->sample_aspect_ratio.num; |
434 | |
435 | |
436 | if ((ret = config_props(outlink)) < 0) |
437 | return ret; |
438 | } |
439 | |
440 | if (!scale->sws) |
441 | return ff_filter_frame(outlink, in); |
442 | |
443 | scale->hsub = desc->log2_chroma_w; |
444 | scale->vsub = desc->log2_chroma_h; |
445 | |
446 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
447 | if (!out) { |
448 | av_frame_free(&in); |
449 | return AVERROR(ENOMEM); |
450 | } |
451 | |
452 | av_frame_copy_props(out, in); |
453 | out->width = outlink->w; |
454 | out->height = outlink->h; |
455 | |
456 | if(scale->output_is_pal) |
457 | avpriv_set_systematic_pal2((uint32_t*)out->data[1], outlink->format == AV_PIX_FMT_PAL8 ? AV_PIX_FMT_BGR8 : outlink->format); |
458 | |
459 | in_range = av_frame_get_color_range(in); |
460 | |
461 | if ( scale->in_color_matrix |
462 | || scale->out_color_matrix |
463 | || scale-> in_range != AVCOL_RANGE_UNSPECIFIED |
464 | || in_range != AVCOL_RANGE_UNSPECIFIED |
465 | || scale->out_range != AVCOL_RANGE_UNSPECIFIED) { |
466 | int in_full, out_full, brightness, contrast, saturation; |
467 | const int *inv_table, *table; |
468 | |
469 | sws_getColorspaceDetails(scale->sws, (int **)&inv_table, &in_full, |
470 | (int **)&table, &out_full, |
471 | &brightness, &contrast, &saturation); |
472 | |
473 | if (scale->in_color_matrix) |
474 | inv_table = parse_yuv_type(scale->in_color_matrix, av_frame_get_colorspace(in)); |
475 | if (scale->out_color_matrix) |
476 | table = parse_yuv_type(scale->out_color_matrix, AVCOL_SPC_UNSPECIFIED); |
477 | else if (scale->in_color_matrix) |
478 | table = inv_table; |
479 | |
480 | if (scale-> in_range != AVCOL_RANGE_UNSPECIFIED) |
481 | in_full = (scale-> in_range == AVCOL_RANGE_JPEG); |
482 | else if (in_range != AVCOL_RANGE_UNSPECIFIED) |
483 | in_full = (in_range == AVCOL_RANGE_JPEG); |
484 | if (scale->out_range != AVCOL_RANGE_UNSPECIFIED) |
485 | out_full = (scale->out_range == AVCOL_RANGE_JPEG); |
486 | |
487 | sws_setColorspaceDetails(scale->sws, inv_table, in_full, |
488 | table, out_full, |
489 | brightness, contrast, saturation); |
490 | if (scale->isws[0]) |
491 | sws_setColorspaceDetails(scale->isws[0], inv_table, in_full, |
492 | table, out_full, |
493 | brightness, contrast, saturation); |
494 | if (scale->isws[1]) |
495 | sws_setColorspaceDetails(scale->isws[1], inv_table, in_full, |
496 | table, out_full, |
497 | brightness, contrast, saturation); |
498 | |
499 | av_frame_set_color_range(out, out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG); |
500 | } |
501 | |
502 | av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den, |
503 | (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w, |
504 | (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h, |
505 | INT_MAX); |
506 | |
507 | if(scale->interlaced>0 || (scale->interlaced<0 && in->interlaced_frame)){ |
508 | scale_slice(link, out, in, scale->isws[0], 0, (link->h+1)/2, 2, 0); |
509 | scale_slice(link, out, in, scale->isws[1], 0, link->h /2, 2, 1); |
510 | }else if (scale->nb_slices) { |
511 | int i, slice_h, slice_start, slice_end = 0; |
512 | const int nb_slices = FFMIN(scale->nb_slices, link->h); |
513 | for (i = 0; i < nb_slices; i++) { |
514 | slice_start = slice_end; |
515 | slice_end = (link->h * (i+1)) / nb_slices; |
516 | slice_h = slice_end - slice_start; |
517 | scale_slice(link, out, in, scale->sws, slice_start, slice_h, 1, 0); |
518 | } |
519 | }else{ |
520 | scale_slice(link, out, in, scale->sws, 0, link->h, 1, 0); |
521 | } |
522 | |
523 | av_frame_free(&in); |
524 | return ff_filter_frame(outlink, out); |
525 | } |
526 | |
527 | static int filter_frame_ref(AVFilterLink *link, AVFrame *in) |
528 | { |
529 | AVFilterLink *outlink = link->dst->outputs[1]; |
530 | |
531 | return ff_filter_frame(outlink, in); |
532 | } |
533 | |
534 | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
535 | char *res, int res_len, int flags) |
536 | { |
537 | ScaleContext *scale = ctx->priv; |
538 | int ret; |
539 | |
540 | if ( !strcmp(cmd, "width") || !strcmp(cmd, "w") |
541 | || !strcmp(cmd, "height") || !strcmp(cmd, "h")) { |
542 | |
543 | int old_w = scale->w; |
544 | int old_h = scale->h; |
545 | AVFilterLink *outlink = ctx->outputs[0]; |
546 | |
547 | av_opt_set(scale, cmd, args, 0); |
548 | if ((ret = config_props(outlink)) < 0) { |
549 | scale->w = old_w; |
550 | scale->h = old_h; |
551 | } |
552 | } else |
553 | ret = AVERROR(ENOSYS); |
554 | |
555 | return ret; |
556 | } |
557 | |
558 | static const AVClass *child_class_next(const AVClass *prev) |
559 | { |
560 | return prev ? NULL : sws_get_class(); |
561 | } |
562 | |
563 | #define OFFSET(x) offsetof(ScaleContext, x) |
564 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
565 | |
566 | static const AVOption scale_options[] = { |
567 | { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, |
568 | { "width", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, |
569 | { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, |
570 | { "height","Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = FLAGS }, |
571 | { "flags", "Flags to pass to libswscale", OFFSET(flags_str), AV_OPT_TYPE_STRING, { .str = "bilinear" }, .flags = FLAGS }, |
572 | { "interl", "set interlacing", OFFSET(interlaced), AV_OPT_TYPE_BOOL, {.i64 = 0 }, -1, 1, FLAGS }, |
573 | { "size", "set video size", OFFSET(size_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, FLAGS }, |
574 | { "s", "set video size", OFFSET(size_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, FLAGS }, |
575 | { "in_color_matrix", "set input YCbCr type", OFFSET(in_color_matrix), AV_OPT_TYPE_STRING, { .str = "auto" }, .flags = FLAGS }, |
576 | { "out_color_matrix", "set output YCbCr type", OFFSET(out_color_matrix), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, |
577 | { "in_range", "set input color range", OFFSET( in_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 2, FLAGS, "range" }, |
578 | { "out_range", "set output color range", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 2, FLAGS, "range" }, |
579 | { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 0, FLAGS, "range" }, |
580 | { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, |
581 | { "jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, |
582 | { "mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, |
583 | { "tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, |
584 | { "pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, |
585 | { "in_v_chr_pos", "input vertical chroma position in luma grid/256" , OFFSET(in_v_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, |
586 | { "in_h_chr_pos", "input horizontal chroma position in luma grid/256", OFFSET(in_h_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, |
587 | { "out_v_chr_pos", "output vertical chroma position in luma grid/256" , OFFSET(out_v_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, |
588 | { "out_h_chr_pos", "output horizontal chroma position in luma grid/256", OFFSET(out_h_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, |
589 | { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" }, |
590 | { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, |
591 | { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, |
592 | { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, |
593 | { "param0", "Scaler param 0", OFFSET(param[0]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, FLAGS }, |
594 | { "param1", "Scaler param 1", OFFSET(param[1]), AV_OPT_TYPE_DOUBLE, { .dbl = SWS_PARAM_DEFAULT }, INT_MIN, INT_MAX, FLAGS }, |
595 | { "nb_slices", "set the number of slices (debug purpose only)", OFFSET(nb_slices), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, |
596 | { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, |
597 | { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, |
598 | { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, |
599 | { NULL } |
600 | }; |
601 | |
602 | static const AVClass scale_class = { |
603 | .class_name = "scale", |
604 | .item_name = av_default_item_name, |
605 | .option = scale_options, |
606 | .version = LIBAVUTIL_VERSION_INT, |
607 | .category = AV_CLASS_CATEGORY_FILTER, |
608 | .child_class_next = child_class_next, |
609 | }; |
610 | |
611 | static const AVFilterPad avfilter_vf_scale_inputs[] = { |
612 | { |
613 | .name = "default", |
614 | .type = AVMEDIA_TYPE_VIDEO, |
615 | .filter_frame = filter_frame, |
616 | }, |
617 | { NULL } |
618 | }; |
619 | |
620 | static const AVFilterPad avfilter_vf_scale_outputs[] = { |
621 | { |
622 | .name = "default", |
623 | .type = AVMEDIA_TYPE_VIDEO, |
624 | .config_props = config_props, |
625 | }, |
626 | { NULL } |
627 | }; |
628 | |
629 | AVFilter ff_vf_scale = { |
630 | .name = "scale", |
631 | .description = NULL_IF_CONFIG_SMALL("Scale the input video size and/or convert the image format."), |
632 | .init_dict = init_dict, |
633 | .uninit = uninit, |
634 | .query_formats = query_formats, |
635 | .priv_size = sizeof(ScaleContext), |
636 | .priv_class = &scale_class, |
637 | .inputs = avfilter_vf_scale_inputs, |
638 | .outputs = avfilter_vf_scale_outputs, |
639 | .process_command = process_command, |
640 | }; |
641 | |
642 | static const AVClass scale2ref_class = { |
643 | .class_name = "scale2ref", |
644 | .item_name = av_default_item_name, |
645 | .option = scale_options, |
646 | .version = LIBAVUTIL_VERSION_INT, |
647 | .category = AV_CLASS_CATEGORY_FILTER, |
648 | .child_class_next = child_class_next, |
649 | }; |
650 | |
651 | static const AVFilterPad avfilter_vf_scale2ref_inputs[] = { |
652 | { |
653 | .name = "default", |
654 | .type = AVMEDIA_TYPE_VIDEO, |
655 | .filter_frame = filter_frame, |
656 | }, |
657 | { |
658 | .name = "ref", |
659 | .type = AVMEDIA_TYPE_VIDEO, |
660 | .filter_frame = filter_frame_ref, |
661 | }, |
662 | { NULL } |
663 | }; |
664 | |
665 | static const AVFilterPad avfilter_vf_scale2ref_outputs[] = { |
666 | { |
667 | .name = "default", |
668 | .type = AVMEDIA_TYPE_VIDEO, |
669 | .config_props = config_props, |
670 | .request_frame= request_frame, |
671 | }, |
672 | { |
673 | .name = "ref", |
674 | .type = AVMEDIA_TYPE_VIDEO, |
675 | .config_props = config_props_ref, |
676 | .request_frame= request_frame_ref, |
677 | }, |
678 | { NULL } |
679 | }; |
680 | |
681 | AVFilter ff_vf_scale2ref = { |
682 | .name = "scale2ref", |
683 | .description = NULL_IF_CONFIG_SMALL("Scale the input video size and/or convert the image format to the given reference."), |
684 | .init_dict = init_dict, |
685 | .uninit = uninit, |
686 | .query_formats = query_formats, |
687 | .priv_size = sizeof(ScaleContext), |
688 | .priv_class = &scale2ref_class, |
689 | .inputs = avfilter_vf_scale2ref_inputs, |
690 | .outputs = avfilter_vf_scale2ref_outputs, |
691 | .process_command = process_command, |
692 | }; |
693 |