You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
474 lines
10 KiB
474 lines
10 KiB
4 years ago
|
/*
|
||
|
* 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<height; i=i+2) {
|
||
|
|
||
|
ac_memcpy(out, in, block);
|
||
|
in += 2*block;
|
||
|
out += 2*block;
|
||
|
}
|
||
|
|
||
|
|
||
|
block = width/2;
|
||
|
|
||
|
//Cb
|
||
|
in = src2 + width*height + block;
|
||
|
out = src1 + width*height + block;
|
||
|
|
||
|
//move every second row
|
||
|
for (i=0; i<height/2; i=i+2) {
|
||
|
|
||
|
ac_memcpy(out, in, block);
|
||
|
in += 2*block;
|
||
|
out += 2*block;
|
||
|
}
|
||
|
|
||
|
|
||
|
//Cr
|
||
|
in = src2 + width*height*5/4 + block;
|
||
|
out = src1 + width*height*5/4 + block;
|
||
|
|
||
|
//move every second row
|
||
|
for (i=0; i<height/2; i=i+2) {
|
||
|
|
||
|
ac_memcpy(out, in, block);
|
||
|
in += 2*block;
|
||
|
out += 2*block;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void merge_rgb_fields(unsigned char *src1, unsigned char *src2, int width, int height)
|
||
|
{
|
||
|
|
||
|
char *in, *out;
|
||
|
|
||
|
int i, block;
|
||
|
|
||
|
block = 3*width;
|
||
|
|
||
|
in = src2;
|
||
|
out = src1;
|
||
|
|
||
|
//move every second row
|
||
|
for (i=0; i<height; i=i+2) {
|
||
|
|
||
|
ac_memcpy(out, in, block);
|
||
|
in += 2*block;
|
||
|
out += 2*block;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int interlace_test(char *video_buf, int width, int height)
|
||
|
{
|
||
|
|
||
|
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;
|
||
|
|
||
|
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<bytes; ++y) {
|
||
|
tmp = ((unsigned char) row2[y] + (unsigned char) row1[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);
|
||
|
}
|
||
|
|