From 630b0fc7072379071fb53c12efdcc52360f0c4e5 Mon Sep 17 00:00:00 2001 From: Gildas Bazin Date: Mon, 27 Oct 2003 01:04:38 +0000 Subject: [PATCH] * modules/codec/ffmpeg/*: ported the ffmpeg audio and video decoders to the new api. Isolated the video postprocessing routines in postprocess.c * modules/codec/ffmpeg/encoder.c, modules/codec/vorbis.c, modules/stream_out/transcode.c, include/vlc_block.h, include/vlc_codec.h: extracted the encoders out of transcode.c. transcode now uses encoder plugins (currently ffmpeg, vorbis and theora). PS: transcoding is currently a bit broken but I'll fix that ASAP. --- configure.ac | 12 +- include/vlc_block.h | 3 +- include/vlc_codec.h | 16 +- modules/codec/ffmpeg/Modules.am | 4 +- modules/codec/ffmpeg/audio.c | 385 +++++----- modules/codec/ffmpeg/audio.h | 51 -- modules/codec/ffmpeg/chroma.c | 8 +- modules/codec/ffmpeg/encoder.c | 472 +++++++++++++ modules/codec/ffmpeg/ffmpeg.c | 1042 +++++++++++++--------------- modules/codec/ffmpeg/ffmpeg.h | 305 +++----- modules/codec/ffmpeg/postprocess.c | 202 ++++++ modules/codec/ffmpeg/video.c | 864 ++++++++++------------- modules/codec/ffmpeg/video.h | 56 -- modules/codec/vorbis.c | 255 ++++++- modules/stream_out/transcode.c | 762 +++++--------------- 15 files changed, 2260 insertions(+), 2177 deletions(-) delete mode 100644 modules/codec/ffmpeg/audio.h create mode 100644 modules/codec/ffmpeg/encoder.c create mode 100644 modules/codec/ffmpeg/postprocess.c delete mode 100644 modules/codec/ffmpeg/video.h diff --git a/configure.ac b/configure.ac index bed364fb5d..7a7e7797e5 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ dnl Autoconf settings for vlc -dnl $Id: configure.ac,v 1.98 2003/10/26 17:11:56 gbazin Exp $ +dnl $Id: configure.ac,v 1.99 2003/10/27 01:04:38 gbazin Exp $ AC_INIT(vlc,0.6.3-cvs) @@ -1706,13 +1706,6 @@ then AC_MSG_ERROR([cannot find ${real_ffmpeg_tree}/libavcodec/libavcodec.a, make sure you compiled libavcodec in ${with_ffmpeg_tree}]) fi fi - - ac_have_vorbis_headers=yes - AC_CHECK_HEADERS(vorbis/vorbisenc.h vorbis/codec.h,, - ac_have_vorbis_headers=no) - if test "$ac_have_vorbis_headers" = "yes"; then - AX_ADD_LDFLAGS([stream_out_transcode],[-lvorbisenc -lvorbis -logg]) - fi fi dnl @@ -2020,6 +2013,9 @@ then AC_CHECK_HEADERS(vorbis/codec.h, [ AX_ADD_PLUGINS([vorbis]) AX_ADD_LDFLAGS([vorbis],[-lvorbis -logg]) ],[]) + + AC_CHECK_HEADERS(vorbis/vorbisenc.h, [ + AX_ADD_LDFLAGS([vorbis],[-lvorbisenc]) ],[]) fi dnl diff --git a/include/vlc_block.h b/include/vlc_block.h index e4282d606f..779183147f 100644 --- a/include/vlc_block.h +++ b/include/vlc_block.h @@ -2,7 +2,7 @@ * vlc_block.h: Data blocks management functions ***************************************************************************** * Copyright (C) 2003 VideoLAN - * $Id: vlc_block.h,v 1.3 2003/09/30 20:23:03 gbazin Exp $ + * $Id: vlc_block.h,v 1.4 2003/10/27 01:04:38 gbazin Exp $ * * Authors: Laurent Aimar * @@ -37,6 +37,7 @@ struct block_t vlc_bool_t b_frame_start; mtime_t i_pts; mtime_t i_dts; + mtime_t i_length; vlc_bool_t b_discontinuity; /* only temporary */ diff --git a/include/vlc_codec.h b/include/vlc_codec.h index 5ab767cef3..466e1060ed 100644 --- a/include/vlc_codec.h +++ b/include/vlc_codec.h @@ -2,7 +2,7 @@ * vlc_codec.h: codec related structures ***************************************************************************** * Copyright (C) 1999-2003 VideoLAN - * $Id: vlc_codec.h,v 1.1 2003/10/08 21:01:07 gbazin Exp $ + * $Id: vlc_codec.h,v 1.2 2003/10/27 01:04:38 gbazin Exp $ * * Authors: Gildas Bazin * @@ -87,6 +87,20 @@ struct encoder_t /* Properties of the output of the encoder */ vlc_fourcc_t i_fourcc; int i_bitrate; + + int i_extra_data; + uint8_t *p_extra_data; + + /* FIXME: move these to the ffmpeg encoder */ + int i_frame_rate; + int i_frame_rate_base; + int i_key_int; + int i_b_frames; + int i_vtolerance; + int i_qmin; + int i_qmax; + int i_hq; + }; /** diff --git a/modules/codec/ffmpeg/Modules.am b/modules/codec/ffmpeg/Modules.am index 2dbb03acc0..1a674bd32e 100644 --- a/modules/codec/ffmpeg/Modules.am +++ b/modules/codec/ffmpeg/Modules.am @@ -2,9 +2,9 @@ SOURCES_ffmpeg = \ ffmpeg.c \ ffmpeg.h \ video.c \ - video.h \ audio.c \ - audio.h \ chroma.c \ + encoder.c \ + postprocess.c \ $(NULL) diff --git a/modules/codec/ffmpeg/audio.c b/modules/codec/ffmpeg/audio.c index 0bbbeac05c..36a43cc7ed 100644 --- a/modules/codec/ffmpeg/audio.c +++ b/modules/codec/ffmpeg/audio.c @@ -1,10 +1,11 @@ /***************************************************************************** * audio.c: audio decoder using ffmpeg library ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: audio.c,v 1.19 2003/07/10 01:33:41 fenrir Exp $ + * Copyright (C) 1999-2003 VideoLAN + * $Id: audio.c,v 1.20 2003/10/27 01:04:38 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 @@ -25,6 +26,7 @@ * Preamble *****************************************************************************/ #include /* malloc(), free() */ +#include #include #include @@ -36,12 +38,10 @@ #include /* getpid() */ #endif -#include -#include - #ifdef HAVE_SYS_TIMES_H # include #endif + #include "codecs.h" #include "aout_internal.h" @@ -52,304 +52,241 @@ # include #endif -//#include "postprocessing/postprocessing.h" #include "ffmpeg.h" #include "audio.h" -/* - * Local prototypes - */ -int E_( InitThread_Audio ) ( adec_thread_t * ); -void E_( EndThread_Audio ) ( adec_thread_t * ); -void E_( DecodeThread_Audio ) ( adec_thread_t * ); - static unsigned 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_REARLEFT + | AOUT_CHAN_REARRIGHT, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT }; /***************************************************************************** - * locales Functions - *****************************************************************************/ - -/***************************************************************************** - * - * Functions that initialize, decode and end the decoding process - * - * Functions exported for ffmpeg.c - * * E_( InitThread_Audio ) - * * E_( DecodeThread_Audio ) - * * E_( EndThread_Video_Audio ) + * decoder_sys_t : decoder descriptor *****************************************************************************/ +struct decoder_sys_t +{ + /* Common part between video and audio decoder */ + int i_cat; + int i_codec_id; + char *psz_namecodec; + AVCodecContext *p_context; + AVCodec *p_codec; + + /* Temporary buffer for libavcodec */ + uint8_t *p_output; + + /* + * Output properties + */ + aout_instance_t *p_aout; + aout_input_t *p_aout_input; + audio_sample_format_t aout_format; + audio_date_t end_date; +}; /***************************************************************************** - * InitThread: initialize vdec output thread + * InitAudioDec: initialize audio decoder ***************************************************************************** - * This function is called from decoder_Run and performs the second step - * of the initialization. It returns 0 on success. Note that the thread's - * flag are not modified inside this function. - * - * ffmpeg codec will be open, some memory allocated. + * The ffmpeg codec will be opened, some memory allocated. *****************************************************************************/ -int E_( InitThread_Audio )( adec_thread_t *p_adec ) +int E_(InitAudioDec)( decoder_t *p_dec, AVCodecContext *p_context, + AVCodec *p_codec, int i_codec_id, char *psz_namecodec ) { + decoder_sys_t *p_sys; WAVEFORMATEX wf, *p_wf; - if( ( p_wf = p_adec->p_fifo->p_waveformatex ) == NULL ) + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_dec->p_sys = p_sys = + (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL ) { - msg_Warn( p_adec->p_fifo, "audio informations missing" ); + msg_Err( p_dec, "out of memory" ); + return VLC_EGENERIC; + } + + p_dec->p_sys->p_context = p_context; + p_dec->p_sys->p_codec = p_codec; + p_dec->p_sys->i_codec_id = i_codec_id; + p_dec->p_sys->psz_namecodec = psz_namecodec; + + if( ( p_wf = p_dec->p_fifo->p_waveformatex ) == NULL ) + { + msg_Warn( p_dec, "audio informations missing" ); p_wf = &wf; memset( p_wf, 0, sizeof( WAVEFORMATEX ) ); } /* ***** Fill p_context with init values ***** */ - p_adec->p_context->sample_rate = p_wf->nSamplesPerSec; - p_adec->p_context->channels = p_wf->nChannels; - p_adec->p_context->block_align = p_wf->nBlockAlign; - p_adec->p_context->bit_rate = p_wf->nAvgBytesPerSec * 8; + p_sys->p_context->sample_rate = p_wf->nSamplesPerSec; + p_sys->p_context->channels = p_wf->nChannels; + p_sys->p_context->block_align = p_wf->nBlockAlign; + p_sys->p_context->bit_rate = p_wf->nAvgBytesPerSec * 8; - if( ( p_adec->p_context->extradata_size = p_wf->cbSize ) > 0 ) + if( ( p_sys->p_context->extradata_size = p_wf->cbSize ) > 0 ) { - p_adec->p_context->extradata = malloc( p_wf->cbSize + FF_INPUT_BUFFER_PADDING_SIZE ); - - memcpy( p_adec->p_context->extradata, &p_wf[1], p_wf->cbSize); - memset( &((uint8_t*)p_adec->p_context->extradata)[p_wf->cbSize], 0, FF_INPUT_BUFFER_PADDING_SIZE ); + p_sys->p_context->extradata = + malloc( p_wf->cbSize + FF_INPUT_BUFFER_PADDING_SIZE ); + memcpy( p_sys->p_context->extradata, &p_wf[1], p_wf->cbSize); + memset( &((uint8_t*)p_sys->p_context->extradata)[p_wf->cbSize], 0, + FF_INPUT_BUFFER_PADDING_SIZE ); } /* ***** Open the codec ***** */ - if (avcodec_open(p_adec->p_context, p_adec->p_codec) < 0) + if (avcodec_open( p_sys->p_context, p_sys->p_codec ) < 0) { - msg_Err( p_adec->p_fifo, - "cannot open codec (%s)", - p_adec->psz_namecodec ); - return( -1 ); + msg_Err( p_dec, "cannot open codec (%s)", p_sys->psz_namecodec ); + return VLC_EGENERIC; } else { - msg_Dbg( p_adec->p_fifo, - "ffmpeg codec (%s) started", - p_adec->psz_namecodec ); + msg_Dbg( p_dec, "ffmpeg codec (%s) started", p_sys->psz_namecodec ); } - p_adec->p_output = malloc( 3 * AVCODEC_MAX_AUDIO_FRAME_SIZE ); - + p_sys->p_output = malloc( 3 * AVCODEC_MAX_AUDIO_FRAME_SIZE ); - p_adec->output_format.i_format = AOUT_FMT_S16_NE; - p_adec->output_format.i_rate = p_wf->nSamplesPerSec; - p_adec->output_format.i_physical_channels - = p_adec->output_format.i_original_channels - = pi_channels_maps[p_wf->nChannels]; + p_sys->p_aout = NULL; + p_sys->p_aout_input = NULL; - p_adec->p_aout = NULL; - p_adec->p_aout_input = NULL; + aout_DateSet( &p_sys->end_date, 0 ); - return( 0 ); + return VLC_SUCCESS; } - /***************************************************************************** - * DecodeThread: Called for decode one frame + * DecodeAudio: Called to decode one frame *****************************************************************************/ -void E_( DecodeThread_Audio )( adec_thread_t *p_adec ) +int E_( DecodeAudio )( decoder_t *p_dec, block_t *p_block ) { - pes_packet_t *p_pes; - aout_buffer_t *p_aout_buffer; + decoder_sys_t *p_sys = p_dec->p_sys; + aout_buffer_t *p_aout_buffer; + mtime_t i_pts; - int i_samplesperchannel; - int i_output_size; - int i_frame_size; - int i_used; + uint8_t *p_buffer, *p_samples; + int i_buffer, i_samples; - uint8_t *p; + if( !aout_DateGet( &p_sys->end_date ) && !p_block->i_pts ) + { + /* We've just started the stream, wait for the first PTS. */ + block_Release( p_block ); + return VLC_SUCCESS; + } - do + i_pts = p_block->i_pts; + i_buffer = p_block->i_buffer; + p_buffer = p_block->p_buffer; + + while( i_buffer ) { - input_ExtractPES( p_adec->p_fifo, &p_pes ); - if( !p_pes ) + int i_used, i_output; + + i_used = avcodec_decode_audio( p_sys->p_context, + (int16_t*)p_sys->p_output, &i_output, + p_buffer, i_buffer ); + if( i_used < 0 ) { - p_adec->p_fifo->b_error = 1; - return; + msg_Warn( p_dec, "cannot decode one frame (%d bytes)", i_buffer ); + break; } - p_adec->pts = p_pes->i_pts; - i_frame_size = p_pes->i_pes_size; - if( i_frame_size > 0 ) - { - int i_need; + i_buffer -= i_used; + p_buffer += i_used; + if( p_sys->p_context->channels <= 0 || p_sys->p_context->channels > 6 ) + { + msg_Warn( p_dec, "invalid channels count %d", + p_sys->p_context->channels ); + break; + } - i_need = i_frame_size + FF_INPUT_BUFFER_PADDING_SIZE + p_adec->i_buffer; - if( p_adec->i_buffer_size < i_need ) + /* **** First check if we have a valid output **** */ + if( p_sys->p_aout_input == NULL || + p_sys->aout_format.i_original_channels != + pi_channels_maps[p_sys->p_context->channels] ) + { + if( p_sys->p_aout_input != NULL ) { - uint8_t *p_last = p_adec->p_buffer; - - p_adec->p_buffer = malloc( i_need ); - p_adec->i_buffer_size = i_need; - if( p_adec->i_buffer > 0 ) - { - memcpy( p_adec->p_buffer, p_last, p_adec->i_buffer ); - } - FREE( p_last ); + /* **** Delete the old **** */ + aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input ); } - i_frame_size = - E_( GetPESData )( p_adec->p_buffer + p_adec->i_buffer, - i_frame_size, - p_pes ); - /* make ffmpeg happier but I'm not sure it's needed for audio */ - memset( p_adec->p_buffer + p_adec->i_buffer + i_frame_size, 0, FF_INPUT_BUFFER_PADDING_SIZE ); - } - input_DeletePES( p_adec->p_fifo->p_packets_mgt, p_pes ); - } while( i_frame_size <= 0 ); - - i_frame_size += p_adec->i_buffer; + /* **** Create a new audio output **** */ + p_sys->aout_format.i_format = AOUT_FMT_S16_NE; + p_sys->aout_format.i_rate = p_sys->p_context->sample_rate; + p_sys->aout_format.i_physical_channels = + p_sys->aout_format.i_original_channels = + pi_channels_maps[p_sys->p_context->channels]; -usenextdata: - i_used = avcodec_decode_audio( p_adec->p_context, - (int16_t*)p_adec->p_output, - &i_output_size, - p_adec->p_buffer, - i_frame_size ); - if( i_used < 0 ) - { - msg_Warn( p_adec->p_fifo, - "cannot decode one frame (%d bytes)", - i_frame_size ); - p_adec->i_buffer = 0; - return; - } - else if( i_used < i_frame_size ) - { - memmove( p_adec->p_buffer, - p_adec->p_buffer + i_used, - p_adec->i_buffer_size - i_used ); - - p_adec->i_buffer = i_frame_size - i_used; - } - else - { - p_adec->i_buffer = 0; - } - - i_frame_size -= i_used; - - //msg_Dbg( p_adec->p_fifo, "frame size:%d buffer used:%d", i_frame_size, i_used ); - if( i_output_size <= 0 ) - { - msg_Warn( p_adec->p_fifo, - "decoded %d samples bytes", - i_output_size ); - } - - if( p_adec->p_context->channels <= 0 || - p_adec->p_context->channels > 5 ) - { - msg_Warn( p_adec->p_fifo, - "invalid channels count %d", - p_adec->p_context->channels ); - } + aout_DateInit( &p_sys->end_date, p_sys->aout_format.i_rate ); + p_sys->p_aout_input = aout_DecNew( p_dec, &p_sys->p_aout, + &p_sys->aout_format ); + } - /* **** First check if we have a valid output **** */ - if( ( p_adec->p_aout_input == NULL )|| - ( p_adec->output_format.i_original_channels != - pi_channels_maps[p_adec->p_context->channels] ) ) - { - if( p_adec->p_aout_input != NULL ) + if( !p_sys->p_aout_input ) { - /* **** Delete the old **** */ - aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input ); + msg_Err( p_dec, "cannot create audio output" ); + block_Release( p_block ); + return VLC_EGENERIC; } - /* **** Create a new audio output **** */ - p_adec->output_format.i_physical_channels = - p_adec->output_format.i_original_channels = - pi_channels_maps[p_adec->p_context->channels]; - - aout_DateInit( &p_adec->date, p_adec->output_format.i_rate ); - p_adec->p_aout_input = aout_DecNew( p_adec->p_fifo, - &p_adec->p_aout, - &p_adec->output_format ); - } - - if( !p_adec->p_aout_input ) - { - msg_Err( p_adec->p_fifo, "cannot create aout" ); - return; - } - - if( p_adec->pts != 0 && p_adec->pts != aout_DateGet( &p_adec->date ) ) - { - aout_DateSet( &p_adec->date, p_adec->pts ); - } - else if( !aout_DateGet( &p_adec->date ) ) - { - return; - } - - /* **** Now we can output these samples **** */ - i_samplesperchannel = i_output_size / 2 - / aout_FormatNbChannels( &p_adec->output_format ); - - p = &p_adec->p_output[0]; - while( i_samplesperchannel > 0 ) - { - int i_samples; + if( i_pts != 0 && i_pts != aout_DateGet( &p_sys->end_date ) ) + { + aout_DateSet( &p_sys->end_date, i_pts ); + i_pts = 0; + } - i_samples = __MIN( 8000, i_samplesperchannel ); + /* **** Now we can output these samples **** */ + i_samples = i_output / 2 / p_sys->p_context->channels; - p_aout_buffer = aout_DecNewBuffer( p_adec->p_aout, - p_adec->p_aout_input, - i_samples ); - if( !p_aout_buffer ) + p_samples = p_sys->p_output; + while( i_samples > 0 ) { - msg_Err( p_adec->p_fifo, "cannot get aout buffer" ); - p_adec->p_fifo->b_error = 1; - return; - } + int i_smaller_samples; - p_aout_buffer->start_date = aout_DateGet( &p_adec->date ); - p_aout_buffer->end_date = aout_DateIncrement( &p_adec->date, - i_samples ); - memcpy( p_aout_buffer->p_buffer, - p, - p_aout_buffer->i_nb_bytes ); + i_smaller_samples = __MIN( 8000, i_samples ); - aout_DecPlay( p_adec->p_aout, p_adec->p_aout_input, p_aout_buffer ); + p_aout_buffer = aout_DecNewBuffer( p_sys->p_aout, + p_sys->p_aout_input, + i_smaller_samples ); + if( !p_aout_buffer ) + { + msg_Err( p_dec, "cannot get aout buffer" ); + block_Release( p_block ); + return VLC_EGENERIC; + } - p += i_samples * 2 * aout_FormatNbChannels( &p_adec->output_format ); - i_samplesperchannel -= i_samples; + p_aout_buffer->start_date = aout_DateGet( &p_sys->end_date ); + p_aout_buffer->end_date = aout_DateIncrement( &p_sys->end_date, + i_smaller_samples ); + memcpy( p_aout_buffer->p_buffer, p_samples, + p_aout_buffer->i_nb_bytes ); - } + aout_DecPlay( p_sys->p_aout, p_sys->p_aout_input, p_aout_buffer ); - if( i_frame_size > 0 ) - { - goto usenextdata; + p_samples += i_smaller_samples * 2 * p_sys->p_context->channels; + i_samples -= i_smaller_samples; + } } - return; + block_Release( p_block ); + return VLC_SUCCESS; } - /***************************************************************************** - * EndThread: thread destruction - ***************************************************************************** - * This function is called when the thread ends after a sucessful - * initialization. + * EndAudioDec: audio decoder destruction *****************************************************************************/ -void E_( EndThread_Audio )( adec_thread_t *p_adec ) +void E_(EndAudioDec)( decoder_t *p_dec ) { -// FREE( p_adec->format.p_data ); - FREE( p_adec->p_output ); + decoder_sys_t *p_sys = p_dec->p_sys; + + if( p_sys->p_output ) free( p_sys->p_output ); - if( p_adec->p_aout_input ) + if( p_sys->p_aout_input ) { - aout_DecDelete( p_adec->p_aout, p_adec->p_aout_input ); + aout_DecDelete( p_sys->p_aout, p_sys->p_aout_input ); } - } - diff --git a/modules/codec/ffmpeg/audio.h b/modules/codec/ffmpeg/audio.h deleted file mode 100644 index 59258e0e0a..0000000000 --- a/modules/codec/ffmpeg/audio.h +++ /dev/null @@ -1,51 +0,0 @@ -/***************************************************************************** - * audio.h: video decoder using ffmpeg library - ***************************************************************************** - * Copyright (C) 1999-2001 VideoLAN - * $Id: audio.h,v 1.4 2003/02/07 01:22:55 fenrir Exp $ - * - * Authors: Laurent Aimar - * - * 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 - * (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. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. - *****************************************************************************/ - - -typedef struct adec_thread_s -{ - DECODER_THREAD_COMMON - -// waveformatex_t format; - - /* - * Output properties - */ - - uint8_t * p_output; - - aout_instance_t * p_aout; /* opaque */ - aout_input_t * p_aout_input; /* opaque */ - audio_sample_format_t output_format; - - audio_date_t date; - -} adec_thread_t; - -/* - * Local prototypes - */ -int E_( InitThread_Audio ) ( adec_thread_t * ); -void E_( EndThread_Audio ) ( adec_thread_t * ); -void E_( DecodeThread_Audio ) ( adec_thread_t * ); - diff --git a/modules/codec/ffmpeg/chroma.c b/modules/codec/ffmpeg/chroma.c index 227807214e..dac452e048 100644 --- a/modules/codec/ffmpeg/chroma.c +++ b/modules/codec/ffmpeg/chroma.c @@ -2,7 +2,7 @@ * chroma.c: chroma conversion using ffmpeg library ***************************************************************************** * Copyright (C) 1999-2001 VideoLAN - * $Id: chroma.c,v 1.2 2003/09/26 16:10:24 gbazin Exp $ + * $Id: chroma.c,v 1.3 2003/10/27 01:04:38 gbazin Exp $ * * Authors: Gildas Bazin * @@ -38,7 +38,7 @@ #include "ffmpeg.h" -void E_(ffmpeg_InitLibavcodec) ( vlc_object_t *p_object ); +void E_(InitLibavcodec) ( vlc_object_t *p_object ); static void ChromaConversion( vout_thread_t *, picture_t *, picture_t * ); /***************************************************************************** @@ -104,7 +104,7 @@ int E_(OpenChroma)( vlc_object_t *p_this ) case VLC_FOURCC('U','Y','V','Y'): i_ffmpeg_chroma[i] = PIX_FMT_YUV422; break; - + /* Packed RGB formats */ case VLC_FOURCC('R','V','3','2'): @@ -148,7 +148,7 @@ int E_(OpenChroma)( vlc_object_t *p_this ) p_vout->chroma.p_sys->i_dst_ffmpeg_chroma = i_ffmpeg_chroma[1]; /* libavcodec needs to be initialized for some chroma conversions */ - E_(ffmpeg_InitLibavcodec)(p_this); + E_(InitLibavcodec)(p_this); return VLC_SUCCESS; } diff --git a/modules/codec/ffmpeg/encoder.c b/modules/codec/ffmpeg/encoder.c new file mode 100644 index 0000000000..199cd04225 --- /dev/null +++ b/modules/codec/ffmpeg/encoder.c @@ -0,0 +1,472 @@ +/***************************************************************************** + * encoder.c: video and audio encoder using the ffmpeg library + ***************************************************************************** + * Copyright (C) 1999-2001 VideoLAN + * $Id: encoder.c,v 1.1 2003/10/27 01:04:38 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 + * the Free Software Foundation; either version 2 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. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ +#include /* malloc(), free() */ +#include + +#include +#include +#include +#include +#include +#include + +#include "aout_internal.h" + +/* ffmpeg header */ +#ifdef HAVE_FFMPEG_AVCODEC_H +# include +#else +# include +#endif + +#include "ffmpeg.h" + +#define AVCODEC_MAX_VIDEO_FRAME_SIZE (3*1024*1024) + +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +int E_(OpenVideoEncoder) ( vlc_object_t * ); +void E_(CloseVideoEncoder)( vlc_object_t * ); + +int E_(OpenAudioEncoder) ( vlc_object_t * ); +void E_(CloseAudioEncoder)( vlc_object_t * ); + +static block_t *EncodeVideo( encoder_t *, picture_t * ); +static block_t *EncodeAudio( encoder_t *, aout_buffer_t * ); + +/***************************************************************************** + * encoder_sys_t : ffmpeg encoder descriptor + *****************************************************************************/ +struct encoder_sys_t +{ + /* + * Ffmpeg properties + */ + AVCodec *p_codec; + AVCodecContext *p_context; + + /* + * Packetizer output properties + */ + sout_packetizer_input_t *p_sout_input; + sout_format_t sout_format; + + /* + * Common properties + */ + int i_last_block_size; + int i_samples_delay; + mtime_t i_pts; + + mtime_t i_last_ref_pts; + mtime_t i_buggy_pts_detect; + + int i_frame_size; + + char *p_buffer; + char *p_buffer_out; +}; + +/***************************************************************************** + * OpenVideoEncoder: probe the encoder + *****************************************************************************/ +int E_(OpenVideoEncoder)( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys = p_enc->p_sys; + AVCodecContext *p_context; + AVCodec *p_codec; + int i_ff_codec; + + /* find encoder */ + i_ff_codec = E_(GetFfmpegCodec)( p_enc->i_fourcc, 0, 0, 0 ); + if( !i_ff_codec ) + { + return VLC_EGENERIC; + } + + p_codec = avcodec_find_encoder( i_ff_codec ); + if( !p_codec ) + { + return VLC_EGENERIC; + } + + /* libavcodec needs to be initialized */ + E_(InitLibavcodec)(p_this); + + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) + { + msg_Err( p_enc, "out of memory" ); + return VLC_EGENERIC; + } + p_enc->p_sys = p_sys; + p_sys->p_codec = p_codec; + + p_enc->pf_header = NULL; + p_enc->pf_encode_video = EncodeVideo; + p_enc->format.video.i_chroma = VLC_FOURCC('I','4','2','0'); + + if( p_enc->i_fourcc == VLC_FOURCC( 'm','p','1','v' ) || + p_enc->i_fourcc == VLC_FOURCC( 'm','p','2','v' ) ) + { + p_enc->i_fourcc = VLC_FOURCC( 'm','p','g','v' ); + } + + p_sys->p_context = p_context = avcodec_alloc_context(); + p_context->width = p_enc->format.video.i_width; + p_context->height = p_enc->format.video.i_height; + p_context->bit_rate = p_enc->i_bitrate; + + p_context->frame_rate = p_enc->i_frame_rate; + p_context->frame_rate_base= p_enc->i_frame_rate_base; + + p_context->gop_size = p_enc->i_key_int >= 0 ? p_enc->i_key_int : 50; + p_context->max_b_frames = + __MIN( p_enc->i_b_frames, FF_MAX_B_FRAMES ); + p_context->b_frame_strategy = 0; + p_context->b_quant_factor = 2.0; + + if( p_enc->i_vtolerance >= 0 ) + { + p_context->bit_rate_tolerance = p_enc->i_vtolerance; + } + p_context->qmin = p_enc->i_qmin; + p_context->qmax = p_enc->i_qmax; + +#if LIBAVCODEC_BUILD >= 4673 + p_context->mb_decision = p_enc->i_hq; +#else + if( p_enc->i_hq ) + { + p_context->flags |= CODEC_FLAG_HQ; + } +#endif + + if( i_ff_codec == CODEC_ID_RAWVIDEO ) + { + p_context->pix_fmt = E_(GetFfmpegChroma)( p_enc->i_fourcc ); + } + + /* Make sure we get extradata filled by the encoder */ + p_context->extradata_size = 0; + p_context->extradata = NULL; + p_context->flags |= CODEC_FLAG_GLOBAL_HEADER; + + if( avcodec_open( p_context, p_sys->p_codec ) ) + { + msg_Err( p_enc, "cannot open encoder" ); + return VLC_EGENERIC; + } + + p_enc->i_extra_data = p_context->extradata_size; + p_enc->p_extra_data = p_context->extradata; + p_context->flags &= ~CODEC_FLAG_GLOBAL_HEADER; + + p_sys->p_buffer_out = malloc( AVCODEC_MAX_VIDEO_FRAME_SIZE ); + p_sys->i_last_ref_pts = 0; + p_sys->i_buggy_pts_detect = 0; + + return VLC_SUCCESS; +} + +/**************************************************************************** + * EncodeVideo: the whole thing + ****************************************************************************/ +static block_t *EncodeVideo( encoder_t *p_enc, picture_t *p_pict ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + AVFrame frame; + int i_out, i_plane; + + for( i_plane = 0; i_plane < p_pict->i_planes; i_plane++ ) + { + frame.data[i_plane] = p_pict->p[i_plane].p_pixels; + frame.linesize[i_plane] = p_pict->p[i_plane].i_pitch; + } + + frame.pts = p_pict->date; + + /* Let ffmpeg select the frame type */ + frame.pict_type = 0; + + i_out = avcodec_encode_video( p_sys->p_context, p_sys->p_buffer_out, + AVCODEC_MAX_VIDEO_FRAME_SIZE, &frame ); + if( i_out > 0 ) + { + block_t *p_block = block_New( p_enc, i_out ); + memcpy( p_block->p_buffer, p_sys->p_buffer_out, i_out ); + + if( p_sys->p_context->coded_frame->pts != 0 && + p_sys->i_buggy_pts_detect != p_sys->p_context->coded_frame->pts ) + { + p_sys->i_buggy_pts_detect = p_sys->p_context->coded_frame->pts; + + /* FIXME, 3-2 pulldown is not handled correctly */ + p_block->i_length = 0;//in->i_length; + p_block->i_pts = p_sys->p_context->coded_frame->pts; + + if( !p_sys->p_context->delay || + ( p_sys->p_context->coded_frame->pict_type != FF_I_TYPE && + p_sys->p_context->coded_frame->pict_type != FF_P_TYPE ) ) + { + p_block->i_dts = p_block->i_pts; + } + else + { + if( p_sys->i_last_ref_pts ) + { + p_block->i_dts = p_sys->i_last_ref_pts; + } + else + { + /* Let's put something sensible */ + p_block->i_dts = p_block->i_pts; + } + + p_sys->i_last_ref_pts = p_block->i_pts; + } + } + else + { + /* Buggy libavcodec which doesn't update coded_frame->pts + * correctly */ + p_block->i_length = 0;//in->i_length; + p_block->i_dts = p_block->i_pts = p_pict->date; + } + + return p_block; + } + + return NULL; +} + +/***************************************************************************** + * CloseVideoEncoder: ffmpeg video encoder destruction + *****************************************************************************/ +void E_(CloseVideoEncoder)( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys = p_enc->p_sys; + + avcodec_close( p_sys->p_context ); + free( p_sys->p_context ); + free( p_sys->p_buffer_out ); + free( p_sys ); +} + +/***************************************************************************** + * OpenAudioEncoder: probe the encoder + *****************************************************************************/ +int E_(OpenAudioEncoder)( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys = p_enc->p_sys; + AVCodecContext *p_context; + AVCodec *p_codec; + int i_ff_codec; + + i_ff_codec = E_(GetFfmpegCodec)( p_enc->i_fourcc, 0, 0, 0 ); + if( i_ff_codec == 0 ) + { + msg_Err( p_enc, "cannot find encoder id" ); + return VLC_EGENERIC; + } + + p_codec = avcodec_find_encoder( i_ff_codec ); + if( !p_codec ) + { + msg_Err( p_enc, "cannot find encoder (avcodec)" ); + return VLC_EGENERIC; + } + + /* libavcodec needs to be initialized */ + E_(InitLibavcodec)(p_this); + + /* Allocate the memory needed to store the decoder's structure */ + if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL ) + { + msg_Err( p_enc, "out of memory" ); + return VLC_EGENERIC; + } + p_enc->p_sys = p_sys; + p_sys->p_codec = p_codec; + + p_enc->pf_header = NULL; + p_enc->pf_encode_audio = EncodeAudio; + p_enc->format.audio.i_format = VLC_FOURCC('s','1','6','n'); + + p_sys->p_context = p_context = avcodec_alloc_context(); + p_context->bit_rate = p_enc->i_bitrate; + p_context->sample_rate = p_enc->format.audio.i_rate; + p_context->channels = + aout_FormatNbChannels( &p_enc->format.audio ); + + /* Make sure we get extradata filled by the encoder */ + p_context->extradata_size = 0; + p_context->extradata = NULL; + p_context->flags |= CODEC_FLAG_GLOBAL_HEADER; + + p_sys->i_samples_delay = 0; + p_sys->i_last_block_size = 0; + p_sys->i_pts = 0; + + if( avcodec_open( p_context, p_sys->p_codec ) ) + { +#if 0 + if( p_context->channels > 2 ) + { + p_context->channels = 2; + id->f_dst.i_channels = 2; + if( avcodec_open( id->ff_enc_c, id->ff_enc ) ) + { + msg_Err( p_enc, "cannot open encoder" ); + return VLC_EGENERIC; + } + msg_Warn( p_enc, "stereo mode selected (codec limitation)" ); + } + else + { + msg_Err( p_enc, "cannot open encoder" ); + return VLC_EGENERIC; + } +#endif + } + + p_enc->i_extra_data = p_context->extradata_size; + p_enc->p_extra_data = p_context->extradata; + p_context->flags &= ~CODEC_FLAG_GLOBAL_HEADER; + + p_sys->p_buffer = malloc( p_context->frame_size * 2 * + p_context->channels * 2 ); + + p_sys->p_buffer_out = malloc( 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE ); + + p_sys->i_frame_size = p_sys->p_context->frame_size * 2 * + p_context->channels; + + /* Hack for mp3 transcoding support */ + if( p_enc->i_fourcc == VLC_FOURCC( 'm','p','3',' ' ) ) + { + p_enc->i_fourcc = VLC_FOURCC( 'm','p','g','a' ); + } + + return VLC_SUCCESS; +} + +/**************************************************************************** + * EncodeAudio: the whole thing + ****************************************************************************/ +static block_t *EncodeAudio( encoder_t *p_enc, aout_buffer_t *p_aout_buf ) +{ + encoder_sys_t *p_sys = p_enc->p_sys; + block_t *p_block, *p_chain = NULL; + char *p_buffer = p_aout_buf->p_buffer; + int i_samples = p_aout_buf->i_nb_samples; + int i_samples_delay = p_sys->i_samples_delay; + + p_sys->i_pts = p_aout_buf->start_date - + (mtime_t)1000000 * (mtime_t)p_sys->i_samples_delay / + (mtime_t)p_enc->format.audio.i_rate; + + p_sys->i_samples_delay += i_samples; + + while( p_sys->i_samples_delay >= p_sys->p_context->frame_size ) + { + int16_t *p_samples; + int i_out; + + if( i_samples_delay ) + { + /* Take care of the left-over from last time */ + int i_delay_size = i_samples_delay * 2 * + p_sys->p_context->channels; + int i_size = p_sys->i_frame_size - i_delay_size; + + p_samples = (int16_t *)p_sys->p_buffer; + memcpy( p_sys->p_buffer + i_delay_size, p_buffer, i_size ); + p_buffer -= i_delay_size; + i_samples += i_samples_delay; + i_samples_delay = 0; + } + else + { + p_samples = (int16_t *)p_buffer; + } + + i_out = avcodec_encode_audio( p_sys->p_context, p_sys->p_buffer_out, + 2 * AVCODEC_MAX_AUDIO_FRAME_SIZE, + p_samples ); + if( i_out <= 0 ) + { + break; + } + + p_buffer += p_sys->i_frame_size; + p_sys->i_samples_delay -= p_sys->p_context->frame_size; + i_samples = p_sys->p_context->frame_size; + + p_block = block_New( p_enc, i_out ); + memcpy( p_block->p_buffer, p_sys->p_buffer_out, i_out ); + + p_block->i_length = (mtime_t)1000000 * + (mtime_t)p_sys->p_context->frame_size / + (mtime_t)p_sys->p_context->sample_rate; + + p_block->i_dts = p_block->i_pts = p_sys->i_pts; + + /* Update pts */ + p_sys->i_pts += p_block->i_length; + block_ChainAppend( &p_chain, p_block ); + } + + /* Backup the remaining raw samples */ + if( p_sys->i_samples_delay > 0 ) + { + memcpy( p_sys->p_buffer, p_buffer + i_samples_delay, + i_samples * 2 * p_sys->p_context->channels ); + } + + return p_chain; +} + +/***************************************************************************** + * CloseAudioEncoder: ffmpeg audio encoder destruction + *****************************************************************************/ +void E_(CloseAudioEncoder)( vlc_object_t *p_this ) +{ + encoder_t *p_enc = (encoder_t *)p_this; + encoder_sys_t *p_sys = p_enc->p_sys; + + avcodec_close( p_sys->p_context ); + free( p_sys->p_context ); + free( p_sys->p_buffer ); + free( p_sys->p_buffer_out ); + free( p_sys ); +} diff --git a/modules/codec/ffmpeg/ffmpeg.c b/modules/codec/ffmpeg/ffmpeg.c index db2347abf9..19a7a0ab56 100644 --- a/modules/codec/ffmpeg/ffmpeg.c +++ b/modules/codec/ffmpeg/ffmpeg.c @@ -2,9 +2,10 @@ * ffmpeg.c: video decoder using ffmpeg library ***************************************************************************** * Copyright (C) 1999-2001 VideoLAN - * $Id: ffmpeg.c,v 1.54 2003/10/25 00:49:13 sam Exp $ + * $Id: ffmpeg.c,v 1.55 2003/10/27 01:04:38 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 @@ -25,6 +26,7 @@ * Preamble *****************************************************************************/ #include /* malloc(), free() */ +#include #include #include @@ -32,8 +34,6 @@ #include #include -#include - #ifdef HAVE_SYS_TIMES_H # include #endif @@ -49,7 +49,6 @@ # error You must have a libavcodec >= 4655 (get CVS) #endif - #include "ffmpeg.h" #ifdef LIBAVCODEC_PP @@ -60,117 +59,54 @@ # endif #endif -#include "video.h" // video ffmpeg specific -#include "audio.h" // audio ffmpeg specific - -/* - * Local prototypes - */ -int E_(OpenChroma) ( vlc_object_t * ); -void E_(ffmpeg_InitLibavcodec) ( vlc_object_t *p_object ); - -static int OpenDecoder ( vlc_object_t * ); -static int RunDecoder ( decoder_fifo_t * ); +/***************************************************************************** + * decoder_sys_t: decoder descriptor + *****************************************************************************/ +struct decoder_sys_t +{ + /* Common part between video and audio decoder */ + int i_cat; + int i_codec_id; + char *psz_namecodec; -static int InitThread ( generic_thread_t * ); -static void EndThread ( generic_thread_t * ); + AVCodecContext *p_context; + AVCodec *p_codec; +}; -static int b_ffmpeginit = 0; +/**************************************************************************** + * Local prototypes + ****************************************************************************/ +static int OpenDecoder( vlc_object_t * ); +static int InitDecoder( decoder_t * ); +static int EndDecoder( decoder_t * ); -static int ffmpeg_GetFfmpegCodec( vlc_fourcc_t, int *, int *, char ** ); +static int b_ffmpeginit = 0; /***************************************************************************** * Module descriptor *****************************************************************************/ -#define DR_TEXT N_("Direct rendering") - -#define ERROR_TEXT N_("Error resilience") -#define ERROR_LONGTEXT N_( \ - "ffmpeg can make errors resiliences. \n" \ - "Nevertheless, with a buggy encoder (like ISO MPEG-4 encoder from M$) " \ - "this will produce a lot of errors.\n" \ - "Valid range is -1 to 99 (-1 disables all errors resiliences).") - -#define BUGS_TEXT N_("Workaround bugs") -#define BUGS_LONGTEXT N_( \ - "Try to fix some bugs\n" \ - "1 autodetect\n" \ - "2 old msmpeg4\n" \ - "4 xvid interlaced\n" \ - "8 ump4 \n" \ - "16 no padding\n" \ - "32 ac vlc\n" \ - "64 Qpel chroma") - -#define HURRYUP_TEXT N_("Hurry up") -#define HURRYUP_LONGTEXT N_( \ - "Allow the decoder to partially decode or skip frame(s) " \ - "when there is not enough time. It's useful with low CPU power " \ - "but it can produce distorted pictures.") - -#define TRUNC_TEXT N_("Truncated stream") -#define TRUNC_LONGTEXT N_("truncated stream -1:auto,0:disable,:1:enable") - -#define PP_Q_TEXT N_("Post processing quality") -#define PP_Q_LONGTEXT N_( \ - "Quality of post processing. Valid range is 0 to 6\n" \ - "Higher levels require considerable more CPU power, but produce " \ - "better looking pictures." ) - -#define LIBAVCODEC_PP_TEXT N_("Ffmpeg postproc filter chains") -/* FIXME (cut/past from ffmpeg */ -#define LIBAVCODEC_PP_LONGTEXT \ -"[: