static void transcode_video_filter_init( sout_stream_t *p_stream, sout_stream_id_t *id ) { id->p_f_chain = filter_chain_New( p_stream, "video filter2", false, transcode_video_filter_allocation_init, transcode_video_filter_allocation_clear, p_stream->p_sys ); /* Deinterlace */ if( p_stream->p_sys->b_deinterlace ) { filter_chain_AppendFilter( id->p_f_chain, p_stream->p_sys->psz_deinterlace, p_stream->p_sys->p_deinterlace_cfg, &id->p_decoder->fmt_out, &id->p_decoder->fmt_out ); } /* Take care of the scaling and chroma conversions */ 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_in.video.i_width ) || ( id->p_decoder->fmt_out.video.i_height != id->p_encoder->fmt_in.video.i_height ) ) { filter_chain_AppendFilter( id->p_f_chain, NULL, NULL, &id->p_decoder->fmt_out, &id->p_encoder->fmt_in ); } if( p_stream->p_sys->psz_vf2 ) { const es_format_t *p_fmt_out; id->p_uf_chain = filter_chain_New( p_stream, "video filter2", true, transcode_video_filter_allocation_init, transcode_video_filter_allocation_clear, p_stream->p_sys ); filter_chain_Reset( id->p_uf_chain, &id->p_encoder->fmt_in, &id->p_encoder->fmt_in ); filter_chain_AppendFromString( id->p_uf_chain, p_stream->p_sys->psz_vf2 ); p_fmt_out = filter_chain_GetFmtOut( id->p_uf_chain ); es_format_Copy( &id->p_encoder->fmt_in, p_fmt_out ); id->p_encoder->fmt_out.video.i_width = id->p_encoder->fmt_in.video.i_width; id->p_encoder->fmt_out.video.i_height = id->p_encoder->fmt_in.video.i_height; id->p_encoder->fmt_out.video.i_sar_num = id->p_encoder->fmt_in.video.i_sar_num; id->p_encoder->fmt_out.video.i_sar_den = id->p_encoder->fmt_in.video.i_sar_den; } }
static int CreateChain( filter_chain_t *p_chain, es_format_t *p_fmt_mid, config_chain_t *p_cfg ) { filter_t *p_filter1; if( !( p_filter1 = filter_chain_AppendFilter( p_chain, NULL, p_cfg, NULL, p_fmt_mid )) ) return VLC_EGENERIC; if( !filter_chain_AppendFilter( p_chain, NULL, p_cfg, p_fmt_mid, NULL ) ) { filter_chain_DeleteFilter( p_chain, p_filter1 ); return VLC_EGENERIC; } return VLC_SUCCESS; }
static int BuildFilterChain( filter_t *p_filter ) { es_format_t fmt_mid; int i_ret = VLC_EGENERIC; filter_sys_t *p_sys = p_filter->p_sys; /* Now try chroma format list */ const vlc_fourcc_t *pi_allowed_chromas = get_allowed_chromas( p_filter ); for( int i = 0; pi_allowed_chromas[i]; i++ ) { filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out ); const vlc_fourcc_t i_chroma = pi_allowed_chromas[i]; if( i_chroma == p_filter->fmt_in.i_codec || i_chroma == p_filter->fmt_out.i_codec ) continue; msg_Dbg( p_filter, "Trying to use chroma %4.4s as middle man", (char*)&i_chroma ); es_format_Copy( &fmt_mid, &p_filter->fmt_in ); fmt_mid.i_codec = fmt_mid.video.i_chroma = i_chroma; fmt_mid.video.i_rmask = 0; fmt_mid.video.i_gmask = 0; fmt_mid.video.i_bmask = 0; video_format_FixRgb(&fmt_mid.video); if( filter_chain_AppendConverter( p_sys->p_chain, NULL, &fmt_mid ) == VLC_SUCCESS ) { p_sys->p_video_filter = filter_chain_AppendFilter( p_sys->p_chain, p_filter->psz_name, p_filter->p_cfg, &fmt_mid, &fmt_mid ); if( p_sys->p_video_filter ) { filter_AddProxyCallbacks( p_filter, p_sys->p_video_filter, RestartFilterCallback ); if (p_sys->p_video_filter->pf_video_mouse != NULL) p_filter->pf_video_mouse = ChainMouse; es_format_Clean( &fmt_mid ); i_ret = VLC_SUCCESS; break; } } es_format_Clean( &fmt_mid ); } if( i_ret != VLC_SUCCESS ) filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, &p_filter->fmt_out ); return i_ret; }
static void transcode_video_filter_init( sout_stream_t *p_stream, sout_stream_id_t *id ) { es_format_t *p_fmt_out = &id->p_decoder->fmt_out; id->p_encoder->fmt_in.video.i_chroma = id->p_encoder->fmt_in.i_codec; id->p_f_chain = filter_chain_New( p_stream, "video filter2", false, transcode_video_filter_allocation_init, transcode_video_filter_allocation_clear, p_stream->p_sys ); filter_chain_Reset( id->p_f_chain, p_fmt_out, p_fmt_out ); /* Deinterlace */ if( p_stream->p_sys->b_deinterlace ) { filter_chain_AppendFilter( id->p_f_chain, p_stream->p_sys->psz_deinterlace, p_stream->p_sys->p_deinterlace_cfg, &id->p_decoder->fmt_out, &id->p_decoder->fmt_out ); p_fmt_out = filter_chain_GetFmtOut( id->p_f_chain ); } /* Check that we have visible_width/height*/ if( !p_fmt_out->video.i_visible_height ) p_fmt_out->video.i_visible_height = p_fmt_out->video.i_height; if( !p_fmt_out->video.i_visible_width ) p_fmt_out->video.i_visible_width = p_fmt_out->video.i_width; if( p_stream->p_sys->psz_vf2 ) { id->p_uf_chain = filter_chain_New( p_stream, "video filter2", true, transcode_video_filter_allocation_init, transcode_video_filter_allocation_clear, p_stream->p_sys ); filter_chain_Reset( id->p_uf_chain, p_fmt_out, &id->p_encoder->fmt_in ); filter_chain_AppendFromString( id->p_uf_chain, p_stream->p_sys->psz_vf2 ); p_fmt_out = filter_chain_GetFmtOut( id->p_uf_chain ); es_format_Copy( &id->p_encoder->fmt_in, p_fmt_out ); id->p_encoder->fmt_out.video.i_width = id->p_encoder->fmt_in.video.i_width; id->p_encoder->fmt_out.video.i_height = id->p_encoder->fmt_in.video.i_height; id->p_encoder->fmt_out.video.i_sar_num = id->p_encoder->fmt_in.video.i_sar_num; id->p_encoder->fmt_out.video.i_sar_den = id->p_encoder->fmt_in.video.i_sar_den; } }
static int CreateChain( filter_t *p_parent, es_format_t *p_fmt_mid, config_chain_t *p_cfg ) { filter_chain_Reset( p_parent->p_sys->p_chain, &p_parent->fmt_in, &p_parent->fmt_out ); filter_t *p_filter; if( p_parent->fmt_in.video.orientation != p_fmt_mid->video.orientation) { p_filter = AppendTransform( p_parent->p_sys->p_chain, &p_parent->fmt_in, p_fmt_mid ); } else { p_filter = filter_chain_AppendFilter( p_parent->p_sys->p_chain, NULL, p_cfg, NULL, p_fmt_mid ); } if( !p_filter ) return VLC_EGENERIC; //Check if first filter was enough (transform filter most likely): if( es_format_IsSimilar(&p_filter->fmt_out, &p_parent->fmt_out )) return VLC_SUCCESS; if( p_fmt_mid->video.orientation != p_parent->fmt_out.video.orientation) { p_filter = AppendTransform( p_parent->p_sys->p_chain, p_fmt_mid, &p_parent->fmt_out ); } else { p_filter = filter_chain_AppendFilter( p_parent->p_sys->p_chain, NULL, p_cfg, p_fmt_mid, NULL ); } if( !p_filter ) { //Clean up. filter_chain_Reset( p_parent->p_sys->p_chain, NULL, NULL ); return VLC_EGENERIC; } return VLC_SUCCESS; }
static filter_t * AppendTransform( filter_chain_t *p_chain, const es_format_t *p_fmt1, const es_format_t *p_fmt2 ) { video_transform_t transform = video_format_GetTransform(p_fmt1->video.orientation, p_fmt2->video.orientation); const char *type; switch ( transform ) { case TRANSFORM_R90: type = "90"; break; case TRANSFORM_R180: type = "180"; break; case TRANSFORM_R270: type = "270"; break; case TRANSFORM_HFLIP: type = "hflip"; break; case TRANSFORM_VFLIP: type = "vflip"; break; case TRANSFORM_TRANSPOSE: type = "transpose"; break; case TRANSFORM_ANTI_TRANSPOSE: type = "antitranspose"; break; default: type = NULL; break; } if( !type ) return NULL; config_chain_t *cfg; char *name; char config[100]; snprintf( config, 100, "transform{type=%s}", type ); char *next = config_ChainCreate( &name, &cfg, config ); filter_t *p_filter = filter_chain_AppendFilter( p_chain, name, cfg, p_fmt1, p_fmt2 ); config_ChainDestroy(cfg); free(name); free(next); return p_filter; }
/* Take care of the scaling and chroma conversions. * * XXX: Shouldn't this really be after p_uf_chain, not p_f_chain, * in case p_uf_chain changes the format? */ static void conversion_video_filter_append( sout_stream_id_t *id ) { const es_format_t *p_fmt_out = &id->p_decoder->fmt_out; if( id->p_f_chain ) p_fmt_out = filter_chain_GetFmtOut( id->p_f_chain ); if( ( p_fmt_out->video.i_chroma != id->p_encoder->fmt_in.video.i_chroma ) || ( p_fmt_out->video.i_width != id->p_encoder->fmt_in.video.i_width ) || ( p_fmt_out->video.i_height != id->p_encoder->fmt_in.video.i_height ) ) { filter_chain_AppendFilter( id->p_f_chain, NULL, NULL, p_fmt_out, &id->p_encoder->fmt_in ); } }
static int transcode_audio_filter_chain_build( sout_stream_t *p_stream, filter_chain_t *p_chain, const es_format_t *p_dst, const es_format_t *p_src ) { if( !transcode_audio_filter_needed( p_dst, p_src ) ) return VLC_SUCCESS; es_format_t current = *p_src; msg_Dbg( p_stream, "Looking for filter " "(%4.4s->%4.4s, channels %d->%d, rate %d->%d)", (const char *)&p_src->i_codec, (const char *)&p_dst->i_codec, p_src->audio.i_channels, p_dst->audio.i_channels, p_src->audio.i_rate, p_dst->audio.i_rate ); /* If any filter is needed, convert to fl32 */ if( current.i_codec != VLC_CODEC_FL32 ) { /* First step, convert to fl32 */ current.i_codec = current.audio.i_format = VLC_CODEC_FL32; aout_FormatPrepare( ¤t.audio ); if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, ¤t ) ) { msg_Err( p_stream, "Failed to find conversion filter to fl32" ); return VLC_EGENERIC; } current = *filter_chain_GetFmtOut( p_chain ); } /* Fix sample rate */ if( current.audio.i_rate != p_dst->audio.i_rate ) { current.audio.i_rate = p_dst->audio.i_rate; aout_FormatPrepare( ¤t.audio ); if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, ¤t ) ) { msg_Err( p_stream, "Failed to find conversion filter for resampling" ); return VLC_EGENERIC; } current = *filter_chain_GetFmtOut( p_chain ); } /* Fix channels */ if( current.audio.i_channels != p_dst->audio.i_channels ) { current.audio.i_channels = p_dst->audio.i_channels; current.audio.i_physical_channels = p_dst->audio.i_physical_channels; current.audio.i_original_channels = p_dst->audio.i_original_channels; if( ( !current.audio.i_physical_channels || !current.audio.i_original_channels ) && current.audio.i_channels < 6 ) current.audio.i_physical_channels = current.audio.i_original_channels = pi_channels_maps[current.audio.i_channels]; aout_FormatPrepare( ¤t.audio ); if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, ¤t ) ) { msg_Err( p_stream, "Failed to find conversion filter for channel mixing" ); return VLC_EGENERIC; } current = *filter_chain_GetFmtOut( p_chain ); } /* And last step, convert to the requested codec */ if( current.i_codec != p_dst->i_codec ) { current.i_codec = p_dst->i_codec; aout_FormatPrepare( ¤t.audio ); if( !filter_chain_AppendFilter( p_chain, NULL, NULL, NULL, ¤t ) ) { msg_Err( p_stream, "Failed to find conversion filter to %4.4s", (const char*)&p_dst->i_codec); return VLC_EGENERIC; } current = *filter_chain_GetFmtOut( p_chain ); } if( transcode_audio_filter_needed( p_dst, ¤t ) ) { /* Weird case, a filter has side effects, doomed */ msg_Err( p_stream, "Failed to create a valid audio filter chain" ); return VLC_EGENERIC; } msg_Dbg( p_stream, "Got complete audio filter chain" ); return VLC_SUCCESS; }
static int Activate( vlc_object_t *p_this ) { filter_t *p_filter = (filter_t *)p_this; unsigned i_canvas_width; /* width of output canvas */ unsigned i_canvas_height; /* height of output canvas */ unsigned i_canvas_aspect; /* canvas PictureAspectRatio */ es_format_t fmt; /* target format after up/down conversion */ char psz_croppadd[100]; int i_padd,i_offset; char *psz_aspect, *psz_parser; bool b_padd; unsigned i_fmt_in_aspect; if( !p_filter->b_allow_fmt_out_change ) { msg_Err( p_filter, "Picture format change isn't allowed" ); return VLC_EGENERIC; } if( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma ) { msg_Err( p_filter, "Input and output chromas don't match" ); return VLC_EGENERIC; } config_ChainParse( p_filter, CFG_PREFIX, ppsz_filter_options, p_filter->p_cfg ); i_canvas_width = var_CreateGetInteger( p_filter, CFG_PREFIX "width" ); i_canvas_height = var_CreateGetInteger( p_filter, CFG_PREFIX "height" ); if( i_canvas_width == 0 || i_canvas_height == 0 ) { msg_Err( p_filter, "Width and height options must be set" ); return VLC_EGENERIC; } if( i_canvas_width & 1 || i_canvas_height & 1 ) { /* If this restriction were ever relaxed, it is very important to * get the field polatiry correct */ msg_Err( p_filter, "Width and height options must be even integers" ); return VLC_EGENERIC; } i_fmt_in_aspect = (int64_t)p_filter->fmt_in.video.i_sar_num * p_filter->fmt_in.video.i_width * VOUT_ASPECT_FACTOR / p_filter->fmt_in.video.i_sar_den / p_filter->fmt_in.video.i_height; psz_aspect = var_CreateGetNonEmptyString( p_filter, CFG_PREFIX "aspect" ); if( psz_aspect ) { psz_parser = strchr( psz_aspect, ':' ); int numerator = atoi( psz_aspect ); int denominator = psz_parser ? atoi( psz_parser+1 ) : 0; denominator = denominator == 0 ? 1 : denominator; i_canvas_aspect = numerator * VOUT_ASPECT_FACTOR / denominator; free( psz_aspect ); if( numerator <= 0 || denominator < 0 ) { msg_Err( p_filter, "Aspect ratio must be strictly positive" ); return VLC_EGENERIC; } } else { /* if there is no user supplied aspect ratio, assume the canvas * has the same sample aspect ratio as the subpicture */ /* aspect = subpic_sar * canvas_width / canvas_height * where subpic_sar = subpic_ph * subpic_par / subpic_pw */ i_canvas_aspect = (uint64_t) p_filter->fmt_in.video.i_height * i_fmt_in_aspect * i_canvas_width / (i_canvas_height * p_filter->fmt_in.video.i_width); } b_padd = var_CreateGetBool( p_filter, CFG_PREFIX "padd" ); filter_sys_t *p_sys = (filter_sys_t *)malloc( sizeof( filter_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; p_filter->p_sys = p_sys; p_sys->p_chain = filter_chain_New( p_filter, "video filter2", true, alloc_init, NULL, p_filter ); if( !p_sys->p_chain ) { msg_Err( p_filter, "Could not allocate filter chain" ); free( p_sys ); return VLC_EGENERIC; } es_format_Copy( &fmt, &p_filter->fmt_in ); /* one dimension will end up with one of the following: */ fmt.video.i_width = i_canvas_width; fmt.video.i_height = i_canvas_height; if( b_padd ) { /* Padd */ if( i_canvas_aspect > i_fmt_in_aspect ) { /* The canvas has a wider aspect than the subpicture: * ie, pillarbox the [scaled] subpicture */ /* The following is derived form: * width = upconverted_subpic_height * subpic_par / canvas_sar * where canvas_sar = canvas_width / (canvas_height * canvas_par) * then simplify */ fmt.video.i_width = i_canvas_width * i_fmt_in_aspect / i_canvas_aspect; if( fmt.video.i_width & 1 ) fmt.video.i_width -= 1; i_padd = (i_canvas_width - fmt.video.i_width) / 2; i_offset = (i_padd & 1); _snprintf( psz_croppadd, 100, "croppadd{paddleft=%d,paddright=%d}", i_padd - i_offset, i_padd + i_offset ); // sunqueen modify } else { /* The canvas has a taller aspect than the subpicture: * ie, letterbox the [scaled] subpicture */ fmt.video.i_height = i_canvas_height * i_canvas_aspect / i_fmt_in_aspect; if( fmt.video.i_height & 1 ) fmt.video.i_height -= 1; i_padd = (i_canvas_height - fmt.video.i_height ) / 2; i_offset = (i_padd & 1); _snprintf( psz_croppadd, 100, "croppadd{paddtop=%d,paddbottom=%d}", i_padd - i_offset, i_padd + i_offset ); // sunqueen modify } } else { /* Crop */ if( i_canvas_aspect < i_fmt_in_aspect ) { /* The canvas has a narrower aspect than the subpicture: * ie, crop the [scaled] subpicture horizontally */ fmt.video.i_width = i_canvas_width * i_fmt_in_aspect / i_canvas_aspect; if( fmt.video.i_width & 1 ) fmt.video.i_width -= 1; i_padd = (fmt.video.i_width - i_canvas_width) / 2; i_offset = (i_padd & 1); _snprintf( psz_croppadd, 100, "croppadd{cropleft=%d,cropright=%d}", i_padd - i_offset, i_padd + i_offset ); // sunqueen modify } else { /* The canvas has a shorter aspect than the subpicture: * ie, crop the [scaled] subpicture vertically */ fmt.video.i_height = i_canvas_height * i_canvas_aspect / i_fmt_in_aspect; if( fmt.video.i_height & 1 ) fmt.video.i_height -= 1; i_padd = (fmt.video.i_height - i_canvas_height) / 2; i_offset = (i_padd & 1); _snprintf( psz_croppadd, 100, "croppadd{croptop=%d,cropbottom=%d}", i_padd - i_offset, i_padd + i_offset ); // sunqueen modify } } /* xxx, should the clean area include the letter-boxing? * probably not, as some codecs can make use of that information * and it should be a scaled version of the input clean area * -- davidf */ fmt.video.i_visible_width = fmt.video.i_width; fmt.video.i_visible_height = fmt.video.i_height; filter_chain_Reset( p_sys->p_chain, &p_filter->fmt_in, &fmt ); /* Append scaling module */ filter_chain_AppendFilter( p_sys->p_chain, NULL, NULL, NULL, NULL ); /* Append padding module */ filter_chain_AppendFromString( p_sys->p_chain, psz_croppadd ); fmt = *filter_chain_GetFmtOut( p_sys->p_chain ); es_format_Copy( &p_filter->fmt_out, &fmt ); p_filter->fmt_out.video.i_sar_num = i_canvas_aspect * p_filter->fmt_out.video.i_height; p_filter->fmt_out.video.i_sar_den = VOUT_ASPECT_FACTOR * p_filter->fmt_out.video.i_width; if( p_filter->fmt_out.video.i_width != i_canvas_width || p_filter->fmt_out.video.i_height != i_canvas_height ) { msg_Warn( p_filter, "Looks like something went wrong. " "Output size is %dx%d while we asked for %dx%d", p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, i_canvas_width, i_canvas_height ); } p_filter->pf_video_filter = Filter; return VLC_SUCCESS; }