yading@11: /* yading@11: * Copyright (c) 2002 Michael Niedermayer yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or modify yading@11: * it under the terms of the GNU General Public License as published by yading@11: * the Free Software Foundation; either version 2 of the License, or yading@11: * (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the yading@11: * GNU General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU General Public License along yading@11: * with FFmpeg; if not, write to the Free Software Foundation, Inc., yading@11: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * MP test source, ported from MPlayer libmpcodecs/vf_test.c yading@11: */ yading@11: yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/opt.h" yading@11: #include "libavutil/parseutils.h" yading@11: #include "libavutil/pixdesc.h" yading@11: #include "avfilter.h" yading@11: #include "internal.h" yading@11: #include "formats.h" yading@11: #include "video.h" yading@11: yading@11: #define WIDTH 512 yading@11: #define HEIGHT 512 yading@11: yading@11: enum test_type { yading@11: TEST_DC_LUMA, yading@11: TEST_DC_CHROMA, yading@11: TEST_FREQ_LUMA, yading@11: TEST_FREQ_CHROMA, yading@11: TEST_AMP_LUMA, yading@11: TEST_AMP_CHROMA, yading@11: TEST_CBP, yading@11: TEST_MV, yading@11: TEST_RING1, yading@11: TEST_RING2, yading@11: TEST_ALL, yading@11: TEST_NB yading@11: }; yading@11: yading@11: typedef struct MPTestContext { yading@11: const AVClass *class; yading@11: unsigned int frame_nb; yading@11: AVRational frame_rate; yading@11: int64_t pts, max_pts, duration; yading@11: int hsub, vsub; yading@11: enum test_type test; yading@11: } MPTestContext; yading@11: yading@11: #define OFFSET(x) offsetof(MPTestContext, x) yading@11: #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM yading@11: static const AVOption mptestsrc_options[]= { yading@11: { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, yading@11: { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, FLAGS }, yading@11: { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, yading@11: { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, yading@11: yading@11: { "test", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, "test" }, yading@11: { "t", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, "test" }, yading@11: { "dc_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "dc_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "freq_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "freq_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "amp_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "amp_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "cbp", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_CBP}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "mv", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_MV}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "ring1", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING1}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "ring2", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING2}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_ALL}, INT_MIN, INT_MAX, FLAGS, "test" }, yading@11: { NULL }, yading@11: }; yading@11: yading@11: AVFILTER_DEFINE_CLASS(mptestsrc); yading@11: yading@11: static double c[64]; yading@11: yading@11: static void init_idct(void) yading@11: { yading@11: int i, j; yading@11: yading@11: for (i = 0; i < 8; i++) { yading@11: double s = i == 0 ? sqrt(0.125) : 0.5; yading@11: yading@11: for (j = 0; j < 8; j++) yading@11: c[i*8+j] = s*cos((M_PI/8.0)*i*(j+0.5)); yading@11: } yading@11: } yading@11: yading@11: static void idct(uint8_t *dst, int dst_linesize, int src[64]) yading@11: { yading@11: int i, j, k; yading@11: double tmp[64]; yading@11: yading@11: for (i = 0; i < 8; i++) { yading@11: for (j = 0; j < 8; j++) { yading@11: double sum = 0.0; yading@11: yading@11: for (k = 0; k < 8; k++) yading@11: sum += c[k*8+j] * src[8*i+k]; yading@11: yading@11: tmp[8*i+j] = sum; yading@11: } yading@11: } yading@11: yading@11: for (j = 0; j < 8; j++) { yading@11: for (i = 0; i < 8; i++) { yading@11: double sum = 0.0; yading@11: yading@11: for (k = 0; k < 8; k++) yading@11: sum += c[k*8+i]*tmp[8*k+j]; yading@11: yading@11: dst[dst_linesize*i + j] = av_clip((int)floor(sum+0.5), 0, 255); yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void draw_dc(uint8_t *dst, int dst_linesize, int color, int w, int h) yading@11: { yading@11: int x, y; yading@11: yading@11: for (y = 0; y < h; y++) yading@11: for (x = 0; x < w; x++) yading@11: dst[x + y*dst_linesize] = color; yading@11: } yading@11: yading@11: static void draw_basis(uint8_t *dst, int dst_linesize, int amp, int freq, int dc) yading@11: { yading@11: int src[64]; yading@11: yading@11: memset(src, 0, 64*sizeof(int)); yading@11: src[0] = dc; yading@11: if (amp) yading@11: src[freq] = amp; yading@11: idct(dst, dst_linesize, src); yading@11: } yading@11: yading@11: static void draw_cbp(uint8_t *dst[3], int dst_linesize[3], int cbp, int amp, int dc) yading@11: { yading@11: if (cbp&1) draw_basis(dst[0] , dst_linesize[0], amp, 1, dc); yading@11: if (cbp&2) draw_basis(dst[0]+8 , dst_linesize[0], amp, 1, dc); yading@11: if (cbp&4) draw_basis(dst[0]+ 8*dst_linesize[0], dst_linesize[0], amp, 1, dc); yading@11: if (cbp&8) draw_basis(dst[0]+8+8*dst_linesize[0], dst_linesize[0], amp, 1, dc); yading@11: if (cbp&16) draw_basis(dst[1] , dst_linesize[1], amp, 1, dc); yading@11: if (cbp&32) draw_basis(dst[2] , dst_linesize[2], amp, 1, dc); yading@11: } yading@11: yading@11: static void dc_test(uint8_t *dst, int dst_linesize, int w, int h, int off) yading@11: { yading@11: const int step = FFMAX(256/(w*h/256), 1); yading@11: int x, y, color = off; yading@11: yading@11: for (y = 0; y < h; y += 16) { yading@11: for (x = 0; x < w; x += 16) { yading@11: draw_dc(dst + x + y*dst_linesize, dst_linesize, color, 8, 8); yading@11: color += step; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void freq_test(uint8_t *dst, int dst_linesize, int off) yading@11: { yading@11: int x, y, freq = 0; yading@11: yading@11: for (y = 0; y < 8*16; y += 16) { yading@11: for (x = 0; x < 8*16; x += 16) { yading@11: draw_basis(dst + x + y*dst_linesize, dst_linesize, 4*(96+off), freq, 128*8); yading@11: freq++; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void amp_test(uint8_t *dst, int dst_linesize, int off) yading@11: { yading@11: int x, y, amp = off; yading@11: yading@11: for (y = 0; y < 16*16; y += 16) { yading@11: for (x = 0; x < 16*16; x += 16) { yading@11: draw_basis(dst + x + y*dst_linesize, dst_linesize, 4*amp, 1, 128*8); yading@11: amp++; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void cbp_test(uint8_t *dst[3], int dst_linesize[3], int off) yading@11: { yading@11: int x, y, cbp = 0; yading@11: yading@11: for (y = 0; y < 16*8; y += 16) { yading@11: for (x = 0; x < 16*8; x += 16) { yading@11: uint8_t *dst1[3]; yading@11: dst1[0] = dst[0] + x*2 + y*2*dst_linesize[0]; yading@11: dst1[1] = dst[1] + x + y* dst_linesize[1]; yading@11: dst1[2] = dst[2] + x + y* dst_linesize[2]; yading@11: yading@11: draw_cbp(dst1, dst_linesize, cbp, (64+off)*4, 128*8); yading@11: cbp++; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void mv_test(uint8_t *dst, int dst_linesize, int off) yading@11: { yading@11: int x, y; yading@11: yading@11: for (y = 0; y < 16*16; y++) { yading@11: if (y&16) yading@11: continue; yading@11: for (x = 0; x < 16*16; x++) yading@11: dst[x + y*dst_linesize] = x + off*8/(y/32+1); yading@11: } yading@11: } yading@11: yading@11: static void ring1_test(uint8_t *dst, int dst_linesize, int off) yading@11: { yading@11: int x, y, color = 0; yading@11: yading@11: for (y = off; y < 16*16; y += 16) { yading@11: for (x = off; x < 16*16; x += 16) { yading@11: draw_dc(dst + x + y*dst_linesize, dst_linesize, ((x+y)&16) ? color : -color, 16, 16); yading@11: color++; yading@11: } yading@11: } yading@11: } yading@11: yading@11: static void ring2_test(uint8_t *dst, int dst_linesize, int off) yading@11: { yading@11: int x, y; yading@11: yading@11: for (y = 0; y < 16*16; y++) { yading@11: for (x = 0; x < 16*16; x++) { yading@11: double d = sqrt((x-8*16)*(x-8*16) + (y-8*16)*(y-8*16)); yading@11: double r = d/20 - (int)(d/20); yading@11: if (r < off/30.0) { yading@11: dst[x + y*dst_linesize] = 255; yading@11: dst[x + y*dst_linesize+256] = 0; yading@11: } else { yading@11: dst[x + y*dst_linesize] = x; yading@11: dst[x + y*dst_linesize+256] = x; yading@11: } yading@11: } yading@11: } yading@11: } yading@11: yading@11: static av_cold int init(AVFilterContext *ctx) yading@11: { yading@11: MPTestContext *test = ctx->priv; yading@11: yading@11: test->max_pts = test->duration >= 0 ? yading@11: av_rescale_q(test->duration, AV_TIME_BASE_Q, av_inv_q(test->frame_rate)) : -1; yading@11: test->frame_nb = 0; yading@11: test->pts = 0; yading@11: yading@11: av_log(ctx, AV_LOG_VERBOSE, "rate:%d/%d duration:%f\n", yading@11: test->frame_rate.num, test->frame_rate.den, yading@11: test->duration < 0 ? -1 : test->max_pts * av_q2d(av_inv_q(test->frame_rate))); yading@11: init_idct(); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int config_props(AVFilterLink *outlink) yading@11: { yading@11: AVFilterContext *ctx = outlink->src; yading@11: MPTestContext *test = ctx->priv; yading@11: const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(outlink->format); yading@11: yading@11: test->hsub = pix_desc->log2_chroma_w; yading@11: test->vsub = pix_desc->log2_chroma_h; yading@11: yading@11: outlink->w = WIDTH; yading@11: outlink->h = HEIGHT; yading@11: outlink->time_base = av_inv_q(test->frame_rate); yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int query_formats(AVFilterContext *ctx) yading@11: { yading@11: static const enum AVPixelFormat pix_fmts[] = { yading@11: AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE yading@11: }; yading@11: yading@11: ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); yading@11: return 0; yading@11: } yading@11: yading@11: static int request_frame(AVFilterLink *outlink) yading@11: { yading@11: MPTestContext *test = outlink->src->priv; yading@11: AVFrame *picref; yading@11: int w = WIDTH, h = HEIGHT, cw = w>>test->hsub, ch = h>>test->vsub; yading@11: unsigned int frame = test->frame_nb; yading@11: enum test_type tt = test->test; yading@11: int i; yading@11: yading@11: if (test->max_pts >= 0 && test->pts > test->max_pts) yading@11: return AVERROR_EOF; yading@11: picref = ff_get_video_buffer(outlink, w, h); yading@11: if (!picref) yading@11: return AVERROR(ENOMEM); yading@11: picref->pts = test->pts++; yading@11: yading@11: // clean image yading@11: for (i = 0; i < h; i++) yading@11: memset(picref->data[0] + i*picref->linesize[0], 0, w); yading@11: for (i = 0; i < ch; i++) { yading@11: memset(picref->data[1] + i*picref->linesize[1], 128, cw); yading@11: memset(picref->data[2] + i*picref->linesize[2], 128, cw); yading@11: } yading@11: yading@11: if (tt == TEST_ALL && frame%30) /* draw a black frame at the beginning of each test */ yading@11: tt = (frame/30)%(TEST_NB-1); yading@11: yading@11: switch (tt) { yading@11: case TEST_DC_LUMA: dc_test(picref->data[0], picref->linesize[0], 256, 256, frame%30); break; yading@11: case TEST_DC_CHROMA: dc_test(picref->data[1], picref->linesize[1], 256, 256, frame%30); break; yading@11: case TEST_FREQ_LUMA: freq_test(picref->data[0], picref->linesize[0], frame%30); break; yading@11: case TEST_FREQ_CHROMA: freq_test(picref->data[1], picref->linesize[1], frame%30); break; yading@11: case TEST_AMP_LUMA: amp_test(picref->data[0], picref->linesize[0], frame%30); break; yading@11: case TEST_AMP_CHROMA: amp_test(picref->data[1], picref->linesize[1], frame%30); break; yading@11: case TEST_CBP: cbp_test(picref->data , picref->linesize , frame%30); break; yading@11: case TEST_MV: mv_test(picref->data[0], picref->linesize[0], frame%30); break; yading@11: case TEST_RING1: ring1_test(picref->data[0], picref->linesize[0], frame%30); break; yading@11: case TEST_RING2: ring2_test(picref->data[0], picref->linesize[0], frame%30); break; yading@11: } yading@11: yading@11: test->frame_nb++; yading@11: return ff_filter_frame(outlink, picref); yading@11: } yading@11: yading@11: static const AVFilterPad mptestsrc_outputs[] = { yading@11: { yading@11: .name = "default", yading@11: .type = AVMEDIA_TYPE_VIDEO, yading@11: .request_frame = request_frame, yading@11: .config_props = config_props, yading@11: }, yading@11: { NULL } yading@11: }; yading@11: yading@11: AVFilter avfilter_vsrc_mptestsrc = { yading@11: .name = "mptestsrc", yading@11: .description = NULL_IF_CONFIG_SMALL("Generate various test pattern."), yading@11: .priv_size = sizeof(MPTestContext), yading@11: .init = init, yading@11: yading@11: .query_formats = query_formats, yading@11: yading@11: .inputs = NULL, yading@11: .outputs = mptestsrc_outputs, yading@11: .priv_class = &mptestsrc_class, yading@11: };