Index: test/test.c===================================================================--- test/test.c (revision 1335)+++ test/test.c (working copy)@@ -300,6 +300,13 @@ fprintf( stderr, " + %d, %s (iso639-2: %s)\n", i + 1, subtitle->lang, subtitle->iso639_2); }++ if(title->detected_interlacing)+ {+ /* Interlacing was found in 6 or more preview frames */+ fprintf( stderr, " + interlacing artifacts detected\n");+ }+ } static int HandleEvents( hb_handle_t * h )Index: libhb/hb.c===================================================================--- libhb/hb.c (revision 1335)+++ libhb/hb.c (working copy)@@ -426,6 +426,111 @@ avpicture_free( &pic_in ); }+ /**+ * Analyzes a frame to detect interlacing artifacts+ * and returns true if interlacing (combing) is found.+ *+ * Code taken from Thomas Oestreich's 32detect filter+ * in the Transcode project, with minor formatting changes.+ *+ * @param buf An hb_buffer structure holding valid frame data.+ * @param width The frame's width in pixels.+ * @param height The frame's height in pixels.+ */+int hb_detect_comb( hb_buffer_t * buf, int width, int height, int color_equal, int color_diff, int threshold)+{+ int j, k, n, off, block, cc_1, cc_2, cc[3], flag[3];+ uint16_t s1, s2, s3, s4;+ cc_1 = 0; cc_2 = 0;++ /* These values are defined in hb.h */+ int thres = threshold;+ int eq = color_equal;+ int diff = color_diff;++ int offset = 0;++ 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+ and divide all the other values by 2, since Cr and Cb+ are half-size compared to Y. */+ offset = width * height;+ width >>= 1;+ height >>= 1;+ thres >>= 1;+ eq >>= 1;+ diff >>= 1;+ }+ else if (k == 2 )+ {+ /* Y and Cb are done, so the offset needs to be bumped+ so it's width*height + (width / 2) * (height / 2) */+ offset *= 5/4;+ }++ /* Look at one horizontal line at a time */+ block = width;++ for(j=0; j<block; ++j)+ {+ off=0;++ for(n=0; n<(height-4); n=n+2)+ {+ /* 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);++ /* Note if the 1st and 2nd lines are more different in+ color than the 1st and 3rd lines are similar in color.*/+ if((abs(s1 - s3) < eq) &&+ (abs(s1 - s2) > diff)) ++cc_1;++ /* Note if the 2nd and 3rd lines are more different in+ color than the 2nd and 4th lines are similar in color.*/+ if((abs(s2 - s4) < eq) &&+ (abs(s2 - s3) > diff)) ++cc_2;++ /* Now move down 2 horizontal lines before starting over.*/+ off +=2*block;+ }+ }++ // 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. */+ 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] > thres)+ {+ flag[k] = 1;+ }+ }++#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]);+#endif++ /* When more than one plane shows combing, tell the caller. */+ if (flag[0] || flag[1] || flag[2] )+ {+ return 1;+ }++ return 0;+}++ /** * Calculates job width and height for anamorphic content, *Index: libhb/hb.h===================================================================--- libhb/hb.h (revision 1335)+++ libhb/hb.h (working copy)@@ -75,6 +75,14 @@ Returns the list of valid titles detected by the latest scan. */ hb_list_t * hb_get_titles( hb_handle_t * );+/* 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);+#define COLOR_EQUAL 10 // Sensitivity for detecting similar colors.+#define COLOR_DIFF 30 // Sensitivity for detecting different colors+#define THRESHOLD 9 // Sensitivity for flagging planes as combed+ void hb_get_preview( hb_handle_t *, hb_title_t *, int, uint8_t * ); void hb_set_size( hb_job_t *, int ratio, int pixels );Index: libhb/deinterlace.c===================================================================--- libhb/deinterlace.c (revision 1335)+++ libhb/deinterlace.c (working copy)@@ -42,6 +42,12 @@ int yadif_parity; int yadif_ready;+ int comb;+ int comb_detect;+ int color_equal;+ int color_diff;+ int threshold;+ uint8_t * yadif_ref[4][3]; int yadif_ref_stride[3];@@ -58,6 +64,9 @@ AVPicture pic_out; hb_buffer_t * buf_out[2]; hb_buffer_t * buf_settings;++ int deinterlaced_frames;+ int passed_frames; }; hb_filter_private_t * hb_deinterlace_init( int pix_fmt,@@ -87,6 +96,7 @@ static void yadif_store_ref( const uint8_t ** pic, hb_filter_private_t * pv ) {+ /* 1st entry becomes 4th, 2nd entry because 1st. */ memcpy( pv->yadif_ref[3], pv->yadif_ref[0], sizeof(uint8_t *)*3 );@@ -94,17 +104,22 @@ memmove( pv->yadif_ref[0], pv->yadif_ref[1], sizeof(uint8_t *)*3*3 );-++ /* 3 color planes */ int i; for( i = 0; i < 3; i++ ) {+ /* Source is the input plane */ const uint8_t * src = pic[i];+ /* Ref points to where it'll be stored, the 3rd slot in the ref array.*/ uint8_t * ref = pv->yadif_ref[2][i];-++ /* Dimensions will be halved for Cb + Cr, stride is offset for line length..*/ int w = pv->width[i]; int h = pv->height[i]; int ref_stride = pv->yadif_ref_stride[i];-++ /* Go through each horizontal line of the src plane and copy it to the buffer. */ int y; for( y = 0; y < pv->height[i]; y++ ) {@@ -115,6 +130,27 @@ } }+static void yadif_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->yadif_ref[frm][i];+ int w = pv->width[i];+ int ref_stride = pv->yadif_ref_stride[i];++ int y;+ for( y = 0; y < pv->height[i]; y++ )+ {+ memcpy(dst, ref, w);+ dst += w;+ ref += ref_stride;+ }+ }+}++ static void yadif_filter_line( uint8_t *dst, uint8_t *prev, uint8_t *cur,@@ -123,26 +159,83 @@ int parity, hb_filter_private_t * pv ) {+ /* If TFF, look at the previous and current frames, otherwise the current and next */ uint8_t *prev2 = parity ? prev : cur ; uint8_t *next2 = parity ? cur : next;-++ /* Width aries with plane size.*/ int w = pv->width[plane];+ /* Stride offset varies with planar width. */ int refs = pv->yadif_ref_stride[plane];-++ /* Step through the horizontal pixels in this line. */ int x; for( x = 0; x < w; x++) {+ /* C: Pixel above*/ int c = cur[-refs];+ /* D: the average of this pixel in the frames before and after.*/ int d = (prev2[0] + next2[0])>>1;+ /* E: Pixel below*/ int e = cur[+refs];+ /* diff0: The energy delta between this pixel in the previous and next frames.*/ int temporal_diff0 = ABS(prev2[0] - next2[0]);+ /* diff1: The average of the deltas between:+ The pixel above in the last frame minus the pixel above in the current frame, and+ The pixel below in the last frame minus the pixel below in the current frame. */ int temporal_diff1 = ( ABS(prev[-refs] - c) + ABS(prev[+refs] - e) ) >> 1;+ /* diff2: The average of the deltas between:+ The pixel above in the next frame minus the pixel above in the current frame, and+ The pixel below in the next frame minus the pixel below in the current frame. */ int temporal_diff2 = ( ABS(next[-refs] - c) + ABS(next[+refs] - e) ) >> 1;+ /* diff: Choose the largest of the three:+ Half the change in the current pixel between the previous and next frames,+ The average vertical change since the last frame, or+ The average vertical change in the next frame. */ int diff = MAX3(temporal_diff0>>1, temporal_diff1, temporal_diff2);+ /* spatial_pred: Average of the pixels above and below in the current frame. */ int spatial_pred = (c+e)>>1;++ /* spatial_score:+ The difference between the pixels above and below the pixel to the left, plus+ The difference between the pixels above and below the current pixel, plus,+ The difference between the pixels above and below the pixel to the right,+ ...minus one. Why? */ int spatial_score = ABS(cur[-refs-1] - cur[+refs-1]) + ABS(c-e) + ABS(cur[-refs+1] - cur[+refs+1]) - 1;+/* The Yadif Spatial Check Score is run 2-4 times.+ PASS ONE, j=-1+ The pixel 2 to the left above minus the pixel below, plus+ The pixel 1 to the left abve minus the pixel 1 to the right below, plus+ The pixel in the line above minus the pixel 2 to the right below.+ IF this is less than the spatial score:+ It replaces the spatial score.+ The spatial_pred is set to the average of the pixel 1 to the left above and the pixel 1 to the right below.+ Run PASS 2:+ PASS TWO j = -2+ The pixel 3 to the left above minus the pixel 1 to the right below, plus+ The pixel 2 to the left above minus the pixel 2 to the right below, plus+ The pixel 1 to the left above minus the pixel 3 to the right below.+ IF this is less than the spatial score:+ It replaces the spatial score.+ The spatial_pred is set to the average of the pixel 2 to the left above minus the pixel 2 to the right below.+ PASS THREE: j = 1+ The pixel above minus the pixel 2 to the left below, plus+ The pixel 1 to the right above minus the pixel 1 to the left below, plus+ The pixel 2 to the right above minus the pixel below.+ IF this is less than the spatial score:+ It replaces the spatial score.+ The spatial_pred is set ot the average of the pixel 1 to the right above and the pixel 1 to the left below.+ Run PASS 4:+ PASS FOUR: j = 2+ The pixel 1 to the right above minus the pixel 3 to the left below, plus+ The pixel 2 to the right above minus the pixel 2 to the left below, plus+ The pixel 3 to the right above, minus the pixel 1 to the left below.+ IF this is less than the spatal score:+ It repalces the spatial score.+ The spatial_pred is set to the average of the pixel 2 to the right above and 2 to the left below.+*/ #define YADIF_CHECK(j)\ { int score = ABS(cur[-refs-1+j] - cur[+refs-1-j])\ + ABS(cur[-refs +j] - cur[+refs -j])\@@ -153,18 +246,32 @@ YADIF_CHECK(-1) YADIF_CHECK(-2) }} }} YADIF_CHECK( 1) YADIF_CHECK( 2) }} }}-++ /* The slow stuff */ if( pv->yadif_mode < 2 ) {+ /* B: the average of the pixel 2 lines above in the frames before and after. */ int b = (prev2[-2*refs] + next2[-2*refs])>>1;+ /* F: the average of the pixel 2 lines below in the frames before and after. */ int f = (prev2[+2*refs] + next2[+2*refs])>>1;-++ /* Which is bigger? / Which is smaller?+ The average of this pixel in the frames before and after minus the pixel below, or+ The average of this pixel in the frames before and after miinus the pixel above, or+ The smaller of: / The bigger of:+ The average of the pixel 2 lines above in the frames before and afer minus the pixel above, or+ The average of the pixel 2 lines below in the frames before and after minus the pixel below */ int max = MAX3(d-e, d-c, MIN(b-c, f-e)); int min = MIN3(d-e, d-c, MAX(b-c, f-e));-++ /* For the real temporal diff, use whichever's largest.*/ diff = MAX3( diff, min, -max ); }-++ /* If the prediction is larger than the average of the pixel in frames before and after+ plus temporal correction, replace it. Otherwise --+ If the prediction is smaller than the average of the pixel in the frames before and+ after minus temporal correction, replace it. */ if( spatial_pred > d + diff ) { spatial_pred = d + diff;@@ -173,9 +280,11 @@ { spatial_pred = d - diff; }-++ /* Use the prediction as the output. */ dst[0] = spatial_pred;-++ /* Bump the head for the next pixel. */ dst++; cur++; prev++;@@ -190,27 +299,32 @@ int tff, hb_filter_private_t * pv ) {+ /* Step through the color planes. */ int i; for( i = 0; i < 3; i++ ) {+ /* Dimensions will be halved for Cb+Cr, stride is offset for previous planes.*/ int w = pv->width[i]; int h = pv->height[i]; int ref_stride = pv->yadif_ref_stride[i];-++ /* Step through horizontal lines.*/ int y; for( y = 0; y < h; y++ ) { if( (y ^ parity) & 1 ) {+ /* Only filter second field? */ 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 {+ /* Pass the line through unscathed.*/ memcpy( &dst[i][y*w], &pv->yadif_ref[1][i][y*ref_stride], w * sizeof(uint8_t) );@@ -356,6 +470,13 @@ 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->comb_detect = 0;+ pv->color_equal = 10;+ pv->color_diff = 30;+ pv->threshold = 9;+ pv->yadif_ready = 0; pv->yadif_mode = YADIF_MODE_DEFAULT; pv->yadif_parity = YADIF_PARITY_DEFAULT;@@ -365,13 +486,19 @@ if( settings ) {- sscanf( settings, "%d:%d:%d:%d",+ sscanf( settings, "%d:%d:%d:%d:%d:%d:%d:%d", &pv->yadif_mode, &pv->yadif_parity, &pv->mcdeint_mode,- &pv->mcdeint_qp );+ &pv->mcdeint_qp,+ &pv->comb_detect,+ &pv->color_equal,+ &pv->color_diff,+ &pv->threshold ); }+ hb_log("DEINTERLACE: detect: %i eq: %i diff: %i thresh: %i", pv->comb_detect, pv->color_equal, pv->color_diff, pv->threshold);+ /* Allocate yadif specific buffers */ if( pv->yadif_mode >= 0 ) {@@ -451,6 +578,8 @@ return; }+ hb_log("deinterlacer: filtered %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] ) {@@ -521,13 +650,31 @@ 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 );+ /* Check for combing on the input frame */+ int interlaced = pv->comb_detect ? ( hb_detect_comb(buf_in, width, height, pv->color_equal, pv->color_diff, pv->threshold)) : 1;++ if(interlaced)+ {+ avpicture_deinterlace( &pv->pic_out, &pv->pic_in,+ pix_fmt, width, height );- hb_buffer_copy_settings( pv->buf_out[0], buf_in );+ pv->deinterlaced_frames++;- *buf_out = pv->buf_out[0];+ 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; }@@ -545,6 +692,9 @@ /* Store current frame in yadif cache */ yadif_store_ref( (const uint8_t**)pv->pic_in.data, pv );+ /* Note down if the input frame is combed */+ pv->comb = (pv->comb << 1) | ( pv->comb_detect? hb_detect_comb(buf_in, width, height, pv->color_equal, pv->color_diff, pv->threshold) : 1);+ /* If yadif is not ready, store another ref and return FILTER_DELAY */ if( pv->yadif_ready == 0 ) {@@ -559,32 +709,51 @@ return FILTER_DELAY; }-- /* Perform yadif and mcdeint filtering */- int frame;- for( frame = 0; frame <= (pv->yadif_mode & 1); frame++ )++ /* yadif & mcdeint work one frame behind so if the previous frame+ * had combing, deinterlace it otherwise just output it. */+ if( (pv->comb & 2 ) == 0 ) {- int parity = frame ^ tff ^ 1;+ /* 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 );+ yadif_get_ref( (uint8_t**)pv->pic_out.data, pv, 1 );+ *buf_out = pv->buf_out[0];+ }+ else+ {+ /* Perform yadif and mcdeint filtering */++ pv->deinterlaced_frames++;++ 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 );+ avpicture_fill( &pv->pic_out, pv->buf_out[!(frame^1)]->data,+ pix_fmt, width, height );- yadif_filter( pv->pic_out.data, parity, tff, pv );+ 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 );+ 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 );+ mcdeint_filter( pv->pic_in.data, pv->pic_out.data, parity, pv );- *buf_out = pv->buf_out[ (frame^1)];+ *buf_out = pv->buf_out[ (frame^1)];+ }+ else+ {+ *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 );Index: libhb/scan.c===================================================================--- libhb/scan.c (revision 1335)+++ libhb/scan.c (working copy)@@ -286,6 +286,8 @@ hb_list_t * list_es, * list_raw; hb_libmpeg2_t * mpeg2; int progressive_count = 0;+ int interlacing[10];+ int ar16_count = 0, ar4_count = 0; buf_ps = hb_buffer_init( HB_DVD_READ_BUFFER_SIZE );@@ -419,7 +421,7 @@ */ if( progressive_count == 6 ) {- hb_log("Title's mostly progressive NTSC, setting fps to 23.976");+ hb_log("Title's mostly NTSC Film, setting fps to 23.976"); } title->rate_base = 1126125; }@@ -449,7 +451,19 @@ } 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) )+ {+ hb_log("Interlacing detected in preview frame %i", i);+ interlacing[i] = 1;+ }+ else+ {+ interlacing[i] = 0;+ }++ hb_get_tempory_filename( data->h, filename, "%x%d", (intptr_t)title, i );@@ -522,7 +536,28 @@ (float) title->rate_base, title->crop[0], title->crop[1], title->crop[2], title->crop[3], title->aspect == HB_ASPECT_BASE * 16 / 9 ? "16:9" :- title->aspect == HB_ASPECT_BASE * 4 / 3 ? "4:3" : "none" );+ title->aspect == HB_ASPECT_BASE * 4 / 3 ? "4:3" : "none" );++ /* Add up how many previews were interlaced.*/+ int interlacing_sum, t;+ for(t = 0; t < 10; t++ )+ {+ if( interlacing[t] == 1 )+ {+ interlacing_sum++;+ }+ }++ if( interlacing_sum >= 6)+ {+ hb_log("Title is mostly interlaced or telecined (%i out of 10 previews). You should do something about that.", interlacing_sum);+ title->detected_interlacing = 1;+ }+ else+ {+ title->detected_interlacing = 0;+ }+ goto cleanup; error:Index: libhb/common.h===================================================================--- libhb/common.h (revision 1335)+++ libhb/common.h (working copy)@@ -426,6 +426,7 @@ int rate; int rate_base; int crop[4];+ int detected_interlacing; uint32_t palette[16];