1 /*
2   *  yuvcorrect.c
3   *  Copyright (C) 2002 Xavier Biquard <xbiquard@free.fr>
4   *
5   *  This program is free software; you can redistribute it and/or modify
6   *  it under the terms of the GNU General Public License as published by
7   *  the Free Software Foundation; either version 2 of the License, or
8   *  (at your option) any later version.
9   *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18   */
19 // September/October 2002: First version
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <math.h>
30 #include <signal.h>
31 #include "yuv4mpeg.h"
32 #include "yuvcorrect.h"
33 
34 extern const uint16_t OFFSET;
35 extern const uint16_t ALIGNENEMENT;
36 extern const char *legal_opt_flags;
37 
38 
39 const char PIPE[] = "PIPE";
40 const char STAT[] = "STAT";
41 const char FULL[] = "FULL";
42 const char HALF[] = "HALF";
43 const char TOP_FIRST[] = "INTERLACED_TOP_FIRST";
44 const char BOT_FIRST[] = "INTERLACED_BOTTOM_FIRST";
45 const char NOT_INTER[] = "NOT_INTERLACED";
46 const char PROGRESSIVE[] = "PROGRESSIVE";
47 const char LINESWITCH[] = "LINE_SWITCH";
48 const char NO_HEADER[] = "NO_HEADER";
49 const char TOP_FORWARD[] = "TOP_FORWARD";
50 const char BOTT_FORWARD[] = "BOTT_FORWARD";
51 const char RGBFIRST[] = "RGBFIRST";
52 
53 // Prototypes specific to yuvcorrect
54 void yuvcorrect_print_usage (void);
55 void yuvcorrect_print_information (general_correction_t * gen_correct,
56 				   yuv_correction_t * yuv_correct,
57 				   rgb_correction_t * rgb_correct);
58 void yuvcorrect_handle_args (int argc, char *argv[], overall_t * overall,
59 			     general_correction_t * gen_correct);
60 
61 void
yuvcorrect_print_usage(void)62 yuvcorrect_print_usage (void)
63 {
64   fprintf (stderr,
65 	   "usage: yuvcorrect -M [mode_keyword] -T [general_keyword] -Y [yuv_keyword] -R [RGB_keyword]  [-v 0-2] [-h]\n"
66 	   "yuvcorrect applies different corrections related to interlacing and color\n"
67 	   "to yuv frames coming from stdin (in yuv4MPEG 4:2:0 format) to stdout.\n"
68 	   "In contrast to yuvscaler, frame size is kept constant.\n"
69 	   "\n"
70 	   "yuvcorrect is keyword driven :\n"
71 	   "\t -M for keyword concerning the correction MODE of yuvcorrect\n"
72 	   "\t -T for keyword concerning spatial, temporal or header corrections to be applied\n"
73 	   "\t -Y for keyword concerning color corrections in the yuv space\n"
74 	   "\t -R for keyword concerning color corrections in the RGB space\n"
75 	   "By default, yuvcorrect will not modify frames and simply act as a pass-through. Also, it will apply\n"
76 	   "YUV corrections first and then RGB corrections\n"
77 	   "\n" "Possible mode keyword are:\n"
78 	   "\t STAT to have yuvcorrect print statistical information on your frames _before_ corrections\n"
79 	   "\t RGBFIRST to have yuvcorrect apply RGB corrections first, then YUV corrections\n"
80 	   "\n"
81 	   "Possible general keyword are:\n"
82 	   "\t If you suspect that your video capture was given a wrong interlacing type,\n"
83 	   "\t and/or was spatially or temporarly missed up, please use and combine:\n"
84 	   "\t INTERLACED_TOP_FIRST    to correct file header by specifying top_field_first as interlacing type\n"
85 	   "\t INTERLACED_BOTTOM_FIRST to correct file header by specifying bottom_field_first as interlacing\n"
86 	   "\t NOT_INTERLACED          to correct file header by specifying not-interlaced/progressive as interlacing type\n"
87 	   "\t PROGRESSIVE             to correct file header by specifying not-interlaced/progressive as interlacing type\n"
88 	   "\t NO_HEADER    to suppress stream header generation (apply different corrections to different part of an input file)\n"
89 	   "\t LINE_SWITCH  to switch lines two by two\n"
90 	   "\t BOTT_FORWARD to move the bottom field one frame forward\n"
91 	   "\t TOP_FORWARD  to move the top    field one frame forward\n"
92 	   "\n"
93 	   "Possible yuv keywords are:\n"
94 	   "\t LUMINANCE_Gamma_InputYmin_InputYmax_OutputYmin_OutputYmax or\n"
95 	   "\t Y_Gamma_InputYmin_InputYmax_OutputYmin_OutputYmax\n"
96 	   "\t    to correct the input frame luminance by clipping it inside range [InputYmin;InputYmax],\n"
97 	   "\t    scale with power (1/Gamma), and expand/shrink/shift it to [OutputYmin;OutputYmax]\n"
98 	   "\t CHROMINANCE_UVrotation_Ufactor_Urotcenter_Vfactor_Vrotcenter_UVmin_UVmax or\n"
99 	   "\t UV_UVrotation_Ufactor_Urotcenter_Vfactor_Vrotcenter_UVmin_UVmax\n"
100 	   "\t    to rotate rescaled UV chroma components Ufactor*(U-Urotcenter) and Vfactor*(V-Vrotcenter)\n"
101 	   "\t    by (float) UVrotation degrees, recenter to the normalize (128,128) center,\n"
102 	   "\t    and _clip_ the result to range [UVmin;UVmax]\n"
103 	   "\t CONFORM to have yuvcorrect generate frames conform to the Rec.601 specification for Y'CbCr frames\n"
104 	   "\t    that is LUMINANCE_1.0_16_235_16_235 and CHROMINANCE_0.0_1.0_128_1.0_128_16_240\n"
105 	   "\n"
106 	   "\t Possible RGB keywords are:\n"
107 	   "\t R_Gamma_InputRmin_InputRmax_OutputRmin_OutputRmax\n"
108 	   "\t G_Gamma_InputGmin_InputGmax_OutputGmin_OutputGmax\n"
109 	   "\t B_Gamma_InputBmin_InputBmax_OutputBmin_OutputBmax\n"
110 	   "\t    to correct the input frame RGB color by clipping it inside range [InputRGBmin;InputRGBmax],\n"
111 	   "\t    scale with power (1/Gamma), and expand/shrink/shift it to [OutputRGBmin;OutputRGBmax]\n"
112 	   "\n"
113 	   "-v  Specifies the degree of verbosity: 0=quiet, 1=normal, 2=verbose/debug\n"
114 	   "-h : print this lot!\n");
115   exit (1);
116 }
117 
118 void
handle_args_overall(int argc,char * argv[],overall_t * overall)119 handle_args_overall (int argc, char *argv[], overall_t * overall)
120 {
121   int c, verb;
122 
123   while ((c = getopt (argc, argv, legal_opt_flags)) != -1)
124     {
125       switch (c)
126 	{
127 	case 'v':
128 	  verb = atoi (optarg);
129 	  if (verb < 0 || verb > 2)
130 	    {
131 	      mjpeg_info
132 		("Verbose level must be 0, 1 or 2 ! => resuming to default 1");
133 	    }
134 	  else
135 	    {
136 	      overall->verbose = verb;
137 	    }
138 	  break;
139 
140 	case 'h':
141 	  yuvcorrect_print_usage ();
142 	  break;
143 
144 	default:
145 	  break;
146 	}
147     }
148   if (optind != argc)
149     yuvcorrect_print_usage ();
150 }
151 
152 void
yuvcorrect_handle_args(int argc,char * argv[],overall_t * overall,general_correction_t * gen_correct)153 yuvcorrect_handle_args (int argc, char *argv[], overall_t * overall,
154 	     general_correction_t * gen_correct)
155 {
156   // This function handles argument passing on the command line
157   int c;
158   int k_mode, k_general;
159 
160   // Ne pas oublier de mettre la putain de ligne qui suit, sinon, plus d'argument � la lign de commande, ils auront �t� bouff�s par l'appel pr�c�dnt � getopt!!
161   optind = 1;
162   while ((c = getopt (argc, argv, legal_opt_flags)) != -1)
163     {
164       switch (c)
165 	{
166 	  // **************
167 	  // MODE KEYOWRD
168 	  // *************
169 	case 'M':
170 	  k_mode = 0;
171 	   if (strcmp (optarg, STAT) == 0)
172 	    {
173 	      k_mode = 1;
174 	      overall->stat = 1;
175 	    }
176 	  if (strcmp (optarg, RGBFIRST) == 0)
177 	    {
178 	      k_mode = 1;
179 	      overall->rgbfirst = 1;
180 	    }
181 	   if (k_mode == 0)
182 	    mjpeg_error_exit1 ("Unrecognized MODE keyword: %s", optarg);
183 	  break;
184 	  // *************
185 
186 
187 	  // **************
188 	  // GENERAL KEYOWRD
189 	  // *************
190 	case 'T':
191 	  k_general = 0;
192 	  if (strcmp (optarg, TOP_FIRST) == 0)
193 	    {
194 	      k_general = 1;
195 	      y4m_si_set_interlace (&gen_correct->streaminfo,
196 				    Y4M_ILACE_TOP_FIRST);
197 	    }
198 	  if (strcmp (optarg, BOT_FIRST) == 0)
199 	    {
200 	      k_general = 1;
201 	      y4m_si_set_interlace (&gen_correct->streaminfo,
202 				    Y4M_ILACE_BOTTOM_FIRST);
203 	    }
204 	  if ((strcmp (optarg, NOT_INTER) == 0)
205 	      || (strcmp (optarg, PROGRESSIVE) == 0))
206 	    {
207 	      k_general = 1;
208 	      y4m_si_set_interlace (&gen_correct->streaminfo, Y4M_ILACE_NONE);
209 	    }
210 	  if (strcmp (optarg, NO_HEADER) == 0)
211 	    {
212 	      k_general = 1;
213 	      gen_correct->no_header = 1;
214 	    }
215 	  if (strcmp (optarg, LINESWITCH) == 0)
216 	    {
217 	      k_general = 1;
218 	      gen_correct->line_switch = 1;
219 	    }
220 	  if (strcmp (optarg, BOTT_FORWARD) == 0)
221 	    {
222 	      k_general = 1;
223 	      gen_correct->field_move = 1;
224 	    }
225 	  if (strcmp (optarg, TOP_FORWARD) == 0)
226 	    {
227 	      k_general = 1;
228 	      gen_correct->field_move = -1;
229 	    }
230 	  if (k_general == 0)
231 	    mjpeg_error_exit1 ("Unrecognized GENERAL keyword: %s", optarg);
232 	  break;
233 	  // *************
234 
235 	default:
236 	  break;
237 	}
238     }
239 
240 
241 }
242 
243 void
yuvcorrect_print_information(general_correction_t * gen_correct,yuv_correction_t * yuv_correct,rgb_correction_t * rgb_correct)244 yuvcorrect_print_information (general_correction_t * gen_correct,
245 			      yuv_correction_t * yuv_correct,
246 			      rgb_correction_t * rgb_correct)
247 {
248   // This function print USER'S INFORMATION
249 
250   y4m_log_stream_info (mjpeg_loglev_t("info"), "input: ", &gen_correct->streaminfo);
251 
252   switch (gen_correct->line_switch)
253     {
254     case 0:
255       mjpeg_info ("no line switching");
256       break;
257     case 1:
258       mjpeg_info ("with line switching");
259       break;
260     default:
261       mjpeg_error_exit1 ("Unknown line switching status: %d",
262 			 gen_correct->line_switch);
263     }
264 
265   switch (gen_correct->field_move)
266     {
267     case 0:
268       mjpeg_info ("no time forwarding");
269       break;
270     case 1:
271       mjpeg_info ("with bottom field one frame forward");
272       break;
273     case -1:
274       mjpeg_info ("with top field one frame forward");
275       break;
276     default:
277       mjpeg_error_exit1 ("Unknown time reordering status: %d",
278 			 gen_correct->field_move);
279     }
280 
281   switch (yuv_correct->luma)
282     {
283     case 0:
284       mjpeg_info ("Without luminance correction");
285       break;
286     case 1:
287       mjpeg_info ("With luminance correction");
288       break;
289     default:
290       mjpeg_error_exit1 ("Unknown luminance correction status %u",
291 			 yuv_correct->luma);
292     }
293 
294   switch (yuv_correct->chroma)
295     {
296     case 0:
297       mjpeg_info ("Without chrominance correction");
298       break;
299     case 1:
300       mjpeg_info ("With chrominance correction");
301       break;
302     default:
303       mjpeg_error_exit1 ("Unknown chrominance correction status %u",
304 			 yuv_correct->chroma);
305     }
306 
307   switch (rgb_correct->rgb)
308     {
309     case 0:
310       mjpeg_info ("Without rgb correction");
311       break;
312     case 1:
313       mjpeg_info ("With rgb correction");
314       break;
315     default:
316       mjpeg_error_exit1 ("Unknown rgb correction status %u",
317 			 rgb_correct->rgb);
318     }
319 
320 }
321 
322 int
main(int argc,char * argv[])323 main (int argc, char *argv[])
324 {
325 
326   // Defining yuvcorrect dedicated structures (see yuvcorrect.h)
327   overall_t *overall=NULL;
328   frame_t *frame=NULL;
329   general_correction_t *gen_correct=NULL;
330   yuv_correction_t *yuv_correct=NULL;
331   rgb_correction_t *rgb_correct=NULL;
332 
333   int err = Y4M_OK;
334   uint8_t oddeven;
335 
336   unsigned long int frame_num = 0;
337 
338   y4m_accept_extensions(1);
339 
340   // START OF INITIALISATION
341   // yuvcorrect overall structure initialisation
342   if (!(overall = (overall_t *) malloc (sizeof (overall_t))))
343     mjpeg_error_exit1
344       ("Could not allocate memory for overall structure pointer");
345   overall->verbose = 1;
346   overall->mode = overall->stat = overall->rgbfirst = 0;
347   handle_args_overall (argc, argv, overall);
348   mjpeg_default_handler_verbosity (overall->verbose);
349 
350   mjpeg_debug ("Start of initialisation");
351 
352    // yuvcorrect general_correction_t structure initialisations
353    if (!(gen_correct = (general_correction_t *) malloc (sizeof (general_correction_t))))
354      mjpeg_error_exit1
355      ("Could not allocate memory for gen_correct structure pointer");
356    // yuvcorrect frame_t structure initialisations
357    if (!(frame = (frame_t *) malloc (sizeof (frame_t))))
358      mjpeg_error_exit1
359      ("Could not allocate memory for frame structure pointer");
360    // yuvcorrect yuv_correction_t structure initialisation
361    if (!(yuv_correct = (yuv_correction_t *) malloc (sizeof (yuv_correction_t))))
362      mjpeg_error_exit1
363      ("Could not allocate memory for yuv_correct structure pointer");
364    // rgbcorrect rgb_correction_t structure initialisation
365    if (!(rgb_correct = (rgb_correction_t *) malloc (sizeof (rgb_correction_t))))
366      mjpeg_error_exit1
367      ("Could not allocate memory for rgb_correct structure pointer");
368 
369    initialisation1(0,frame,gen_correct,yuv_correct,rgb_correct);
370    // Deal with args
371    handle_args_yuv_rgb (argc, argv, yuv_correct, rgb_correct);
372    yuvcorrect_handle_args (argc, argv, overall, gen_correct);
373   // Further initialisations depending on the stream itself
374   // General correction initialisations
375   if (gen_correct->field_move != 0)
376     {
377       if (!(frame->field1 = (uint8_t *) malloc ((frame->length >> 1)*sizeof(uint8_t))) ||
378 	  !(frame->field2 = (uint8_t *) malloc ((frame->length >> 1)*sizeof(uint8_t))))
379 	 mjpeg_error_exit1
380 	  ("Could not allocate memory for field1 or field2 tables. STOP!");
381     }
382    initialisation2(yuv_correct,rgb_correct);
383 
384   // USER'S INFORMATION OUTPUT
385   yuvcorrect_print_information (gen_correct, yuv_correct, rgb_correct);
386 
387   mjpeg_debug ("End of Initialisation");
388   // END OF INITIALISATION
389 
390   // Eventually output file header
391   if (gen_correct->no_header == 0)
392     y4m_write_stream_header (1, &gen_correct->streaminfo);
393 
394   mjpeg_debug ("overall: verbose=%u, mode=%d, stat=%u", overall->verbose,
395 	       overall->mode, overall->stat);
396   mjpeg_debug ("frame: Y:%ux%u=>%lu UV:%ux%u=>%lu Size=%lu", frame->y_width,
397 	       frame->y_height, frame->nb_y, frame->uv_width,
398 	       frame->uv_height, frame->nb_uv, frame->length);
399   mjpeg_debug
400     ("yuv: Gamma=%f, InputYmin=%u, InputYmax=%u, OutputYmin=%u, OutputYmax=%u",
401      yuv_correct->Gamma, yuv_correct->InputYmin, yuv_correct->InputYmax,
402      yuv_correct->OutputYmin, yuv_correct->OutputYmax);
403   // Master loop : continue until there is no next frame in stdin
404   while ((err = yuvcorrect_y4m_read_frame (0, &gen_correct->streaminfo, frame, gen_correct->line_switch)) == Y4M_OK)
405     {
406       if (overall->stat == 1)
407 	yuvstat (frame);
408 
409       mjpeg_info ("Frame number %ld", frame_num);
410 
411       // Time reordering
412       if (gen_correct->field_move != 0)
413 	{
414 	  oddeven = frame_num & (unsigned long int) 1;	// oddeven = frame_num % 2, fast implementation
415 	  if (gen_correct->field_move == 1)
416 	    {
417 	      // Bottom field one frame forward
418 	      if (frame_num == 0)
419 		{
420 		  bottom_field_storage (frame, oddeven, frame->field1, frame->field2);
421 		  if (yuvcorrect_y4m_read_frame (0, &gen_correct->streaminfo, frame, gen_correct->line_switch) !=
422 		      Y4M_OK)
423 		    mjpeg_error_exit1 ("Can't read frame %ld", frame_num);
424 		  frame_num++;
425 		  oddeven = frame_num & (unsigned long int) 1;
426 		  mjpeg_info ("Frame number %ld", frame_num);
427 		}
428 	      bottom_field_storage (frame, oddeven, frame->field1, frame->field2);
429 	      bottom_field_replace (frame, oddeven, frame->field1, frame->field2);
430 	    }
431 	  else
432 	    {
433 	      // Top field one frame forward
434 	      if (frame_num == 0)
435 		{
436 		  top_field_storage (frame, oddeven, frame->field1, frame->field2);
437 		  if (yuvcorrect_y4m_read_frame (0, &gen_correct->streaminfo, frame, gen_correct->line_switch) !=
438 		      Y4M_OK)
439 		    mjpeg_error_exit1 ("Can't read frame %ld", frame_num);
440 		  frame_num++;
441 		  oddeven = frame_num & (unsigned long int) 1;
442 		  mjpeg_info ("Frame number %ld", frame_num);
443 		}
444 	      top_field_storage (frame, oddeven, frame->field1, frame->field2);
445 	      top_field_replace (frame, oddeven, frame->field1, frame->field2);
446 	    }
447 	}
448 
449 
450       if (overall->rgbfirst == 1)
451 	{
452 	  // RGB correction
453 	  if (rgb_correct->rgb == 1)
454 	    yuvcorrect_RGB_treatment (frame, rgb_correct);
455 	}
456       // luminance correction
457       if (yuv_correct->luma == 1)
458 	yuvcorrect_luminance_treatment (frame, yuv_correct);
459       // chrominance correction
460       if (yuv_correct->chroma == 1)
461 	yuvcorrect_chrominance_treatment (frame, yuv_correct);
462       if (overall->rgbfirst != 1)
463 	{
464 	  // RGB correction
465 	  if (rgb_correct->rgb == 1)
466 	    yuvcorrect_RGB_treatment (frame, rgb_correct);
467 	}
468 
469       // Output Frame Header
470       if (y4m_write_frame_header (1, &gen_correct->streaminfo, &frame->info) != Y4M_OK)
471 	goto out_error;
472 
473       // Output Frame content
474       if (y4m_write (1, frame->y, frame->length) != Y4M_OK)
475 	goto out_error;
476 
477       frame_num++;
478 
479     }
480   // End of master loop => no more frame in stdin
481 
482   if (err != Y4M_ERR_EOF)
483     mjpeg_error_exit1 ("Couldn't read frame number %ld!", frame_num);
484   else
485     mjpeg_info ("Normal exit: end of stream with frame number %ld!",
486 		frame_num);
487   y4m_fini_stream_info (&gen_correct->streaminfo);
488   y4m_fini_frame_info (&frame->info);
489   return 0;
490 
491 
492 out_error:
493   mjpeg_error_exit1 ("Unable to write to output - aborting!");
494   return 1;
495 }
496 
497 /*
498  * Local variables:
499  *  tab-width: 8
500  *  indent-tabs-mode: nil
501  * End:
502  */
503