/*
  *  yuvfade.c

  *  black-fade  2004 Mark Heath <mjpeg at silicontrip.org>
  *  will fade a yuv4 stream to black after a specified number of frames
  *
  *  based on code:
  *  Copyright (C) 2002 Alfonso Garcia-Pati<F1>o Barbolani <barbolani at jazzfree.com>
  *
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include "yuv4mpeg.h"
#include "mpegconsts.h"

#define YUVFPS_VERSION "0.1"

static void print_usage() 
{
  fprintf (stderr,
	   "usage: yuvfps -c Count -f fadeCount [-v -h]\n"
	   "yuvfade fades the last f frames of a yuv video stream read from stdin\n"
           "\n"
           "\t -c skip this number of frames\n"
           "\t -f fade to black for this many frames\n"
	   "\t -v Verbosity degree : 0=quiet, 1=normal, 2=verbose/debug\n"
	   "\t -h print this help\n"
         );
}
//
// Resamples the video stream coming from fdIn and writes it
// to fdOut.
// There are two variations of the same theme:
//
//   Upsampling: frames are duplicated when needed
//   Downsampling: frames from the original are skipped
//
//   Parameters:
//
//      fdIn,fdOut : File descriptors for reading and writing the stream
//      inStream, outStream: stream handlers for the source and destination streams.
//                           It is assumed that they are correctly initialized from/to
//                           their respective file I/O streams with the desired sample rates.
//
//      src_frame_rate, frame_rate: ratios for source and destination frame rate
//                           (note that this may not match the information contained
//                            in the stream itself due to command line options      
//                           )
// 
//  In both cases a Bresenham - style algorithm is used.
//
static void resample(  int fdIn 
                      , y4m_stream_info_t  *inStrInfo
                      , int fdOut
                      , y4m_stream_info_t  *outStrInfo
                      , int after
                      , int count
                    )
{
  y4m_frame_info_t   in_frame ;
  uint8_t            *yuv_data[3] ;
  int                frame_data_size ;
  int                read_error_code ;
  int                write_error_code ;
  int                src_frame_counter ;
	int w,h,x,y;
float mul,val;

  // Allocate memory for the YUV channels
h = y4m_si_get_height(inStrInfo) ;
w = y4m_si_get_width(inStrInfo);
  frame_data_size =  w * h;
  yuv_data[0] = (uint8_t *)malloc( frame_data_size );
  yuv_data[1] = (uint8_t *)malloc( frame_data_size >> 2);
  yuv_data[2] = (uint8_t *)malloc( frame_data_size >> 2);

  if( !yuv_data[0] || !yuv_data[1] || !yuv_data[2] )
    mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!");
 

  /* Initialize counters */

  write_error_code = Y4M_OK ;

  src_frame_counter = 0 ;
  y4m_init_frame_info( &in_frame );
  read_error_code = y4m_read_frame(fdIn,inStrInfo,&in_frame,yuv_data );
  ++src_frame_counter ;

  while( Y4M_ERR_EOF != read_error_code && write_error_code == Y4M_OK ) {

	if (src_frame_counter > after) {
		// fade frame
		mul =   1.0 * (after + count - src_frame_counter)  / count;
		if (mul < 0)
			mul = 0;
		for (y=0; y<h; y++)
			for (x=0; x<w; x++)
			{
				val = (yuv_data[0][x+y*w] - 16) * mul + 16;
				yuv_data[0][x+y*w] = val;
/* I think chroma fading needs to be started after luma fading */
				if ( (mul * 1.5 < 1.0) && (!(y%2) && !(x%2)) )
				{
					val = (yuv_data[1][x/2+y/2*w/2] - 128) * 1.5 * mul + 128;
					yuv_data[1][x/2+y/2*w/2] = val;
					val = (yuv_data[2][x/2+y/2*w/2] - 128) * 1.5 * mul + 128;
					yuv_data[2][x/2+y/2*w/2] = val;
				}
			}
	}
		

    write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_data );
      ++src_frame_counter ;
      y4m_fini_frame_info( &in_frame );
      y4m_init_frame_info( &in_frame );
      read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_data );
  }
  // Clean-up regardless an error happened or not
  y4m_fini_frame_info( &in_frame );
  free( yuv_data[0] );
  free( yuv_data[1] );
  free( yuv_data[2] );

  if( read_error_code != Y4M_ERR_EOF )
    mjpeg_error_exit1 ("Error reading from input stream!");
  if( write_error_code != Y4M_OK )
    mjpeg_error_exit1 ("Error writing output stream!");

}

// *************************************************************************************
// MAIN
// *************************************************************************************
int main (int argc, char *argv[])
{

  int verbose = LOG_ERROR ;
  int count = 0 ;
  int frames = 0;
  int fdIn = 0 ;
  int fdOut = 1 ;
  y4m_stream_info_t in_streaminfo, out_streaminfo ;
  y4m_ratio_t frame_rate, src_frame_rate ;

  const static char *legal_flags = "f:c:v:h";
  int c ;

  while ((c = getopt (argc, argv, legal_flags)) != -1) {
    switch (c) {
      case 'v':
        verbose = atoi (optarg);
        if (verbose < 0 || verbose > 2)
          mjpeg_error_exit1 ("Verbose level must be [0..2]");
        break;
	case 'c':
	count =  atoi (optarg);
	break;
	case 'f':
	frames = atoi (optarg);
	break;
        case 'h':
        case '?':
          print_usage (argv);
          return 0 ;
          break;
    }
  }
  // mjpeg tools global initialisations
  mjpeg_default_handler_verbosity (verbose);

  // Initialize input streams
  y4m_init_stream_info (&in_streaminfo);
  y4m_init_stream_info (&out_streaminfo);

  // ***************************************************************
  // Get video stream informations (size, framerate, interlacing, aspect ratio).
  // The streaminfo structure is filled in
  // ***************************************************************
  // INPUT comes from stdin, we check for a correct file header
  if (y4m_read_stream_header (fdIn, &in_streaminfo) != Y4M_OK)
    mjpeg_error_exit1 ("Could'nt read YUV4MPEG header!");

  // Prepare output stream
  src_frame_rate = y4m_si_get_framerate( &in_streaminfo );
  frame_rate = src_frame_rate ;
  y4m_copy_stream_info( &out_streaminfo, &in_streaminfo );

  
  // Information output
  mjpeg_info ("yuvfade (version " YUVFPS_VERSION
              ") is a general black fade utility for yuv streams");

  y4m_write_stream_header(fdOut,&out_streaminfo);
    
  /* in that function we do all the important work */
  resample( fdIn,&in_streaminfo,  fdOut,&out_streaminfo,
            count, frames );

  y4m_fini_stream_info (&in_streaminfo);
  y4m_fini_stream_info (&out_streaminfo);

  return 0;
}
/*
 * Local variables:
 *  tab-width: 8
 *  indent-tabs-mode: nil
 * End:
 */