]> granicus.if.org Git - handbrake/commitdiff
This huge patch from huevos_rancheros ports a number of video filters from mencoder...
authorjbrjake <jb.rubin@gmail.com>
Fri, 27 Jul 2007 14:55:58 +0000 (14:55 +0000)
committerjbrjake <jb.rubin@gmail.com>
Fri, 27 Jul 2007 14:55:58 +0000 (14:55 +0000)
HandBrake is growing up =)

Thank you, huevos_rancheros!

git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@749 b64f7644-9d1e-0410-96f1-a4d463321fa5

14 files changed:
libhb/Jamfile
libhb/Makefile
libhb/common.h
libhb/deblock.c [new file with mode: 0644]
libhb/decmpeg2.c
libhb/deinterlace.c [new file with mode: 0644]
libhb/denoise.c [new file with mode: 0644]
libhb/detelecine.c [new file with mode: 0644]
libhb/fifo.c
libhb/internal.h
libhb/render.c
macosx/Controller.mm
macosx/HandBrake.xcodeproj/project.pbxproj
test/test.c

index ce04831dfd429d1dfab14cd713a80548914d2b8c..41cc93886d77bbb2ef0c3654145164a6e58979be 100644 (file)
@@ -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) ;
 
index 6135b116106a528df893722e7e6b3658d9bc69b6..deba9a30b87510889910d3a20d804da3f7bbe493 100644 (file)
@@ -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)
 
index 81988de3d032f189d4653f1eebb4413b9779b632..de56a5c63079d20a5165e420fc434b48a73a1d67 100644 (file)
@@ -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 (file)
index 0000000..2d8b166
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ Copyright (C) 2005 Michael Niedermayer <michaelni@gmx.at>
+ 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;
+}
+
+
index 73252d9273be0d5c0cdc18747f4b270192b4e9aa..9e76be1bfdee17a54ac1d78ea761cf3db437dc8f 100644 (file)
@@ -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 (file)
index 0000000..754ed28
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
+ 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 (file)
index 0000000..5022d47
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ Copyright (C) 2003 Daniel Moreno <comac@comac.darktech.org>
+ 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 (file)
index 0000000..93b153c
--- /dev/null
@@ -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;
+}
+
+
index fe03c47f94222f53f2ac50fb01c7d46e1f675891..44c64d9b072733612a064aca8563d5c25039ad8e 100644 (file)
@@ -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
 {
index ced8d5777b393c8028e758560e366694e27a67bb..b4870a128f03c403bcf7611264fcef3108027cbf 100644 (file)
@@ -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
index b6f793fea88df275ace1f77cb90504a27217820e..20b5813838c6121dcaad79b71886df4bf0ef7b3f 100644 (file)
@@ -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;
 }
index 7bf9b50d51331f1aa7e93e49b9183189533a81ca..fb8d0ad8d34c4bae5228d7e159d347c653935d79 100644 (file)
@@ -1317,7 +1317,30 @@ list = hb_get_titles( fHandle );
     job->arate = hb_audio_rates[[fAudRatePopUp
                      indexOfSelectedItem]].rate;
     job->abitrate = [[fAudBitratePopUp selectedItem] tag];
+    
+    /* TODO: Filter settings */ 
+    job->filters = hb_list_init();
+#if 1
+    /* Run old deinterlacer if deinterlacing specified */
+    if( job->deinterlace )
+    {        
+        hb_filter_deinterlace.settings = "-1"; 
+        hb_list_add( job->filters, &hb_filter_deinterlace );
+    }
+#else
+    /* Choose your own filters! Here's some examples... */
+    hb_filter_detelecine.settings = "1:1:4:4:0:0";
+    hb_list_add( job->filters, &hb_filter_detelecine );
+
+    hb_filter_deinterlace.settings = "3:-1:2:10";
+    hb_list_add( job->filters, &hb_filter_deinterlace );
+
+    hb_filter_deblock.settings = "4:2";
+    hb_list_add( job->filters, &hb_filter_deblock );
 
+    hb_filter_denoise.settings = "3:2:3:3";
+    hb_list_add( job->filters, &hb_filter_denoise );
+#endif     
 }
 
 
index fc250f584a0bbbf5269f88d283776ca7fefdc095..a8f017a44bb054dd545159a90753e991af639418 100644 (file)
                A29E058B0BE12889000533F5 /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = A29E057F0BE1283E000533F5 /* Growl.framework */; };
                EAA526930C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; };
                EAA526940C3B25D200944FF2 /* stream.c in Sources */ = {isa = PBXBuildFile; fileRef = EAA526920C3B25D200944FF2 /* stream.c */; };
+               FC8519500C59A02C0073812C /* denoise.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194C0C59A02C0073812C /* denoise.c */; };
+               FC8519510C59A02C0073812C /* deinterlace.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194D0C59A02C0073812C /* deinterlace.c */; };
+               FC8519520C59A02C0073812C /* deblock.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194E0C59A02C0073812C /* deblock.c */; };
+               FC8519530C59A02C0073812C /* detelecine.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194F0C59A02C0073812C /* detelecine.c */; };
+               FC8519540C59A02C0073812C /* denoise.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194C0C59A02C0073812C /* denoise.c */; };
+               FC8519550C59A02C0073812C /* deinterlace.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194D0C59A02C0073812C /* deinterlace.c */; };
+               FC8519560C59A02C0073812C /* deblock.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194E0C59A02C0073812C /* deblock.c */; };
+               FC8519570C59A02C0073812C /* detelecine.c in Sources */ = {isa = PBXBuildFile; fileRef = FC85194F0C59A02C0073812C /* detelecine.c */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                A273E0950C57C19500493A45 /* muxmkv.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = muxmkv.c; path = ../libhb/muxmkv.c; sourceTree = SOURCE_ROOT; };
                A29E057F0BE1283E000533F5 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Growl.framework; sourceTree = "<group>"; };
                EAA526920C3B25D200944FF2 /* stream.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream.c; path = ../libhb/stream.c; sourceTree = SOURCE_ROOT; };
+               FC85194C0C59A02C0073812C /* denoise.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = denoise.c; path = ../libhb/denoise.c; sourceTree = SOURCE_ROOT; };
+               FC85194D0C59A02C0073812C /* deinterlace.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = deinterlace.c; path = ../libhb/deinterlace.c; sourceTree = SOURCE_ROOT; };
+               FC85194E0C59A02C0073812C /* deblock.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = deblock.c; path = ../libhb/deblock.c; sourceTree = SOURCE_ROOT; };
+               FC85194F0C59A02C0073812C /* detelecine.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = detelecine.c; path = ../libhb/detelecine.c; sourceTree = SOURCE_ROOT; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                0DFA5C840B8DD3B60020BC09 /* encfaac.c */,
                                0DFA5C850B8DD3B60020BC09 /* enclame.c */,
                                0DFA5C860B8DD3B60020BC09 /* encvorbis.c */,
+                               FC85194C0C59A02C0073812C /* denoise.c */,
+                               FC85194D0C59A02C0073812C /* deinterlace.c */,
+                               FC85194E0C59A02C0073812C /* deblock.c */,
+                               FC85194F0C59A02C0073812C /* detelecine.c */,
                                0DFA5C870B8DD3B60020BC09 /* encx264.c */,
                                0DFA5C880B8DD3B60020BC09 /* encxvid.c */,
                                0DFA5C890B8DD3B60020BC09 /* fifo.c */,
                                5558B56A0BE3BADF00E15E27 /* decdca.c in Sources */,
                                EAA526940C3B25D200944FF2 /* stream.c in Sources */,
                                A273E09B0C57C1CD00493A45 /* muxmkv.c in Sources */,
+                               FC8519540C59A02C0073812C /* denoise.c in Sources */,
+                               FC8519550C59A02C0073812C /* deinterlace.c in Sources */,
+                               FC8519560C59A02C0073812C /* deblock.c in Sources */,
+                               FC8519570C59A02C0073812C /* detelecine.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                5558B5690BE3BADF00E15E27 /* decdca.c in Sources */,
                                EAA526930C3B25D200944FF2 /* stream.c in Sources */,
                                A273E09A0C57C1CC00493A45 /* muxmkv.c in Sources */,
+                               FC8519500C59A02C0073812C /* denoise.c in Sources */,
+                               FC8519510C59A02C0073812C /* deinterlace.c in Sources */,
+                               FC8519520C59A02C0073812C /* deblock.c in Sources */,
+                               FC8519530C59A02C0073812C /* detelecine.c in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 929e3eb207487477c6a4c772969c52a8abe421be..d2a0a2d4f5ebab40eae842b9f3064371e97d1b7c 100644 (file)
@@ -24,7 +24,14 @@ static int    longest_title = 0;
 static int    subtitle_scan = 0;
 static char * native_language = NULL;
 static int    twoPass     = 0;
-static int    deinterlace = 0;
+static int    deinterlace           = 0;
+static char * deinterlace_opt       = 0;
+static int    deblock               = 0;
+static char * deblock_opt           = 0;
+static int    denoise               = 0;
+static char * denoise_opt           = 0;
+static int    detelecine            = 0;
+static char * detelecine_opt        = 0;
 static int    grayscale   = 0;
 static int    vcodec      = HB_VCODEC_FFMPEG;
 static int    h264_13     = 0;
@@ -427,6 +434,29 @@ static int HandleEvents( hb_handle_t * h )
             job->grayscale   = grayscale;
             job->pixel_ratio = pixelratio;
 
+            /* Add selected filters */
+            job->filters = hb_list_init();
+            if( detelecine )
+            {
+                hb_filter_detelecine.settings = detelecine_opt;
+                hb_list_add( job->filters, &hb_filter_detelecine );
+            }
+            if( deinterlace )
+            {
+                hb_filter_deinterlace.settings = deinterlace_opt;
+                hb_list_add( job->filters, &hb_filter_deinterlace );
+            }
+            if( deblock )
+            {
+                hb_filter_deblock.settings = deblock_opt;
+                hb_list_add( job->filters, &hb_filter_deblock );
+            }
+            if( denoise )
+            {
+                hb_filter_denoise.settings = denoise_opt;
+                hb_list_add( job->filters, &hb_filter_denoise );
+            }
+            
             if( width && height )
             {
                 job->width  = width;
@@ -778,7 +808,14 @@ static void ShowHelp()
     fprintf( stderr, ")\n"
        "\n"
        "    -2, --two-pass          Use two-pass mode\n"
-    "    -d, --deinterlace       Deinterlace video\n"
+     "    -d, --deinterlace       Deinterlace video with yadif/mcdeint filter\n"
+     "          <YM:FD:MM:QP>     (default 0:-1:-1:1)\n"            
+     "    -7, --deblock           Deblock video with pp7 filter\n"
+     "          <QP:M>            (default 0:2)\n"
+     "    -8, --denoise           Denoise video with hqdn3d filter\n"
+     "          <SL:SC:TL:TC>     (default 4:3:6:4.5)\n"
+     "    -8, --detelecine        Detelecine video with pullup filter\n"
+     "          <L:R:T:B:SB:MP>   (default 1:1:4:4:0:0)\n"
     "    -g, --grayscale         Grayscale encoding\n"
     "    -p, --pixelratio        Store pixel aspect ratio in video stream\n"
        
@@ -849,7 +886,10 @@ static int ParseOptions( int argc, char ** argv )
             { "encoder",     required_argument, NULL,    'e' },
             { "aencoder",    required_argument, NULL,    'E' },
             { "two-pass",    no_argument,       NULL,    '2' },
-            { "deinterlace", no_argument,       NULL,    'd' },
+            { "deinterlace", optional_argument, NULL,    'd' },
+            { "deblock",     optional_argument, NULL,    '7' },
+            { "denoise",     optional_argument, NULL,    '8' },
+            { "detelecine",  optional_argument, NULL,    '9' },
             { "grayscale",   no_argument,       NULL,    'g' },
             { "pixelratio",  no_argument,       NULL,    'p' },
             { "width",       required_argument, NULL,    'w' },
@@ -876,7 +916,7 @@ static int ParseOptions( int argc, char ** argv )
         int c;
 
         c = getopt_long( argc, argv,
-                         "hvuC:f:4i:o:t:Lc:ma:6:s:UN:e:E:2dgpw:l:n:b:q:S:B:r:R:Qx:TY:X:",
+                         "hvuC:f:4i:o:t:Lc:ma:6:s:UN:e:E:2d789gpw:l:n:b:q:S:B:r:R:Qx:TY:X:",
                          long_options, &option_index );
         if( c < 0 )
         {
@@ -982,8 +1022,33 @@ static int ParseOptions( int argc, char ** argv )
                 twoPass = 1;
                 break;
             case 'd':
+                if( optarg != NULL )
+                {
+                    deinterlace_opt = strdup( optarg );
+                }
                 deinterlace = 1;
                 break;
+            case '7':
+                if( optarg != NULL )
+                {
+                    deblock_opt = strdup( optarg );
+                }
+                deblock = 1;
+                break;
+            case '8':
+                if( optarg != NULL )
+                {
+                    denoise_opt = strdup( optarg );
+                }
+                denoise = 1;
+                break;                
+            case '9':
+                if( optarg != NULL )
+                {
+                    detelecine_opt = strdup( optarg );
+                }
+                detelecine = 1;
+                break;                
             case 'g':
                 grayscale = 1;
                 break;