Originally committed as revision 20519 to svn://svn.ffmpeg.org/ffmpeg/trunk
Stefano Sabatini authored on 2009/11/12 08:50:21... | ... |
@@ -164,6 +164,31 @@ not specified it will use the default value of 16. |
164 | 164 |
Adding this in the beginning of filter chains should make filtering |
165 | 165 |
faster due to better use of the memory cache. |
166 | 166 |
|
167 |
+@section scale |
|
168 |
+ |
|
169 |
+Scale the input video to width:height and/or convert the image format. |
|
170 |
+ |
|
171 |
+For example the command: |
|
172 |
+ |
|
173 |
+@example |
|
174 |
+./ffmpeg -i in.avi -vfilters "scale=200:100" out.avi |
|
175 |
+@end example |
|
176 |
+ |
|
177 |
+will scale the input video to a size of 200x100. |
|
178 |
+ |
|
179 |
+If the input image format is different from the format requested by |
|
180 |
+the next filter, the scale filter will convert the input to the |
|
181 |
+requested format. |
|
182 |
+ |
|
183 |
+If the value for ``width'' or ``height'' is 0, the respective input |
|
184 |
+size is used for the output. |
|
185 |
+ |
|
186 |
+If the value for ``width'' or ``height'' is -1, the scale filter will |
|
187 |
+use, for the respective output size, a value that maintains the aspect |
|
188 |
+ratio of the input image. |
|
189 |
+ |
|
190 |
+The default value of ``width'' and ``height'' is 0. |
|
191 |
+ |
|
167 | 192 |
@section vflip |
168 | 193 |
|
169 | 194 |
Flip the input video vertically. |
... | ... |
@@ -16,6 +16,7 @@ OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o |
16 | 16 |
OBJS-$(CONFIG_FORMAT_FILTER) += vf_format.o |
17 | 17 |
OBJS-$(CONFIG_NOFORMAT_FILTER) += vf_format.o |
18 | 18 |
OBJS-$(CONFIG_NULL_FILTER) += vf_null.o |
19 |
+OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o |
|
19 | 20 |
OBJS-$(CONFIG_SLICIFY_FILTER) += vf_slicify.o |
20 | 21 |
OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o |
21 | 22 |
|
... | ... |
@@ -38,6 +38,7 @@ void avfilter_register_all(void) |
38 | 38 |
REGISTER_FILTER (FORMAT, format, vf); |
39 | 39 |
REGISTER_FILTER (NOFORMAT, noformat, vf); |
40 | 40 |
REGISTER_FILTER (NULL, null, vf); |
41 |
+ REGISTER_FILTER (SCALE, scale, vf); |
|
41 | 42 |
REGISTER_FILTER (SLICIFY, slicify, vf); |
42 | 43 |
REGISTER_FILTER (VFLIP, vflip, vf); |
43 | 44 |
} |
... | ... |
@@ -23,7 +23,7 @@ |
23 | 23 |
#define AVFILTER_AVFILTER_H |
24 | 24 |
|
25 | 25 |
#define LIBAVFILTER_VERSION_MAJOR 1 |
26 |
-#define LIBAVFILTER_VERSION_MINOR 7 |
|
26 |
+#define LIBAVFILTER_VERSION_MINOR 8 |
|
27 | 27 |
#define LIBAVFILTER_VERSION_MICRO 0 |
28 | 28 |
|
29 | 29 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
30 | 30 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,203 @@ |
0 |
+/* |
|
1 |
+ * copyright (c) 2007 Bobby Bingham |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+/** |
|
21 |
+ * @file libavfilter/vf_scale.c |
|
22 |
+ * scale video filter |
|
23 |
+ */ |
|
24 |
+ |
|
25 |
+#include "avfilter.h" |
|
26 |
+#include "libswscale/swscale.h" |
|
27 |
+ |
|
28 |
+typedef struct { |
|
29 |
+ struct SwsContext *sws; ///< software scaler context |
|
30 |
+ |
|
31 |
+ /** |
|
32 |
+ * New dimensions. Special values are: |
|
33 |
+ * 0 = original width/height |
|
34 |
+ * -1 = keep original aspect |
|
35 |
+ */ |
|
36 |
+ int w, h; |
|
37 |
+ |
|
38 |
+ int hsub, vsub; ///< chroma subsampling |
|
39 |
+ int slice_y; ///< top of current output slice |
|
40 |
+ int slice_dir; ///< detected slice direction order for the current frame |
|
41 |
+ int input_is_pal; ///< set to 1 if the input format is paletted |
|
42 |
+} ScaleContext; |
|
43 |
+ |
|
44 |
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) |
|
45 |
+{ |
|
46 |
+ ScaleContext *scale = ctx->priv; |
|
47 |
+ |
|
48 |
+ if (args) |
|
49 |
+ sscanf(args, "%d:%d", &scale->w, &scale->h); |
|
50 |
+ |
|
51 |
+ /* sanity check params */ |
|
52 |
+ if (scale->w < -1 || scale->h < -1) { |
|
53 |
+ av_log(ctx, AV_LOG_ERROR, "Size values less than -1 are not acceptable.\n"); |
|
54 |
+ return -1; |
|
55 |
+ } |
|
56 |
+ if (scale->w == -1 && scale->h == -1) |
|
57 |
+ scale->w = scale->h = 0; |
|
58 |
+ |
|
59 |
+ return 0; |
|
60 |
+} |
|
61 |
+ |
|
62 |
+static av_cold void uninit(AVFilterContext *ctx) |
|
63 |
+{ |
|
64 |
+ ScaleContext *scale = ctx->priv; |
|
65 |
+ sws_freeContext(scale->sws); |
|
66 |
+ scale->sws = NULL; |
|
67 |
+} |
|
68 |
+ |
|
69 |
+static int query_formats(AVFilterContext *ctx) |
|
70 |
+{ |
|
71 |
+ AVFilterFormats *formats; |
|
72 |
+ |
|
73 |
+ if (ctx->inputs[0]) { |
|
74 |
+ formats = avfilter_all_colorspaces(); |
|
75 |
+ avfilter_formats_ref(formats, &ctx->inputs[0]->out_formats); |
|
76 |
+ } |
|
77 |
+ if (ctx->outputs[0]) { |
|
78 |
+ formats = avfilter_all_colorspaces(); |
|
79 |
+ avfilter_formats_ref(formats, &ctx->outputs[0]->in_formats); |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ return 0; |
|
83 |
+} |
|
84 |
+ |
|
85 |
+static int config_props(AVFilterLink *outlink) |
|
86 |
+{ |
|
87 |
+ AVFilterContext *ctx = outlink->src; |
|
88 |
+ AVFilterLink *inlink = outlink->src->inputs[0]; |
|
89 |
+ ScaleContext *scale = ctx->priv; |
|
90 |
+ int64_t w, h; |
|
91 |
+ |
|
92 |
+ if (!(w = scale->w)) |
|
93 |
+ w = inlink->w; |
|
94 |
+ if (!(h = scale->h)) |
|
95 |
+ h = inlink->h; |
|
96 |
+ if (w == -1) |
|
97 |
+ w = av_rescale(h, inlink->w, inlink->h); |
|
98 |
+ if (h == -1) |
|
99 |
+ h = av_rescale(w, inlink->h, inlink->w); |
|
100 |
+ |
|
101 |
+ if (w > INT_MAX || h > INT_MAX || |
|
102 |
+ (h * inlink->w) > INT_MAX || |
|
103 |
+ (w * inlink->h) > INT_MAX) |
|
104 |
+ av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); |
|
105 |
+ |
|
106 |
+ outlink->w = w; |
|
107 |
+ outlink->h = h; |
|
108 |
+ |
|
109 |
+ /* TODO: make algorithm configurable */ |
|
110 |
+ scale->sws = sws_getContext(inlink ->w, inlink ->h, inlink ->format, |
|
111 |
+ outlink->w, outlink->h, outlink->format, |
|
112 |
+ SWS_BILINEAR, NULL, NULL, NULL); |
|
113 |
+ |
|
114 |
+ av_log(ctx, AV_LOG_INFO, "w:%d h:%d fmt:%s\n", |
|
115 |
+ outlink->w, outlink->h, avcodec_get_pix_fmt_name(outlink->format)); |
|
116 |
+ |
|
117 |
+ avcodec_get_chroma_sub_sample(outlink->format, &scale->hsub, &scale->vsub); |
|
118 |
+ |
|
119 |
+ scale->input_is_pal = inlink->format == PIX_FMT_PAL8 || |
|
120 |
+ inlink->format == PIX_FMT_BGR4_BYTE || |
|
121 |
+ inlink->format == PIX_FMT_RGB4_BYTE || |
|
122 |
+ inlink->format == PIX_FMT_BGR8 || |
|
123 |
+ inlink->format == PIX_FMT_RGB8; |
|
124 |
+ |
|
125 |
+ return !scale->sws; |
|
126 |
+} |
|
127 |
+ |
|
128 |
+static void start_frame(AVFilterLink *link, AVFilterPicRef *picref) |
|
129 |
+{ |
|
130 |
+ ScaleContext *scale = link->dst->priv; |
|
131 |
+ AVFilterLink *outlink = link->dst->outputs[0]; |
|
132 |
+ AVFilterPicRef *outpicref; |
|
133 |
+ |
|
134 |
+ outpicref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h); |
|
135 |
+ outpicref->pts = picref->pts; |
|
136 |
+ outlink->outpic = outpicref; |
|
137 |
+ |
|
138 |
+ av_reduce(&outpicref->pixel_aspect.num, &outpicref->pixel_aspect.den, |
|
139 |
+ (int64_t)picref->pixel_aspect.num * outlink->h * link->w, |
|
140 |
+ (int64_t)picref->pixel_aspect.den * outlink->w * link->h, |
|
141 |
+ INT_MAX); |
|
142 |
+ |
|
143 |
+ scale->slice_dir = 0; |
|
144 |
+ avfilter_start_frame(outlink, avfilter_ref_pic(outpicref, ~0)); |
|
145 |
+} |
|
146 |
+ |
|
147 |
+static void draw_slice(AVFilterLink *link, int y, int h) |
|
148 |
+{ |
|
149 |
+ ScaleContext *scale = link->dst->priv; |
|
150 |
+ int out_h; |
|
151 |
+ AVFilterPicRef *cur_pic = link->cur_pic; |
|
152 |
+ uint8_t *data[4]; |
|
153 |
+ |
|
154 |
+ if (!scale->slice_dir) { |
|
155 |
+ if (y != 0 && y + h != link->h) { |
|
156 |
+ av_log(scale, AV_LOG_ERROR, "Slices start in the middle!\n"); |
|
157 |
+ return; |
|
158 |
+ } |
|
159 |
+ scale->slice_dir = y ? -1 : 1; |
|
160 |
+ scale->slice_y = y ? link->dst->outputs[0]->h : y; |
|
161 |
+ } |
|
162 |
+ |
|
163 |
+ data[0] = cur_pic->data[0] + y * cur_pic->linesize[0]; |
|
164 |
+ data[1] = scale->input_is_pal ? |
|
165 |
+ cur_pic->data[1] : |
|
166 |
+ cur_pic->data[1] + (y>>scale->vsub) * cur_pic->linesize[1]; |
|
167 |
+ data[2] = cur_pic->data[2] + (y>>scale->vsub) * cur_pic->linesize[2]; |
|
168 |
+ data[3] = cur_pic->data[3] + y * cur_pic->linesize[3]; |
|
169 |
+ |
|
170 |
+ out_h = sws_scale(scale->sws, data, cur_pic->linesize, y, h, |
|
171 |
+ link->dst->outputs[0]->outpic->data, |
|
172 |
+ link->dst->outputs[0]->outpic->linesize); |
|
173 |
+ |
|
174 |
+ if (scale->slice_dir == -1) |
|
175 |
+ scale->slice_y -= out_h; |
|
176 |
+ avfilter_draw_slice(link->dst->outputs[0], scale->slice_y, out_h); |
|
177 |
+ if (scale->slice_dir == 1) |
|
178 |
+ scale->slice_y += out_h; |
|
179 |
+} |
|
180 |
+ |
|
181 |
+AVFilter avfilter_vf_scale = { |
|
182 |
+ .name = "scale", |
|
183 |
+ .description = "Scale the input video to width:height size and/or convert the image format.", |
|
184 |
+ |
|
185 |
+ .init = init, |
|
186 |
+ .uninit = uninit, |
|
187 |
+ |
|
188 |
+ .query_formats = query_formats, |
|
189 |
+ |
|
190 |
+ .priv_size = sizeof(ScaleContext), |
|
191 |
+ |
|
192 |
+ .inputs = (AVFilterPad[]) {{ .name = "default", |
|
193 |
+ .type = CODEC_TYPE_VIDEO, |
|
194 |
+ .start_frame = start_frame, |
|
195 |
+ .draw_slice = draw_slice, |
|
196 |
+ .min_perms = AV_PERM_READ, }, |
|
197 |
+ { .name = NULL}}, |
|
198 |
+ .outputs = (AVFilterPad[]) {{ .name = "default", |
|
199 |
+ .type = CODEC_TYPE_VIDEO, |
|
200 |
+ .config_props = config_props, }, |
|
201 |
+ { .name = NULL}}, |
|
202 |
+}; |