diff options
-rw-r--r-- | libhb/common.h | 1 | ||||
-rw-r--r-- | libhb/internal.h | 3 | ||||
-rw-r--r-- | libhb/rotate.c | 387 | ||||
-rw-r--r-- | test/test.c | 19 |
4 files changed, 409 insertions, 1 deletions
diff --git a/libhb/common.h b/libhb/common.h index d76022238..57447b09a 100644 --- a/libhb/common.h +++ b/libhb/common.h @@ -732,6 +732,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; +extern hb_filter_object_t hb_filter_rotate; typedef void hb_error_handler_t( const char *errmsg ); diff --git a/libhb/internal.h b/libhb/internal.h index efe18fa44..ada06b6b7 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -307,7 +307,8 @@ enum FILTER_DEBLOCK, FILTER_DENOISE, FILTER_DETELECINE, - FILTER_DECOMB + FILTER_DECOMB, + FILTER_ROTATE }; extern hb_work_object_t * hb_objects; diff --git a/libhb/rotate.c b/libhb/rotate.c new file mode 100644 index 000000000..efa825a91 --- /dev/null +++ b/libhb/rotate.c @@ -0,0 +1,387 @@ + +#include "hb.h" +#include "hbffmpeg.h" +//#include "mpeg2dec/mpeg2.h" + +#define MODE_DEFAULT 3 +// Mode 1: Flip vertically (y0 becomes yN and yN becomes y0) +// Mode 2: Flip horizontally (x0 becomes xN and xN becomes x0) +// Mode 3: Flip both horizontally and vertically (modes 1 and 2 combined) + +typedef struct rotate_arguments_s { + uint8_t **dst; + int stop; +} rotate_arguments_t; + +struct hb_filter_private_s +{ + int pix_fmt; + int width[3]; + int height[3]; + + int mode; + + int ref_stride[3]; + + int cpu_count; + + hb_thread_t ** rotate_threads; // Threads for Rotate - one per CPU + hb_lock_t ** rotate_begin_lock; // Thread has work + hb_lock_t ** rotate_complete_lock; // Thread has completed work + rotate_arguments_t *rotate_arguments; // Arguments to thread for work + + AVPicture pic_in; + AVPicture pic_out; + hb_buffer_t * buf_out; + hb_buffer_t * buf_settings; +}; + +hb_filter_private_t * hb_rotate_init( int pix_fmt, + int width, + int height, + char * settings ); + +int hb_rotate_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_rotate_close( hb_filter_private_t * pv ); + +hb_filter_object_t hb_filter_rotate = +{ + FILTER_ROTATE, + "Rotate (flips image axes)", + NULL, + hb_rotate_init, + hb_rotate_work, + hb_rotate_close, +}; + + +static void rotate_filter_line( uint8_t *dst, + uint8_t *cur, + int plane, + hb_filter_private_t * pv ) +{ + + int w = pv->width[plane]; + + int x; + for( x = 0; x < w; x++) + { + if( pv->mode & 2 ) + { + dst[x] = cur[w-x-1]; + } + else + { + dst[x] = cur[x]; + } + } +} + +typedef struct rotate_thread_arg_s { + hb_filter_private_t *pv; + int segment; +} rotate_thread_arg_t; + +/* + * rotate this segment of all three planes in a single thread. + */ +void rotate_filter_thread( void *thread_args_v ) +{ + rotate_arguments_t *rotate_work = NULL; + hb_filter_private_t * pv; + int run = 1; + int plane; + int segment, segment_start, segment_stop; + rotate_thread_arg_t *thread_args = thread_args_v; + uint8_t **dst; + int y, w, h, ref_stride; + + + pv = thread_args->pv; + segment = thread_args->segment; + + hb_log("Rotate 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->rotate_begin_lock[segment] ); + + rotate_work = &pv->rotate_arguments[segment]; + + if( rotate_work->stop ) + { + /* + * No more work to do, exit this thread. + */ + run = 0; + continue; + } + + if( rotate_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 = rotate_work->dst; + 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++ ) + { + uint8_t * cur; + + if( pv->mode & 1 ) + { + cur = &pv->pic_in.data[plane][(h-y-1)*pv->pic_in.linesize[plane]]; + } + else + { + cur = &pv->pic_in.data[plane][(y)*pv->pic_in.linesize[plane]]; + } + uint8_t *dst2 = &dst[plane][y*w]; + + rotate_filter_line( dst2, + cur, + plane, + pv ); + } + } + /* + * Finished this segment, let everyone know. + */ + hb_unlock( pv->rotate_complete_lock[segment] ); + } + free( thread_args_v ); +} + + +/* + * threaded rotate - each thread rptates a single segment of all + * three planes. Where a segment is defined as the frame divided by + * the number of CPUs. + * + * This function blocks until the frame is rotated. + */ +static void rotate_filter( uint8_t ** dst, + hb_filter_private_t * pv ) +{ + + int segment; + + for( segment = 0; segment < pv->cpu_count; segment++ ) + { + /* + * Setup the work for this plane. + */ + pv->rotate_arguments[segment].dst = dst; + + /* + * 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->rotate_complete_lock[segment] ); + hb_unlock( pv->rotate_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->rotate_complete_lock[segment] ); + hb_unlock( pv->rotate_complete_lock[segment] ); + } + + /* + * Entire frame is now rotated. + */ +} + + +hb_filter_private_t * hb_rotate_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; + + pv->buf_out = hb_video_buffer_init( width, height ); + pv->buf_settings = hb_buffer_init( 0 ); + + pv->mode = MODE_DEFAULT; + + pv->ref_stride[0] = pv->width[0]; + pv->ref_stride[1] = pv->width[1]; + pv->ref_stride[2] = pv->width[2]; + + if( settings ) + { + sscanf( settings, "%d", + &pv->mode ); + } + + pv->cpu_count = hb_get_cpu_count(); + + + /* + * Create threads and locks. + */ + pv->rotate_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); + pv->rotate_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); + pv->rotate_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); + pv->rotate_arguments = malloc( sizeof( rotate_arguments_t ) * pv->cpu_count ); + + int i; + for( i = 0; i < pv->cpu_count; i++ ) + { + rotate_thread_arg_t *thread_args; + + thread_args = malloc( sizeof( rotate_thread_arg_t ) ); + + if( thread_args ) { + thread_args->pv = pv; + thread_args->segment = i; + + pv->rotate_begin_lock[i] = hb_lock_init(); + pv->rotate_complete_lock[i] = hb_lock_init(); + + /* + * Important to start off with the threads locked waiting + * on input. + */ + hb_lock( pv->rotate_begin_lock[i] ); + + pv->rotate_arguments[i].stop = 0; + pv->rotate_arguments[i].dst = NULL; + + pv->rotate_threads[i] = hb_thread_init( "rotate_filter_segment", + rotate_filter_thread, + thread_args, + HB_NORMAL_PRIORITY ); + } else { + hb_error( "rotate could not create threads" ); + } + } + + return pv; +} + +void hb_rotate_close( hb_filter_private_t * pv ) +{ + if( !pv ) + { + return; + } + + /* Cleanup frame buffers */ + if( pv->buf_out ) + { + hb_buffer_close( &pv->buf_out ); + } + if (pv->buf_settings ) + { + hb_buffer_close( &pv->buf_settings ); + } + + int i; + for( i = 0; i < pv->cpu_count; i++) + { + /* + * Tell each rotate thread to stop, and then cleanup. + */ + pv->rotate_arguments[i].stop = 1; + hb_unlock( pv->rotate_begin_lock[i] ); + + hb_thread_close( &pv->rotate_threads[i] ); + hb_lock_close( &pv->rotate_begin_lock[i] ); + hb_lock_close( &pv->rotate_complete_lock[i] ); + } + + /* + * free memory for rotate structs + */ + free( pv->rotate_threads ); + free( pv->rotate_begin_lock ); + free( pv->rotate_complete_lock ); + free( pv->rotate_arguments ); + + free( pv ); +} + +int hb_rotate_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 ); + + avpicture_fill( &pv->pic_out, pv->buf_out->data, + pix_fmt, width, height ); + + //do stuff here + rotate_filter( pv->pic_out.data, pv ); + hb_buffer_copy_settings( pv->buf_out, buf_in ); + + *buf_out = pv->buf_out; + + return FILTER_OK; +} + + diff --git a/test/test.c b/test/test.c index 3d5e960a9..cc7629757 100644 --- a/test/test.c +++ b/test/test.c @@ -51,6 +51,8 @@ static int detelecine = 0; static char * detelecine_opt = 0; static int decomb = 0; static char * decomb_opt = 0; +static int rotate = 0; +static char * rotate_opt = 0; static int grayscale = 0; static int vcodec = HB_VCODEC_FFMPEG; static int h264_13 = 0; @@ -1070,6 +1072,12 @@ static int HandleEvents( hb_handle_t * h ) /* Add selected filters */ job->filters = hb_list_init(); + + if( rotate ) + { + hb_filter_rotate.settings = rotate_opt; + hb_list_add( job->filters, &hb_filter_rotate); + } if( detelecine ) { hb_filter_detelecine.settings = detelecine_opt; @@ -2326,6 +2334,8 @@ static void ShowHelp() " <weak/medium/strong>\n" " -7, --deblock Deblock video with pp7 filter\n" " <QP:M> (default 5:2)\n" + " --rotate Flips images axes\n" + " <M> (default 3)\n" " -g, --grayscale Grayscale encoding\n" "\n" @@ -2498,6 +2508,7 @@ static int ParseOptions( int argc, char ** argv ) #define SRT_OFFSET 271 #define SRT_LANG 272 #define SRT_DEFAULT 273 + #define ROTATE_FILTER 274 for( ;; ) { @@ -2544,6 +2555,7 @@ static int ParseOptions( int argc, char ** argv ) { "detelecine", optional_argument, NULL, '9' }, { "decomb", optional_argument, NULL, '5' }, { "grayscale", no_argument, NULL, 'g' }, + { "rotate", optional_argument, NULL, ROTATE_FILTER }, { "strict-anamorphic", no_argument, &anamorphic_mode, 1 }, { "loose-anamorphic", no_argument, &anamorphic_mode, 2 }, { "custom-anamorphic", no_argument, &anamorphic_mode, 3 }, @@ -2844,6 +2856,13 @@ static int ParseOptions( int argc, char ** argv ) case 'g': grayscale = 1; break; + case ROTATE_FILTER: + if( optarg != NULL ) + { + rotate_opt = strdup( optarg ); + } + rotate = 1; + break; case DISPLAY_WIDTH: if( optarg != NULL ) { |