X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_out%2Ftranscode%2Fvideo.c;h=19591e14fe510dabbf2b3c1a1dad6d292d393d1c;hb=20ce6b1fbbbb735b6274d0d5f21d1819719bbe30;hp=189ffd1b7e48717a7f64a85b10ca2a3cc09a6177;hpb=e3a897cf11f768a9b9bf3b3fab9a1b8bc82e9235;p=vlc diff --git a/modules/stream_out/transcode/video.c b/modules/stream_out/transcode/video.c index 189ffd1b7e..19591e14fe 100644 --- a/modules/stream_out/transcode/video.c +++ b/modules/stream_out/transcode/video.c @@ -1,7 +1,7 @@ /***************************************************************************** * video.c: transcoding stream output module (video) ***************************************************************************** - * Copyright (C) 2003-2009 the VideoLAN team + * Copyright (C) 2003-2009 VLC authors and VideoLAN * $Id$ * * Authors: Laurent Aimar @@ -9,19 +9,19 @@ * Jean-Paul Saman * Antoine Cellerier * - * 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 - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -30,11 +30,12 @@ #include "transcode.h" +#include #include #include #include -#define ENC_FRAMERATE (25 * 1000 + .5) +#define ENC_FRAMERATE (25 * 1000) #define ENC_FRAMERATE_BASE 1000 struct decoder_owner_sys_t @@ -42,45 +43,21 @@ struct decoder_owner_sys_t sout_stream_sys_t *p_sys; }; -static void video_del_buffer_decoder( decoder_t *p_decoder, picture_t *p_pic ) +static int video_update_format_decoder( decoder_t *p_dec ) { - VLC_UNUSED(p_decoder); - picture_Release( p_pic ); -} - -static void video_link_picture_decoder( decoder_t *p_dec, picture_t *p_pic ) -{ - VLC_UNUSED(p_dec); - picture_Hold( p_pic ); + p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec; + return 0; } -static void video_unlink_picture_decoder( decoder_t *p_dec, picture_t *p_pic ) +static picture_t *video_new_buffer_decoder( decoder_t *p_dec ) { - VLC_UNUSED(p_dec); - picture_Release( p_pic ); + return picture_NewFromFormat( &p_dec->fmt_out.video ); } -static picture_t *video_new_buffer_decoder( decoder_t *p_dec ) +static picture_t *video_new_buffer_encoder( encoder_t *p_enc ) { - sout_stream_sys_t *p_ssys = p_dec->p_owner->p_sys; - if( p_ssys->i_threads >= 1 ) - { - int i_first_pic = p_ssys->i_first_pic; - - if( p_ssys->i_first_pic != p_ssys->i_last_pic ) - { - /* Encoder still has stuff to encode, wait to clear-up the list */ - while( p_ssys->i_first_pic == i_first_pic ) - { -#warning THERE IS DEFINITELY A BUG! LOCKING IS INSUFFICIENT! - msleep( 10000 ); - barrier (); - } - } - } - - p_dec->fmt_out.video.i_chroma = p_dec->fmt_out.i_codec; - return picture_NewFromFormat( &p_dec->fmt_out.video ); + p_enc->fmt_in.video.i_chroma = p_enc->fmt_in.i_codec; + return picture_NewFromFormat( &p_enc->fmt_in.video ); } static picture_t *transcode_video_filter_buffer_new( filter_t *p_filter ) @@ -88,73 +65,60 @@ static picture_t *transcode_video_filter_buffer_new( filter_t *p_filter ) p_filter->fmt_out.video.i_chroma = p_filter->fmt_out.i_codec; return picture_NewFromFormat( &p_filter->fmt_out.video ); } -static void transcode_video_filter_buffer_del( filter_t *p_filter, picture_t *p_pic ) -{ - VLC_UNUSED(p_filter); - picture_Release( p_pic ); -} - -static int transcode_video_filter_allocation_init( filter_t *p_filter, - void *p_data ) -{ - VLC_UNUSED(p_data); - p_filter->pf_video_buffer_new = transcode_video_filter_buffer_new; - p_filter->pf_video_buffer_del = transcode_video_filter_buffer_del; - return VLC_SUCCESS; -} - -static void transcode_video_filter_allocation_clear( filter_t *p_filter ) -{ - VLC_UNUSED(p_filter); -} static void* EncoderThread( void *obj ) { sout_stream_sys_t *p_sys = (sout_stream_sys_t*)obj; - sout_stream_id_t *id = p_sys->id_video; - picture_t *p_pic; + sout_stream_id_sys_t *id = p_sys->id_video; + picture_t *p_pic = NULL; int canc = vlc_savecancel (); + block_t *p_block = NULL; + + vlc_mutex_lock( &p_sys->lock_out ); for( ;; ) { - block_t *p_block; - - vlc_mutex_lock( &p_sys->lock_out ); - while( !p_sys->b_abort && p_sys->i_last_pic == p_sys->i_first_pic ) + while( !p_sys->b_abort && + (p_pic = picture_fifo_Pop( p_sys->pp_pics )) == NULL ) vlc_cond_wait( &p_sys->cond, &p_sys->lock_out ); - if( p_sys->b_abort ) + if( p_pic ) { + /* release lock while encoding */ vlc_mutex_unlock( &p_sys->lock_out ); - break; - } - - p_pic = p_sys->pp_pics[p_sys->i_first_pic++]; - p_sys->i_first_pic %= PICTURE_RING_SIZE; - vlc_mutex_unlock( &p_sys->lock_out ); - - p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); + p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); + picture_Release( p_pic ); + vlc_mutex_lock( &p_sys->lock_out ); - vlc_mutex_lock( &p_sys->lock_out ); - block_ChainAppend( &p_sys->p_buffers, p_block ); + block_ChainAppend( &p_sys->p_buffers, p_block ); + } - vlc_mutex_unlock( &p_sys->lock_out ); - picture_Release( p_pic ); + if( p_sys->b_abort ) + break; } - while( p_sys->i_last_pic != p_sys->i_first_pic ) + /*Encode what we have in the buffer on closing*/ + while( (p_pic = picture_fifo_Pop( p_sys->pp_pics )) != NULL ) { - p_pic = p_sys->pp_pics[p_sys->i_first_pic++]; - p_sys->i_first_pic %= PICTURE_RING_SIZE; + p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); picture_Release( p_pic ); + block_ChainAppend( &p_sys->p_buffers, p_block ); } - block_ChainRelease( p_sys->p_buffers ); + + /*Now flush encoder*/ + do { + p_block = id->p_encoder->pf_encode_video(id->p_encoder, NULL ); + block_ChainAppend( &p_sys->p_buffers, p_block ); + } while( p_block ); + + vlc_mutex_unlock( &p_sys->lock_out ); vlc_restorecancel (canc); + return NULL; } -int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) +int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_sys_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; @@ -167,10 +131,8 @@ int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) id->p_decoder->pf_decode_video = NULL; id->p_decoder->pf_get_cc = NULL; id->p_decoder->pf_get_cc = 0; + id->p_decoder->pf_vout_format_update = video_update_format_decoder; id->p_decoder->pf_vout_buffer_new = video_new_buffer_decoder; - id->p_decoder->pf_vout_buffer_del = video_del_buffer_decoder; - id->p_decoder->pf_picture_link = video_link_picture_decoder; - id->p_decoder->pf_picture_unlink = video_unlink_picture_decoder; id->p_decoder->p_owner = malloc( sizeof(decoder_owner_sys_t) ); if( !id->p_decoder->p_owner ) return VLC_EGENERIC; @@ -212,8 +174,16 @@ int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) ? id->p_encoder->fmt_out.video.i_height : id->p_decoder->fmt_in.video.i_height ? id->p_decoder->fmt_in.video.i_height : 16; - id->p_encoder->fmt_in.video.i_frame_rate = ENC_FRAMERATE; - id->p_encoder->fmt_in.video.i_frame_rate_base = ENC_FRAMERATE_BASE; + id->p_encoder->fmt_in.video.i_visible_width = + id->p_encoder->fmt_out.video.i_visible_width + ? id->p_encoder->fmt_out.video.i_visible_width + : id->p_decoder->fmt_in.video.i_visible_width + ? id->p_decoder->fmt_in.video.i_visible_width : id->p_encoder->fmt_in.video.i_width; + id->p_encoder->fmt_in.video.i_visible_height = + id->p_encoder->fmt_out.video.i_visible_height + ? id->p_encoder->fmt_out.video.i_visible_height + : id->p_decoder->fmt_in.video.i_visible_height + ? id->p_decoder->fmt_in.video.i_visible_height : id->p_encoder->fmt_in.video.i_height; id->p_encoder->i_threads = p_sys->i_threads; id->p_encoder->p_cfg = p_sys->p_video_cfg; @@ -242,73 +212,156 @@ int transcode_video_new( sout_stream_t *p_stream, sout_stream_id_t *id ) } id->p_encoder->p_module = NULL; - if( p_sys->i_threads >= 1 ) + if( p_sys->i_threads <= 0 ) + return VLC_SUCCESS; + + int i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT : + VLC_THREAD_PRIORITY_VIDEO; + p_sys->id_video = id; + p_sys->pp_pics = picture_fifo_New(); + if( p_sys->pp_pics == NULL ) { - int i_priority = p_sys->b_high_priority ? VLC_THREAD_PRIORITY_OUTPUT : - VLC_THREAD_PRIORITY_VIDEO; - p_sys->id_video = id; - vlc_mutex_init( &p_sys->lock_out ); - vlc_cond_init( &p_sys->cond ); - memset( p_sys->pp_pics, 0, sizeof(p_sys->pp_pics) ); - p_sys->i_first_pic = 0; - p_sys->i_last_pic = 0; - p_sys->p_buffers = NULL; - p_sys->b_abort = 0; - if( vlc_clone( &p_sys->thread, EncoderThread, p_sys, i_priority ) ) - { - msg_Err( p_stream, "cannot spawn encoder thread" ); - module_unneed( id->p_decoder, id->p_decoder->p_module ); - id->p_decoder->p_module = 0; - free( id->p_decoder->p_owner ); - return VLC_EGENERIC; - } + msg_Err( p_stream, "cannot create picture fifo" ); + module_unneed( id->p_decoder, id->p_decoder->p_module ); + id->p_decoder->p_module = NULL; + free( id->p_decoder->p_owner ); + return VLC_ENOMEM; + } + vlc_mutex_init( &p_sys->lock_out ); + vlc_cond_init( &p_sys->cond ); + p_sys->p_buffers = NULL; + p_sys->b_abort = false; + if( vlc_clone( &p_sys->thread, EncoderThread, p_sys, i_priority ) ) + { + msg_Err( p_stream, "cannot spawn encoder thread" ); + vlc_mutex_destroy( &p_sys->lock_out ); + vlc_cond_destroy( &p_sys->cond ); + picture_fifo_Delete( p_sys->pp_pics ); + module_unneed( id->p_decoder, id->p_decoder->p_module ); + id->p_decoder->p_module = NULL; + free( id->p_decoder->p_owner ); + return VLC_EGENERIC; } return VLC_SUCCESS; } +static filter_t *AppendDeinterlaceFilter( sout_stream_t *p_stream, + filter_chain_t *p_chain, + const char *psz_name, + config_chain_t *p_cfg, + const es_format_t *p_fmt_in, + const es_format_t *p_fmt_out ) +{ + filter_t *p_filter = + filter_chain_AppendFilter( p_chain, psz_name, p_cfg, p_fmt_in, p_fmt_out ); + if( p_filter ) + return p_filter; + + msg_Dbg( p_stream, "deinterlace init failed, trying colorspace conversion" ); + + /* Most likely the filter failed due to wrong (e.g. non-planar) input format. + * Try converting to a different color space and try again. + * + * We use the recommended fallback formats in order, to get a conversion that's + * as cheap (both CPU-wise and quality-wise) as possible. + */ + for( const vlc_fourcc_t *fourcc = vlc_fourcc_GetYUVFallback( p_fmt_out->video.i_chroma ); + *fourcc != 0; + ++fourcc ) + { + msg_Dbg( p_stream, "trying deinterlace through colorspace conversion to %4.4s", (char *)fourcc ); + + es_format_t p_new_fmt; + es_format_Copy( &p_new_fmt, p_fmt_in ); + p_new_fmt.video.i_chroma = *fourcc; // XXX: is this enough? + + filter_chain_Reset( p_chain, NULL, NULL ); + + p_filter = filter_chain_AppendFilter( p_chain, + NULL, NULL, + p_fmt_in, + &p_new_fmt ); + if( !p_filter ) + continue; + + p_filter = filter_chain_AppendFilter( p_chain, + psz_name, + p_cfg, + &p_new_fmt, + &p_new_fmt ); + if( p_filter ) + { + msg_Dbg( p_stream, "deinterlace through colorspace conversion to %4.4s succeeded", (char *)fourcc ); + return p_filter; + } + } + + msg_Err( p_stream, "deinterlace init failed, even with colorspace conversion" ); + return NULL; +} + static void transcode_video_filter_init( sout_stream_t *p_stream, - sout_stream_id_t *id ) + sout_stream_id_sys_t *id ) { + filter_owner_t owner = { + .sys = p_stream->p_sys, + .video = { + .buffer_new = transcode_video_filter_buffer_new, + }, + }; + 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_NewVideo( p_stream, false, &owner ); + filter_chain_Reset( id->p_f_chain, p_fmt_out, p_fmt_out ); - 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 ); - } + filter_t *p_filter = + AppendDeinterlaceFilter( p_stream, + id->p_f_chain, + p_stream->p_sys->psz_deinterlace, + p_stream->p_sys->p_deinterlace_cfg, + p_fmt_out, + p_fmt_out ); + if( !p_filter ) + { + msg_Err( p_stream, "deinterlace init failed, even with colorspace conversion" ); + return; + } - /* 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 ) ) + p_fmt_out = filter_chain_GetFmtOut( id->p_f_chain ); + } + if( p_stream->p_sys->b_master_sync ) { - filter_chain_AppendFilter( id->p_f_chain, - NULL, NULL, - &id->p_decoder->fmt_out, - &id->p_encoder->fmt_in ); + filter_chain_AppendFilter( id->p_f_chain, + "fps", + NULL, + p_fmt_out, + &id->p_encoder->fmt_in ); + + 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 ) { - 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_uf_chain = filter_chain_NewVideo( p_stream, true, &owner ); + filter_chain_Reset( id->p_uf_chain, p_fmt_out, &id->p_encoder->fmt_in ); + if( p_fmt_out->video.i_chroma != id->p_encoder->fmt_in.video.i_chroma ) + { + filter_chain_AppendFilter( id->p_uf_chain, + NULL, NULL, + 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 ); @@ -324,134 +377,168 @@ static void transcode_video_filter_init( sout_stream_t *p_stream, } +/* Take care of the scaling and chroma conversions. */ +static void conversion_video_filter_append( sout_stream_id_sys_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( id->p_uf_chain ) + p_fmt_out = filter_chain_GetFmtOut( id->p_uf_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_uf_chain ? id->p_uf_chain : id->p_f_chain, + NULL, NULL, + p_fmt_out, + &id->p_encoder->fmt_in ); + } +} + static void transcode_video_encoder_init( sout_stream_t *p_stream, - sout_stream_id_t *id ) + sout_stream_id_sys_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; + 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( id->p_uf_chain ) { + p_fmt_out = filter_chain_GetFmtOut( id->p_uf_chain ); + } + /* Calculate scaling * 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; + int i_src_visible_width = p_fmt_out->video.i_visible_width; + int i_src_visible_height = p_fmt_out->video.i_visible_height; + + if (i_src_visible_width == 0) + i_src_visible_width = p_fmt_out->video.i_width; + if (i_src_visible_height == 0) + i_src_visible_height = p_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 = (double)id->p_decoder->fmt_out.video.i_sar_num * - id->p_decoder->fmt_out.video.i_width / - id->p_decoder->fmt_out.video.i_sar_den / - id->p_decoder->fmt_out.video.i_height; + float f_aspect = (double)p_fmt_out->video.i_sar_num * + p_fmt_out->video.i_width / + p_fmt_out->video.i_sar_den / + p_fmt_out->video.i_height; - msg_Dbg( p_stream, "decoder aspect is %f:1", f_aspect ); + msg_Dbg( p_stream, "decoder aspect is %f:1", (double) f_aspect ); /* 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 ); + f_aspect = f_aspect * i_src_visible_height / i_src_visible_width; + msg_Dbg( p_stream, "source pixel aspect is %f:1", (double) f_aspect ); /* 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 ) + if( id->p_encoder->fmt_out.video.i_visible_width <= 0 && + id->p_encoder->fmt_out.video.i_visible_height <= 0 && p_sys->f_scale ) { /* Global scaling. Make sure width will remain a factor of 16 */ float f_real_scale; int i_new_height; - int i_new_width = i_src_width * p_sys->f_scale; + int i_new_width = i_src_visible_width * p_sys->f_scale; if( i_new_width % 16 <= 7 && i_new_width >= 16 ) i_new_width -= i_new_width % 16; else i_new_width += 16 - i_new_width % 16; - f_real_scale = (float)( i_new_width ) / (float) i_src_width; + f_real_scale = (float)( i_new_width ) / (float) i_src_visible_width; - i_new_height = __MAX( 16, i_src_height * (float)f_real_scale ); + i_new_height = __MAX( 16, i_src_visible_height * (float)f_real_scale ); f_scale_width = f_real_scale; - f_scale_height = (float) i_new_height / (float) i_src_height; + f_scale_height = (float) i_new_height / (float) i_src_visible_height; } - else if( id->p_encoder->fmt_out.video.i_width > 0 && - id->p_encoder->fmt_out.video.i_height <= 0 ) + else if( id->p_encoder->fmt_out.video.i_visible_width > 0 && + id->p_encoder->fmt_out.video.i_visible_height <= 0 ) { /* Only width specified */ - f_scale_width = (float)id->p_encoder->fmt_out.video.i_width/i_src_width; + f_scale_width = (float)id->p_encoder->fmt_out.video.i_visible_width/i_src_visible_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 ) + else if( id->p_encoder->fmt_out.video.i_visible_width <= 0 && + id->p_encoder->fmt_out.video.i_visible_height > 0 ) { /* Only height specified */ - f_scale_height = (float)id->p_encoder->fmt_out.video.i_height/i_src_height; + f_scale_height = (float)id->p_encoder->fmt_out.video.i_visible_height/i_src_visible_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 ) + else if( id->p_encoder->fmt_out.video.i_visible_width > 0 && + id->p_encoder->fmt_out.video.i_visible_height > 0 ) { /* Width and height specified */ - f_scale_width = (float)id->p_encoder->fmt_out.video.i_width/i_src_width; - f_scale_height = (float)id->p_encoder->fmt_out.video.i_height/i_src_height; + f_scale_width = (float)id->p_encoder->fmt_out.video.i_visible_width/i_src_visible_width; + f_scale_height = (float)id->p_encoder->fmt_out.video.i_visible_height/i_src_visible_height; } /* check maxwidth and maxheight */ if( p_sys->i_maxwidth && f_scale_width > (float)p_sys->i_maxwidth / - i_src_width ) + i_src_visible_width ) { - f_scale_width = (float)p_sys->i_maxwidth / i_src_width; + f_scale_width = (float)p_sys->i_maxwidth / i_src_visible_width; } if( p_sys->i_maxheight && f_scale_height > (float)p_sys->i_maxheight / - i_src_height ) + i_src_visible_height ) { - f_scale_height = (float)p_sys->i_maxheight / i_src_height; + f_scale_height = (float)p_sys->i_maxheight / i_src_visible_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 ); + msg_Dbg( p_stream, "scaled pixel aspect is %f:1", (double) 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)(f_scale_width*i_src_width/2+0.5); - i_dst_height = 2 * (int)(f_scale_height*i_src_height/2+0.5); + /* width/height of output stream */ + int i_dst_visible_width = 2 * lroundf(f_scale_width*i_src_visible_width/2); + int i_dst_visible_height = 2 * lroundf(f_scale_height*i_src_visible_height/2); + int i_dst_width = 2 * lroundf(f_scale_width*p_fmt_out->video.i_width/2); + int i_dst_height = 2 * lroundf(f_scale_height*p_fmt_out->video.i_height/2); /* Change aspect ratio from scaled pixel to output frame */ - f_aspect = f_aspect * i_dst_width / i_dst_height; + f_aspect = f_aspect * i_dst_visible_width / i_dst_visible_height; /* Store calculated values */ - id->p_encoder->fmt_out.video.i_width = - id->p_encoder->fmt_out.video.i_visible_width = i_dst_width; - id->p_encoder->fmt_out.video.i_height = - id->p_encoder->fmt_out.video.i_visible_height = i_dst_height; + id->p_encoder->fmt_out.video.i_width = i_dst_width; + id->p_encoder->fmt_out.video.i_visible_width = i_dst_visible_width; + id->p_encoder->fmt_out.video.i_height = i_dst_height; + id->p_encoder->fmt_out.video.i_visible_height = i_dst_visible_height; - id->p_encoder->fmt_in.video.i_width = - id->p_encoder->fmt_in.video.i_visible_width = i_dst_width; - id->p_encoder->fmt_in.video.i_height = - id->p_encoder->fmt_in.video.i_visible_height = i_dst_height; + id->p_encoder->fmt_in.video.i_width = i_dst_width; + id->p_encoder->fmt_in.video.i_visible_width = i_dst_visible_width; + id->p_encoder->fmt_in.video.i_height = i_dst_height; + id->p_encoder->fmt_in.video.i_visible_height = i_dst_visible_height; msg_Dbg( p_stream, "source %ix%i, destination %ix%i", - i_src_width, i_src_height, - i_dst_width, i_dst_height + i_src_visible_width, i_src_visible_height, + i_dst_visible_width, i_dst_visible_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 ) { - if( id->p_decoder->fmt_out.video.i_frame_rate && - id->p_decoder->fmt_out.video.i_frame_rate_base ) + if( p_fmt_out->video.i_frame_rate && + p_fmt_out->video.i_frame_rate_base ) { id->p_encoder->fmt_out.video.i_frame_rate = - id->p_decoder->fmt_out.video.i_frame_rate; + p_fmt_out->video.i_frame_rate; id->p_encoder->fmt_out.video.i_frame_rate_base = - id->p_decoder->fmt_out.video.i_frame_rate_base; + p_fmt_out->video.i_frame_rate_base; } else { @@ -461,33 +548,45 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream, } } + id->p_encoder->fmt_in.video.orientation = + id->p_encoder->fmt_out.video.orientation = + id->p_decoder->fmt_in.video.orientation; + id->p_encoder->fmt_in.video.i_frame_rate = id->p_encoder->fmt_out.video.i_frame_rate; id->p_encoder->fmt_in.video.i_frame_rate_base = id->p_encoder->fmt_out.video.i_frame_rate_base; - date_Init( &id->interpolated_pts, - id->p_encoder->fmt_out.video.i_frame_rate, - id->p_encoder->fmt_out.video.i_frame_rate_base ); + vlc_ureduce( &id->p_encoder->fmt_in.video.i_frame_rate, + &id->p_encoder->fmt_in.video.i_frame_rate_base, + id->p_encoder->fmt_in.video.i_frame_rate, + id->p_encoder->fmt_in.video.i_frame_rate_base, + 0 ); + msg_Dbg( p_stream, "source fps %d/%d, destination %d/%d", + id->p_decoder->fmt_out.video.i_frame_rate, + id->p_decoder->fmt_out.video.i_frame_rate_base, + id->p_encoder->fmt_in.video.i_frame_rate, + id->p_encoder->fmt_in.video.i_frame_rate_base ); + /* Check whether a particular aspect ratio was requested */ if( id->p_encoder->fmt_out.video.i_sar_num <= 0 || id->p_encoder->fmt_out.video.i_sar_den <= 0 ) { - vlc_ureduce( &id->p_decoder->fmt_out.video.i_sar_num, - &id->p_decoder->fmt_out.video.i_sar_den, - id->p_decoder->fmt_out.video.i_sar_num, - id->p_decoder->fmt_out.video.i_sar_den, + vlc_ureduce( &id->p_encoder->fmt_out.video.i_sar_num, + &id->p_encoder->fmt_out.video.i_sar_den, + (uint64_t)p_fmt_out->video.i_sar_num * i_src_visible_width * i_dst_visible_height, + (uint64_t)p_fmt_out->video.i_sar_den * i_src_visible_height * i_dst_visible_width, + 0 ); + } + else + { + vlc_ureduce( &id->p_encoder->fmt_out.video.i_sar_num, + &id->p_encoder->fmt_out.video.i_sar_den, + id->p_encoder->fmt_out.video.i_sar_num, + id->p_encoder->fmt_out.video.i_sar_den, 0 ); - - id->p_encoder->fmt_out.video.i_sar_num = id->p_decoder->fmt_out.video.i_sar_num * i_src_width / i_dst_width; - id->p_encoder->fmt_out.video.i_sar_den = id->p_decoder->fmt_out.video.i_sar_den * i_src_height / i_dst_height; } - vlc_ureduce( &id->p_encoder->fmt_out.video.i_sar_num, - &id->p_encoder->fmt_out.video.i_sar_den, - id->p_encoder->fmt_out.video.i_sar_num, - id->p_encoder->fmt_out.video.i_sar_den, - 0 ); id->p_encoder->fmt_in.video.i_sar_num = id->p_encoder->fmt_out.video.i_sar_num; @@ -498,11 +597,10 @@ static void transcode_video_encoder_init( sout_stream_t *p_stream, id->p_encoder->fmt_out.video.i_sar_num * id->p_encoder->fmt_out.video.i_width, id->p_encoder->fmt_out.video.i_sar_den * id->p_encoder->fmt_out.video.i_height ); - id->p_encoder->fmt_in.video.i_chroma = id->p_encoder->fmt_in.i_codec; } static int transcode_video_encoder_open( sout_stream_t *p_stream, - sout_stream_id_t *id ) + sout_stream_id_sys_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; @@ -538,9 +636,9 @@ static int transcode_video_encoder_open( sout_stream_t *p_stream, } void transcode_video_close( sout_stream_t *p_stream, - sout_stream_id_t *id ) + sout_stream_id_sys_t *id ) { - if( p_stream->p_sys->i_threads >= 1 ) + if( p_stream->p_sys->i_threads >= 1 && !p_stream->p_sys->b_abort ) { vlc_mutex_lock( &p_stream->p_sys->lock_out ); p_stream->p_sys->b_abort = true; @@ -548,10 +646,14 @@ void transcode_video_close( sout_stream_t *p_stream, vlc_mutex_unlock( &p_stream->p_sys->lock_out ); vlc_join( p_stream->p_sys->thread, NULL ); - vlc_mutex_destroy( &p_stream->p_sys->lock_out ); - vlc_cond_destroy( &p_stream->p_sys->cond ); + + picture_fifo_Delete( p_stream->p_sys->pp_pics ); + block_ChainRelease( p_stream->p_sys->p_buffers ); } + vlc_mutex_destroy( &p_stream->p_sys->lock_out ); + vlc_cond_destroy( &p_stream->p_sys->cond ); + /* Close decoder */ if( id->p_decoder->p_module ) module_unneed( id->p_decoder, id->p_decoder->p_module ); @@ -571,15 +673,83 @@ void transcode_video_close( sout_stream_t *p_stream, filter_chain_Delete( id->p_uf_chain ); } -int transcode_video_process( sout_stream_t *p_stream, sout_stream_id_t *id, +static void OutputFrame( sout_stream_t *p_stream, picture_t *p_pic, sout_stream_id_sys_t *id, block_t **out ) +{ + sout_stream_sys_t *p_sys = p_stream->p_sys; + picture_t *p_pic2 = NULL; + + /* + * Encoding + */ + /* Check if we have a subpicture to overlay */ + if( p_sys->p_spu ) + { + video_format_t fmt = id->p_encoder->fmt_in.video; + if( fmt.i_visible_width <= 0 || fmt.i_visible_height <= 0 ) + { + fmt.i_visible_width = fmt.i_width; + fmt.i_visible_height = fmt.i_height; + fmt.i_x_offset = 0; + fmt.i_y_offset = 0; + } + + subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, + &id->p_decoder->fmt_out.video, + p_pic->date, p_pic->date, false ); + + /* Overlay subpicture */ + if( p_subpic ) + { + if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) ) + { + /* We can't modify the picture, we need to duplicate it, + * in this point the picture is already p_encoder->fmt.in format*/ + picture_t *p_tmp = video_new_buffer_encoder( id->p_encoder ); + if( likely( p_tmp ) ) + { + picture_Copy( p_tmp, p_pic ); + picture_Release( p_pic ); + p_pic = p_tmp; + } + } + if( unlikely( !p_sys->p_spu_blend ) ) + p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt ); + if( likely( p_sys->p_spu_blend ) ) + picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic ); + subpicture_Delete( p_subpic ); + } + } + + if( p_sys->i_threads == 0 ) + { + block_t *p_block; + + p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); + block_ChainAppend( out, p_block ); + } + + if( p_sys->i_threads ) + { + vlc_mutex_lock( &p_sys->lock_out ); + picture_fifo_Push( p_sys->pp_pics, p_pic ); + vlc_cond_signal( &p_sys->cond ); + vlc_mutex_unlock( &p_sys->lock_out ); + } + + if( p_sys->i_threads && p_pic2 ) + picture_Release( p_pic2 ); + else if ( p_sys->i_threads == 0 ) + picture_Release( p_pic ); +} + +int transcode_video_process( sout_stream_t *p_stream, sout_stream_id_sys_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; - bool b_need_duplicate = false; - picture_t *p_pic, *p_pic2 = NULL; + picture_t *p_pic = NULL; *out = NULL; - if( in == NULL ) + if( unlikely( in == NULL ) ) { if( p_sys->i_threads == 0 ) { @@ -591,10 +761,19 @@ int transcode_video_process( sout_stream_t *p_stream, sout_stream_id_t *id, } else { - /* - * FIXME: we need EncoderThread() to flush buffers and signal us - * when it's done so we can send the last frames to the chain - */ + msg_Dbg( p_stream, "Flushing thread and waiting that"); + vlc_mutex_lock( &p_stream->p_sys->lock_out ); + p_stream->p_sys->b_abort = true; + vlc_cond_signal( &p_stream->p_sys->cond ); + vlc_mutex_unlock( &p_stream->p_sys->lock_out ); + + vlc_join( p_stream->p_sys->thread, NULL ); + vlc_mutex_lock( &p_sys->lock_out ); + *out = p_sys->p_buffers; + p_sys->p_buffers = NULL; + vlc_mutex_unlock( &p_sys->lock_out ); + + msg_Dbg( p_stream, "Flushing done"); } return VLC_SUCCESS; } @@ -603,62 +782,48 @@ int transcode_video_process( sout_stream_t *p_stream, sout_stream_id_t *id, while( (p_pic = id->p_decoder->pf_decode_video( id->p_decoder, &in )) ) { - if( p_stream->p_sout->i_out_pace_nocontrol && p_sys->b_hurry_up ) - { - mtime_t current_date = mdate(); - if( current_date + 50000 > p_pic->date ) - { - msg_Dbg( p_stream, "late picture skipped (%"PRId64")", - current_date + 50000 - p_pic->date ); - picture_Release( p_pic ); - continue; - } - } - - if( p_sys->b_master_sync ) + if( unlikely ( + id->p_encoder->p_module && + !video_format_IsSimilar( &id->fmt_input_video, &id->p_decoder->fmt_out.video ) + ) + ) { - mtime_t i_video_drift; - mtime_t i_master_drift = p_sys->i_master_drift; - mtime_t i_pts; + msg_Info( p_stream, "aspect-ratio changed, reiniting. %i -> %i : %i -> %i.", + id->fmt_input_video.i_sar_num, id->p_decoder->fmt_out.video.i_sar_num, + id->fmt_input_video.i_sar_den, id->p_decoder->fmt_out.video.i_sar_den + ); + /* Close filters */ + if( id->p_f_chain ) + filter_chain_Delete( id->p_f_chain ); + id->p_f_chain = NULL; + if( id->p_uf_chain ) + filter_chain_Delete( id->p_uf_chain ); + id->p_uf_chain = NULL; + + /* Reinitialize filters */ + id->p_encoder->fmt_out.video.i_visible_width = p_sys->i_width & ~1; + id->p_encoder->fmt_out.video.i_visible_height = p_sys->i_height & ~1; + id->p_encoder->fmt_out.video.i_sar_num = id->p_encoder->fmt_out.video.i_sar_den = 0; - i_pts = date_Get( &id->interpolated_pts ) + 1; - if ( p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT - || p_pic->date - i_pts < -MASTER_SYNC_MAX_DRIFT ) - { - msg_Dbg( p_stream, "drift is too high, resetting master sync" ); - date_Set( &id->interpolated_pts, p_pic->date ); - i_pts = p_pic->date + 1; - } - i_video_drift = p_pic->date - i_pts; - b_need_duplicate = false; - - /* Set the pts of the frame being encoded */ - p_pic->date = i_pts; - - if( i_video_drift < (i_master_drift - 50000) ) - { -#if 0 - msg_Dbg( p_stream, "dropping frame (%i)", - (int)(i_video_drift - i_master_drift) ); -#endif - picture_Release( p_pic ); - continue; - } - else if( i_video_drift > (i_master_drift + 50000) ) - { -#if 0 - msg_Dbg( p_stream, "adding frame (%i)", - (int)(i_video_drift - i_master_drift) ); -#endif - b_need_duplicate = true; - } + transcode_video_filter_init( p_stream, id ); + transcode_video_encoder_init( p_stream, id ); + conversion_video_filter_append( id ); + memcpy( &id->fmt_input_video, &id->p_decoder->fmt_out.video, sizeof(video_format_t)); } + if( unlikely( !id->p_encoder->p_module ) ) { - transcode_video_encoder_init( p_stream, id ); + if( id->p_f_chain ) + filter_chain_Delete( id->p_f_chain ); + if( id->p_uf_chain ) + filter_chain_Delete( id->p_uf_chain ); + id->p_f_chain = id->p_uf_chain = NULL; transcode_video_filter_init( p_stream, id ); + transcode_video_encoder_init( p_stream, id ); + conversion_video_filter_append( id ); + memcpy( &id->fmt_input_video, &id->p_decoder->fmt_out.video, sizeof(video_format_t)); if( transcode_video_encoder_open( p_stream, id ) != VLC_SUCCESS ) { @@ -669,121 +834,51 @@ int transcode_video_process( sout_stream_t *p_stream, sout_stream_id_t *id, } } - /* Run filter chain */ - if( id->p_f_chain ) - p_pic = filter_chain_VideoFilter( id->p_f_chain, p_pic ); - - /* - * Encoding + /* Run the filter and output chains; first with the picture, + * and then with NULL as many times as we need until they + * stop outputting frames. */ + for ( ;; ) { + picture_t *p_filtered_pic = p_pic; - /* Check if we have a subpicture to overlay */ - if( p_sys->p_spu ) - { - video_format_t fmt; - if( filter_chain_GetLength( id->p_f_chain ) > 0 ) - fmt = filter_chain_GetFmtOut( id->p_f_chain )->video; - else - fmt = id->p_decoder->fmt_out.video; + /* Run filter chain */ + if( id->p_f_chain ) + p_filtered_pic = filter_chain_VideoFilter( id->p_f_chain, p_filtered_pic ); + if( !p_filtered_pic ) + break; - subpicture_t *p_subpic = spu_Render( p_sys->p_spu, NULL, &fmt, &fmt, - p_pic->date, p_pic->date, false ); + for ( ;; ) { + picture_t *p_user_filtered_pic = p_filtered_pic; - /* Overlay subpicture */ - if( p_subpic ) - { - if( picture_IsReferenced( p_pic ) && !filter_chain_GetLength( id->p_f_chain ) ) - { - /* We can't modify the picture, we need to duplicate it */ - picture_t *p_tmp = video_new_buffer_decoder( id->p_decoder ); - if( p_tmp ) - { - picture_Copy( p_tmp, p_pic ); - picture_Release( p_pic ); - p_pic = p_tmp; - } - } - if( !p_sys->p_spu_blend ) - p_sys->p_spu_blend = filter_NewBlend( VLC_OBJECT( p_sys->p_spu ), &fmt ); - if( p_sys->p_spu_blend ) - picture_BlendSubpicture( p_pic, p_sys->p_spu_blend, p_subpic ); - subpicture_Delete( p_subpic ); - } - } - - /* Run user specified filter chain */ - if( id->p_uf_chain ) - p_pic = filter_chain_VideoFilter( id->p_uf_chain, p_pic ); + /* Run user specified filter chain */ + if( id->p_uf_chain ) + p_user_filtered_pic = filter_chain_VideoFilter( id->p_uf_chain, p_user_filtered_pic ); + if( !p_user_filtered_pic ) + break; - if( p_sys->i_threads == 0 ) - { - block_t *p_block; + OutputFrame( p_stream, p_user_filtered_pic, id, out ); - p_block = id->p_encoder->pf_encode_video( id->p_encoder, p_pic ); - block_ChainAppend( out, p_block ); - } - - if( p_sys->b_master_sync ) - { - mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1; - if ( p_pic->date - i_pts > MASTER_SYNC_MAX_DRIFT - || p_pic->date - i_pts < -MASTER_SYNC_MAX_DRIFT ) - { - msg_Dbg( p_stream, "drift is too high, resetting master sync" ); - date_Set( &id->interpolated_pts, p_pic->date ); - i_pts = p_pic->date + 1; + p_filtered_pic = NULL; } - date_Increment( &id->interpolated_pts, 1 ); - if( unlikely( b_need_duplicate ) ) - { - - if( p_sys->i_threads >= 1 ) - { - /* We can't modify the picture, we need to duplicate it */ - p_pic2 = video_new_buffer_decoder( id->p_decoder ); - if( p_pic2 != NULL ) - { - picture_Copy( p_pic2, p_pic ); - p_pic2->date = i_pts; - } - } - else - { - block_t *p_block; - p_pic->date = i_pts; - p_block = id->p_encoder->pf_encode_video(id->p_encoder, p_pic); - block_ChainAppend( out, p_block ); - } - } + p_pic = NULL; } + } - if( p_sys->i_threads == 0 ) - { - picture_Release( p_pic ); - } - else - { - vlc_mutex_lock( &p_sys->lock_out ); - p_sys->pp_pics[p_sys->i_last_pic++] = p_pic; - p_sys->i_last_pic %= PICTURE_RING_SIZE; - *out = p_sys->p_buffers; - p_sys->p_buffers = NULL; - if( p_pic2 != NULL ) - { - p_sys->pp_pics[p_sys->i_last_pic++] = p_pic2; - p_sys->i_last_pic %= PICTURE_RING_SIZE; - } - vlc_cond_signal( &p_sys->cond ); - vlc_mutex_unlock( &p_sys->lock_out ); - } + if( p_sys->i_threads >= 1 ) + { + /* Pick up any return data the encoder thread wants to output. */ + vlc_mutex_lock( &p_sys->lock_out ); + *out = p_sys->p_buffers; + p_sys->p_buffers = NULL; + vlc_mutex_unlock( &p_sys->lock_out ); } return VLC_SUCCESS; } -bool transcode_video_add( sout_stream_t *p_stream, es_format_t *p_fmt, - sout_stream_id_t *id ) +bool transcode_video_add( sout_stream_t *p_stream, const es_format_t *p_fmt, + sout_stream_id_sys_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; @@ -793,8 +888,8 @@ bool transcode_video_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 & ~1; - id->p_encoder->fmt_out.video.i_height = p_sys->i_height & ~1; + id->p_encoder->fmt_out.video.i_visible_width = p_sys->i_width & ~1; + id->p_encoder->fmt_out.video.i_visible_height = p_sys->i_height & ~1; id->p_encoder->fmt_out.i_bitrate = p_sys->i_vbitrate; /* Build decoder -> filter -> encoder chain */ @@ -808,10 +903,10 @@ bool transcode_video_add( sout_stream_t *p_stream, es_format_t *p_fmt, * all the characteristics of the decoded stream yet */ id->b_transcode = true; - if( p_sys->f_fps > 0 ) + if( p_sys->fps_num ) { - id->p_encoder->fmt_out.video.i_frame_rate = (p_sys->f_fps * 1000) + 0.5; - id->p_encoder->fmt_out.video.i_frame_rate_base = ENC_FRAMERATE_BASE; + id->p_encoder->fmt_in.video.i_frame_rate = id->p_encoder->fmt_out.video.i_frame_rate = (p_sys->fps_num ); + id->p_encoder->fmt_in.video.i_frame_rate_base = id->p_encoder->fmt_out.video.i_frame_rate_base = (p_sys->fps_den ? p_sys->fps_den : 1); } return true;