Browse code

lavfi: add testsrc source

Stefano Sabatini authored on 2011/06/18 10:11:16
Showing 5 changed files
... ...
@@ -1970,6 +1970,49 @@ Some examples follow:
1970 1970
 frei0r_src=200x200:10:partik0l=1234 [overlay]; [in][overlay] overlay
1971 1971
 @end example
1972 1972
 
1973
+@section testsrc
1974
+
1975
+Generate a test video pattern, showing a color pattern, a scrolling
1976
+gradient and a timestamp. This is mainly intended for testing
1977
+purposes.
1978
+
1979
+It accepts an optional sequence of @var{key}=@var{value} pairs,
1980
+separated by ":". The description of the accepted options follows.
1981
+
1982
+@table @option
1983
+
1984
+@item size, s
1985
+Specify the size of the sourced video, it may be a string of the form
1986
+@var{width}x@var{heigth}, or the name of a size abbreviation. The
1987
+default value is "320x240".
1988
+
1989
+@item rate, r
1990
+Specify the frame rate of the sourced video, as the number of frames
1991
+generated per second. It has to be a string in the format
1992
+@var{frame_rate_num}/@var{frame_rate_den}, an integer number, a float
1993
+number or a valid video frame rate abbreviation. The default value is
1994
+"25".
1995
+
1996
+@item duration
1997
+Set the video duration of the sourced video. The accepted syntax is:
1998
+@example
1999
+[-]HH[:MM[:SS[.m...]]]
2000
+[-]S+[.m...]
2001
+@end example
2002
+See also the function @code{av_parse_time()}.
2003
+
2004
+If not specified, or the expressed duration is negative, the video is
2005
+supposed to be generated forever.
2006
+@end table
2007
+
2008
+For example the following:
2009
+@example
2010
+testsrc=duration=5.3:size=qcif:rate=10
2011
+@end example
2012
+
2013
+will generate a video with a duration of 5.3 seconds, with size
2014
+176x144 and a framerate of 10 frames per second.
2015
+
1973 2016
 @c man end VIDEO SOURCES
1974 2017
 
1975 2018
 @chapter Video Sinks
... ...
@@ -68,6 +68,7 @@ OBJS-$(CONFIG_COLOR_FILTER)                  += vsrc_color.o
68 68
 OBJS-$(CONFIG_FREI0R_SRC_FILTER)             += vf_frei0r.o
69 69
 OBJS-$(CONFIG_MOVIE_FILTER)                  += vsrc_movie.o
70 70
 OBJS-$(CONFIG_NULLSRC_FILTER)                += vsrc_nullsrc.o
71
+OBJS-$(CONFIG_TESTSRC_FILTER)                += vsrc_testsrc.o
71 72
 
72 73
 OBJS-$(CONFIG_BUFFERSINK_FILTER)             += vsink_buffer.o
73 74
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
... ...
@@ -84,6 +84,7 @@ void avfilter_register_all(void)
84 84
     REGISTER_FILTER (FREI0R,      frei0r_src,  vsrc);
85 85
     REGISTER_FILTER (MOVIE,       movie,       vsrc);
86 86
     REGISTER_FILTER (NULLSRC,     nullsrc,     vsrc);
87
+    REGISTER_FILTER (TESTSRC,     testsrc,     vsrc);
87 88
 
88 89
     REGISTER_FILTER (BUFFER,      buffersink,  vsink);
89 90
     REGISTER_FILTER (NULLSINK,    nullsink,    vsink);
... ...
@@ -26,7 +26,7 @@
26 26
 #include "libavutil/samplefmt.h"
27 27
 
28 28
 #define LIBAVFILTER_VERSION_MAJOR  2
29
-#define LIBAVFILTER_VERSION_MINOR 23
29
+#define LIBAVFILTER_VERSION_MINOR 24
30 30
 #define LIBAVFILTER_VERSION_MICRO  0
31 31
 
32 32
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
33 33
new file mode 100644
... ...
@@ -0,0 +1,338 @@
0
+/*
1
+ * Copyright (c) 2007 Nicolas George <nicolas.george@normalesup.org>
2
+ * Copyright (c) 2011 Stefano Sabatini
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
+ * Based on the test pattern generator demuxer by Nicolas George:
24
+ * http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2007-October/037845.html
25
+ */
26
+
27
+#include <float.h>
28
+
29
+#include "libavutil/opt.h"
30
+#include "libavutil/parseutils.h"
31
+#include "avfilter.h"
32
+
33
+typedef struct {
34
+    const AVClass *class;
35
+    int h, w;
36
+    unsigned int nb_frame;
37
+    AVRational time_base;
38
+    int64_t pts, max_pts;
39
+    char *size;                 ///< video frame size
40
+    char *rate;                 ///< video frame rate
41
+    char *duration;             ///< total duration of the generated video
42
+} TestSourceContext;
43
+
44
+#define OFFSET(x) offsetof(TestSourceContext, x)
45
+
46
+static const AVOption testsrc_options[]= {
47
+    { "size",     "set video size",     OFFSET(size),     FF_OPT_TYPE_STRING, {.str = "320x240"}, 0, 0 },
48
+    { "s",        "set video size",     OFFSET(size),     FF_OPT_TYPE_STRING, {.str = "320x240"}, 0, 0 },
49
+    { "rate",     "set video rate",     OFFSET(rate),     FF_OPT_TYPE_STRING, {.str = "25"},      0, 0 },
50
+    { "r",        "set video rate",     OFFSET(rate),     FF_OPT_TYPE_STRING, {.str = "25"},      0, 0 },
51
+    { "duration", "set video duration", OFFSET(duration), FF_OPT_TYPE_STRING, {.str = NULL},      0, 0 },
52
+    { NULL },
53
+};
54
+
55
+static const char *testsrc_get_name(void *ctx)
56
+{
57
+    return "testsrc";
58
+}
59
+
60
+static const AVClass testsrc_class = {
61
+    "TestSourceContext",
62
+    testsrc_get_name,
63
+    testsrc_options
64
+};
65
+
66
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
67
+{
68
+    TestSourceContext *test = ctx->priv;
69
+    AVRational frame_rate_q;
70
+    int64_t duration = -1;
71
+    int ret = 0;
72
+
73
+    test->class = &testsrc_class;
74
+    av_opt_set_defaults2(test, 0, 0);
75
+
76
+    if ((ret = (av_set_options_string(test, args, "=", ":"))) < 0) {
77
+        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
78
+        return ret;
79
+    }
80
+
81
+    if ((ret = av_parse_video_size(&test->w, &test->h, test->size)) < 0) {
82
+        av_log(ctx, AV_LOG_ERROR, "Invalid frame size: '%s'\n", test->size);
83
+        return ret;
84
+    }
85
+
86
+    if ((ret = av_parse_video_rate(&frame_rate_q, test->rate)) < 0 ||
87
+        frame_rate_q.den <= 0 || frame_rate_q.num <= 0) {
88
+        av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", test->rate);
89
+        return ret;
90
+    }
91
+
92
+    if ((test->duration) && (ret = av_parse_time(&duration, test->duration, 1)) < 0) {
93
+        av_log(ctx, AV_LOG_ERROR, "Invalid duration: '%s'\n", test->duration);
94
+        return ret;
95
+    }
96
+
97
+    test->time_base.num = frame_rate_q.den;
98
+    test->time_base.den = frame_rate_q.num;
99
+    test->max_pts = duration >= 0 ?
100
+        av_rescale_q(duration, AV_TIME_BASE_Q, test->time_base) : -1;
101
+    test->nb_frame = 0;
102
+    test->pts = 0;
103
+
104
+    av_log(ctx, AV_LOG_INFO, "size:%dx%d rate:%d/%d duration:%f\n",
105
+           test->w, test->h, frame_rate_q.num, frame_rate_q.den,
106
+           duration < 0 ? -1 : test->max_pts * av_q2d(test->time_base));
107
+    return 0;
108
+}
109
+
110
+static int config_props(AVFilterLink *outlink)
111
+{
112
+    TestSourceContext *test = outlink->src->priv;
113
+
114
+    outlink->w = test->w;
115
+    outlink->h = test->h;
116
+    outlink->time_base = test->time_base;
117
+
118
+    return 0;
119
+}
120
+
121
+/**
122
+ * Fill a rectangle with value val.
123
+ *
124
+ * @param val the RGB value to set
125
+ * @param dst pointer to the destination buffer to fill
126
+ * @param dst_linesize linesize of destination
127
+ * @param segment_width width of the segment
128
+ * @param x horizontal coordinate where to draw the rectangle in the destination buffer
129
+ * @param y horizontal coordinate where to draw the rectangle in the destination buffer
130
+ * @param w width  of the rectangle to draw, expressed as a number of segment_width units
131
+ * @param h height of the rectangle to draw, expressed as a number of segment_width units
132
+ */
133
+static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, unsigned segment_width,
134
+                           unsigned x, unsigned y, unsigned w, unsigned h)
135
+{
136
+    int i;
137
+    int step = 3;
138
+
139
+    dst += segment_width * (step * x + y * dst_linesize);
140
+    w *= segment_width * step;
141
+    h *= segment_width;
142
+    for (i = 0; i < h; i++) {
143
+        memset(dst, val, w);
144
+        dst += dst_linesize;
145
+    }
146
+}
147
+
148
+static void draw_digit(int digit, uint8_t *dst, unsigned dst_linesize,
149
+                       unsigned segment_width)
150
+{
151
+#define TOP_HBAR        1
152
+#define MID_HBAR        2
153
+#define BOT_HBAR        4
154
+#define LEFT_TOP_VBAR   8
155
+#define LEFT_BOT_VBAR  16
156
+#define RIGHT_TOP_VBAR 32
157
+#define RIGHT_BOT_VBAR 64
158
+    struct {
159
+        int x, y, w, h;
160
+    } segments[] = {
161
+        { 1,  0, 5, 1 }, /* TOP_HBAR */
162
+        { 1,  6, 5, 1 }, /* MID_HBAR */
163
+        { 1, 12, 5, 1 }, /* BOT_HBAR */
164
+        { 0,  1, 1, 5 }, /* LEFT_TOP_VBAR */
165
+        { 0,  7, 1, 5 }, /* LEFT_BOT_VBAR */
166
+        { 6,  1, 1, 5 }, /* RIGHT_TOP_VBAR */
167
+        { 6,  7, 1, 5 }  /* RIGHT_BOT_VBAR */
168
+    };
169
+    static const unsigned char masks[10] = {
170
+        /* 0 */ TOP_HBAR         |BOT_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR|RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
171
+        /* 1 */                                                        RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
172
+        /* 2 */ TOP_HBAR|MID_HBAR|BOT_HBAR|LEFT_BOT_VBAR                             |RIGHT_TOP_VBAR,
173
+        /* 3 */ TOP_HBAR|MID_HBAR|BOT_HBAR                            |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
174
+        /* 4 */          MID_HBAR         |LEFT_TOP_VBAR              |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
175
+        /* 5 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR                             |RIGHT_BOT_VBAR,
176
+        /* 6 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR               |RIGHT_BOT_VBAR,
177
+        /* 7 */ TOP_HBAR                                              |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
178
+        /* 8 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR|LEFT_BOT_VBAR|RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
179
+        /* 9 */ TOP_HBAR|BOT_HBAR|MID_HBAR|LEFT_TOP_VBAR              |RIGHT_TOP_VBAR|RIGHT_BOT_VBAR,
180
+    };
181
+    unsigned mask = masks[digit];
182
+    int i;
183
+
184
+    draw_rectangle(0, dst, dst_linesize, segment_width, 0, 0, 8, 13);
185
+    for (i = 0; i < FF_ARRAY_ELEMS(segments); i++)
186
+        if (mask & (1<<i))
187
+            draw_rectangle(255, dst, dst_linesize, segment_width,
188
+                           segments[i].x, segments[i].y, segments[i].w, segments[i].h);
189
+}
190
+
191
+#define GRADIENT_SIZE (6 * 256)
192
+
193
+static void fill_picture(TestSourceContext *test, AVFilterBufferRef *picref)
194
+{
195
+    uint8_t *p, *p0;
196
+    int x, y;
197
+    int color, color_rest;
198
+    int icolor;
199
+    int radius;
200
+    int quad0, quad;
201
+    int dquad_x, dquad_y;
202
+    int grad, dgrad, rgrad, drgrad;
203
+    int seg_size;
204
+    int second;
205
+    int i;
206
+    uint8_t *data = picref->data[0];
207
+    int width  = picref->video->w;
208
+    int height = picref->video->h;
209
+
210
+    /* draw colored bars and circle */
211
+    radius = (width + height) / 4;
212
+    quad0 = width * width / 4 + height * height / 4 - radius * radius;
213
+    dquad_y = 1 - height;
214
+    p0 = data;
215
+    for (y = 0; y < height; y++) {
216
+        p = p0;
217
+        color = 0;
218
+        color_rest = 0;
219
+        quad = quad0;
220
+        dquad_x = 1 - width;
221
+        for (x = 0; x < width; x++) {
222
+            icolor = color;
223
+            if (quad < 0)
224
+                icolor ^= 7;
225
+            quad += dquad_x;
226
+            dquad_x += 2;
227
+            *(p++) = icolor & 1 ? 255 : 0;
228
+            *(p++) = icolor & 2 ? 255 : 0;
229
+            *(p++) = icolor & 4 ? 255 : 0;
230
+            color_rest += 8;
231
+            if (color_rest >= width) {
232
+                color_rest -= width;
233
+                color++;
234
+            }
235
+        }
236
+        quad0 += dquad_y;
237
+        dquad_y += 2;
238
+        p0 += picref->linesize[0];
239
+    }
240
+
241
+    /* draw sliding color line */
242
+    p = data + picref->linesize[0] * height * 3/4;
243
+    grad = (256 * test->nb_frame * test->time_base.num / test->time_base.den) %
244
+        GRADIENT_SIZE;
245
+    rgrad = 0;
246
+    dgrad = GRADIENT_SIZE / width;
247
+    drgrad = GRADIENT_SIZE % width;
248
+    for (x = 0; x < width; x++) {
249
+        *(p++) =
250
+            grad < 256 || grad >= 5 * 256 ? 255 :
251
+            grad >= 2 * 256 && grad < 4 * 256 ? 0 :
252
+            grad < 2 * 256 ? 2 * 256 - 1 - grad : grad - 4 * 256;
253
+        *(p++) =
254
+            grad >= 4 * 256 ? 0 :
255
+            grad >= 1 * 256 && grad < 3 * 256 ? 255 :
256
+            grad < 1 * 256 ? grad : 4 * 256 - 1 - grad;
257
+        *(p++) =
258
+            grad < 2 * 256 ? 0 :
259
+            grad >= 3 * 256 && grad < 5 * 256 ? 255 :
260
+            grad < 3 * 256 ? grad - 2 * 256 : 6 * 256 - 1 - grad;
261
+        grad += dgrad;
262
+        rgrad += drgrad;
263
+        if (rgrad >= GRADIENT_SIZE) {
264
+            grad++;
265
+            rgrad -= GRADIENT_SIZE;
266
+        }
267
+        if (grad >= GRADIENT_SIZE)
268
+            grad -= GRADIENT_SIZE;
269
+    }
270
+    for (y = height / 8; y > 0; y--) {
271
+        memcpy(p, p - picref->linesize[0], 3 * width);
272
+        p += picref->linesize[0];
273
+    }
274
+
275
+    /* draw digits */
276
+    seg_size = width / 80;
277
+    if (seg_size >= 1 && height >= 13 * seg_size) {
278
+        second = test->nb_frame * test->time_base.num / test->time_base.den;
279
+        x = width - (width - seg_size * 64) / 2;
280
+        y = (height - seg_size * 13) / 2;
281
+        p = data + (x*3 + y * picref->linesize[0]);
282
+        for (i = 0; i < 8; i++) {
283
+            p -= 3 * 8 * seg_size;
284
+            draw_digit(second % 10, p, picref->linesize[0], seg_size);
285
+            second /= 10;
286
+            if (second == 0)
287
+                break;
288
+        }
289
+    }
290
+}
291
+
292
+static int request_frame(AVFilterLink *outlink)
293
+{
294
+    TestSourceContext *test = outlink->src->priv;
295
+    AVFilterBufferRef *picref;
296
+
297
+    if (test->max_pts >= 0 && test->pts > test->max_pts)
298
+        return AVERROR_EOF;
299
+    picref = avfilter_get_video_buffer(outlink, AV_PERM_WRITE,
300
+                                       test->w, test->h);
301
+    picref->pts = test->pts++;
302
+    test->nb_frame++;
303
+    fill_picture(test, picref);
304
+
305
+    avfilter_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
306
+    avfilter_draw_slice(outlink, 0, picref->video->h, 1);
307
+    avfilter_end_frame(outlink);
308
+    avfilter_unref_buffer(picref);
309
+
310
+    return 0;
311
+}
312
+
313
+static int query_formats(AVFilterContext *ctx)
314
+{
315
+    static const enum PixelFormat pix_fmts[] = {
316
+        PIX_FMT_RGB24, PIX_FMT_NONE
317
+    };
318
+    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
319
+    return 0;
320
+}
321
+
322
+AVFilter avfilter_vsrc_testsrc = {
323
+    .name      = "testsrc",
324
+    .description = NULL_IF_CONFIG_SMALL("Generate test pattern."),
325
+    .priv_size = sizeof(TestSourceContext),
326
+    .init      = init,
327
+
328
+    .query_formats   = query_formats,
329
+
330
+    .inputs    = (AVFilterPad[]) {{ .name = NULL}},
331
+
332
+    .outputs   = (AVFilterPad[]) {{ .name = "default",
333
+                                    .type = AVMEDIA_TYPE_VIDEO,
334
+                                    .request_frame = request_frame,
335
+                                    .config_props  = config_props, },
336
+                                  { .name = NULL }},
337
+};