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