/* * ivtc.c * * Copyright (C) Thomas Oestreich - June 2001 * * This file is part of transcode, a video stream processing tool * * transcode 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, or (at your option) * any later version. * * transcode 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 GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "transcode.h" #include "ivtc.h" #include "libtc/libtc.h" // basic parameter static int color_diff_threshold1=50; static int color_diff_threshold2=100; static double critical_threshold=0.00001; static void merge_yuv_fields(unsigned char *src1, unsigned char *src2, int width, int height) { char *in, *out; int i, block; block = width; in = src2+block; out = src1+block; //move every second row //Y for (i=0; i color_diff_threshold2)) ++cc_1; if((abs(s2 - s4) < color_diff_threshold1) && (abs(s2 - s3) > color_diff_threshold2)) ++cc_2; off +=2*block; } } // compare results cc = (((double) (cc_1 + cc_2))/(width*height) > critical_threshold) ? 1:0; return(cc); } static inline void rgb_average(char *row1, char *row2, char *out, int bytes) { // calculate the average of each color entry in two arrays and return // result in char *out unsigned int y; unsigned short tmp; for (y = 0; y>1; out[y] = tmp & 0xff; } return; } static void yuv_deinterlace(char *image, int width, int height) { char *in, *out; unsigned int y, block; block = width; in = image; out = image; // convert half frame to full frame by simple interpolation out +=block; for (y = 0; y < (height>>1)-1; y++) { rgb_average(in, in+(block<<1), out, block); in += block<<1; out += block<<1; } // clone last row ac_memcpy(out, in, block); return; } static void rgb_deinterlace(char *image, int width, int height) { char *in, *out; unsigned int y, block; block = width * 3; in = image; out = image; // convert half frame to full frame by simple interpolation out +=block; for (y = 0; y < (height>>1)-1; y++) { rgb_average(in, in+(block<<1), out, block); in += block<<1; out += block<<1; } // clone last row ac_memcpy(out, in, block); return; } // ---------------------------------------------------------------- // // reverse 3:2 pulldown (inverse telecine) // currently, only a few number of hardcoded sequences // indicated by pulldown flag, are supported, s. below: static int pulldown_drop_ctr=0, pulldown_frame_ctr=0, pulldown_buffer_flag=0; int ivtc(int *flag, int pflag, char *buffer, char *pulldown_buffer, int width, int height, int size, int vcodec, int verbose) { int interlace_flag = 0; int merge_flag = 0, flush_flag=0; int last_frame = 0, must_drop=0; static int merge_ctr=0, interlace_ctr=0, flush_ctr=0, post_interlace_ctr=0; //Ok, this sequence has 3:2 pulldown applied, says demuxer //ignore demuxer drop suggestions int clone_flag=*flag; ++pulldown_frame_ctr; // check if frame is interlaced if(vcodec==CODEC_RGB) { interlace_flag=interlace_test(buffer, 3*width, height); } else { interlace_flag=interlace_test(buffer, width, height); } //case 1: if(pulldown_buffer_flag==0 && interlace_flag==1) { //copy first frame of pair to tmp buffer if(verbose & TC_STATS) tc_log_msg(__FILE__, "COPY: (%2d)", pulldown_frame_ctr); ac_memcpy(pulldown_buffer, buffer, size); pulldown_buffer_flag=1; clone_flag=0; //drop frame ++pulldown_drop_ctr; goto resume; } //case 2: if(pulldown_buffer_flag==1 && interlace_flag==1) { //this means, we need to contruct a new frame from the current //and the previous, stored in pulldown_buffer if(verbose & TC_STATS) tc_log_msg(__FILE__, "MERGE (%2d)", pulldown_frame_ctr); if(vcodec==CODEC_RGB) { merge_rgb_fields(buffer, pulldown_buffer, width, height); } else { merge_yuv_fields(buffer, pulldown_buffer, width, height); } clone_flag=1; //use this frame merge_flag=1; pulldown_buffer_flag=0; goto resume; } //case 3: if(pulldown_buffer_flag==1 && interlace_flag==0) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "FLUSH: (%2d)", pulldown_frame_ctr); //failed to detect the second interlaced frame of the pair pulldown_buffer_flag=0; //clear buffer flush_flag=1; clone_flag=1; goto resume; } //case 4: if(pulldown_buffer_flag==0 && interlace_flag==0) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "PASS: (%2d)", pulldown_frame_ctr); clone_flag=1; goto resume; } resume: if(interlace_flag) ++interlace_ctr; if(merge_flag) ++merge_ctr; if(flush_flag) ++flush_ctr; switch(pflag) { case 1: //15 frames, 3 to drop last_frame = 15; must_drop = 3; //force frame drop - no interlaced frames detected at checkpoint 1 if(pulldown_frame_ctr==5 && pulldown_drop_ctr == 0) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } //force frame drop - no interlaced frames detected at checkpoint 2 if(pulldown_frame_ctr==10 && pulldown_drop_ctr < 2) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } //force frame drop - no interlaced frames detected at checkpoint 3 if(pulldown_frame_ctr==15 && pulldown_drop_ctr < 3) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } break; case 2: //15 frames, 4 to drop last_frame = 15; must_drop=4; //force frame drop - no interlaced frames detected at checkpoint 1 if(pulldown_frame_ctr==4 && pulldown_drop_ctr == 0) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } //force frame drop - no interlaced frames detected at checkpoint 2 if(pulldown_frame_ctr==8 && pulldown_drop_ctr < 2) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } //force frame drop - no interlaced frames detected at checkpoint 3 if(pulldown_frame_ctr==12 && pulldown_drop_ctr < 3) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } //force frame drop - no interlaced frames detected at checkpoint 4 if(pulldown_frame_ctr==15 && pulldown_drop_ctr < 4) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } break; case 3: //4 frames, 2 to drop last_frame=4; must_drop=2; //force frame drop - no interlaced frames detected at checkpoint 1 if(pulldown_frame_ctr==2 && pulldown_drop_ctr == 0) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } //force frame drop - no interlaced frames detected at checkpoint 2 if(pulldown_frame_ctr==4 && pulldown_drop_ctr < 2) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } break; case 4: //11 frames, 1 to drop last_frame = 11; must_drop=1; //force frame drop - no interlaced frames detected at checkpoint 1 if(pulldown_frame_ctr==11 && pulldown_drop_ctr == 0) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "ADJUST"); clone_flag=0; ++pulldown_drop_ctr; } break; } //check for over drop if(pulldown_drop_ctr>must_drop) { clone_flag=1; --pulldown_drop_ctr; } //still deinterlaced? if(interlace_flag==1 && merge_flag==0 && clone_flag==1) { //FIXME: move this to main frame processing if(vcodec==CODEC_RGB) { rgb_deinterlace(buffer, width, height); } else { yuv_deinterlace(buffer, width, height); } ++post_interlace_ctr; } //reset if(pulldown_frame_ctr==last_frame) { if(verbose & TC_STATS) tc_log_msg(__FILE__, "DROP: (%2d)", pulldown_drop_ctr); //summary if(verbose & TC_COUNTER) tc_log_msg(__FILE__, "frames=(%2d|%d), interlaced=%2d, merged=%2d, flushed=%2d, post=%2d", last_frame, must_drop, interlace_ctr, merge_ctr, flush_ctr, post_interlace_ctr); pulldown_frame_ctr = 0; pulldown_drop_ctr = 0; flush_ctr=0; merge_ctr=0; interlace_ctr=0; post_interlace_ctr=0; //do not reset buffer flag, important for next sequence } *flag=clone_flag; return(0); }