X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fstream_out%2Ftranscode.c;h=3a2b8fd7c49afde4c3cf1daa485ba4c7027c6a63;hb=3e59b60f0686a4a11bb3dca794586b394371cc56;hp=6cd02b46c3d52dbc355dff998816ba048f1de777;hpb=d4a5241682dfde2991f511da9887b752b9307ec7;p=vlc diff --git a/modules/stream_out/transcode.c b/modules/stream_out/transcode.c index 6cd02b46c3..3a2b8fd7c4 100644 --- a/modules/stream_out/transcode.c +++ b/modules/stream_out/transcode.c @@ -2,9 +2,10 @@ * transcode.c ***************************************************************************** * Copyright (C) 2001, 2002 VideoLAN - * $Id: transcode.c,v 1.21 2003/06/30 20:44:35 gbazin Exp $ + * $Id: transcode.c,v 1.60 2003/12/07 12:11:13 gbazin Exp $ * * Authors: Laurent Aimar + * Gildas Bazin * * 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 @@ -30,6 +31,8 @@ #include #include #include +#include +#include /* ffmpeg header */ #ifdef HAVE_FFMPEG_AVCODEC_H @@ -38,18 +41,13 @@ # include #endif -/* vorbis header */ -#ifdef HAVE_VORBIS_VORBISENC_H -# include -#endif - /***************************************************************************** * Exported prototypes *****************************************************************************/ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); -static sout_stream_id_t *Add ( sout_stream_t *, sout_format_t * ); +static sout_stream_id_t *Add ( sout_stream_t *, es_format_t * ); static int Del ( sout_stream_t *, sout_stream_id_t * ); static int Send( sout_stream_t *, sout_stream_id_t *, sout_buffer_t* ); @@ -61,6 +59,19 @@ 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 *); + +static int pi_channels_maps[6] = +{ + 0, + AOUT_CHAN_CENTER, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, + AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT + | AOUT_CHAN_REARRIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT +}; + /***************************************************************************** * Module descriptor *****************************************************************************/ @@ -85,16 +96,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; + vlc_bool_t b_strict_rc; + vlc_bool_t b_pre_me; + vlc_bool_t b_hurry_up; 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; }; /***************************************************************************** @@ -107,35 +125,25 @@ static int Open( vlc_object_t *p_this ) char *codec; p_sys = malloc( sizeof( sout_stream_sys_t ) ); + memset( p_sys, 0, sizeof(struct sout_stream_sys_t) ); p_sys->p_out = sout_stream_new( p_stream->p_sout, p_stream->psz_next ); - p_sys->i_acodec = 0; - p_sys->i_sample_rate = 0; - p_sys->i_channels = 0; - p_sys->i_abitrate = 0; - - p_sys->i_vcodec = 0; - p_sys->i_vbitrate = 0; p_sys->i_vtolerance = -1; - p_sys->i_width = 0; - p_sys->i_height = 0; p_sys->i_key_int = -1; p_sys->i_qmin = 2; p_sys->i_qmax = 31; - p_sys->b_hq = VLC_FALSE; - p_sys->b_deinterlace= VLC_FALSE; - - p_sys->i_crop_top = 0; - p_sys->i_crop_bottom= 0; - p_sys->i_crop_right = 0; - p_sys->i_crop_left = 0; +#if LIBAVCODEC_BUILD >= 4673 + p_sys->i_hq = FF_MB_DECISION_SIMPLE; +#else + p_sys->i_hq = VLC_FALSE; +#endif if( ( codec = sout_cfg_find_value( p_stream->p_cfg, "acodec" ) ) ) { char fcc[4] = " "; char *val; - memcpy( fcc, codec, strlen( codec ) ); + memcpy( fcc, codec, __MIN( strlen( codec ), 4 ) ); p_sys->i_acodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] ); @@ -156,8 +164,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 ); } @@ -167,7 +174,7 @@ static int Open( vlc_object_t *p_this ) char fcc[4] = " "; char *val; - memcpy( fcc, codec, strlen( codec ) ); + memcpy( fcc, codec, __MIN( strlen( codec ), 4 ) ); p_sys->i_vcodec = VLC_FOURCC( fcc[0], fcc[1], fcc[2], fcc[3] ); @@ -195,6 +202,18 @@ static int Open( vlc_object_t *p_this ) { p_sys->b_deinterlace = VLC_TRUE; } + if( sout_cfg_find( p_stream->p_cfg, "strict_rc" ) ) + { + p_sys->b_strict_rc = VLC_TRUE; + } + if( sout_cfg_find( p_stream->p_cfg, "pre_me" ) ) + { + p_sys->b_pre_me = VLC_TRUE; + } + if( sout_cfg_find( p_stream->p_cfg, "hurry_up" ) ) + { + p_sys->b_hurry_up = VLC_TRUE; + } /* crop */ if( ( val = sout_cfg_find_value( p_stream->p_cfg, "croptop" ) ) ) { @@ -216,10 +235,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 ); @@ -250,13 +295,15 @@ 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; } /***************************************************************************** * Close: *****************************************************************************/ - static void Close( vlc_object_t * p_this ) { sout_stream_t *p_stream = (sout_stream_t*)p_this; @@ -269,58 +316,38 @@ static void Close( vlc_object_t * p_this ) struct sout_stream_id_t { vlc_fourcc_t b_transcode; - sout_format_t f_src; /* only if transcoding */ - sout_format_t f_dst; /* " " " */ + es_format_t f_src; /* only if transcoding */ + es_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; + vlc_fourcc_t b_enc_inited; + /* ffmpeg part */ AVCodec *ff_dec; AVCodecContext *ff_dec_c; - - vlc_fourcc_t b_enc_inited; - AVCodec *ff_enc; - AVCodecContext *ff_enc_c; - mtime_t i_dts; mtime_t i_length; - int i_buffer_in; - int i_buffer_in_pos; - uint8_t *p_buffer_in; - int i_buffer; int i_buffer_pos; uint8_t *p_buffer; - int i_buffer_out; - int i_buffer_out_pos; - uint8_t *p_buffer_out; - AVFrame *p_ff_pic; AVFrame *p_ff_pic_tmp0; /* to do deinterlace */ AVFrame *p_ff_pic_tmp1; /* to do pix conversion */ 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, es_format_t *p_fmt ) { sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_id_t *id; @@ -328,25 +355,27 @@ static sout_stream_id_t * Add( sout_stream_t *p_stream, sout_format_t *p_fmt ) 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, "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'", - (char*)&p_fmt->i_fourcc, + (char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec ); /* src format */ - memcpy( &id->f_src, p_fmt, sizeof( sout_format_t ) ); + memcpy( &id->f_src, p_fmt, sizeof( es_format_t ) ); /* create dst format */ id->f_dst.i_cat = AUDIO_ES; - id->f_dst.i_fourcc = p_sys->i_acodec; - id->f_dst.i_sample_rate = p_sys->i_sample_rate > 0 ? p_sys->i_sample_rate : id->f_src.i_sample_rate; - id->f_dst.i_channels = p_sys->i_channels > 0 ? p_sys->i_channels : id->f_src.i_channels; + id->f_dst.i_codec = p_sys->i_acodec; + id->f_dst.audio.i_rate = p_sys->i_sample_rate > 0 ? p_sys->i_sample_rate : id->f_src.audio.i_rate; + id->f_dst.audio.i_channels = p_sys->i_channels > 0 ? p_sys->i_channels : id->f_src.audio.i_channels; id->f_dst.i_bitrate = p_sys->i_abitrate > 0 ? p_sys->i_abitrate : 64000; - id->f_dst.i_block_align = 0; - id->f_dst.i_extra_data = 0; - id->f_dst.p_extra_data = NULL; + id->f_dst.audio.i_blockalign = 0; + id->f_dst.i_extra = 0; + id->f_dst.p_extra = NULL; /* build decoder -> filter -> encoder */ if( transcode_audio_ffmpeg_new( p_stream, id ) ) @@ -359,24 +388,30 @@ static sout_stream_id_t * Add( sout_stream_t *p_stream, sout_format_t *p_fmt ) /* 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 ) { msg_Dbg( p_stream, "creating video transcoding from fcc=`%4.4s' to fcc=`%4.4s'", - (char*)&p_fmt->i_fourcc, + (char*)&p_fmt->i_codec, (char*)&p_sys->i_vcodec ); - memcpy( &id->f_src, p_fmt, sizeof( sout_format_t ) ); + memcpy( &id->f_src, p_fmt, sizeof( es_format_t ) ); /* create dst format */ id->f_dst.i_cat = VIDEO_ES; - id->f_dst.i_fourcc = p_sys->i_vcodec; - id->f_dst.i_width = p_sys->i_width ; /* > 0 ? p_sys->i_width : id->f_src.i_width; */ - id->f_dst.i_height = p_sys->i_height; /* > 0 ? p_sys->i_height: id->f_src.i_height; */ + id->f_dst.i_codec = p_sys->i_vcodec; + id->f_dst.video.i_width = p_sys->i_width ; /* > 0 ? p_sys->i_width : id->f_src.i_width; */ + id->f_dst.video.i_height= p_sys->i_height; /* > 0 ? p_sys->i_height: id->f_src.i_height; */ id->f_dst.i_bitrate = p_sys->i_vbitrate > 0 ? p_sys->i_vbitrate : 800*1000; - id->f_dst.i_extra_data = 0; - id->f_dst.p_extra_data = NULL; + id->f_dst.i_extra = 0; + id->f_dst.p_extra = NULL; /* build decoder -> filter -> encoder */ if( transcode_video_ffmpeg_new( p_stream, id ) ) @@ -393,7 +428,7 @@ static sout_stream_id_t * Add( sout_stream_t *p_stream, sout_format_t *p_fmt ) } else { - msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')", (char*)&p_fmt->i_fourcc ); + msg_Dbg( p_stream, "not transcoding a stream (fcc=`%4.4s')", (char*)&p_fmt->i_codec ); id->id = p_sys->p_out->pf_add( p_sys->p_out, p_fmt ); id->b_transcode = VLC_FALSE; @@ -476,20 +511,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 }, @@ -504,7 +547,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 } @@ -535,8 +581,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: @@ -544,49 +598,82 @@ static inline int get_ff_chroma( vlc_fourcc_t i_chroma ) } } +static inline vlc_fourcc_t get_vlc_chroma( int i_pix_fmt ) +{ + switch( i_pix_fmt ) + { + case PIX_FMT_YUV420P: + return VLC_FOURCC('I','4','2','0'); + case PIX_FMT_YUV422P: + return VLC_FOURCC('I','4','2','2'); + case PIX_FMT_YUV444P: + return VLC_FOURCC('I','4','4','4'); + + case PIX_FMT_YUV422: + return VLC_FOURCC('Y','U','Y','2'); + + case PIX_FMT_RGB555: + return VLC_FOURCC('R','V','1','5'); + case PIX_FMT_RGB565: + return VLC_FOURCC('R','V','1','6'); + case PIX_FMT_RGB24: + return VLC_FOURCC('R','V','2','4'); + case PIX_FMT_RGBA32: + return VLC_FOURCC('R','V','3','2'); + case PIX_FMT_GRAY8: + return VLC_FOURCC('G','R','E','Y'); + + case PIX_FMT_YUV410P: + case PIX_FMT_YUV411P: + case PIX_FMT_BGR24: + default: + return 0; + } +} + static int transcode_audio_ffmpeg_new( sout_stream_t *p_stream, sout_stream_id_t *id ) { int i_ff_codec; - if( id->f_src.i_fourcc == VLC_FOURCC('s','1','6','l') || - id->f_src.i_fourcc == VLC_FOURCC('s','1','6','b') || - id->f_src.i_fourcc == VLC_FOURCC('s','8',' ',' ') || - id->f_src.i_fourcc == VLC_FOURCC('u','8',' ',' ') ) + if( id->f_src.i_codec == VLC_FOURCC('s','1','6','l') || + id->f_src.i_codec == VLC_FOURCC('s','1','6','b') || + id->f_src.i_codec == VLC_FOURCC('s','8',' ',' ') || + id->f_src.i_codec == VLC_FOURCC('u','8',' ',' ') ) { id->ff_dec = NULL; id->ff_dec_c = avcodec_alloc_context(); - id->ff_dec_c->sample_rate = id->f_src.i_sample_rate; - id->ff_dec_c->channels = id->f_src.i_channels; - id->ff_dec_c->block_align = id->f_src.i_block_align; + id->ff_dec_c->sample_rate = id->f_src.audio.i_rate; + id->ff_dec_c->channels = id->f_src.audio.i_channels; + id->ff_dec_c->block_align = id->f_src.audio.i_blockalign; id->ff_dec_c->bit_rate = id->f_src.i_bitrate; } else { /* find decoder */ - i_ff_codec = get_ff_codec( id->f_src.i_fourcc ); + i_ff_codec = get_ff_codec( id->f_src.i_codec ); 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; } id->ff_dec_c = avcodec_alloc_context(); - id->ff_dec_c->sample_rate = id->f_src.i_sample_rate; - id->ff_dec_c->channels = id->f_src.i_channels; - id->ff_dec_c->block_align = id->f_src.i_block_align; + id->ff_dec_c->sample_rate = id->f_src.audio.i_rate; + id->ff_dec_c->channels = id->f_src.audio.i_channels; + id->ff_dec_c->block_align = id->f_src.audio.i_blockalign; id->ff_dec_c->bit_rate = id->f_src.i_bitrate; - id->ff_dec_c->extradata_size = id->f_src.i_extra_data; - id->ff_dec_c->extradata = id->f_src.p_extra_data; + id->ff_dec_c->extradata_size = id->f_src.i_extra; + id->ff_dec_c->extradata = id->f_src.p_extra; if( avcodec_open( id->ff_dec_c, id->ff_dec ) ) { msg_Err( p_stream, "cannot open decoder" ); @@ -594,73 +681,47 @@ static int transcode_audio_ffmpeg_new( sout_stream_t *p_stream, } } - /* 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.audio.i_channels = __MIN( id->f_dst.audio.i_channels, id->f_src.audio.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 ) + /* find encoder */ + id->p_encoder = vlc_object_create( p_stream, VLC_OBJECT_ENCODER ); + + /* Initialization of encoder format structures */ + es_format_Init( &id->p_encoder->fmt_in, AUDIO_ES, AOUT_FMT_S16_NE ); + id->p_encoder->fmt_in.audio.i_format = AOUT_FMT_S16_NE; + id->p_encoder->fmt_in.audio.i_rate = id->f_dst.audio.i_rate; + id->p_encoder->fmt_in.audio.i_physical_channels = + id->p_encoder->fmt_in.audio.i_original_channels = + pi_channels_maps[id->f_dst.audio.i_channels]; + id->p_encoder->fmt_in.audio.i_channels = id->f_dst.audio.i_channels; + + id->p_encoder->fmt_out = id->p_encoder->fmt_in; + id->p_encoder->fmt_out.i_codec = id->f_dst.i_codec; + id->p_encoder->fmt_out.i_bitrate = id->f_dst.i_bitrate; + + id->p_encoder->p_module = + module_Need( id->p_encoder, "encoder", NULL ); + if( !id->p_encoder->p_module ) { - msg_Err( p_stream, "cannot find encoder" ); + vlc_object_destroy( id->p_encoder ); + msg_Err( p_stream, "cannot open encoder" ); return VLC_EGENERIC; } - id->ff_enc = avcodec_find_encoder( i_ff_codec ); - if( !id->ff_enc ) - { - msg_Err( p_stream, "cannot find encoder" ); - return VLC_EGENERIC; - } + id->b_enc_inited = VLC_FALSE; - 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; + id->f_dst.i_extra = id->p_encoder->fmt_out.i_extra; + id->f_dst.p_extra = id->p_encoder->fmt_out.p_extra; - if( avcodec_open( id->ff_enc_c, id->ff_enc ) ) + /* Hack for mp3 transcoding support */ + if( id->f_dst.i_codec == VLC_FOURCC( 'm','p','3',' ' ) ) { - msg_Err( p_stream, "cannot open encoder" ); - return VLC_EGENERIC; + id->f_dst.i_codec = VLC_FOURCC( 'm','p','g','a' ); } return VLC_SUCCESS; @@ -672,32 +733,13 @@ static void transcode_audio_ffmpeg_close( sout_stream_t *p_stream, if( id->ff_dec ) { avcodec_close( id->ff_dec_c ); + free( id->ff_dec_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 ); - if( id->f_dst.i_fourcc != VLC_FOURCC('v','o','r','b') ) - free( id->ff_enc_c ); + module_Unneed( id->p_encoder, id->p_encoder->p_module ); + vlc_object_destroy( id->p_encoder ); - 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, @@ -705,310 +747,192 @@ static int transcode_audio_ffmpeg_process( sout_stream_t *p_stream, sout_buffer_t *in, sout_buffer_t **out ) { - vlc_bool_t b_again = VLC_FALSE; - + aout_buffer_t aout_buf; + block_t *p_block; + int i_buffer = in->i_size; + char *p_buffer = in->p_buffer; + id->i_dts = in->i_dts; *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; - - if( id->i_buffer_in_pos + (int)in->i_size > id->i_buffer_in ) + while( i_buffer ) { - /* 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 ); - id->i_buffer_in_pos += in->i_size; + id->i_buffer_pos = 0; - do - { /* decode as much data as possible */ if( id->ff_dec ) { - for( ;; ) - { - int i_buffer_size; - int i_used; - - i_buffer_size = id->i_buffer - id->i_buffer_pos; + int i_used; - 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 ); + i_used = avcodec_decode_audio( id->ff_dec_c, + (int16_t*)id->p_buffer, &id->i_buffer_pos, + p_buffer, i_buffer ); - /* msg_Warn( p_stream, "avcodec_decode_audio: %d used", - i_used ); */ - id->i_buffer_pos += i_buffer_size; - - if( i_used < 0 ) - { - msg_Warn( p_stream, "error"); - id->i_buffer_in_pos = 0; - break; - } - else if( i_used < id->i_buffer_in_pos ) - { - memmove( id->p_buffer_in, - &id->p_buffer_in[i_used], - id->i_buffer_in - i_used ); - id->i_buffer_in_pos -= i_used; - } - else - { - id->i_buffer_in_pos = 0; - break; - } - - if( id->i_buffer_pos >= AVCODEC_MAX_AUDIO_FRAME_SIZE ) - { - /* buffer full */ - b_again = VLC_TRUE; - break; - } +#if 0 + msg_Warn( p_stream, "avcodec_decode_audio: %d used on %d", + i_used, i_buffer ); +#endif + if( i_used < 0 ) + { + msg_Warn( p_stream, "error audio decoding"); + break; } + + i_buffer -= i_used; + p_buffer += i_used; } else { - int16_t *sout = (int16_t*)&id->p_buffer[id->i_buffer_pos]; - int i_used = 0; + int16_t *sout = (int16_t*)id->p_buffer; - if( id->f_src.i_fourcc == VLC_FOURCC( 's', '8', ' ', ' ' ) ) + if( id->f_src.i_codec == VLC_FOURCC( 's', '8', ' ', ' ' ) || + id->f_src.i_codec == 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 ); - i_used = i_samples; - while( i_samples > 0 ) - { - *sout++ = ( *sin++ ) << 8; - i_samples--; - } - } - 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 ); - i_used = i_samples; - while( i_samples > 0 ) - { - *sout++ = ( *sin++ - 128 ) << 8; - i_samples--; - } + int8_t *sin = (int8_t*)p_buffer; + int i_used = __MIN( id->i_buffer/2, i_buffer ); + int i_samples = i_used; + + if( id->f_src.i_codec == VLC_FOURCC( 's', '8', ' ', ' ' ) ) + while( i_samples > 0 ) + { + *sout++ = ( *sin++ ) << 8; + i_samples--; + } + else + while( i_samples > 0 ) + { + *sout++ = ( *sin++ - 128 ) << 8; + i_samples--; + } + + i_buffer -= i_used; + p_buffer += i_used; + id->i_buffer_pos = i_used * 2; } - else if( id->f_src.i_fourcc == VLC_FOURCC( 's', '1', '6', 'l' ) ) + else if( id->f_src.i_codec == VLC_FOURCC( 's', '1', '6', 'l' ) || + id->f_src.i_codec == VLC_FOURCC( 's', '1', '6', 'b' ) ) { - 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; - while( i_samples > 0 ) - { - uint8_t tmp[2]; - - tmp[1] = *sin++; - tmp[0] = *sin++; - *sout++ = *(int16_t*)tmp; - i_samples--; - } + int16_t *sin = (int16_t*)p_buffer; + int i_used = __MIN( id->i_buffer, i_buffer ); + int i_samples = i_used / 2; + int b_invert_indianness; + if( id->f_src.i_codec == VLC_FOURCC( 's', '1', '6', 'l' ) ) +#ifdef WORDS_BIGENDIAN + b_invert_indianness = 1; #else - memcpy( sout, id->p_buffer_in, i_samples * 2 ); - sout += i_samples; - i_used = i_samples * 2; + b_invert_indianness = 0; #endif - } - 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); + else #ifdef WORDS_BIGENDIAN - memcpy( sout, id->p_buffer_in, i_samples * 2 ); - sout += i_samples; - i_used = i_samples * 2; + b_invert_indianness = 0; #else - uint8_t *sin = (uint8_t*)id->p_buffer_in; - i_used = i_samples * 2; - while( i_samples > 0 ) - { - uint8_t tmp[2]; + b_invert_indianness = 1; +#endif - tmp[1] = *sin++; - tmp[0] = *sin++; - *sout++ = *(int16_t*)tmp; - i_samples--; + if( b_invert_indianness ) + { + while( i_samples > 0 ) + { + uint8_t tmp[2]; + + tmp[1] = *sin++; + tmp[0] = *sin++; + *sout++ = *(int16_t*)tmp; + i_samples--; + } + } + else + { + memcpy( sout, sin, i_used ); + sout += i_samples; } -#endif - } - id->i_buffer_pos = (uint8_t*)sout - id->p_buffer; - if( i_used < id->i_buffer_in_pos ) - { - memmove( id->p_buffer_in, - &id->p_buffer_in[i_used], - id->i_buffer_in - i_used ); + i_buffer -= i_used; + p_buffer += i_used; + id->i_buffer_pos = i_used; } - id->i_buffer_in_pos -= i_used; } - /* encode as much data as possible */ + if( id->i_buffer_pos == 0 ) continue; -#ifdef HAVE_VORBIS_VORBISENC_H - if( id->i_buffer_pos == 0 ); - else if( id->f_dst.i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ) + /* Encode as much data as possible */ + if( !id->b_enc_inited && id->p_encoder->pf_header ) { - float **buffer; - int i, j, i_samples; - sout_buffer_t *p_out; - ogg_packet op; - - if( id->i_buffer_pos == 0 ) - { - break; - } - - if( !id->b_headers_sent ) + p_block = id->p_encoder->pf_header( id->p_encoder ); + while( p_block ) { - 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 ); + sout_buffer_t *p_out; + block_t *p_prev_block = p_block; - p_out->i_size = header[i].bytes; - p_out->i_length = 0; + 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_out->i_pts = in->i_dts; + p_out->i_length = 0; + sout_BufferChain( out, p_out ); - p_out->i_dts = p_out->i_pts = 0; - - sout_BufferChain( out, p_out ); - } - id->b_headers_sent = VLC_TRUE; + p_block = p_block->p_next; + block_Release( p_prev_block ); } - i_samples = id->i_buffer_pos / id->f_dst.i_channels / 2; - id->i_samples_delay += i_samples; - id->i_buffer_pos = 0; - - buffer = vorbis_analysis_buffer( id->p_vd, i_samples ); + id->b_enc_inited = VLC_TRUE; + } - /* 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_dst.i_channels + i ] ))/ 32768.f; - } - } + aout_buf.p_buffer = id->p_buffer; + aout_buf.i_nb_bytes = id->i_buffer_pos; + aout_buf.i_nb_samples = id->i_buffer_pos / 2 / id->f_src.audio.i_channels; + aout_buf.start_date = id->i_dts; + aout_buf.end_date = id->i_dts; - vorbis_analysis_wrote( id->p_vd, i_samples ); + id->i_dts += ( I64C(1000000) * id->i_buffer_pos / 2 / + id->f_src.audio.i_channels / id->f_src.audio.i_rate ); - while( vorbis_analysis_blockout( id->p_vd, id->p_vb ) == 1 ) + if( id->f_src.audio.i_channels != + id->p_encoder->fmt_in.audio.i_channels ) + { + unsigned int i; + int j; + + /* This is for liba52 which is what ffmpeg uses to decode ac3 */ + static const int translation[7][6] = + {{ 0, 0, 0, 0, 0, 0 }, /* 0 channels (rarely used) */ + { 0, 0, 0, 0, 0, 0 }, /* 1 ch */ + { 0, 1, 0, 0, 0, 0 }, /* 2 */ + { 1, 2, 0, 0, 0, 0 }, /* 3 */ + { 1, 3, 2, 0, 0, 0 }, /* 4 */ + { 1, 3, 4, 2, 0, 0 }, /* 5 */ + { 1, 3, 4, 5, 2, 0 }}; /* 6 */ + + /* dumb downmixing */ + for( i = 0; i < aout_buf.i_nb_samples; i++ ) { - vorbis_analysis( id->p_vb, NULL ); - vorbis_bitrate_addblock( id->p_vb ); - - while( vorbis_bitrate_flushpacket( id->p_vd, &op ) ) + uint16_t *p_buffer = (uint16_t *)aout_buf.p_buffer; + for( j = 0 ; j < id->p_encoder->fmt_in.audio.i_channels; j++ ) { - 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 ); - + p_buffer[i*id->p_encoder->fmt_in.audio.i_channels+j] = + p_buffer[i*id->f_src.audio.i_channels+ + translation[id->f_src.audio.i_channels][j]]; } } + aout_buf.i_nb_bytes = i*id->p_encoder->fmt_in.audio.i_channels * 2; } - else -#endif - for( ;; ) + p_block = id->p_encoder->pf_encode_audio( id->p_encoder, &aout_buf ); + while( p_block ) { - int i_frame_size = id->ff_enc_c->frame_size * 2 * - id->ff_enc_c->channels; - int i_out_size; sout_buffer_t *p_out; + block_t *p_prev_block = p_block; - if( id->i_buffer_pos < i_frame_size ) - { - break; - } - - /* 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 ); - - 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; - - /* FIXME */ - p_out->i_dts = id->i_dts; - p_out->i_pts = id->i_dts; - - /* 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 ); */ - + 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; + p_out->i_length = p_block->i_length; sout_BufferChain( out, p_out ); - } - } while( b_again ); + p_block = p_block->p_next; + block_Release( p_prev_block ); + } + } return VLC_SUCCESS; } @@ -1024,22 +948,27 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream, int i_ff_codec; - if( id->f_src.i_fourcc == VLC_FOURCC( 'I', '4', '2', '0' ) || - 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' ) ) + /* Open decoder */ + if( id->f_src.i_codec == VLC_FOURCC( 'I', '4', '2', '0' ) || + id->f_src.i_codec == VLC_FOURCC( 'I', '4', '2', '2' ) || + id->f_src.i_codec == VLC_FOURCC( 'I', '4', '4', '4' ) || + id->f_src.i_codec == VLC_FOURCC( 'Y', 'U', 'Y', '2' ) || + id->f_src.i_codec == VLC_FOURCC( 'R', 'V', '1', '5' ) || + id->f_src.i_codec == VLC_FOURCC( 'R', 'V', '1', '6' ) || + id->f_src.i_codec == VLC_FOURCC( 'R', 'V', '2', '4' ) || + id->f_src.i_codec == VLC_FOURCC( 'R', 'V', '3', '2' ) || + id->f_src.i_codec == VLC_FOURCC( 'G', 'R', 'E', 'Y' ) ) { - id->ff_dec = NULL; + id->ff_dec = NULL; id->ff_dec_c = avcodec_alloc_context(); - id->ff_dec_c->width = id->f_src.i_width; - id->ff_dec_c->height = id->f_src.i_height; - id->ff_dec_c->pix_fmt = get_ff_chroma( id->f_src.i_fourcc ); + id->ff_dec_c->width = id->f_src.video.i_width; + id->ff_dec_c->height = id->f_src.video.i_height; + id->ff_dec_c->pix_fmt = get_ff_chroma( id->f_src.i_codec ); } else { /* find decoder */ - i_ff_codec = get_ff_codec( id->f_src.i_fourcc ); + i_ff_codec = get_ff_codec( id->f_src.i_codec ); if( i_ff_codec == 0 ) { msg_Err( p_stream, "cannot find decoder" ); @@ -1054,17 +983,15 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream, } id->ff_dec_c = avcodec_alloc_context(); - id->ff_dec_c->width = id->f_src.i_width; - id->ff_dec_c->height = id->f_src.i_height; + id->ff_dec_c->width = id->f_src.video.i_width; + id->ff_dec_c->height = id->f_src.video.i_height; /* id->ff_dec_c->bit_rate = id->f_src.i_bitrate; */ - id->ff_dec_c->extradata_size= id->f_src.i_extra_data; - id->ff_dec_c->extradata = id->f_src.p_extra_data; + id->ff_dec_c->extradata_size= id->f_src.i_extra; + id->ff_dec_c->extradata = id->f_src.p_extra; 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 ) { @@ -1076,89 +1003,123 @@ static int transcode_video_ffmpeg_new( sout_stream_t *p_stream, { 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 ); } } + /* Open encoder */ + id->p_encoder = vlc_object_create( p_stream, VLC_OBJECT_ENCODER ); - /* find encoder */ - i_ff_codec = get_ff_codec( id->f_dst.i_fourcc ); - if( i_ff_codec == 0 ) + /* Initialization of encoder format structures */ + es_format_Init( &id->p_encoder->fmt_in, + id->f_src.i_cat, get_vlc_chroma(id->ff_dec_c->pix_fmt) ); + + id->p_encoder->fmt_in.video.i_width = id->f_dst.video.i_width; + id->p_encoder->fmt_in.video.i_height = id->f_dst.video.i_height; + + if( id->p_encoder->fmt_in.video.i_width <= 0 ) { - msg_Err( p_stream, "cannot find encoder" ); - return VLC_EGENERIC; + id->p_encoder->fmt_in.video.i_width = id->f_dst.video.i_width = + id->ff_dec_c->width - p_sys->i_crop_left - p_sys->i_crop_right; } - - id->ff_enc = avcodec_find_encoder( i_ff_codec ); - if( !id->ff_enc ) + if( id->p_encoder->fmt_in.video.i_height <= 0 ) { - msg_Err( p_stream, "cannot find encoder" ); - return VLC_EGENERIC; + id->p_encoder->fmt_in.video.i_height = id->f_dst.video.i_height = + id->ff_dec_c->height - p_sys->i_crop_top - p_sys->i_crop_bottom; } - 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; + id->p_encoder->fmt_in.video.i_frame_rate = 25; /* FIXME as it break mpeg */ + id->p_encoder->fmt_in.video.i_frame_rate_base= 1; + if( id->ff_dec ) + { + id->p_encoder->fmt_in.video.i_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; -#else - id->ff_enc_c->frame_rate = 25 * FRAME_RATE_BASE; + id->p_encoder->fmt_in.video.i_frame_rate_base = + id->ff_dec_c->frame_rate_base; #endif - id->ff_enc_c->gop_size = p_sys->i_key_int >= 0 ? p_sys->i_key_int : 50; - if( p_sys->i_vtolerance >= 0 ) - { - id->ff_enc_c->bit_rate_tolerance = p_sys->i_vtolerance; - } - id->ff_enc_c->qmin = p_sys->i_qmin; - id->ff_enc_c->qmax = p_sys->i_qmax; - if( p_sys->b_hq ) - { - id->ff_enc_c->flags |= CODEC_FLAG_HQ; +#if LIBAVCODEC_BUILD >= 4687 + id->p_encoder->fmt_in.video.i_aspect = VOUT_ASPECT_FACTOR * + ( av_q2d(id->ff_dec_c->sample_aspect_ratio) * + id->ff_dec_c->width / id->ff_dec_c->height ); +#else + id->p_encoder->fmt_in.video.i_aspect = VOUT_ASPECT_FACTOR * + id->ff_dec_c->aspect_ratio; +#endif } - if( i_ff_codec == CODEC_ID_RAWVIDEO ) + /* Check whether a particular aspect ratio was requested */ + if( id->f_src.video.i_aspect ) { - id->ff_enc_c->pix_fmt = get_ff_chroma( id->f_dst.i_fourcc ); + id->p_encoder->fmt_in.video.i_aspect = id->f_src.video.i_aspect; + id->f_dst.video.i_aspect = id->f_src.video.i_aspect; } - /* 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->p_encoder->fmt_out = id->p_encoder->fmt_in; + id->p_encoder->fmt_out.i_codec = id->f_dst.i_codec; + id->p_encoder->fmt_out.i_bitrate = id->f_dst.i_bitrate; - id->i_buffer_out = 0; - id->i_buffer_out_pos = 0; - id->p_buffer_out = NULL; + id->p_encoder->i_vtolerance = p_sys->i_vtolerance; + id->p_encoder->i_key_int = p_sys->i_key_int; + id->p_encoder->i_b_frames = p_sys->i_b_frames; + id->p_encoder->i_qmin = p_sys->i_qmin; + id->p_encoder->i_qmax = p_sys->i_qmax; + id->p_encoder->i_hq = p_sys->i_hq; + id->p_encoder->b_strict_rc = p_sys->b_strict_rc; + id->p_encoder->b_pre_me = p_sys->b_pre_me; + id->p_encoder->b_hurry_up = p_sys->b_hurry_up; 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; + + id->p_encoder->p_module = + module_Need( id->p_encoder, "encoder", NULL ); + + if( !id->p_encoder->p_module ) + { + vlc_object_destroy( id->p_encoder ); + msg_Err( p_stream, "cannot find encoder" ); + return VLC_EGENERIC; + } + + /* Close the encoder. + * We'll open it only when we have the first frame */ + module_Unneed( id->p_encoder, id->p_encoder->p_module ); + + id->b_enc_inited = VLC_FALSE; + return VLC_SUCCESS; } -static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, sout_stream_id_t *id ) +static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, + sout_stream_id_t *id ) { + /* Close decoder */ if( id->ff_dec ) { avcodec_close( id->ff_dec_c ); - } - if( id->b_enc_inited ) - { - avcodec_close( id->ff_enc_c ); + free( id->ff_dec_c ); } + /* Close encoder */ + module_Unneed( id->p_encoder, id->p_encoder->p_module ); + vlc_object_destroy( id->p_encoder ); + + /* Misc cleanup */ if( id->p_ff_pic) { free( id->p_ff_pic ); @@ -1183,19 +1144,13 @@ static void transcode_video_ffmpeg_close ( sout_stream_t *p_stream, sout_stream_ { free( id->p_vresample ); } - - free( id->ff_dec_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; - int i_out; int b_gotpicture; AVFrame *frame; @@ -1206,11 +1161,16 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ i_data = in->i_size; p_data = in->p_buffer; - + for( ;; ) { + block_t *p_block; + picture_t pic; + int i_plane; + /* 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, @@ -1220,12 +1180,14 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ else { /* raw video */ - avpicture_fill( (AVPicture*)frame, - p_data, + avpicture_fill( (AVPicture*)frame, p_data, id->ff_dec_c->pix_fmt, 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 ) @@ -1241,38 +1203,82 @@ static int transcode_video_ffmpeg_process( sout_stream_t *p_stream, sout_stream_ return VLC_SUCCESS; } + /* 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 ) { - /* XXX hack because of copy packetizer and mpeg4video that can failed - detecting size */ - if( id->ff_enc_c->width <= 0 ) + /* XXX hack because of copy packetizer and mpeg4video that can fail + * detecting size */ + if( id->p_encoder->fmt_in.video.i_width <= 0 ) { - id->ff_enc_c->width = - id->f_dst.i_width = id->ff_dec_c->width - p_sys->i_crop_left - p_sys->i_crop_right; + id->p_encoder->fmt_in.video.i_width = + id->p_encoder->fmt_out.video.i_width = id->f_dst.video.i_width = + id->ff_dec_c->width - p_sys->i_crop_left - + p_sys->i_crop_right; } - if( id->ff_enc_c->height <= 0 ) + if( id->p_encoder->fmt_in.video.i_height <= 0 ) { - id->ff_enc_c->height = - id->f_dst.i_height = id->ff_dec_c->height - p_sys->i_crop_top - p_sys->i_crop_bottom; + id->p_encoder->fmt_in.video.i_height = + id->p_encoder->fmt_out.video.i_height = id->f_dst.video.i_height = + id->ff_dec_c->height - p_sys->i_crop_top - + p_sys->i_crop_bottom; } - if( avcodec_open( id->ff_enc_c, id->ff_enc ) ) + id->p_encoder->fmt_out.i_extra = 0; + id->p_encoder->fmt_out.p_extra = NULL; + + id->p_encoder->p_module = + module_Need( id->p_encoder, "encoder", NULL ); + if( !id->p_encoder->p_module ) { - msg_Err( p_stream, "cannot open encoder" ); + vlc_object_destroy( id->p_encoder ); + msg_Err( p_stream, "cannot find encoder" ); return VLC_EGENERIC; } - if( !( id->id = p_stream->p_sys->p_out->pf_add( p_stream->p_sys->p_out, &id->f_dst ) ) ) + id->f_dst.i_extra = id->p_encoder->fmt_out.i_extra; + id->f_dst.p_extra = id->p_encoder->fmt_out.p_extra; + + /* Hack for mp2v/mp1v transcoding support */ + if( id->f_dst.i_codec == VLC_FOURCC( 'm','p','1','v' ) || + id->f_dst.i_codec == VLC_FOURCC( 'm','p','2','v' ) ) + { + id->f_dst.i_codec = VLC_FOURCC( 'm','p','g','v' ); + } + + 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" ); transcode_video_ffmpeg_close( p_stream, id ); id->b_transcode = VLC_FALSE; return VLC_EGENERIC; } + + while( id->p_encoder->pf_header && + (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; + p_out->i_length = 0; + sout_BufferChain( out, p_out ); + } + + id->i_inter_pixfmt = + get_ff_chroma( id->p_encoder->fmt_in.i_codec ); + id->b_enc_inited = VLC_TRUE; } - /* deinterlace */ if( p_stream->p_sys->b_deinterlace ) { @@ -1287,7 +1293,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 ); } @@ -1299,33 +1305,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.video.i_width || + id->ff_dec_c->height != id->f_dst.video.i_height || p_sys->i_crop_top > 0 || p_sys->i_crop_bottom > 0 || p_sys->i_crop_left > 0 || p_sys->i_crop_right ) { @@ -1334,17 +1341,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.video.i_width, + id->f_dst.video.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.video.i_width, id->f_dst.video.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.video.i_width, + id->f_dst.video.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, @@ -1352,25 +1361,57 @@ 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 ); - if( i_out > 0 ) + /* Encoding */ + vout_InitPicture( VLC_OBJECT(p_stream), &pic, + id->p_encoder->fmt_in.i_codec, + id->f_dst.video.i_width, id->f_dst.video.i_height, + id->f_dst.video.i_width * VOUT_ASPECT_FACTOR / + id->f_dst.video.i_height ); + + for( i_plane = 0; i_plane < pic.i_planes; i_plane++ ) { - sout_buffer_t *p_out; - p_out = sout_BufferNew( p_stream->p_sout, i_out ); + pic.p[i_plane].p_pixels = frame->data[i_plane]; + pic.p[i_plane].i_pitch = frame->linesize[i_plane]; + } - memcpy( p_out->p_buffer, id->p_buffer, i_out ); + /* Set the pts of the frame being encoded */ + pic.date = p_sys->i_output_pts; - 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 */ + pic.b_progressive = 1; /* ffmpeg doesn't support interlaced encoding */ + pic.i_nb_fields = frame->repeat_pict; +#if LIBAVCODEC_BUILD >= 4684 + pic.b_top_field_first = frame->top_field_first; +#endif + /* 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); + } + + p_block = id->p_encoder->pf_encode_video( id->p_encoder, &pic ); + while( p_block ) + { + sout_buffer_t *p_out; + block_t *p_prev_block = p_block; + + 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; + p_out->i_length = p_block->i_length; sout_BufferChain( out, p_out ); + + p_block = p_block->p_next; + block_Release( p_prev_block ); } if( i_data <= 0 ) @@ -1381,3 +1422,20 @@ 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 ); +}