summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorjbrjake <[email protected]>2007-07-27 14:55:58 +0000
committerjbrjake <[email protected]>2007-07-27 14:55:58 +0000
commit5a4a250000ce8735639503aac2ee29def935865d (patch)
treed7efc0e7e44e06e80ba67827e137c970ac02743e /libhb
parent50c3c15c88172bb00dd787e39ce66eb11480717d (diff)
This huge patch from huevos_rancheros ports a number of video filters from mencoder to HandBrake: yadif+mcdeint, hqdn3d, pp7, and pullup+softskip+harddup. What this means is that HB now has stateless inverse telecine, temporal denoising, and motion-adaptive deinterlacing!
HandBrake is growing up =) Thank you, huevos_rancheros! git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@749 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/Jamfile2
-rw-r--r--libhb/Makefile2
-rw-r--r--libhb/common.h32
-rw-r--r--libhb/deblock.c491
-rw-r--r--libhb/decmpeg2.c3
-rw-r--r--libhb/deinterlace.c595
-rw-r--r--libhb/denoise.c463
-rw-r--r--libhb/detelecine.c1039
-rw-r--r--libhb/fifo.c9
-rw-r--r--libhb/internal.h12
-rw-r--r--libhb/render.c234
11 files changed, 2819 insertions, 63 deletions
diff --git a/libhb/Jamfile b/libhb/Jamfile
index ce04831df..41cc93886 100644
--- a/libhb/Jamfile
+++ b/libhb/Jamfile
@@ -10,7 +10,7 @@ LIBHB_SRC =
ipodutil.cpp common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c update.c
demuxmpeg.c fifo.c render.c reader.c muxcommon.c muxmp4.c sync.c stream.c
decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c decavcodec.c encxvid.c
-muxavi.c enclame.c muxogm.c encvorbis.c dvd.c muxmkv.c ;
+muxavi.c enclame.c muxogm.c encvorbis.c dvd.c muxmkv.c deblock.c deinterlace.c denoise.c detelecine.c;
Library libhb : $(LIBHB_SRC) ;
diff --git a/libhb/Makefile b/libhb/Makefile
index 6135b1161..deba9a30b 100644
--- a/libhb/Makefile
+++ b/libhb/Makefile
@@ -25,7 +25,7 @@ SRCS = common.c hb.c ports.c scan.c work.c decmpeg2.c encavcodec.c \
update.c demuxmpeg.c fifo.c render.c reader.c muxcommon.c stream.c \
muxmp4.c sync.c decsub.c deca52.c decdca.c encfaac.c declpcm.c encx264.c \
decavcodec.c encxvid.c muxmkv.c muxavi.c enclame.c muxogm.c encvorbis.c \
- dvd.c ipodutil.cpp
+ dvd.c ipodutil.cpp deblock.c deinterlace.c denoise.c detelecine.c
OTMP = $(SRCS:%.c=%.o)
OBJS = $(OTMP:%.cpp=%.o)
diff --git a/libhb/common.h b/libhb/common.h
index 81988de3d..de56a5c63 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -43,6 +43,8 @@ typedef struct hb_state_s hb_state_t;
typedef union hb_esconfig_u hb_esconfig_t;
typedef struct hb_work_private_s hb_work_private_t;
typedef struct hb_work_object_s hb_work_object_t;
+typedef struct hb_filter_private_s hb_filter_private_t;
+typedef struct hb_filter_object_s hb_filter_object_t;
typedef struct hb_buffer_s hb_buffer_t;
typedef struct hb_fifo_s hb_fifo_t;
typedef struct hb_lock_s hb_lock_t;
@@ -126,6 +128,7 @@ struct hb_job_s
int crop[4];
int deinterlace;
+ hb_list_t * filters;
int width;
int height;
int keep_ratio;
@@ -516,4 +519,33 @@ extern hb_work_object_t hb_encfaac;
extern hb_work_object_t hb_enclame;
extern hb_work_object_t hb_encvorbis;
+#define FILTER_OK 0
+#define FILTER_DELAY 1
+#define FILTER_FAILED 2
+#define FILTER_DROP 3
+
+struct hb_filter_object_s
+{
+ int id;
+ char * name;
+ char * settings;
+
+#ifdef __LIBHB__
+ hb_filter_private_t* (* init) ( int, int, int, char * );
+
+ int (* work) ( const hb_buffer_t *, hb_buffer_t **,
+ int, int, int, hb_filter_private_t * );
+
+ void (* close) ( hb_filter_private_t * );
+
+ hb_filter_private_t * private_data;
+ //hb_buffer_t * buffer;
+#endif
+};
+
+extern hb_filter_object_t hb_filter_detelecine;
+extern hb_filter_object_t hb_filter_deinterlace;
+extern hb_filter_object_t hb_filter_deblock;
+extern hb_filter_object_t hb_filter_denoise;
+
#endif
diff --git a/libhb/deblock.c b/libhb/deblock.c
new file mode 100644
index 000000000..2d8b16658
--- /dev/null
+++ b/libhb/deblock.c
@@ -0,0 +1,491 @@
+/*
+ Copyright (C) 2005 Michael Niedermayer <[email protected]>
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "hb.h"
+#include "ffmpeg/avcodec.h"
+#include "mpeg2dec/mpeg2.h"
+
+#define PP7_QP_DEFAULT 0
+#define PP7_MODE_DEFAULT 2
+
+#define XMIN(a,b) ((a) < (b) ? (a) : (b))
+#define XMAX(a,b) ((a) > (b) ? (a) : (b))
+
+typedef short DCTELEM;
+
+//===========================================================================//
+static const uint8_t __attribute__((aligned(8))) pp7_dither[8][8] =
+{
+ { 0, 48, 12, 60, 3, 51, 15, 63, },
+ { 32, 16, 44, 28, 35, 19, 47, 31, },
+ { 8, 56, 4, 52, 11, 59, 7, 55, },
+ { 40, 24, 36, 20, 43, 27, 39, 23, },
+ { 2, 50, 14, 62, 1, 49, 13, 61, },
+ { 34, 18, 46, 30, 33, 17, 45, 29, },
+ { 10, 58, 6, 54, 9, 57, 5, 53, },
+ { 42, 26, 38, 22, 41, 25, 37, 21, },
+};
+
+struct hb_filter_private_s
+{
+ int pix_fmt;
+ int width[3];
+ int height[3];
+
+ int pp7_qp;
+ int pp7_mode;
+ int pp7_mpeg2;
+ int pp7_temp_stride;
+ uint8_t * pp7_src;
+
+ AVPicture pic_in;
+ AVPicture pic_out;
+ hb_buffer_t * buf_out;
+};
+
+hb_filter_private_t * hb_deblock_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings );
+
+int hb_deblock_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv );
+
+void hb_deblock_close( hb_filter_private_t * pv );
+
+hb_filter_object_t hb_filter_deblock =
+{
+ FILTER_DEBLOCK,
+ "Deblock (pp7)",
+ NULL,
+ hb_deblock_init,
+ hb_deblock_work,
+ hb_deblock_close,
+};
+
+static inline void pp7_dct_a( DCTELEM * dst, uint8_t * src, int stride )
+{
+ int i;
+
+ for( i = 0; i < 4; i++ )
+ {
+ int s0 = src[0*stride] + src[6*stride];
+ int s1 = src[1*stride] + src[5*stride];
+ int s2 = src[2*stride] + src[4*stride];
+ int s3 = src[3*stride];
+ int s = s3+s3;
+
+ s3 = s - s0;
+ s0 = s + s0;
+ s = s2 + s1;
+ s2 = s2 - s1;
+
+ dst[0] = s0 + s;
+ dst[2] = s0 - s;
+ dst[1] = 2*s3 + s2;
+ dst[3] = s3 - s2*2;
+
+ src++;
+ dst += 4;
+ }
+}
+
+static void pp7_dct_b( DCTELEM * dst, DCTELEM * src )
+{
+ int i;
+
+ for( i = 0; i < 4; i++ )
+ {
+ int s0 = src[0*4] + src[6*4];
+ int s1 = src[1*4] + src[5*4];
+ int s2 = src[2*4] + src[4*4];
+ int s3 = src[3*4];
+ int s = s3+s3;
+
+ s3 = s - s0;
+ s0 = s + s0;
+ s = s2 + s1;
+ s2 = s2 - s1;
+
+ dst[0*4] = s0 + s;
+ dst[2*4] = s0 - s;
+ dst[1*4] = 2*s3 + s2;
+ dst[3*4] = s3 - s2*2;
+
+ src++;
+ dst++;
+ }
+}
+
+#define N (1<<16)
+#define N0 4
+#define N1 5
+#define N2 10
+#define SN0 2
+#define SN1 2.2360679775
+#define SN2 3.16227766017
+
+static const int pp7_factor[16] =
+{
+ N/(N0*N0), N/(N0*N1), N/(N0*N0),N/(N0*N2),
+ N/(N1*N0), N/(N1*N1), N/(N1*N0),N/(N1*N2),
+ N/(N0*N0), N/(N0*N1), N/(N0*N0),N/(N0*N2),
+ N/(N2*N0), N/(N2*N1), N/(N2*N0),N/(N2*N2),
+};
+
+static int pp7_threshold[99][16];
+
+static void pp7_init_threshold( void )
+{
+ int qp, i;
+ int bias = 0;
+
+ for( qp = 0; qp < 99; qp++ )
+ {
+ for( i = 0; i < 16; i++ )
+ {
+ pp7_threshold[qp][i] =
+ ((i&1)?SN2:SN0) * ((i&4)?SN2:SN0) *
+ XMAX(1,qp) * (1<<2) - 1 - bias;
+ }
+ }
+}
+
+static int pp7_hard_threshold( DCTELEM * src, int qp )
+{
+ int i;
+ int a;
+
+ a = src[0] * pp7_factor[0];
+ for( i = 1; i < 16; i++ )
+ {
+ unsigned int threshold1 = pp7_threshold[qp][i];
+ unsigned int threshold2 = (threshold1<<1);
+ int level= src[i];
+ if( ((unsigned)(level+threshold1)) > threshold2 )
+ {
+ a += level * pp7_factor[i];
+ }
+ }
+ return (a + (1<<11)) >> 12;
+}
+
+static int pp7_medium_threshold( DCTELEM * src, int qp )
+{
+ int i;
+ int a;
+
+ a = src[0] * pp7_factor[0];
+ for( i = 1; i < 16; i++ )
+ {
+ unsigned int threshold1 = pp7_threshold[qp][i];
+ unsigned int threshold2 = (threshold1<<1);
+ int level= src[i];
+ if( ((unsigned)(level+threshold1)) > threshold2 )
+ {
+ if( ((unsigned)(level+2*threshold1)) > 2*threshold2 )
+ {
+ a += level * pp7_factor[i];
+ }
+ else
+ {
+ if( level>0 )
+ {
+ a += 2*(level - (int)threshold1) * pp7_factor[i];
+ }
+ else
+ {
+ a += 2*(level + (int)threshold1) * pp7_factor[i];
+ }
+ }
+ }
+ }
+ return (a + (1<<11)) >> 12;
+}
+
+static int pp7_soft_threshold( DCTELEM * src, int qp )
+{
+ int i;
+ int a;
+
+ a = src[0] * pp7_factor[0];
+ for( i = 1; i < 16; i++ )
+ {
+ unsigned int threshold1 = pp7_threshold[qp][i];
+ unsigned int threshold2 = (threshold1<<1);
+ int level= src[i];
+ if( ((unsigned)(level+threshold1))>threshold2 )
+ {
+ if( level>0 )
+ {
+ a += (level - (int)threshold1) * pp7_factor[i];
+ }
+ else
+ {
+ a += (level + (int)threshold1) * pp7_factor[i];
+ }
+ }
+ }
+ return (a + (1<<11)) >> 12;
+}
+
+static int ( * pp7_requantize )( DCTELEM * src, int qp ) = pp7_hard_threshold;
+
+static void pp7_filter( hb_filter_private_t * pv,
+ uint8_t * dst,
+ uint8_t * src,
+ int width,
+ int height,
+ uint8_t * qp_store,
+ int qp_stride,
+ int is_luma)
+{
+ int x, y;
+
+ const int stride = is_luma ? pv->pp7_temp_stride : ((width+16+15)&(~15));
+ uint8_t * p_src = pv->pp7_src + 8*stride;
+ DCTELEM * block = (DCTELEM *)(pv->pp7_src);
+ DCTELEM * temp = (DCTELEM *)(pv->pp7_src + 32);
+
+ if( !src || !dst )
+ {
+ return;
+ }
+
+ for( y = 0; y < height; y++ )
+ {
+ int index = 8 + 8*stride + y*stride;
+ memcpy( p_src + index, src + y*width, width );
+
+ for( x = 0; x < 8; x++ )
+ {
+ p_src[index - x - 1] = p_src[index + x ];
+ p_src[index + width + x ] = p_src[index + width - x - 1];
+ }
+ }
+
+ for( y = 0; y < 8; y++ )
+ {
+ memcpy( p_src + ( 7-y)*stride,
+ p_src + ( y+8)*stride, stride );
+ memcpy( p_src + (height+8+y)*stride,
+ p_src + (height-y+7)*stride, stride );
+ }
+
+ for( y = 0; y < height; y++ )
+ {
+ for( x = -8; x < 0; x += 4 )
+ {
+ const int index = x + y*stride + (8-3)*(1+stride) + 8;
+ uint8_t * src = p_src + index;
+ DCTELEM * tp = temp+4*x;
+
+ pp7_dct_a( tp+4*8, src, stride );
+ }
+
+ for( x = 0; x < width; )
+ {
+ const int qps = 3 + is_luma;
+ int end = XMIN(x+8, width);
+
+ int qp;
+ if( pv->pp7_qp )
+ {
+ qp = pv->pp7_qp;
+ }
+ else
+ {
+ qp = qp_store[ (XMIN(x, width-1)>>qps) +
+ (XMIN(y, height-1)>>qps) * qp_stride ];
+
+ if( pv->pp7_mpeg2 )
+ {
+ qp >>= 1;
+ }
+ }
+
+ for( ; x < end; x++ )
+ {
+ const int index = x + y*stride + (8-3)*(1+stride) + 8;
+ uint8_t * src = p_src + index;
+ DCTELEM * tp = temp+4*x;
+ int v;
+
+ if( (x&3) == 0 )
+ {
+ pp7_dct_a( tp+4*8, src, stride );
+ }
+
+ pp7_dct_b( block, tp );
+
+ v = pp7_requantize( block, qp );
+ v = (v + pp7_dither[y&7][x&7]) >> 6;
+ if( (unsigned)v > 255 )
+ {
+ v = (-v) >> 31;
+ }
+ dst[x + y*width] = v;
+ }
+ }
+ }
+}
+
+hb_filter_private_t * hb_deblock_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings )
+{
+ if( pix_fmt != PIX_FMT_YUV420P )
+ {
+ return 0;
+ }
+
+ hb_filter_private_t * pv = malloc( sizeof(struct hb_filter_private_s) );
+
+ pv->pix_fmt = pix_fmt;
+
+ pv->width[0] = width;
+ pv->height[0] = height;
+
+ pv->width[1] = pv->width[2] = width >> 1;
+ pv->height[1] = pv->height[2] = height >> 1;
+
+
+ pv->pp7_qp = PP7_QP_DEFAULT;
+ pv->pp7_mode = PP7_MODE_DEFAULT;
+ pv->pp7_mpeg2 = 1; /*mpi->qscale_type;*/
+
+ if( settings )
+ {
+ sscanf( settings, "%d:%d", &pv->pp7_qp, &pv->pp7_mode );
+ }
+
+ if( pv->pp7_qp < 0 )
+ {
+ pv->pp7_qp = 0;
+ }
+
+ pp7_init_threshold();
+
+ switch( pv->pp7_mode )
+ {
+ case 0:
+ pp7_requantize = pp7_hard_threshold;
+ break;
+ case 1:
+ pp7_requantize = pp7_soft_threshold;
+ break;
+ case 2:
+ pp7_requantize = pp7_medium_threshold;
+ break;
+ }
+
+ int h = (height+16+15)&(~15);
+
+ pv->pp7_temp_stride = (width+16+15)&(~15);
+
+ pv->pp7_src = (uint8_t*)malloc( pv->pp7_temp_stride*(h+8)*sizeof(uint8_t) );
+
+ int buf_size = 3 * width * height / 2;
+ pv->buf_out = hb_buffer_init( buf_size );
+
+ return pv;
+}
+
+void hb_deblock_close( hb_filter_private_t * pv )
+{
+ if( !pv )
+ {
+ return;
+ }
+
+ if( pv->buf_out )
+ {
+ hb_buffer_close( &pv->buf_out );
+ }
+
+ free( pv );
+}
+
+int hb_deblock_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv )
+{
+ if( !pv ||
+ pix_fmt != pv->pix_fmt ||
+ width != pv->width[0] ||
+ height != pv->height[0] )
+ {
+ return FILTER_FAILED;
+ }
+
+ avpicture_fill( &pv->pic_in, buf_in->data,
+ pix_fmt, width, height );
+
+ avpicture_fill( &pv->pic_out, pv->buf_out->data,
+ pix_fmt, width, height );
+
+ if( /*TODO: mpi->qscale ||*/ pv->pp7_qp )
+ {
+ pp7_filter( pv,
+ pv->pic_out.data[0],
+ pv->pic_in.data[0],
+ pv->width[0],
+ pv->height[0],
+ NULL, /* TODO: mpi->qscale*/
+ 0, /* TODO: mpi->qstride*/
+ 1 );
+
+ pp7_filter( pv,
+ pv->pic_out.data[1],
+ pv->pic_in.data[1],
+ pv->width[1],
+ pv->height[1],
+ NULL, /* TODO: mpi->qscale*/
+ 0, /* TODO: mpi->qstride*/
+ 0 );
+
+ pp7_filter( pv,
+ pv->pic_out.data[2],
+ pv->pic_in.data[2],
+ pv->width[2],
+ pv->height[2],
+ NULL, /* TODO: mpi->qscale*/
+ 0, /* TODO: mpi->qstride*/
+ 0 );
+ }
+ else
+ {
+ memcpy( pv->buf_out->data, buf_in->data, buf_in->size );
+ }
+
+ hb_buffer_copy_settings( pv->buf_out, buf_in );
+
+ *buf_out = pv->buf_out;
+
+ return FILTER_OK;
+}
+
+
diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c
index 73252d927..9e76be1bf 100644
--- a/libhb/decmpeg2.c
+++ b/libhb/decmpeg2.c
@@ -180,6 +180,9 @@ int hb_libmpeg2_decode( hb_libmpeg2_t * m, hb_buffer_t * buf_es,
}
m->last_pts = buf->start;
+ /* Store picture flags for later use by filters */
+ buf->flags = m->info->display_picture->flags;
+
hb_list_add( list_raw, buf );
}
}
diff --git a/libhb/deinterlace.c b/libhb/deinterlace.c
new file mode 100644
index 000000000..754ed284d
--- /dev/null
+++ b/libhb/deinterlace.c
@@ -0,0 +1,595 @@
+/*
+ Copyright (C) 2006 Michael Niedermayer <[email protected]>
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "hb.h"
+#include "ffmpeg/avcodec.h"
+#include "mpeg2dec/mpeg2.h"
+
+#define SUPPRESS_AV_LOG
+
+#define YADIF_MODE_DEFAULT -1
+#define YADIF_PARITY_DEFAULT -1
+
+#define MCDEINT_MODE_DEFAULT -1
+#define MCDEINT_QP_DEFAULT 1
+
+#define ABS(a) ((a) > 0 ? (a) : (-(a)))
+#define MIN3(a,b,c) MIN(MIN(a,b),c)
+#define MAX3(a,b,c) MAX(MAX(a,b),c)
+
+struct hb_filter_private_s
+{
+ int pix_fmt;
+ int width[3];
+ int height[3];
+
+ int yadif_mode;
+ int yadif_parity;
+ int yadif_ready;
+
+ uint8_t * yadif_ref[4][3];
+ int yadif_ref_stride[3];
+
+ int mcdeint_mode;
+ int mcdeint_qp;
+
+ int mcdeint_outbuf_size;
+ uint8_t * mcdeint_outbuf;
+ AVCodecContext * mcdeint_avctx_enc;
+ AVFrame * mcdeint_frame;
+ AVFrame * mcdeint_frame_dec;
+
+ AVPicture pic_in;
+ AVPicture pic_out;
+ hb_buffer_t * buf_out[2];
+ hb_buffer_t * buf_settings;
+};
+
+hb_filter_private_t * hb_deinterlace_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings );
+
+int hb_deinterlace_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv );
+
+void hb_deinterlace_close( hb_filter_private_t * pv );
+
+hb_filter_object_t hb_filter_deinterlace =
+{
+ FILTER_DEINTERLACE,
+ "Deinterlace (yadif/mcdeint)",
+ NULL,
+ hb_deinterlace_init,
+ hb_deinterlace_work,
+ hb_deinterlace_close,
+};
+
+static void yadif_store_ref( const uint8_t ** pic,
+ hb_filter_private_t * pv )
+{
+ memcpy( pv->yadif_ref[3],
+ pv->yadif_ref[0],
+ sizeof(uint8_t *)*3 );
+
+ memmove( pv->yadif_ref[0],
+ pv->yadif_ref[1],
+ sizeof(uint8_t *)*3*3 );
+
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ const uint8_t * src = pic[i];
+ uint8_t * ref = pv->yadif_ref[2][i];
+
+ int w = pv->width[i];
+ int h = pv->height[i];
+ int ref_stride = pv->yadif_ref_stride[i];
+
+ int y;
+ for( y = 0; y < pv->height[i]; y++ )
+ {
+ memcpy(ref, src, w);
+ src = (uint8_t*)src + w;
+ ref = (uint8_t*)ref + ref_stride;
+ }
+ }
+}
+
+static void yadif_filter_line( uint8_t *dst,
+ uint8_t *prev,
+ uint8_t *cur,
+ uint8_t *next,
+ int plane,
+ int parity,
+ hb_filter_private_t * pv )
+{
+ uint8_t *prev2 = parity ? prev : cur ;
+ uint8_t *next2 = parity ? cur : next;
+
+ int w = pv->width[plane];
+ int refs = pv->yadif_ref_stride[plane];
+
+ int x;
+ for( x = 0; x < w; x++)
+ {
+ int c = cur[-refs];
+ int d = (prev2[0] + next2[0])>>1;
+ int e = cur[+refs];
+ int temporal_diff0 = ABS(prev2[0] - next2[0]);
+ int temporal_diff1 = ( ABS(prev[-refs] - c) + ABS(prev[+refs] - e) ) >> 1;
+ int temporal_diff2 = ( ABS(next[-refs] - c) + ABS(next[+refs] - e) ) >> 1;
+ int diff = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2);
+ int spatial_pred = (c+e)>>1;
+ int spatial_score = ABS(cur[-refs-1] - cur[+refs-1]) + ABS(c-e) +
+ ABS(cur[-refs+1] - cur[+refs+1]) - 1;
+
+#define YADIF_CHECK(j)\
+ { int score = ABS(cur[-refs-1+j] - cur[+refs-1-j])\
+ + ABS(cur[-refs +j] - cur[+refs -j])\
+ + ABS(cur[-refs+1+j] - cur[+refs+1-j]);\
+ if( score < spatial_score ){\
+ spatial_score = score;\
+ spatial_pred = (cur[-refs +j] + cur[+refs -j])>>1;\
+
+ YADIF_CHECK(-1) YADIF_CHECK(-2) }} }}
+ YADIF_CHECK( 1) YADIF_CHECK( 2) }} }}
+
+ if( pv->yadif_mode < 2 )
+ {
+ int b = (prev2[-2*refs] + next2[-2*refs])>>1;
+ int f = (prev2[+2*refs] + next2[+2*refs])>>1;
+
+ int max = MAX3(d-e, d-c, MIN(b-c, f-e));
+ int min = MIN3(d-e, d-c, MAX(b-c, f-e));
+
+ diff = MAX3( diff, min, -max );
+ }
+
+ if( spatial_pred > d + diff )
+ {
+ spatial_pred = d + diff;
+ }
+ else if( spatial_pred < d - diff )
+ {
+ spatial_pred = d - diff;
+ }
+
+ dst[0] = spatial_pred;
+
+ dst++;
+ cur++;
+ prev++;
+ next++;
+ prev2++;
+ next2++;
+ }
+}
+
+static void yadif_filter( uint8_t ** dst,
+ int parity,
+ int tff,
+ hb_filter_private_t * pv )
+{
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ int w = pv->width[i];
+ int h = pv->height[i];
+ int ref_stride = pv->yadif_ref_stride[i];
+
+ int y;
+ for( y = 0; y < h; y++ )
+ {
+ if( (y ^ parity) & 1 )
+ {
+ uint8_t *prev = &pv->yadif_ref[0][i][y*ref_stride];
+ uint8_t *cur = &pv->yadif_ref[1][i][y*ref_stride];
+ uint8_t *next = &pv->yadif_ref[2][i][y*ref_stride];
+ uint8_t *dst2 = &dst[i][y*w];
+
+ yadif_filter_line( dst2, prev, cur, next, i, parity ^ tff, pv );
+ }
+ else
+ {
+ memcpy( &dst[i][y*w],
+ &pv->yadif_ref[1][i][y*ref_stride],
+ w * sizeof(uint8_t) );
+ }
+ }
+ }
+}
+
+static void mcdeint_filter( uint8_t ** dst,
+ uint8_t ** src,
+ int parity,
+ hb_filter_private_t * pv )
+{
+ int x, y, i;
+ int out_size;
+
+#ifdef SUPPRESS_AV_LOG
+ /* TODO: temporarily change log level to suppress obnoxious debug output */
+ int loglevel = av_log_get_level();
+ av_log_set_level( AV_LOG_QUIET );
+#endif
+
+ for( i=0; i<3; i++ )
+ {
+ pv->mcdeint_frame->data[i] = src[i];
+ pv->mcdeint_frame->linesize[i] = pv->width[i];
+ }
+ pv->mcdeint_avctx_enc->me_cmp = FF_CMP_SAD;
+ pv->mcdeint_avctx_enc->me_sub_cmp = FF_CMP_SAD;
+ pv->mcdeint_frame->quality = pv->mcdeint_qp * FF_QP2LAMBDA;
+
+ out_size = avcodec_encode_video( pv->mcdeint_avctx_enc,
+ pv->mcdeint_outbuf,
+ pv->mcdeint_outbuf_size,
+ pv->mcdeint_frame );
+
+ pv->mcdeint_frame_dec = pv->mcdeint_avctx_enc->coded_frame;
+
+ for( i = 0; i < 3; i++ )
+ {
+ int w = pv->width[i];
+ int h = pv->height[i];
+ int fils = pv->mcdeint_frame_dec->linesize[i];
+ int srcs = pv->width[i];
+
+ for( y = 0; y < h; y++ )
+ {
+ if( (y ^ parity) & 1 )
+ {
+ for( x = 0; x < w; x++ )
+ {
+ if( (x-2)+(y-1)*w >= 0 && (x+2)+(y+1)*w < w*h )
+ {
+ uint8_t * filp =
+ &pv->mcdeint_frame_dec->data[i][x + y*fils];
+ uint8_t * srcp = &src[i][x + y*srcs];
+
+ int diff0 = filp[-fils] - srcp[-srcs];
+ int diff1 = filp[+fils] - srcp[+srcs];
+
+ int spatial_score =
+ ABS(srcp[-srcs-1] - srcp[+srcs-1])
+ + ABS(srcp[-srcs ] - srcp[+srcs ])
+ + ABS(srcp[-srcs+1] - srcp[+srcs+1]) - 1;
+
+ int temp = filp[0];
+
+#define MCDEINT_CHECK(j)\
+ { int score = ABS(srcp[-srcs-1+j] - srcp[+srcs-1-j])\
+ + ABS(srcp[-srcs +j] - srcp[+srcs -j])\
+ + ABS(srcp[-srcs+1+j] - srcp[+srcs+1-j]);\
+ if( score < spatial_score ) {\
+ spatial_score = score;\
+ diff0 = filp[-fils+j] - srcp[-srcs+j];\
+ diff1 = filp[+fils-j] - srcp[+srcs-j];
+
+ MCDEINT_CHECK(-1) MCDEINT_CHECK(-2) }} }}
+ MCDEINT_CHECK( 1) MCDEINT_CHECK( 2) }} }}
+
+ if(diff0 + diff1 > 0)
+ {
+ temp -= (diff0 + diff1 -
+ ABS( ABS(diff0) - ABS(diff1) ) / 2) / 2;
+ }
+ else
+ {
+ temp -= (diff0 + diff1 +
+ ABS( ABS(diff0) - ABS(diff1) ) / 2) / 2;
+ }
+
+ filp[0] = dst[i][x + y*w] =
+ temp > 255U ? ~(temp>>31) : temp;
+ }
+ else
+ {
+ dst[i][x + y*w] =
+ pv->mcdeint_frame_dec->data[i][x + y*fils];
+ }
+ }
+ }
+ }
+
+ for( y = 0; y < h; y++ )
+ {
+ if( !((y ^ parity) & 1) )
+ {
+ for( x = 0; x < w; x++ )
+ {
+ pv->mcdeint_frame_dec->data[i][x + y*fils] =
+ dst[i][x + y*w]= src[i][x + y*srcs];
+ }
+ }
+ }
+ }
+
+#ifdef SUPPRESS_AV_LOG
+ /* TODO: restore previous log level */
+ av_log_set_level(loglevel);
+#endif
+}
+
+hb_filter_private_t * hb_deinterlace_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings )
+{
+ if( pix_fmt != PIX_FMT_YUV420P )
+ {
+ return 0;
+ }
+
+ hb_filter_private_t * pv = calloc( 1, sizeof(struct hb_filter_private_s) );
+
+ pv->pix_fmt = pix_fmt;
+
+ pv->width[0] = width;
+ pv->height[0] = height;
+ pv->width[1] = pv->width[2] = width >> 1;
+ pv->height[1] = pv->height[2] = height >> 1;
+
+ int buf_size = 3 * width * height / 2;
+ pv->buf_out[0] = hb_buffer_init( buf_size );
+ pv->buf_out[1] = hb_buffer_init( buf_size );
+ pv->buf_settings = hb_buffer_init( 0 );
+
+ pv->yadif_ready = 0;
+ pv->yadif_mode = YADIF_MODE_DEFAULT;
+ pv->yadif_parity = YADIF_PARITY_DEFAULT;
+
+ pv->mcdeint_mode = MCDEINT_MODE_DEFAULT;
+ pv->mcdeint_qp = MCDEINT_QP_DEFAULT;
+
+ if( settings )
+ {
+ sscanf( settings, "%d:%d:%d:%d",
+ &pv->yadif_mode,
+ &pv->yadif_parity,
+ &pv->mcdeint_mode,
+ &pv->mcdeint_qp );
+ }
+
+ /* Allocate yadif specific buffers */
+ if( pv->yadif_mode >= 0 )
+ {
+ int i, j;
+ for( i = 0; i < 3; i++ )
+ {
+ int is_chroma = !!i;
+ int w = ((width + 31) & (~31))>>is_chroma;
+ int h = ((height+6+ 31) & (~31))>>is_chroma;
+
+ pv->yadif_ref_stride[i] = w;
+
+ for( j = 0; j < 3; j++ )
+ {
+ pv->yadif_ref[j][i] = malloc( w*h*sizeof(uint8_t) ) + 3*w;
+ }
+ }
+ }
+
+ /* Allocate mcdeint specific buffers */
+ if( pv->mcdeint_mode >= 0 )
+ {
+ avcodec_init();
+ avcodec_register_all();
+
+ AVCodec * enc = avcodec_find_encoder( CODEC_ID_SNOW );
+
+ int i;
+ for (i = 0; i < 3; i++ )
+ {
+ AVCodecContext * avctx_enc;
+
+ avctx_enc = pv->mcdeint_avctx_enc = avcodec_alloc_context();
+
+ avctx_enc->width = width;
+ avctx_enc->height = height;
+ avctx_enc->time_base = (AVRational){1,25}; // meaningless
+ avctx_enc->gop_size = 300;
+ avctx_enc->max_b_frames = 0;
+ avctx_enc->pix_fmt = PIX_FMT_YUV420P;
+ avctx_enc->flags = CODEC_FLAG_QSCALE | CODEC_FLAG_LOW_DELAY;
+ avctx_enc->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
+ avctx_enc->global_quality = 1;
+ avctx_enc->flags2 = CODEC_FLAG2_MEMC_ONLY;
+ avctx_enc->me_cmp = FF_CMP_SAD; //SSE;
+ avctx_enc->me_sub_cmp = FF_CMP_SAD; //SSE;
+ avctx_enc->mb_cmp = FF_CMP_SSE;
+
+ switch( pv->mcdeint_mode )
+ {
+ case 3:
+ avctx_enc->refs = 3;
+ case 2:
+ avctx_enc->me_method = ME_ITER;
+ case 1:
+ avctx_enc->flags |= CODEC_FLAG_4MV;
+ avctx_enc->dia_size =2;
+ case 0:
+ avctx_enc->flags |= CODEC_FLAG_QPEL;
+ }
+
+ avcodec_open(avctx_enc, enc);
+ }
+
+ pv->mcdeint_frame = avcodec_alloc_frame();
+ pv->mcdeint_outbuf_size = width * height * 10;
+ pv->mcdeint_outbuf = malloc( pv->mcdeint_outbuf_size );
+ }
+
+ return pv;
+}
+
+void hb_deinterlace_close( hb_filter_private_t * pv )
+{
+ if( !pv )
+ {
+ return;
+ }
+
+ /* Cleanup frame buffers */
+ if( pv->buf_out[0] )
+ {
+ hb_buffer_close( &pv->buf_out[0] );
+ }
+ if( pv->buf_out[1] )
+ {
+ hb_buffer_close( &pv->buf_out[1] );
+ }
+ if (pv->buf_settings )
+ {
+ hb_buffer_close( &pv->buf_settings );
+ }
+
+ /* Cleanup yadif specific buffers */
+ if( pv->yadif_mode >= 0 )
+ {
+ int i;
+ for( i = 0; i<3*3; i++ )
+ {
+ uint8_t **p = &pv->yadif_ref[i%3][i/3];
+ if (*p)
+ {
+ free( *p - 3*pv->yadif_ref_stride[i/3] );
+ *p = NULL;
+ }
+ }
+ }
+
+ /* Cleanup mcdeint specific buffers */
+ if( pv->mcdeint_mode >= 0 )
+ {
+ if( pv->mcdeint_avctx_enc )
+ {
+ avcodec_close( pv->mcdeint_avctx_enc );
+ av_freep( &pv->mcdeint_avctx_enc );
+ }
+ if( pv->mcdeint_outbuf )
+ {
+ free( pv->mcdeint_outbuf );
+ }
+ }
+
+ free( pv );
+}
+
+int hb_deinterlace_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv )
+{
+ if( !pv ||
+ pix_fmt != pv->pix_fmt ||
+ width != pv->width[0] ||
+ height != pv->height[0] )
+ {
+ return FILTER_FAILED;
+ }
+
+ avpicture_fill( &pv->pic_in, buf_in->data,
+ pix_fmt, width, height );
+
+ /* Use libavcodec deinterlace if yadif_mode < 0 */
+ if( pv->yadif_mode < 0 )
+ {
+ avpicture_fill( &pv->pic_out, pv->buf_out[0]->data,
+ pix_fmt, width, height );
+
+ avpicture_deinterlace( &pv->pic_out, &pv->pic_in,
+ pix_fmt, width, height );
+
+ hb_buffer_copy_settings( pv->buf_out[0], buf_in );
+
+ *buf_out = pv->buf_out[0];
+
+ return FILTER_OK;
+ }
+
+ /* Determine if top-field first layout */
+ int tff;
+ if( pv->yadif_parity < 0 )
+ {
+ tff = !!(buf_in->flags & PIC_FLAG_TOP_FIELD_FIRST);
+ }
+ else
+ {
+ tff = (pv->yadif_parity & 1) ^ 1;
+ }
+
+ /* Store current frame in yadif cache */
+ yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
+
+ /* If yadif is not ready, store another ref and return FILTER_DELAY */
+ if( pv->yadif_ready == 0 )
+ {
+ yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );
+
+ hb_buffer_copy_settings( pv->buf_settings, buf_in );
+
+ pv->yadif_ready = 1;
+
+ return FILTER_DELAY;
+ }
+
+ /* Perform yadif and mcdeint filtering */
+ int frame;
+ for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )
+ {
+ int parity = frame ^ tff ^ 1;
+
+ avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data,
+ pix_fmt, width, height );
+
+ yadif_filter( pv->pic_out.data, parity, tff, pv );
+
+ if( pv->mcdeint_mode >= 0 )
+ {
+ avpicture_fill( &pv->pic_in, pv->buf_out[(frame^1)]->data,
+ pix_fmt, width, height );
+
+ mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );
+
+ *buf_out = pv->buf_out[ (frame^1)];
+ }
+ else
+ {
+ *buf_out = pv->buf_out[!(frame^1)];
+ }
+ }
+
+ /* Copy buffered settings to output buffer settings */
+ hb_buffer_copy_settings( *buf_out, pv->buf_settings );
+
+ /* Replace buffered settings with input buffer settings */
+ hb_buffer_copy_settings( pv->buf_settings, buf_in );
+
+ return FILTER_OK;
+}
+
+
diff --git a/libhb/denoise.c b/libhb/denoise.c
new file mode 100644
index 000000000..5022d4721
--- /dev/null
+++ b/libhb/denoise.c
@@ -0,0 +1,463 @@
+/*
+ Copyright (C) 2003 Daniel Moreno <[email protected]>
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "hb.h"
+#include "ffmpeg/avcodec.h"
+#include "mpeg2dec/mpeg2.h"
+
+#define HQDN3D_SPATIAL_LUMA_DEFAULT 4.0f
+#define HQDN3D_SPATIAL_CHROMA_DEFAULT 3.0f
+#define HQDN3D_TEMPORAL_LUMA_DEFAULT 6.0f
+
+#define ABS(A) ( (A) > 0 ? (A) : -(A) )
+
+struct hb_filter_private_s
+{
+ int pix_fmt;
+ int width[3];
+ int height[3];
+
+ int hqdn3d_coef[4][512*16];
+ unsigned int * hqdn3d_line;
+ unsigned short * hqdn3d_frame[3];
+
+ AVPicture pic_in;
+ AVPicture pic_out;
+ hb_buffer_t * buf_out;
+};
+
+hb_filter_private_t * hb_denoise_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings );
+
+int hb_denoise_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv );
+
+void hb_denoise_close( hb_filter_private_t * pv );
+
+hb_filter_object_t hb_filter_denoise =
+{
+ FILTER_DENOISE,
+ "Denoise (hqdn3d)",
+ NULL,
+ hb_denoise_init,
+ hb_denoise_work,
+ hb_denoise_close,
+};
+
+static void hqdn3d_precalc_coef( int * ct,
+ double dist25 )
+{
+ int i;
+ double gamma, simil, c;
+
+ gamma = log( 0.25 ) / log( 1.0 - dist25/255.0 - 0.00001 );
+
+ for( i = -255*16; i <= 255*16; i++ )
+ {
+ simil = 1.0 - ABS(i) / (16*255.0);
+ c = pow( simil, gamma ) * 65536.0 * (double)i / 16.0;
+ ct[16*256+i] = (c<0) ? (c-0.5) : (c+0.5);
+ }
+
+ ct[0] = (dist25 != 0);
+}
+
+static inline unsigned int hqdn3d_lowpass_mul( unsigned int prev_mul,
+ unsigned int curr_mul,
+ int * coef )
+{
+ int diff_mul = prev_mul - curr_mul;
+ int d = ((diff_mul+0x10007FF)>>12);
+ return curr_mul + coef[d];
+}
+
+static void hqdn3d_denoise_temporal( unsigned char * frame_src,
+ unsigned char * frame_dst,
+ unsigned short * frame_ant,
+ int w, int h,
+ int * temporal)
+{
+ int x, y;
+ unsigned int pixel_dst;
+
+ for( y = 0; y < h; y++ )
+ {
+ for( x = 0; x < w; x++ )
+ {
+ pixel_dst = hqdn3d_lowpass_mul( frame_ant[x]<<8,
+ frame_src[x]<<16,
+ temporal );
+
+ frame_ant[x] = ((pixel_dst+0x1000007F)>>8);
+ frame_dst[x] = ((pixel_dst+0x10007FFF)>>16);
+ }
+
+ frame_src += w;
+ frame_dst += w;
+ frame_ant += w;
+ }
+}
+
+static void hqdn3d_denoise_spatial( unsigned char * frame_src,
+ unsigned char * frame_dst,
+ unsigned int * line_ant,
+ int w, int h,
+ int * horizontal,
+ int * vertical )
+{
+ int x, y;
+ int line_offset_src = 0, line_offset_dst = 0;
+ unsigned int pixel_ant;
+ unsigned int pixel_dst;
+
+ /* First pixel has no left nor top neighbor. */
+ pixel_dst = line_ant[0] = pixel_ant = frame_src[0]<<16;
+ frame_dst[0] = ((pixel_dst+0x10007FFF)>>16);
+
+ /* First line has no top neighbor, only left. */
+ for( x = 1; x < w; x++ )
+ {
+ pixel_dst = line_ant[x] = hqdn3d_lowpass_mul(pixel_ant,
+ frame_src[x]<<16,
+ horizontal);
+
+ frame_dst[x] = ((pixel_dst+0x10007FFF)>>16);
+ }
+
+ for( y = 1; y < h; y++ )
+ {
+ unsigned int pixel_ant;
+ line_offset_src += w, line_offset_dst += w;
+
+ /* First pixel on each line doesn't have previous pixel */
+ pixel_ant = frame_src[line_offset_src]<<16;
+
+ pixel_dst = line_ant[0] = hqdn3d_lowpass_mul( line_ant[0],
+ pixel_ant,
+ vertical);
+
+ frame_dst[line_offset_dst] = ((pixel_dst+0x10007FFF)>>16);
+
+ /* The rest of the pixels in the line are normal */
+ for( x = 1; x < w; x++ )
+ {
+ unsigned int pixel_dst;
+
+ pixel_ant = hqdn3d_lowpass_mul( pixel_ant,
+ frame_src[line_offset_src+x]<<16,
+ horizontal );
+ pixel_dst = line_ant[x] = hqdn3d_lowpass_mul( line_ant[x],
+ pixel_ant,
+ vertical );
+
+ frame_dst[line_offset_dst+x]= ((pixel_dst+0x10007FFF)>>16);
+ }
+ }
+}
+
+static void hqdn3d_denoise( unsigned char * frame_src,
+ unsigned char * frame_dst,
+ unsigned int * line_ant,
+ unsigned short ** frame_ant_ptr,
+ int w,
+ int h,
+ int * horizontal,
+ int * vertical,
+ int * temporal)
+{
+ int x, y;
+ int line_offset_src = 0, line_offset_dst = 0;
+ unsigned int pixel_ant;
+ unsigned int pixel_dst;
+ unsigned short* frame_ant = (*frame_ant_ptr);
+
+ if( !frame_ant)
+ {
+ (*frame_ant_ptr) = frame_ant = malloc( w*h*sizeof(unsigned short) );
+ for( y = 0; y < h; y++ )
+ {
+ unsigned short* dst = &frame_ant[y*w];
+ unsigned char* src = frame_src + y*w;
+
+ for( x = 0; x < w; x++ )
+ {
+ dst[x] = src[x] << 8;
+ }
+ }
+ }
+
+ /* If no spatial coefficients, do temporal denoise only */
+ if( !horizontal[0] && !vertical[0] )
+ {
+ hqdn3d_denoise_temporal( frame_src,
+ frame_dst,
+ frame_ant,
+ w, h,
+ temporal);
+ return;
+ }
+
+ /* If no temporal coefficients, do spatial denoise only */
+ if( !temporal[0] )
+ {
+ hqdn3d_denoise_spatial( frame_src,
+ frame_dst,
+ line_ant,
+ w, h,
+ horizontal,
+ vertical);
+ return;
+ }
+
+ /* First pixel has no left nor top neighbor. Only previous frame */
+ line_ant[0] = pixel_ant = frame_src[0] << 16;
+
+ pixel_dst = hqdn3d_lowpass_mul( frame_ant[0]<<8,
+ pixel_ant,
+ temporal );
+
+ frame_ant[0] = ((pixel_dst+0x1000007F)>>8);
+ frame_dst[0] = ((pixel_dst+0x10007FFF)>>16);
+
+ /* First line has no top neighbor. Only left one for each pixel and last frame */
+ for( x = 1; x < w; x++ )
+ {
+ line_ant[x] = pixel_ant = hqdn3d_lowpass_mul( pixel_ant,
+ frame_src[x]<<16,
+ horizontal);
+
+ pixel_dst = hqdn3d_lowpass_mul( frame_ant[x]<<8,
+ pixel_ant,
+ temporal);
+
+ frame_ant[x] = ((pixel_dst+0x1000007F)>>8);
+ frame_dst[x] = ((pixel_dst+0x10007FFF)>>16);
+ }
+
+ /* The rest of the lines in the frame are normal */
+ for( y = 1; y < h; y++ )
+ {
+ unsigned int pixel_ant;
+ unsigned short * line_prev = &frame_ant[y*w];
+ line_offset_src += w, line_offset_dst += w;
+
+ /* First pixel on each line doesn't have previous pixel */
+ pixel_ant = frame_src[line_offset_src]<<16;
+ line_ant[0] = hqdn3d_lowpass_mul( line_ant[0],
+ pixel_ant,
+ vertical);
+ pixel_dst = hqdn3d_lowpass_mul( line_prev[0]<<8,
+ line_ant[0],
+ temporal);
+ line_prev[0] = ((pixel_dst+0x1000007F)>>8);
+
+ frame_dst[line_offset_dst] = ((pixel_dst+0x10007FFF)>>16);
+
+ /* The rest of the pixels in the line are normal */
+ for( x = 1; x < w; x++ )
+ {
+ unsigned int pixel_dst;
+ pixel_ant = hqdn3d_lowpass_mul( pixel_ant,
+ frame_src[line_offset_src+x]<<16,
+ horizontal );
+ line_ant[x] = hqdn3d_lowpass_mul( line_ant[x],
+ pixel_ant, vertical);
+ pixel_dst = hqdn3d_lowpass_mul( line_prev[x]<<8,
+ line_ant[x],
+ temporal );
+ line_prev[x] = ((pixel_dst+0x1000007F)>>8);
+
+ frame_dst[line_offset_dst+x] = ((pixel_dst+0x10007FFF)>>16);
+ }
+ }
+}
+
+hb_filter_private_t * hb_denoise_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings )
+{
+ if( pix_fmt != PIX_FMT_YUV420P )
+ {
+ return 0;
+ }
+
+ hb_filter_private_t * pv = malloc( sizeof(struct hb_filter_private_s) );
+
+ pv->pix_fmt = pix_fmt;
+ pv->width[0] = width;
+ pv->height[0] = height;
+ pv->width[1] = pv->width[2] = width >> 1;
+ pv->height[1] = pv->height[2] = height >> 1;
+
+ double spatial_luma, temporal_luma, spatial_chroma, temporal_chroma;
+
+ if( settings )
+ {
+ switch( sscanf( settings, "%lf:%lf:%lf:%lf",
+ &spatial_luma, &spatial_chroma,
+ &temporal_luma, &temporal_chroma ) )
+ {
+ case 0:
+ spatial_luma = HQDN3D_SPATIAL_LUMA_DEFAULT;
+
+ spatial_chroma = HQDN3D_SPATIAL_CHROMA_DEFAULT;
+
+ temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT;
+
+ temporal_chroma = temporal_luma *
+ spatial_chroma / spatial_luma;
+ break;
+
+ case 1:
+ spatial_chroma = HQDN3D_SPATIAL_CHROMA_DEFAULT *
+ spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT;
+
+ temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT *
+ spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT;
+
+ temporal_chroma = temporal_luma *
+ spatial_chroma / spatial_luma;
+ break;
+
+ case 2:
+ temporal_luma = HQDN3D_TEMPORAL_LUMA_DEFAULT *
+ spatial_luma / HQDN3D_SPATIAL_LUMA_DEFAULT;
+
+ temporal_chroma = temporal_luma *
+ spatial_chroma / spatial_luma;
+ break;
+
+ case 3:
+ temporal_chroma = temporal_luma *
+ spatial_chroma / spatial_luma;
+ break;
+ }
+ }
+
+ pv->hqdn3d_line = malloc( width * sizeof(int) );
+
+ hqdn3d_precalc_coef( pv->hqdn3d_coef[0], spatial_luma );
+ hqdn3d_precalc_coef( pv->hqdn3d_coef[1], temporal_luma );
+ hqdn3d_precalc_coef( pv->hqdn3d_coef[2], spatial_chroma );
+ hqdn3d_precalc_coef( pv->hqdn3d_coef[3], temporal_chroma );
+
+ int buf_size = 3 * width * height / 2;
+ pv->buf_out = hb_buffer_init( buf_size );
+
+ return pv;
+}
+
+void hb_denoise_close( hb_filter_private_t * pv )
+{
+ if( !pv )
+ {
+ return;
+ }
+
+ if( pv->hqdn3d_line )
+ {
+ free( pv->hqdn3d_line );
+ pv->hqdn3d_line = NULL;
+ }
+ if( pv->hqdn3d_frame[0] )
+ {
+ free( pv->hqdn3d_frame[0] );
+ pv->hqdn3d_frame[0] = NULL;
+ }
+ if( pv->hqdn3d_frame[1] )
+ {
+ free( pv->hqdn3d_frame[1] );
+ pv->hqdn3d_frame[1] = NULL;
+ }
+ if( pv->hqdn3d_frame[2] )
+ {
+ free( pv->hqdn3d_frame[2] );
+ pv->hqdn3d_frame[2] = NULL;
+ }
+ if( pv->buf_out )
+ {
+ hb_buffer_close( &pv->buf_out );
+ }
+
+ free( pv );
+}
+
+int hb_denoise_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv )
+{
+ if( !pv ||
+ pix_fmt != pv->pix_fmt ||
+ width != pv->width[0] ||
+ height != pv->height[0] )
+ {
+ return FILTER_FAILED;
+ }
+
+ avpicture_fill( &pv->pic_in, buf_in->data,
+ pix_fmt, width, height );
+
+ avpicture_fill( &pv->pic_out, pv->buf_out->data,
+ pix_fmt, width, height );
+
+ hqdn3d_denoise( pv->pic_in.data[0],
+ pv->pic_out.data[0],
+ pv->hqdn3d_line,
+ &pv->hqdn3d_frame[0],
+ pv->width[0],
+ pv->height[0],
+ pv->hqdn3d_coef[0],
+ pv->hqdn3d_coef[0],
+ pv->hqdn3d_coef[1] );
+
+ hqdn3d_denoise( pv->pic_in.data[1],
+ pv->pic_out.data[1],
+ pv->hqdn3d_line,
+ &pv->hqdn3d_frame[1],
+ pv->width[1],
+ pv->height[1],
+ pv->hqdn3d_coef[2],
+ pv->hqdn3d_coef[2],
+ pv->hqdn3d_coef[3] );
+
+ hqdn3d_denoise( pv->pic_in.data[2],
+ pv->pic_out.data[2],
+ pv->hqdn3d_line,
+ &pv->hqdn3d_frame[2],
+ pv->width[2],
+ pv->height[2],
+ pv->hqdn3d_coef[2],
+ pv->hqdn3d_coef[2],
+ pv->hqdn3d_coef[3] );
+
+ hb_buffer_copy_settings( pv->buf_out, buf_in );
+
+ *buf_out = pv->buf_out;
+
+ return FILTER_OK;
+}
diff --git a/libhb/detelecine.c b/libhb/detelecine.c
new file mode 100644
index 000000000..93b153c8d
--- /dev/null
+++ b/libhb/detelecine.c
@@ -0,0 +1,1039 @@
+#include "hb.h"
+#include "ffmpeg/avcodec.h"
+#include "mpeg2dec/mpeg2.h"
+
+/*
+ *
+ * PULLUP DEFINITIONS
+ *
+ */
+
+#define PULLUP_FMT_Y 1
+#define PULLUP_HAVE_BREAKS 1
+#define PULLUP_HAVE_AFFINITY 2
+#define PULLUP_BREAK_LEFT 1
+#define PULLUP_BREAK_RIGHT 2
+
+#define PULLUP_ABS( a ) (((a)^((a)>>31))-((a)>>31))
+
+#ifndef PIC_FLAG_REPEAT_FIRST_FIELD
+#define PIC_FLAG_REPEAT_FIRST_FIELD 256
+#endif
+
+struct pullup_buffer
+{
+ int lock[2];
+ unsigned char **planes;
+};
+
+struct pullup_field
+{
+ int parity;
+ struct pullup_buffer *buffer;
+ unsigned int flags;
+ int breaks;
+ int affinity;
+ int *diffs;
+ int *comb;
+ int *var;
+ struct pullup_field *prev, *next;
+};
+
+struct pullup_frame
+{
+ int lock;
+ int length;
+ int parity;
+ struct pullup_buffer **ifields, *ofields[2];
+ struct pullup_buffer *buffer;
+};
+
+struct pullup_context
+{
+ /* Public interface */
+ int format;
+ int nplanes;
+ int *bpp, *w, *h, *stride, *background;
+ unsigned int cpu;
+ int junk_left, junk_right, junk_top, junk_bottom;
+ int verbose;
+ int metric_plane;
+ int strict_breaks;
+ int strict_pairs;
+ /* Internal data */
+ struct pullup_field *first, *last, *head;
+ struct pullup_buffer *buffers;
+ int nbuffers;
+ int (*diff)(unsigned char *, unsigned char *, int);
+ int (*comb)(unsigned char *, unsigned char *, int);
+ int (*var)(unsigned char *, unsigned char *, int);
+ int metric_w, metric_h, metric_len, metric_offset;
+ struct pullup_frame *frame;
+};
+
+/*
+ *
+ * DETELECINE FILTER DEFINITIONS
+ *
+ */
+
+struct hb_filter_private_s
+{
+ int pix_fmt;
+ int width[3];
+ int height[3];
+
+ struct pullup_context * pullup_ctx;
+ int pullup_fakecount;
+ int pullup_skipflag;
+
+ AVPicture pic_in;
+ AVPicture pic_out;
+ hb_buffer_t * buf_out;
+};
+
+hb_filter_private_t * hb_detelecine_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings );
+
+int hb_detelecine_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv );
+
+void hb_detelecine_close( hb_filter_private_t * pv );
+
+hb_filter_object_t hb_filter_detelecine =
+{
+ FILTER_DETELECINE,
+ "Detelecine (pullup)",
+ NULL,
+ hb_detelecine_init,
+ hb_detelecine_work,
+ hb_detelecine_close,
+};
+
+/*
+ *
+ * PULLUP STATIC FUNCTIONS
+ *
+ */
+
+static int pullup_diff_y( unsigned char *a, unsigned char * b, int s )
+{
+ int i, j, diff = 0;
+ for( i = 4; i; i-- )
+ {
+ for( j = 0; j < 8; j++ )
+ {
+ diff += PULLUP_ABS( a[j]-b[j] );
+ }
+ a+=s; b+=s;
+ }
+ return diff;
+}
+
+static int pullup_licomb_y( unsigned char * a, unsigned char * b, int s )
+{
+ int i, j, diff = 0;
+ for( i = 4; i; i-- )
+ {
+ for( j = 0; j < 8; j++ )
+ {
+ diff += PULLUP_ABS( (a[j]<<1) - b[j-s] - b[j] )
+ + PULLUP_ABS( (b[j]<<1) - a[j] - a[j+s] );
+ }
+ a+=s; b+=s;
+ }
+ return diff;
+}
+
+static int pullup_var_y( unsigned char * a, unsigned char * b, int s )
+{
+ int i, j, var = 0;
+ for( i = 3; i; i-- )
+ {
+ for( j = 0; j < 8; j++ )
+ {
+ var += PULLUP_ABS( a[j]-a[j+s] );
+ }
+ a+=s; b+=s;
+ }
+ return 4*var;
+}
+
+static void pullup_alloc_metrics( struct pullup_context * c,
+ struct pullup_field * f )
+{
+ f->diffs = calloc( c->metric_len, sizeof(int) );
+ f->comb = calloc( c->metric_len, sizeof(int) );
+ f->var = calloc( c->metric_len, sizeof(int) );
+}
+
+static void pullup_compute_metric( struct pullup_context * c,
+ struct pullup_field * fa, int pa,
+ struct pullup_field * fb, int pb,
+ int (* func)( unsigned char *,
+ unsigned char *, int),
+ int * dest )
+{
+ unsigned char *a, *b;
+ int x, y;
+ int mp = c->metric_plane;
+ int xstep = c->bpp[mp];
+ int ystep = c->stride[mp]<<3;
+ int s = c->stride[mp]<<1; /* field stride */
+ int w = c->metric_w*xstep;
+
+ if( !fa->buffer || !fb->buffer ) return;
+
+ /* Shortcut for duplicate fields (e.g. from RFF flag) */
+ if( fa->buffer == fb->buffer && pa == pb )
+ {
+ memset( dest, 0, c->metric_len * sizeof(int) );
+ return;
+ }
+
+ a = fa->buffer->planes[mp] + pa * c->stride[mp] + c->metric_offset;
+ b = fb->buffer->planes[mp] + pb * c->stride[mp] + c->metric_offset;
+
+ for( y = c->metric_h; y; y-- )
+ {
+ for( x = 0; x < w; x += xstep )
+ {
+ *dest++ = func( a + x, b + x, s );
+ }
+ a += ystep; b += ystep;
+ }
+}
+
+static struct pullup_field * pullup_make_field_queue( struct pullup_context * c,
+ int len )
+{
+ struct pullup_field * head, * f;
+ f = head = calloc( 1, sizeof(struct pullup_field) );
+ pullup_alloc_metrics( c, f );
+ for ( ; len > 0; len-- )
+ {
+ f->next = calloc( 1, sizeof(struct pullup_field) );
+ f->next->prev = f;
+ f = f->next;
+ pullup_alloc_metrics( c, f );
+ }
+ f->next = head;
+ head->prev = f;
+ return head;
+}
+
+static void pullup_check_field_queue( struct pullup_context * c )
+{
+ if( c->head->next == c->first )
+ {
+ struct pullup_field *f = calloc( 1, sizeof(struct pullup_field) );
+ pullup_alloc_metrics( c, f );
+ f->prev = c->head;
+ f->next = c->first;
+ c->head->next = f;
+ c->first->prev = f;
+ }
+}
+
+static void pullup_copy_field( struct pullup_context * c,
+ struct pullup_buffer * dest,
+ struct pullup_buffer * src,
+ int parity )
+{
+ int i, j;
+ unsigned char *d, *s;
+ for( i = 0; i < c->nplanes; i++ )
+ {
+ s = src->planes[i] + parity*c->stride[i];
+ d = dest->planes[i] + parity*c->stride[i];
+ for( j = c->h[i]>>1; j; j-- )
+ {
+ memcpy( d, s, c->stride[i] );
+ s += c->stride[i]<<1;
+ d += c->stride[i]<<1;
+ }
+ }
+}
+
+
+static int pullup_queue_length( struct pullup_field * begin,
+ struct pullup_field * end )
+{
+ int count = 1;
+ struct pullup_field * f;
+
+ if( !begin || !end ) return 0;
+ for( f = begin; f != end; f = f->next ) count++;
+ return count;
+}
+
+static int pullup_find_first_break( struct pullup_field * f, int max )
+{
+ int i;
+ for( i = 0; i < max; i++ )
+ {
+ if( f->breaks & PULLUP_BREAK_RIGHT ||
+ f->next->breaks & PULLUP_BREAK_LEFT )
+ {
+ return i+1;
+ }
+ f = f->next;
+ }
+ return 0;
+}
+
+static void pullup_compute_breaks( struct pullup_context * c,
+ struct pullup_field * f0 )
+{
+ int i;
+ struct pullup_field *f1 = f0->next;
+ struct pullup_field *f2 = f1->next;
+ struct pullup_field *f3 = f2->next;
+ int l, max_l=0, max_r=0;
+
+ if( f0->flags & PULLUP_HAVE_BREAKS ) return;
+ f0->flags |= PULLUP_HAVE_BREAKS;
+
+ /* Special case when fields are 100% identical */
+ if( f0->buffer == f2->buffer && f1->buffer != f3->buffer )
+ {
+ f2->breaks |= PULLUP_BREAK_RIGHT;
+ return;
+ }
+ if( f0->buffer != f2->buffer && f1->buffer == f3->buffer )
+ {
+ f1->breaks |= PULLUP_BREAK_LEFT;
+ return;
+ }
+
+ for( i = 0; i < c->metric_len; i++ )
+ {
+ l = f2->diffs[i] - f3->diffs[i];
+ if( l > max_l) max_l = l;
+ if( -l > max_r) max_r = -l;
+ }
+
+ /* Don't get tripped up when differences are mostly quant error */
+ if( max_l + max_r < 128 ) return;
+ if( max_l > 4*max_r ) f1->breaks |= PULLUP_BREAK_LEFT;
+ if( max_r > 4*max_l ) f2->breaks |= PULLUP_BREAK_RIGHT;
+}
+
+static void pullup_compute_affinity( struct pullup_context * c,
+ struct pullup_field * f )
+{
+ int i;
+ int max_l = 0, max_r = 0, l;
+
+ if( f->flags & PULLUP_HAVE_AFFINITY )
+ {
+ return;
+ }
+ f->flags |= PULLUP_HAVE_AFFINITY;
+
+ if( f->buffer == f->next->next->buffer )
+ {
+ f->affinity = 1;
+ f->next->affinity = 0;
+ f->next->next->affinity = -1;
+
+ f->next->flags |= PULLUP_HAVE_AFFINITY;
+ f->next->next->flags |= PULLUP_HAVE_AFFINITY;
+
+ return;
+ }
+
+ for( i = 0; i < c->metric_len; i++ )
+ {
+ int lv = f->prev->var[i];
+ int rv = f->next->var[i];
+ int v = f->var[i];
+ int lc = f->comb[i] - (v+lv) + PULLUP_ABS( v-lv );
+ int rc = f->next->comb[i] - (v+rv) + PULLUP_ABS( v-rv );
+
+ lc = (lc > 0) ? lc : 0;
+ rc = (rc > 0) ? rc : 0;
+ l = lc - rc;
+ if( l > max_l ) max_l = l;
+ if( -l > max_r ) max_r = -l;
+ }
+
+ if( max_l + max_r < 64 )
+ {
+ return;
+ }
+
+ if( max_r > 6*max_l )
+ {
+ f->affinity = -1;
+ }
+ else if( max_l > 6*max_r )
+ {
+ f->affinity = 1;
+ }
+}
+
+static void pullup_foo( struct pullup_context * c )
+{
+ struct pullup_field * f = c->first;
+ int i, n = pullup_queue_length (f, c->last );
+ for( i = 0; i < n-1; i++ )
+ {
+ if( i < n-3 ) pullup_compute_breaks( c, f );
+ pullup_compute_affinity( c, f );
+ f = f->next;
+ }
+}
+
+static int pullup_decide_frame_length( struct pullup_context * c )
+{
+ struct pullup_field *f0 = c->first;
+ struct pullup_field *f1 = f0->next;
+ struct pullup_field *f2 = f1->next;
+ struct pullup_field *f3 = f2->next;
+ int l;
+
+ if( pullup_queue_length( c->first, c->last ) < 4 )
+ {
+ return 0;
+ }
+ pullup_foo( c );
+
+ if( f0->affinity == -1 ) return 1;
+
+ l = pullup_find_first_break( f0, 3 );
+ if( l == 1 && c->strict_breaks < 0 ) l = 0;
+
+ switch (l)
+ {
+ case 1:
+ if ( c->strict_breaks < 1 &&
+ f0->affinity == 1 &&
+ f1->affinity == -1 )
+ {
+ return 2;
+ }
+ else
+ {
+ return 1;
+ }
+
+ case 2:
+ /* FIXME: strictly speaking, f0->prev is no longer valid... :) */
+ if( c->strict_pairs &&
+ (f0->prev->breaks & PULLUP_BREAK_RIGHT) &&
+ (f2->breaks & PULLUP_BREAK_LEFT) &&
+ (f0->affinity != 1 || f1->affinity != -1) )
+ {
+ return 1;
+ }
+ if( f1->affinity == 1 )
+ {
+ return 1;
+ }
+ else
+ {
+ return 2;
+ }
+
+ case 3:
+ if( f2->affinity == 1 )
+ {
+ return 2;
+ }
+ else
+ {
+ return 3;
+ }
+
+ default:
+ /* 9 possibilities covered before switch */
+ if( f1->affinity == 1 )
+ {
+ return 1; /* covers 6 */
+ }
+ else if( f1->affinity == -1 )
+ {
+ return 2; /* covers 6 */
+ }
+ else if( f2->affinity == -1 )
+ {
+ /* covers 2 */
+ if( f0->affinity == 1 )
+ {
+ return 3;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else
+ {
+ return 2; /* the remaining 6 */
+ }
+ }
+}
+
+static void pullup_print_aff_and_breaks(struct pullup_context * c,
+ struct pullup_field * f )
+{
+ int i;
+ struct pullup_field * f0 = f;
+ const char aff_l[] = "+..", aff_r[] = "..+";
+ printf( "\naffinity: " );
+ for( i = 0; i < 4; i++ )
+ {
+ printf( "%c%d%c",
+ aff_l[1+f->affinity],
+ i,
+ aff_r[1+f->affinity] );
+
+ f = f->next;
+ }
+ f = f0;
+ printf("\nbreaks: ");
+ for( i = 0; i < 4; i++ )
+ {
+ printf( "%c%d%c",
+ f->breaks & PULLUP_BREAK_LEFT ? '|' : '.',
+ i,
+ f->breaks & PULLUP_BREAK_RIGHT ? '|' : '.' );
+
+ f = f->next;
+ }
+ printf("\n");
+}
+
+/*
+ *
+ * PULLUP CONTEXT FUNCTIONS
+ *
+ */
+
+struct pullup_context * pullup_alloc_context( void )
+{
+ struct pullup_context * c;
+
+ c = calloc( 1, sizeof(struct pullup_context)) ;
+
+ return c;
+}
+
+void pullup_preinit_context( struct pullup_context * c )
+{
+ c->bpp = calloc( c->nplanes, sizeof(int) );
+ c->w = calloc( c->nplanes, sizeof(int) );
+ c->h = calloc( c->nplanes, sizeof(int) );
+ c->stride = calloc( c->nplanes, sizeof(int) );
+ c->background = calloc( c->nplanes, sizeof(int) );
+}
+
+void pullup_init_context( struct pullup_context * c )
+{
+ int mp = c->metric_plane;
+ if ( c->nbuffers < 10 )
+ {
+ c->nbuffers = 10;
+ }
+ c->buffers = calloc( c->nbuffers, sizeof (struct pullup_buffer) );
+
+ c->metric_w = (c->w[mp] - ((c->junk_left + c->junk_right) << 3)) >> 3;
+ c->metric_h = (c->h[mp] - ((c->junk_top + c->junk_bottom) << 1)) >> 3;
+ c->metric_offset = c->junk_left*c->bpp[mp] + (c->junk_top<<1)*c->stride[mp];
+ c->metric_len = c->metric_w * c->metric_h;
+
+ c->head = pullup_make_field_queue( c, 8 );
+
+ c->frame = calloc( 1, sizeof (struct pullup_frame) );
+ c->frame->ifields = calloc( 3, sizeof (struct pullup_buffer *) );
+
+ if( c->format == PULLUP_FMT_Y )
+ {
+ c->diff = pullup_diff_y;
+ c->comb = pullup_licomb_y;
+ c->var = pullup_var_y;
+ }
+}
+
+void pullup_free_context( struct pullup_context * c )
+{
+ struct pullup_field * f;
+
+ free( c->buffers );
+
+ f = c->head;
+ do
+ {
+ free( f->diffs );
+ free( f->comb );
+ f = f->next;
+ free( f->prev );
+ }
+ while( f != c->head );
+
+ free( c->frame );
+ free( c );
+}
+
+/*
+ *
+ * PULLUP BUFFER FUNCTIONS
+ *
+ */
+
+static void pullup_alloc_buffer( struct pullup_context * c,
+ struct pullup_buffer * b )
+{
+ int i;
+ if( b->planes ) return;
+ b->planes = calloc( c->nplanes, sizeof(unsigned char *) );
+ for ( i = 0; i < c->nplanes; i++ )
+ {
+ b->planes[i] = malloc(c->h[i]*c->stride[i]);
+ /* Deal with idiotic 128=0 for chroma: */
+ memset( b->planes[i], c->background[i], c->h[i]*c->stride[i] );
+ }
+}
+
+struct pullup_buffer * pullup_lock_buffer( struct pullup_buffer * b,
+ int parity )
+{
+ if( !b ) return 0;
+ if( (parity+1) & 1 ) b->lock[0]++;
+ if( (parity+1) & 2 ) b->lock[1]++;
+
+ return b;
+}
+
+void pullup_release_buffer( struct pullup_buffer * b,
+ int parity )
+{
+ if( !b ) return;
+ if( (parity+1) & 1 ) b->lock[0]--;
+ if( (parity+1) & 2 ) b->lock[1]--;
+}
+
+struct pullup_buffer * pullup_get_buffer( struct pullup_context * c,
+ int parity )
+{
+ int i;
+
+ /* Try first to get the sister buffer for the previous field */
+ if( parity < 2 &&
+ c->last &&
+ parity != c->last->parity &&
+ !c->last->buffer->lock[parity])
+ {
+ pullup_alloc_buffer( c, c->last->buffer );
+ return pullup_lock_buffer( c->last->buffer, parity );
+ }
+
+ /* Prefer a buffer with both fields open */
+ for( i = 0; i < c->nbuffers; i++ )
+ {
+ if( c->buffers[i].lock[0] ) continue;
+ if( c->buffers[i].lock[1] ) continue;
+ pullup_alloc_buffer( c, &c->buffers[i] );
+ return pullup_lock_buffer( &c->buffers[i], parity );
+ }
+
+ if( parity == 2 ) return 0;
+
+ /* Search for any half-free buffer */
+ for( i = 0; i < c->nbuffers; i++ )
+ {
+ if( ((parity+1) & 1) && c->buffers[i].lock[0] ) continue;
+ if( ((parity+1) & 2) && c->buffers[i].lock[1] ) continue;
+ pullup_alloc_buffer( c, &c->buffers[i] );
+ return pullup_lock_buffer( &c->buffers[i], parity );
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * PULLUP FRAME FUNCTIONS
+ *
+ */
+
+struct pullup_frame * pullup_get_frame( struct pullup_context * c )
+{
+ int i;
+ struct pullup_frame * fr = c->frame;
+ int n = pullup_decide_frame_length( c );
+ int aff = c->first->next->affinity;
+
+ if ( !n ) return 0;
+ if ( fr->lock ) return 0;
+
+ if ( c->verbose )
+ {
+ pullup_print_aff_and_breaks(c, c->first);
+ printf("duration: %d \n", n);
+ }
+
+ fr->lock++;
+ fr->length = n;
+ fr->parity = c->first->parity;
+ fr->buffer = 0;
+ for( i = 0; i < n; i++ )
+ {
+ /* We cheat and steal the buffer without release+relock */
+ fr->ifields[i] = c->first->buffer;
+ c->first->buffer = 0;
+ c->first = c->first->next;
+ }
+
+ if( n == 1 )
+ {
+ fr->ofields[fr->parity] = fr->ifields[0];
+ fr->ofields[fr->parity^1] = 0;
+ }
+ else if( n == 2 )
+ {
+ fr->ofields[fr->parity] = fr->ifields[0];
+ fr->ofields[fr->parity^1] = fr->ifields[1];
+ }
+ else if( n == 3 )
+ {
+ if( aff == 0 )
+ {
+ aff = (fr->ifields[0] == fr->ifields[1]) ? -1 : 1;
+ }
+ fr->ofields[fr->parity] = fr->ifields[1+aff];
+ fr->ofields[fr->parity^1] = fr->ifields[1];
+ }
+ pullup_lock_buffer( fr->ofields[0], 0 );
+ pullup_lock_buffer( fr->ofields[1], 1 );
+
+ if( fr->ofields[0] == fr->ofields[1] )
+ {
+ fr->buffer = fr->ofields[0];
+ pullup_lock_buffer(fr->buffer, 2);
+ return fr;
+ }
+ return fr;
+}
+
+void pullup_pack_frame( struct pullup_context * c, struct pullup_frame * fr)
+{
+ int i;
+ if (fr->buffer) return;
+ if (fr->length < 2) return; /* FIXME: deal with this */
+ for( i = 0; i < 2; i++ )
+ {
+ if( fr->ofields[i]->lock[i^1] ) continue;
+ fr->buffer = fr->ofields[i];
+ pullup_lock_buffer(fr->buffer, 2);
+ pullup_copy_field( c, fr->buffer, fr->ofields[i^1], i^1 );
+ return;
+ }
+ fr->buffer = pullup_get_buffer( c, 2 );
+ pullup_copy_field( c, fr->buffer, fr->ofields[0], 0 );
+ pullup_copy_field( c, fr->buffer, fr->ofields[1], 1 );
+}
+
+void pullup_release_frame( struct pullup_frame * fr )
+{
+ int i;
+ for( i = 0; i < fr->length; i++ )
+ {
+ pullup_release_buffer( fr->ifields[i], fr->parity ^ (i&1) );
+ }
+ pullup_release_buffer( fr->ofields[0], 0 );
+ pullup_release_buffer( fr->ofields[1], 1 );
+ if (fr->buffer) pullup_release_buffer( fr->buffer, 2 );
+ fr->lock--;
+}
+
+/*
+ *
+ * PULLUP FIELD FUNCTIONS
+ *
+ */
+
+void pullup_submit_field( struct pullup_context * c,
+ struct pullup_buffer * b,
+ int parity )
+{
+ struct pullup_field * f;
+
+ /* Grow the circular list if needed */
+ pullup_check_field_queue( c );
+
+ /* Cannot have two fields of same parity in a row; drop the new one */
+ if( c->last && c->last->parity == parity ) return;
+
+ f = c->head;
+ f->parity = parity;
+ f->buffer = pullup_lock_buffer( b, parity );
+ f->flags = 0;
+ f->breaks = 0;
+ f->affinity = 0;
+
+ pullup_compute_metric( c, f, parity, f->prev->prev,
+ parity, c->diff, f->diffs );
+ pullup_compute_metric( c, parity?f->prev:f, 0,
+ parity?f:f->prev, 1, c->comb, f->comb );
+ pullup_compute_metric( c, f, parity, f,
+ -1, c->var, f->var );
+
+ /* Advance the circular list */
+ if( !c->first ) c->first = c->head;
+ c->last = c->head;
+ c->head = c->head->next;
+}
+
+void pullup_flush_fields( struct pullup_context * c )
+{
+ struct pullup_field * f;
+
+ for( f = c->first; f && f != c->head; f = f->next )
+ {
+ pullup_release_buffer( f->buffer, f->parity );
+ f->buffer = 0;
+ }
+ c->first = c->last = 0;
+}
+
+/*
+ *
+ * DETELECINE FILTER FUNCTIONS
+ *
+ */
+
+hb_filter_private_t * hb_detelecine_init( int pix_fmt,
+ int width,
+ int height,
+ char * settings )
+{
+ if( pix_fmt != PIX_FMT_YUV420P )
+ {
+ return 0;
+ }
+
+ hb_filter_private_t * pv = malloc( sizeof(struct hb_filter_private_s) );
+
+ pv->pix_fmt = pix_fmt;
+ pv->width[0] = width;
+ pv->height[0] = height;
+ pv->width[1] = pv->width[2] = width >> 1;
+ pv->height[1] = pv->height[2] = height >> 1;
+
+ int buf_size = 3 * width * height / 2;
+ pv->buf_out = hb_buffer_init( buf_size );
+
+ struct pullup_context * ctx;
+ pv->pullup_ctx = ctx = pullup_alloc_context();
+
+ ctx->junk_left = ctx->junk_right = 1;
+ ctx->junk_top = ctx->junk_bottom = 4;
+ ctx->strict_breaks = 0;
+ ctx->metric_plane = 0;
+
+ if( settings )
+ {
+ sscanf( settings, "%d:%d:%d:%d:%d:%d",
+ &ctx->junk_left,
+ &ctx->junk_right,
+ &ctx->junk_top,
+ &ctx->junk_bottom,
+ &ctx->strict_breaks,
+ &ctx->metric_plane );
+ }
+
+ ctx->format = PULLUP_FMT_Y;
+ ctx->nplanes = 4;
+
+ pullup_preinit_context( ctx );
+
+ ctx->bpp[0] = ctx->bpp[1] = ctx->bpp[2] = 8;
+ ctx->background[1] = ctx->background[2] = 128;
+
+ ctx->w[0] = pv->width[0];
+ ctx->h[0] = pv->height[0];
+ ctx->stride[0] = pv->width[0];
+
+ ctx->w[1] = pv->width[1];
+ ctx->h[1] = pv->height[1];
+ ctx->stride[1] = pv->width[1];
+
+ ctx->w[2] = pv->width[2];
+ ctx->h[2] = pv->height[2];
+ ctx->stride[2] = pv->width[2];
+
+ ctx->w[3] = ((width+15)/16) * ((height+15)/16);
+ ctx->h[3] = 2;
+ ctx->stride[3] = ctx->w[3];
+
+#if 0
+ ctx->verbose = 1;
+#endif
+
+ pullup_init_context( ctx );
+
+ pv->pullup_fakecount = 1;
+ pv->pullup_skipflag = 0;
+
+ return pv;
+}
+
+void hb_detelecine_close( hb_filter_private_t * pv )
+{
+ if( !pv )
+ {
+ return;
+ }
+
+ if( pv->buf_out )
+ {
+ hb_buffer_close( &pv->buf_out );
+ }
+
+ if( pv->pullup_ctx )
+ {
+ pullup_free_context( pv->pullup_ctx );
+ }
+
+ free( pv );
+}
+
+int hb_detelecine_work( const hb_buffer_t * buf_in,
+ hb_buffer_t ** buf_out,
+ int pix_fmt,
+ int width,
+ int height,
+ hb_filter_private_t * pv )
+{
+ if( !pv ||
+ pix_fmt != pv->pix_fmt ||
+ width != pv->width[0] ||
+ height != pv->height[0] )
+ {
+ return FILTER_FAILED;
+ }
+
+ struct pullup_context * ctx = pv->pullup_ctx;
+ struct pullup_buffer * buf;
+ struct pullup_frame * frame;
+
+ buf = pullup_get_buffer( ctx, 2 );
+ if( !buf )
+ {
+ frame = pullup_get_frame( ctx );
+ pullup_release_frame( frame );
+ hb_log( "Could not get buffer from pullup!" );
+ return FILTER_FAILED;
+ }
+
+ /* Copy input buffer into pullup buffer */
+ avpicture_fill( &pv->pic_in, buf_in->data,
+ pix_fmt, width, height );
+
+ hb_buffer_copy_settings( pv->buf_out, buf_in );
+
+ memcpy( buf->planes[0], pv->pic_in.data[0],
+ pv->width[0] * pv->height[0] * sizeof(uint8_t) );
+ memcpy( buf->planes[1], pv->pic_in.data[1],
+ pv->width[1] * pv->height[1] * sizeof(uint8_t) );
+ memcpy( buf->planes[2], pv->pic_in.data[2],
+ pv->width[2] * pv->height[2] * sizeof(uint8_t) );
+
+ /* Submit buffer fields based on buffer flags */
+ int parity = 1;
+ if( buf_in->flags & PIC_FLAG_TOP_FIELD_FIRST )
+ {
+ parity = 0;
+ }
+ pullup_submit_field( ctx, buf, parity );
+ pullup_submit_field( ctx, buf, parity^1 );
+ if( buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD )
+ {
+ pullup_submit_field( ctx, buf, parity );
+ }
+ pullup_release_buffer( buf, 2 );
+
+ /* Get frame and check if pullup is ready */
+ frame = pullup_get_frame( ctx );
+ if( !frame )
+ {
+ if( pv->pullup_fakecount )
+ {
+ pv->pullup_fakecount--;
+
+ memcpy( pv->buf_out->data, buf_in->data, buf_in->size );
+
+ goto output_frame;
+ }
+ else
+ {
+ goto output_frame;
+ }
+ }
+
+ /* Check to see if frame should be dropped */
+ if( frame->length < 2 )
+ {
+ pullup_release_frame( frame );
+ frame = pullup_get_frame( ctx );
+
+ if (!frame)
+ {
+ goto output_frame;
+ }
+ if( frame->length < 2 )
+ {
+ pullup_release_frame( frame );
+
+ if( !(buf_in->flags & PIC_FLAG_REPEAT_FIRST_FIELD) )
+ {
+ goto output_frame;
+ }
+
+ frame = pullup_get_frame( ctx );
+
+ if( !frame )
+ {
+ goto output_frame;
+ }
+ if( frame->length < 2 )
+ {
+ pullup_release_frame( frame );
+ goto output_frame;
+ }
+ }
+ }
+
+ /* Check to see if frame buffer is ready for export */
+ if( !frame->buffer )
+ {
+ pullup_pack_frame( ctx, frame );
+ }
+
+ /* Copy pullup frame buffer into output buffer */
+ avpicture_fill( &pv->pic_out, pv->buf_out->data,
+ pix_fmt, width, height );
+
+ memcpy( pv->pic_out.data[0], frame->buffer->planes[0],
+ pv->width[0] * pv->height[0] * sizeof(uint8_t) );
+ memcpy( pv->pic_out.data[1], frame->buffer->planes[1],
+ pv->width[1] * pv->height[1] * sizeof(uint8_t) );
+ memcpy( pv->pic_out.data[2], frame->buffer->planes[2],
+ pv->width[2] * pv->height[2] * sizeof(uint8_t) );
+
+ pullup_release_frame( frame );
+
+output_frame:
+ *buf_out = pv->buf_out;
+ return FILTER_OK;
+}
+
+
diff --git a/libhb/fifo.c b/libhb/fifo.c
index fe03c47f9..44c64d9b0 100644
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -62,6 +62,15 @@ void hb_buffer_close( hb_buffer_t ** _b )
*_b = NULL;
}
+void hb_buffer_copy_settings( hb_buffer_t * dst, const hb_buffer_t * src )
+{
+ dst->start = src->start;
+ dst->stop = src->stop;
+ dst->new_chap = src->new_chap;
+ dst->frametype = src->frametype;
+ dst->flags = src->flags;
+}
+
/* Fifo */
struct hb_fifo_s
{
diff --git a/libhb/internal.h b/libhb/internal.h
index ced8d5777..b4870a128 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -48,6 +48,7 @@ struct hb_buffer_s
#define HB_FRAME_KEY 0x0F
#define HB_FRAME_REF 0xF0
uint8_t frametype;
+ uint8_t flags;
/* Holds the output PTS from x264, for use by b-frame offsets in muxmp4.c */
int64_t renderOffset;
@@ -65,7 +66,8 @@ struct hb_buffer_s
hb_buffer_t * hb_buffer_init( int size );
void hb_buffer_realloc( hb_buffer_t *, int size );
void hb_buffer_close( hb_buffer_t ** );
-
+void hb_buffer_copy_settings( hb_buffer_t * dst,
+ const hb_buffer_t * src );
hb_fifo_t * hb_fifo_init();
int hb_fifo_size( hb_fifo_t * );
@@ -199,6 +201,14 @@ enum
WORK_ENCVORBIS
};
+enum
+{
+ FILTER_DEINTERLACE = 1,
+ FILTER_DEBLOCK,
+ FILTER_DENOISE,
+ FILTER_DETELECINE
+};
+
extern hb_work_object_t * hb_objects;
#define HB_WORK_IDLE 0
diff --git a/libhb/render.c b/libhb/render.c
index b6f793fea..20b581383 100644
--- a/libhb/render.c
+++ b/libhb/render.c
@@ -13,10 +13,10 @@ struct hb_work_private_s
hb_job_t * job;
ImgReSampleContext * context;
- AVPicture pic_raw;
- AVPicture pic_deint;
- AVPicture pic_render;
- hb_buffer_t * buf_deint;
+ AVPicture pic_tmp_in;
+ AVPicture pic_tmp_out;
+ hb_buffer_t * buf_scale;
+ hb_fifo_t * subtitle_queue;
};
int renderInit( hb_work_object_t *, hb_job_t * );
@@ -101,80 +101,180 @@ int renderWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_work_private_t * pv = w->private_data;
hb_job_t * job = pv->job;
hb_title_t * title = job->title;
- hb_buffer_t * in = *buf_in, * buf;
+ hb_buffer_t * in = *buf_in, * buf_tmp_in = *buf_in;
+ int title_size = 3 * title->width * title->height / 2;
+ int job_size = 3 * job->width * job->height / 2;
+
if(!in->data)
{
/* If the input buffer is end of stream, send out an empty one to the next stage as well. */
- *buf_out = hb_buffer_init(0);
- return HB_WORK_OK;
+ *buf_out = hb_buffer_init(0);
+ return HB_WORK_OK;
}
-
- avpicture_fill( &pv->pic_raw, in->data, PIX_FMT_YUV420P,
- title->width, title->height );
-
- buf = hb_buffer_init( 3 * job->width * job->height / 2 );
- buf->start = in->start;
- buf->stop = in->stop;
-
- if( job->deinterlace && pv->context )
+
+ /* Push subtitles onto queue just in case we need to delay a frame */
+ if( in->sub )
{
- avpicture_fill( &pv->pic_render, buf->data, PIX_FMT_YUV420P,
- job->width, job->height );
- avpicture_deinterlace( &pv->pic_deint, &pv->pic_raw,
- PIX_FMT_YUV420P, title->width,
- title->height );
- ApplySub( job, pv->buf_deint, &in->sub );
- img_resample( pv->context, &pv->pic_render, &pv->pic_deint );
+ hb_fifo_push( pv->subtitle_queue, in->sub );
}
- else if( job->deinterlace )
+ else
{
- avpicture_fill( &pv->pic_deint, buf->data, PIX_FMT_YUV420P,
- job->width, job->height );
- avpicture_deinterlace( &pv->pic_deint, &pv->pic_raw,
- PIX_FMT_YUV420P, title->width,
- title->height );
- ApplySub( job, buf, &in->sub );
+ hb_fifo_push( pv->subtitle_queue, hb_buffer_init(0) );
}
- else if( pv->context )
+
+ /* Setup render buffer */
+ hb_buffer_t * buf_render = hb_buffer_init( job_size );
+
+ /* Apply filters */
+ if( job->filters )
{
- ApplySub( job, in, &in->sub );
- avpicture_fill( &pv->pic_render, buf->data, PIX_FMT_YUV420P,
- job->width, job->height );
- img_resample( pv->context, &pv->pic_render, &pv->pic_raw );
- }
- else
+ int filter_count = hb_list_count( job->filters );
+ int i;
+
+ for( i = 0; i < filter_count; i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->filters, i );
+
+ if( !filter )
+ {
+ continue;
+ }
+
+ hb_buffer_t * buf_tmp_out = NULL;
+
+ int result = filter->work( buf_tmp_in,
+ &buf_tmp_out,
+ PIX_FMT_YUV420P,
+ title->width,
+ title->height,
+ filter->private_data );
+
+ /*
+ * FILTER_OK: set temp buffer to filter buffer, continue
+ * FILTER_DELAY: set temp buffer to NULL, abort
+ * FILTER_DROP: set temp buffer to NULL, pop subtitle, abort
+ * FILTER_FAILED: leave temp buffer alone, continue
+ */
+ if( result == FILTER_OK )
+ {
+ buf_tmp_in = buf_tmp_out;
+ }
+ else if( result == FILTER_DELAY )
+ {
+ buf_tmp_in = NULL;
+ break;
+ }
+ else if( result == FILTER_DROP )
+ {
+ hb_fifo_get( pv->subtitle_queue );
+ buf_tmp_in = NULL;
+ break;
+ }
+ }
+ }
+
+ /* Apply subtitles */
+ if( buf_tmp_in )
{
- hb_buffer_close( &buf );
- ApplySub( job, in, &in->sub );
- buf = in;
- *buf_in = NULL;
+ hb_buffer_t * subtitles = hb_fifo_get( pv->subtitle_queue );
+ if( subtitles )
+ {
+ ApplySub( job, buf_tmp_in, &subtitles );
+ }
}
+
+ /* Apply crop/scale if specified */
+ if( buf_tmp_in && pv->context )
+ {
+ avpicture_fill( &pv->pic_tmp_in, buf_tmp_in->data,
+ PIX_FMT_YUV420P,
+ title->width, title->height );
+
+ avpicture_fill( &pv->pic_tmp_out, buf_render->data,
+ PIX_FMT_YUV420P,
+ job->width, job->height );
+
+ img_resample( pv->context, &pv->pic_tmp_out, &pv->pic_tmp_in );
+
+ hb_buffer_copy_settings( buf_render, buf_tmp_in );
+
+ buf_tmp_in = buf_render;
+ }
- (*buf_out) = buf;
+ /* Set output to render buffer */
+ (*buf_out) = buf_render;
+
+ if( buf_tmp_in == NULL )
+ {
+ /* Teardown and cleanup buffers if we are emitting NULL */
+ if( buf_in && *buf_in )
+ {
+ hb_buffer_close( buf_in );
+ *buf_in = NULL;
+ }
+ if( buf_out && *buf_out )
+ {
+ hb_buffer_close( buf_out );
+ *buf_out = NULL;
+ }
+ }
+ else if( buf_tmp_in != buf_render )
+ {
+ /* Copy temporary results and settings into render buffer */
+ memcpy( buf_render->data, buf_tmp_in->data, buf_render->size );
+ hb_buffer_copy_settings( buf_render, buf_tmp_in );
+ }
return HB_WORK_OK;
}
void renderClose( hb_work_object_t * w )
{
- hb_work_private_t * pv = w->private_data;
+ hb_work_private_t * pv = w->private_data;
+
+ /* Cleanup subtitle queue */
+ if( pv->subtitle_queue )
+ {
+ hb_fifo_close( &pv->subtitle_queue );
+ }
+ /* Cleanup filters */
+ /* TODO: Move to work.c? */
+ if( pv->job->filters )
+ {
+ int filter_count = hb_list_count( pv->job->filters );
+ int i;
+
+ for( i = 0; i < filter_count; i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( pv->job->filters, i );
+
+ if( !filter ) continue;
+
+ filter->close( filter->private_data );
+ }
+
+ hb_list_close( &pv->job->filters );
+ }
+
+ /* Cleanup render work structure */
free( pv );
- w->private_data = NULL;
+ w->private_data = NULL;
}
int renderInit( hb_work_object_t * w, hb_job_t * job )
-{
- hb_title_t * title;
-
+{
+ /* Allocate new private work object */
hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
- w->private_data = pv;
-
- title = job->title;
-
pv->job = job;
+ w->private_data = pv;
+ /* Get title and title size */
+ hb_title_t * title = job->title;
+ int title_size = 3 * title->width * title->height / 2;
+
+ /* If crop or scale is specified, setup rescale context */
if( job->crop[0] || job->crop[1] || job->crop[2] || job->crop[3] ||
job->width != title->width || job->height != title->height )
{
@@ -182,16 +282,30 @@ int renderInit( hb_work_object_t * w, hb_job_t * job )
job->width, job->height, title->width, title->height,
job->crop[0], job->crop[1], job->crop[2], job->crop[3],
0, 0, 0, 0 );
- }
-
- if( job->deinterlace )
+ }
+
+ /* Setup FIFO queue for subtitle cache */
+ pv->subtitle_queue = hb_fifo_init( 8 );
+
+ /* Setup filters */
+ /* TODO: Move to work.c? */
+ if( job->filters )
{
- /* Allocate a constant buffer used for deinterlacing */
- pv->buf_deint = hb_buffer_init( 3 * title->width *
- title->height / 2 );
- avpicture_fill( &pv->pic_deint, pv->buf_deint->data,
- PIX_FMT_YUV420P, title->width, title->height );
- }
+ int filter_count = hb_list_count( job->filters );
+ int i;
+
+ for( i = 0; i < filter_count; i++ )
+ {
+ hb_filter_object_t * filter = hb_list_item( job->filters, i );
+ if( !filter ) continue;
+
+ filter->private_data = filter->init( PIX_FMT_YUV420P,
+ title->width,
+ title->height,
+ filter->settings );
+ }
+ }
+
return 0;
}