All pastes #1180325 Raw Edit

threaded decomb

public diff v1 · immutable
#1180325 ·published 2008-08-21 03:31 UTC
rendered paste body
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";