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