1/*
2 * Generates a synthetic YUV video sequence suitable for codec testing.
3 * NOTE: No floats are used to guarantee a bit exact output.
4 *
5 * Copyright (c) 2002 Fabrice Bellard
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24#include <stdlib.h>
25#include <stdint.h>
26#include <stdio.h>
27
28#define SCALEBITS 8
29#define ONE_HALF  (1 << (SCALEBITS - 1))
30#define FIX(x)    ((int) ((x) * (1L<<SCALEBITS) + 0.5))
31
32static void rgb24_to_yuv420p(uint8_t *lum, uint8_t *cb, uint8_t *cr,
33                              uint8_t *src, int width, int height)
34{
35    int wrap, wrap3, x, y;
36    int r, g, b, r1, g1, b1;
37    uint8_t *p;
38
39    wrap = width;
40    wrap3 = width * 3;
41    p = src;
42    for(y=0;y<height;y+=2) {
43        for(x=0;x<width;x+=2) {
44            r = p[0];
45            g = p[1];
46            b = p[2];
47            r1 = r;
48            g1 = g;
49            b1 = b;
50            lum[0] = (FIX(0.29900) * r + FIX(0.58700) * g +
51                      FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;
52            r = p[3];
53            g = p[4];
54            b = p[5];
55            r1 += r;
56            g1 += g;
57            b1 += b;
58            lum[1] = (FIX(0.29900) * r + FIX(0.58700) * g +
59                      FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;
60            p += wrap3;
61            lum += wrap;
62
63            r = p[0];
64            g = p[1];
65            b = p[2];
66            r1 += r;
67            g1 += g;
68            b1 += b;
69            lum[0] = (FIX(0.29900) * r + FIX(0.58700) * g +
70                      FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;
71            r = p[3];
72            g = p[4];
73            b = p[5];
74            r1 += r;
75            g1 += g;
76            b1 += b;
77            lum[1] = (FIX(0.29900) * r + FIX(0.58700) * g +
78                      FIX(0.11400) * b + ONE_HALF) >> SCALEBITS;
79
80            cb[0] = ((- FIX(0.16874) * r1 - FIX(0.33126) * g1 +
81                      FIX(0.50000) * b1 + 4 * ONE_HALF - 1) >> (SCALEBITS + 2)) + 128;
82            cr[0] = ((FIX(0.50000) * r1 - FIX(0.41869) * g1 -
83                     FIX(0.08131) * b1 + 4 * ONE_HALF - 1) >> (SCALEBITS + 2)) + 128;
84
85            cb++;
86            cr++;
87            p += -wrap3 + 2 * 3;
88            lum += -wrap + 2;
89        }
90        p += wrap3;
91        lum += wrap;
92    }
93}
94
95/* cif format */
96#define DEFAULT_WIDTH   352
97#define DEFAULT_HEIGHT  288
98#define DEFAULT_NB_PICT 50 /* 2 seconds */
99
100static void pgmyuv_save(const char *filename, int w, int h,
101                        unsigned char *rgb_tab)
102{
103    FILE *f;
104    int i, h2, w2;
105    unsigned char *cb, *cr;
106    unsigned char *lum_tab, *cb_tab, *cr_tab;
107
108    lum_tab = malloc(w * h);
109    cb_tab = malloc((w * h) / 4);
110    cr_tab = malloc((w * h) / 4);
111
112    rgb24_to_yuv420p(lum_tab, cb_tab, cr_tab, rgb_tab, w, h);
113
114    f = fopen(filename,"wb");
115    fprintf(f, "P5\n%d %d\n%d\n", w, (h * 3) / 2, 255);
116    fwrite(lum_tab, 1, w * h, f);
117    h2 = h / 2;
118    w2 = w / 2;
119    cb = cb_tab;
120    cr = cr_tab;
121    for(i=0;i<h2;i++) {
122        fwrite(cb, 1, w2, f);
123        fwrite(cr, 1, w2, f);
124        cb += w2;
125        cr += w2;
126    }
127    fclose(f);
128
129    free(lum_tab);
130    free(cb_tab);
131    free(cr_tab);
132}
133
134unsigned char *rgb_tab;
135int width, height, wrap;
136
137static void put_pixel(int x, int y, int r, int g, int b)
138{
139    unsigned char *p;
140
141    if (x < 0 || x >= width ||
142        y < 0 || y >= height)
143        return;
144
145    p = rgb_tab + y * wrap + x * 3;
146    p[0] = r;
147    p[1] = g;
148    p[2] = b;
149}
150
151static unsigned int myrnd(unsigned int *seed_ptr, int n)
152{
153    unsigned int seed, val;
154
155    seed = *seed_ptr;
156    seed = (seed * 314159) + 1;
157    if (n == 256) {
158        val = seed >> 24;
159    } else {
160        val = seed % n;
161    }
162    *seed_ptr = seed;
163    return val;
164}
165
166#define NOISE_X  10
167#define NOISE_Y  30
168#define NOISE_W  26
169
170#define FRAC_BITS 8
171#define FRAC_ONE (1 << FRAC_BITS)
172
173/* cosine approximate with 1-x^2 */
174static int int_cos(int a)
175{
176    int v, neg;
177    a = a & (FRAC_ONE - 1);
178    if (a >= (FRAC_ONE / 2))
179        a = FRAC_ONE - a;
180    neg = 0;
181    if (a > (FRAC_ONE / 4)) {
182        neg = -1;
183        a = (FRAC_ONE / 2) - a;
184    }
185    v = FRAC_ONE - ((a * a) >> 4);
186    v = (v ^ neg) - neg;
187    return v;
188}
189
190#define NB_OBJS  10
191
192typedef struct VObj {
193    int x, y, w, h;
194    int r, g, b;
195} VObj;
196
197VObj objs[NB_OBJS];
198
199unsigned int seed = 1;
200
201static void gen_image(int num, int w, int h)
202{
203    int r, g, b, x, y, i, dx, dy, x1, y1;
204    unsigned int seed1;
205
206    if (num == 0) {
207        for(i=0;i<NB_OBJS;i++) {
208            objs[i].x = myrnd(&seed, w);
209            objs[i].y = myrnd(&seed, h);
210            objs[i].w = myrnd(&seed, w / 4) + 10;
211            objs[i].h = myrnd(&seed, h / 4) + 10;
212            objs[i].r = myrnd(&seed, 256);
213            objs[i].g = myrnd(&seed, 256);
214            objs[i].b = myrnd(&seed, 256);
215        }
216    }
217
218    /* first a moving background with gradients */
219    /* test motion estimation */
220    dx = int_cos(num * FRAC_ONE / 50) * 35;
221    dy = int_cos(num * FRAC_ONE / 50 + FRAC_ONE / 10) * 30;
222    for(y=0;y<h;y++) {
223        for(x=0;x<w;x++) {
224            x1 = (x << FRAC_BITS) + dx;
225            y1 = (y << FRAC_BITS) + dx;
226            r = ((y1 * 7) >> FRAC_BITS) & 0xff;
227            g = (((x1 + y1) * 9) >> FRAC_BITS) & 0xff;
228            b = ((x1 * 5) >> FRAC_BITS) & 0xff;
229            put_pixel(x, y, r, g, b);
230        }
231    }
232
233    /* then some noise with very high intensity to test saturation */
234    seed1 = num;
235    for(y=0;y<NOISE_W;y++) {
236        for(x=0;x<NOISE_W;x++) {
237            r = myrnd(&seed1, 256);
238            g = myrnd(&seed1, 256);
239            b = myrnd(&seed1, 256);
240            put_pixel(x + NOISE_X, y + NOISE_Y, r, g, b);
241        }
242    }
243
244    /* then moving objects */
245    for(i=0;i<NB_OBJS;i++) {
246        VObj *p = &objs[i];
247        seed1 = i;
248        for(y=0;y<p->h;y++) {
249            for(x=0;x<p->w;x++) {
250                r = p->r;
251                g = p->g;
252                b = p->b;
253                /* add a per object noise */
254                r += myrnd(&seed1, 50);
255                g += myrnd(&seed1, 50);
256                b += myrnd(&seed1, 50);
257                put_pixel(x + p->x, y + p->y, r, g, b);
258            }
259        }
260        p->x += myrnd(&seed, 21) - 10;
261        p->y += myrnd(&seed, 21) - 10;
262    }
263}
264
265int main(int argc, char **argv)
266{
267    int w, h, i;
268    char buf[1024];
269
270    if (argc != 2) {
271        printf("usage: %s file\n"
272               "generate a test video stream\n", argv[0]);
273        exit(1);
274    }
275
276#if 0
277    for(i=0;i<256;i++)
278        printf("cos(%d)=%d\n", i, int_cos(i));
279#endif
280
281    w = DEFAULT_WIDTH;
282    h = DEFAULT_HEIGHT;
283
284    rgb_tab = malloc(w * h * 3);
285    wrap = w * 3;
286    width = w;
287    height = h;
288
289    for(i=0;i<DEFAULT_NB_PICT;i++) {
290        snprintf(buf, sizeof(buf), "%s%02d.pgm", argv[1], i);
291        gen_image(i, w, h);
292        pgmyuv_save(buf, w, h, rgb_tab);
293    }
294
295    free(rgb_tab);
296    return 0;
297}
298