/* * yuvdeinterlace.c * deinterlace 2004 Mark Heath * converts interlaced source material into progressive by halving * the vertical resolution and doubling the frame rate. * * based on code: * Copyright (C) 2002 Alfonso Garcia-Patiņo Barbolani * * 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 YUVDE_VERSION "0.1" static void print_usage() { fprintf (stderr, "usage: yuvdeinterlace [-v -t -b -d -h]\n" "yuvdeinterlace deinterlaces source material by\n" "doubling the frame rate and halving the vertical resolution.\n" "\n" "\t -t force top field first\n" "\t -b force bottom field first\n" "\t -n half vertical height (not duplicating fields)\n" "\t -i interlace (rather than deinterlace -n has no meaning)\n" "\t -v Verbosity degree : 0=quiet, 1=normal, 2=verbose/debug\n" "\t -h print this help\n" ); } static void deinterlace( int fdIn , y4m_stream_info_t *inStrInfo , int fdOut , y4m_stream_info_t *outStrInfo ) { y4m_frame_info_t in_frame ; uint8_t *yuv_data[3] ; uint8_t *yuv_o1data[3] ; uint8_t *yuv_o2data[3] ; int frame_data_size ; int read_error_code ; int write_error_code ; int interlaced = -1; //=Y4M_ILACE_NONE for not-interlaced scaling, =Y4M_ILACE_TOP_FIRST or Y4M_ILACE_BOTTOM_FIRST for interlaced scaling int w,ih,oh,y; // Allocate memory for the YUV channels interlaced = y4m_si_get_interlace(inStrInfo); ih = y4m_si_get_height(inStrInfo) ; w = y4m_si_get_width(inStrInfo); frame_data_size = ih * w; 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); oh = y4m_si_get_height(outStrInfo) ; w = y4m_si_get_width(outStrInfo); frame_data_size = oh * w; yuv_o1data[0] = (uint8_t *)malloc( frame_data_size ); yuv_o1data[1] = (uint8_t *)malloc( frame_data_size >> 2); yuv_o1data[2] = (uint8_t *)malloc( frame_data_size >> 2); yuv_o2data[0] = (uint8_t *)malloc( frame_data_size ); yuv_o2data[1] = (uint8_t *)malloc( frame_data_size >> 2); yuv_o2data[2] = (uint8_t *)malloc( frame_data_size >> 2); if( !yuv_o1data[0] || !yuv_o1data[1] || !yuv_o1data[2] ) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); if( !yuv_o2data[0] || !yuv_o2data[1] || !yuv_o2data[2] ) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); 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 ; y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_data ); while( Y4M_ERR_EOF != read_error_code && write_error_code == Y4M_OK ) { // de interlace frame. if (read_error_code == Y4M_OK) { for (y=0; y<(ih/2); y++) { if (oh == ih) { memcpy(&yuv_o1data[0][2*y*w],&yuv_data[0][2*y*w],w); memcpy(&yuv_o1data[0][(2*y+1)*w],&yuv_data[0][2*y*w],w); memcpy(&yuv_o2data[0][2*y*w],&yuv_data[0][(2*y+1)*w],w); memcpy(&yuv_o2data[0][(2*y+1)*w],&yuv_data[0][(2*y+1)*w],w); } else { memcpy(&yuv_o1data[0][y*w],&yuv_data[0][2*y*w],w); memcpy(&yuv_o2data[0][y*w],&yuv_data[0][(2*y+1)*w],w); } if (y % 2) { if (oh == ih) { memcpy(&yuv_o2data[1][y*w/2],&yuv_data[1][y*w/2],w/2); memcpy(&yuv_o2data[1][(y-1)*w/2],&yuv_data[1][y*w/2],w/2); memcpy(&yuv_o2data[2][y*w/2],&yuv_data[2][y*w/2],w/2); memcpy(&yuv_o2data[2][(y-1)*w/2],&yuv_data[2][y*w/2],w/2); } else { memcpy(&yuv_o2data[1][y/2*w/2],&yuv_data[1][y*w/2],w/2); memcpy(&yuv_o2data[2][y/2*w/2],&yuv_data[2][y*w/2],w/2); } } else { if (oh == ih) { memcpy(&yuv_o1data[1][y*w/2],&yuv_data[1][y*w/2],w/2); memcpy(&yuv_o1data[1][(y+1)*w/2],&yuv_data[1][y*w/2],w/2); memcpy(&yuv_o1data[2][y*w/2],&yuv_data[2][y*w/2],w/2); memcpy(&yuv_o1data[2][(y+1)*w/2],&yuv_data[2][y*w/2],w/2); } else { memcpy(&yuv_o1data[1][y/2*w/2],&yuv_data[1][y*w/2],w/2); memcpy(&yuv_o1data[2][y/2*w/2],&yuv_data[2][y*w/2],w/2); } } } if (interlaced == Y4M_ILACE_BOTTOM_FIRST) { write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_o1data ); write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_o2data ); } else { // assume top field first // other possibility is non interlaced so why use this program // We've already printed a warning. write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_o2data ); write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_o1data ); } } 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] ); free( yuv_o1data[0] ); free( yuv_o1data[1] ); free( yuv_o1data[2] ); free( yuv_o2data[0] ); free( yuv_o2data[1] ); free( yuv_o2data[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!"); } static void depro( int fdIn , y4m_stream_info_t *inStrInfo , int fdOut , y4m_stream_info_t *outStrInfo ) { y4m_frame_info_t in_frame ; uint8_t *yuv_data[3] ; uint8_t *yuv_o1data[3] ; uint8_t *yuv_o2data[3] ; int frame_data_size ; int read_error_code ; int write_error_code ; int interlaced = -1; //=Y4M_ILACE_NONE for not-interlaced scaling, =Y4M_ILACE_TOP_FIRST or Y4M_ILACE_BOTTOM_FIRST for interlaced scaling int w,h,y; // Allocate memory for the YUV channels interlaced = y4m_si_get_interlace(outStrInfo); h = y4m_si_get_height(inStrInfo) ; w = y4m_si_get_width(inStrInfo); frame_data_size = h * w; 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); h = y4m_si_get_height(outStrInfo) ; w = y4m_si_get_width(outStrInfo); frame_data_size = h * w; yuv_o1data[0] = (uint8_t *)malloc( frame_data_size ); yuv_o1data[1] = (uint8_t *)malloc( frame_data_size >> 2); yuv_o1data[2] = (uint8_t *)malloc( frame_data_size >> 2); yuv_o2data[0] = (uint8_t *)malloc( frame_data_size ); yuv_o2data[1] = (uint8_t *)malloc( frame_data_size >> 2); yuv_o2data[2] = (uint8_t *)malloc( frame_data_size >> 2); if( !yuv_o1data[0] || !yuv_o1data[1] || !yuv_o1data[2] ) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); if( !yuv_o2data[0] || !yuv_o2data[1] || !yuv_o2data[2] ) mjpeg_error_exit1 ("Could'nt allocate memory for the YUV4MPEG data!"); 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 ; y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o1data ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o2data ); while( Y4M_ERR_EOF != read_error_code && write_error_code == Y4M_OK ) { if (read_error_code == Y4M_OK) { // de interlace frame. for (y=0; y>1); x++) { yuv_data[1][(y>>1)*(w>>1)+x]= (yuv_o1data[1][(y>>1)*(w>>1)+x]+yuv_o2data[1][(y>>1)*(w>>1)+x])/2 ; yuv_data[2][(y>>1)*(w>>1)+x]= (yuv_o1data[2][(y>>1)*(w>>1)+x]+yuv_o2data[2][(y>>1)*(w>>1)+x])/2 ; } } write_error_code = y4m_write_frame( fdOut, outStrInfo, &in_frame, yuv_data ); } y4m_fini_frame_info( &in_frame ); y4m_init_frame_info( &in_frame ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o1data ); read_error_code = y4m_read_frame(fdIn, inStrInfo,&in_frame,yuv_o2data ); } // 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_o1data[0] ); free( yuv_o1data[1] ); free( yuv_o1data[2] ); free( yuv_o2data[0] ); free( yuv_o2data[1] ); free( yuv_o2data[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 top_field =0, bottom_field = 0,double_height=1; int fdIn = 0 ; int fdOut = 1 ; y4m_stream_info_t in_streaminfo, out_streaminfo ; y4m_ratio_t frame_rate; int interlaced,ilace=0; int height; int c ; const static char *legal_flags = "vnitbh"; 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; case 't': top_field = 1 ; break; case 'b': bottom_field = 1; break; case 'n': double_height = 0; break; case 'i': ilace = 1; break; } } if ((bottom_field==1) && (top_field==1)) { fprintf (stderr,"top field and bottom field specified\n"); exit(1); } // 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!"); // Check input parameters height = y4m_si_get_height (&in_streaminfo); frame_rate = y4m_si_get_framerate( &in_streaminfo ); interlaced = y4m_si_get_interlace (&in_streaminfo); if ((height%2) && (ilace == 0)) mjpeg_error_exit1("material is not even frame height"); if ((interlaced == Y4M_ILACE_NONE) && (ilace == 0)) mjpeg_warn("source material not interlaced"); if ((interlaced != Y4M_ILACE_NONE) && (ilace == 1)) mjpeg_warn("source material is interlaced"); if (ilace == 1) { if (top_field == 1) interlaced = Y4M_ILACE_TOP_FIRST; if (bottom_field == 1) interlaced = Y4M_ILACE_BOTTOM_FIRST; frame_rate.n /= 2; } if (ilace == 0) { if (top_field == 1) y4m_si_set_interlace(&in_streaminfo, Y4M_ILACE_TOP_FIRST); if (bottom_field == 1) y4m_si_set_interlace(&in_streaminfo, Y4M_ILACE_BOTTOM_FIRST); if (! double_height) height /=2; frame_rate.n *= 2 ; interlaced = Y4M_ILACE_NONE; } // Prepare output stream // De interlacer doubles framerate but halves vertical resolution. y4m_copy_stream_info( &out_streaminfo, &in_streaminfo ); // Information output mjpeg_info ("yuvdeinterlace (version " YUVDE_VERSION ") is a general deinterlace/interlace utility for yuv streams"); mjpeg_info ("(C) 2005 Mark Heath "); mjpeg_info ("yuvdeinterlace -h for help"); y4m_si_set_framerate( &out_streaminfo, frame_rate ); y4m_si_set_height (&out_streaminfo, height); y4m_si_set_interlace(&out_streaminfo, interlaced); y4m_write_stream_header(fdOut,&out_streaminfo); /* in that function we do all the important work */ if (ilace == 0) deinterlace( fdIn,&in_streaminfo, fdOut,&out_streaminfo); if (ilace == 1) depro( fdIn,&in_streaminfo, fdOut,&out_streaminfo); y4m_fini_stream_info (&in_streaminfo); y4m_fini_stream_info (&out_streaminfo); return 0; } /* * Local variables: * tab-width: 8 * indent-tabs-mode: nil * End: */