@ -48,6 +48,8 @@ typedef struct StackContext {
int is_vertical ;
int is_horizontal ;
int nb_planes ;
int nb_grid_columns ;
int nb_grid_rows ;
uint8_t fillcolor [ 4 ] ;
char * fillcolor_str ;
int fillcolor_enable ;
@ -85,33 +87,42 @@ static av_cold int init(AVFilterContext *ctx)
if ( ! strcmp ( ctx - > filter - > name , " hstack " ) )
s - > is_horizontal = 1 ;
s - > frames = av_calloc ( s - > nb_inputs , sizeof ( * s - > frames ) ) ;
if ( ! s - > frames )
return AVERROR ( ENOMEM ) ;
s - > items = av_calloc ( s - > nb_inputs , sizeof ( * s - > items ) ) ;
if ( ! s - > items )
return AVERROR ( ENOMEM ) ;
if ( ! strcmp ( ctx - > filter - > name , " xstack " ) ) {
int is_grid ;
if ( strcmp ( s - > fillcolor_str , " none " ) & &
av_parse_color ( s - > fillcolor , s - > fillcolor_str , - 1 , ctx ) > = 0 ) {
s - > fillcolor_enable = 1 ;
} else {
s - > fillcolor_enable = 0 ;
}
if ( ! s - > layout ) {
is_grid = s - > nb_grid_rows & & s - > nb_grid_columns ;
if ( s - > layout & & is_grid ) {
av_log ( ctx , AV_LOG_ERROR , " Both layout and grid were specified. Only one is allowed. \n " ) ;
return AVERROR ( EINVAL ) ;
}
if ( ! s - > layout & & ! is_grid ) {
if ( s - > nb_inputs = = 2 ) {
s - > layout = av_strdup ( " 0_0|w0_0 " ) ;
if ( ! s - > layout )
return AVERROR ( ENOMEM ) ;
s - > nb_grid_rows = 1 ;
s - > nb_grid_columns = 2 ;
is_grid = 1 ;
} else {
av_log ( ctx , AV_LOG_ERROR , " No layout specified. \n " ) ;
av_log ( ctx , AV_LOG_ERROR , " No layout or grid specified. \n " ) ;
return AVERROR ( EINVAL ) ;
}
}
if ( is_grid )
s - > nb_inputs = s - > nb_grid_rows * s - > nb_grid_columns ;
}
s - > frames = av_calloc ( s - > nb_inputs , sizeof ( * s - > frames ) ) ;
if ( ! s - > frames )
return AVERROR ( ENOMEM ) ;
s - > items = av_calloc ( s - > nb_inputs , sizeof ( * s - > items ) ) ;
if ( ! s - > items )
return AVERROR ( ENOMEM ) ;
for ( i = 0 ; i < s - > nb_inputs ; i + + ) {
AVFilterPad pad = { 0 } ;
@ -244,6 +255,48 @@ static int config_output(AVFilterLink *outlink)
width + = ctx - > inputs [ i ] - > w ;
}
}
} else if ( s - > nb_grid_rows & & s - > nb_grid_columns ) {
int inw = 0 , inh = 0 ;
int k = 0 ;
int row_height ;
height = 0 ;
width = 0 ;
for ( i = 0 ; i < s - > nb_grid_rows ; i + + , inh + = row_height ) {
row_height = ctx - > inputs [ i * s - > nb_grid_columns ] - > h ;
inw = 0 ;
for ( int j = 0 ; j < s - > nb_grid_columns ; j + + , k + + ) {
AVFilterLink * inlink = ctx - > inputs [ k ] ;
StackItem * item = & s - > items [ k ] ;
if ( ctx - > inputs [ k ] - > h ! = row_height ) {
av_log ( ctx , AV_LOG_ERROR , " Input %d height %d does not match current row's height %d. \n " ,
k , ctx - > inputs [ k ] - > h , row_height ) ;
return AVERROR ( EINVAL ) ;
}
if ( ( ret = av_image_fill_linesizes ( item - > linesize , inlink - > format , inlink - > w ) ) < 0 ) {
return ret ;
}
item - > height [ 1 ] = item - > height [ 2 ] = AV_CEIL_RSHIFT ( inlink - > h , s - > desc - > log2_chroma_h ) ;
item - > height [ 0 ] = item - > height [ 3 ] = inlink - > h ;
if ( ( ret = av_image_fill_linesizes ( item - > x , inlink - > format , inw ) ) < 0 ) {
return ret ;
}
item - > y [ 1 ] = item - > y [ 2 ] = AV_CEIL_RSHIFT ( inh , s - > desc - > log2_chroma_h ) ;
item - > y [ 0 ] = item - > y [ 3 ] = inh ;
inw + = ctx - > inputs [ k ] - > w ;
}
height + = row_height ;
if ( ! i )
width = inw ;
if ( i & & width ! = inw ) {
av_log ( ctx , AV_LOG_ERROR , " Row %d width %d does not match previous row width %d. \n " , i , inw , width ) ;
return AVERROR ( EINVAL ) ;
}
}
} else {
char * arg , * p = s - > layout , * saveptr = NULL ;
char * arg2 , * p2 , * saveptr2 = NULL ;
@ -436,6 +489,7 @@ const AVFilter ff_vf_vstack = {
static const AVOption xstack_options [ ] = {
{ " inputs " , " set number of inputs " , OFFSET ( nb_inputs ) , AV_OPT_TYPE_INT , { . i64 = 2 } , 2 , INT_MAX , . flags = FLAGS } ,
{ " layout " , " set custom layout " , OFFSET ( layout ) , AV_OPT_TYPE_STRING , { . str = NULL } , 0 , 0 , . flags = FLAGS } ,
{ " grid " , " set fixed size grid layout " , OFFSET ( nb_grid_columns ) , AV_OPT_TYPE_IMAGE_SIZE , { . str = NULL } , 0 , 0 , . flags = FLAGS } ,
{ " shortest " , " force termination when the shortest input terminates " , OFFSET ( shortest ) , AV_OPT_TYPE_BOOL , { . i64 = 0 } , 0 , 1 , . flags = FLAGS } ,
{ " fill " , " set the color for unused pixels " , OFFSET ( fillcolor_str ) , AV_OPT_TYPE_STRING , { . str = " none " } , . flags = FLAGS } ,
{ NULL } ,