2 * Copyright (c) 2007 Nicolas George <nicolas.george@normalesup.org>
3 * Copyright (c) 2011 Stefano Sabatini
5 * This file is part of Libav.
7 * Libav is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * Libav is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 * testsrc is based on the test pattern generator demuxer by Nicolas George:
27 * http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2007-October/037845.html
29 * rgbtestsrc is ported from MPlayer libmpcodecs/vf_rgbtest.c by
30 * Michael Niedermayer.
35 #include "libavutil/common.h"
36 #include "libavutil/mathematics.h"
37 #include "libavutil/opt.h"
38 #include "libavutil/intreadwrite.h"
39 #include "libavutil/parseutils.h"
48 unsigned int nb_frame
;
51 char *size
; ///< video frame size
52 char *rate
; ///< video frame rate
53 char *duration
; ///< total duration of the generated video
54 AVRational sar
; ///< sample aspect ratio
56 void (* fill_picture_fn
)(AVFilterContext
*ctx
, AVFilterBufferRef
*picref
);
58 /* only used by rgbtest */
62 #define OFFSET(x) offsetof(TestSourceContext, x)
64 static const AVOption testsrc_options
[] = {
65 { "size", "set video size", OFFSET(size
), AV_OPT_TYPE_STRING
, {.str
= "320x240"}},
66 { "s", "set video size", OFFSET(size
), AV_OPT_TYPE_STRING
, {.str
= "320x240"}},
67 { "rate", "set video rate", OFFSET(rate
), AV_OPT_TYPE_STRING
, {.str
= "25"}, },
68 { "r", "set video rate", OFFSET(rate
), AV_OPT_TYPE_STRING
, {.str
= "25"}, },
69 { "duration", "set video duration", OFFSET(duration
), AV_OPT_TYPE_STRING
, {.str
= NULL
}, },
70 { "sar", "set video sample aspect ratio", OFFSET(sar
), AV_OPT_TYPE_RATIONAL
, {.dbl
= 1}, 0, INT_MAX
},
74 static av_cold
int init_common(AVFilterContext
*ctx
, const char *args
)
76 TestSourceContext
*test
= ctx
->priv
;
77 AVRational frame_rate_q
;
78 int64_t duration
= -1;
81 av_opt_set_defaults(test
);
83 if ((ret
= (av_set_options_string(test
, args
, "=", ":"))) < 0) {
84 av_log(ctx
, AV_LOG_ERROR
, "Error parsing options string: '%s'\n", args
);
88 if ((ret
= av_parse_video_size(&test
->w
, &test
->h
, test
->size
)) < 0) {
89 av_log(ctx
, AV_LOG_ERROR
, "Invalid frame size: '%s'\n", test
->size
);
93 if ((ret
= av_parse_video_rate(&frame_rate_q
, test
->rate
)) < 0 ||
94 frame_rate_q
.den
<= 0 || frame_rate_q
.num
<= 0) {
95 av_log(ctx
, AV_LOG_ERROR
, "Invalid frame rate: '%s'\n", test
->rate
);
99 if ((test
->duration
) && (ret
= av_parse_time(&duration
, test
->duration
, 1)) < 0) {
100 av_log(ctx
, AV_LOG_ERROR
, "Invalid duration: '%s'\n", test
->duration
);
104 test
->time_base
.num
= frame_rate_q
.den
;
105 test
->time_base
.den
= frame_rate_q
.num
;
106 test
->max_pts
= duration
>= 0 ?
107 av_rescale_q(duration
, AV_TIME_BASE_Q
, test
->time_base
) : -1;
111 av_log(ctx
, AV_LOG_DEBUG
, "size:%dx%d rate:%d/%d duration:%f sar:%d/%d\n",
112 test
->w
, test
->h
, frame_rate_q
.num
, frame_rate_q
.den
,
113 duration
< 0 ? -1 : test
->max_pts
* av_q2d(test
->time_base
),
114 test
->sar
.num
, test
->sar
.den
);
118 static int config_props(AVFilterLink
*outlink
)
120 TestSourceContext
*test
= outlink
->src
->priv
;
122 outlink
->w
= test
->w
;
123 outlink
->h
= test
->h
;
124 outlink
->sample_aspect_ratio
= test
->sar
;
125 outlink
->time_base
= test
->time_base
;
130 static int request_frame(AVFilterLink
*outlink
)
132 TestSourceContext
*test
= outlink
->src
->priv
;
133 AVFilterBufferRef
*picref
;
135 if (test
->max_pts
>= 0 && test
->pts
> test
->max_pts
)
137 picref
= ff_get_video_buffer(outlink
, AV_PERM_WRITE
, test
->w
, test
->h
);
139 return AVERROR(ENOMEM
);
141 picref
->pts
= test
->pts
++;
143 picref
->video
->key_frame
= 1;
144 picref
->video
->interlaced
= 0;
145 picref
->video
->pict_type
= AV_PICTURE_TYPE_I
;
146 picref
->video
->pixel_aspect
= test
->sar
;
148 test
->fill_picture_fn(outlink
->src
, picref
);
150 return ff_filter_frame(outlink
, picref
);
153 #if CONFIG_TESTSRC_FILTER
155 static const char *testsrc_get_name(void *ctx
)
160 static const AVClass testsrc_class
= {
161 .class_name
= "TestSourceContext",
162 .item_name
= testsrc_get_name
,
163 .option
= testsrc_options
,
167 * Fill a rectangle with value val.
169 * @param val the RGB value to set
170 * @param dst pointer to the destination buffer to fill
171 * @param dst_linesize linesize of destination
172 * @param segment_width width of the segment
173 * @param x horizontal coordinate where to draw the rectangle in the destination buffer
174 * @param y horizontal coordinate where to draw the rectangle in the destination buffer
175 * @param w width of the rectangle to draw, expressed as a number of segment_width units
176 * @param h height of the rectangle to draw, expressed as a number of segment_width units
178 static void draw_rectangle(unsigned val
, uint8_t *dst
, int dst_linesize
, unsigned segment_width
,
179 unsigned x
, unsigned y
, unsigned w
, unsigned h
)
184 dst
+= segment_width
* (step
* x
+ y
* dst_linesize
);
185 w
*= segment_width
* step
;
187 for (i
= 0; i
< h
; i
++) {
193 static void draw_digit(int digit
, uint8_t *dst
, unsigned dst_linesize
,
194 unsigned segment_width
)
199 #define LEFT_TOP_VBAR 8
200 #define LEFT_BOT_VBAR 16
201 #define RIGHT_TOP_VBAR 32
202 #define RIGHT_BOT_VBAR 64
206 { 1, 0, 5, 1 }, /* TOP_HBAR */
207 { 1, 6, 5, 1 }, /* MID_HBAR */
208 { 1, 12, 5, 1 }, /* BOT_HBAR */
209 { 0, 1, 1, 5 }, /* LEFT_TOP_VBAR */
210 { 0, 7, 1, 5 }, /* LEFT_BOT_VBAR */
211 { 6, 1, 1, 5 }, /* RIGHT_TOP_VBAR */
212 { 6, 7, 1, 5 } /* RIGHT_BOT_VBAR */
214 static const unsigned char masks
[10] = {
215 /* 0 */ TOP_HBAR
|BOT_HBAR
|LEFT_TOP_VBAR
|LEFT_BOT_VBAR
|RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
216 /* 1 */ RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
217 /* 2 */ TOP_HBAR
|MID_HBAR
|BOT_HBAR
|LEFT_BOT_VBAR
|RIGHT_TOP_VBAR
,
218 /* 3 */ TOP_HBAR
|MID_HBAR
|BOT_HBAR
|RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
219 /* 4 */ MID_HBAR
|LEFT_TOP_VBAR
|RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
220 /* 5 */ TOP_HBAR
|BOT_HBAR
|MID_HBAR
|LEFT_TOP_VBAR
|RIGHT_BOT_VBAR
,
221 /* 6 */ TOP_HBAR
|BOT_HBAR
|MID_HBAR
|LEFT_TOP_VBAR
|LEFT_BOT_VBAR
|RIGHT_BOT_VBAR
,
222 /* 7 */ TOP_HBAR
|RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
223 /* 8 */ TOP_HBAR
|BOT_HBAR
|MID_HBAR
|LEFT_TOP_VBAR
|LEFT_BOT_VBAR
|RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
224 /* 9 */ TOP_HBAR
|BOT_HBAR
|MID_HBAR
|LEFT_TOP_VBAR
|RIGHT_TOP_VBAR
|RIGHT_BOT_VBAR
,
226 unsigned mask
= masks
[digit
];
229 draw_rectangle(0, dst
, dst_linesize
, segment_width
, 0, 0, 8, 13);
230 for (i
= 0; i
< FF_ARRAY_ELEMS(segments
); i
++)
232 draw_rectangle(255, dst
, dst_linesize
, segment_width
,
233 segments
[i
].x
, segments
[i
].y
, segments
[i
].w
, segments
[i
].h
);
236 #define GRADIENT_SIZE (6 * 256)
238 static void test_fill_picture(AVFilterContext
*ctx
, AVFilterBufferRef
*picref
)
240 TestSourceContext
*test
= ctx
->priv
;
243 int color
, color_rest
;
247 int dquad_x
, dquad_y
;
248 int grad
, dgrad
, rgrad
, drgrad
;
252 uint8_t *data
= picref
->data
[0];
253 int width
= picref
->video
->w
;
254 int height
= picref
->video
->h
;
256 /* draw colored bars and circle */
257 radius
= (width
+ height
) / 4;
258 quad0
= width
* width
/ 4 + height
* height
/ 4 - radius
* radius
;
259 dquad_y
= 1 - height
;
261 for (y
= 0; y
< height
; y
++) {
267 for (x
= 0; x
< width
; x
++) {
273 *(p
++) = icolor
& 1 ? 255 : 0;
274 *(p
++) = icolor
& 2 ? 255 : 0;
275 *(p
++) = icolor
& 4 ? 255 : 0;
277 if (color_rest
>= width
) {
284 p0
+= picref
->linesize
[0];
287 /* draw sliding color line */
288 p
= data
+ picref
->linesize
[0] * height
* 3/4;
289 grad
= (256 * test
->nb_frame
* test
->time_base
.num
/ test
->time_base
.den
) %
292 dgrad
= GRADIENT_SIZE
/ width
;
293 drgrad
= GRADIENT_SIZE
% width
;
294 for (x
= 0; x
< width
; x
++) {
296 grad
< 256 || grad
>= 5 * 256 ? 255 :
297 grad
>= 2 * 256 && grad
< 4 * 256 ? 0 :
298 grad
< 2 * 256 ? 2 * 256 - 1 - grad
: grad
- 4 * 256;
300 grad
>= 4 * 256 ? 0 :
301 grad
>= 1 * 256 && grad
< 3 * 256 ? 255 :
302 grad
< 1 * 256 ? grad
: 4 * 256 - 1 - grad
;
305 grad
>= 3 * 256 && grad
< 5 * 256 ? 255 :
306 grad
< 3 * 256 ? grad
- 2 * 256 : 6 * 256 - 1 - grad
;
309 if (rgrad
>= GRADIENT_SIZE
) {
311 rgrad
-= GRADIENT_SIZE
;
313 if (grad
>= GRADIENT_SIZE
)
314 grad
-= GRADIENT_SIZE
;
316 for (y
= height
/ 8; y
> 0; y
--) {
317 memcpy(p
, p
- picref
->linesize
[0], 3 * width
);
318 p
+= picref
->linesize
[0];
322 seg_size
= width
/ 80;
323 if (seg_size
>= 1 && height
>= 13 * seg_size
) {
324 second
= test
->nb_frame
* test
->time_base
.num
/ test
->time_base
.den
;
325 x
= width
- (width
- seg_size
* 64) / 2;
326 y
= (height
- seg_size
* 13) / 2;
327 p
= data
+ (x
*3 + y
* picref
->linesize
[0]);
328 for (i
= 0; i
< 8; i
++) {
329 p
-= 3 * 8 * seg_size
;
330 draw_digit(second
% 10, p
, picref
->linesize
[0], seg_size
);
338 static av_cold
int test_init(AVFilterContext
*ctx
, const char *args
)
340 TestSourceContext
*test
= ctx
->priv
;
342 test
->class = &testsrc_class
;
343 test
->fill_picture_fn
= test_fill_picture
;
344 return init_common(ctx
, args
);
347 static int test_query_formats(AVFilterContext
*ctx
)
349 static const enum AVPixelFormat pix_fmts
[] = {
350 AV_PIX_FMT_RGB24
, AV_PIX_FMT_NONE
352 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
356 static const AVFilterPad avfilter_vsrc_testsrc_outputs
[] = {
359 .type
= AVMEDIA_TYPE_VIDEO
,
360 .request_frame
= request_frame
,
361 .config_props
= config_props
,
366 AVFilter avfilter_vsrc_testsrc
= {
368 .description
= NULL_IF_CONFIG_SMALL("Generate test pattern."),
369 .priv_size
= sizeof(TestSourceContext
),
372 .query_formats
= test_query_formats
,
376 .outputs
= avfilter_vsrc_testsrc_outputs
,
379 #endif /* CONFIG_TESTSRC_FILTER */
381 #if CONFIG_RGBTESTSRC_FILTER
383 static const char *rgbtestsrc_get_name(void *ctx
)
388 static const AVClass rgbtestsrc_class
= {
389 .class_name
= "RGBTestSourceContext",
390 .item_name
= rgbtestsrc_get_name
,
391 .option
= testsrc_options
,
399 static void rgbtest_put_pixel(uint8_t *dst
, int dst_linesize
,
400 int x
, int y
, int r
, int g
, int b
, enum AVPixelFormat fmt
,
407 case AV_PIX_FMT_BGR444
: ((uint16_t*)(dst
+ y
*dst_linesize
))[x
] = ((r
>> 4) << 8) | ((g
>> 4) << 4) | (b
>> 4); break;
408 case AV_PIX_FMT_RGB444
: ((uint16_t*)(dst
+ y
*dst_linesize
))[x
] = ((b
>> 4) << 8) | ((g
>> 4) << 4) | (r
>> 4); break;
409 case AV_PIX_FMT_BGR555
: ((uint16_t*)(dst
+ y
*dst_linesize
))[x
] = ((r
>>3)<<10) | ((g
>>3)<<5) | (b
>>3); break;
410 case AV_PIX_FMT_RGB555
: ((uint16_t*)(dst
+ y
*dst_linesize
))[x
] = ((b
>>3)<<10) | ((g
>>3)<<5) | (r
>>3); break;
411 case AV_PIX_FMT_BGR565
: ((uint16_t*)(dst
+ y
*dst_linesize
))[x
] = ((r
>>3)<<11) | ((g
>>2)<<5) | (b
>>3); break;
412 case AV_PIX_FMT_RGB565
: ((uint16_t*)(dst
+ y
*dst_linesize
))[x
] = ((b
>>3)<<11) | ((g
>>2)<<5) | (r
>>3); break;
413 case AV_PIX_FMT_RGB24
:
414 case AV_PIX_FMT_BGR24
:
415 v
= (r
<< (rgba_map
[R
]*8)) + (g
<< (rgba_map
[G
]*8)) + (b
<< (rgba_map
[B
]*8));
416 p
= dst
+ 3*x
+ y
*dst_linesize
;
419 case AV_PIX_FMT_RGBA
:
420 case AV_PIX_FMT_BGRA
:
421 case AV_PIX_FMT_ARGB
:
422 case AV_PIX_FMT_ABGR
:
423 v
= (r
<< (rgba_map
[R
]*8)) + (g
<< (rgba_map
[G
]*8)) + (b
<< (rgba_map
[B
]*8));
424 p
= dst
+ 4*x
+ y
*dst_linesize
;
430 static void rgbtest_fill_picture(AVFilterContext
*ctx
, AVFilterBufferRef
*picref
)
432 TestSourceContext
*test
= ctx
->priv
;
433 int x
, y
, w
= picref
->video
->w
, h
= picref
->video
->h
;
435 for (y
= 0; y
< h
; y
++) {
436 for (x
= 0; x
< picref
->video
->w
; x
++) {
438 int r
= 0, g
= 0, b
= 0;
441 else if (3*y
< 2*h
) g
= c
;
444 rgbtest_put_pixel(picref
->data
[0], picref
->linesize
[0], x
, y
, r
, g
, b
,
445 ctx
->outputs
[0]->format
, test
->rgba_map
);
450 static av_cold
int rgbtest_init(AVFilterContext
*ctx
, const char *args
)
452 TestSourceContext
*test
= ctx
->priv
;
454 test
->class = &rgbtestsrc_class
;
455 test
->fill_picture_fn
= rgbtest_fill_picture
;
456 return init_common(ctx
, args
);
459 static int rgbtest_query_formats(AVFilterContext
*ctx
)
461 static const enum AVPixelFormat pix_fmts
[] = {
462 AV_PIX_FMT_RGBA
, AV_PIX_FMT_ARGB
, AV_PIX_FMT_BGRA
, AV_PIX_FMT_ABGR
,
463 AV_PIX_FMT_BGR24
, AV_PIX_FMT_RGB24
,
464 AV_PIX_FMT_RGB444
, AV_PIX_FMT_BGR444
,
465 AV_PIX_FMT_RGB565
, AV_PIX_FMT_BGR565
,
466 AV_PIX_FMT_RGB555
, AV_PIX_FMT_BGR555
,
469 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
473 static int rgbtest_config_props(AVFilterLink
*outlink
)
475 TestSourceContext
*test
= outlink
->src
->priv
;
477 switch (outlink
->format
) {
478 case AV_PIX_FMT_ARGB
: test
->rgba_map
[A
] = 0; test
->rgba_map
[R
] = 1; test
->rgba_map
[G
] = 2; test
->rgba_map
[B
] = 3; break;
479 case AV_PIX_FMT_ABGR
: test
->rgba_map
[A
] = 0; test
->rgba_map
[B
] = 1; test
->rgba_map
[G
] = 2; test
->rgba_map
[R
] = 3; break;
480 case AV_PIX_FMT_RGBA
:
481 case AV_PIX_FMT_RGB24
: test
->rgba_map
[R
] = 0; test
->rgba_map
[G
] = 1; test
->rgba_map
[B
] = 2; test
->rgba_map
[A
] = 3; break;
482 case AV_PIX_FMT_BGRA
:
483 case AV_PIX_FMT_BGR24
: test
->rgba_map
[B
] = 0; test
->rgba_map
[G
] = 1; test
->rgba_map
[R
] = 2; test
->rgba_map
[A
] = 3; break;
486 return config_props(outlink
);
489 static const AVFilterPad avfilter_vsrc_rgbtestsrc_outputs
[] = {
492 .type
= AVMEDIA_TYPE_VIDEO
,
493 .request_frame
= request_frame
,
494 .config_props
= rgbtest_config_props
,
499 AVFilter avfilter_vsrc_rgbtestsrc
= {
500 .name
= "rgbtestsrc",
501 .description
= NULL_IF_CONFIG_SMALL("Generate RGB test pattern."),
502 .priv_size
= sizeof(TestSourceContext
),
503 .init
= rgbtest_init
,
505 .query_formats
= rgbtest_query_formats
,
509 .outputs
= avfilter_vsrc_rgbtestsrc_outputs
,
512 #endif /* CONFIG_RGBTESTSRC_FILTER */