From: Clément Stenac Date: Sat, 18 Mar 2006 11:27:10 +0000 (+0000) Subject: Transcode cropping/padding patch by Udo Richter < udo underscore richter at gmx d0t... X-Git-Tag: 0.9.0-test0~11870 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=32438ffbc0b9c6599c6063c1f9e02de1c727626f;p=vlc Transcode cropping/padding patch by Udo Richter < udo underscore richter at gmx d0t de> (with added round-to-16 fix) . Many thanks for this. Seems to work neatly, please test nevertheless. Closes:#448 --- diff --git a/modules/codec/ffmpeg/ffmpeg.c b/modules/codec/ffmpeg/ffmpeg.c index bc9649f3f4..b4f32a93bc 100644 --- a/modules/codec/ffmpeg/ffmpeg.c +++ b/modules/codec/ffmpeg/ffmpeg.c @@ -197,6 +197,12 @@ vlc_module_begin(); set_callbacks( E_(OpenFilter), E_(CloseFilter) ); set_description( _("ffmpeg video filter") ); + /* crop/padd submodule */ + add_submodule(); + set_capability( "crop padd", 10 ); + set_callbacks( E_(OpenCropPadd), E_(CloseFilter) ); + set_description( _("ffmpeg crop padd filter") ); + /* video filter submodule */ add_submodule(); set_capability( "video filter2", 0 ); diff --git a/modules/codec/ffmpeg/ffmpeg.h b/modules/codec/ffmpeg/ffmpeg.h index dfdd86ecc3..5d22f96016 100644 --- a/modules/codec/ffmpeg/ffmpeg.h +++ b/modules/codec/ffmpeg/ffmpeg.h @@ -70,6 +70,7 @@ void E_(CloseDemux)( vlc_object_t * ); /* Video filter module */ int E_(OpenFilter)( vlc_object_t * ); +int E_(OpenCropPadd)( vlc_object_t * ); void E_(CloseFilter)( vlc_object_t * ); int E_(OpenDeinterlace)( vlc_object_t * ); void E_(CloseDeinterlace)( vlc_object_t * ); diff --git a/modules/codec/ffmpeg/video_filter.c b/modules/codec/ffmpeg/video_filter.c index af62ccd43e..c3d538874f 100644 --- a/modules/codec/ffmpeg/video_filter.c +++ b/modules/codec/ffmpeg/video_filter.c @@ -51,6 +51,7 @@ struct filter_sys_t vlc_bool_t b_resize; vlc_bool_t b_convert; vlc_bool_t b_resize_first; + vlc_bool_t b_enable_croppadd; es_format_t fmt_in; int i_src_ffmpeg_chroma; @@ -61,10 +62,11 @@ struct filter_sys_t ImgReSampleContext *p_rsc; }; + /***************************************************************************** - * OpenFilter: probe the filter and return score + * OpenFilterEx: common code to OpenFilter and OpenCropPadd *****************************************************************************/ -int E_(OpenFilter)( vlc_object_t *p_this ) +static int OpenFilterEx( vlc_object_t *p_this, vlc_bool_t b_enable_croppadd ) { filter_t *p_filter = (filter_t*)p_this; filter_sys_t *p_sys; @@ -80,6 +82,20 @@ int E_(OpenFilter)( vlc_object_t *p_this ) b_resize = p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width || p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height; + + if ( b_enable_croppadd ) + { + b_resize = b_resize || + p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width || + p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height || + p_filter->fmt_in.video.i_x_offset != 0 || + p_filter->fmt_in.video.i_y_offset != 0 || + p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width || + p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height || + p_filter->fmt_out.video.i_x_offset != 0 || + p_filter->fmt_out.video.i_y_offset != 0; + } + b_convert = p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma; @@ -99,6 +115,7 @@ int E_(OpenFilter)( vlc_object_t *p_this ) /* Misc init */ p_sys->p_rsc = NULL; + p_sys->b_enable_croppadd = b_enable_croppadd; p_sys->i_src_ffmpeg_chroma = E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma ); p_sys->i_dst_ffmpeg_chroma = @@ -130,6 +147,23 @@ int E_(OpenFilter)( vlc_object_t *p_this ) return VLC_SUCCESS; } +/***************************************************************************** + * OpenFilter: probe the filter and return score + *****************************************************************************/ +int E_(OpenFilter)( vlc_object_t *p_this ) +{ + return OpenFilterEx( p_this, VLC_FALSE ); +} + +/***************************************************************************** + * OpenCropPadd: probe the filter and return score + *****************************************************************************/ +int E_(OpenCropPadd)( vlc_object_t *p_this ) +{ + return OpenFilterEx( p_this, VLC_TRUE ); +} + + /***************************************************************************** * CloseFilter: clean up the filter *****************************************************************************/ @@ -151,11 +185,36 @@ void E_(CloseFilter)( vlc_object_t *p_this ) static int CheckInit( filter_t *p_filter ) { filter_sys_t *p_sys = p_filter->p_sys; - - if( p_filter->fmt_in.video.i_width != p_sys->fmt_in.video.i_width || + vlc_bool_t b_change; + int i_croptop=0; + int i_cropbottom=0; + int i_cropleft=0; + int i_cropright=0; + int i_paddtop=0; + int i_paddbottom=0; + int i_paddleft=0; + int i_paddright=0; + + + b_change= p_filter->fmt_in.video.i_width != p_sys->fmt_in.video.i_width || p_filter->fmt_in.video.i_height != p_sys->fmt_in.video.i_height || p_filter->fmt_out.video.i_width != p_sys->fmt_out.video.i_width || - p_filter->fmt_out.video.i_height != p_sys->fmt_out.video.i_height ) + p_filter->fmt_out.video.i_height != p_sys->fmt_out.video.i_height; + + if ( p_sys->b_enable_croppadd ) + { + b_change = b_change || + p_filter->fmt_in.video.i_y_offset != p_sys->fmt_in.video.i_y_offset || + p_filter->fmt_in.video.i_x_offset != p_sys->fmt_in.video.i_x_offset || + p_filter->fmt_in.video.i_visible_width != p_sys->fmt_in.video.i_visible_width || + p_filter->fmt_in.video.i_visible_height != p_sys->fmt_in.video.i_visible_height || + p_filter->fmt_out.video.i_y_offset != p_sys->fmt_out.video.i_y_offset || + p_filter->fmt_out.video.i_x_offset != p_sys->fmt_out.video.i_x_offset || + p_filter->fmt_out.video.i_visible_width != p_sys->fmt_out.video.i_visible_width || + p_filter->fmt_out.video.i_visible_height != p_sys->fmt_out.video.i_visible_height; + } + + if ( b_change ) { if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc ); p_sys->p_rsc = 0; @@ -191,12 +250,67 @@ static int CheckInit( filter_t *p_filter ) p_sys->b_resize_first = VLC_TRUE; } + if ( p_sys->b_enable_croppadd ) + { + p_sys->b_resize = p_sys->b_resize || + p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width || + p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height || + p_filter->fmt_in.video.i_x_offset != 0 || + p_filter->fmt_in.video.i_y_offset != 0 || + p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width || + p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height || + p_filter->fmt_out.video.i_x_offset != 0 || + p_filter->fmt_out.video.i_y_offset != 0; + } + if( p_sys->b_resize ) { - p_sys->p_rsc = img_resample_init( p_filter->fmt_out.video.i_width, + if ( p_sys->b_enable_croppadd ) + { + i_croptop=p_filter->fmt_in.video.i_y_offset; + i_cropbottom=p_filter->fmt_in.video.i_height + - p_filter->fmt_in.video.i_visible_height + - p_filter->fmt_in.video.i_y_offset; + i_cropleft=p_filter->fmt_in.video.i_x_offset; + i_cropright=p_filter->fmt_in.video.i_width + - p_filter->fmt_in.video.i_visible_width + - p_filter->fmt_in.video.i_x_offset; + + + i_paddtop=p_filter->fmt_out.video.i_y_offset; + i_paddbottom=p_filter->fmt_out.video.i_height + - p_filter->fmt_out.video.i_visible_height + - p_filter->fmt_out.video.i_y_offset; + i_paddleft=p_filter->fmt_out.video.i_x_offset; + i_paddright=p_filter->fmt_out.video.i_width + - p_filter->fmt_out.video.i_visible_width + - p_filter->fmt_out.video.i_x_offset; + } + +#if LIBAVCODEC_BUILD >= 4708 + p_sys->p_rsc = img_resample_full_init( + p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, p_filter->fmt_in.video.i_width, - p_filter->fmt_in.video.i_height ); + p_filter->fmt_in.video.i_height, + i_croptop,i_cropbottom, + i_cropleft,i_cropright, + i_paddtop,i_paddbottom, + i_paddleft,i_paddright ); +#else + p_sys->p_rsc = img_resample_full_init( + p_filter->fmt_out.video.i_width - i_paddleft - i_paddright, + p_filter->fmt_out.video.i_height - i_paddtop - i_paddbottom, + p_filter->fmt_in.video.i_width, + p_filter->fmt_in.video.i_height, + i_croptop,i_cropbottom, + i_cropleft,i_cropright ); +#endif + msg_Dbg( p_filter, "input: %ix%i -> %ix%i", + p_filter->fmt_out.video.i_width, + p_filter->fmt_out.video.i_height, + p_filter->fmt_in.video.i_width, + p_filter->fmt_in.video.i_height); if( !p_sys->p_rsc ) { @@ -229,6 +343,65 @@ static int CheckInit( filter_t *p_filter ) return VLC_SUCCESS; } +/* fill padd code from ffmpeg */ +static int padcolor[3] = { 16, 128, 128 }; + +/* Expects img to be yuv420 */ +static void fill_pad_region( AVPicture* img, int height, int width, + int padtop, int padbottom, int padleft, int padright, int *color ) +{ + int i, y, shift; + uint8_t *optr; + + for ( i = 0; i < 3; i++ ) + { + shift = ( i == 0 ) ? 0 : 1; + + if ( padtop || padleft ) + { + memset( img->data[i], color[i], ( ( ( img->linesize[i] * padtop ) + + padleft ) >> shift) ); + } + + if ( padleft || padright ) + { + optr = img->data[i] + ( img->linesize[i] * ( padtop >> shift ) ) + + ( img->linesize[i] - ( padright >> shift ) ); + + for ( y = 0; y < ( ( height - ( padtop + padbottom ) ) >> shift ); y++ ) + { + memset( optr, color[i], ( padleft + padright ) >> shift ); + optr += img->linesize[i]; + } + } + + if (padbottom) + { + optr = img->data[i] + ( img->linesize[i] * ( ( height - padbottom ) >> shift ) ); + memset( optr, color[i], ( ( img->linesize[i] * padbottom ) >> shift ) ); + } + } +} + +#if LIBAVCODEC_BUILD < 4708 + +/* Workaround, because old libavcodec doesnt know how to padd */ + +static void img_resample_padd( ImgReSampleContext *s, AVPicture *output, + const AVPicture *input, int padtop, int padleft ) +{ + AVPicture nopadd_pic = *output; + + /* shift out top and left padding for old ffmpeg */ + nopadd_pic.data[0] += ( nopadd_pic.linesize[0] * padtop + padleft ); + nopadd_pic.data[1] += ( nopadd_pic.linesize[1] * padtop + padleft ) >> 1; + nopadd_pic.data[2] += ( nopadd_pic.linesize[2] * padtop + padleft ) >> 1; + img_resample( s, &nopadd_pic, input ); +} +#endif + + + /***************************************************************************** * Do the processing here *****************************************************************************/ @@ -291,7 +464,43 @@ static picture_t *Process( filter_t *p_filter, picture_t *p_pic ) if( p_sys->b_resize_first ) { if( p_sys->b_convert ) p_dst = &p_sys->tmp_pic; + +#if LIBAVCODEC_BUILD >= 4708 img_resample( p_sys->p_rsc, p_dst, p_src ); +#else + if ( p_sys->b_enable_croppadd ) + { + img_resample_padd( p_sys->p_rsc, p_dst, p_src, + p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_x_offset ); + } + else + { + img_resample( p_sys->p_rsc, p_dst, p_src ); + } +#endif + + if (p_sys->b_enable_croppadd) + { + if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width || + p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height || + p_filter->fmt_out.video.i_x_offset != 0 || + p_filter->fmt_out.video.i_y_offset != 0) + { + fill_pad_region(p_dst, p_filter->fmt_out.video.i_height, + p_filter->fmt_out.video.i_width, + p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_height + - p_filter->fmt_out.video.i_visible_height + - p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_x_offset, + p_filter->fmt_out.video.i_width + - p_filter->fmt_out.video.i_visible_width + - p_filter->fmt_out.video.i_x_offset, + padcolor); + } + } + p_src = p_dst; } } @@ -316,7 +525,42 @@ static picture_t *Process( filter_t *p_filter, picture_t *p_pic ) if( p_sys->b_resize && !p_sys->b_resize_first && p_sys->p_rsc ) { p_dst = &dest_pic; + +#if LIBAVCODEC_BUILD >= 4708 img_resample( p_sys->p_rsc, p_dst, p_src ); +#else + if ( p_sys->b_enable_croppadd ) + { + img_resample_padd( p_sys->p_rsc, p_dst, p_src, + p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_x_offset ); + } + else + { + img_resample( p_sys->p_rsc, p_dst, p_src ); + } +#endif + + if (p_sys->b_enable_croppadd) + { + if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width || + p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height || + p_filter->fmt_out.video.i_x_offset != 0 || + p_filter->fmt_out.video.i_y_offset != 0) + { + fill_pad_region(p_dst, p_filter->fmt_out.video.i_height, + p_filter->fmt_out.video.i_width, + p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_height + - p_filter->fmt_out.video.i_visible_height + - p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_x_offset, + p_filter->fmt_out.video.i_width + - p_filter->fmt_out.video.i_visible_width + - p_filter->fmt_out.video.i_x_offset, + padcolor); + } + } } /* Special case for RV32 -> YUVA */ diff --git a/modules/stream_out/transcode.c b/modules/stream_out/transcode.c index 2e7567b9f3..4880d395a8 100644 --- a/modules/stream_out/transcode.c +++ b/modules/stream_out/transcode.c @@ -6,7 +6,7 @@ * * Authors: Laurent Aimar * Gildas Bazin - * Jean-Paul Saman + * Jean-Paul Saman * * 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 @@ -97,6 +97,29 @@ #define CROPRIGHT_LONGTEXT N_( \ "Allows you to specify the right coordinate for the video cropping." ) +#define PADDTOP_TEXT N_("Video padd top") +#define PADDTOP_LONGTEXT N_( \ + "Allows you to specify padding of black lines at the top." ) +#define PADDLEFT_TEXT N_("Video padd left") +#define PADDLEFT_LONGTEXT N_( \ + "Allows you to specify padding of black lines on the left." ) +#define PADDBOTTOM_TEXT N_("Video padd bottom") +#define PADDBOTTOM_LONGTEXT N_( \ + "Allows you to specify padding of black lines at the top." ) +#define PADDRIGHT_TEXT N_("Video padd right") +#define PADDRIGHT_LONGTEXT N_( \ + "Allows you to specify padding of black lines on the right." ) + +#define CANVAS_WIDTH_TEXT N_("Video canvas width") +#define CANVAS_WIDTH_LONGTEXT N_( \ + "Allows to padd or crop the frame to a specified width" ) +#define CANVAS_HEIGHT_TEXT N_("Video canvas height") +#define CANVAS_HEIGHT_LONGTEXT N_( \ + "Allows to padd or crop the frame to a specified height" ) +#define CANVAS_ASPECT_TEXT N_("Video canvas aspect ratio") +#define CANVAS_ASPECT_LONGTEXT N_( \ + "Set aspect (like 4:3) of video canvas and letterbox accordingly" ) + #define AENC_TEXT N_("Audio encoder") #define AENC_LONGTEXT N_( \ "Allows you to specify the audio encoder to use and its associated " \ @@ -211,6 +234,22 @@ vlc_module_begin(); add_integer( SOUT_CFG_PREFIX "cropright", 0, NULL, CROPRIGHT_TEXT, CROPRIGHT_LONGTEXT, VLC_TRUE ); + add_integer( SOUT_CFG_PREFIX "paddtop", 0, NULL, PADDTOP_TEXT, + PADDTOP_LONGTEXT, VLC_TRUE ); + add_integer( SOUT_CFG_PREFIX "paddleft", 0, NULL, PADDLEFT_TEXT, + PADDLEFT_LONGTEXT, VLC_TRUE ); + add_integer( SOUT_CFG_PREFIX "paddbottom", 0, NULL, PADDBOTTOM_TEXT, + PADDBOTTOM_LONGTEXT, VLC_TRUE ); + add_integer( SOUT_CFG_PREFIX "paddright", 0, NULL, PADDRIGHT_TEXT, + PADDRIGHT_LONGTEXT, VLC_TRUE ); + + add_integer( SOUT_CFG_PREFIX "canvas-width", 0, NULL, CANVAS_WIDTH_TEXT, + CANVAS_WIDTH_LONGTEXT, VLC_TRUE ); + add_integer( SOUT_CFG_PREFIX "canvas-height", 0, NULL, CANVAS_HEIGHT_TEXT, + CANVAS_HEIGHT_LONGTEXT, VLC_TRUE ); + add_string( SOUT_CFG_PREFIX "canvas-aspect", NULL, NULL, CANVAS_ASPECT_TEXT, + CANVAS_ASPECT_LONGTEXT, VLC_FALSE ); + set_section( N_("Audio"), NULL ); add_string( SOUT_CFG_PREFIX "aenc", NULL, NULL, AENC_TEXT, AENC_LONGTEXT, VLC_FALSE ); @@ -250,6 +289,8 @@ vlc_module_end(); static const char *ppsz_sout_options[] = { "venc", "vcodec", "vb", "croptop", "cropbottom", "cropleft", "cropright", + "paddtop", "paddbottom", "paddleft", "paddright", + "canvas-width", "canvas-height", "canvas-aspect", "scale", "fps", "width", "height", "vfilter", "deinterlace", "deinterlace-module", "threads", "hurry-up", "aenc", "acodec", "ab", "samplerate", "channels", "senc", "scodec", "soverlay", "sfilter", @@ -355,6 +396,26 @@ struct sout_stream_sys_t int i_crop_right; int i_crop_left; + int i_padd_top; + int i_padd_bottom; + int i_padd_right; + int i_padd_left; + + int i_canvas_width; + int i_canvas_height; + int i_canvas_aspect; + + /* Video, calculated */ + int i_src_x_offset; + int i_src_y_offset; + int i_crop_width; + int i_crop_height; + + int i_dst_x_offset; + int i_dst_y_offset; + int i_nopadd_width; + int i_nopadd_height; + /* SPU */ vlc_fourcc_t i_scodec; /* codec spu (0 if not transcode) */ char *psz_senc; @@ -550,6 +611,44 @@ static int Open( vlc_object_t *p_this ) var_Get( p_stream, SOUT_CFG_PREFIX "cropright", &val ); p_sys->i_crop_right = val.i_int; + var_Get( p_stream, SOUT_CFG_PREFIX "paddtop", &val ); + p_sys->i_padd_top = val.i_int; + var_Get( p_stream, SOUT_CFG_PREFIX "paddbottom", &val ); + p_sys->i_padd_bottom = val.i_int; + var_Get( p_stream, SOUT_CFG_PREFIX "paddleft", &val ); + p_sys->i_padd_left = val.i_int; + var_Get( p_stream, SOUT_CFG_PREFIX "paddright", &val ); + p_sys->i_padd_right = val.i_int; + + var_Get( p_stream, SOUT_CFG_PREFIX "canvas-width", &val ); + p_sys->i_canvas_width = val.i_int; + var_Get( p_stream, SOUT_CFG_PREFIX "canvas-height", &val ); + p_sys->i_canvas_height = val.i_int; + + var_Get( p_stream, SOUT_CFG_PREFIX "canvas-aspect", &val ); + if ( val.psz_string ) + { + char *psz_parser = strchr( val.psz_string, ':' ); + + if( psz_parser ) + { + *psz_parser++ = '\0'; + p_sys->i_canvas_aspect = atoi( val.psz_string ) * VOUT_ASPECT_FACTOR + / atoi( psz_parser ); + } + else + { + msg_Warn( p_stream, "bad aspect ratio %s", val.psz_string ); + p_sys->i_canvas_aspect = 0; + } + + free( val.psz_string ); + } + else + { + p_sys->i_canvas_aspect = 0; + } + var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val ); p_sys->i_threads = val.i_int; var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val ); @@ -866,8 +965,8 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt ) /* Complete destination format */ id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec; - id->p_encoder->fmt_out.video.i_width = p_sys->i_width; - id->p_encoder->fmt_out.video.i_height = p_sys->i_height; + id->p_encoder->fmt_out.video.i_width = p_sys->i_width & ~1; + id->p_encoder->fmt_out.video.i_height = p_sys->i_height & ~1; id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate; /* Build decoder -> filter -> encoder chain */ @@ -1536,67 +1635,234 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, { sout_stream_sys_t *p_sys = p_stream->p_sys; - /* Hack because of the copy packetizer which can fail to detect the - * proper size (which forces us to wait until the 1st frame - * is decoded) */ - int i_width = id->p_decoder->fmt_out.video.i_width - - p_sys->i_crop_left - p_sys->i_crop_right; - int i_height = id->p_decoder->fmt_out.video.i_height - - p_sys->i_crop_top - p_sys->i_crop_bottom; + /* Calculate scaling, padding, cropping etc. */ + /* width/height of source */ + int i_src_width = id->p_decoder->fmt_out.video.i_width; + int i_src_height = id->p_decoder->fmt_out.video.i_height; + + /* with/height scaling */ + float f_scale_width = 1; + float f_scale_height = 1; + + /* width/height of output stream */ + int i_dst_width; + int i_dst_height; + + /* aspect ratio */ + float f_aspect = (float)id->p_decoder->fmt_out.video.i_aspect / + VOUT_ASPECT_FACTOR; + + msg_Dbg( p_stream, "decoder aspect is %i:%i", + id->p_decoder->fmt_out.video.i_aspect, VOUT_ASPECT_FACTOR ); + + /* Change f_aspect from source frame to source pixel */ + f_aspect = f_aspect * i_src_height / i_src_width; + msg_Dbg( p_stream, "source pixel aspect is %f:1", f_aspect ); + + /* width/height after cropping */ + p_sys->i_src_x_offset = p_sys->i_crop_left & ~1; + p_sys->i_src_y_offset = p_sys->i_crop_top & ~1; + p_sys->i_crop_width = i_src_width - ( p_sys->i_crop_left & ~1 ) - + ( p_sys->i_crop_right & ~1 ); + p_sys->i_crop_height = i_src_height - ( p_sys->i_crop_top & ~1 ) - + ( p_sys->i_crop_bottom & ~1 ); + + /* Calculate scaling factor for specified parameters */ if( id->p_encoder->fmt_out.video.i_width <= 0 && id->p_encoder->fmt_out.video.i_height <= 0 && p_sys->f_scale ) { + /* Global scaling. Make sure width will remain a factor of 16 */ float f_real_scale; - id->p_encoder->fmt_out.video.i_width = i_width * p_sys->f_scale; - if( id->p_encoder->fmt_out.video.i_width % 16 <= 7 && - id->p_encoder->fmt_out.video.i_width >= 16 ) - id->p_encoder->fmt_out.video.i_width -= - id->p_encoder->fmt_out.video.i_width % 16; + int i_new_height; + int i_new_width = i_src_width * p_sys->f_scale; + + if( i_new_width % 16 <= 7 && i_new_width >= 16 ) + i_new_width -= i_new_width % 16; else - id->p_encoder->fmt_out.video.i_width += - 16 - id->p_encoder->fmt_out.video.i_width % 16; + i_new_width += 16 - i_new_width % 16; + + f_real_scale = (float)( i_new_width ) / (float) i_src_width; - f_real_scale = (float)( id->p_encoder->fmt_out.video.i_width) / - (float)i_width; + i_new_height = __MAX( 16, i_src_height * (float)f_real_scale ); - id->p_encoder->fmt_out.video.i_height = __MAX( 16, - i_height * f_real_scale ); - msg_Dbg( p_stream, "scaling to %ix%i", - id->p_encoder->fmt_out.video.i_width, - id->p_encoder->fmt_out.video.i_height ); + f_scale_width = f_real_scale; + f_scale_height = (float) i_new_height / (float) i_src_height; } else if( id->p_encoder->fmt_out.video.i_width > 0 && id->p_encoder->fmt_out.video.i_height <= 0 ) { - id->p_encoder->fmt_out.video.i_height = - id->p_encoder->fmt_out.video.i_width / (double)i_width * i_height; + /* Only width specified */ + f_scale_width = (float)id->p_encoder->fmt_out.video.i_width / + p_sys->i_crop_width; + f_scale_height = f_scale_width; } else if( id->p_encoder->fmt_out.video.i_width <= 0 && id->p_encoder->fmt_out.video.i_height > 0 ) { - id->p_encoder->fmt_out.video.i_width = - id->p_encoder->fmt_out.video.i_height / (double)i_height * i_width; - } - - if( p_sys->i_maxwidth - && id->p_encoder->fmt_out.video.i_width > p_sys->i_maxwidth ) - id->p_encoder->fmt_out.video.i_width = p_sys->i_maxwidth; - if( p_sys->i_maxheight - && id->p_encoder->fmt_out.video.i_height > p_sys->i_maxheight ) - id->p_encoder->fmt_out.video.i_height = p_sys->i_maxheight; - - /* Make sure the size is at least a multiple of 2 */ - id->p_encoder->fmt_out.video.i_width = - (id->p_encoder->fmt_out.video.i_width + 1) >> 1 << 1; - id->p_encoder->fmt_out.video.i_height = - (id->p_encoder->fmt_out.video.i_height + 1) >> 1 << 1; - - id->p_encoder->fmt_in.video.i_width = - id->p_encoder->fmt_out.video.i_width; - id->p_encoder->fmt_in.video.i_height = - id->p_encoder->fmt_out.video.i_height; - + /* Only height specified */ + f_scale_height = (float)id->p_encoder->fmt_out.video.i_height / + p_sys->i_crop_height; + f_scale_width = f_scale_height; + } + else if( id->p_encoder->fmt_out.video.i_width > 0 && + id->p_encoder->fmt_out.video.i_height > 0 ) + { + /* Width and height specified */ + f_scale_width = (float)id->p_encoder->fmt_out.video.i_width + / p_sys->i_crop_width; + f_scale_height = (float)id->p_encoder->fmt_out.video.i_height + / p_sys->i_crop_height; + } + + /* check maxwidth and maxheight */ + /* note: maxwidth and maxheight currently does not handle + * canvas and padding, just scaling and cropping. */ + + if( p_sys->i_maxwidth && f_scale_width > (float)p_sys->i_maxwidth / + p_sys->i_crop_width ) + { + f_scale_width = (float)p_sys->i_maxwidth / p_sys->i_crop_width; + } + if( p_sys->i_maxheight && f_scale_height > (float)p_sys->i_maxheight / + p_sys->i_crop_height ) + { + f_scale_height = (float)p_sys->i_maxheight / p_sys->i_crop_height; + } + + /* Change aspect ratio from source pixel to scaled pixel */ + f_aspect = f_aspect * f_scale_height / f_scale_width; + msg_Dbg( p_stream, "scaled pixel aspect is %f:1", f_aspect ); + + /* Correct scaling for target aspect ratio */ + /* Shrink video if necessary */ + if ( p_sys->i_canvas_aspect > 0 ) + { + float f_target_aspect = (float)p_sys->i_canvas_aspect / + VOUT_ASPECT_FACTOR; + + if( p_sys->i_canvas_width > 0 && p_sys->i_canvas_height > 0) + { + /* Calculate pixel aspect of canvas */ + f_target_aspect = f_target_aspect / p_sys->i_canvas_width * + p_sys->i_canvas_height; + } + if( f_target_aspect > f_aspect ) + { + /* Reduce width scale to increase aspect */ + f_scale_width = f_scale_width * f_aspect / f_target_aspect; + } + else + { + /* Reduce height scale to decrease aspect */ + f_scale_height = f_scale_height * f_target_aspect / f_aspect; + } + f_aspect = f_target_aspect; + msg_Dbg( p_stream, "Canvas scaled pixel aspect is %f:1", f_aspect ); + } + + /* f_scale_width and f_scale_height are now final */ + + /* Calculate width, height from scaling */ + /* Make sure its multiple of 2 */ + + i_dst_width = 2 * (int)( p_sys->i_crop_width * f_scale_width / 2 + 0.5 ); + i_dst_height = 2 * + (int)( p_sys->i_crop_height * f_scale_height / 2 + 0.5 ); + + p_sys->i_nopadd_width = i_dst_width; + p_sys->i_nopadd_height = i_dst_height; + p_sys->i_dst_x_offset = 0; + p_sys->i_dst_y_offset = 0; + + /* Handle canvas and padding */ + + if( p_sys->i_canvas_width <= 0 ) + { + /* No canvas width set, add explicit padding border */ + i_dst_width = p_sys->i_nopadd_width + ( p_sys->i_padd_left & ~1 ) + + ( p_sys->i_padd_right & ~1 ); + p_sys->i_dst_x_offset = ( p_sys->i_padd_left & ~1 ); + } + else + { + /* Canvas set, check if we have to padd or crop */ + + if( p_sys->i_canvas_width < p_sys->i_nopadd_width ) + { + /* need to crop more, but keep same scaling */ + int i_crop = 2 * (int)( ( p_sys->i_canvas_width & ~1 ) / + f_scale_width / 2 + 0.5 ); + + p_sys->i_src_x_offset += ( ( p_sys->i_crop_width - i_crop ) / 2 ) + & ~1; + p_sys->i_crop_width = i_crop; + i_dst_width = p_sys->i_canvas_width & ~1; + p_sys->i_nopadd_width = i_dst_width; + } + else if( p_sys->i_canvas_width > p_sys->i_nopadd_width ) + { + /* need to padd */ + i_dst_width = p_sys->i_canvas_width & ~1; + p_sys->i_dst_x_offset = ( i_dst_width - p_sys->i_nopadd_width )/2; + p_sys->i_dst_x_offset = p_sys->i_dst_x_offset & ~1; + } + } + + if( p_sys->i_canvas_height <= 0 ) + { + /* No canvas set, add padding border */ + i_dst_height = p_sys->i_nopadd_height + ( p_sys->i_padd_top & ~1 ) + + ( p_sys->i_padd_bottom & ~1 ); + p_sys->i_dst_y_offset = ( p_sys->i_padd_top & ~1 ); + } + else + { + /* Canvas set, check if we have to padd or crop */ + + if( p_sys->i_canvas_height < p_sys->i_nopadd_height ) + { + /* need to crop more, but keep same scaling */ + int i_crop = 2 * (int)( ( p_sys->i_canvas_height & ~1 ) / + f_scale_height / 2 + 0.5 ); + + p_sys->i_src_y_offset += ( ( p_sys->i_crop_height - i_crop ) / 2 ) + & ~1; + p_sys->i_crop_height = i_crop; + i_dst_height = p_sys->i_canvas_height & ~1; + p_sys->i_nopadd_height = i_dst_height; + } + else if( p_sys->i_canvas_height > p_sys->i_nopadd_height ) + { + /* need to padd */ + i_dst_height = p_sys->i_canvas_height & ~1; + p_sys->i_dst_y_offset = ( i_dst_height - p_sys->i_nopadd_height ) + /2; + p_sys->i_dst_y_offset = p_sys->i_dst_y_offset & ~1; + } + } + + + /* Change aspect ratio from scaled pixel to output frame */ + + f_aspect = f_aspect * i_dst_width / i_dst_height; + + /* Store calculated values */ + id->p_encoder->fmt_out.video.i_width = i_dst_width; + id->p_encoder->fmt_out.video.i_height = i_dst_height; + + id->p_encoder->fmt_in.video.i_width = i_dst_width; + id->p_encoder->fmt_in.video.i_height = i_dst_height; + + msg_Dbg( p_stream, "Source %ix%i, crop %ix%i, " + "destination %ix%i, padding %ix%i", + i_src_width, i_src_height, + p_sys->i_crop_width, p_sys->i_crop_height, + p_sys->i_nopadd_width, p_sys->i_nopadd_height, + i_dst_width, i_dst_height + ); + + /* Handle frame rate conversion */ if( !id->p_encoder->fmt_out.video.i_frame_rate || !id->p_encoder->fmt_out.video.i_frame_rate_base ) { @@ -1628,12 +1894,13 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, /* Check whether a particular aspect ratio was requested */ if( !id->p_encoder->fmt_out.video.i_aspect ) { - id->p_encoder->fmt_out.video.i_aspect = - id->p_decoder->fmt_out.video.i_aspect; + id->p_encoder->fmt_out.video.i_aspect = (int)( f_aspect * VOUT_ASPECT_FACTOR + 0.5 ); } id->p_encoder->fmt_in.video.i_aspect = id->p_encoder->fmt_out.video.i_aspect; + msg_Dbg( p_stream, "encoder aspect is %i:%i", id->p_encoder->fmt_out.video.i_aspect, VOUT_ASPECT_FACTOR ); + id->p_encoder->p_module = module_Need( id->p_encoder, "encoder", p_sys->psz_venc, VLC_TRUE ); if( !id->p_encoder->p_module ) @@ -1850,12 +2117,14 @@ static int transcode_video_process( sout_stream_t *p_stream, /* Check if we need a filter for chroma conversion or resizing */ if( id->p_decoder->fmt_out.video.i_chroma != id->p_encoder->fmt_in.video.i_chroma || - id->p_decoder->fmt_out.video.i_width != - id->p_encoder->fmt_out.video.i_width || - id->p_decoder->fmt_out.video.i_height != - id->p_encoder->fmt_out.video.i_height || - p_sys->i_crop_top > 0 || p_sys->i_crop_bottom > 0 || - p_sys->i_crop_left > 0 || p_sys->i_crop_right > 0 ) + + (int)id->p_decoder->fmt_out.video.i_width != p_sys->i_crop_width || + p_sys->i_crop_width != p_sys->i_nopadd_width || + p_sys->i_nopadd_width != (int)id->p_encoder->fmt_out.video.i_width || + + (int)id->p_decoder->fmt_out.video.i_height != p_sys->i_crop_height || + p_sys->i_crop_height != p_sys->i_nopadd_height || + p_sys->i_nopadd_height != (int)id->p_encoder->fmt_out.video.i_height) { id->pp_filter[id->i_filter] = vlc_object_create( p_stream, VLC_OBJECT_FILTER ); @@ -1869,9 +2138,21 @@ static int transcode_video_process( sout_stream_t *p_stream, id->pp_filter[id->i_filter]->fmt_in = id->p_decoder->fmt_out; id->pp_filter[id->i_filter]->fmt_out = id->p_encoder->fmt_in; id->pp_filter[id->i_filter]->p_cfg = NULL; + + + id->pp_filter[id->i_filter]->fmt_in.video.i_x_offset = p_sys->i_src_x_offset; + id->pp_filter[id->i_filter]->fmt_in.video.i_y_offset = p_sys->i_src_y_offset; + id->pp_filter[id->i_filter]->fmt_in.video.i_visible_width = p_sys->i_crop_width; + id->pp_filter[id->i_filter]->fmt_in.video.i_visible_height = p_sys->i_crop_height; + + id->pp_filter[id->i_filter]->fmt_out.video.i_x_offset = p_sys->i_dst_x_offset; + id->pp_filter[id->i_filter]->fmt_out.video.i_y_offset = p_sys->i_dst_y_offset; + id->pp_filter[id->i_filter]->fmt_out.video.i_visible_width = p_sys->i_nopadd_width; + id->pp_filter[id->i_filter]->fmt_out.video.i_visible_height = p_sys->i_nopadd_height; + id->pp_filter[id->i_filter]->p_module = module_Need( id->pp_filter[id->i_filter], - "video filter2", 0, 0 ); + "crop padd", 0, 0 ); if( id->pp_filter[id->i_filter]->p_module ) { id->pp_filter[id->i_filter]->p_owner =