Commit 32438ffb authored by Clément Stenac's avatar Clément Stenac

Transcode cropping/padding patch by Udo Richter < udo underscore richter at...

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

parent 8d263243
...@@ -197,6 +197,12 @@ vlc_module_begin(); ...@@ -197,6 +197,12 @@ vlc_module_begin();
set_callbacks( E_(OpenFilter), E_(CloseFilter) ); set_callbacks( E_(OpenFilter), E_(CloseFilter) );
set_description( _("ffmpeg video filter") ); 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 */ /* video filter submodule */
add_submodule(); add_submodule();
set_capability( "video filter2", 0 ); set_capability( "video filter2", 0 );
......
...@@ -70,6 +70,7 @@ void E_(CloseDemux)( vlc_object_t * ); ...@@ -70,6 +70,7 @@ void E_(CloseDemux)( vlc_object_t * );
/* Video filter module */ /* Video filter module */
int E_(OpenFilter)( vlc_object_t * ); int E_(OpenFilter)( vlc_object_t * );
int E_(OpenCropPadd)( vlc_object_t * );
void E_(CloseFilter)( vlc_object_t * ); void E_(CloseFilter)( vlc_object_t * );
int E_(OpenDeinterlace)( vlc_object_t * ); int E_(OpenDeinterlace)( vlc_object_t * );
void E_(CloseDeinterlace)( vlc_object_t * ); void E_(CloseDeinterlace)( vlc_object_t * );
......
...@@ -51,6 +51,7 @@ struct filter_sys_t ...@@ -51,6 +51,7 @@ struct filter_sys_t
vlc_bool_t b_resize; vlc_bool_t b_resize;
vlc_bool_t b_convert; vlc_bool_t b_convert;
vlc_bool_t b_resize_first; vlc_bool_t b_resize_first;
vlc_bool_t b_enable_croppadd;
es_format_t fmt_in; es_format_t fmt_in;
int i_src_ffmpeg_chroma; int i_src_ffmpeg_chroma;
...@@ -61,10 +62,11 @@ struct filter_sys_t ...@@ -61,10 +62,11 @@ struct filter_sys_t
ImgReSampleContext *p_rsc; 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_t *p_filter = (filter_t*)p_this;
filter_sys_t *p_sys; filter_sys_t *p_sys;
...@@ -80,6 +82,20 @@ int E_(OpenFilter)( vlc_object_t *p_this ) ...@@ -80,6 +82,20 @@ int E_(OpenFilter)( vlc_object_t *p_this )
b_resize = b_resize =
p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width || 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; 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 = b_convert =
p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma; 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 ) ...@@ -99,6 +115,7 @@ int E_(OpenFilter)( vlc_object_t *p_this )
/* Misc init */ /* Misc init */
p_sys->p_rsc = NULL; p_sys->p_rsc = NULL;
p_sys->b_enable_croppadd = b_enable_croppadd;
p_sys->i_src_ffmpeg_chroma = p_sys->i_src_ffmpeg_chroma =
E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma ); E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma );
p_sys->i_dst_ffmpeg_chroma = p_sys->i_dst_ffmpeg_chroma =
...@@ -130,6 +147,23 @@ int E_(OpenFilter)( vlc_object_t *p_this ) ...@@ -130,6 +147,23 @@ int E_(OpenFilter)( vlc_object_t *p_this )
return VLC_SUCCESS; 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 * CloseFilter: clean up the filter
*****************************************************************************/ *****************************************************************************/
...@@ -151,11 +185,36 @@ void E_(CloseFilter)( vlc_object_t *p_this ) ...@@ -151,11 +185,36 @@ void E_(CloseFilter)( vlc_object_t *p_this )
static int CheckInit( filter_t *p_filter ) static int CheckInit( filter_t *p_filter )
{ {
filter_sys_t *p_sys = p_filter->p_sys; filter_sys_t *p_sys = p_filter->p_sys;
vlc_bool_t b_change;
if( p_filter->fmt_in.video.i_width != p_sys->fmt_in.video.i_width || 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_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_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 ); if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
p_sys->p_rsc = 0; p_sys->p_rsc = 0;
...@@ -191,12 +250,67 @@ static int CheckInit( filter_t *p_filter ) ...@@ -191,12 +250,67 @@ static int CheckInit( filter_t *p_filter )
p_sys->b_resize_first = VLC_TRUE; 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 ) 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_out.video.i_height,
p_filter->fmt_in.video.i_width, 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 ) if( !p_sys->p_rsc )
{ {
...@@ -229,6 +343,65 @@ static int CheckInit( filter_t *p_filter ) ...@@ -229,6 +343,65 @@ static int CheckInit( filter_t *p_filter )
return VLC_SUCCESS; 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 * Do the processing here
*****************************************************************************/ *****************************************************************************/
...@@ -291,7 +464,43 @@ static picture_t *Process( filter_t *p_filter, picture_t *p_pic ) ...@@ -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_resize_first )
{ {
if( p_sys->b_convert ) p_dst = &p_sys->tmp_pic; 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 ); 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; p_src = p_dst;
} }
} }
...@@ -316,7 +525,42 @@ static picture_t *Process( filter_t *p_filter, picture_t *p_pic ) ...@@ -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 ) if( p_sys->b_resize && !p_sys->b_resize_first && p_sys->p_rsc )
{ {
p_dst = &dest_pic; p_dst = &dest_pic;
#if LIBAVCODEC_BUILD >= 4708
img_resample( p_sys->p_rsc, p_dst, p_src ); 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 */ /* Special case for RV32 -> YUVA */
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Authors: Laurent Aimar <fenrir@via.ecp.fr> * Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Gildas Bazin <gbazin@videolan.org> * Gildas Bazin <gbazin@videolan.org>
* Jean-Paul Saman <jpsaman #_at_# m2x dot nl> * Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -97,6 +97,29 @@ ...@@ -97,6 +97,29 @@
#define CROPRIGHT_LONGTEXT N_( \ #define CROPRIGHT_LONGTEXT N_( \
"Allows you to specify the right coordinate for the video cropping." ) "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_TEXT N_("Audio encoder")
#define AENC_LONGTEXT N_( \ #define AENC_LONGTEXT N_( \
"Allows you to specify the audio encoder to use and its associated " \ "Allows you to specify the audio encoder to use and its associated " \
...@@ -211,6 +234,22 @@ vlc_module_begin(); ...@@ -211,6 +234,22 @@ vlc_module_begin();
add_integer( SOUT_CFG_PREFIX "cropright", 0, NULL, CROPRIGHT_TEXT, add_integer( SOUT_CFG_PREFIX "cropright", 0, NULL, CROPRIGHT_TEXT,
CROPRIGHT_LONGTEXT, VLC_TRUE ); 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 ); set_section( N_("Audio"), NULL );
add_string( SOUT_CFG_PREFIX "aenc", NULL, NULL, AENC_TEXT, add_string( SOUT_CFG_PREFIX "aenc", NULL, NULL, AENC_TEXT,
AENC_LONGTEXT, VLC_FALSE ); AENC_LONGTEXT, VLC_FALSE );
...@@ -250,6 +289,8 @@ vlc_module_end(); ...@@ -250,6 +289,8 @@ vlc_module_end();
static const char *ppsz_sout_options[] = { static const char *ppsz_sout_options[] = {
"venc", "vcodec", "vb", "croptop", "cropbottom", "cropleft", "cropright", "venc", "vcodec", "vb", "croptop", "cropbottom", "cropleft", "cropright",
"paddtop", "paddbottom", "paddleft", "paddright",
"canvas-width", "canvas-height", "canvas-aspect",
"scale", "fps", "width", "height", "vfilter", "deinterlace", "scale", "fps", "width", "height", "vfilter", "deinterlace",
"deinterlace-module", "threads", "hurry-up", "aenc", "acodec", "ab", "deinterlace-module", "threads", "hurry-up", "aenc", "acodec", "ab",
"samplerate", "channels", "senc", "scodec", "soverlay", "sfilter", "samplerate", "channels", "senc", "scodec", "soverlay", "sfilter",
...@@ -355,6 +396,26 @@ struct sout_stream_sys_t ...@@ -355,6 +396,26 @@ struct sout_stream_sys_t
int i_crop_right; int i_crop_right;
int i_crop_left; 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 */ /* SPU */
vlc_fourcc_t i_scodec; /* codec spu (0 if not transcode) */ vlc_fourcc_t i_scodec; /* codec spu (0 if not transcode) */
char *psz_senc; char *psz_senc;
...@@ -550,6 +611,44 @@ static int Open( vlc_object_t *p_this ) ...@@ -550,6 +611,44 @@ static int Open( vlc_object_t *p_this )
var_Get( p_stream, SOUT_CFG_PREFIX "cropright", &val ); var_Get( p_stream, SOUT_CFG_PREFIX "cropright", &val );
p_sys->i_crop_right = val.i_int; 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 ); var_Get( p_stream, SOUT_CFG_PREFIX "threads", &val );
p_sys->i_threads = val.i_int; p_sys->i_threads = val.i_int;
var_Get( p_stream, SOUT_CFG_PREFIX "high-priority", &val ); 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 ) ...@@ -866,8 +965,8 @@ static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt )
/* Complete destination format */ /* Complete destination format */
id->p_encoder->fmt_out.i_codec = p_sys->i_vcodec; 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_width = p_sys->i_width & ~1;
id->p_encoder->fmt_out.video.i_height = p_sys->i_height; id->p_encoder->fmt_out.video.i_height = p_sys->i_height & ~1;
id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate; id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate;
/* Build decoder -> filter -> encoder chain */ /* Build decoder -> filter -> encoder chain */
...@@ -1536,67 +1635,234 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, ...@@ -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; sout_stream_sys_t *p_sys = p_stream->p_sys;
/* Hack because of the copy packetizer which can fail to detect the /* Calculate scaling, padding, cropping etc. */
* 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;
/* 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 && if( id->p_encoder->fmt_out.video.i_width <= 0 &&
id->p_encoder->fmt_out.video.i_height <= 0 && p_sys->f_scale ) 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; float f_real_scale;
id->p_encoder->fmt_out.video.i_width = i_width * p_sys->f_scale; int i_new_height;
if( id->p_encoder->fmt_out.video.i_width % 16 <= 7 && int i_new_width = i_src_width * p_sys->f_scale;
id->p_encoder->fmt_out.video.i_width >= 16 )
id->p_encoder->fmt_out.video.i_width -= if( i_new_width % 16 <= 7 && i_new_width >= 16 )
id->p_encoder->fmt_out.video.i_width % 16; i_new_width -= i_new_width % 16;
else else
id->p_encoder->fmt_out.video.i_width += i_new_width += 16 - i_new_width % 16;
16 - id->p_encoder->fmt_out.video.i_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) / i_new_height = __MAX( 16, i_src_height * (float)f_real_scale );
(float)i_width;
id->p_encoder->fmt_out.video.i_height = __MAX( 16, f_scale_width = f_real_scale;
i_height * f_real_scale ); f_scale_height = (float) i_new_height / (float) i_src_height;
msg_Dbg( p_stream, "scaling to %ix%i",
id->p_encoder->fmt_out.video.i_width,
id->p_encoder->fmt_out.video.i_height );
} }
else if( id->p_encoder->fmt_out.video.i_width > 0 && 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 <= 0 )
{ {
id->p_encoder->fmt_out.video.i_height = /* Only width specified */
id->p_encoder->fmt_out.video.i_width / (double)i_width * i_height; 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 && 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 > 0 )
{ {
id->p_encoder->fmt_out.video.i_width = /* Only height specified */
id->p_encoder->fmt_out.video.i_height / (double)i_height * i_width; f_scale_height = (float)id->p_encoder->fmt_out.video.i_height /
} p_sys->i_crop_height;
f_scale_width = f_scale_height;
if( p_sys->i_maxwidth }
&& id->p_encoder->fmt_out.video.i_width > p_sys->i_maxwidth ) else if( id->p_encoder->fmt_out.video.i_width > 0 &&
id->p_encoder->fmt_out.video.i_width = p_sys->i_maxwidth; id->p_encoder->fmt_out.video.i_height > 0 )
if( p_sys->i_maxheight {
&& id->p_encoder->fmt_out.video.i_height > p_sys->i_maxheight ) /* Width and height specified */
id->p_encoder->fmt_out.video.i_height = p_sys->i_maxheight; f_scale_width = (float)id->p_encoder->fmt_out.video.i_width
/ p_sys->i_crop_width;
/* Make sure the size is at least a multiple of 2 */ f_scale_height = (float)id->p_encoder->fmt_out.video.i_height
id->p_encoder->fmt_out.video.i_width = / p_sys->i_crop_height;
(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; /* check maxwidth and maxheight */
/* note: maxwidth and maxheight currently does not handle
id->p_encoder->fmt_in.video.i_width = * canvas and padding, just scaling and cropping. */
id->p_encoder->fmt_out.video.i_width;
id->p_encoder->fmt_in.video.i_height = if( p_sys->i_maxwidth && f_scale_width > (float)p_sys->i_maxwidth /
id->p_encoder->fmt_out.video.i_height; 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 || if( !id->p_encoder->fmt_out.video.i_frame_rate ||
!id->p_encoder->fmt_out.video.i_frame_rate_base ) !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, ...@@ -1628,12 +1894,13 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream,
/* Check whether a particular aspect ratio was requested */ /* Check whether a particular aspect ratio was requested */
if( !id->p_encoder->fmt_out.video.i_aspect ) if( !id->p_encoder->fmt_out.video.i_aspect )
{ {
id->p_encoder->fmt_out.video.i_aspect = id->p_encoder->fmt_out.video.i_aspect = (int)( f_aspect * VOUT_ASPECT_FACTOR + 0.5 );
id->p_decoder->fmt_out.video.i_aspect;
} }
id->p_encoder->fmt_in.video.i_aspect = id->p_encoder->fmt_in.video.i_aspect =
id->p_encoder->fmt_out.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 = id->p_encoder->p_module =
module_Need( id->p_encoder, "encoder", p_sys->psz_venc, VLC_TRUE ); module_Need( id->p_encoder, "encoder", p_sys->psz_venc, VLC_TRUE );
if( !id->p_encoder->p_module ) if( !id->p_encoder->p_module )
...@@ -1850,12 +2117,14 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -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 */ /* Check if we need a filter for chroma conversion or resizing */
if( id->p_decoder->fmt_out.video.i_chroma != if( id->p_decoder->fmt_out.video.i_chroma !=
id->p_encoder->fmt_in.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 || (int)id->p_decoder->fmt_out.video.i_width != p_sys->i_crop_width ||
id->p_decoder->fmt_out.video.i_height != p_sys->i_crop_width != p_sys->i_nopadd_width ||
id->p_encoder->fmt_out.video.i_height || p_sys->i_nopadd_width != (int)id->p_encoder->fmt_out.video.i_width ||
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_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] = id->pp_filter[id->i_filter] =
vlc_object_create( p_stream, VLC_OBJECT_FILTER ); vlc_object_create( p_stream, VLC_OBJECT_FILTER );
...@@ -1869,9 +2138,21 @@ static int transcode_video_process( sout_stream_t *p_stream, ...@@ -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_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]->fmt_out = id->p_encoder->fmt_in;
id->pp_filter[id->i_filter]->p_cfg = NULL; 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 = id->pp_filter[id->i_filter]->p_module =
module_Need( id->pp_filter[id->i_filter], 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 ) if( id->pp_filter[id->i_filter]->p_module )
{ {
id->pp_filter[id->i_filter]->p_owner = id->pp_filter[id->i_filter]->p_owner =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment