00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00026 #include "libavutil/avstring.h"
00027 #include "libavutil/opt.h"
00028 #include "libavutil/parseutils.h"
00029 #include "libavutil/pixdesc.h"
00030 #include "avfilter.h"
00031 #include "internal.h"
00032 #include "formats.h"
00033 #include "video.h"
00034
00035 #define WIDTH 512
00036 #define HEIGHT 512
00037
00038 enum test_type {
00039 TEST_DC_LUMA,
00040 TEST_DC_CHROMA,
00041 TEST_FREQ_LUMA,
00042 TEST_FREQ_CHROMA,
00043 TEST_AMP_LUMA,
00044 TEST_AMP_CHROMA,
00045 TEST_CBP,
00046 TEST_MV,
00047 TEST_RING1,
00048 TEST_RING2,
00049 TEST_ALL,
00050 TEST_NB
00051 };
00052
00053 typedef struct MPTestContext {
00054 const AVClass *class;
00055 unsigned int frame_nb;
00056 AVRational time_base;
00057 int64_t pts, max_pts;
00058 int hsub, vsub;
00059 char *size, *rate, *duration;
00060 enum test_type test;
00061 } MPTestContext;
00062
00063 #define OFFSET(x) offsetof(MPTestContext, x)
00064
00065 static const AVOption mptestsrc_options[]= {
00066 { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0 },
00067 { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0 },
00068 { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
00069 { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0 },
00070
00071 { "test", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, 0, "test" },
00072 { "t", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, 0, "test" },
00073 { "dc_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_LUMA}, INT_MIN, INT_MAX, 0, "test" },
00074 { "dc_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_CHROMA}, INT_MIN, INT_MAX, 0, "test" },
00075 { "freq_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_LUMA}, INT_MIN, INT_MAX, 0, "test" },
00076 { "freq_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_CHROMA}, INT_MIN, INT_MAX, 0, "test" },
00077 { "amp_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_LUMA}, INT_MIN, INT_MAX, 0, "test" },
00078 { "amp_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_CHROMA}, INT_MIN, INT_MAX, 0, "test" },
00079 { "cbp", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_CBP}, INT_MIN, INT_MAX, 0, "test" },
00080 { "mv", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_MV}, INT_MIN, INT_MAX, 0, "test" },
00081 { "ring1", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING1}, INT_MIN, INT_MAX, 0, "test" },
00082 { "ring2", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING2}, INT_MIN, INT_MAX, 0, "test" },
00083 { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_ALL}, INT_MIN, INT_MAX, 0, "test" },
00084
00085 { NULL },
00086 };
00087
00088 AVFILTER_DEFINE_CLASS(mptestsrc);
00089
00090 static double c[64];
00091
00092 static void init_idct(void)
00093 {
00094 int i, j;
00095
00096 for (i = 0; i < 8; i++) {
00097 double s = i == 0 ? sqrt(0.125) : 0.5;
00098
00099 for (j = 0; j < 8; j++)
00100 c[i*8+j] = s*cos((M_PI/8.0)*i*(j+0.5));
00101 }
00102 }
00103
00104 static void idct(uint8_t *dst, int dst_linesize, int src[64])
00105 {
00106 int i, j, k;
00107 double tmp[64];
00108
00109 for (i = 0; i < 8; i++) {
00110 for (j = 0; j < 8; j++) {
00111 double sum = 0.0;
00112
00113 for (k = 0; k < 8; k++)
00114 sum += c[k*8+j] * src[8*i+k];
00115
00116 tmp[8*i+j] = sum;
00117 }
00118 }
00119
00120 for (j = 0; j < 8; j++) {
00121 for (i = 0; i < 8; i++) {
00122 double sum = 0.0;
00123
00124 for (k = 0; k < 8; k++)
00125 sum += c[k*8+i]*tmp[8*k+j];
00126
00127 dst[dst_linesize*i + j] = av_clip((int)floor(sum+0.5), 0, 255);
00128 }
00129 }
00130 }
00131
00132 static void draw_dc(uint8_t *dst, int dst_linesize, int color, int w, int h)
00133 {
00134 int x, y;
00135
00136 for (y = 0; y < h; y++)
00137 for (x = 0; x < w; x++)
00138 dst[x + y*dst_linesize] = color;
00139 }
00140
00141 static void draw_basis(uint8_t *dst, int dst_linesize, int amp, int freq, int dc)
00142 {
00143 int src[64];
00144
00145 memset(src, 0, 64*sizeof(int));
00146 src[0] = dc;
00147 if (amp)
00148 src[freq] = amp;
00149 idct(dst, dst_linesize, src);
00150 }
00151
00152 static void draw_cbp(uint8_t *dst[3], int dst_linesize[3], int cbp, int amp, int dc)
00153 {
00154 if (cbp&1) draw_basis(dst[0] , dst_linesize[0], amp, 1, dc);
00155 if (cbp&2) draw_basis(dst[0]+8 , dst_linesize[0], amp, 1, dc);
00156 if (cbp&4) draw_basis(dst[0]+ 8*dst_linesize[0], dst_linesize[0], amp, 1, dc);
00157 if (cbp&8) draw_basis(dst[0]+8+8*dst_linesize[0], dst_linesize[0], amp, 1, dc);
00158 if (cbp&16) draw_basis(dst[1] , dst_linesize[1], amp, 1, dc);
00159 if (cbp&32) draw_basis(dst[2] , dst_linesize[2], amp, 1, dc);
00160 }
00161
00162 static void dc_test(uint8_t *dst, int dst_linesize, int w, int h, int off)
00163 {
00164 const int step = FFMAX(256/(w*h/256), 1);
00165 int x, y, color = off;
00166
00167 for (y = 0; y < h; y += 16) {
00168 for (x = 0; x < w; x += 16) {
00169 draw_dc(dst + x + y*dst_linesize, dst_linesize, color, 8, 8);
00170 color += step;
00171 }
00172 }
00173 }
00174
00175 static void freq_test(uint8_t *dst, int dst_linesize, int off)
00176 {
00177 int x, y, freq = 0;
00178
00179 for (y = 0; y < 8*16; y += 16) {
00180 for (x = 0; x < 8*16; x += 16) {
00181 draw_basis(dst + x + y*dst_linesize, dst_linesize, 4*(96+off), freq, 128*8);
00182 freq++;
00183 }
00184 }
00185 }
00186
00187 static void amp_test(uint8_t *dst, int dst_linesize, int off)
00188 {
00189 int x, y, amp = off;
00190
00191 for (y = 0; y < 16*16; y += 16) {
00192 for (x = 0; x < 16*16; x += 16) {
00193 draw_basis(dst + x + y*dst_linesize, dst_linesize, 4*amp, 1, 128*8);
00194 amp++;
00195 }
00196 }
00197 }
00198
00199 static void cbp_test(uint8_t *dst[3], int dst_linesize[3], int off)
00200 {
00201 int x, y, cbp = 0;
00202
00203 for (y = 0; y < 16*8; y += 16) {
00204 for (x = 0; x < 16*8; x += 16) {
00205 uint8_t *dst1[3];
00206 dst1[0] = dst[0] + x*2 + y*2*dst_linesize[0];
00207 dst1[1] = dst[1] + x + y* dst_linesize[1];
00208 dst1[2] = dst[2] + x + y* dst_linesize[2];
00209
00210 draw_cbp(dst1, dst_linesize, cbp, (64+off)*4, 128*8);
00211 cbp++;
00212 }
00213 }
00214 }
00215
00216 static void mv_test(uint8_t *dst, int dst_linesize, int off)
00217 {
00218 int x, y;
00219
00220 for (y = 0; y < 16*16; y++) {
00221 if (y&16)
00222 continue;
00223 for (x = 0; x < 16*16; x++)
00224 dst[x + y*dst_linesize] = x + off*8/(y/32+1);
00225 }
00226 }
00227
00228 static void ring1_test(uint8_t *dst, int dst_linesize, int off)
00229 {
00230 int x, y, color = 0;
00231
00232 for (y = off; y < 16*16; y += 16) {
00233 for (x = off; x < 16*16; x += 16) {
00234 draw_dc(dst + x + y*dst_linesize, dst_linesize, ((x+y)&16) ? color : -color, 16, 16);
00235 color++;
00236 }
00237 }
00238 }
00239
00240 static void ring2_test(uint8_t *dst, int dst_linesize, int off)
00241 {
00242 int x, y;
00243
00244 for (y = 0; y < 16*16; y++) {
00245 for (x = 0; x < 16*16; x++) {
00246 double d = sqrt((x-8*16)*(x-8*16) + (y-8*16)*(y-8*16));
00247 double r = d/20 - (int)(d/20);
00248 if (r < off/30.0) {
00249 dst[x + y*dst_linesize] = 255;
00250 dst[x + y*dst_linesize+256] = 0;
00251 } else {
00252 dst[x + y*dst_linesize] = x;
00253 dst[x + y*dst_linesize+256] = x;
00254 }
00255 }
00256 }
00257 }
00258
00259 static av_cold int init(AVFilterContext *ctx, const char *args)
00260 {
00261 MPTestContext *test = ctx->priv;
00262 AVRational frame_rate_q;
00263 int64_t duration = -1;
00264 int ret;
00265
00266 test->class = &mptestsrc_class;
00267 av_opt_set_defaults(test);
00268
00269 if ((ret = (av_set_options_string(test, args, "=", ":"))) < 0)
00270 return ret;
00271
00272 if ((ret = av_parse_video_rate(&frame_rate_q, test->rate)) < 0) {
00273 av_log(ctx, AV_LOG_ERROR, "Invalid frame rate: '%s'\n", test->rate);
00274 return ret;
00275 }
00276
00277 if ((test->duration) && (ret = av_parse_time(&duration, test->duration, 1)) < 0) {
00278 av_log(ctx, AV_LOG_ERROR, "Invalid duration: '%s'\n", test->duration);
00279 return ret;
00280 }
00281
00282 test->time_base.num = frame_rate_q.den;
00283 test->time_base.den = frame_rate_q.num;
00284 test->max_pts = duration >= 0 ?
00285 av_rescale_q(duration, AV_TIME_BASE_Q, test->time_base) : -1;
00286 test->frame_nb = 0;
00287 test->pts = 0;
00288
00289 av_log(ctx, AV_LOG_VERBOSE, "rate:%d/%d duration:%f\n",
00290 frame_rate_q.num, frame_rate_q.den,
00291 duration < 0 ? -1 : test->max_pts * av_q2d(test->time_base));
00292 init_idct();
00293
00294 return 0;
00295 }
00296
00297 static int config_props(AVFilterLink *outlink)
00298 {
00299 AVFilterContext *ctx = outlink->src;
00300 MPTestContext *test = ctx->priv;
00301 const AVPixFmtDescriptor *pix_desc = &av_pix_fmt_descriptors[outlink->format];
00302
00303 test->hsub = pix_desc->log2_chroma_w;
00304 test->vsub = pix_desc->log2_chroma_h;
00305
00306 outlink->w = WIDTH;
00307 outlink->h = HEIGHT;
00308 outlink->time_base = test->time_base;
00309
00310 return 0;
00311 }
00312
00313 static int query_formats(AVFilterContext *ctx)
00314 {
00315 static const enum PixelFormat pix_fmts[] = {
00316 PIX_FMT_YUV420P, PIX_FMT_NONE
00317 };
00318
00319 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
00320 return 0;
00321 }
00322
00323 static int request_frame(AVFilterLink *outlink)
00324 {
00325 MPTestContext *test = outlink->src->priv;
00326 AVFilterBufferRef *picref;
00327 int w = WIDTH, h = HEIGHT, ch = h>>test->vsub;
00328 unsigned int frame = test->frame_nb;
00329 enum test_type tt = test->test;
00330
00331 if (test->max_pts >= 0 && test->pts > test->max_pts)
00332 return AVERROR_EOF;
00333 picref = ff_get_video_buffer(outlink, AV_PERM_WRITE, w, h);
00334 picref->pts = test->pts++;
00335
00336
00337 memset(picref->data[0], 0, picref->linesize[0] * h);
00338 memset(picref->data[1], 128, picref->linesize[1] * ch);
00339 memset(picref->data[2], 128, picref->linesize[2] * ch);
00340
00341 if (tt == TEST_ALL && frame%30)
00342 tt = (frame/30)%(TEST_NB-1);
00343
00344 switch (tt) {
00345 case TEST_DC_LUMA: dc_test(picref->data[0], picref->linesize[0], 256, 256, frame%30); break;
00346 case TEST_DC_CHROMA: dc_test(picref->data[1], picref->linesize[1], 256, 256, frame%30); break;
00347 case TEST_FREQ_LUMA: freq_test(picref->data[0], picref->linesize[0], frame%30); break;
00348 case TEST_FREQ_CHROMA: freq_test(picref->data[1], picref->linesize[1], frame%30); break;
00349 case TEST_AMP_LUMA: amp_test(picref->data[0], picref->linesize[0], frame%30); break;
00350 case TEST_AMP_CHROMA: amp_test(picref->data[1], picref->linesize[1], frame%30); break;
00351 case TEST_CBP: cbp_test(picref->data , picref->linesize , frame%30); break;
00352 case TEST_MV: mv_test(picref->data[0], picref->linesize[0], frame%30); break;
00353 case TEST_RING1: ring1_test(picref->data[0], picref->linesize[0], frame%30); break;
00354 case TEST_RING2: ring2_test(picref->data[0], picref->linesize[0], frame%30); break;
00355 }
00356
00357 test->frame_nb++;
00358
00359 ff_start_frame(outlink, avfilter_ref_buffer(picref, ~0));
00360 ff_draw_slice(outlink, 0, picref->video->h, 1);
00361 ff_end_frame(outlink);
00362 avfilter_unref_buffer(picref);
00363
00364 return 0;
00365 }
00366
00367 AVFilter avfilter_vsrc_mptestsrc = {
00368 .name = "mptestsrc",
00369 .description = NULL_IF_CONFIG_SMALL("Generate various test pattern."),
00370 .priv_size = sizeof(MPTestContext),
00371 .init = init,
00372
00373 .query_formats = query_formats,
00374
00375 .inputs = (const AVFilterPad[]) {{ .name = NULL}},
00376
00377 .outputs = (const AVFilterPad[]) {{ .name = "default",
00378 .type = AVMEDIA_TYPE_VIDEO,
00379 .request_frame = request_frame,
00380 .config_props = config_props, },
00381 { .name = NULL }},
00382 };