/*
 *  filter_32detect.c
 *
 *  Copyright (C) Thomas Östreich - 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. 
 *
 */

#define MOD_NAME    "filter_32drop.so"
#define MOD_VERSION "v0.4 (2003-02-01)"
#define MOD_CAP     "3:2 inverse telecine removal plugin"
#define MOD_AUTHOR  "Chad Page"

#include "transcode.h"
#include "filter.h"
#include "optstr.h"

#include <inttypes.h>

// basic parameter
static int color_diff_threshold1=50;
static int color_diff_threshold2=100;
static double critical_threshold=0.00005;

/*-------------------------------------------------
 *
 * single function interface
 *
 *-------------------------------------------------*/

// static int sq( int d ) { return d * d; } // unused, EMS

static int interlace_test(char *video_buf, int width, int height, int id, int verbose)
{

    int j, n, off, block, cc_1, cc_2, cc;

    uint16_t s1, s2, s3, s4;
    
    cc_1 = 0;
    cc_2 = 0;
    
    block = width;
	
    for(j=0; j<block; ++j) {

	off=0;
	
	for(n=0; n<(height-4); n=n+2) {

	    
	    s1 = (video_buf[off+j        ] & 0xff);
	    s2 = (video_buf[off+j+  block] & 0xff);
	    s3 = (video_buf[off+j+2*block] & 0xff);
	    s4 = (video_buf[off+j+3*block] & 0xff);
	    
	    if((abs(s1 - s3) < color_diff_threshold1) &&
	       (abs(s1 - s2) > 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;
    
    if(0) fprintf(stderr, "frame [%06d]: (1) = %5d | (2) = %5d | (3) = %f | interlaced = %s\n", id, cc_1, cc_2, (double)(cc_1 + cc_2)/(width*height), ((cc)?"yes":"no")); 
    
    return(cc);
}

void merge_frames(unsigned char *f1, unsigned char *f2, int width, int height, int pw)
{
	int i;
	char *cbuf1, *cbuf2;

	/* In YUV, only merge the Y plane, since CrCb planes can't be discerned
	 * due to the merger.  This lets us also reuse the code for RGB */
	for (i = 0; i < height; i += 2) {
		tc_memcpy(&f2[i * width * pw], &f1[i * width * pw], width * pw); 
	}

	/* If we're in YUV mode, the previous frame has the correct color data */
	if (pw == 1) {
		cbuf1 = &f1[height * width];
		cbuf2 = &f2[height * width];
		tc_memcpy(cbuf2, cbuf1, (height * width / 2)); 
	}
}

int tc_filter(vframe_list_t *ptr, char *options)
{

  static vob_t *vob=NULL;
  static char *lastframe, *lastiframe;
  static int linum = -1, lfnum = -1, fnum = 0, isint = 0, dcnt = 0, dfnum = 0;

  if(ptr->tag & TC_FILTER_GET_CONFIG) {

    optstr_filter_desc (options, MOD_NAME, MOD_CAP, MOD_VERSION, "Thomas Oestreich", "VRYE", "1");

    return 0;
  }

  //----------------------------------
  //
  // filter init
  //
  //----------------------------------


  if(ptr->tag & TC_FILTER_INIT) {
    
    if((vob = tc_get_vob())==NULL) return(-1);
    
    // filter init ok.
    
    if(verbose) printf("[%s] %s %s\n", MOD_NAME, MOD_VERSION, MOD_CAP);

    lastframe = malloc(SIZE_RGB_FRAME);
    lastiframe = malloc(SIZE_RGB_FRAME);

    return(0);
  }

  //----------------------------------
  //
  // filter close
  //
  //----------------------------------

  
  if(ptr->tag & TC_FILTER_CLOSE) {
     free(lastframe); 
     free(lastiframe); 
     return(0);
  }
  
  //----------------------------------
  //
  // filter frame routine
  //
  //----------------------------------

    
  // tag variable indicates, if we are called before
  // transcodes internal video/audo frame processing routines
  // or after and determines video/audio context
  
  if((ptr->tag & TC_PRE_PROCESS) && (ptr->tag & TC_VIDEO))  {
    
    if(vob->im_v_codec==CODEC_RGB) {
      isint = interlace_test(ptr->video_buf, 3*ptr->v_width, ptr->v_height, ptr->id, 1); 
    } else {
      isint = interlace_test(ptr->video_buf, ptr->v_width, ptr->v_height, ptr->id, 1);
    }

    // fprintf(stderr, "%d %d\n", fnum, dcnt);

    if (isint) {
 	linum = fnum;

	/* If this is the second interlaced frame in a row, do a copy of the
	 * bottom field of the previous frame.
	 */
		
	if ((fnum - lfnum) == 2) {
	    merge_frames(lastiframe, ptr->video_buf, ptr->v_width, ptr->v_height, ((vob->im_v_codec == CODEC_RGB) ? 3:1) ); 
	} else {
          tc_memcpy(lastiframe, ptr->video_buf, ptr->video_size);
	  /* The use of the drop counter ensures syncronization even with
	   * video-based sources.  */
	  if (dcnt < 8) { 
	      ptr->attributes |= TC_FRAME_IS_SKIPPED;  
	      dcnt += 5;  
	      dfnum++;
	   } else {
	       /* If we'd lose sync by dropping, copy the last frame in. 
	        * If there are more than 3 interlaced frames in a row, it's
	        * probably video and we don't want to copy the last frame over */
	       if (((fnum - lfnum) < 3) && fnum) 
		   tc_memcpy(ptr->video_buf, lastframe, ptr->video_size);
	   } 
	}
    } else {
        tc_memcpy(lastframe, ptr->video_buf, ptr->video_size);
        lfnum = fnum; 
    }
    /* If we're dealing with a non-interlaced source, or close to it, it won't
     * drop enough interlaced frames, so drop here to keep sync */
    if (dcnt <= -5) {
	 ptr->attributes |= TC_FRAME_IS_SKIPPED;  
	 dcnt += 5;
	dfnum++;
    }
    fnum++;
    dcnt--;
  }  
  
  return(0);
}

