X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=modules%2Fstream_out%2Ftranscode.c;h=f5c115f772b073baa0e171ddffb438e0e373928a;hb=1a67448183a9c5ab7b5b427fb80d2d1a2e34ff8d;hp=5cfd9811d6982fa10476fc7637d1e73ed33ee6f1;hpb=209f2eaf33a457f9d2543a409b3a721a4c132bb2;p=vlc diff --git a/modules/stream_out/transcode.c b/modules/stream_out/transcode.c index 5cfd9811d6..f5c115f772 100644 --- a/modules/stream_out/transcode.c +++ b/modules/stream_out/transcode.c @@ -2,7 +2,7 @@ * transcode.c ***************************************************************************** * Copyright (C) 2001, 2002 VideoLAN - * $Id: transcode.c,v 1.18 2003/06/25 21:47:05 fenrir Exp $ + * $Id: transcode.c,v 1.43 2003/10/24 21:27:06 gbazin Exp $ * * Authors: Laurent Aimar * @@ -30,6 +30,8 @@ #include #include #include +#include +#include /* ffmpeg header */ #ifdef HAVE_FFMPEG_AVCODEC_H @@ -38,6 +40,14 @@ # include #endif +/* vorbis header */ +#ifdef HAVE_VORBIS_VORBISENC_H +# include +# ifndef OV_ECTL_RATEMANAGE_AVG +# define OV_ECTL_RATEMANAGE_AVG 0x0 +# endif +#endif + /***************************************************************************** * Exported prototypes *****************************************************************************/ @@ -56,6 +66,8 @@ static int transcode_video_ffmpeg_new ( sout_stream_t *, sout_stream_id_t * static void transcode_video_ffmpeg_close ( sout_stream_t *, sout_stream_id_t * ); static int transcode_video_ffmpeg_process( sout_stream_t *, sout_stream_id_t *, sout_buffer_t *, sout_buffer_t ** ); +static int transcode_video_ffmpeg_getframebuf( struct AVCodecContext *, AVFrame *); + /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -80,16 +92,23 @@ struct sout_stream_sys_t int i_vtolerance; int i_width; int i_height; + int i_b_frames; int i_key_int; int i_qmin; int i_qmax; - vlc_bool_t b_hq; + vlc_bool_t i_hq; vlc_bool_t b_deinterlace; int i_crop_top; int i_crop_bottom; int i_crop_right; int i_crop_left; + + mtime_t i_input_pts; + mtime_t i_output_pts; + mtime_t i_last_ref_pts; + + mtime_t i_buggy_pts_detect; }; /***************************************************************************** @@ -115,9 +134,14 @@ static int Open( vlc_object_t *p_this ) p_sys->i_width = 0; p_sys->i_height = 0; p_sys->i_key_int = -1; + p_sys->i_b_frames = 0; p_sys->i_qmin = 2; p_sys->i_qmax = 31; - p_sys->b_hq = VLC_FALSE; +#if LIBAVCODEC_BUILD >= 4673 + p_sys->i_hq = FF_MB_DECISION_SIMPLE; +#else + p_sys->i_hq = VLC_FALSE; +#endif p_sys->b_deinterlace= VLC_FALSE; p_sys->i_crop_top = 0; @@ -151,8 +175,7 @@ static int Open( vlc_object_t *p_this ) } } - msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s", - fcc, + msg_Dbg( p_stream, "codec audio=%4.4s %dHz %d channels %dKb/s", fcc, p_sys->i_sample_rate, p_sys->i_channels, p_sys->i_abitrate / 1024 ); } @@ -211,10 +234,36 @@ static int Open( vlc_object_t *p_this ) { p_sys->i_key_int = atoi( val ); } + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "bframes" ) ) ) + { + p_sys->i_b_frames = atoi( val ); + } +#if LIBAVCODEC_BUILD >= 4673 + if( ( val = sout_cfg_find_value( p_stream->p_cfg, "hq" ) ) ) + { + if( !strcmp( val, "rd" ) ) + { + p_sys->i_hq = FF_MB_DECISION_RD; + } + else if( !strcmp( val, "bits" ) ) + { + p_sys->i_hq = FF_MB_DECISION_BITS; + } + else if( !strcmp( val, "simple" ) ) + { + p_sys->i_hq = FF_MB_DECISION_SIMPLE; + } + else + { + p_sys->i_hq = FF_MB_DECISION_RD; + } + } +#else if( sout_cfg_find( p_stream->p_cfg, "hq" ) ) { - p_sys->b_hq = VLC_TRUE; + p_sys->i_hq = VLC_TRUE; } +#endif if( ( val = sout_cfg_find_value( p_stream->p_cfg, "qmin" ) ) ) { p_sys->i_qmin = atoi( val ); @@ -245,6 +294,9 @@ static int Open( vlc_object_t *p_this ) avcodec_init(); avcodec_register_all(); + /* ffmpeg needs some padding at the end of each buffer */ + p_stream->p_sout->i_padding += FF_INPUT_BUFFER_PADDING_SIZE; + return VLC_SUCCESS; } @@ -266,10 +318,14 @@ struct sout_stream_id_t vlc_fourcc_t b_transcode; sout_format_t f_src; /* only if transcoding */ sout_format_t f_dst; /* " " " */ + unsigned int i_inter_pixfmt; /* intermediary format when transcoding */ /* id of the out stream */ void *id; + /* Encoder */ + encoder_t *p_encoder; + /* ffmpeg part */ AVCodec *ff_dec; AVCodecContext *ff_dec_c; @@ -300,10 +356,22 @@ struct sout_stream_id_t AVFrame *p_ff_pic_tmp2; /* to do resample */ ImgReSampleContext *p_vresample; + +#ifdef HAVE_VORBIS_VORBISENC_H + + /* Vorbis part */ + vorbis_info *p_vi; + vorbis_dsp_state *p_vd; + vorbis_block *p_vb; + vorbis_comment *p_vc; + int i_last_block_size; + int i_samples_delay; + vlc_bool_t b_headers_sent; +#endif }; -static sout_stream_id_t * Add ( sout_stream_t *p_stream, sout_format_t *p_fmt ) +static sout_stream_id_t * Add( sout_stream_t *p_stream, sout_format_t *p_fmt ) { sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_id_t *id; @@ -311,6 +379,8 @@ static sout_stream_id_t * Add ( sout_stream_t *p_stream, sout_format_t *p_f id = malloc( sizeof( sout_stream_id_t ) ); id->i_dts = 0; id->id = NULL; + id->p_encoder = NULL; + if( p_fmt->i_cat == AUDIO_ES && p_sys->i_acodec != 0 ) { msg_Dbg( p_stream, @@ -342,6 +412,12 @@ static sout_stream_id_t * Add ( sout_stream_t *p_stream, sout_format_t *p_f /* open output stream */ id->id = p_sys->p_out->pf_add( p_sys->p_out, &id->f_dst ); id->b_transcode = VLC_TRUE; + + if( id->id == NULL ) + { + free( id ); + return NULL; + } } else if( p_fmt->i_cat == VIDEO_ES && p_sys->i_vcodec != 0 ) { @@ -412,7 +488,8 @@ static int Del ( sout_stream_t *p_stream, sout_stream_id_t *id ) return VLC_SUCCESS; } -static int Send ( sout_stream_t *p_stream, sout_stream_id_t *id, sout_buffer_t *p_buffer ) +static int Send( sout_stream_t *p_stream, sout_stream_id_t *id, + sout_buffer_t *p_buffer ) { sout_stream_sys_t *p_sys = p_stream->p_sys; @@ -458,19 +535,28 @@ static struct { /* audio */ { VLC_FOURCC( 'm', 'p', 'g', 'a' ), CODEC_ID_MP2 }, + { VLC_FOURCC( 'm', 'p', '3', ' ' ), CODEC_ID_MP3LAME }, + { VLC_FOURCC( 'm', 'p', '4', 'a' ), CODEC_ID_AAC }, { VLC_FOURCC( 'a', '5', '2', ' ' ), CODEC_ID_AC3 }, { VLC_FOURCC( 'a', 'c', '3', ' ' ), CODEC_ID_AC3 }, { VLC_FOURCC( 'w', 'm', 'a', '1' ), CODEC_ID_WMAV1 }, { VLC_FOURCC( 'w', 'm', 'a', '2' ), CODEC_ID_WMAV2 }, + { VLC_FOURCC( 'v', 'o', 'r', 'b' ), CODEC_ID_VORBIS }, + { VLC_FOURCC( 'a', 'l', 'a', 'w' ), CODEC_ID_PCM_ALAW }, /* video */ - { VLC_FOURCC( 'm', 'p', '4', 'v'), CODEC_ID_MPEG4 }, { VLC_FOURCC( 'm', 'p', 'g', 'v' ), CODEC_ID_MPEG1VIDEO }, + { VLC_FOURCC( 'm', 'p', '1', 'v' ), CODEC_ID_MPEG1VIDEO }, +#if LIBAVCODEC_BUILD >= 4676 + { VLC_FOURCC( 'm', 'p', '2', 'v' ), CODEC_ID_MPEG2VIDEO }, +#endif + { VLC_FOURCC( 'm', 'p', '4', 'v'), CODEC_ID_MPEG4 }, { VLC_FOURCC( 'D', 'I', 'V', '1' ), CODEC_ID_MSMPEG4V1 }, { VLC_FOURCC( 'D', 'I', 'V', '2' ), CODEC_ID_MSMPEG4V2 }, { VLC_FOURCC( 'D', 'I', 'V', '3' ), CODEC_ID_MSMPEG4V3 }, { VLC_FOURCC( 'H', '2', '6', '3' ), CODEC_ID_H263 }, { VLC_FOURCC( 'I', '2', '6', '3' ), CODEC_ID_H263I }, + { VLC_FOURCC( 'h', 'u', 'f', 'f' ), CODEC_ID_HUFFYUV }, { VLC_FOURCC( 'W', 'M', 'V', '1' ), CODEC_ID_WMV1 }, { VLC_FOURCC( 'W', 'M', 'V', '2' ), CODEC_ID_WMV2 }, { VLC_FOURCC( 'M', 'J', 'P', 'G' ), CODEC_ID_MJPEG }, @@ -485,7 +571,10 @@ static struct { VLC_FOURCC( 'I', '4', '2', '0' ), CODEC_ID_RAWVIDEO }, { VLC_FOURCC( 'I', '4', '2', '2' ), CODEC_ID_RAWVIDEO }, { VLC_FOURCC( 'I', '4', '4', '4' ), CODEC_ID_RAWVIDEO }, + { VLC_FOURCC( 'R', 'V', '1', '5' ), CODEC_ID_RAWVIDEO }, + { VLC_FOURCC( 'R', 'V', '1', '6' ), CODEC_ID_RAWVIDEO }, { VLC_FOURCC( 'R', 'V', '2', '4' ), CODEC_ID_RAWVIDEO }, + { VLC_FOURCC( 'R', 'V', '3', '2' ), CODEC_ID_RAWVIDEO }, { VLC_FOURCC( 'Y', 'U', 'Y', '2' ), CODEC_ID_RAWVIDEO }, { VLC_FOURCC( 0, 0, 0, 0 ), 0 } @@ -516,8 +605,16 @@ static inline int get_ff_chroma( vlc_fourcc_t i_chroma ) return PIX_FMT_YUV422P; case VLC_FOURCC( 'I', '4', '4', '4' ): return PIX_FMT_YUV444P; + case VLC_FOURCC( 'R', 'V', '1', '5' ): + return PIX_FMT_RGB555; + case VLC_FOURCC( 'R', 'V', '1', '6' ): + return PIX_FMT_RGB565; case VLC_FOURCC( 'R', 'V', '2', '4' ): return PIX_FMT_RGB24; + case VLC_FOURCC( 'R', 'V', '3', '2' ): + return PIX_FMT_RGBA32; + case VLC_FOURCC( 'G', 'R', 'E', 'Y' ): + return PIX_FMT_GRAY8; case VLC_FOURCC( 'Y', 'U', 'Y', '2' ): return PIX_FMT_YUV422; default: @@ -525,7 +622,8 @@ static inline int get_ff_chroma( vlc_fourcc_t i_chroma ) } } -static int transcode_audio_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_id_t *id ) +static int transcode_audio_ffmpeg_new( sout_stream_t *p_stream, + sout_stream_id_t *id ) { int i_ff_codec; @@ -548,14 +646,14 @@ static int transcode_audio_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_i i_ff_codec = get_ff_codec( id->f_src.i_fourcc ); if( i_ff_codec == 0 ) { - msg_Err( p_stream, "cannot find decoder" ); + msg_Err( p_stream, "cannot find decoder id" ); return VLC_EGENERIC; } id->ff_dec = avcodec_find_decoder( i_ff_codec ); if( !id->ff_dec ) { - msg_Err( p_stream, "cannot find decoder" ); + msg_Err( p_stream, "cannot find decoder (avcodec)" ); return VLC_EGENERIC; } @@ -575,90 +673,180 @@ static int transcode_audio_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_i } /* find encoder */ + id->i_buffer_in = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; + id->i_buffer_in_pos = 0; + id->p_buffer_in = malloc( id->i_buffer_in ); + + id->i_buffer = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; + id->i_buffer_pos = 0; + id->p_buffer = malloc( id->i_buffer ); + + id->i_buffer_out = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; + id->i_buffer_out_pos = 0; + id->p_buffer_out = malloc( id->i_buffer_out ); + + /* Sanity check for audio channels */ + id->f_dst.i_channels = __MIN( id->f_dst.i_channels, id->f_src.i_channels ); + +#ifdef HAVE_VORBIS_VORBISENC_H + if( id->f_dst.i_fourcc == VLC_FOURCC('v','o','r','b') ) + { + id->p_vi = (vorbis_info *)malloc( sizeof(vorbis_info) ); + id->p_vd = (vorbis_dsp_state *)malloc( sizeof(vorbis_dsp_state) ); + id->p_vb = (vorbis_block *)malloc( sizeof(vorbis_block) ); + id->p_vc = (vorbis_comment *)malloc( sizeof(vorbis_comment) ); + + vorbis_info_init( id->p_vi ); + + if( vorbis_encode_setup_managed( id->p_vi, id->f_dst.i_channels, + id->f_dst.i_sample_rate, -1, id->f_dst.i_bitrate, -1 ) || + vorbis_encode_ctl( id->p_vi, OV_ECTL_RATEMANAGE_AVG, NULL ) || + vorbis_encode_setup_init( id->p_vi ) ){} + + /* add a comment */ + vorbis_comment_init( id->p_vc); + vorbis_comment_add_tag( id->p_vc, "ENCODER", "VLC media player"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init( id->p_vd, id->p_vi ); + vorbis_block_init( id->p_vd, id->p_vb ); + + id->b_headers_sent = VLC_FALSE; + id->i_last_block_size = 0; + id->i_samples_delay = 0; + + return VLC_SUCCESS; + } +#endif + i_ff_codec = get_ff_codec( id->f_dst.i_fourcc ); if( i_ff_codec == 0 ) { - msg_Err( p_stream, "cannot find encoder" ); + msg_Err( p_stream, "cannot find encoder id" ); return VLC_EGENERIC; } id->ff_enc = avcodec_find_encoder( i_ff_codec ); if( !id->ff_enc ) { - msg_Err( p_stream, "cannot find encoder" ); + msg_Err( p_stream, "cannot find encoder (avcodec)" ); return VLC_EGENERIC; } + /* Hack for mp3 transcoding support */ + if( id->f_dst.i_fourcc == VLC_FOURCC( 'm','p','3',' ' ) ) + { + id->f_dst.i_fourcc = VLC_FOURCC( 'm','p','g','a' ); + } + id->ff_enc_c = avcodec_alloc_context(); id->ff_enc_c->bit_rate = id->f_dst.i_bitrate; id->ff_enc_c->sample_rate = id->f_dst.i_sample_rate; id->ff_enc_c->channels = id->f_dst.i_channels; + /* Make sure we get extradata filled by the encoder */ + id->ff_enc_c->extradata_size = 0; + id->ff_enc_c->extradata = NULL; + id->ff_enc_c->flags |= CODEC_FLAG_GLOBAL_HEADER; + if( avcodec_open( id->ff_enc_c, id->ff_enc ) ) { - msg_Err( p_stream, "cannot open encoder" ); - return VLC_EGENERIC; + if( id->ff_enc_c->channels > 2 ) + { + id->ff_enc_c->channels = 2; + id->f_dst.i_channels = 2; + if( avcodec_open( id->ff_enc_c, id->ff_enc ) ) + { + msg_Err( p_stream, "cannot open encoder" ); + return VLC_EGENERIC; + } + msg_Warn( p_stream, "stereo mode selected (codec limitation)" ); + } + else + { + msg_Err( p_stream, "cannot open encoder" ); + return VLC_EGENERIC; + } } - - id->i_buffer_in = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; - id->i_buffer_in_pos = 0; - id->p_buffer_in = malloc( id->i_buffer_in ); - - id->i_buffer = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; - id->i_buffer_pos = 0; - id->p_buffer = malloc( id->i_buffer ); - - id->i_buffer_out = 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE; - id->i_buffer_out_pos = 0; - id->p_buffer_out = malloc( id->i_buffer_out ); + id->f_dst.i_extra_data = id->ff_enc_c->extradata_size; + id->f_dst.p_extra_data = id->ff_enc_c->extradata; + id->ff_enc_c->flags &= ~CODEC_FLAG_GLOBAL_HEADER; return VLC_SUCCESS; } -static void transcode_audio_ffmpeg_close ( sout_stream_t *p_stream, sout_stream_id_t *id ) +static void transcode_audio_ffmpeg_close( sout_stream_t *p_stream, + sout_stream_id_t *id ) { if( id->ff_dec ) { avcodec_close( id->ff_dec_c ); } - avcodec_close( id->ff_enc_c ); + +#ifdef HAVE_VORBIS_VORBISENC_H + if( id->f_dst.i_fourcc == VLC_FOURCC('v','o','r','b') ) + { + vorbis_block_clear( id->p_vb ); + vorbis_dsp_clear( id->p_vd ); + vorbis_comment_clear( id->p_vc ); + vorbis_info_clear( id->p_vi ); /* must be called last */ + + free( id->p_vi ); + free( id->p_vd ); + free( id->p_vb ); + free( id->p_vc ); + } + else +#endif + avcodec_close( id->ff_enc_c ); free( id->ff_dec_c ); - free( id->ff_enc_c ); + if( id->f_dst.i_fourcc != VLC_FOURCC('v','o','r','b') ) + free( id->ff_enc_c ); free( id->p_buffer_in ); free( id->p_buffer ); free( id->p_buffer_out ); } -static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_id_t *id, - sout_buffer_t *in, sout_buffer_t **out ) +static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, + sout_stream_id_t *id, + sout_buffer_t *in, + sout_buffer_t **out ) { vlc_bool_t b_again = VLC_FALSE; *out = NULL; /* gather data into p_buffer_in */ +#ifdef HAVE_VORBIS_VORBISENC_H + if( id->f_dst.i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ) + id->i_dts = in->i_dts - + (mtime_t)1000000 * (mtime_t)id->i_samples_delay / + (mtime_t)id->f_dst.i_sample_rate; + else +#endif id->i_dts = in->i_dts - (mtime_t)1000000 * - (mtime_t)(id->i_buffer_pos / 2 / id->ff_enc_c->channels )/ - (mtime_t)id->ff_enc_c->sample_rate; + (mtime_t)(id->i_buffer_pos / 2 / id->ff_dec_c->channels )/ + (mtime_t)id->ff_dec_c->sample_rate; - if( id->i_buffer_in_pos + in->i_size > id->i_buffer_in ) + if( id->i_buffer_in_pos + (int)in->i_size > id->i_buffer_in ) { /* extend buffer_in */ id->i_buffer_in = id->i_buffer_in_pos + in->i_size + 1024; id->p_buffer_in = realloc( id->p_buffer_in, id->i_buffer_in ); } memcpy( &id->p_buffer_in[id->i_buffer_in_pos], - in->p_buffer, - in->i_size ); + in->p_buffer, in->i_size ); id->i_buffer_in_pos += in->i_size; do { - /* decode as many data as possible */ + int i_buffer_pos; + + /* decode as much data as possible */ if( id->ff_dec ) { for( ;; ) @@ -669,10 +857,12 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ i_buffer_size = id->i_buffer - id->i_buffer_pos; i_used = avcodec_decode_audio( id->ff_dec_c, - (int16_t*)&id->p_buffer[id->i_buffer_pos], &i_buffer_size, - id->p_buffer_in, id->i_buffer_in_pos ); + (int16_t*)&id->p_buffer[id->i_buffer_pos], + &i_buffer_size, id->p_buffer_in, + id->i_buffer_in_pos ); - /* msg_Warn( p_stream, "avcodec_decode_audio: %d used", i_used ); */ + /* msg_Warn( p_stream, "avcodec_decode_audio: %d used", + i_used ); */ id->i_buffer_pos += i_buffer_size; if( i_used < 0 ) @@ -710,7 +900,8 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ if( id->f_src.i_fourcc == VLC_FOURCC( 's', '8', ' ', ' ' ) ) { int8_t *sin = (int8_t*)id->p_buffer_in; - int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) / 2, id->i_buffer_in_pos ); + int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) + / 2, id->i_buffer_in_pos ); i_used = i_samples; while( i_samples > 0 ) { @@ -721,7 +912,8 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ else if( id->f_src.i_fourcc == VLC_FOURCC( 'u', '8', ' ', ' ' ) ) { int8_t *sin = (int8_t*)id->p_buffer_in; - int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) / 2, id->i_buffer_in_pos ); + int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) + / 2, id->i_buffer_in_pos ); i_used = i_samples; while( i_samples > 0 ) { @@ -731,7 +923,8 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ } else if( id->f_src.i_fourcc == VLC_FOURCC( 's', '1', '6', 'l' ) ) { - int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) / 2, id->i_buffer_in_pos / 2); + int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) + / 2, id->i_buffer_in_pos / 2); #ifdef WORDS_BIGENDIAN uint8_t *sin = (uint8_t*)id->p_buffer_in; i_used = i_samples * 2; @@ -753,7 +946,8 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ } else if( id->f_src.i_fourcc == VLC_FOURCC( 's', '1', '6', 'b' ) ) { - int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) / 2, id->i_buffer_in_pos / 2); + int i_samples = __MIN( ( id->i_buffer - id->i_buffer_pos ) + / 2, id->i_buffer_in_pos / 2); #ifdef WORDS_BIGENDIAN memcpy( sout, id->p_buffer_in, i_samples * 2 ); sout += i_samples; @@ -783,36 +977,144 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ id->i_buffer_in_pos -= i_used; } - /* encode as many data as possible */ + i_buffer_pos = id->i_buffer_pos; + + /* encode as much data as possible */ + +#ifdef HAVE_VORBIS_VORBISENC_H + if( id->i_buffer_pos == 0 ); + else if( id->f_dst.i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ) + { + float **buffer; + int i, j, i_samples; + sout_buffer_t *p_out; + ogg_packet op; + + if( !id->b_headers_sent ) + { + ogg_packet header[3]; + vorbis_analysis_headerout( id->p_vd, id->p_vc, + &header[0], &header[1], &header[2]); + for( i = 0; i < 3; i++ ) + { + p_out = sout_BufferNew( p_stream->p_sout, header[i].bytes); + memcpy( p_out->p_buffer, header[i].packet, + header[i].bytes ); + + p_out->i_size = header[i].bytes; + p_out->i_length = 0; + + p_out->i_dts = p_out->i_pts = id->i_dts; + + sout_BufferChain( out, p_out ); + } + id->b_headers_sent = VLC_TRUE; + } + + i_samples = id->i_buffer_pos / id->f_src.i_channels / 2; + id->i_samples_delay += i_samples; + id->i_buffer_pos = 0; + + buffer = vorbis_analysis_buffer( id->p_vd, i_samples ); + + /* convert samples to float and uninterleave */ + for( i = 0; i < id->f_dst.i_channels; i++ ) + { + for( j = 0 ; j < i_samples ; j++ ) + { + buffer[i][j]= ((float)( ((int16_t *)id->p_buffer) + [j*id->f_src.i_channels + i ] ))/ 32768.f; + } + } + + vorbis_analysis_wrote( id->p_vd, i_samples ); + + while( vorbis_analysis_blockout( id->p_vd, id->p_vb ) == 1 ) + { + vorbis_analysis( id->p_vb, NULL ); + vorbis_bitrate_addblock( id->p_vb ); + + while( vorbis_bitrate_flushpacket( id->p_vd, &op ) ) + { + int i_block_size; + p_out = sout_BufferNew( p_stream->p_sout, op.bytes ); + memcpy( p_out->p_buffer, op.packet, op.bytes ); + + i_block_size = vorbis_packet_blocksize( id->p_vi, &op ); + + if( i_block_size < 0 ) i_block_size = 0; + i_samples = ( id->i_last_block_size + + i_block_size ) >> 2; + id->i_last_block_size = i_block_size; + + p_out->i_size = op.bytes; + p_out->i_length = (mtime_t)1000000 * + (mtime_t)i_samples / + (mtime_t)id->f_dst.i_sample_rate; + + //msg_Err( p_stream, "i_dts: %lld", id->i_dts ); + + /* FIXME */ + p_out->i_dts = id->i_dts; + p_out->i_pts = id->i_dts; + + id->i_samples_delay -= i_samples; + + /* update dts */ + id->i_dts += p_out->i_length; + sout_BufferChain( out, p_out ); + + } + } + } + else +#endif + for( ;; ) { - int i_frame_size = id->ff_enc_c->frame_size * 2 * id->ff_enc_c->channels; - int i_out_size; + int i_frame_size = id->ff_enc_c->frame_size * 2 * + id->ff_dec_c->channels; + int i_out_size, i, j; sout_buffer_t *p_out; + int16_t *p_buffer = (int16_t *)(id->p_buffer + i_buffer_pos - + id->i_buffer_pos); if( id->i_buffer_pos < i_frame_size ) { break; } - /* msg_Warn( p_stream, "avcodec_encode_audio: frame size%d", i_frame_size); */ + if( id->ff_dec_c->channels != id->ff_enc_c->channels ) + { + /* dumb downmixing */ + for( i = 0; i < id->ff_enc_c->frame_size; i++ ) + { + for( j = 0 ; j < id->f_dst.i_channels; j++ ) + { + p_buffer[i*id->f_dst.i_channels+j] = p_buffer[i*id->f_src.i_channels+j]; + } + } + } + + /* msg_Warn( p_stream, "avcodec_encode_audio: frame size%d", + i_frame_size); */ i_out_size = avcodec_encode_audio( id->ff_enc_c, - id->p_buffer_out, id->i_buffer_out, - (int16_t*)id->p_buffer ); + id->p_buffer_out, id->i_buffer_out, p_buffer ); if( i_out_size <= 0 ) { break; } - memmove( id->p_buffer, - &id->p_buffer[i_frame_size], - id->i_buffer - i_frame_size ); + id->i_buffer_pos -= i_frame_size; p_out = sout_BufferNew( p_stream->p_sout, i_out_size ); memcpy( p_out->p_buffer, id->p_buffer_out, i_out_size ); p_out->i_size = i_out_size; - p_out->i_length = (mtime_t)1000000 * (mtime_t)id->ff_enc_c->frame_size / (mtime_t)id->ff_enc_c->sample_rate; + p_out->i_length = (mtime_t)1000000 * + (mtime_t)id->ff_enc_c->frame_size / + (mtime_t)id->ff_enc_c->sample_rate; + /* FIXME */ p_out->i_dts = id->i_dts; p_out->i_pts = id->i_dts; @@ -820,10 +1122,20 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ /* update dts */ id->i_dts += p_out->i_length; - /* msg_Warn( p_stream, "frame dts=%lld len %lld out=%d", p_out->i_dts, p_out->i_length, i_out_size ); */ + /* msg_Warn( p_stream, "frame dts=%lld len %lld out=%d", + p_out->i_dts, p_out->i_length, i_out_size ); */ + sout_BufferChain( out, p_out ); } + /* Copy the remaining raw samples */ + if( id->i_buffer_pos != 0 ) + { + memmove( id->p_buffer, + &id->p_buffer[i_buffer_pos - id->i_buffer_pos], + id->i_buffer_pos ); + } + } while( b_again ); return VLC_SUCCESS; @@ -833,7 +1145,8 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ /* * video */ -static int transcode_video_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_id_t *id ) +static int transcode_video_ffmpeg_new( sout_stream_t *p_stream, + sout_stream_id_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; @@ -843,7 +1156,11 @@ static int transcode_video_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_i id->f_src.i_fourcc == VLC_FOURCC( 'I', '4', '2', '2' ) || id->f_src.i_fourcc == VLC_FOURCC( 'I', '4', '4', '4' ) || id->f_src.i_fourcc == VLC_FOURCC( 'Y', 'U', 'Y', '2' ) || - id->f_src.i_fourcc == VLC_FOURCC( 'R', 'V', '2', '4' ) ) + id->f_src.i_fourcc == VLC_FOURCC( 'R', 'V', '1', '5' ) || + id->f_src.i_fourcc == VLC_FOURCC( 'R', 'V', '1', '6' ) || + id->f_src.i_fourcc == VLC_FOURCC( 'R', 'V', '2', '4' ) || + id->f_src.i_fourcc == VLC_FOURCC( 'R', 'V', '3', '2' ) || + id->f_src.i_fourcc == VLC_FOURCC( 'G', 'R', 'E', 'Y' ) ) { id->ff_dec = NULL; id->ff_dec_c = avcodec_alloc_context(); @@ -876,10 +1193,8 @@ static int transcode_video_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_i id->ff_dec_c->extradata = id->f_src.p_extra_data; id->ff_dec_c->workaround_bugs = FF_BUG_AUTODETECT; id->ff_dec_c->error_resilience= -1; - if( id->ff_dec->capabilities & CODEC_CAP_TRUNCATED ) - { - id->ff_dec_c->flags |= CODEC_FLAG_TRUNCATED; - } + id->ff_dec_c->get_buffer = transcode_video_ffmpeg_getframebuf; + id->ff_dec_c->opaque = p_sys; if( avcodec_open( id->ff_dec_c, id->ff_dec ) < 0 ) { @@ -891,40 +1206,127 @@ static int transcode_video_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_i { int b_gotpicture; AVFrame frame; - - avcodec_decode_video( id->ff_dec_c, &frame, - &b_gotpicture, - id->ff_dec_c->extradata, id->ff_dec_c->extradata_size ); + uint8_t *p_vol = malloc( id->ff_dec_c->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE ); + + memcpy( p_vol, id->ff_dec_c->extradata, + id->ff_dec_c->extradata_size ); + memset( p_vol + id->ff_dec_c->extradata_size, 0, + FF_INPUT_BUFFER_PADDING_SIZE ); + + avcodec_decode_video( id->ff_dec_c, &frame, &b_gotpicture, + id->ff_dec_c->extradata, + id->ff_dec_c->extradata_size ); + free( p_vol ); } } /* find encoder */ + id->ff_enc = id->ff_enc_c = NULL; i_ff_codec = get_ff_codec( id->f_dst.i_fourcc ); - if( i_ff_codec == 0 ) + if( i_ff_codec != 0 ) { - msg_Err( p_stream, "cannot find encoder" ); - return VLC_EGENERIC; + id->ff_enc = avcodec_find_encoder( i_ff_codec ); } - id->ff_enc = avcodec_find_encoder( i_ff_codec ); + /* Hack for external encoders */ if( !id->ff_enc ) + { + id->p_encoder = vlc_object_create( p_stream, VLC_OBJECT_ENCODER ); + id->p_encoder->i_fourcc = id->f_dst.i_fourcc; + id->p_encoder->format.video.i_width = p_sys->i_width; + id->p_encoder->format.video.i_height = p_sys->i_height; + id->p_encoder->i_bitrate = p_sys->i_vbitrate; + + if( id->p_encoder->format.video.i_width <= 0 ) + { + id->p_encoder->format.video.i_width = id->f_dst.i_width = + id->ff_dec_c->width - p_sys->i_crop_left - + p_sys->i_crop_right; + } + if( id->p_encoder->format.video.i_height <= 0 ) + { + id->p_encoder->format.video.i_height = id->f_dst.i_height = + id->ff_dec_c->height - p_sys->i_crop_top - + p_sys->i_crop_bottom; + } + + id->p_encoder->p_module = + module_Need( id->p_encoder, "video encoder", NULL ); + + if( !id->p_encoder->p_module ) + { + free( id->p_encoder ); + id->p_encoder = NULL; + } + } + /* End hack for external encoders */ + + if( !id->ff_enc && !id->p_encoder ) { msg_Err( p_stream, "cannot find encoder" ); return VLC_EGENERIC; } + /* XXX open it only when we have the first frame */ + id->b_enc_inited = VLC_FALSE; + id->i_buffer_in = 0; + id->i_buffer_in_pos = 0; + id->p_buffer_in = NULL; + + id->i_buffer = 3*1024*1024; + id->i_buffer_pos = 0; + id->p_buffer = malloc( id->i_buffer ); + + id->i_buffer_out = 0; + id->i_buffer_out_pos = 0; + id->p_buffer_out = NULL; + + id->p_ff_pic = avcodec_alloc_frame(); + id->p_ff_pic_tmp0 = NULL; + id->p_ff_pic_tmp1 = NULL; + id->p_ff_pic_tmp2 = NULL; + id->p_vresample = NULL; + + p_sys->i_last_ref_pts = 0; + p_sys->i_buggy_pts_detect = 0; + + /* This is enough for external encoders */ + if( id->p_encoder && id->p_encoder->p_module ) return VLC_SUCCESS; + + if( id->f_dst.i_fourcc == VLC_FOURCC( 'm','p','1','v' )|| + id->f_dst.i_fourcc == VLC_FOURCC( 'm','p','2','v' ) ) + { + id->f_dst.i_fourcc = VLC_FOURCC( 'm','p','g','v' ); + } + id->ff_enc_c = avcodec_alloc_context(); id->ff_enc_c->width = id->f_dst.i_width; id->ff_enc_c->height = id->f_dst.i_height; id->ff_enc_c->bit_rate = id->f_dst.i_bitrate; + + if( id->ff_dec ) + { + id->ff_enc_c->frame_rate = id->ff_dec_c->frame_rate; #if LIBAVCODEC_BUILD >= 4662 - id->ff_enc_c->frame_rate = 25 ; /* FIXME as it break mpeg */ - id->ff_enc_c->frame_rate_base= 1; + id->ff_enc_c->frame_rate_base= id->ff_dec_c->frame_rate_base; +#endif + } + else + { +#if LIBAVCODEC_BUILD >= 4662 + id->ff_enc_c->frame_rate = 25 ; /* FIXME as it break mpeg */ + id->ff_enc_c->frame_rate_base= 1; #else - id->ff_enc_c->frame_rate = 25 * FRAME_RATE_BASE; + id->ff_enc_c->frame_rate = 25 * FRAME_RATE_BASE; #endif + } + id->ff_enc_c->gop_size = p_sys->i_key_int >= 0 ? p_sys->i_key_int : 50; + id->ff_enc_c->max_b_frames = __MIN( p_sys->i_b_frames, FF_MAX_B_FRAMES ); + id->ff_enc_c->b_frame_strategy = 0; + id->ff_enc_c->b_quant_factor = 2.0; if( p_sys->i_vtolerance >= 0 ) { @@ -932,34 +1334,21 @@ static int transcode_video_ffmpeg_new ( sout_stream_t *p_stream, sout_stream_i } id->ff_enc_c->qmin = p_sys->i_qmin; id->ff_enc_c->qmax = p_sys->i_qmax; - if( p_sys->b_hq ) + +#if LIBAVCODEC_BUILD >= 4673 + id->ff_enc_c->mb_decision = p_sys->i_hq; +#else + if( p_sys->i_hq ) { id->ff_enc_c->flags |= CODEC_FLAG_HQ; } +#endif if( i_ff_codec == CODEC_ID_RAWVIDEO ) { id->ff_enc_c->pix_fmt = get_ff_chroma( id->f_dst.i_fourcc ); } - /* XXX open it only when we have the first frame */ - id->b_enc_inited = VLC_FALSE; - id->i_buffer_in = 0; - id->i_buffer_in_pos = 0; - id->p_buffer_in = NULL; - id->i_buffer = 3*1024*1024; - id->i_buffer_pos = 0; - id->p_buffer = malloc( id->i_buffer ); - - id->i_buffer_out = 0; - id->i_buffer_out_pos = 0; - id->p_buffer_out = NULL; - - id->p_ff_pic = avcodec_alloc_frame(); - id->p_ff_pic_tmp0 = NULL; - id->p_ff_pic_tmp1 = NULL; - id->p_ff_pic_tmp2 = NULL; - id->p_vresample = NULL; return VLC_SUCCESS; } @@ -969,7 +1358,13 @@ static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, sout_stream_ { avcodec_close( id->ff_dec_c ); } - if( id->b_enc_inited ) + if( id->p_encoder ) + { + /* External encoding */ + module_Unneed( id->p_encoder, id->p_encoder->p_module ); + vlc_object_destroy( id->p_encoder->p_module ); + } + else if( id->b_enc_inited ) { avcodec_close( id->ff_enc_c ); } @@ -1000,13 +1395,12 @@ static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, sout_stream_ } free( id->ff_dec_c ); - free( id->ff_enc_c ); - + if( id->ff_enc_c ) free( id->ff_enc_c ); free( id->p_buffer ); } -static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_id_t *id, - sout_buffer_t *in, sout_buffer_t **out ) +static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, + sout_stream_id_t *id, sout_buffer_t *in, sout_buffer_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; int i_used; @@ -1026,6 +1420,7 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ { /* decode frame */ frame = id->p_ff_pic; + p_sys->i_input_pts = in->i_pts; if( id->ff_dec ) { i_used = avcodec_decode_video( id->ff_dec_c, frame, @@ -1041,6 +1436,9 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ id->ff_dec_c->width, id->ff_dec_c->height ); i_used = i_data; b_gotpicture = 1; + + /* Set PTS */ + frame->pts = p_sys->i_input_pts; } if( i_used < 0 ) @@ -1056,10 +1454,62 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ return VLC_SUCCESS; } - if( !id->b_enc_inited ) + /* Get the pts of the decoded frame if any, otherwise keep the + * interpolated one */ + if( frame->pts > 0 ) + { + p_sys->i_output_pts = frame->pts; + } + + if( !id->b_enc_inited && id->p_encoder ) + { + block_t *p_block; + + /* XXX hack because of copy packetizer and mpeg4video that can fail + * detecting size */ +#if 0 + if( id->p_encoder->i_width <= 0 ) + { + id->p_encoder->i_width = id->f_dst.i_width = + id->ff_dec_c->width - p_sys->i_crop_left - + p_sys->i_crop_right; + } + if( id->p_encoder->i_height <= 0 ) + { + id->p_encoder->i_height = id->f_dst.i_height = + id->ff_dec_c->height - p_sys->i_crop_top - + p_sys->i_crop_bottom; + } +#endif + + id->p_encoder->i_bitrate = p_sys->i_vbitrate; + + if( !( id->id = p_stream->p_sys->p_out->pf_add( p_stream->p_sys->p_out, &id->f_dst ) ) ) + { + msg_Err( p_stream, "cannot add this stream" ); + id->b_transcode = VLC_FALSE; + return VLC_EGENERIC; + } + + while( (p_block = id->p_encoder->pf_header( id->p_encoder )) ) + { + sout_buffer_t *p_out; + p_out = sout_BufferNew( p_stream->p_sout, p_block->i_buffer ); + memcpy( p_out->p_buffer, p_block->p_buffer, p_block->i_buffer); + p_out->i_dts = in->i_dts; + p_out->i_pts = in->i_dts; + sout_BufferChain( out, p_out ); + } + + id->i_inter_pixfmt = + get_ff_chroma( id->p_encoder->format.video.i_chroma ); + + id->b_enc_inited = VLC_TRUE; + } + else if( !id->b_enc_inited ) { - /* XXX hack because of copy packetizer and mpeg4video that can failed - detecting size */ + /* XXX hack because of copy packetizer and mpeg4video that can fail + * detecting size */ if( id->ff_enc_c->width <= 0 ) { id->ff_enc_c->width = @@ -1071,12 +1521,21 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ id->f_dst.i_height = id->ff_dec_c->height - p_sys->i_crop_top - p_sys->i_crop_bottom; } + /* Make sure we get extradata filled by the encoder */ + id->ff_enc_c->extradata_size = 0; + id->ff_enc_c->extradata = NULL; + id->ff_enc_c->flags |= CODEC_FLAG_GLOBAL_HEADER; + if( avcodec_open( id->ff_enc_c, id->ff_enc ) ) { msg_Err( p_stream, "cannot open encoder" ); return VLC_EGENERIC; } + id->f_dst.i_extra_data = id->ff_enc_c->extradata_size; + id->f_dst.p_extra_data = id->ff_enc_c->extradata; + id->ff_enc_c->flags &= ~CODEC_FLAG_GLOBAL_HEADER; + if( !( id->id = p_stream->p_sys->p_out->pf_add( p_stream->p_sys->p_out, &id->f_dst ) ) ) { msg_Err( p_stream, "cannot add this stream" ); @@ -1084,6 +1543,9 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ id->b_transcode = VLC_FALSE; return VLC_EGENERIC; } + + id->i_inter_pixfmt = id->ff_enc_c->pix_fmt; + id->b_enc_inited = VLC_TRUE; } @@ -1102,7 +1564,7 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ buf = malloc( i_size ); avpicture_fill( (AVPicture*)id->p_ff_pic_tmp0, buf, - id->ff_enc_c->pix_fmt, + id->i_inter_pixfmt, id->ff_dec_c->width, id->ff_dec_c->height ); } @@ -1114,33 +1576,34 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ } /* convert pix format */ - if( id->ff_dec_c->pix_fmt != id->ff_enc_c->pix_fmt ) + if( id->ff_dec_c->pix_fmt != id->i_inter_pixfmt ) { if( id->p_ff_pic_tmp1 == NULL ) { int i_size; uint8_t *buf; id->p_ff_pic_tmp1 = avcodec_alloc_frame(); - i_size = avpicture_get_size( id->ff_enc_c->pix_fmt, - id->ff_dec_c->width, id->ff_dec_c->height ); + i_size = avpicture_get_size( id->i_inter_pixfmt, + id->ff_dec_c->width, + id->ff_dec_c->height ); buf = malloc( i_size ); avpicture_fill( (AVPicture*)id->p_ff_pic_tmp1, buf, - id->ff_enc_c->pix_fmt, + id->i_inter_pixfmt, id->ff_dec_c->width, id->ff_dec_c->height ); } - img_convert( (AVPicture*)id->p_ff_pic_tmp1, id->ff_enc_c->pix_fmt, - (AVPicture*)frame, id->ff_dec_c->pix_fmt, + img_convert( (AVPicture*)id->p_ff_pic_tmp1, id->i_inter_pixfmt, + (AVPicture*)frame, id->ff_dec_c->pix_fmt, id->ff_dec_c->width, id->ff_dec_c->height ); frame = id->p_ff_pic_tmp1; } /* convert size and crop */ - if( id->ff_dec_c->width != id->ff_enc_c->width || - id->ff_dec_c->height != id->ff_enc_c->height || + if( id->ff_dec_c->width != id->f_dst.i_width || + id->ff_dec_c->height != id->f_dst.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 ) { @@ -1149,17 +1612,19 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ int i_size; uint8_t *buf; id->p_ff_pic_tmp2 = avcodec_alloc_frame(); - i_size = avpicture_get_size( id->ff_enc_c->pix_fmt, - id->ff_enc_c->width, id->ff_enc_c->height ); + i_size = avpicture_get_size( id->i_inter_pixfmt, + id->f_dst.i_width, + id->f_dst.i_height ); buf = malloc( i_size ); avpicture_fill( (AVPicture*)id->p_ff_pic_tmp2, buf, - id->ff_enc_c->pix_fmt, - id->ff_enc_c->width, id->ff_enc_c->height ); + id->i_inter_pixfmt, + id->f_dst.i_width, id->f_dst.i_height ); id->p_vresample = - img_resample_full_init( id->ff_enc_c->width, id->ff_enc_c->height, + img_resample_full_init( id->f_dst.i_width, + id->f_dst.i_height, id->ff_dec_c->width, id->ff_dec_c->height, p_stream->p_sys->i_crop_top, p_stream->p_sys->i_crop_bottom, @@ -1167,12 +1632,68 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ p_stream->p_sys->i_crop_right ); } - img_resample( id->p_vresample, (AVPicture*)id->p_ff_pic_tmp2, (AVPicture*)frame ); + img_resample( id->p_vresample, (AVPicture*)id->p_ff_pic_tmp2, + (AVPicture*)frame ); frame = id->p_ff_pic_tmp2; } - i_out = avcodec_encode_video( id->ff_enc_c, id->p_buffer, id->i_buffer, frame ); + /* Set the pts of the frame being encoded (segfaults with mpeg4!)*/ + if( id->p_encoder || + id->f_dst.i_fourcc == VLC_FOURCC( 'm', 'p', 'g', 'v' ) ) + frame->pts = p_sys->i_output_pts; + else + frame->pts = 0; + + /* Interpolate the next PTS + * (needed by the mpeg video packetizer which can send pts <= 0 ) */ + if( id->ff_dec_c && id->ff_dec_c->frame_rate > 0 ) + { + p_sys->i_output_pts += I64C(1000000) * (2 + frame->repeat_pict) * + id->ff_dec_c->frame_rate_base / (2 * id->ff_dec_c->frame_rate); + } + + if( id->p_encoder ) + { + /* External encoding */ + block_t *p_block; + picture_t pic; + int i_plane; + + vout_InitPicture( VLC_OBJECT(p_stream), &pic, + id->p_encoder->format.video.i_chroma, + id->f_dst.i_width, id->f_dst.i_height, + id->f_dst.i_width * VOUT_ASPECT_FACTOR / + id->f_dst.i_height ); + + for( i_plane = 0; i_plane < pic.i_planes; i_plane++ ) + { + pic.p[i_plane].p_pixels = frame->data[i_plane]; + pic.p[i_plane].i_pitch = frame->linesize[i_plane]; + } + + pic.date = frame->pts; + + p_block = id->p_encoder->pf_encode_video( id->p_encoder, &pic ); + if( p_block ) + { + sout_buffer_t *p_out; + p_out = sout_BufferNew( p_stream->p_sout, p_block->i_buffer ); + memcpy( p_out->p_buffer, p_block->p_buffer, p_block->i_buffer); + p_out->i_dts = p_block->i_dts; + p_out->i_pts = p_block->i_pts; + sout_BufferChain( out, p_out ); + block_Release( p_block ); + } + + return VLC_SUCCESS; + } + + /* Let ffmpeg select the frame type */ + frame->pict_type = 0; + + i_out = avcodec_encode_video( id->ff_enc_c, id->p_buffer, + id->i_buffer, frame ); if( i_out > 0 ) { sout_buffer_t *p_out; @@ -1181,9 +1702,45 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ memcpy( p_out->p_buffer, id->p_buffer, i_out ); p_out->i_size = i_out; - p_out->i_length = in->i_length; - p_out->i_dts = in->i_dts; - p_out->i_pts = in->i_dts; /* FIXME */ + + if( id->ff_enc_c->coded_frame->pts != 0 && + p_sys->i_buggy_pts_detect != id->ff_enc_c->coded_frame->pts ) + { + p_sys->i_buggy_pts_detect = id->ff_enc_c->coded_frame->pts; + + /* FIXME, 3-2 pulldown is not handled correctly */ + p_out->i_length = in->i_length; + p_out->i_pts = id->ff_enc_c->coded_frame->pts; + + if( !id->ff_enc_c->delay || + ( id->ff_enc_c->coded_frame->pict_type != FF_I_TYPE && + id->ff_enc_c->coded_frame->pict_type != FF_P_TYPE ) ) + { + p_out->i_dts = p_out->i_pts; + } + else + { + if( p_sys->i_last_ref_pts ) + { + p_out->i_dts = p_sys->i_last_ref_pts; + } + else + { + /* Let's put something sensible */ + p_out->i_dts = p_out->i_pts; + } + + p_sys->i_last_ref_pts = p_out->i_pts; + } + } + else + { + /* Buggy libavcodec which doesn't update coded_frame->pts + * correctly */ + p_out->i_length = in->i_length; + p_out->i_dts = in->i_dts; + p_out->i_pts = in->i_dts; + } sout_BufferChain( out, p_out ); } @@ -1197,3 +1754,19 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ return VLC_SUCCESS; } +/***************************************************************************** + * transcode_video_ffmpeg_getframebuf: + * + * Callback used by ffmpeg to get a frame buffer. + * We use it to get the right PTS for each decoded picture. + *****************************************************************************/ +static int transcode_video_ffmpeg_getframebuf(struct AVCodecContext *p_context, + AVFrame *p_frame) +{ + sout_stream_sys_t *p_sys = (sout_stream_sys_t *)p_context->opaque; + + /* Set PTS */ + p_frame->pts = p_sys->i_input_pts; + + return avcodec_default_get_buffer( p_context, p_frame ); +}