diff --git a/Makefile b/Makefile index 57d6e9c..d126956 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ SRCS = common/mc.c common/predict.c common/pixel.c common/macroblock.c \ common/frame.c common/dct.c common/cpu.c common/cabac.c \ common/common.c common/mdate.c common/set.c \ common/quant.c common/vlc.c \ - encoder/analyse.c encoder/me.c encoder/ratecontrol.c \ + encoder/analyse.c encoder/me.c encoder/ratecontrol.c encoder/lookahead.c\ encoder/set.c encoder/macroblock.c encoder/cabac.c \ encoder/cavlc.c encoder/encoder.c diff --git a/common/common.c b/common/common.c index c163e09..f628107 100644 --- a/common/common.c +++ b/common/common.c @@ -104,6 +104,7 @@ void x264_param_default( x264_param_t *param ) param->rc.f_qblur = 0.5; param->rc.f_complexity_blur = 20; param->rc.i_zones = 0; + param->rc.i_lookahead = 0; /* Log */ param->pf_log = x264_log_default; @@ -533,6 +534,8 @@ int x264_param_parse( x264_param_t *p, const char *name, const char *value ) p->rc.f_ip_factor = atof(value); OPT2("pbratio", "pb-factor") p->rc.f_pb_factor = atof(value); + OPT("lookahead") + p->rc.i_lookahead = atoi(value); OPT("aq-mode") p->rc.i_aq_mode = atoi(value); OPT("aq-strength") @@ -718,6 +721,16 @@ void *x264_malloc( int i_size ) } /**************************************************************************** + * x264_mallocz: + ****************************************************************************/ +void *x264_mallocz( int i_size ) +{ + void *p = x264_malloc( i_size ); + memset( p, 0, i_size ); + return p; +} + +/**************************************************************************** * x264_free: ****************************************************************************/ void x264_free( void *p ) @@ -770,9 +783,9 @@ void x264_reduce_fraction( int *n, int *d ) c = a % b; while(c) { - a = b; - b = c; - c = a % b; + a = b; + b = c; + c = a % b; } *n /= b; *d /= b; @@ -878,6 +891,9 @@ char *x264_param2string( x264_param_t *p, int b_res ) if( p->rc.i_vbv_buffer_size ) s += sprintf( s, " vbv_maxrate=%d vbv_bufsize=%d", p->rc.i_vbv_max_bitrate, p->rc.i_vbv_buffer_size ); + if( !p->rc.b_stat_read ) + s += sprintf( s, " lookahead=%d", p->rc.i_lookahead ); + } else if( p->rc.i_rc_method == X264_RC_CQP ) s += sprintf( s, " qp=%d", p->rc.i_qp_constant ); diff --git a/common/common.h b/common/common.h index 394f9da..1ba69f3 100644 --- a/common/common.h +++ b/common/common.h @@ -24,6 +24,14 @@ #ifndef X264_COMMON_H #define X264_COMMON_H + +/**************************************************************************** + * Error handling + ****************************************************************************/ +#define X264_SUCCESS 0 +#define X264_ERROR_UNSPECIFIED 1 +#define X264_ERROR 1 + /**************************************************************************** * Macros ****************************************************************************/ @@ -82,11 +90,13 @@ /* x264_malloc : will do or emulate a memalign * you have to use x264_free for buffers allocated with x264_malloc */ void *x264_malloc( int ); +void *x264_mallocz( int ); void *x264_realloc( void *p, int i_size ); void x264_free( void * ); /* x264_slurp_file: malloc space for the whole file and read it */ char *x264_slurp_file( const char *filename ); +void parse_qpfile( cli_opt_t *, x264_picture_t *, int ); /* mdate: return the current date in microsecond */ int64_t x264_mdate( void ); @@ -209,6 +219,27 @@ typedef struct } x264_slice_header_t; +typedef struct x264_lookahead_t +{ + int b_thread_active; + int b_exit_thread; + int i_last_idr_planned; + int i_input; + x264_pthread_t thread_handle; + x264_frame_t *last_nonb_planned; + /* Using a synch list here since encoder.c pushes frames back to this list */ + synch_frame_list_t next; + /* Input buffer (for next) */ + synch_frame_list_t ofbuf; + + cli_opt_t *opt; + synch_frame_list_t unused; + x264_pthread_mutex_t mutex; + + + +} x264_lookahead_t; + /* From ffmpeg */ #define X264_SCAN8_SIZE (6*8) @@ -318,11 +349,9 @@ struct x264_t struct { /* Frames to be encoded (whose types have been decided) */ - x264_frame_t *current[X264_BFRAME_MAX*4+3]; - /* Temporary buffer (frames types not yet decided) */ - x264_frame_t *next[X264_BFRAME_MAX*4+3]; - /* Unused frames */ - x264_frame_t *unused[X264_BFRAME_MAX*4 + X264_THREAD_MAX*2 + 16+4]; + x264_frame_t **current; + + /* For adaptive B decision */ x264_frame_t *last_nonb; @@ -628,6 +657,7 @@ struct x264_t #if VISUALIZE struct visualize_t *visualize; #endif + x264_lookahead_t *lookahead; }; // included at the end because it needs x264_t diff --git a/common/frame.c b/common/frame.c index de305d1..c155bbd 100644 --- a/common/frame.c +++ b/common/frame.c @@ -937,17 +937,17 @@ void x264_frame_push_unused( x264_t *h, x264_frame_t *frame ) assert( frame->i_reference_count > 0 ); frame->i_reference_count--; if( frame->i_reference_count == 0 ) - x264_frame_push( h->frames.unused, frame ); - assert( h->frames.unused[ sizeof(h->frames.unused) / sizeof(*h->frames.unused) - 1 ] == NULL ); + x264_synch_frame_list_put( &h->lookahead->unused, frame ); } x264_frame_t *x264_frame_pop_unused( x264_t *h ) { x264_frame_t *frame; - if( h->frames.unused[0] ) - frame = x264_frame_pop( h->frames.unused ); + if( h->lookahead->unused.list[0] ) + frame = x264_synch_frame_list_get( &h->lookahead->unused ); else frame = x264_frame_new( h ); + assert( frame->i_reference_count == 0 ); frame->i_reference_count = 1; frame->b_intra_calculated = 0; @@ -973,3 +973,126 @@ void x264_frame_sort( x264_frame_t **list, int b_dts ) } } while( !b_ok ); } + +void x264_frame_delete_list( x264_frame_t **frame_list ) +{ + int i; + + if ( !frame_list ) + return; + + for( i = 0; frame_list[i]; i++ ) + x264_frame_delete( frame_list[i] ); + + x264_free( frame_list ); +} + +int x264_synch_frame_list_init( synch_frame_list_t *slist, int max_size ) +{ + slist->i_max_size = max_size; + assert( slist->i_max_size ); + slist->list = x264_mallocz( (max_size + 3) * sizeof(x264_frame_t*) ); + if ( !slist->list ) + return X264_ERROR; +#ifdef HAVE_PTHREAD + if ( x264_pthread_mutex_init( &slist->mutex, NULL ) + || x264_pthread_cond_init( &slist->cv_full, NULL ) + || x264_pthread_cond_init( &slist->cv_empty, NULL ) ) + { + return X264_ERROR; + } +#endif + return X264_SUCCESS; +} + +void x264_synch_frame_list_invalidate( synch_frame_list_t *slist ) +{ + int i; + slist->i_max_size = -1; //safe -- not changed since initialization + + for( i = 0; slist->list[i]; i++ ) + x264_frame_delete( slist->list[i] ); + +#ifdef HAVE_PTHREAD + x264_pthread_cond_broadcast( &slist->cv_full ); + x264_pthread_cond_broadcast( &slist->cv_empty ); +#endif +} + +int x264_synch_frame_list_destroy( synch_frame_list_t *slist ) +{ + + if ( slist->i_max_size > 0 ) + return X264_ERROR; +#ifdef HAVE_PTHREAD + + if ( x264_pthread_mutex_destroy( &slist->mutex ) + || x264_pthread_cond_destroy( &slist->cv_full ) + || x264_pthread_cond_destroy( &slist->cv_empty ) ) + { + return X264_ERROR; + } +#endif + + x264_frame_delete_list( slist->list ); + slist->list = NULL; + + return X264_SUCCESS; +} + +void x264_synch_frame_list_put( synch_frame_list_t *slist, x264_frame_t *frame ) +{ + x264_pthread_mutex_lock( &slist->mutex ); + + if ( slist->i_max_size < 1 ) return; + if ( ( slist->i_max_size > 0 ) && (slist->list[slist->i_max_size - 1] ) ) + x264_pthread_cond_wait( &slist->cv_empty, &slist->mutex ); + + x264_frame_push( slist->list,frame ); + x264_pthread_mutex_unlock( &slist->mutex ); + + if ( x264_synch_frame_list_get_size(slist) >= slist->i_max_size) + x264_pthread_cond_broadcast( &slist->cv_full ); + +} + +x264_frame_t * x264_synch_frame_list_get( synch_frame_list_t *slist ) +{ + x264_pthread_mutex_lock( &slist->mutex ); + x264_frame_t *frame = NULL; + if ( slist->i_max_size < 1 ) return NULL; + + if ( ( slist->i_max_size > 0 ) && (!slist->list[0]) ) + x264_pthread_cond_wait( &slist->cv_full, &slist->mutex ); + + frame = x264_frame_shift( slist->list ); + x264_pthread_mutex_unlock( &slist->mutex ); + + x264_pthread_cond_broadcast( &slist->cv_empty ); + + return frame; +} + +int x264_synch_frame_list_get_size( synch_frame_list_t *slist ) +{ + int fno = 0; + + x264_pthread_mutex_lock( &slist->mutex ); + while (slist->list[fno]) fno++; + x264_pthread_mutex_unlock( &slist->mutex ); + return fno; +} + +void x264_synch_frame_list_sort_pts( synch_frame_list_t *slist ) +{ + x264_pthread_mutex_lock( &slist->mutex ); + x264_frame_sort_pts(slist->list); + x264_pthread_mutex_unlock( &slist->mutex ); +} + +void x264_synch_frame_list_unshift( synch_frame_list_t *slist, x264_frame_t *frame ) +{ + x264_pthread_mutex_lock( &slist->mutex ); + x264_frame_unshift( slist->list, frame ); + x264_pthread_mutex_unlock( &slist->mutex ); +} diff --git a/common/frame.h b/common/frame.h index aad77f5..a6f3a1d 100644 --- a/common/frame.h +++ b/common/frame.h @@ -93,6 +93,17 @@ typedef struct } x264_frame_t; +// synchronized frame list +typedef struct +{ + x264_frame_t **list; + char name[0x100]; + int i_max_size; + x264_pthread_mutex_t mutex; + x264_pthread_cond_t cv_full; + x264_pthread_cond_t cv_empty; +} synch_frame_list_t; + typedef void (*x264_deblock_inter_t)( uint8_t *pix, int stride, int alpha, int beta, int8_t *tc0 ); typedef void (*x264_deblock_intra_t)( uint8_t *pix, int stride, int alpha, int beta ); typedef struct @@ -135,7 +146,21 @@ x264_frame_t *x264_frame_shift( x264_frame_t **list ); void x264_frame_push_unused( x264_t *h, x264_frame_t *frame ); x264_frame_t *x264_frame_pop_unused( x264_t *h ); void x264_frame_sort( x264_frame_t **list, int b_dts ); +void x264_frame_delete_list( x264_frame_t **frame_list ); + +int x264_synch_frame_list_init( synch_frame_list_t *slist, int nelem ); +void x264_synch_frame_list_invalidate( synch_frame_list_t *slist ); +int x264_synch_frame_list_destroy( synch_frame_list_t *slist ); +void x264_synch_frame_list_put( synch_frame_list_t *slist, x264_frame_t *frame ); +x264_frame_t *x264_synch_frame_list_get( synch_frame_list_t *slist ); + +int x264_synch_frame_list_get_size( synch_frame_list_t *slist ); +void x264_synch_frame_list_sort_pts( synch_frame_list_t *slist ); +void x264_synch_frame_list_unshift( synch_frame_list_t *slist, x264_frame_t *frame ); + #define x264_frame_sort_dts(list) x264_frame_sort(list, 1) #define x264_frame_sort_pts(list) x264_frame_sort(list, 0) + + #endif diff --git a/common/osdep.h b/common/osdep.h index 168d6b2..8b49e1c 100644 --- a/common/osdep.h +++ b/common/osdep.h @@ -84,6 +84,14 @@ #define NOINLINE #endif +#if ( defined(__MINGW32__) || defined(_MSC_VER)) +#define usleep(t) + +#else +#include +#endif //usleep + + /* threads */ #if defined(SYS_BEOS) #include @@ -108,19 +116,25 @@ #endif //SYS_* #ifdef USE_REAL_PTHREAD -#define x264_pthread_t pthread_t -#define x264_pthread_create pthread_create -#define x264_pthread_join pthread_join -#define x264_pthread_mutex_t pthread_mutex_t -#define x264_pthread_mutex_init pthread_mutex_init -#define x264_pthread_mutex_destroy pthread_mutex_destroy -#define x264_pthread_mutex_lock pthread_mutex_lock -#define x264_pthread_mutex_unlock pthread_mutex_unlock -#define x264_pthread_cond_t pthread_cond_t -#define x264_pthread_cond_init pthread_cond_init -#define x264_pthread_cond_destroy pthread_cond_destroy -#define x264_pthread_cond_broadcast pthread_cond_broadcast -#define x264_pthread_cond_wait pthread_cond_wait +#define x264_pthread_t pthread_t +#define x264_pthread_create pthread_create +#define x264_pthread_join pthread_join +#define x264_pthread_mutex_t pthread_mutex_t +#define x264_pthread_mutex_init pthread_mutex_init +#define x264_pthread_mutex_destroy pthread_mutex_destroy +#define x264_pthread_mutex_lock pthread_mutex_lock +#define x264_pthread_mutex_unlock pthread_mutex_unlock +#define x264_pthread_cond_t pthread_cond_t +#define x264_pthread_cond_init pthread_cond_init +#define x264_pthread_cond_destroy pthread_cond_destroy +#define x264_pthread_cond_broadcast pthread_cond_broadcast +#define x264_pthread_cond_signal pthread_cond_signal +#define x264_pthread_cond_wait pthread_cond_wait +#define x264_pthread_cond_timedwait pthread_cond_timedwait +#define x264_pthread_self pthread_self +#define x264_pthread_mutexattr_init pthread_mutexattr_init +#define x264_pthread_mutexattr_destroy pthread_mutexattr_destroy +#define x264_pthread_mutexattr_settype pthread_mutexattr_settype #else #define x264_pthread_mutex_t int #define x264_pthread_mutex_init(m,f) @@ -131,7 +145,10 @@ #define x264_pthread_cond_init(c,f) #define x264_pthread_cond_destroy(c) #define x264_pthread_cond_broadcast(c) +#define x264_pthread_cond_signal(c) #define x264_pthread_cond_wait(c,m) +#define x264_pthread_cond_timedwait(c,m,t) usleep(100) +#define x264_pthread_self() #endif #define WORD_SIZE sizeof(void*) diff --git a/encoder/analyse.h b/encoder/analyse.h index b8c828f..f89dbd7 100644 --- a/encoder/analyse.h +++ b/encoder/analyse.h @@ -27,4 +27,13 @@ void x264_macroblock_analyse( x264_t *h ); void x264_slicetype_decide( x264_t *h ); +// lookahead functions +int x264_lookahead_init( x264_t *h ); +int x264_lookahead_empty( x264_t *h ); +void x264_lookahead_put_frame( x264_t *h, x264_frame_t *frame ); +void x264_lookahead_get_frames( x264_t *h ); +int x264_lookahead_done_frames( x264_t *h ); +int x264_lookahead_try_frames( x264_t *h ); +int x264_lookahead_destroy( x264_t *h, int thread_handle); + #endif diff --git a/encoder/encoder.c b/encoder/encoder.c index 593f780..53071b4 100644 --- a/encoder/encoder.c +++ b/encoder/encoder.c @@ -372,6 +372,9 @@ static int x264_validate_parameters( x264_t *h ) #endif } + if( h->param.rc.i_lookahead < 0 || h->param.i_threads < 2 ) + h->param.rc.i_lookahead = 0; + if( h->param.b_interlaced ) { if( h->param.analyse.i_me_method >= X264_ME_ESA ) @@ -699,9 +702,9 @@ x264_t *x264_encoder_open ( x264_param_t *param ) /* Init frames. */ if( h->param.i_bframe_adaptive == X264_B_ADAPT_TRELLIS ) - h->frames.i_delay = X264_MAX(h->param.i_bframe,3)*4 + h->param.i_threads - 1; + h->frames.i_delay = h->param.rc.i_lookahead + X264_MAX(h->param.i_bframe,3)*4 + h->param.i_threads - 1; else - h->frames.i_delay = h->param.i_bframe + h->param.i_threads - 1; + h->frames.i_delay = h->param.rc.i_lookahead + h->param.i_bframe + h->param.i_threads - 1; h->frames.i_max_ref0 = h->param.i_frame_reference; h->frames.i_max_ref1 = h->sps->vui.i_num_reorder_frames; h->frames.i_max_dpb = h->sps->vui.i_max_dec_frame_buffering; @@ -717,6 +720,8 @@ x264_t *x264_encoder_open ( x264_param_t *param ) h->frames.i_input = 0; h->frames.last_nonb = NULL; + h->frames.current = x264_mallocz( (h->param.rc.i_lookahead + h->param.i_bframe + 3) * sizeof(x264_frame_t*) ); + h->i_ref0 = 0; h->i_ref1 = 0; @@ -768,17 +773,23 @@ x264_t *x264_encoder_open ( x264_param_t *param ) h->thread[0] = h; h->i_thread_num = 0; - for( i = 1; i < h->param.i_threads; i++ ) - h->thread[i] = x264_malloc( sizeof(x264_t) ); + for( i = 1; i <= h->param.i_threads; i++ ) + h->thread[i] = x264_mallocz( sizeof(x264_t) ); - for( i = 0; i < h->param.i_threads; i++ ) + if( x264_lookahead_init( h ) != X264_SUCCESS ) { - if( i > 0 ) - *h->thread[i] = *h; - h->thread[i]->fdec = x264_frame_pop_unused( h ); - h->thread[i]->out.p_bitstream = x264_malloc( h->out.i_bitstream ); - if( x264_macroblock_cache_init( h->thread[i] ) < 0 ) - return NULL; + x264_log(h, X264_LOG_ERROR, "%s:%d: x264_lookahead_init( h ) failed\n", __FILE__, __LINE__); + return NULL; + } + + for( i = 0; i <= h->param.i_threads; i++ ) + { + if( i > 0 ) + *h->thread[i] = *h; + h->thread[i]->fdec = x264_frame_pop_unused( h ); + h->thread[i]->out.p_bitstream = x264_malloc( h->out.i_bitstream ); + if( x264_macroblock_cache_init( h->thread[i] ) < 0 ) + return NULL; } if( x264_ratecontrol_new( h ) < 0 ) @@ -1078,8 +1089,8 @@ static inline void x264_reference_update( x264_t *h ) } /* adaptive B decision needs a pointer, since it can't use the ref lists */ - if( h->sh.i_type != SLICE_TYPE_B ) - h->frames.last_nonb = h->fdec; + if( (h->param.rc.i_lookahead == 0) && (h->sh.i_type != SLICE_TYPE_B) ) + h->lookahead->last_nonb_planned = h->fdec; /* move frame in the buffer */ x264_frame_push( h->frames.reference, h->fdec ); @@ -1343,6 +1354,47 @@ static int x264_slices_write( x264_t *h ) return 0; } +void parse_qpfile( cli_opt_t *opt, x264_picture_t *pic, int i_frame ) +{ + int num = -1, qp, ret; + char type; + uint64_t file_pos; + while( num < i_frame ) + { + file_pos = ftell( opt->qpfile ); + ret = fscanf( opt->qpfile, "%d %c %d\n", &num, &type, &qp ); + if( num > i_frame || ret == EOF ) + { + pic->i_type = X264_TYPE_AUTO; + pic->i_qpplus1 = 0; + fseek( opt->qpfile , file_pos , SEEK_SET ); + break; + } + if( num < i_frame ) + continue; + pic->i_qpplus1 = qp+1; + if ( type == 'I' ) pic->i_type = X264_TYPE_IDR; + else if( type == 'i' ) pic->i_type = X264_TYPE_I; + else if( type == 'P' ) pic->i_type = X264_TYPE_P; + else if( type == 'B' ) pic->i_type = X264_TYPE_BREF; + else if( type == 'b' ) pic->i_type = X264_TYPE_B; + else ret = 0; + if( ret != 3 || qp < -1 || qp > 51 ) + { + fprintf( stderr, "x264 [error]: can't parse qpfile for frame %d\n", i_frame ); + fclose( opt->qpfile ); + opt->qpfile = NULL; + pic->i_type = X264_TYPE_AUTO; + pic->i_qpplus1 = 0; + break; + } + } +} + +int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); + + + /**************************************************************************** * x264_encoder_encode: * XXX: i_poc : is the poc of the current given picture @@ -1407,55 +1459,38 @@ int x264_encoder_encode( x264_t *h, h->param.i_height != 16 * h->sps->i_mb_height ) x264_frame_expand_border_mod16( h, fenc ); - fenc->i_frame = h->frames.i_input++; - - x264_frame_push( h->frames.next, fenc ); - if( h->frames.b_have_lowres ) x264_frame_init_lowres( h, fenc ); if( h->param.rc.i_aq_mode ) x264_adaptive_quant_frame( h, fenc ); - if( h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads ) - { - /* Nothing yet to encode */ - /* waiting for filling bframe buffer */ - pic_out->i_type = X264_TYPE_AUTO; - return 0; - } + /* 2: Place the frame into the queue for its slice type decision */ + x264_lookahead_put_frame( h, fenc ); } - if( h->frames.current[0] == NULL ) + /* 3: The picture is analyzed in the lookahead (or when x264_lookahead_get_frames is called if threads==1 */ + if( !h->lookahead->b_thread_active && h->lookahead->i_input <= h->frames.i_delay + 1 - h->param.i_threads ) { - int bframes = 0; - /* 2: Select frame types */ - if( h->frames.next[0] == NULL ) - { - x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out ); - return 0; - } + /* Nothing yet to encode */ + /* waiting for filling bframe buffer */ + pic_out->i_type = X264_TYPE_AUTO; + return 0; + } - x264_stack_align( x264_slicetype_decide, h ); + if ( !h->frames.current[0] ) + x264_lookahead_get_frames( h ); - /* 3: move some B-frames and 1 non-B to encode queue */ - while( IS_X264_TYPE_B( h->frames.next[bframes]->i_type ) ) - bframes++; - x264_frame_push( h->frames.current, x264_frame_shift( &h->frames.next[bframes] ) ); - /* FIXME: when max B-frames > 3, BREF may no longer be centered after GOP closing */ - if( h->param.b_bframe_pyramid && bframes > 1 ) - { - x264_frame_t *mid = x264_frame_shift( &h->frames.next[bframes/2] ); - mid->i_type = X264_TYPE_BREF; - x264_frame_push( h->frames.current, mid ); - bframes--; - } - while( bframes-- ) - x264_frame_push( h->frames.current, x264_frame_shift( h->frames.next ) ); + if( !h->frames.current[0] && x264_lookahead_empty( h ) ) + { + x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out ); + return 0; } /* ------------------- Get frame to be encoded ------------------------- */ /* 4: get picture to encode */ + if( h->frames.current[0] == NULL) + return 0; /* Nothing to do yet - legacy - this should never happen because of cond_wait in x264_lookahead_get_frames */ h->fenc = x264_frame_shift( h->frames.current ); if( h->fenc == NULL ) { @@ -1469,7 +1504,9 @@ do_encode: if( h->fenc->i_type == X264_TYPE_IDR ) { +// fprintf(stderr, "%d(IDR)\n", h->fenc->i_frame); h->frames.i_last_idr = h->fenc->i_frame; + h->i_frame_num = 0; } /* ------------------- Setup frame context ----------------------------- */ @@ -1485,24 +1522,28 @@ do_encode: } else if( h->fenc->i_type == X264_TYPE_I ) { +// fprintf(stderr, "%d(I)\n", h->fenc->i_frame); i_nal_type = NAL_SLICE; i_nal_ref_idc = NAL_PRIORITY_HIGH; /* Not completely true but for now it is (as all I/P are kept as ref)*/ h->sh.i_type = SLICE_TYPE_I; } else if( h->fenc->i_type == X264_TYPE_P ) { +// fprintf(stderr, "%d(P)\n", h->fenc->i_frame); i_nal_type = NAL_SLICE; i_nal_ref_idc = NAL_PRIORITY_HIGH; /* Not completely true but for now it is (as all I/P are kept as ref)*/ h->sh.i_type = SLICE_TYPE_P; } else if( h->fenc->i_type == X264_TYPE_BREF ) { +// fprintf(stderr, "%d(B)\n", h->fenc->i_frame); i_nal_type = NAL_SLICE; i_nal_ref_idc = NAL_PRIORITY_HIGH; /* maybe add MMCO to forget it? -> low */ h->sh.i_type = SLICE_TYPE_B; } else /* B frame */ { +// fprintf(stderr, "%d(b)\n", h->fenc->i_frame); i_nal_type = NAL_SLICE; i_nal_ref_idc = NAL_PRIORITY_DISPOSABLE; h->sh.i_type = SLICE_TYPE_B; @@ -1666,7 +1707,7 @@ do_encode: if( h->param.i_bframe_adaptive || b > 1 ) h->fenc->i_type = X264_TYPE_AUTO; x264_frame_sort_pts( h->frames.current ); - x264_frame_unshift( h->frames.next, h->fenc ); + x264_synch_frame_list_unshift( &h->lookahead->next, h->fenc ); h->fenc = h->frames.current[b-1]; h->frames.current[b-1] = NULL; h->fenc->i_type = X264_TYPE_P; @@ -1675,17 +1716,14 @@ do_encode: /* Do IDR if needed */ else if( i_gop_size >= h->param.i_keyint_min ) { - /* Reset */ - h->i_frame_num = 0; - /* Reinit field of fenc */ h->fenc->i_type = X264_TYPE_IDR; h->fenc->i_poc = 0; /* Put enqueued frames back in the pool */ while( h->frames.current[0] ) - x264_frame_push( h->frames.next, x264_frame_shift( h->frames.current ) ); - x264_frame_sort_pts( h->frames.next ); + x264_synch_frame_list_put( &h->lookahead->next, x264_frame_shift( h->frames.current ) ); + x264_synch_frame_list_sort_pts( &h->lookahead->next ); } else { @@ -1881,6 +1919,8 @@ void x264_encoder_close ( x264_t *h ) || h->stat.i_mb_count[SLICE_TYPE_P][I_PCM] || h->stat.i_mb_count[SLICE_TYPE_B][I_PCM]; + x264_lookahead_destroy(h->thread[h->param.i_threads], h->thread_handle); + for( i=0; iparam.i_threads; i++ ) { // don't strictly have to wait for the other threads, but it's simpler than canceling them @@ -2095,16 +2135,6 @@ void x264_encoder_close ( x264_t *h ) assert( h->frames.current[i]->i_reference_count == 1 ); x264_frame_delete( h->frames.current[i] ); } - for( i = 0; h->frames.next[i]; i++ ) - { - assert( h->frames.next[i]->i_reference_count == 1 ); - x264_frame_delete( h->frames.next[i] ); - } - for( i = 0; h->frames.unused[i]; i++ ) - { - assert( h->frames.unused[i]->i_reference_count == 0 ); - x264_frame_delete( h->frames.unused[i] ); - } h = h->thread[0]; @@ -2129,4 +2159,5 @@ void x264_encoder_close ( x264_t *h ) x264_free( h->thread[i]->out.p_bitstream ); x264_free( h->thread[i] ); } + x264_free( h->frames.current ); } diff --git a/encoder/lookahead.c b/encoder/lookahead.c new file mode 100644 index 0000000..39ca9d6 --- /dev/null +++ b/encoder/lookahead.c @@ -0,0 +1,373 @@ +/***************************************************************************** + * c: h264 encoder library + ***************************************************************************** + * Copyright (C) 2008 Avail Media + * + * Authors: Alex Giladi + * Michael Kazmier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/* LOOKAHEAD (threaded and non-threaded mode + * + * Lookahead types: + * [1] Slice type / scene cut; + * + * In non-threaded mode, we run the existing slicetype decision code as it was + * In threaded mode, we run in a separate thread, that lives between the calls + * to x264_encoder_open() and x264_encoder_close(), and performs lookahead for + * the number of frames specified in rc_lookahead + */ +#include "common/osdep.h" +#include "common/common.h" +#include "common/cpu.h" + +#include "analyse.h" +#include "ratecontrol.h" + +void x264_lookahead_thread( x264_t *h ); +void x264_lookahead_thread_worker( x264_t *h ); +void x264_lookahead_shift(x264_t *h, x264_frame_t **dst , x264_frame_t **src); + +#ifdef DEBUG_LOOKAHEAD +int _print_slice_type(int type) +{ + int ret = 'u'; + switch ( type ) + { + case X264_TYPE_IDR: + ret = 'I'; + break; + case X264_TYPE_I: + ret = 'i'; + break; + case X264_TYPE_P: + ret = 'P'; + break; + case X264_TYPE_BREF: + ret = 'B'; + break; + case X264_TYPE_B: + ret = 'b'; + break; + default: + ret = 'u'; + } + return ret; +} + +void _print_queue_state( x264_frame_t **queue ) +{ + fprintf(stderr,"print_queue_state: "); + int i; + for ( i = 0; queue[i]; i++ ) + { + fprintf(stderr," %d(%c)", queue[i]->i_frame, _print_slice_type(queue[i]->i_type) ); + } + fprintf(stderr,"\n"); +} +#endif + + +void x264_lookahead_shift(x264_t *h, x264_frame_t **dst , x264_frame_t **src) +{ + int bframes = 0; + int i_frames = 0; + + while( src[i_frames] ) + { + if ( IS_X264_TYPE_B( src[i_frames]->i_type ) ) + bframes++; + else + break; + i_frames++; + } + x264_frame_push( dst, x264_frame_shift( &src[bframes] ) ); + h->frames.i_input++; + /* FIXME: when max B-frames > 3, BREF may no longer be centered after GOP closing */ + if( h->param.b_bframe_pyramid && bframes > 1 ) + { + x264_frame_t *mid = x264_frame_shift( &src[bframes/2] ); + mid->i_type = X264_TYPE_BREF; + x264_frame_push( dst, mid ); + h->frames.i_input++; + bframes--; + } + while( bframes-- ) + { + x264_frame_push( dst, x264_frame_shift( src ) ); + h->frames.i_input++; + } +} + +int x264_lookahead_init( x264_t *h ) +{ + x264_lookahead_t *h_lookahead = x264_mallocz( sizeof( x264_lookahead_t ) ); + int i; + for( i=0; i <= h->param.i_threads; i++ ) + h->thread[i]->lookahead = h_lookahead; + + h_lookahead->b_thread_active = 0; + h_lookahead->opt = NULL; + h_lookahead->i_input = 0; + h_lookahead->thread_handle = 0; + h_lookahead->b_exit_thread = 1; + h_lookahead->i_last_idr_planned = - h->param.i_keyint_max; + h_lookahead->last_nonb_planned = NULL; + h_lookahead->ofbuf.i_max_size = -1; + +#ifdef HAVE_PTHREAD + x264_pthread_mutex_init( &h_lookahead->mutex, NULL ); +#endif + + if ( ( x264_synch_frame_list_init( &h_lookahead->ofbuf,h->param.rc.i_lookahead + h->param.i_threads + h->param.i_bframe + 4 ) != X264_SUCCESS ) + || ( x264_synch_frame_list_init( &h_lookahead->next,h->frames.i_delay + h->param.i_threads*2 + 4) != X264_SUCCESS ) + || ( x264_synch_frame_list_init( &h_lookahead->unused,h_lookahead->next.i_max_size*2) != X264_SUCCESS )) + { + x264_log( h, X264_LOG_ERROR, + "failed to initialize synchronized lists\n"); + + return X264_ERROR; + } + + sprintf(h_lookahead->ofbuf.name, "ofbuf"); + sprintf(h_lookahead->next.name, "next"); + sprintf(h_lookahead->unused.name, "unused"); + if ( h->param.i_bframe == 0 ) + { + x264_log( h, X264_LOG_ERROR, "currently slicetype lookahead does not work with bframes=0 and is disabled\n"); + h->param.rc.i_lookahead = 0; + return X264_SUCCESS; + } + + if ( ( h->param.rc.i_lookahead < 1 ) || ( h->param.i_threads < 2 ) ) + return X264_SUCCESS; + +#ifdef HAVE_PTHREAD + h_lookahead->b_exit_thread = 0; + + if ( x264_pthread_create( + &h->lookahead->thread_handle, NULL, + (void *)x264_lookahead_thread, (void *)h->thread[h->param.i_threads] ) ) + { + x264_log( h, X264_LOG_ERROR, + "failed to create lookahead thread\n"); + return X264_ERROR; + } + + while ( !h_lookahead->b_thread_active ) + usleep(100); +#endif //HAVE_PTHREAD + + return X264_SUCCESS; +} + + +void x264_lookahead_put_frame( x264_t *h, x264_frame_t *frame ) +{ + frame->i_frame = h->lookahead->i_input++;; + x264_synch_frame_list_put( &h->lookahead->next, frame ); +} + +int x264_lookahead_empty( x264_t *h ) +{ + return ( ( x264_synch_frame_list_get_size( &h->lookahead->ofbuf ) == 0 ) + && ( x264_synch_frame_list_get_size( &h->lookahead->next ) == 0 ) ); +} + +void x264_lookahead_get_frames( x264_t *h ) +{ + if ( h->lookahead->b_thread_active ) + { + x264_pthread_mutex_lock( &h->lookahead->ofbuf.mutex ); + if ( !h->lookahead->ofbuf.list[0] ) + x264_pthread_cond_wait( &h->lookahead->ofbuf.cv_full, &h->lookahead->ofbuf.mutex ); + + x264_lookahead_shift( h, h->frames.current, h->lookahead->ofbuf.list); + x264_pthread_mutex_unlock( &h->lookahead->ofbuf.mutex ); + x264_pthread_cond_broadcast( &h->lookahead->ofbuf.cv_empty ); + } + else + { + if ( h->frames.current[0] != NULL || !h->lookahead->next.list[0] ) + return; + + /* 3: Make the decision and push decided frames to the h->frames.current queue for encoding */ + x264_stack_align( x264_slicetype_decide, h ); + + int bframes=0; + while( IS_X264_TYPE_B( h->lookahead->next.list[bframes]->i_type ) ) + bframes++; + + h->lookahead->last_nonb_planned = h->lookahead->next.list[bframes]; + + while (bframes--) + x264_frame_push( h->lookahead->ofbuf.list, x264_frame_shift( h->lookahead->next.list) ); // move all b frames + x264_frame_push( h->lookahead->ofbuf.list, x264_frame_shift( h->lookahead->next.list) ); // and the last non-brframe +#ifdef DEBUG_LOOKAHEAD + fprintf (stderr, "ofbuf max size=%d size=%d ", h->lookahead->ofbuf.i_max_size, x264_synch_frame_list_get_size( &h->lookahead->ofbuf ) ); + if (h->lookahead->ofbuf.list[0]) + _print_queue_state(h->lookahead->ofbuf.list); + fprintf (stderr, "next size=%d ", x264_synch_frame_list_get_size( &h->lookahead->next ) ); + if (h->lookahead->next.list[0]) + _print_queue_state(h->lookahead->next.list); +#endif + x264_lookahead_shift( h, h->frames.current, h->lookahead->ofbuf.list); // Now make all b and the last non-bframe available to the current list + } +} + +void x264_lookahead_thread( x264_t *h ) +{ +#ifdef HAVE_MMX + /* Misalign mask has to be set separately for each thread. */ + if( h->param.cpu&X264_CPU_SSE_MISALIGN ) + x264_cpu_mask_misalign_sse(); +#endif + x264_stack_align( x264_lookahead_thread_worker, h ); +} + +int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); + +void _x264_lookahead_slicetype_decide (x264_t *h) +{ + x264_pthread_mutex_lock( &h->lookahead->next.mutex ); + x264_stack_align( x264_slicetype_decide, h ); + int bframes=0; + while( IS_X264_TYPE_B( h->lookahead->next.list[bframes]->i_type ) ) + bframes++; + h->lookahead->last_nonb_planned = h->lookahead->next.list[bframes]; // Set in x264_reference_update when not threading + + x264_pthread_mutex_lock( &h->lookahead->ofbuf.mutex ); + while ( ( h->lookahead->ofbuf.i_max_size > 0 ) && (h->lookahead->ofbuf.list[h->lookahead->ofbuf.i_max_size - bframes - 1] ) ) + x264_pthread_cond_wait( &h->lookahead->ofbuf.cv_empty, &h->lookahead->ofbuf.mutex ); + + while (bframes--) + x264_frame_push( h->lookahead->ofbuf.list, x264_frame_shift( h->lookahead->next.list) ); // move all b frames + + x264_frame_push( h->lookahead->ofbuf.list, x264_frame_shift( h->lookahead->next.list) ); // and the last non-bframe + + x264_pthread_mutex_unlock( &h->lookahead->ofbuf.mutex ); + x264_pthread_mutex_unlock( &h->lookahead->next.mutex ); + +#ifdef DEBUG_LOOKAHEAD + fprintf (stderr, "ofbuf size=%d ", x264_synch_frame_list_get_size( &h->lookahead->ofbuf ) ); + if (h->lookahead->ofbuf.list[0]) + _print_queue_state(h->lookahead->ofbuf.list); + fprintf (stderr, "next size=%d ", x264_synch_frame_list_get_size( &h->lookahead->next ) ); + if (h->lookahead->next.list[0]) + _print_queue_state(h->lookahead->next.list); +#endif + + x264_pthread_cond_broadcast( &h->lookahead->ofbuf.cv_full ); +} +void x264_lookahead_thread_worker( x264_t *h) +{ + char buf[200]; + h->lookahead->b_thread_active = 1; + + x264_picture_t pic; + int i_frame, i_file, i_shifted; + int64_t i_start; + + while ( h->lookahead->opt == NULL ) + usleep (100); + + /* Create a new pic */ + x264_picture_alloc( &pic, X264_CSP_I420, h->param.i_width, h->param.i_height ); + if( h->lookahead->opt->b_progress ) + { + sprintf( buf, "Loading lookahead buffer..."); + fprintf( stderr, "%s \r", buf ); + fflush( stderr ); // needed in windows + } + i_start = x264_mdate(); + + /* Read frames */ + for( i_frame = 0, i_file = 0, i_shifted = 0; h->lookahead->b_exit_thread == 0 && (i_frame < h->param.i_frame_total || h->param.i_frame_total == 0); ) + { + if( p_read_frame( &pic, h->lookahead->opt->hin, i_frame + h->lookahead->opt->i_seek ) ) + break; + + pic.i_pts = (int64_t)i_frame * h->param.i_fps_den; + + if( h->lookahead->opt->qpfile ) + parse_qpfile( h->lookahead->opt, &pic, i_frame + h->lookahead->opt->i_seek ); + else + { + /* Do not force any parameters */ + pic.i_type = X264_TYPE_AUTO; + pic.i_qpplus1 = 0; + } + + /* 1: Copy the picture to a frame and move it to a buffer */ + x264_frame_t *fenc = x264_frame_pop_unused( h ); + + if( x264_frame_copy_picture( h, fenc, &pic ) < 0 ) + return; + + if( h->param.i_width != 16 * h->sps->i_mb_width || + h->param.i_height != 16 * h->sps->i_mb_height ) + x264_frame_expand_border_mod16( h, fenc ); + + if( h->frames.b_have_lowres ) + x264_frame_init_lowres( h, fenc ); + + if( h->param.rc.i_aq_mode ) + x264_adaptive_quant_frame( h, fenc ); + + fenc->i_frame = h->lookahead->i_input = i_frame++; + + /* 2: Place the frame into the queue for its slice type decision */ + x264_synch_frame_list_put(&h->lookahead->next, fenc ); + + /* 3: Make the decision and push decided frames to the h->frames.current queue for encoding */ + if ( x264_synch_frame_list_get_size( &h->lookahead->next ) > h->frames.i_delay + 1 ) + _x264_lookahead_slicetype_decide (h); + } + x264_picture_clean( &pic ); + + while ( h->lookahead->b_exit_thread == 0 && x264_synch_frame_list_get_size( &h->lookahead->next ) > 0 ) + _x264_lookahead_slicetype_decide (h); + while ( h->lookahead->b_exit_thread == 0 && x264_synch_frame_list_get_size( &h->lookahead->ofbuf ) > 0 ) + usleep (100); + h->lookahead->b_thread_active = 0; +} + + +int x264_lookahead_destroy( x264_t *h, int thread_handle ) +{ + +#ifdef HAVE_PTHREAD + if ( h->b_thread_active ) + { + h->lookahead->b_exit_thread = 1; + usleep(100); + + x264_synch_frame_list_invalidate(&h->lookahead->ofbuf); + x264_synch_frame_list_invalidate(&h->lookahead->next); + x264_synch_frame_list_invalidate(&h->lookahead->unused); + + x264_pthread_join( thread_handle, NULL ); + h->lookahead->thread_handle = 0; + + x264_synch_frame_list_destroy(&h->lookahead->ofbuf); + x264_synch_frame_list_destroy(&h->lookahead->next); + x264_synch_frame_list_destroy(&h->lookahead->unused); + } +#endif // HAVE_PTHREAD + + return 0; +} diff --git a/encoder/ratecontrol.c b/encoder/ratecontrol.c index c69ebb5..6b28ad3 100644 --- a/encoder/ratecontrol.c +++ b/encoder/ratecontrol.c @@ -271,8 +271,7 @@ int x264_ratecontrol_new( x264_t *h ) x264_emms(); - rc = h->rc = x264_malloc( h->param.i_threads * sizeof(x264_ratecontrol_t) ); - memset( rc, 0, h->param.i_threads * sizeof(x264_ratecontrol_t) ); + rc = h->rc = x264_mallocz( h->param.i_threads * sizeof(x264_ratecontrol_t) ); rc->b_abr = h->param.rc.i_rc_method != X264_RC_CQP && !h->param.rc.b_stat_read; rc->b_2pass = h->param.rc.i_rc_method == X264_RC_ABR && h->param.rc.b_stat_read; @@ -490,8 +489,7 @@ int x264_ratecontrol_new( x264_t *h ) return -1; } - rc->entry = (ratecontrol_entry_t*) x264_malloc(rc->num_entries * sizeof(ratecontrol_entry_t)); - memset(rc->entry, 0, rc->num_entries * sizeof(ratecontrol_entry_t)); + rc->entry = (ratecontrol_entry_t*) x264_mallocz(rc->num_entries * sizeof(ratecontrol_entry_t)); /* init all to skipped p frames */ for(i=0; inum_entries; i++) diff --git a/encoder/slicetype.c b/encoder/slicetype.c index e00be6f..fd53e23 100644 --- a/encoder/slicetype.c +++ b/encoder/slicetype.c @@ -439,7 +439,7 @@ static int scenecut( x264_t *h, x264_frame_t *frame, int pdist ) int icost = frame->i_cost_est[0][0]; int pcost = frame->i_cost_est[pdist][0]; float f_bias; - int i_gop_size = frame->i_frame - h->frames.i_last_idr; + int i_gop_size = frame->i_frame - h->lookahead->i_last_idr_planned; float f_thresh_max = h->param.i_scenecut_threshold / 100.0; /* magic numbers pulled out of thin air */ float f_thresh_min = f_thresh_max * h->param.i_keyint_min @@ -483,21 +483,22 @@ static void x264_slicetype_analyse( x264_t *h ) int i_mb_count = NUM_MBS; int cost1p0, cost2p0, cost1b1, cost2p1; int idr_frame_type; + x264_frame_t *last_nonb = h->lookahead->last_nonb_planned; assert( h->frames.b_have_lowres ); - if( !h->frames.last_nonb ) + if( !last_nonb ) return; - frames[0] = h->frames.last_nonb; - for( j = 0; h->frames.next[j] && h->frames.next[j]->i_type == X264_TYPE_AUTO; j++ ) - frames[j+1] = h->frames.next[j]; - keyint_limit = h->param.i_keyint_max - frames[0]->i_frame + h->frames.i_last_idr - 1; + + frames[0] = last_nonb; + for( j = 0; h->lookahead->next.list[j] && h->lookahead->next.list[j]->i_type == X264_TYPE_AUTO && j <= X264_BFRAME_MAX*4+3; j++ ) + frames[j+1] = h->lookahead->next.list[j]; + keyint_limit = h->param.i_keyint_max - frames[0]->i_frame + h->lookahead->i_last_idr_planned - 1; num_frames = X264_MIN( j, keyint_limit ); if( num_frames == 0 ) return; - x264_lowres_context_init( h, &a ); - idr_frame_type = frames[1]->i_frame - h->frames.i_last_idr >= h->param.i_keyint_min ? X264_TYPE_IDR : X264_TYPE_I; + idr_frame_type = frames[1]->i_frame - h->lookahead->i_last_idr_planned >= h->param.i_keyint_min ? X264_TYPE_IDR : X264_TYPE_I; if( num_frames == 1 ) { @@ -552,12 +553,10 @@ no_b_frames: if( cost1p0 + cost2p0 < cost1b1 + cost2p1 ) goto no_b_frames; - // arbitrary and untuned #define INTER_THRESH 300 #define P_SENS_BIAS (50 - h->param.i_bframe_bias) frames[1]->i_type = X264_TYPE_B; - for( j = 2; j <= X264_MIN( h->param.i_bframe, num_frames-1 ); j++ ) { int pthresh = X264_MAX(INTER_THRESH - P_SENS_BIAS * (j-1), INTER_THRESH/10); @@ -580,15 +579,15 @@ void x264_slicetype_decide( x264_t *h ) int bframes; int i; - if( h->frames.next[0] == NULL ) + if( h->lookahead->next.list[0] == NULL ) return; if( h->param.rc.b_stat_read ) { /* Use the frame types from the first pass */ - for( i = 0; h->frames.next[i] != NULL; i++ ) - h->frames.next[i]->i_type = - x264_ratecontrol_slice_type( h, h->frames.next[i]->i_frame ); + for( i = 0; h->lookahead->next.list[i] != NULL; i++ ) + h->lookahead->next.list[i]->i_type = + x264_ratecontrol_slice_type( h, h->lookahead->next.list[i]->i_frame ); } else if( (h->param.i_bframe && h->param.i_bframe_adaptive) || h->param.b_pre_scenecut ) @@ -596,10 +595,9 @@ void x264_slicetype_decide( x264_t *h ) for( bframes = 0;; bframes++ ) { - frm = h->frames.next[bframes]; - + frm = h->lookahead->next.list[bframes]; /* Limit GOP size */ - if( frm->i_frame - h->frames.i_last_idr >= h->param.i_keyint_max ) + if( frm->i_frame - h->lookahead->i_last_idr_planned >= h->param.i_keyint_max ) { if( frm->i_type == X264_TYPE_AUTO ) frm->i_type = X264_TYPE_IDR; @@ -609,19 +607,16 @@ void x264_slicetype_decide( x264_t *h ) if( frm->i_type == X264_TYPE_IDR ) { /* Close GOP */ + h->lookahead->i_last_idr_planned = frm->i_frame; if( bframes > 0 ) { bframes--; - h->frames.next[bframes]->i_type = X264_TYPE_P; - } - else - { - h->i_frame_num = 0; + h->lookahead->next.list[bframes]->i_type = X264_TYPE_P; } } if( bframes == h->param.i_bframe - || h->frames.next[bframes+1] == NULL ) + || h->lookahead->next.list[bframes+1] == NULL ) { if( IS_X264_TYPE_B( frm->i_type ) ) x264_log( h, X264_LOG_WARNING, "specified frame type is not compatible with max B-frames\n" ); @@ -630,7 +625,8 @@ void x264_slicetype_decide( x264_t *h ) frm->i_type = X264_TYPE_P; } - if( frm->i_type == X264_TYPE_AUTO ) frm->i_type = X264_TYPE_B; + if( frm->i_type == X264_TYPE_AUTO ) + frm->i_type = X264_TYPE_B; else if( !IS_X264_TYPE_B( frm->i_type ) ) break; } } diff --git a/muxers.h b/muxers.h index aaede1c..df030be 100644 --- a/muxers.h +++ b/muxers.h @@ -24,7 +24,7 @@ #ifndef X264_MUXERS_H #define X264_MUXERS_H -typedef void *hnd_t; + int open_file_yuv( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); int get_frame_total_yuv( hnd_t handle ); diff --git a/x264.c b/x264.c index 04bb44c..b99aec0 100644 --- a/x264.c +++ b/x264.c @@ -56,18 +56,11 @@ static void SigIntHandler( int a ) b_ctrl_c = 1; } -typedef struct { - int b_progress; - int i_seek; - hnd_t hin; - hnd_t hout; - FILE *qpfile; -} cli_opt_t; + /* input file operation function pointers */ int (*p_open_infile)( char *psz_filename, hnd_t *p_handle, x264_param_t *p_param ); int (*p_get_frame_total)( hnd_t handle ); -int (*p_read_frame)( x264_picture_t *p_pic, hnd_t handle, int i_frame ); int (*p_close_infile)( hnd_t handle ); /* output file operation function pointers */ @@ -197,6 +190,7 @@ static void Help( x264_param_t *defaults, int b_longhelp ) H0( " --ratetol Allowed variance of average bitrate [%.1f]\n", defaults->rc.f_rate_tolerance ); H0( " --ipratio QP factor between I and P [%.2f]\n", defaults->rc.f_ip_factor ); H0( " --pbratio QP factor between P and B [%.2f]\n", defaults->rc.f_pb_factor ); + H0( " --lookahead Buffer within which to run VBV planning [%d]\n", defaults->rc.i_lookahead ); H1( " --chroma-qp-offset QP difference between chroma and luma [%d]\n", defaults->analyse.i_chroma_qp_offset ); H1( " --aq-mode AQ method [%d]\n" " - 0: Disabled\n" @@ -444,6 +438,7 @@ static int Parse( int argc, char **argv, { "vbv-init", required_argument,NULL, 0 }, { "ipratio", required_argument, NULL, 0 }, { "pbratio", required_argument, NULL, 0 }, + { "lookahead", required_argument, NULL, 0 }, { "chroma-qp-offset", required_argument, NULL, 0 }, { "pass", required_argument, NULL, 'p' }, { "stats", required_argument, NULL, 0 }, @@ -706,54 +701,16 @@ static int Parse( int argc, char **argv, return 0; } -static void parse_qpfile( cli_opt_t *opt, x264_picture_t *pic, int i_frame ) -{ - int num = -1, qp, ret; - char type; - uint64_t file_pos; - while( num < i_frame ) - { - file_pos = ftell( opt->qpfile ); - ret = fscanf( opt->qpfile, "%d %c %d\n", &num, &type, &qp ); - if( num > i_frame || ret == EOF ) - { - pic->i_type = X264_TYPE_AUTO; - pic->i_qpplus1 = 0; - fseek( opt->qpfile , file_pos , SEEK_SET ); - break; - } - if( num < i_frame ) - continue; - pic->i_qpplus1 = qp+1; - if ( type == 'I' ) pic->i_type = X264_TYPE_IDR; - else if( type == 'i' ) pic->i_type = X264_TYPE_I; - else if( type == 'P' ) pic->i_type = X264_TYPE_P; - else if( type == 'B' ) pic->i_type = X264_TYPE_BREF; - else if( type == 'b' ) pic->i_type = X264_TYPE_B; - else ret = 0; - if( ret != 3 || qp < -1 || qp > 51 ) - { - fprintf( stderr, "x264 [error]: can't parse qpfile for frame %d\n", i_frame ); - fclose( opt->qpfile ); - opt->qpfile = NULL; - pic->i_type = X264_TYPE_AUTO; - pic->i_qpplus1 = 0; - break; - } - } -} /***************************************************************************** * Encode: *****************************************************************************/ - static int Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic ) { x264_picture_t pic_out; x264_nal_t *nal; int i_nal, i; int i_file = 0; - if( x264_encoder_encode( h, &nal, &i_nal, pic, &pic_out ) < 0 ) { fprintf( stderr, "x264 [error]: x264_encoder_encode failed\n" ); @@ -783,8 +740,6 @@ static int Encode_frame( x264_t *h, hnd_t hout, x264_picture_t *pic ) static int Encode( x264_param_t *param, cli_opt_t *opt ) { x264_t *h; - x264_picture_t pic; - int i_frame, i_frame_total; int64_t i_start, i_end; int64_t i_file; @@ -815,54 +770,92 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) p_close_outfile( opt->hout ); return -1; } + h->lookahead->opt = opt; + i_start = x264_mdate(); - /* Create a new pic */ - x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height ); - - i_start = x264_mdate(); - - /* Encode frames */ - for( i_frame = 0, i_file = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); ) + if ( h->lookahead->b_thread_active ) { - if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) ) - break; - - pic.i_pts = (int64_t)i_frame * param->i_fps_den; - - if( opt->qpfile ) - parse_qpfile( opt, &pic, i_frame + opt->i_seek ); - else - { - /* Do not force any parameters */ - pic.i_type = X264_TYPE_AUTO; - pic.i_qpplus1 = 0; - } - - i_file += Encode_frame( h, opt->hout, &pic ); + i_file = i_frame = 0; + while ( b_ctrl_c == 0 && h->lookahead->b_thread_active ) + { + i_file += Encode_frame( h, opt->hout, NULL ); + i_frame++; + /* update status line (up to 1000 times per input file) */ + if( opt->b_progress && i_frame % i_update_interval == 0 ) + { + int64_t i_elapsed = x264_mdate() - i_start; + double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0; + double bitrate = (double) i_file * 8 * param->i_fps_num / ( (double) param->i_fps_den * i_frame * 1000 ); + if( i_frame_total ) + { + int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000); + sprintf( buf, "x264 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d", + 100. * i_frame / i_frame_total, i_frame, i_frame_total, fps, bitrate, + eta/3600, (eta/60)%60, eta%60 ); + } + else + { + sprintf( buf, "x264 %d frames: %.2f fps, %.2f kb/s", i_frame, fps, bitrate ); + } + fprintf( stderr, "%s \r", buf+5 ); + SetConsoleTitle( buf ); + fflush( stderr ); // needed in windows + } + } + if (b_ctrl_c == 1) + h->lookahead->b_exit_thread = 1; /* Stop the lookahead / ingest thread */ + } + else + { + x264_picture_t pic; + /* Create a new pic */ + x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height ); - i_frame++; - /* update status line (up to 1000 times per input file) */ - if( opt->b_progress && i_frame % i_update_interval == 0 ) - { - int64_t i_elapsed = x264_mdate() - i_start; - double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0; - double bitrate = (double) i_file * 8 * param->i_fps_num / ( (double) param->i_fps_den * i_frame * 1000 ); - if( i_frame_total ) - { - int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000); - sprintf( buf, "x264 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d", - 100. * i_frame / i_frame_total, i_frame, i_frame_total, fps, bitrate, - eta/3600, (eta/60)%60, eta%60 ); - } - else - { - sprintf( buf, "x264 %d frames: %.2f fps, %.2f kb/s", i_frame, fps, bitrate ); - } - fprintf( stderr, "%s \r", buf+5 ); - SetConsoleTitle( buf ); - fflush( stderr ); // needed in windows - } + /* Encode frames */ + for( i_frame = 0, i_file = 0; b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); ) + { + if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) ) + break; + + pic.i_pts = (int64_t)i_frame * param->i_fps_den; + + if( opt->qpfile ) + parse_qpfile( opt, &pic, i_frame + opt->i_seek ); + else + { + /* Do not force any parameters */ + pic.i_type = X264_TYPE_AUTO; + pic.i_qpplus1 = 0; + } + + i_file += Encode_frame( h, opt->hout, &pic ); + + i_frame++; + + /* update status line (up to 1000 times per input file) */ + if( opt->b_progress && i_frame % i_update_interval == 0 ) + { + int64_t i_elapsed = x264_mdate() - i_start; + double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0; + double bitrate = (double) i_file * 8 * param->i_fps_num / ( (double) param->i_fps_den * i_frame * 1000 ); + if( i_frame_total ) + { + int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000); + sprintf( buf, "x264 [%.1f%%] %d/%d frames, %.2f fps, %.2f kb/s, eta %d:%02d:%02d", + 100. * i_frame / i_frame_total, i_frame, i_frame_total, fps, bitrate, + eta/3600, (eta/60)%60, eta%60 ); + } + else + { + sprintf( buf, "x264 %d frames: %.2f fps, %.2f kb/s", i_frame, fps, bitrate ); + } + fprintf( stderr, "%s \r", buf+5 ); + SetConsoleTitle( buf ); + fflush( stderr ); // needed in windows + } + } + x264_picture_clean( &pic ); } /* Flush delayed B-frames */ do { @@ -871,7 +864,6 @@ static int Encode( x264_param_t *param, cli_opt_t *opt ) } while( i_frame_size ); i_end = x264_mdate(); - x264_picture_clean( &pic ); /* Erase progress indicator before printing encoding stats. */ if( opt->b_progress ) fprintf( stderr, " \r" ); diff --git a/x264.h b/x264.h index 820185f..c4f7dd5 100644 --- a/x264.h +++ b/x264.h @@ -269,6 +269,7 @@ typedef struct x264_param_t float f_vbv_buffer_init; /* <=1: fraction of buffer_size. >1: kbit */ float f_ip_factor; float f_pb_factor; + int i_lookahead; int i_aq_mode; /* psy adaptive QP. (X264_AQ_*) */ float f_aq_strength; @@ -402,6 +403,16 @@ typedef struct uint8_t *p_payload; } x264_nal_t; +typedef void *hnd_t; + +typedef struct { + int b_progress; + int i_seek; + hnd_t hin; + hnd_t hout; + FILE *qpfile; +} cli_opt_t; + /* x264_nal_encode: * encode a nal into a buffer, setting the size. * if b_annexeb then a long synch work is added