Index: test/test.c =================================================================== --- test/test.c (revision 1469) +++ test/test.c (working copy) @@ -40,6 +40,8 @@ static char * denoise_opt = 0; static int detelecine = 0; static char * detelecine_opt = 0; +static int decomb = 0; +static char * decomb_opt = 0; static int grayscale = 0; static int vcodec = HB_VCODEC_FFMPEG; static int h264_13 = 0; @@ -819,6 +821,11 @@ hb_filter_detelecine.settings = detelecine_opt; hb_list_add( job->filters, &hb_filter_detelecine ); } + if( decomb ) + { + hb_filter_decomb.settings = decomb_opt; + hb_list_add( job->filters, &hb_filter_decomb ); + } if( deinterlace ) { hb_filter_deinterlace.settings = deinterlace_opt; @@ -1474,6 +1481,8 @@ " \n" " -9, --detelecine Detelecine video with pullup filter\n" " (default 1:1:4:4:0:0)\n" + " -5, --decomb Selectively deinterlaces when it detects combing\n" + " (default: 4:10:15:9:10:35:9)\n" " -g, --grayscale Grayscale encoding\n" " -p, --pixelratio Store pixel aspect ratio in video stream\n" " -P, --loosePixelratio Store pixel aspect ratio with specified width\n" @@ -1612,6 +1621,7 @@ { "deblock", optional_argument, NULL, '7' }, { "denoise", optional_argument, NULL, '8' }, { "detelecine", optional_argument, NULL, '9' }, + { "decomb", optional_argument, NULL, '5' }, { "grayscale", no_argument, NULL, 'g' }, { "pixelratio", no_argument, NULL, 'p' }, { "loosePixelratio", optional_argument, NULL, 'P' }, @@ -1641,7 +1651,7 @@ int c; c = getopt_long( argc, argv, - "hvuC:f:4i:Io:t:Lc:m::a:6:s:UFN:e:E:2dD:789gpOP::w:l:n:b:q:S:B:r:R:Qx:TY:X:VZ:z", + "hvuC:f:4i:Io:t:Lc:m::a:6:s:UFN:e:E:2dD:7895gpOP::w:l:n:b:q:S:B:r:R:Qx:TY:X:VZ:z", long_options, &option_index ); if( c < 0 ) { @@ -1874,6 +1884,13 @@ } detelecine = 1; break; + case '5': + if( optarg != NULL ) + { + decomb_opt = strdup( optarg ); + } + decomb = 1; + break; case 'g': grayscale = 1; break; Index: libhb/Jamfile =================================================================== --- libhb/Jamfile (revision 1469) +++ libhb/Jamfile (working copy) @@ -11,7 +11,7 @@ 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 deblock.c deinterlace.c -denoise.c detelecine.c lang.c enctheora.c ; +denoise.c detelecine.c decomb.c lang.c enctheora.c ; Library libhb : $(LIBHB_SRC) ; Index: libhb/hb.c =================================================================== --- libhb/hb.c (revision 1469) +++ libhb/hb.c (working copy) @@ -446,18 +446,25 @@ * @param color_diff Sensitivity for detecting different colors * @param threshold Sensitivity for flagging planes as combed */ -int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold ) +int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold, int prog_equal, int prog_diff, int prog_threshold ) { - int j, k, n, off, block, cc_1, cc_2, cc[3], flag[3]; + int j, k, n, off, cc_1, cc_2, cc[3], flag[3], plane_color_equal, plane_color_diff; uint16_t s1, s2, s3, s4; cc_1 = 0; cc_2 = 0; int offset = 0; + + if ( buf->flags & 16 ) + { + /* Frame is progressive, be more discerning. */ + color_diff = prog_diff; + color_equal = prog_equal; + threshold = prog_threshold; + } + /* One pas for Y, one pass for Cb, one pass for Cr */ for( k = 0; k < 3; k++ ) { - /* One pas for Y, one pass for Cb, one pass for Cr */ - if( k == 1 ) { /* Y has already been checked, now offset by Y's dimensions @@ -466,9 +473,6 @@ offset = width * height; width >>= 1; height >>= 1; - threshold >>= 1; - color_equal >>= 1; - color_diff >>= 1; } else if ( k == 2 ) { @@ -477,10 +481,7 @@ offset *= 5/4; } - /* Look at one horizontal line at a time */ - block = width; - - for( j = 0; j < block; ++j ) + for( j = 0; j < width; ++j ) { off = 0; @@ -488,9 +489,9 @@ { /* Look at groups of 4 sequential horizontal lines */ s1 = ( ( buf->data + offset )[ off + j ] & 0xff ); - s2 = ( ( buf->data + offset )[ off + j + block ] & 0xff ); - s3 = ( ( buf->data + offset )[ off + j + 2 * block ] & 0xff ); - s4 = ( ( buf->data + offset )[ off + j + 3 * block ] & 0xff ); + s2 = ( ( buf->data + offset )[ off + j + width ] & 0xff ); + s3 = ( ( buf->data + offset )[ off + j + 2 * width ] & 0xff ); + s4 = ( ( buf->data + offset )[ off + j + 3 * width ] & 0xff ); /* Note if the 1st and 2nd lines are more different in color than the 1st and 3rd lines are similar in color.*/ @@ -505,39 +506,38 @@ ++cc_2; /* Now move down 2 horizontal lines before starting over.*/ - off += 2 * block; + off += 2 * width; } } // compare results - /* The final metric seems to be doing some kind of bits per pixel style calculation - to decide whether or not enough lines showed alternating colors for the frame size. */ + /* The final cc score for a plane is the percentage of combed pixels it contains. + Because sensitivity goes down to hundreths of a percent, multiply by 1000 + so it will be easy to compare against the threhold value which is an integer. */ cc[k] = (int)( ( cc_1 + cc_2 ) * 1000.0 / ( width * height ) ); - - /* If the plane's cc score meets the threshold, flag it as combed. */ - flag[k] = 0; - if ( cc[k] > threshold ) - { - flag[k] = 1; - } } + + /* HandBrake is all yuv420, so weight the average percentage of all 3 planes accordingly.*/ + int average_cc = ( 2 * cc[0] + ( cc[1] / 2 ) + ( cc[2] / 2 ) ) / 3; + + /* Now see if that average percentage of combed pixels surpasses the threshold percentage given by the user.*/ + if( average_cc > threshold ) + { #if 0 -/* Debugging info */ -// if(flag) - hb_log("flags: %i/%i/%i | cc0: %i | cc1: %i | cc2: %i", flag[0], flag[1], flag[2], cc[0], cc[1], cc[2]); + hb_log("Average %i combed (Threshold %i) %i/%i/%i | PTS: %lld (%fs) %s", average_cc, threshold, cc[0], cc[1], cc[2], buf->start, (float)buf->start / 90000, (buf->flags & 16) ? "Film" : "Video" ); #endif - - /* When more than one plane shows combing, tell the caller. */ - if (flag[0] || flag[1] || flag[2] ) - { return 1; } +#if 0 + hb_log("SKIPPED Average %i combed (Threshold %i) %i/%i/%i | PTS: %lld (%fs) %s", average_cc, threshold, cc[0], cc[1], cc[2], buf->start, (float)buf->start / 90000, (buf->flags & 16) ? "Film" : "Video" ); +#endif + + /* Reaching this point means no combing detected. */ return 0; } - /** * Calculates job width and height for anamorphic content, * Index: libhb/Makefile =================================================================== --- libhb/Makefile (revision 1469) +++ libhb/Makefile (working copy) @@ -25,7 +25,7 @@ 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 deblock.c deinterlace.c denoise.c detelecine.c lang.c + dvd.c ipodutil.cpp deblock.c deinterlace.c denoise.c detelecine.c decomb.c lang.c OTMP = $(SRCS:%.c=%.o) OBJS = $(OTMP:%.cpp=%.o) Index: libhb/hb.h =================================================================== --- libhb/hb.h (revision 1469) +++ libhb/hb.h (working copy) @@ -79,7 +79,7 @@ /* hb_detect_comb() Analyze a frame for interlacing artifacts, returns true if they're found. Taken from Thomas Oestreich's 32detect filter in the Transcode project. */ -int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold ); +int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold, int prog_equal, int prog_diff, int prog_threshold ); void hb_get_preview( hb_handle_t *, hb_title_t *, int, uint8_t * ); Index: libhb/internal.h =================================================================== --- libhb/internal.h (revision 1469) +++ libhb/internal.h (working copy) @@ -224,7 +224,8 @@ FILTER_DEINTERLACE = 1, FILTER_DEBLOCK, FILTER_DENOISE, - FILTER_DETELECINE + FILTER_DETELECINE, + FILTER_DECOMB }; extern hb_work_object_t * hb_objects; Index: libhb/scan.c =================================================================== --- libhb/scan.c (revision 1469) +++ libhb/scan.c (working copy) @@ -432,7 +432,7 @@ buf_raw = hb_list_item( list_raw, 0 ); /* Check preview for interlacing artifacts */ - if( hb_detect_comb( buf_raw, title->width, title->height, 10, 30, 9 ) ) + if( hb_detect_comb( buf_raw, title->width, title->height, 10, 30, 9, 10, 30, 9 ) ) { hb_log("Interlacing detected in preview frame %i", i); interlaced_preview_count++; Index: libhb/decomb.c =================================================================== --- libhb/decomb.c (revision 0) +++ libhb/decomb.c (revision 0) @@ -0,0 +1,800 @@ +/* $Id: decomb.c,v 1.14 2008/04/25 5:00:00 jbrjake Exp $ + + This file is part of the HandBrake source code. + Homepage: . + It may be used under the terms of the GNU General Public License. + + The yadif algorithm was created by Michael Niedermayer. */ +#include "hb.h" +#include "ffmpeg/avcodec.h" +#include "mpeg2dec/mpeg2.h" + +#define SUPPRESS_AV_LOG + +#define MODE_DEFAULT 4 +#define 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 mode; + int parity; + + int yadif_ready; + + 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; + + int comb; + int color_equal; + int color_diff; + int threshold; + int prog_equal; + int prog_diff; + int prog_threshold; + int deinterlaced_frames; + int passed_frames; + + uint8_t * ref[4][3]; + int ref_stride[3]; + + AVPicture pic_in; + AVPicture pic_out; + hb_buffer_t * buf_out[2]; + hb_buffer_t * buf_settings; +}; + +hb_filter_private_t * hb_decomb_init( int pix_fmt, + int width, + int height, + char * settings ); + +int hb_decomb_work( hb_buffer_t * buf_in, + hb_buffer_t ** buf_out, + int pix_fmt, + int width, + int height, + hb_filter_private_t * pv ); + +void hb_decomb_close( hb_filter_private_t * pv ); + +hb_filter_object_t hb_filter_decomb = +{ + FILTER_DECOMB, + "Decombs selectively with (ffmpeg or yadif/mcdeint or blending)", + NULL, + hb_decomb_init, + hb_decomb_work, + hb_decomb_close, +}; + +int cubic_interpolate( int y0, int y1, int y2, int y3 ) +{ + /* From http://www.neuron2.net/library/cubicinterp.html */ + int result = ( y0 * -3 ) + ( y1 * 23 ) + ( y2 * 23 ) + ( y3 * -3 ); + result /= 40; + + if( result > 255 ) + { + result = 255; + } + else if( result < 0 ) + { + result = 0; + } + + return result; +} + +static void store_ref( const uint8_t ** pic, + hb_filter_private_t * pv ) +{ + memcpy( pv->ref[3], + pv->ref[0], + sizeof(uint8_t *)*3 ); + + memmove( pv->ref[0], + pv->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->ref[2][i]; + + int w = pv->width[i]; + int h = pv->height[i]; + int ref_stride = pv->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 get_ref( uint8_t ** pic, hb_filter_private_t * pv, int frm ) +{ + int i; + for( i = 0; i < 3; i++ ) + { + uint8_t * dst = pic[i]; + const uint8_t * ref = pv->ref[frm][i]; + int w = pv->width[i]; + int ref_stride = pv->ref_stride[i]; + + int y; + for( y = 0; y < pv->height[i]; y++ ) + { + memcpy(dst, ref, w); + dst += w; + ref += ref_stride; + } + } +} + +int blend_filter_pixel( int up2, int up1, int current, int down1, int down2 ) +{ + /* Low-pass 5-tap filter */ + int result = 0; + result += -up2; + result += up1 * 2; + result += current * 6; + result += down1 *2; + result += -down2; + result /= 8; + + if( result > 255 ) + { + result = 255; + } + if( result < 0 ) + { + result = 0; + } + + return result; +} + +static void blend_filter_line( uint8_t *dst, + uint8_t *cur, + int plane, + int y, + hb_filter_private_t * pv ) +{ + int w = pv->width[plane]; + int refs = pv->ref_stride[plane]; + int x; + + for( x = 0; x < w; x++) + { + + dst[0] = blend_filter_pixel( cur[-2*refs], cur[-refs], cur[0], cur[+refs], cur[2*refs] ); + + dst++; + cur++; + } +} + +static void yadif_filter_line( uint8_t *dst, + uint8_t *prev, + uint8_t *cur, + uint8_t *next, + int plane, + int parity, + int y, + 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->ref_stride[plane]; + int x; + + for( x = 0; x < w; x++) + { + +#if 0 + if( !detect_combed_pixel(cur[0], cur[-refs], cur[+refs], cur[2*refs], prev[0], next[0] ) ) + { + dst[0] = cur[0]; + goto end_of_yadif_filter_pixel; + } +#endif + + /* Pixel above*/ + int c = cur[-refs]; + /* Temporal average -- the current pixel location in the previous and next fields */ + int d = (prev2[0] + next2[0])>>1; + /* Pixel below */ + int e = cur[+refs]; + + /* How the current pixel changes from the field before to the field after */ + int temporal_diff0 = ABS(prev2[0] - next2[0]); + /* The average of how much the pixels above and below change from the field before to now. */ + int temporal_diff1 = ( ABS(prev[-refs] - cur[-refs]) + ABS(prev[+refs] - cur[+refs]) ) >> 1; + /* The average of how much the pixels above and below change from now to the next field. */ + int temporal_diff2 = ( ABS(next[-refs] - cur[-refs]) + ABS(next[+refs] - cur[+refs]) ) >> 1; + /* For the actual difference, use the largest of the previous average diffs. */ + int diff = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2); + + /* SAD of how the pixel-1, the pixel, and the pixel+1 change from the line above to below. */ + int spatial_score = ABS(cur[-refs-1] - cur[+refs-1]) + ABS(cur[-refs]-cur[+refs]) + + ABS(cur[-refs+1] - cur[+refs+1]) - 1; + int spatial_pred; + + /* Spatial pred is either a bilinear or cubic vertical interpolation. */ + if( pv->mode >= 4 ) + { + spatial_pred = cubic_interpolate( cur[-3*refs], cur[-refs], cur[+refs], cur[3*refs] ); + } + else + { + spatial_pred = (c+e)>>1; + } + +/* EDDI: Edge Directed Deinterlacing Interpolation + Uses the Martinez-Lim Line Shift Parametric Modeling algorithm...I think. + Checks 4 different slopes to see if there is more similarity along a diagonal + than there was vertically. If a diagonal is more similar, then it indicates + an edge, so interpolate along that instead of a vertical line, using either + linear or cubic interpolation depending on mode. */ +#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;\ + if( pv->mode >= 4)\ + {\ + switch(j)\ + {\ + case -1:\ + spatial_pred = cubic_interpolate(cur[-3 * refs - 3], cur[-refs -1], cur[+refs + 1], cur[3* refs + 3] );\ + break;\ + case -2:\ + spatial_pred = cubic_interpolate( ( ( cur[-3*refs - 4] + cur[-refs - 4] ) / 2 ) , cur[-refs -2], cur[+refs + 2], ( ( cur[3*refs + 4] + cur[refs + 4] ) / 2 ) );\ + break;\ + case 1:\ + spatial_pred = cubic_interpolate(cur[-3 * refs +3], cur[-refs +1], cur[+refs - 1], cur[3* refs -3] );\ + break;\ + case 2:\ + spatial_pred = cubic_interpolate(( ( cur[-3*refs + 4] + cur[-refs + 4] ) / 2 ), cur[-refs +2], cur[+refs - 2], ( ( cur[3*refs - 4] + cur[refs - 4] ) / 2 ) );\ + break;\ + }\ + }\ + else\ + {\ + spatial_pred = ( cur[-refs +j] + cur[+refs -j] ) >>1;\ + }\ + + YADIF_CHECK(-1) YADIF_CHECK(-2) }} }} + YADIF_CHECK( 1) YADIF_CHECK( 2) }} }} + + /* Temporally adjust the spatial prediction by comparing against the + alternate (associated) fields in the previous and next frames. */ + int b = (prev2[-2*refs] + next2[-2*refs])>>1; + int f = (prev2[+2*refs] + next2[+2*refs])>>1; + + /* Find the median value */ + 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; + +end_of_yadif_filter_pixel: + 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->ref_stride[i]; + + int y; + for( y = 0; y < h; y++ ) + { + if( pv->mode == 3) + { + uint8_t *prev = &pv->ref[0][i][y*ref_stride]; + uint8_t *cur = &pv->ref[1][i][y*ref_stride]; + uint8_t *next = &pv->ref[2][i][y*ref_stride]; + uint8_t *dst2 = &dst[i][y*w]; + + blend_filter_line( dst2, cur, i, y, pv ); + } + else if( (y ^ parity) & 1 ) + { + uint8_t *prev = &pv->ref[0][i][y*ref_stride]; + uint8_t *cur = &pv->ref[1][i][y*ref_stride]; + uint8_t *next = &pv->ref[2][i][y*ref_stride]; + uint8_t *dst2 = &dst[i][y*w]; + + yadif_filter_line( dst2, prev, cur, next, i, parity ^ tff, y, pv ); + } + else + { + memcpy( &dst[i][y*w], + &pv->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_decomb_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->deinterlaced_frames = 0; + pv->passed_frames = 0; + pv->color_equal = 10; + pv->color_diff = 15; + pv->threshold = 9; + pv->prog_equal = 10; + pv->prog_diff = 35; + pv->prog_threshold = 9; + + pv->yadif_ready = 0; + pv->mode = MODE_DEFAULT; + pv->parity = PARITY_DEFAULT; + + pv->mcdeint_mode = MCDEINT_MODE_DEFAULT; + pv->mcdeint_qp = MCDEINT_QP_DEFAULT; + + if( settings ) + { + sscanf( settings, "%d:%d:%d:%d:%d:%d:%d", + &pv->mode, + &pv->color_equal, + &pv->color_diff, + &pv->threshold, + &pv->prog_equal, + &pv->prog_diff, + &pv->prog_threshold ); + } + + if( pv->mode == 2 || pv->mode == 5 ) + { + pv->mcdeint_mode = 0; + } + + /* Allocate yadif specific buffers */ + if( pv->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->ref_stride[i] = w; + + for( j = 0; j < 3; j++ ) + { + pv->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_UMH; + 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_decomb_close( hb_filter_private_t * pv ) +{ + if( !pv ) + { + return; + } + + hb_log("decomb: deinterlaced %i | unfiltered %i | total %i", pv->deinterlaced_frames, pv->passed_frames, pv->deinterlaced_frames + pv->passed_frames); + + /* 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->mode > 0 ) + { + int i; + for( i = 0; i<3*3; i++ ) + { + uint8_t **p = &pv->ref[i%3][i/3]; + if (*p) + { + free( *p - 3*pv->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_decomb_work( 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 mode == 0 */ + if( pv->mode == 0 ) + { + avpicture_fill( &pv->pic_out, pv->buf_out[0]->data, + pix_fmt, width, height ); + + /* Check for combing on the input frame */ + int interlaced = hb_detect_comb(buf_in, width, height, pv->color_equal, pv->color_diff, pv->threshold, pv->prog_equal, pv->prog_diff, pv->prog_threshold); + + if(interlaced) + { + avpicture_deinterlace( &pv->pic_out, &pv->pic_in, + pix_fmt, width, height ); + + pv->deinterlaced_frames++; + //hb_log("Frame %i is combed (Progressive: %s )", pv->deinterlaced_frames + pv->passed_frames, (buf_in->flags & 16) ? "Y" : "N"); + + hb_buffer_copy_settings( pv->buf_out[0], buf_in ); + *buf_out = pv->buf_out[0]; + } + else + { + /* No combing detected, pass input frame through unmolested.*/ + + pv->passed_frames++; + + hb_buffer_copy_settings( pv->buf_out[0], buf_in ); + *buf_out = buf_in; + + } + + return FILTER_OK; + } + + /* Determine if top-field first layout */ + int tff; + if( pv->parity < 0 ) + { + tff = !!(buf_in->flags & PIC_FLAG_TOP_FIELD_FIRST); + } + else + { + tff = (pv->parity & 1) ^ 1; + } + + /* Store current frame in yadif cache */ + store_ref( (const uint8_t**)pv->pic_in.data, pv ); + + /* Note down if the input frame is combed */ + pv->comb = (pv->comb << 1) | hb_detect_comb(buf_in, width, height, pv->color_equal, pv->color_diff, pv->threshold, pv->prog_equal, pv->prog_diff, pv->prog_threshold); + + /* If yadif is not ready, store another ref and return FILTER_DELAY */ + if( pv->yadif_ready == 0 ) + { + store_ref( (const uint8_t**)pv->pic_in.data, pv ); + + hb_buffer_copy_settings( pv->buf_settings, buf_in ); + + /* don't let 'work_loop' send a chapter mark upstream */ + buf_in->new_chap = 0; + + pv->yadif_ready = 1; + + return FILTER_DELAY; + } + + /* yadif works one frame behind so if the previous frame + * had combing, deinterlace it otherwise just output it. */ + if( (pv->comb & 2 ) == 0 ) + { + /* previous frame not interlaced - copy cached input frame to buf_out */ + + pv->passed_frames++; + + avpicture_fill( &pv->pic_out, pv->buf_out[0]->data, pix_fmt, width, height ); + get_ref( (uint8_t**)pv->pic_out.data, pv, 1 ); + *buf_out = pv->buf_out[0]; + } + else + { + /* Perform yadif filtering */ + + pv->deinterlaced_frames++; + int frame; + for( frame = 0; frame <= ( ( pv->mode == 2 || pv->mode == 5 )? 1 : 0 ) ; 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 ) + { + /* Perform mcdeint filtering */ + 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)]; + } + } + + /* 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 ); + + /* don't let 'work_loop' send a chapter mark upstream */ + buf_in->new_chap = 0; + + return FILTER_OK; +} Index: libhb/common.h =================================================================== --- libhb/common.h (revision 1469) +++ libhb/common.h (working copy) @@ -574,6 +574,7 @@ extern hb_filter_object_t hb_filter_deinterlace; extern hb_filter_object_t hb_filter_deblock; extern hb_filter_object_t hb_filter_denoise; +extern hb_filter_object_t hb_filter_decomb; typedef void hb_error_handler_t( const char *errmsg ); Index: macosx/HandBrake.xcodeproj/project.pbxproj =================================================================== --- macosx/HandBrake.xcodeproj/project.pbxproj (revision 1469) +++ macosx/HandBrake.xcodeproj/project.pbxproj (working copy) @@ -89,6 +89,8 @@ 593034EC0BBA39A100172349 /* ChapterTitles.m in Sources */ = {isa = PBXBuildFile; fileRef = 593034EA0BBA39A100172349 /* ChapterTitles.m */; }; 59CBD2370BBB44DA004A3BE3 /* parsecsv.c in Sources */ = {isa = PBXBuildFile; fileRef = 59CBD2360BBB44DA004A3BE3 /* parsecsv.c */; }; 59CBD2650BBB4D1B004A3BE3 /* ChapterTitles.m in Sources */ = {isa = PBXBuildFile; fileRef = 593034EA0BBA39A100172349 /* ChapterTitles.m */; }; + 7497010F0DC281BB009200D8 /* decomb.c in Sources */ = {isa = PBXBuildFile; fileRef = 7497010E0DC281BB009200D8 /* decomb.c */; }; + 749701100DC281BB009200D8 /* decomb.c in Sources */ = {isa = PBXBuildFile; fileRef = 7497010E0DC281BB009200D8 /* decomb.c */; }; A22C85EC0D05D35000C10E36 /* HBPresets.h in Headers */ = {isa = PBXBuildFile; fileRef = A22C85EA0D05D35000C10E36 /* HBPresets.h */; }; A22C85ED0D05D35100C10E36 /* HBPresets.m in Sources */ = {isa = PBXBuildFile; fileRef = A22C85EB0D05D35000C10E36 /* HBPresets.m */; }; A25289E60D87A27D00461D5B /* enctheora.c in Sources */ = {isa = PBXBuildFile; fileRef = A25289E50D87A27D00461D5B /* enctheora.c */; }; @@ -257,6 +259,7 @@ 593034E90BBA39A100172349 /* ChapterTitles.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChapterTitles.h; sourceTree = ""; }; 593034EA0BBA39A100172349 /* ChapterTitles.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChapterTitles.m; sourceTree = ""; }; 59CBD2360BBB44DA004A3BE3 /* parsecsv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parsecsv.c; path = ../test/parsecsv.c; sourceTree = SOURCE_ROOT; }; + 7497010E0DC281BB009200D8 /* decomb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = decomb.c; path = ../libhb/decomb.c; sourceTree = SOURCE_ROOT; }; A22C85EA0D05D35000C10E36 /* HBPresets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBPresets.h; sourceTree = ""; }; A22C85EB0D05D35000C10E36 /* HBPresets.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBPresets.m; sourceTree = ""; }; A25289E50D87A27D00461D5B /* enctheora.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = enctheora.c; path = ../libhb/enctheora.c; sourceTree = SOURCE_ROOT; }; @@ -434,6 +437,7 @@ isa = PBXGroup; children = ( A25289E50D87A27D00461D5B /* enctheora.c */, + 7497010E0DC281BB009200D8 /* decomb.c */, B48359A70C82960500E04440 /* lang.c */, EAA526920C3B25D200944FF2 /* stream.c */, 0DFA5C7E0B8DD3B60020BC09 /* declpcm.c */, @@ -785,6 +789,7 @@ FC8519550C59A02C0073812C /* deinterlace.c in Sources */, FC8519560C59A02C0073812C /* deblock.c in Sources */, FC8519570C59A02C0073812C /* detelecine.c in Sources */, + 749701100DC281BB009200D8 /* decomb.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -830,6 +835,7 @@ FC8519530C59A02C0073812C /* detelecine.c in Sources */, B48359A80C82960500E04440 /* lang.c in Sources */, A25289E60D87A27D00461D5B /* enctheora.c in Sources */, + 7497010F0DC281BB009200D8 /* decomb.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };