1 /*
2 * y4mcolorbars.c: Generate real, standard colorbars in POG form
3 * (where "POG" == "YUV4MPEG2 stream").
4 *
5 *
6 * Copyright (C) 2004 Matthew J. Marjanovic <maddog@mir.com>
7 *
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program 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
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 */
24
25 #include <config.h>
26
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include <yuv4mpeg.h>
34 #include <mpegconsts.h>
35
36 #include "subsample.h"
37 #include "colorspace.h"
38
39 #define DEFAULT_CHROMA_MODE Y4M_CHROMA_444
40
41 #define IQ_MODE_IQ20 0 /* 20 percent -I,+Q */
42 #define IQ_MODE_IQ50 1 /* 50 percent -I,+Q */
43 #define IQ_MODE_CBCR100 2 /* 100 percent -Cb,+Cr (original behavior) */
44
45
46 typedef struct _cl_info {
47 y4m_ratio_t aspect;
48 int interlace;
49 y4m_ratio_t framerate;
50 int framecount;
51 int ss_mode;
52 int width;
53 int height;
54 int verbosity;
55 int iq_mode;
56 } cl_info_t;
57
58
59
60 static
usage(const char * progname)61 void usage(const char *progname)
62 {
63 fprintf(stderr, "usage: %s [options]\n", progname);
64 fprintf(stderr, "\n");
65 fprintf(stderr, "Creates a YUV4MPEG2 stream consisting of frames containing a standard\n");
66 fprintf(stderr, " colorbar test pattern (SMPTE, 75%%).\n");
67 fprintf(stderr, "\n");
68 fprintf(stderr, "Options: (defaults specified in [])\n");
69 fprintf(stderr, "\n");
70 fprintf(stderr, " -n n frame count (output n frames) [1]\n");
71 fprintf(stderr, "\n");
72 fprintf(stderr, " -W w frame width [720]\n");
73 fprintf(stderr, " -H h frame height [480]\n");
74 fprintf(stderr, " -F n:d framerate (as ratio) [30000:1001]\n");
75 fprintf(stderr, " -A w:h pixel aspect ratio [10:11]\n");
76 fprintf(stderr, " -I x interlacing [p]\n");
77 fprintf(stderr, " p = none/progressive\n");
78 fprintf(stderr, " t = top-field-first\n");
79 fprintf(stderr, " b = bottom-field-first\n");
80 fprintf(stderr, " -Q n content of -I/Q areas: [0]\n");
81 fprintf(stderr, " 0 = 20%% -I,+Q\n");
82 fprintf(stderr, " 1 = 50%% -I,+Q\n");
83 fprintf(stderr, " 2 = 100%% +Cb,+Cr\n");
84 fprintf(stderr, "\n");
85 {
86 int m;
87 const char *keyword;
88
89 fprintf(stderr, " -S mode chroma subsampling mode [%s]\n",
90 y4m_chroma_keyword(DEFAULT_CHROMA_MODE));
91 for (m = 0;
92 (keyword = y4m_chroma_keyword(m)) != NULL;
93 m++)
94 if (chroma_sub_implemented(m))
95 fprintf(stderr, " '%s' -> %s\n",
96 keyword, y4m_chroma_description(m));
97 }
98 fprintf(stderr, "\n");
99 fprintf(stderr, " -v n verbosity (0,1,2) [1]\n");
100 }
101
102
103
104 static
parse_args(cl_info_t * cl,int argc,char ** argv)105 void parse_args(cl_info_t *cl, int argc, char **argv)
106 {
107 int c;
108
109 cl->interlace = Y4M_ILACE_NONE;
110 cl->framerate = y4m_fps_NTSC;
111 cl->framecount = 1;
112 cl->ss_mode = DEFAULT_CHROMA_MODE;
113 cl->width = 720;
114 cl->height = 480;
115 cl->aspect = y4m_sar_NTSC_CCIR601;
116 cl->verbosity = 1;
117 cl->iq_mode = IQ_MODE_IQ20;
118
119 while ((c = getopt(argc, argv, "A:F:I:W:H:n:S:Q:v:h")) != -1) {
120 switch (c) {
121 case 'W':
122 if ((cl->width = atoi(optarg)) <= 0) {
123 mjpeg_error("Invalid width: '%s'", optarg);
124 goto ERROR_EXIT;
125 }
126 break;
127 case 'H':
128 if ((cl->height = atoi(optarg)) <= 0) {
129 mjpeg_error("Invalid height: '%s'", optarg);
130 goto ERROR_EXIT;
131 }
132 break;
133 case 'A':
134 if (y4m_parse_ratio(&(cl->aspect), optarg) != Y4M_OK) {
135 mjpeg_error("Could not parse ratio: '%s'", optarg);
136 goto ERROR_EXIT;
137 }
138 break;
139 case 'F':
140 if (y4m_parse_ratio(&(cl->framerate), optarg) != Y4M_OK) {
141 mjpeg_error("Could not parse framerate: '%s'", optarg);
142 goto ERROR_EXIT;
143 }
144 break;
145 case 'I':
146 switch (optarg[0]) {
147 case 'p': cl->interlace = Y4M_ILACE_NONE; break;
148 case 't': cl->interlace = Y4M_ILACE_TOP_FIRST; break;
149 case 'b': cl->interlace = Y4M_ILACE_BOTTOM_FIRST; break;
150 default:
151 mjpeg_error("Unknown value for interlace: '%c'", optarg[0]);
152 goto ERROR_EXIT;
153 break;
154 }
155 break;
156 case 'n':
157 if ((cl->framecount = atoi(optarg)) <= 0) {
158 mjpeg_error("Invalid frame count: '%s'", optarg);
159 goto ERROR_EXIT;
160 }
161 break;
162 case 'S':
163 cl->ss_mode = y4m_chroma_parse_keyword(optarg);
164 if (cl->ss_mode == Y4M_UNKNOWN) {
165 mjpeg_error("Unknown subsampling mode option: %s", optarg);
166 goto ERROR_EXIT;
167 } else if (!chroma_sub_implemented(cl->ss_mode)) {
168 mjpeg_error("Unsupported subsampling mode option: %s", optarg);
169 goto ERROR_EXIT;
170 }
171 break;
172 case 'Q':
173 switch (atoi(optarg)) {
174 case 0: cl->iq_mode = IQ_MODE_IQ20; break;
175 case 1: cl->iq_mode = IQ_MODE_IQ50; break;
176 case 2: cl->iq_mode = IQ_MODE_CBCR100; break;
177 default:
178 mjpeg_error("Unknown -I/+Q mode: %d", atoi(optarg));
179 goto ERROR_EXIT;
180 }
181 break;
182 case 'v':
183 cl->verbosity = atoi(optarg);
184 break;
185 case 'h':
186 usage(argv[0]);
187 exit(0);
188 break;
189 case '?':
190 default:
191 mjpeg_error("Unknown option: '-%c'", optopt);
192 goto ERROR_EXIT;
193 break;
194 }
195 }
196
197 mjpeg_default_handler_verbosity(cl->verbosity);
198
199 mjpeg_info("Colorbar Command-line Parameters:");
200 mjpeg_info(" frame size: %dx%d", cl->width, cl->height);
201 mjpeg_info(" framerate: %d:%d",
202 cl->framerate.n, cl->framerate.d);
203 mjpeg_info(" pixel aspect ratio: %d:%d",
204 cl->aspect.n, cl->aspect.d);
205 mjpeg_info(" interlace: %s",
206 mpeg_interlace_code_definition(cl->interlace));
207 mjpeg_info(" # of frames: %d", cl->framecount);
208 mjpeg_info(" chroma subsampling: %s",
209 y4m_chroma_description(cl->ss_mode));
210 return;
211
212 ERROR_EXIT:
213 mjpeg_error("For usage hints, use option '-h': '%s -h'", argv[0]);
214 exit(1);
215
216 }
217
218
219
220
221 /*
222 * Color Bars:
223 *
224 * top 2/3: 75% white, followed by 75% binary combinations
225 * of R', G', and B' with decreasing luma
226 *
227 * middle 1/12: reverse order of above, but with 75% white and
228 * alternating black
229 *
230 * bottom 1/4: -I, 100% white, +Q, black, PLUGE, black,
231 * where PLUGE is (black - 4 IRE), black, (black + 4 IRE)
232 *
233 */
234
235 /* 75% white */
236 /* 75% yellow */
237 /* 75% cyan */
238 /* 75% green */
239 /* 75% magenta */
240 /* 75% red */
241 /* 75% blue */
242 static uint8_t rainbowRGB[][7] = {
243 { 191, 191, 0, 0, 191, 191, 0 },
244 { 191, 191, 191, 191, 0, 0, 0 },
245 { 191, 0, 191, 0, 191, 0, 191 }
246 };
247 static uint8_t *rainbow[3] = {
248 rainbowRGB[0], rainbowRGB[1], rainbowRGB[2]
249 };
250
251
252 /* 75% blue */
253 /* black */
254 /* 75% magenta */
255 /* black */
256 /* 75% cyan */
257 /* black */
258 /* 75% white */
259 static uint8_t wobnairRGB[][7] = {
260 { 0, 0, 191, 0, 0, 0, 191 },
261 { 0, 0, 0, 0, 191, 0, 191 },
262 { 191, 0, 191, 0, 191, 0, 191 }
263 };
264 static uint8_t *wobnair[3] = {
265 wobnairRGB[0], wobnairRGB[1], wobnairRGB[2]
266 };
267
268
269 /* The ancient Y'IQ
270 *
271 * Classic colorbars have -I and +Q in the PLUGE section.
272 *
273 * Following Poynton, "Digital Video and HDTV", p367:
274 *
275 * -I = (-0.955987, +0.272013, +1.106740) R'G'B'
276 * +Q = (+0.620825, -0.647204, +1.704231) R'G'B'
277 *
278 * Converting to Y'PbPr and Y'CbCr:
279 *
280 * -I = (0, 0.624571 -0.681873) -> (16, 268, -25)
281 * +Q = (0, 0.961755 0.442815) -> (16, 343, 227)
282 *
283 * Uh, oh: these are outside of the Y'CbCr gamut.
284 * We can get around this, kind of, by reducing the magnitude until they
285 * fit, keeping the phase the same. Q is the worst offender, and a factor
286 * of 0.5 does the trick:
287 *
288 * (0.5)*(-I) = (0 0.312286 -0.340937) -> (16, 198, 52)
289 * (0.5)*(+Q) = (0 0.480878 0.221407) -> (16, 236, 178)
290 *
291 * (0.2)*(-I) = (0 0.124914 -0.136375) -> (16 156 97)
292 * (0.2)*(+Q) = (0 0.192351 0.088563) -> (16 171 148)
293
294 10, 9E, 5F 16, 158, 95
295 10, AE, 95 16, 174, 149
296
297 i 244 612 395 61 153 99
298 q 141 697 606 35 174 151
299
300 -4 29 512 512 7.25 128 128
301 +4 99 512 512 24.75 128 128
302
303
304 AD-723: burst ampl: 185-250-315 mV
305 Bt860: 285.7 mV --> 40 IRE (per RS-170A -- 40 IRE top-bottom?)
306
307 Maxim app notes: NTSC: 286mV peak-to-peak
308 PAL: 300mV peak-to-peak
309
310 *
311 */
312 static uint8_t negI_50percent[] = { 16, 198, 52 };
313 static uint8_t posQ_50percent[] = { 16, 236, 178 };
314
315 static uint8_t negI_20percent[] = { 16, 156, 97 };
316 static uint8_t posQ_20percent[] = { 16, 171, 148 };
317
318 /* static uint8_t negCb_100percent[] = { 16, 16, 128 }; */
319 static uint8_t posCb_100percent[] = { 16, 240, 128 };
320
321 /* static uint8_t negCr_100percent[] = { 16, 128, 16 }; */
322 static uint8_t posCr_100percent[] = { 16, 128, 240 };
323
324
325 /* PLUGE (SMPTE EG-1-1990)
326 *
327 * This part of the signal can never be truly synthesized in Y'CbCr space,
328 * because it is fundamentally tied to the analog signal specification.
329 *
330 * The two main components are a (Black - 4 IRE) and (Black + 4 IRE).
331 * Simple enough, except that the relation between "IRE" and Y' units depends
332 * on the specific analog output format, which sets the black-white range:
333 *
334 * Y': 16-235 -> excursion of 219
335 * NTSC with 0% setup: 0-100 IRE -> excursion of 100 IRE
336 * NTSC with 7.5% setup: 7.5-100 IRE -> excursion of 92.5 IRE
337 *
338 * It's the analog output stage (e.g. graphics card, decoder chip) that
339 * converts the Y'CbCr digital values to an analog voltage appropriate
340 * for whatever analog equipment comes next.
341 * To perfectly synthesize a 4-IRE signal in a 0% setup system, we'd
342 * want 8.76 "Y' units". For a 7.5% setup system, we'd want 9.47.
343 * Lucky for us, we're not allowed to use fractions anyway, so we
344 * just split the difference and settle for "9".
345 *
346 */
347 static uint8_t neg4IRE[] = { (16 - 9), 128, 128 };
348 static uint8_t pos4IRE[] = { (16 + 9), 128, 128 };
349
350 static uint8_t black[] = { 16, 128, 128 };
351 static uint8_t white[] = { 235, 128, 128 };
352
353
354
355
356 static
create_bars(uint8_t * ycbcr[],int width,int height,int iq_mode)357 void create_bars(uint8_t *ycbcr[], int width, int height, int iq_mode)
358 {
359 int i, x, y, w;
360 int bnb_start;
361 int pluge_start;
362 int stripe_width;
363 int pl_width;
364 uint8_t *lineY;
365 uint8_t *lineCb;
366 uint8_t *lineCr;
367
368 uint8_t *Y = ycbcr[0];
369 uint8_t *Cb = ycbcr[1];
370 uint8_t *Cr = ycbcr[2];
371
372 uint8_t *i_pixel;
373 uint8_t *q_pixel;
374
375 convert_RGB_to_YCbCr(rainbow, 7);
376 convert_RGB_to_YCbCr(wobnair, 7);
377
378 switch (iq_mode) {
379 case IQ_MODE_CBCR100:
380 i_pixel = posCb_100percent;
381 q_pixel = posCr_100percent;
382 break;
383 case IQ_MODE_IQ50:
384 i_pixel = negI_50percent;
385 q_pixel = posQ_50percent;
386 break;
387 case IQ_MODE_IQ20:
388 default:
389 i_pixel = negI_20percent;
390 q_pixel = posQ_20percent;
391 break;
392 }
393
394 bnb_start = height * 2 / 3;
395 pluge_start = height * 3 / 4;
396 stripe_width = (width + 6) / 7;
397 lineY = malloc(width * sizeof(lineY[0]));
398 lineCb = malloc(width * sizeof(lineCb[0]));
399 lineCr = malloc(width * sizeof(lineCr[0]));
400
401 /* Top: Rainbow */
402 for (i = 0, x = 0; i < 7; i++) {
403 for (w = 0; (w < stripe_width) && (x < width); w++, x++) {
404 lineY[x] = rainbow[0][i];
405 lineCb[x] = rainbow[1][i];
406 lineCr[x] = rainbow[2][i];
407 }
408 }
409 for (y = 0; y < bnb_start; y++) {
410 memcpy(Y, lineY, width);
411 memcpy(Cb, lineCb, width);
412 memcpy(Cr, lineCr, width);
413 Y += width;
414 Cb += width;
415 Cr += width;
416 }
417
418 /* Middle: Wobnair */
419 for (i = 0, x = 0; i < 7; i++) {
420 for (w = 0; (w < stripe_width) && (x < width); w++, x++) {
421 lineY[x] = wobnair[0][i];
422 lineCb[x] = wobnair[1][i];
423 lineCr[x] = wobnair[2][i];
424 }
425 }
426 for (; y < pluge_start; y++) {
427 memcpy(Y, lineY, width);
428 memcpy(Cb, lineCb, width);
429 memcpy(Cr, lineCr, width);
430 Y += width;
431 Cb += width;
432 Cr += width;
433 }
434
435 /* Bottom: PLUGE */
436 pl_width = 5 * stripe_width / 4;
437 /* -I patch */
438 for (x = 0; x < pl_width; x++) {
439 lineY[x] = i_pixel[0];
440 lineCb[x] = i_pixel[1];
441 lineCr[x] = i_pixel[2];
442 }
443 /* white */
444 for (; x < (2 * pl_width); x++) {
445 lineY[x] = white[0];
446 lineCb[x] = white[1];
447 lineCr[x] = white[2];
448 }
449 /* +Q patch */
450 for (; x < (3 * pl_width); x++) {
451 lineY[x] = q_pixel[0];
452 lineCb[x] = q_pixel[1];
453 lineCr[x] = q_pixel[2];
454 }
455 /* black */
456 for (; x < (5 * stripe_width); x++) {
457 lineY[x] = black[0];
458 lineCb[x] = black[1];
459 lineCr[x] = black[2];
460 }
461 /* (black - 4IRE) | black | (black + 4IRE) */
462 for (; x < (5 * stripe_width) + (stripe_width / 3); x++) {
463 lineY[x] = neg4IRE[0];
464 lineCb[x] = neg4IRE[1];
465 lineCr[x] = neg4IRE[2];
466 }
467 for (; x < (5 * stripe_width) + (2 * (stripe_width / 3)); x++) {
468 lineY[x] = black[0];
469 lineCb[x] = black[1];
470 lineCr[x] = black[2];
471 }
472 for (; x < (6 * stripe_width); x++) {
473 lineY[x] = pos4IRE[0];
474 lineCb[x] = pos4IRE[1];
475 lineCr[x] = pos4IRE[2];
476 }
477 /* black */
478 for (; x < width; x++) {
479 lineY[x] = black[0];
480 lineCb[x] = black[1];
481 lineCr[x] = black[2];
482 }
483 for (; y < height; y++) {
484 memcpy(Y, lineY, width);
485 memcpy(Cb, lineCb, width);
486 memcpy(Cr, lineCr, width);
487 Y += width;
488 Cb += width;
489 Cr += width;
490 }
491 free(lineY);
492 free(lineCb);
493 free(lineCr);
494 }
495
496
497
498
main(int argc,char ** argv)499 int main(int argc, char **argv)
500 {
501 cl_info_t cl;
502 y4m_stream_info_t sinfo;
503 y4m_frame_info_t finfo;
504 uint8_t *planes[Y4M_MAX_NUM_PLANES]; /* Y'CbCr frame buffer */
505 int fdout = fileno(stdout);
506 int i;
507 int err;
508
509 y4m_accept_extensions(1);
510 y4m_init_stream_info(&sinfo);
511 y4m_init_frame_info(&finfo);
512
513 parse_args(&cl, argc, argv);
514
515 /* Setup streaminfo and write output header */
516 y4m_si_set_width(&sinfo, cl.width);
517 y4m_si_set_height(&sinfo, cl.height);
518 y4m_si_set_sampleaspect(&sinfo, cl.aspect);
519 y4m_si_set_interlace(&sinfo, cl.interlace);
520 y4m_si_set_framerate(&sinfo, cl.framerate);
521 y4m_si_set_chroma(&sinfo, cl.ss_mode);
522 if ((err = y4m_write_stream_header(fdout, &sinfo)) != Y4M_OK)
523 mjpeg_error_exit1("Write header failed: %s", y4m_strerr(err));
524 mjpeg_info("Colorbar Stream parameters:");
525 y4m_log_stream_info(mjpeg_loglev_t("info"), " ", &sinfo);
526
527 /* Create the colorbars frame */
528 for (i = 0; i < 3; i++)
529 planes[i] = malloc(cl.width * cl.height * sizeof(planes[i][0]));
530 create_bars(planes, cl.width, cl.height, cl.iq_mode);
531 chroma_subsample(cl.ss_mode, planes, cl.width, cl.height);
532
533 /* We're on the air! */
534 for (i = 0; i < cl.framecount; i++) {
535 if ((err = y4m_write_frame(fdout, &sinfo, &finfo, planes)) != Y4M_OK)
536 mjpeg_error_exit1("Write frame failed: %s", y4m_strerr(err));
537 }
538
539 /* We're off the air. :( */
540 for (i = 0; i < 3; i++)
541 free(planes[i]);
542 y4m_fini_stream_info(&sinfo);
543 y4m_fini_frame_info(&finfo);
544 return 0;
545 }
546