From 1529c129b8765a0acf342da91d67cb99c743dc6a Mon Sep 17 00:00:00 2001 From: jbrjake Date: Sat, 19 Dec 2009 15:19:12 +0000 Subject: Adds a very crude, CLI-only rotation filter to flip pixels vertically (mode 1), horizontally (mode 2), or both (mode 3, default). Called with --rotate. git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@3036 b64f7644-9d1e-0410-96f1-a4d463321fa5 --- libhb/rotate.c | 387 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 libhb/rotate.c (limited to 'libhb/rotate.c') 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; +} + + -- cgit v1.2.3