diff --git a/src/y262/ratectrl.c b/src/y262/ratectrl.c index a59f1f7..83b465d 100644 --- a/src/y262/ratectrl.c +++ b/src/y262/ratectrl.c @@ -1125,7 +1125,7 @@ int32_t y262_ratectrl_get_slice_mb_quantizer( y262_t *ps_y262, y262_slice_encode { i_baseline = ( ps_slice_rc->i_slice_num_accumulated_quantizer * 5 ); i_adjusted_slice_coded_size = ps_slice_rc->i_slice_coded_size - i_baseline; - i_adjusted_slice_coded_size = MAX( 0, i_adjusted_slice_coded_size ); + i_adjusted_slice_coded_size = MAX( 100, i_adjusted_slice_coded_size ); i_extra_bits = ( int32_t )( ( ps_slice_rc->i_slice_bit_budget_extra * 0.8 ) * ( ( ( double )ps_slice_rc->i_slice_coded_scaled_satd ) / ps_slice_rc->i_slice_scaled_satd ) ); i_extra_bits += ( int32_t )( ( ps_slice_rc->i_slice_bit_budget_extra * 0.2 ) * MAX( 0.0, @@ -1155,6 +1155,7 @@ int32_t y262_ratectrl_get_slice_mb_quantizer( y262_t *ps_y262, y262_slice_encode if( ( ps_slice_rc->i_slice_bit_budget + i_extra_bits - i_adjusted_slice_coded_size ) > ( 512 + 9 ) ) { double d_scale; + d_scale = 1.0; if( i_adjusted_slice_coded_size > 0 && i_adjusted_slice_predicted_size > 0 ) { d_scale = i_adjusted_slice_coded_size / ( double ) i_adjusted_slice_predicted_size; diff --git a/src/y262/transform.c b/src/y262/transform.c index 1e32a46..6d8ee70 100644 --- a/src/y262/transform.c +++ b/src/y262/transform.c @@ -612,19 +612,155 @@ void y262_quant8x8_trellis_copy_int16( int16_t *pi16_dst, const int16_t *pi16_sr } + +typedef struct y262_trellis_node_s +{ + struct y262_trellis_node_s *ps_next; + int32_t i_cost; + int16_t i16_num_level; + int16_t i16_last_scan_idx; + + int16_t rgi16_idx[ 8 * 8 ]; + int16_t rgi16_level[ 8 * 8 ]; +} y262_trellis_node_t; + +typedef struct +{ + int32_t i_lambda; +#define Y262_TRELLIS_MAX_ACTIVE_NODES 3 +#define Y262_RDOQ_NUM_NODES ( Y262_TRELLIS_MAX_ACTIVE_NODES * 3 ) + y262_trellis_node_t rgs_nodes[ Y262_RDOQ_NUM_NODES ]; + y262_trellis_node_t *ps_active_nodes; + y262_trellis_node_t *ps_next_nodes; + y262_trellis_node_t *ps_free_nodes; +} y262_trellis_t; + + +void y262_quant8x8_trellis_init( y262_t *ps_y262, y262_trellis_t *ps_trellis, int32_t i_lambda ) +{ + int32_t i_idx; + + ps_trellis->i_lambda = i_lambda; + ps_trellis->ps_free_nodes = NULL; + for( i_idx = 0; i_idx < Y262_RDOQ_NUM_NODES - 1; i_idx++ ) + { + ps_trellis->rgs_nodes[ i_idx ].ps_next = ps_trellis->ps_free_nodes; + ps_trellis->ps_free_nodes = &ps_trellis->rgs_nodes[ i_idx ]; + } + ps_trellis->rgs_nodes[ i_idx ].ps_next = NULL; + ps_trellis->rgs_nodes[ i_idx ].i_cost = 0; + ps_trellis->rgs_nodes[ i_idx ].i16_num_level = 0; + ps_trellis->rgs_nodes[ i_idx ].i16_last_scan_idx = -1; + ps_trellis->ps_active_nodes = &ps_trellis->rgs_nodes[ i_idx ]; + +} + +void y262_trellis_spawn_trellis( y262_trellis_t *ps_trellis, y262_trellis_node_t *ps_from, int32_t i_ssd, int32_t i_co_idx, int32_t i_scan_idx, int32_t i_level ) +{ + y262_trellis_node_t *ps_to; + int32_t i_new_cost; + + ps_to = ps_trellis->ps_free_nodes; + ps_trellis->ps_free_nodes = ps_to->ps_next; + + ps_to->i16_num_level = ps_from->i16_num_level; + ps_to->i16_last_scan_idx = ps_from->i16_last_scan_idx; + ps_to->i_cost = ps_from->i_cost; + memcpy( ps_to->rgi16_idx, ps_from->rgi16_idx, ps_from->i16_num_level * sizeof( int16_t ) ); + memcpy( ps_to->rgi16_level, ps_from->rgi16_level, ps_from->i16_num_level * sizeof( int16_t ) ); + + i_new_cost = ps_from->i_cost; + if( i_level ) + { + int32_t i_bits, i_bit_cost, i_run; + + i_run = i_scan_idx - ps_from->i16_last_scan_idx - 1; + i_bits = y262_size_run_level( i_run, i_level ); + i_bit_cost = ( ( i_bits * ps_trellis->i_lambda ) >> ( Y262_LAMBDA_BITS ) ); + i_new_cost += i_bit_cost; + + ps_to->i16_last_scan_idx = i_scan_idx; + ps_to->rgi16_idx[ ps_to->i16_num_level ] = i_co_idx; + ps_to->rgi16_level[ ps_to->i16_num_level ] = i_level; + ps_to->i16_num_level++; + } + i_new_cost += i_ssd; + ps_to->i_cost = i_new_cost; + ps_to->ps_next = ps_trellis->ps_next_nodes; + ps_trellis->ps_next_nodes = ps_to; +} + + +void y262_trellis_shrink_trellis( y262_trellis_t *ps_trellis ) +{ + int32_t i_num_nodes; + y262_trellis_node_t *ps_node, *ps_new_list, **pps_new_list, *ps_list; + + ps_list = ps_trellis->ps_active_nodes; + + ps_new_list = NULL; + while( ps_list ) + { + ps_node = ps_list; + ps_list = ps_list->ps_next; + + pps_new_list = &ps_new_list; + while( *pps_new_list && ( *pps_new_list )->i_cost < ps_node->i_cost ) + { + pps_new_list = &( *pps_new_list )->ps_next; + } + + if( *pps_new_list ) + { + ps_node->ps_next = ( *pps_new_list ); + } + else + { + ps_node->ps_next = NULL; + } + + *pps_new_list = ps_node; + } + ps_list = ps_new_list; + i_num_nodes = 0; + ps_trellis->ps_active_nodes = NULL; + while( ps_list && i_num_nodes < Y262_TRELLIS_MAX_ACTIVE_NODES ) + { + ps_node = ps_list; + ps_list = ps_list->ps_next; + + ps_node->ps_next = ps_trellis->ps_active_nodes; + ps_trellis->ps_active_nodes = ps_node; + i_num_nodes++; + } + while( ps_list ) + { + ps_node = ps_list; + ps_list = ps_list->ps_next; + + ps_node->ps_next = ps_trellis->ps_free_nodes; + ps_trellis->ps_free_nodes = ps_node; + } +} + + + int32_t y262_quant8x8_trellis_fw( y262_t *ps_y262, y262_slice_t *ps_slice, int16_t *pi_coeffs, int32_t i_stride, int32_t i_quantizer, bool_t b_intra ) { - int32_t i_dc, i_nz; - int32_t i_coeff, i_start, i_num_coeff, i_last_coeff, i_idx, i_idx2, i_run, i_level; + int32_t i_dc, i_nz, i_num_active_nodes; + int32_t i_coeff, i_start, i_num_coeff, i_last_coeff, i_idx, i_run, i_level; int16_t rgi16_levels[ 64 ]; int16_t rgi16_coeffs[ 64 ]; int8_t rgi8_idx[ 64 ]; int16_t rgi16_level[ 64 ]; uint8_t *pui8_qmat; y262_macroblock_t *ps_mb; + y262_trellis_t s_trellis; + y262_trellis_node_t *ps_node, *ps_next, *ps_best; - int32_t i_active_toggle; - int32_t i_candidate_level, i_dir, i_end, i_ssd, i_bits, i_cost, i_lambda; + int32_t i_cost, i_lambda, i_ssd, i_candidate_level, i_dir, i_end; + + assert( i_stride == 8 ); ps_mb = &ps_slice->s_macroblock; @@ -687,11 +823,7 @@ int32_t y262_quant8x8_trellis_fw( y262_t *ps_y262, y262_slice_t *ps_slice, int16 return i_dc; } - memset( &ps_y262->trellis.rgi8_path_active, 0, sizeof( ps_y262->trellis.rgi8_path_active ) ); - memset( &ps_y262->trellis.rgi_path_cost, 0, sizeof( ps_y262->trellis.rgi_path_cost ) ); - - i_active_toggle = 0; - ps_y262->trellis.rgi8_path_active[ i_active_toggle ][ 0 ] = 1; + y262_quant8x8_trellis_init( ps_y262, &s_trellis, i_lambda ); for( i_idx = 0; i_idx < i_num_coeff; i_idx++ ) { @@ -706,6 +838,9 @@ int32_t y262_quant8x8_trellis_fw( y262_t *ps_y262, y262_slice_t *ps_slice, int16 i_dir = 1; } i_end = i_level + i_dir * 2; + i_num_active_nodes = 0; + s_trellis.ps_next_nodes = NULL; + for( i_candidate_level = i_level; i_candidate_level != i_end; i_candidate_level += i_dir ) { int32_t i_qm, i_qt, i_x, i_y; @@ -741,72 +876,41 @@ int32_t y262_quant8x8_trellis_fw( y262_t *ps_y262, y262_slice_t *ps_slice, int16 i_ssd = ( i_coeff - rgi16_coeffs[ i_idx ] ) * ( i_coeff - rgi16_coeffs[ i_idx ] ); - for( i_idx2 = i_idx; i_idx2 >= 0; i_idx2-- ) + for( ps_node = s_trellis.ps_active_nodes; ps_node; ps_node = ps_node->ps_next ) { - if( ps_y262->trellis.rgi8_path_active[ i_active_toggle ][ i_idx2 ] ) - { - if( i_candidate_level != 0 ) - { - int32_t i_run; - if( i_idx2 > 0 ) - { - i_run = rgi8_idx[ i_idx ] - ps_y262->trellis.rgi8_path_idx[ i_idx2 ][ i_idx2 ] - 1; - } - else - { - i_run = rgi8_idx[ i_idx ]; - } - i_bits = y262_size_run_level( i_run, i_candidate_level ); - i_cost = ps_y262->trellis.rgi_path_cost[ i_idx2 ] + ( ( i_bits * i_lambda ) >> Y262_LAMBDA_BITS ) + i_ssd; - - if( !ps_y262->trellis.rgi8_path_active[ !i_active_toggle ][ i_idx2 + 1 ] || i_cost < ps_y262->trellis.rgi_path_cost[ i_idx2 + 1 ] ) - { - y262_quant8x8_trellis_copy_int8( &ps_y262->trellis.rgi8_path_idx[ i_idx2 + 1 ][ 0 ], &ps_y262->trellis.rgi8_path_idx[ i_idx2 ][ 0 ], ( i_idx2 + 1 ) ); - y262_quant8x8_trellis_copy_int16( &ps_y262->trellis.rgi16_path_level[ i_idx2 + 1 ][ 0 ], &ps_y262->trellis.rgi16_path_level[ i_idx2 ][ 0 ], ( i_idx2 + 1 ) ); - ps_y262->trellis.rgi8_path_idx[ i_idx2 + 1 ][ i_idx2 + 1 ] = rgi8_idx[ i_idx ]; - ps_y262->trellis.rgi16_path_level[ i_idx2 + 1 ][ i_idx2 + 1 ] = i_candidate_level; - ps_y262->trellis.rgi8_path_active[ !i_active_toggle ][ i_idx2 + 1 ] = 1; - ps_y262->trellis.rgi_path_cost[ i_idx2 + 1 ] = i_cost; - } - } - else - { - /* last coeff candidate iter, we can overwrite/activate current path */ - i_cost = ps_y262->trellis.rgi_path_cost[ i_idx2 ] + i_ssd; - if( !ps_y262->trellis.rgi8_path_active[ !i_active_toggle ][ i_idx2 ] || i_cost < ps_y262->trellis.rgi_path_cost[ i_idx2 ] ) - { - ps_y262->trellis.rgi8_path_active[ !i_active_toggle ][ i_idx2 ] = 1; - ps_y262->trellis.rgi_path_cost[ i_idx2 ] = i_cost; - } - } - } + y262_trellis_spawn_trellis( &s_trellis, ps_node, i_ssd, rgui8_y262_scan_0_table[ rgi8_idx[ i_idx ] ], rgi8_idx[ i_idx ], i_candidate_level ); + i_num_active_nodes++; } } - memset( &ps_y262->trellis.rgi8_path_active[ i_active_toggle ][ 0 ], 0, sizeof( int8_t ) * 65 ); - i_active_toggle = i_active_toggle^1; + for( ps_node = s_trellis.ps_active_nodes; ps_node; ps_node = ps_next ) + { + ps_next = ps_node->ps_next; + ps_node->ps_next = s_trellis.ps_free_nodes; + s_trellis.ps_free_nodes = ps_node; + } + s_trellis.ps_active_nodes = s_trellis.ps_next_nodes; + + + if( i_num_active_nodes > Y262_TRELLIS_MAX_ACTIVE_NODES ) + { + y262_trellis_shrink_trellis( &s_trellis ); + } } i_cost = MAX_COST; - i_idx2 = 0; - for( i_idx = 0; i_idx <= i_num_coeff; i_idx++ ) + for( ps_node = s_trellis.ps_active_nodes; ps_node; ps_node = ps_node->ps_next ) { - if( ps_y262->trellis.rgi8_path_active[ i_active_toggle ][ i_idx ] && ps_y262->trellis.rgi_path_cost[ i_idx ] < i_cost ) + if( ps_node->i_cost < i_cost ) { - i_idx2 = i_idx; - i_cost = ps_y262->trellis.rgi_path_cost[ i_idx ]; + i_cost = ps_node->i_cost; + ps_best = ps_node; } } - for( i_idx = 1; i_idx <= i_idx2; i_idx++ ) + for( i_idx = 0; i_idx < ps_best->i16_num_level; i_idx++ ) { - int32_t i_x, i_y; - if( ps_y262->trellis.rgi16_path_level[ i_idx2 ][ i_idx ] != rgi16_level[ i_idx - 1 ] ) - { - i_idx = i_idx; - } - i_x = rgui8_y262_scan_0_table[ ps_y262->trellis.rgi8_path_idx[ i_idx2 ][ i_idx ] ] % 8; - i_y = rgui8_y262_scan_0_table[ ps_y262->trellis.rgi8_path_idx[ i_idx2 ][ i_idx ] ] / 8; - pi_coeffs[ i_x + i_y * i_stride ] = ps_y262->trellis.rgi16_path_level[ i_idx2 ][ i_idx ]; + pi_coeffs[ ps_best->rgi16_idx[ i_idx ] ] = ps_best->rgi16_level[ i_idx ]; } - return i_idx2 != 0; + return ( ps_best->i16_num_level > 0 ); + } diff --git a/src/y262/types.h b/src/y262/types.h index 61721f1..845351c 100644 --- a/src/y262/types.h +++ b/src/y262/types.h @@ -768,11 +768,4 @@ typedef struct y262_s { int32_t i_psyrd_strength; int32_t i_quality_for_speed; - struct { - int8_t rgi8_path_active[ 2 ][ 65 ]; - int8_t rgi8_path_idx[ 65 ][ 65 ]; - int16_t rgi16_path_level[ 65 ][ 65 ]; - int32_t rgi_path_cost[ 65 ]; - } trellis; - } y262_t; diff --git a/src/y262/y262api.c b/src/y262/y262api.c index cef4400..f622db8 100644 --- a/src/y262/y262api.c +++ b/src/y262/y262api.c @@ -114,14 +114,14 @@ void *y262_create( y262_configuration_t *ps_config ) } -bool_t y262_validate_level( y262_t *ps_y262, int32_t i_level, bool_t b_forderive ) +bool_t y262_validate_level_simple_main_profile( y262_t *ps_y262, int32_t i_level, bool_t b_forderive ) { int32_t i_idx, i_luma_sample_rate; int32_t rgi_max_hfcode[ 4 ] = { 7, 8, 9, 9 }; int32_t rgi_max_vfcode[ 4 ] = { 4, 5, 5, 5 }; int32_t rgi_max_frcode[ 4 ] = { 5, 5, 8, 8 }; int32_t rgi_max_pic_width[ 4 ] = { 352, 720, 1440, 1920 }; - int32_t rgi_max_pic_height[ 4 ] = { 288, 576, 1152, 1152 }; + int32_t rgi_max_pic_height[ 4 ] = { 288, 576, 1088, 1088 }; int32_t rgi_fr[ 16 ] = { 100, 24, 24, 25, 30, 30, 50, 60, 60, 100, 100, 100, 100, 100, 100, 100 }; int32_t rgi_max_fr[ 4 ] = { 30, 30, 60, 60 }; int32_t rgi_max_luma_sample_rate[ 4 ] = { 3041280, 10368000, 47001600, 62668800 }; @@ -186,40 +186,260 @@ bool_t y262_validate_level( y262_t *ps_y262, int32_t i_level, bool_t b_forderive } i_luma_sample_rate = ( int32_t )( ( ( ( int64_t )( ps_y262->i_sequence_width * ps_y262->i_sequence_height ) ) * ps_y262->i_sequence_derived_timescale ) / ps_y262->i_sequence_derived_picture_duration ); + if( i_luma_sample_rate > rgi_max_luma_sample_rate[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t *)"luma sample rate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->s_ratectrl.i_vbvrate > rgi_max_bitrate[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t *)"maximum bitrate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->s_ratectrl.i_vbv_size > rgi_max_buffer_size[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t *)"video buffer size exceeds level limit" ); + } + return FALSE; + } + return TRUE; +} + +bool_t y262_validate_level_high_profile( y262_t *ps_y262, int32_t i_level, bool_t b_forderive ) +{ + int32_t i_idx, i_luma_sample_rate; + int32_t rgi_max_hfcode[ 4 ] = { 0, 7, 8, 8 }; + int32_t rgi_max_vfcode[ 4 ] = { 0, 5, 5, 5 }; + int32_t rgi_max_frcode[ 4 ] = { 0, 5, 8, 8 }; + int32_t rgi_max_pic_width[ 4 ] = { 0, 720, 1440, 1920 }; + int32_t rgi_max_pic_height[ 4 ] = { 0, 576, 1088, 1088 }; + int32_t rgi_fr[ 16 ] = { 100, 24, 24, 25, 30, 30, 50, 60, 60, 100, 100, 100, 100, 100, 100, 100 }; + int32_t rgi_max_fr[ 4 ] = { 0, 30, 60, 60 }; + int32_t rgi_max_luma_sample_rate_420[ 4 ] = { 0, 14745600, 62668800, 83558400 }; + int32_t rgi_max_luma_sample_rate_422[ 4 ] = { 0, 11059200, 47001600, 62668800 }; + int32_t rgi_max_bitrate[ 4 ] = { 0, 15000000, 60000000, 80000000 }; + int32_t rgi_max_buffer_size[ 4 ] = { 0, 2441216, 9781248, 12222464 }; /* enhancement 2 ? */ + + i_idx = -1; + switch( i_level ) + { + case Y262_LEVEL_MAIN: + i_idx = 1; + break; + case Y262_LEVEL_HIGH1440: + i_idx = 2; + break; + case Y262_LEVEL_HIGH: + i_idx = 3; + break; + } + + if( i_idx == -1 ) + { + return FALSE; + } + if( ps_y262->rgi_fcode[ 0 ][ 0 ] < 1 || ps_y262->rgi_fcode[ 0 ][ 0 ] > rgi_max_hfcode[ i_idx ] || + ps_y262->rgi_fcode[ 1 ][ 0 ] < 1 || ps_y262->rgi_fcode[ 1 ][ 0 ] > rgi_max_hfcode[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "horizontal fcode exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->rgi_fcode[ 0 ][ 1 ] < 1 || ps_y262->rgi_fcode[ 0 ][ 1 ] > rgi_max_vfcode[ i_idx ] || + ps_y262->rgi_fcode[ 1 ][ 1 ] < 1 || ps_y262->rgi_fcode[ 1 ][ 1 ] > rgi_max_vfcode[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "vertical fcode exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->i_sequence_frame_rate_code < 1 || ps_y262->i_sequence_frame_rate_code > rgi_max_frcode[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "frame rate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->i_sequence_width > rgi_max_pic_width[ i_idx ] || + ps_y262->i_sequence_height > rgi_max_pic_height[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "picture size exceeds level limit" ); + } + return FALSE; + } + i_luma_sample_rate = ( int32_t ) ( ( ( ( int64_t ) ( ps_y262->i_sequence_width * ps_y262->i_sequence_height ) ) * ps_y262->i_sequence_derived_timescale ) / ps_y262->i_sequence_derived_picture_duration ); + if( ps_y262->i_sequence_chroma_format == Y262_CHROMA_FORMAT_420 ) { - if( i_luma_sample_rate > rgi_max_luma_sample_rate[ i_idx ] ) + if( i_luma_sample_rate > rgi_max_luma_sample_rate_420[ i_idx ] ) { if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) { - ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t *)"luma sample rate exceeds level limit" ); - } - return FALSE; - } - if( ps_y262->s_ratectrl.i_vbvrate > rgi_max_bitrate[ i_idx ] ) - { - if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) - { - ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t *)"maximum bitrate exceeds level limit" ); - } - return FALSE; - } - if( ps_y262->s_ratectrl.i_vbv_size > rgi_max_buffer_size[ i_idx ] ) - { - if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) - { - ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t *)"video buffer size exceeds level limit" ); + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "luma sample rate exceeds level limit" ); } return FALSE; } } else { - /* 422/444 level limits where ? */ + if( i_luma_sample_rate > rgi_max_luma_sample_rate_422[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "luma sample rate exceeds level limit" ); + } + return FALSE; + } + } + + if( ps_y262->s_ratectrl.i_vbvrate > rgi_max_bitrate[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "maximum bitrate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->s_ratectrl.i_vbv_size > rgi_max_buffer_size[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "video buffer size exceeds level limit" ); + } + return FALSE; } return TRUE; } + +bool_t y262_validate_level_422_profile( y262_t *ps_y262, int32_t i_level, bool_t b_forderive ) +{ + int32_t i_idx, i_luma_sample_rate; + int32_t rgi_max_hfcode[ 4 ] = { 7, 8 }; + int32_t rgi_max_vfcode[ 4 ] = { 5, 5 }; + int32_t rgi_max_frcode[ 4 ] = { 5, 8 }; + int32_t rgi_max_pic_width[ 4 ] = { 720, 1920 }; + int32_t rgi_max_pic_height[ 4 ] = { 608, 1088 }; + int32_t rgi_fr[ 16 ] = { 100, 24, 24, 25, 30, 30, 50, 60, 60, 100, 100, 100, 100, 100, 100, 100 }; + int32_t rgi_max_fr[ 4 ] = { 30, 60 }; + int32_t rgi_max_luma_sample_rate[ 4 ] = { 11059200, 62668800 }; + int32_t rgi_max_bitrate[ 4 ] = { 50000000, 300000000 }; + int32_t rgi_max_buffer_size[ 4 ] = { 9437184, 47185920 }; + + i_idx = -1; + switch( i_level ) + { + case Y262_LEVEL_422MAIN: + i_idx = 0; + break; + case Y262_LEVEL_422HIGH: + i_idx = 1; + break; + } + + if( i_idx == -1 ) + { + return FALSE; + } + if( ps_y262->rgi_fcode[ 0 ][ 0 ] < 1 || ps_y262->rgi_fcode[ 0 ][ 0 ] > rgi_max_hfcode[ i_idx ] || + ps_y262->rgi_fcode[ 1 ][ 0 ] < 1 || ps_y262->rgi_fcode[ 1 ][ 0 ] > rgi_max_hfcode[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "horizontal fcode exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->rgi_fcode[ 0 ][ 1 ] < 1 || ps_y262->rgi_fcode[ 0 ][ 1 ] > rgi_max_vfcode[ i_idx ] || + ps_y262->rgi_fcode[ 1 ][ 1 ] < 1 || ps_y262->rgi_fcode[ 1 ][ 1 ] > rgi_max_vfcode[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "vertical fcode exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->i_sequence_frame_rate_code < 1 || ps_y262->i_sequence_frame_rate_code > rgi_max_frcode[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "frame rate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->i_sequence_width > rgi_max_pic_width[ i_idx ] || + ps_y262->i_sequence_height > rgi_max_pic_height[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "picture size exceeds level limit" ); + } + return FALSE; + } + i_luma_sample_rate = ( int32_t ) ( ( ( ( int64_t ) ( ps_y262->i_sequence_width * ps_y262->i_sequence_height ) ) * ps_y262->i_sequence_derived_timescale ) / ps_y262->i_sequence_derived_picture_duration ); + + if( i_luma_sample_rate > rgi_max_luma_sample_rate[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "luma sample rate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->s_ratectrl.i_vbvrate > rgi_max_bitrate[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "maximum bitrate exceeds level limit" ); + } + return FALSE; + } + if( ps_y262->s_ratectrl.i_vbv_size > rgi_max_buffer_size[ i_idx ] ) + { + if( !b_forderive && ps_y262->s_funcs.pf_error_callback ) + { + ps_y262->s_funcs.pf_error_callback( ps_y262->p_cb_handle, Y262_ERROR_PROFILELEVEL, ( int8_t * ) "video buffer size exceeds level limit" ); + } + return FALSE; + } + return TRUE; +} + + +bool_t y262_validate_level( y262_t *ps_y262, int32_t i_profile, int32_t i_level, bool_t b_forderive ) +{ + if( i_profile == Y262_PROFILE_SIMPLE || + i_profile == Y262_PROFILE_MAIN ) + { + return y262_validate_level_simple_main_profile( ps_y262, i_level, b_forderive ); + } + else if( i_profile == Y262_PROFILE_HIGH ) + { + return y262_validate_level_high_profile( ps_y262, i_level, b_forderive ); + } + else if( i_profile == Y262_PROFILE_422 ) + { + return y262_validate_level_422_profile( ps_y262, i_level, b_forderive ); + } + return FALSE; +} + + + int32_t y262_initialize( void *p_y262, y262_configuration_t *ps_config ) { int32_t i_idx, i_mv_range, i_mvs, i_fcode_m1, i_wrap; @@ -293,19 +513,33 @@ int32_t y262_initialize( void *p_y262, y262_configuration_t *ps_config ) } if( ps_config->i_profile != Y262_PROFILE_DERIVE && ps_config->i_profile != Y262_PROFILE_SIMPLE && - ps_config->i_profile != Y262_PROFILE_MAIN ) + ps_config->i_profile != Y262_PROFILE_MAIN && + ps_config->i_profile != Y262_PROFILE_HIGH && + ps_config->i_profile != Y262_PROFILE_422 ) { return Y262_INIT_ERROR_PROFILE; } - if( ps_config->i_level != Y262_LEVEL_DERIVE && - ps_config->i_level != Y262_LEVEL_LOW && - ps_config->i_level != Y262_LEVEL_MAIN && - ps_config->i_level != Y262_LEVEL_HIGH1440 && - ps_config->i_level != Y262_LEVEL_HIGH ) + if( ps_config->i_level != Y262_LEVEL_DERIVE ) { - return Y262_INIT_ERROR_LEVEL; + if( ps_config->i_profile != Y262_PROFILE_422 ) + { + if( ps_config->i_level != Y262_LEVEL_LOW && + ps_config->i_level != Y262_LEVEL_MAIN && + ps_config->i_level != Y262_LEVEL_HIGH1440 && + ps_config->i_level != Y262_LEVEL_HIGH ) + { + return Y262_INIT_ERROR_LEVEL; + } + } + else + { + if( ps_config->i_level != Y262_LEVEL_422MAIN && + ps_config->i_level != Y262_LEVEL_422HIGH ) + { + return Y262_INIT_ERROR_LEVEL; + } + } } - if( ps_config->i_lookahead_pictures < 10 || ps_config->i_lookahead_pictures > 50 ) { return Y262_INIT_ERROR_LOOKAHEADPICS; @@ -672,7 +906,14 @@ int32_t y262_initialize( void *p_y262, y262_configuration_t *ps_config ) if( ps_config->i_profile == Y262_PROFILE_DERIVE ) { - ps_y262->i_derived_profile = ps_y262->i_sequence_num_bframes > 0 ? Y262_PROFILE_MAIN : Y262_PROFILE_SIMPLE; + if( ps_y262->i_sequence_chroma_format != Y262_CHROMA_FORMAT_420 ) + { + ps_y262->i_derived_profile = Y262_PROFILE_422; + } + else + { + ps_y262->i_derived_profile = ps_y262->i_sequence_num_bframes > 0 ? Y262_PROFILE_MAIN : Y262_PROFILE_SIMPLE; + } } else { @@ -684,6 +925,14 @@ int32_t y262_initialize( void *p_y262, y262_configuration_t *ps_config ) } } else if( ps_config->i_profile == Y262_PROFILE_MAIN ) + { + if( ps_y262->i_sequence_chroma_format != Y262_CHROMA_FORMAT_420 ) + { + return Y262_INIT_ERROR_PROFILE_CHROMA_FORMAT; + } + } + else if( ps_config->i_profile == Y262_PROFILE_HIGH || + ps_config->i_profile == Y262_PROFILE_422 ) { } else @@ -695,24 +944,42 @@ int32_t y262_initialize( void *p_y262, y262_configuration_t *ps_config ) if( ps_config->i_level == Y262_LEVEL_DERIVE ) { int32_t i_check; - int32_t rgi_levels[ 4 ] = { Y262_LEVEL_LOW, Y262_LEVEL_MAIN, Y262_LEVEL_HIGH1440, Y262_LEVEL_HIGH }; - - for( i_check = 0; i_check < 4; i_check++ ) + if( ps_y262->i_derived_profile != Y262_PROFILE_422 ) { - if( y262_validate_level( ps_y262, rgi_levels[ i_check ], i_check < 3 ? TRUE : FALSE ) ) + int32_t rgi_levels[ 4 ] = { Y262_LEVEL_LOW, Y262_LEVEL_MAIN, Y262_LEVEL_HIGH1440, Y262_LEVEL_HIGH }; + for( i_check = 0; i_check < 4; i_check++ ) { - ps_y262->i_derived_level = rgi_levels[ i_check ]; - break; + if( y262_validate_level( ps_y262, ps_y262->i_derived_profile, rgi_levels[ i_check ], i_check < 3 ? TRUE : FALSE ) ) + { + ps_y262->i_derived_level = rgi_levels[ i_check ]; + break; + } + } + if( i_check == 4 ) + { + return Y262_INIT_ERROR_LEVEL_LIMITS; } } - if( i_check == 4 ) + else /* i_derived_profile == Y262_PROFILE_422 */ { - return Y262_INIT_ERROR_LEVEL_LIMITS; + int32_t rgi_levels[ 4 ] = { Y262_LEVEL_422MAIN, Y262_LEVEL_422HIGH }; + for( i_check = 0; i_check < 2; i_check++ ) + { + if( y262_validate_level( ps_y262, ps_y262->i_derived_profile, rgi_levels[ i_check ], i_check < 1 ? TRUE : FALSE ) ) + { + ps_y262->i_derived_level = rgi_levels[ i_check ]; + break; + } + } + if( i_check == 2 ) + { + return Y262_INIT_ERROR_LEVEL_LIMITS; + } } } else { - if( !y262_validate_level( ps_y262, ps_config->i_level, FALSE ) ) + if( !y262_validate_level( ps_y262, ps_y262->i_derived_profile, ps_config->i_level, FALSE ) ) { return Y262_INIT_ERROR_LEVEL_LIMITS; } diff --git a/src/y262/y262api.h b/src/y262/y262api.h index 8a3ed76..2406221 100644 --- a/src/y262/y262api.h +++ b/src/y262/y262api.h @@ -113,6 +113,8 @@ typedef struct { #define Y262_PROFILE_DERIVE 256 #define Y262_PROFILE_SIMPLE 5 #define Y262_PROFILE_MAIN 4 +#define Y262_PROFILE_HIGH 1 +#define Y262_PROFILE_422 8 int32_t i_profile; /* profile, one of the profile defines */ #define Y262_LEVEL_DERIVE 256 @@ -120,6 +122,8 @@ typedef struct { #define Y262_LEVEL_MAIN 8 #define Y262_LEVEL_HIGH1440 6 #define Y262_LEVEL_HIGH 4 +#define Y262_LEVEL_422MAIN 5 +#define Y262_LEVEL_422HIGH 2 int32_t i_level; /* level, one of the level defines */ #define Y262_VIDEOFORMAT_PAL 0 @@ -270,6 +274,8 @@ void *y262_create( y262_configuration_t *ps_config ); #define Y262_INIT_ERROR_RESOURCE_INTERNAL -28 /* invalid y262 context */ #define Y262_INIT_ERROR_CONTEXT -29 +/* invalid chroma format for profile */ +#define Y262_INIT_ERROR_PROFILE_CHROMA_FORMAT -30 /* initializes the encoder context p_y262 with the encoding configuration ps_config. diff --git a/src/y262app/main.c b/src/y262app/main.c index 59c8ce1..c499320 100644 --- a/src/y262app/main.c +++ b/src/y262app/main.c @@ -374,6 +374,14 @@ int32_t main( int32_t i_argc, char *rgpi8_argv[] ) { s_config.i_profile = Y262_PROFILE_MAIN; } + else if( strcmp( ( char * ) rgpi8_argv[ i_idx ], "high" ) == 0 ) + { + s_config.i_profile = Y262_PROFILE_HIGH; + } + else if( strcmp( ( char * ) rgpi8_argv[ i_idx ], "422" ) == 0 ) + { + s_config.i_profile = Y262_PROFILE_422; + } else { fprintf( stderr, "unknown profile specified on commandline\n"); @@ -394,10 +402,18 @@ int32_t main( int32_t i_argc, char *rgpi8_argv[] ) { s_config.i_level = Y262_LEVEL_HIGH1440; } - else if( strcmp( ( char *)rgpi8_argv[ i_idx ], "high" ) == 0 ) + else if( strcmp( ( char * ) rgpi8_argv[ i_idx ], "high" ) == 0 ) { s_config.i_level = Y262_LEVEL_HIGH; } + else if( strcmp( ( char * ) rgpi8_argv[ i_idx ], "422main" ) == 0 ) + { + s_config.i_level = Y262_LEVEL_422MAIN; + } + else if( strcmp( ( char * ) rgpi8_argv[ i_idx ], "422high" ) == 0 ) + { + s_config.i_level = Y262_LEVEL_422HIGH; + } else { fprintf( stderr, "unknown level specified on commandline\n" ); @@ -916,6 +932,14 @@ int32_t main( int32_t i_argc, char *rgpi8_argv[] ) { fclose( f_in ); } + if( f_mpass_in ) + { + fclose( f_mpass_in ); + } + if( f_mpass_out ) + { + fclose( f_mpass_out ); + } return 0; }