Index: libhb/decomb.c =================================================================== --- libhb/decomb.c (revision 1647) +++ libhb/decomb.c (working copy) @@ -21,6 +21,18 @@ #define MIN3(a,b,c) MIN(MIN(a,b),c) #define MAX3(a,b,c) MAX(MAX(a,b),c) +typedef struct yadif_arguments_s { + uint8_t **dst; + int parity; + int tff; + int stop; + int is_combed; +} yadif_arguments_t; + +typedef struct decomb_arguments_s { + int stop; +} decomb_arguments_t; + struct hb_filter_private_s { int pix_fmt; @@ -62,6 +74,19 @@ AVPicture pic_out; hb_buffer_t * buf_out[2]; hb_buffer_t * buf_settings; + + int cpu_count; + + hb_thread_t ** yadif_threads; // Threads for Yadif - one per CPU + hb_lock_t ** yadif_begin_lock; // Thread has work + hb_lock_t ** yadif_complete_lock; // Thread has completed work + yadif_arguments_t *yadif_arguments; // Arguments to thread for work + + hb_thread_t ** decomb_threads; // Threads for comb detection - one per CPU + hb_lock_t ** decomb_begin_lock; // Thread has work + hb_lock_t ** decomb_complete_lock; // Thread has completed work + decomb_arguments_t *decomb_arguments; // Arguments to thread for work + }; hb_filter_private_t * hb_decomb_init( int pix_fmt, @@ -81,7 +106,7 @@ hb_filter_object_t hb_filter_decomb = { FILTER_DECOMB, - "Deinterlaces selectively with yadif/mcdeint or lowpass5 blending", + "Deinterlaces selectively with yadif/mcdeint and lowpass5 blending", NULL, hb_decomb_init, hb_decomb_work, @@ -479,6 +504,264 @@ return check_combing_mask( pv ); } +int detect_combed_segment( hb_filter_private_t * pv, int segment_start, int segment_stop ) +{ + /* A mish-mash of various comb detection tricks + picked up from neuron2's Decomb plugin for + AviSynth and tritical's IsCombedT and + IsCombedTIVTC plugins. */ + + int x, y, k, width, height; + + /* Comb scoring algorithm */ + int spatial_metric = pv->spatial_metric; + /* Motion threshold */ + int mthresh = pv->motion_threshold; + /* Spatial threshold */ + int athresh = pv->spatial_threshold; + int athresh_squared = athresh * athresh; + int athresh6 = 6 *athresh; + + /* One pas for Y, one pass for U, one pass for V */ + for( k = 0; k < 1; k++ ) + { + int ref_stride = pv->ref_stride[k]; + width = pv->width[k]; + height = pv->height[k]; + + /* Comb detection eneeds to start at y = 2 and + end at y = height - 2, because it needs to examine + 2 pixels above and 2 below the current pixel. */ + if( segment_start < 2 ) + segment_start = 2; + if( segment_stop > height - 2 ) + segment_stop = height - 2; + + for( y = segment_start; y < segment_stop; y++ ) + { + /* These are just to make the buffer locations easier to read. */ + int back_2 = ( y - 2 )*ref_stride ; + int back_1 = ( y - 1 )*ref_stride; + int current = y*ref_stride; + int forward_1 = ( y + 1 )*ref_stride; + int forward_2 = ( y + 2 )*ref_stride; + + /* We need to examine a column of 5 pixels + in the prev, cur, and next frames. */ + uint8_t previous_frame[5]; + uint8_t current_frame[5]; + uint8_t next_frame[5]; + + for( x = 0; x < width; x++ ) + { + /* Fill up the current frame array with the current pixel values.*/ + current_frame[0] = pv->ref[1][k][back_2 + x]; + current_frame[1] = pv->ref[1][k][back_1 + x]; + current_frame[2] = pv->ref[1][k][current + x]; + current_frame[3] = pv->ref[1][k][forward_1 + x]; + current_frame[4] = pv->ref[1][k][forward_2 + x]; + + int up_diff = current_frame[2] - current_frame[1]; + int down_diff = current_frame[2] - current_frame[3]; + + if( ( up_diff > athresh && down_diff > athresh ) || + ( up_diff < -athresh && down_diff < -athresh ) ) + { + /* The pixel above and below are different, + and they change in the same "direction" too.*/ + int motion = 0; + if( mthresh > 0 ) + { + /* Make sure there's sufficient motion between frame t-1 to frame t+1. */ + previous_frame[0] = pv->ref[0][k][back_2 + x]; + previous_frame[1] = pv->ref[0][k][back_1 + x]; + previous_frame[2] = pv->ref[0][k][current + x]; + previous_frame[3] = pv->ref[0][k][forward_1 + x]; + previous_frame[4] = pv->ref[0][k][forward_2 + x]; + next_frame[0] = pv->ref[2][k][back_2 + x]; + next_frame[1] = pv->ref[2][k][back_1 + x]; + next_frame[2] = pv->ref[2][k][current + x]; + next_frame[3] = pv->ref[2][k][forward_1 + x]; + next_frame[4] = pv->ref[2][k][forward_2 + x]; + + if( abs( previous_frame[2] - current_frame[2] ) > mthresh && + abs( current_frame[1] - next_frame[1] ) > mthresh && + abs( current_frame[3] - next_frame[3] ) > mthresh ) + motion++; + if( abs( next_frame[2] - current_frame[2] ) > mthresh && + abs( previous_frame[1] - current_frame[1] ) > mthresh && + abs( previous_frame[3] - current_frame[3] ) > mthresh ) + motion++; + } + else + { + /* User doesn't want to check for motion, + so move on to the spatial check. */ + motion = 1; + } + + if( motion || ( pv->yadif_deinterlaced_frames==0 && pv->blend_deinterlaced_frames==0 && pv->unfiltered_frames==0) ) + { + /*That means it's time for the spatial check. + We've got several options here. */ + if( spatial_metric == 0 ) + { + /* Simple 32detect style comb detection */ + if( ( abs( current_frame[2] - current_frame[4] ) < 10 ) && + ( abs( current_frame[2] - current_frame[3] ) > 15 ) ) + { + pv->mask[k][y*ref_stride + x] = 255; + } + else + { + pv->mask[k][y*ref_stride + x] = 0; + } + } + else if( spatial_metric == 1 ) + { + /* This, for comparison, is what IsCombed uses. + It's better, but still noise senstive. */ + int combing = ( current_frame[1] - current_frame[2] ) * + ( current_frame[3] - current_frame[2] ); + + if( combing > athresh_squared ) + pv->mask[k][y*ref_stride + x] = 255; + else + pv->mask[k][y*ref_stride + x] = 0; + } + else if( spatial_metric == 2 ) + { + /* Tritical's noise-resistant combing scorer. + The check is done on a bob+blur convolution. */ + int combing = abs( current_frame[0] + + ( 4 * current_frame[2] ) + + current_frame[4] + - ( 3 * ( current_frame[1] + + current_frame[3] ) ) ); + + /* If the frame is sufficiently combed, + then mark it down on the mask as 255. */ + if( combing > athresh6 ) + pv->mask[k][y*ref_stride + x] = 255; + else + pv->mask[k][y*ref_stride + x] = 0; + } + } + else + { + pv->mask[k][y*ref_stride + x] = 0; + } + } + else + { + pv->mask[k][y*ref_stride + x] = 0; + } + } + } + } +} + +typedef struct decomb_thread_arg_s { + hb_filter_private_t *pv; + int segment; +} decomb_thread_arg_t; + +/* + * comb detect this segment of all three planes in a single thread. + */ +void decomb_filter_thread( void *thread_args_v ) +{ + decomb_arguments_t *decomb_work = NULL; + hb_filter_private_t * pv; + int run = 1; + int segment, segment_start, segment_stop, plane; + decomb_thread_arg_t *thread_args = thread_args_v; + + pv = thread_args->pv; + segment = thread_args->segment; + + hb_log("decomb thread started for segment %d", segment); + + while( run ) + { + /* + * Wait here until there is work to do. hb_lock() blocks until + * render releases it to say that there is more work to do. + */ + hb_lock( pv->decomb_begin_lock[segment] ); + + decomb_work = &pv->decomb_arguments[segment]; + + if( decomb_work->stop ) + { + /* + * No more work to do, exit this thread. + */ + run = 0; + continue; + } + + /* + * Process segment (for now just from luma) + */ + for( plane = 0; plane < 1; plane++) + { + + int w = pv->width[plane]; + int h = pv->height[plane]; + int ref_stride = pv->ref_stride[plane]; + segment_start = ( h / pv->cpu_count ) * segment; + if( segment == pv->cpu_count - 1 ) + { + /* + * Final segment + */ + segment_stop = h; + } else { + segment_stop = ( h / pv->cpu_count ) * ( segment + 1 ); + } + + detect_combed_segment( pv, segment_start, segment_stop ); + } + /* + * Finished this segment, let everyone know. + */ + hb_unlock( pv->decomb_complete_lock[segment] ); + } + free( thread_args_v ); +} + +int comb_segmenter( hb_filter_private_t * pv ) +{ + int segment; + + for( segment = 0; segment < pv->cpu_count; segment++ ) + { + /* + * Let the thread for this plane know that we've setup work + * for it by releasing the begin lock (ensuring that the + * complete lock is already locked so that we block when + * we try to lock it again below). + */ + hb_lock( pv->decomb_complete_lock[segment] ); + hb_unlock( pv->decomb_begin_lock[segment] ); + } + + /* + * Wait until all three threads have completed by trying to get + * the complete lock that we locked earlier for each thread, which + * will block until that thread has completed the work on that + * plane. + */ + for( segment = 0; segment < pv->cpu_count; segment++ ) + { + hb_lock( pv->decomb_complete_lock[segment] ); + hb_unlock( pv->decomb_complete_lock[segment] ); + } + + return check_combing_mask( pv ); +} + static void yadif_filter_line( uint8_t *dst, uint8_t *prev, uint8_t *cur, @@ -599,13 +882,123 @@ } } +typedef struct yadif_thread_arg_s { + hb_filter_private_t *pv; + int segment; +} yadif_thread_arg_t; + +/* + * deinterlace this segment of all three planes in a single thread. + */ +void yadif_decomb_filter_thread( void *thread_args_v ) +{ + yadif_arguments_t *yadif_work = NULL; + hb_filter_private_t * pv; + int run = 1; + int plane; + int segment, segment_start, segment_stop; + yadif_thread_arg_t *thread_args = thread_args_v; + uint8_t **dst; + int parity, tff, y, w, h, ref_stride; + + pv = thread_args->pv; + segment = thread_args->segment; + int is_combed = pv->yadif_arguments[segment].is_combed; + + hb_log("yadif thread started for segment %d", segment); + + while( run ) + { + /* + * Wait here until there is work to do. hb_lock() blocks until + * render releases it to say that there is more work to do. + */ + hb_lock( pv->yadif_begin_lock[segment] ); + + yadif_work = &pv->yadif_arguments[segment]; + + if( yadif_work->stop ) + { + /* + * No more work to do, exit this thread. + */ + run = 0; + continue; + } + + if( yadif_work->dst == NULL ) + { + hb_error( "Thread started when no work available" ); + hb_snooze(500); + continue; + } + + /* + * Process all three planes, but only this segment of it. + */ + for( plane = 0; plane < 3; plane++) + { + + dst = yadif_work->dst; + parity = yadif_work->parity; + tff = yadif_work->tff; + w = pv->width[plane]; + h = pv->height[plane]; + ref_stride = pv->ref_stride[plane]; + segment_start = ( h / pv->cpu_count ) * segment; + if( segment == pv->cpu_count - 1 ) + { + /* + * Final segment + */ + segment_stop = h; + } else { + segment_stop = ( h / pv->cpu_count ) * ( segment + 1 ); + } + + for( y = segment_start; y < segment_stop; y++ ) + { + if( ( pv->mode == 4 && is_combed ) || is_combed == 2 ) + { + uint8_t *prev = &pv->ref[0][plane][y*ref_stride]; + uint8_t *cur = &pv->ref[1][plane][y*ref_stride]; + uint8_t *next = &pv->ref[2][plane][y*ref_stride]; + uint8_t *dst2 = &dst[plane][y*w]; + + blend_filter_line( dst2, cur, plane, y, pv ); + } + else if( (y ^ parity) & 1 && is_combed == 1 ) + { + uint8_t *prev = &pv->ref[0][plane][y*ref_stride]; + uint8_t *cur = &pv->ref[1][plane][y*ref_stride]; + uint8_t *next = &pv->ref[2][plane][y*ref_stride]; + uint8_t *dst2 = &dst[plane][y*w]; + + yadif_filter_line( dst2, prev, cur, next, plane, parity ^ tff, y, pv ); + } + else + { + memcpy( &dst[plane][y*w], + &pv->ref[1][plane][y*ref_stride], + w * sizeof(uint8_t) ); + } + } + } + /* + * Finished this segment, let everyone know. + */ + hb_unlock( pv->yadif_complete_lock[segment] ); + } + free( thread_args_v ); +} + static void yadif_filter( uint8_t ** dst, int parity, int tff, hb_filter_private_t * pv ) { - int is_combed = tritical_detect_comb( pv ); + int is_combed = comb_segmenter( pv ); if( is_combed == 1 ) { @@ -619,41 +1012,66 @@ { pv->unfiltered_frames++; } + + if( is_combed ) + { + int segment; - int i; - for( i = 0; i < 3; i++ ) + for( segment = 0; segment < pv->cpu_count; segment++ ) + { + /* + * Setup the work for this plane. + */ + pv->yadif_arguments[segment].parity = parity; + pv->yadif_arguments[segment].tff = tff; + pv->yadif_arguments[segment].dst = dst; + pv->yadif_arguments[segment].is_combed = is_combed; + + /* + * Let the thread for this plane know that we've setup work + * for it by releasing the begin lock (ensuring that the + * complete lock is already locked so that we block when + * we try to lock it again below). + */ + hb_lock( pv->yadif_complete_lock[segment] ); + hb_unlock( pv->yadif_begin_lock[segment] ); + } + + /* + * Wait until all three threads have completed by trying to get + * the complete lock that we locked earlier for each thread, which + * will block until that thread has completed the work on that + * plane. + */ + for( segment = 0; segment < pv->cpu_count; segment++ ) + { + hb_lock( pv->yadif_complete_lock[segment] ); + hb_unlock( pv->yadif_complete_lock[segment] ); + } + + /* + * Entire frame is now deinterlaced. + */ + } + else { - 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++ ) + /* Just passing through... */ + int i; + for( i = 0; i < 3; i++ ) { - if( ( pv->mode == 4 && is_combed ) || is_combed == 2 ) + uint8_t * ref = pv->ref[1][i]; + uint8_t * dest = dst[i]; + + int w = pv->width[i]; + int ref_stride = pv->ref_stride[i]; + + int y; + for( y = 0; y < pv->height[i]; y++ ) { - 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 ); + memcpy(dest, ref, w); + dest += w; + ref += ref_stride; } - else if( (y ^ parity) & 1 && is_combed == 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) ); - } } } } @@ -825,6 +1243,9 @@ &pv->block_width, &pv->block_height ); } + + pv->cpu_count = hb_get_cpu_count(); + if( pv->mode == 2 || pv->mode == 3 ) { @@ -857,6 +1278,91 @@ pv->mask[i] = malloc( w*h*sizeof(uint8_t) ) + 3*w; } + /* + * Create yadif threads and locks. + */ + pv->yadif_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); + pv->yadif_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); + pv->yadif_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); + pv->yadif_arguments = malloc( sizeof( yadif_arguments_t ) * pv->cpu_count ); + + for( i = 0; i < pv->cpu_count; i++ ) + { + yadif_thread_arg_t *thread_args; + + thread_args = malloc( sizeof( yadif_thread_arg_t ) ); + + if( thread_args ) + { + thread_args->pv = pv; + thread_args->segment = i; + + pv->yadif_begin_lock[i] = hb_lock_init(); + pv->yadif_complete_lock[i] = hb_lock_init(); + + /* + * Important to start off with the threads locked waiting + * on input. + */ + hb_lock( pv->yadif_begin_lock[i] ); + + pv->yadif_arguments[i].stop = 0; + pv->yadif_arguments[i].dst = NULL; + + pv->yadif_threads[i] = hb_thread_init( "yadif_filter_segment", + yadif_decomb_filter_thread, + thread_args, + HB_NORMAL_PRIORITY ); + } + else + { + hb_error( "Yadif could not create threads" ); + } + } + + /* + * Create decomb threads and locks. + */ + pv->decomb_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); + pv->decomb_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); + pv->decomb_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); + pv->decomb_arguments = malloc( sizeof( decomb_arguments_t ) * pv->cpu_count ); + + for( i = 0; i < pv->cpu_count; i++ ) + { + decomb_thread_arg_t *decomb_thread_args; + + decomb_thread_args = malloc( sizeof( decomb_thread_arg_t ) ); + + if( decomb_thread_args ) + { + decomb_thread_args->pv = pv; + decomb_thread_args->segment = i; + + pv->decomb_begin_lock[i] = hb_lock_init(); + pv->decomb_complete_lock[i] = hb_lock_init(); + + /* + * Important to start off with the threads locked waiting + * on input. + */ + hb_lock( pv->decomb_begin_lock[i] ); + + pv->decomb_arguments[i].stop = 0; + + pv->decomb_threads[i] = hb_thread_init( "decomb_filter_segment", + decomb_filter_thread, + decomb_thread_args, + HB_NORMAL_PRIORITY ); + } + else + { + hb_error( "decomb could not create threads" ); + } + } + + + /* Allocate mcdeint specific buffers */ if( pv->mcdeint_mode >= 0 ) { @@ -956,6 +1462,48 @@ } } + for( i = 0; i < pv->cpu_count; i++) + { + /* + * Tell each yadif thread to stop, and then cleanup. + */ + pv->yadif_arguments[i].stop = 1; + hb_unlock( pv->yadif_begin_lock[i] ); + + hb_thread_close( &pv->yadif_threads[i] ); + hb_lock_close( &pv->yadif_begin_lock[i] ); + hb_lock_close( &pv->yadif_complete_lock[i] ); + } + + /* + * free memory for yadif structs + */ + free( pv->yadif_threads ); + free( pv->yadif_begin_lock ); + free( pv->yadif_complete_lock ); + free( pv->yadif_arguments ); + + for( i = 0; i < pv->cpu_count; i++) + { + /* + * Tell each decomb thread to stop, and then cleanup. + */ + pv->decomb_arguments[i].stop = 1; + hb_unlock( pv->decomb_begin_lock[i] ); + + hb_thread_close( &pv->decomb_threads[i] ); + hb_lock_close( &pv->decomb_begin_lock[i] ); + hb_lock_close( &pv->decomb_complete_lock[i] ); + } + + /* + * free memory for decomb structs + */ + free( pv->decomb_threads ); + free( pv->decomb_begin_lock ); + free( pv->decomb_complete_lock ); + free( pv->decomb_arguments ); + /* Cleanup mcdeint specific buffers */ if( pv->mcdeint_mode >= 0 ) { Index: macosx/HandBrake.xcodeproj/project.pbxproj =================================================================== --- macosx/HandBrake.xcodeproj/project.pbxproj (revision 1647) +++ macosx/HandBrake.xcodeproj/project.pbxproj (working copy) @@ -1283,7 +1283,7 @@ GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = G5; - GCC_OPTIMIZATION_LEVEL = s; + GCC_OPTIMIZATION_LEVEL = 3; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../contrib/include";