/* * yuvafps.c * Copyright (C) 2002 Alfonso Garcia-Patiņo Barbolani * Linear Frame averaging modification 2004 Mark Heath * * Upsamples or downsamples a yuv stream to a specified frame rate * * 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 #include #include #include #include #include "yuv4mpeg.h" #include "mpegconsts.h" #define YUVFPS_VERSION "0.1" static void print_usage() { fprintf (stderr, "usage: yuvfps -r [NewFpsNum:NewFpsDen] [-s [InputFpsNum:InputFpsDen]] [-c] [-v -h]\n" "yuvfps resamples a yuv video stream read from stdin to a new stream, identical\n" "to the source with frames repeated/copied/removed written to stdout.\n" "\n" "\t -r Frame rate for the resulting stream (in X:Y fractional form)\n" "\t -s Assume this source frame rate ignoring source YUV header\n" "\t -c Change only the output header frame rate, does not modify stream\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. // void mix(int width, int height, uint8_t * const input[], double *output[], double percent) { int x,y; double c; for (y=0; y< height; y++) for (x=0; x max) // max = output[0][x + y * width] ; //output[0][x + y * width] = (uint8_t) input[0][x + y * width]; if (!(x % 2) && !(y % 2)) { //output[1][x/2 + y/2 * width/2] += 1.0 * input[1][x/2 + y/2 * width/2] * percent; //output[2][x/2 + y/2 * width/2] += 1.0 * input[2][x/2 + y/2 * width/2] * percent; c = ( 1.0 * input[1][x/2 + y/2 * width/2] - 128) * percent + (output[1][x/2 + y/2 * width/2] - 128); output[1][x/2 + y/2 * width/2] = c + 128; c = ( 1.0 * input[2][x/2 + y/2 * width/2] - 128) * percent + (output[2][x/2 + y/2 * width/2] - 128); output[2][x/2 + y/2 * width/2] = c + 128; //output[1][x/2 + y/2 * width/2] = input[1][x/2 + y/2 * width/2] ; //output[2][x/2 + y/2 * width/2] = input[2][x/2 + y/2 * width/2] ; } } //fprintf (stderr,"Max output: %d ",max); } void intise(int width, int height, uint8_t *output[], double * const input[] ) { int x,y; for (y=0; y< height; y++) for (x=0; x> 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!"); yuv_odata[0] = (uint8_t *)malloc( frame_data_size ); yuv_odata[1] = (uint8_t *)malloc( frame_data_size >> 2); yuv_odata[2] = (uint8_t *)malloc( frame_data_size >> 2); if( !yuv_odata[0] || !yuv_odata[1] || !yuv_odata[2] ) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); yuv_fdata[0] = (double *)malloc(sizeof(double) * frame_data_size ); yuv_fdata[1] = (double *)malloc(sizeof(double) * frame_data_size >> 2); yuv_fdata[2] = (double *)malloc(sizeof(double) * frame_data_size >> 2); if( !yuv_fdata[0] || !yuv_fdata[1] || !yuv_fdata[2] ) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); if (not_normalize == 0) { /* Trying to normalize the values */ normalized_ratio = mpeg_conform_framerate( (double)src_frame_rate.n/(double)src_frame_rate.d ); mjpeg_warn( "Original framerate: %d:%d, Normalized framerate: %d:%d", src_frame_rate.n, src_frame_rate.d, normalized_ratio.n, normalized_ratio.d ); src_frame_rate.n = normalized_ratio.n; src_frame_rate.d = normalized_ratio.d; } mjpeg_warn( "Converting from %d:%d to %d:%d", src_frame_rate.n,src_frame_rate.d,dst_frame_rate.n,dst_frame_rate.d ); srcfl = (double) src_frame_rate.d / (double)src_frame_rate.n; dstfl = (double) dst_frame_rate.d / (double)dst_frame_rate.n; /* Initialize counters */ write_error_code = Y4M_OK ; read_error_code = Y4M_OK ; src_frame_counter = 0 ; dst_frame_counter = 0 ; y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_data ); black(w,h,yuv_fdata); while( Y4M_ERR_EOF != read_error_code && write_error_code == Y4M_OK ) { if (read_error_code == Y4M_OK ) { ssrcf = src_frame_counter * srcfl; sdstf = dst_frame_counter * dstfl; esrcf = (src_frame_counter + 1) * srcfl; edstf = (dst_frame_counter + 1) * dstfl; per = srcfl; if (sdstf > ssrcf) per -= (sdstf - ssrcf); if (esrcf > edstf) per -= (esrcf - edstf); per /= srcfl; per *= ( srcfl / dstfl ); mix (w,h,yuv_data,yuv_fdata,per); // mjpeg_warn ("Add percent: %f src: %f-%f dst: %f-%f ",per,ssrcf,esrcf,sdstf,edstf); if (esrcf >= edstf) { intise(w,h,yuv_odata,yuv_fdata); write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_odata ); mjpeg_info( "Writing source frame %d at dest frame %d", src_frame_counter,dst_frame_counter ); black(w,h,yuv_fdata); dst_frame_counter++; } } if (esrcf < edstf) { y4m_fini_frame_info( &in_frame ); y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_data ); ++src_frame_counter ; } } // 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] ); free( yuv_odata[0] ); free( yuv_odata[1] ); free( yuv_odata[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 change_header_only = 0 ; int not_normalize = 0; int have_src_framerate = 0; int have_framerate = 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 = "r:s:cnv: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 'h': case '?': print_usage (argv); return 0 ; break; // New frame rate case 'r': if( Y4M_OK != y4m_parse_ratio(&frame_rate, optarg) ) mjpeg_error_exit1 ("Syntax for frame rate should be Numerator:Denominator"); mjpeg_warn( "New Frame rate %d:%d", frame_rate.n,frame_rate.d ); have_framerate =1 ; break; // Assumed frame rate for source (useful when the header contains an // invalid frame rate) case 's': if( Y4M_OK != y4m_parse_ratio(&src_frame_rate,optarg) ) mjpeg_error_exit1 ("Syntax for frame rate should be Numerator:Denominator"); have_src_framerate = 1; break ; // Only change header frame-rate, not the stream itself case 'c': change_header_only = 1 ; case 'n': not_normalize = 1; 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 if (!have_src_framerate) src_frame_rate = y4m_si_get_framerate( &in_streaminfo ); if (!have_framerate) frame_rate = src_frame_rate ; y4m_copy_stream_info( &out_streaminfo, &in_streaminfo ); optind = 0; // Information output mjpeg_info ("yuv2fps (version " YUVFPS_VERSION ") is a general frame resampling utility for yuv streams"); mjpeg_info ("(C) 2002 Alfonso Garcia-Patino Barbolani "); mjpeg_info ("yuvfps -h for help, or man yuvfps"); y4m_si_set_framerate( &out_streaminfo, frame_rate ); y4m_write_stream_header(fdOut,&out_streaminfo); if( change_header_only ) frame_rate = src_frame_rate ; /* in that function we do all the important work */ resample( fdIn,&in_streaminfo, src_frame_rate, fdOut,&out_streaminfo, frame_rate, not_normalize ); y4m_fini_stream_info (&in_streaminfo); y4m_fini_stream_info (&out_streaminfo); return 0; } /* * Local variables: * tab-width: 8 * indent-tabs-mode: nil * End: */