/***************************************************************************** * audio.c: transcoding stream output module (audio) ***************************************************************************** * Copyright (C) 2003-2009 VLC authors and VideoLAN * $Id$ * * Authors: Laurent Aimar * Gildas Bazin * Jean-Paul Saman * Antoine Cellerier * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include "transcode.h" #include #include #include #include static const int pi_channels_maps[9] = { 0, AOUT_CHAN_CENTER, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, AOUT_CHAN_LFE | 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, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE, AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE, }; static int audio_update_format( decoder_t *p_dec ) { sout_stream_id_sys_t *id = p_dec->p_queue_ctx; p_dec->fmt_out.audio.i_format = p_dec->fmt_out.i_codec; aout_FormatPrepare( &p_dec->fmt_out.audio ); vlc_mutex_lock(&id->fifo.lock); id->audio_dec_out = p_dec->fmt_out.audio; vlc_mutex_unlock(&id->fifo.lock); return ( p_dec->fmt_out.audio.i_bitspersample > 0 ) ? 0 : -1; } static int transcode_audio_initialize_filters( sout_stream_t *p_stream, sout_stream_id_sys_t *id, sout_stream_sys_t *p_sys ) { /* Load user specified audio filters */ /* XXX: These variable names come kinda out of nowhere... */ var_Create( p_stream, "audio-time-stretch", VLC_VAR_BOOL ); var_Create( p_stream, "audio-filter", VLC_VAR_STRING ); if( p_sys->psz_af ) var_SetString( p_stream, "audio-filter", p_sys->psz_af ); id->p_af_chain = aout_FiltersNew( p_stream, &id->audio_dec_out, &id->p_encoder->fmt_in.audio, NULL, NULL ); var_Destroy( p_stream, "audio-filter" ); var_Destroy( p_stream, "audio-time-stretch" ); if( id->p_af_chain == NULL ) { msg_Err( p_stream, "Unable to initialize audio filters" ); module_unneed( id->p_encoder, id->p_encoder->p_module ); id->p_encoder->p_module = NULL; module_unneed( id->p_decoder, id->p_decoder->p_module ); id->p_decoder->p_module = NULL; return VLC_EGENERIC; } id->fmt_audio.i_rate = id->audio_dec_out.i_rate; id->fmt_audio.i_physical_channels = id->audio_dec_out.i_physical_channels; return VLC_SUCCESS; } static int transcode_audio_initialize_encoder( sout_stream_id_sys_t *id, sout_stream_t *p_stream ) { sout_stream_sys_t *p_sys = p_stream->p_sys; /* Complete destination format */ id->p_encoder->fmt_out.i_codec = p_sys->i_acodec; id->p_encoder->fmt_out.audio.i_rate = p_sys->i_sample_rate > 0 ? p_sys->i_sample_rate : id->audio_dec_out.i_rate; id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate; id->p_encoder->fmt_out.audio.i_bitspersample = id->audio_dec_out.i_bitspersample; id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ? p_sys->i_channels : id->audio_dec_out.i_channels; assert(id->p_encoder->fmt_out.audio.i_channels > 0); if( id->p_encoder->fmt_out.audio.i_channels >= ARRAY_SIZE(pi_channels_maps) ) id->p_encoder->fmt_out.audio.i_channels = ARRAY_SIZE(pi_channels_maps) - 1; id->p_encoder->fmt_in.audio.i_physical_channels = id->p_encoder->fmt_out.audio.i_physical_channels = pi_channels_maps[id->p_encoder->fmt_out.audio.i_channels]; /* Initialization of encoder format structures */ es_format_Init( &id->p_encoder->fmt_in, id->p_decoder->fmt_in.i_cat, id->audio_dec_out.i_format ); id->p_encoder->fmt_in.audio.i_format = id->audio_dec_out.i_format; id->p_encoder->fmt_in.audio.i_rate = id->p_encoder->fmt_out.audio.i_rate; id->p_encoder->fmt_in.audio.i_physical_channels = id->p_encoder->fmt_out.audio.i_physical_channels; aout_FormatPrepare( &id->p_encoder->fmt_in.audio ); id->p_encoder->p_cfg = p_stream->p_sys->p_audio_cfg; id->p_encoder->p_module = module_need( id->p_encoder, "encoder", p_sys->psz_aenc, true ); /* p_sys->i_acodec = 0 if there isn't acodec defined */ if( !id->p_encoder->p_module && p_sys->i_acodec ) { msg_Err( p_stream, "cannot find audio encoder (module:%s fourcc:%4.4s). " "Take a look few lines earlier to see possible reason.", p_sys->psz_aenc ? p_sys->psz_aenc : "any", (char *)&p_sys->i_acodec ); module_unneed( id->p_decoder, id->p_decoder->p_module ); id->p_decoder->p_module = NULL; return VLC_EGENERIC; } id->p_encoder->fmt_out.i_codec = vlc_fourcc_GetCodec( AUDIO_ES, id->p_encoder->fmt_out.i_codec ); /* Fix input format */ id->p_encoder->fmt_in.audio.i_format = id->p_encoder->fmt_in.i_codec; if( !id->p_encoder->fmt_in.audio.i_physical_channels ) { if( id->p_encoder->fmt_in.audio.i_channels < (sizeof(pi_channels_maps) / sizeof(*pi_channels_maps)) ) id->p_encoder->fmt_in.audio.i_physical_channels = pi_channels_maps[id->p_encoder->fmt_in.audio.i_channels]; } aout_FormatPrepare( &id->p_encoder->fmt_in.audio ); return VLC_SUCCESS; } static int decoder_queue_audio( decoder_t *p_dec, block_t *p_audio ) { sout_stream_id_sys_t *id = p_dec->p_queue_ctx; vlc_mutex_lock(&id->fifo.lock); *id->fifo.audio.last = p_audio; id->fifo.audio.last = &p_audio->p_next; vlc_mutex_unlock(&id->fifo.lock); return 0; } static block_t *transcode_dequeue_all_audios( sout_stream_id_sys_t *id ) { vlc_mutex_lock(&id->fifo.lock); block_t *p_audio_bufs = id->fifo.audio.first; id->fifo.audio.first = NULL; id->fifo.audio.last = &id->fifo.audio.first; vlc_mutex_unlock(&id->fifo.lock); return p_audio_bufs; } static int transcode_audio_new( sout_stream_t *p_stream, sout_stream_id_sys_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; /* * Open decoder */ /* Initialization of decoder structures */ id->p_decoder->pf_decode = NULL; id->p_decoder->pf_queue_audio = decoder_queue_audio; id->p_decoder->p_queue_ctx = id; id->p_decoder->pf_aout_format_update = audio_update_format; /* id->p_decoder->p_cfg = p_sys->p_audio_cfg; */ id->p_decoder->p_module = module_need( id->p_decoder, "audio decoder", "$codec", false ); if( !id->p_decoder->p_module ) { msg_Err( p_stream, "cannot find audio decoder" ); return VLC_EGENERIC; } vlc_mutex_lock(&id->fifo.lock); /* The decoder fmt_out can be uninitialized here (since it can initialized * asynchronously). Fix audio_dec_out with default values in that case. * This should be enough to initialize the encoder for the first time (it * will be reloaded when all information from the decoder are available). * */ id->audio_dec_out = id->p_decoder->fmt_out.audio; id->audio_dec_out.i_format = id->p_decoder->fmt_out.i_codec; if (id->audio_dec_out.i_format == 0) id->audio_dec_out.i_format = VLC_CODEC_FL32; if (id->audio_dec_out.i_rate == 0) { id->audio_dec_out.i_rate = id->p_decoder->fmt_in.audio.i_rate; if (id->audio_dec_out.i_rate == 0) id->audio_dec_out.i_rate = 48000; } if (id->audio_dec_out.i_physical_channels == 0) { id->audio_dec_out.i_physical_channels = id->p_decoder->fmt_in.audio.i_physical_channels; if (id->audio_dec_out.i_physical_channels == 0) id->audio_dec_out.i_physical_channels = AOUT_CHANS_STEREO; } aout_FormatPrepare( &id->audio_dec_out ); /* * Open encoder */ if( transcode_audio_initialize_encoder( id, p_stream ) == VLC_EGENERIC ) { vlc_mutex_unlock(&id->fifo.lock); return VLC_EGENERIC; } if( unlikely( transcode_audio_initialize_filters( p_stream, id, p_sys ) != VLC_SUCCESS ) ) { vlc_mutex_unlock(&id->fifo.lock); return VLC_EGENERIC; } vlc_mutex_unlock(&id->fifo.lock); return VLC_SUCCESS; } void transcode_audio_close( sout_stream_id_sys_t *id ) { /* Close decoder */ if( id->p_decoder->p_module ) module_unneed( id->p_decoder, id->p_decoder->p_module ); id->p_decoder->p_module = NULL; if( id->p_decoder->p_description ) vlc_meta_Delete( id->p_decoder->p_description ); id->p_decoder->p_description = NULL; /* Close encoder */ if( id->p_encoder->p_module ) module_unneed( id->p_encoder, id->p_encoder->p_module ); id->p_encoder->p_module = NULL; /* Close filters */ if( id->p_af_chain != NULL ) aout_FiltersDelete( (vlc_object_t *)NULL, id->p_af_chain ); } int transcode_audio_process( sout_stream_t *p_stream, sout_stream_id_sys_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; *out = NULL; int ret = id->p_decoder->pf_decode( id->p_decoder, in ); if( ret != VLCDEC_SUCCESS ) return VLC_EGENERIC; block_t *p_audio_bufs = transcode_dequeue_all_audios( id ); if( p_audio_bufs == NULL ) goto end; do { block_t *p_audio_buf = p_audio_bufs; p_audio_bufs = p_audio_bufs->p_next; p_audio_buf->p_next = NULL; if( id->b_error ) { block_Release( p_audio_buf ); continue; } vlc_mutex_lock(&id->fifo.lock); if( unlikely( !id->p_encoder->p_module ) ) { if( transcode_audio_initialize_encoder( id, p_stream ) ) { msg_Err( p_stream, "cannot create audio chain" ); vlc_mutex_unlock(&id->fifo.lock); goto error; } if( unlikely( transcode_audio_initialize_filters( p_stream, id, p_sys ) != VLC_SUCCESS ) ) { vlc_mutex_unlock(&id->fifo.lock); goto error; } date_Init( &id->next_input_pts, id->audio_dec_out.i_rate, 1 ); date_Set( &id->next_input_pts, p_audio_buf->i_pts ); if (!id->id) { id->id = sout_StreamIdAdd( p_stream->p_next, &id->p_encoder->fmt_out ); if (!id->id) { vlc_mutex_unlock(&id->fifo.lock); goto error; } } } /* Check if audio format has changed, and filters need reinit */ if( unlikely( ( id->audio_dec_out.i_rate != id->fmt_audio.i_rate ) || ( id->audio_dec_out.i_physical_channels != id->fmt_audio.i_physical_channels ) ) ) { msg_Info( p_stream, "Audio changed, trying to reinitialize filters" ); if( id->p_af_chain != NULL ) aout_FiltersDelete( (vlc_object_t *)NULL, id->p_af_chain ); if( transcode_audio_initialize_filters( p_stream, id, p_sys ) != VLC_SUCCESS ) { vlc_mutex_unlock(&id->fifo.lock); goto error; } /* Set next_input_pts to run with new samplerate */ date_Init( &id->next_input_pts, id->fmt_audio.i_rate, 1 ); date_Set( &id->next_input_pts, p_audio_buf->i_pts ); } vlc_mutex_unlock(&id->fifo.lock); if( p_sys->b_master_sync ) { vlc_tick_t i_pts = date_Get( &id->next_input_pts ); vlc_tick_t i_drift = 0; if( likely( p_audio_buf->i_pts != VLC_TICK_INVALID ) ) i_drift = p_audio_buf->i_pts - i_pts; if ( unlikely(i_drift > MASTER_SYNC_MAX_DRIFT || i_drift < -MASTER_SYNC_MAX_DRIFT) ) { msg_Dbg( p_stream, "audio drift is too high (%"PRId64"), resetting master sync", i_drift ); date_Set( &id->next_input_pts, p_audio_buf->i_pts ); i_pts = date_Get( &id->next_input_pts ); if( likely(p_audio_buf->i_pts != VLC_TICK_INVALID ) ) i_drift = p_audio_buf->i_pts - i_pts; } p_sys->i_master_drift = i_drift; date_Increment( &id->next_input_pts, p_audio_buf->i_nb_samples ); } p_audio_buf->i_dts = p_audio_buf->i_pts; /* Run filter chain */ p_audio_buf = aout_FiltersPlay( id->p_af_chain, p_audio_buf, INPUT_RATE_DEFAULT ); if( !p_audio_buf ) goto error; p_audio_buf->i_dts = p_audio_buf->i_pts; block_t *p_block = id->p_encoder->pf_encode_audio( id->p_encoder, p_audio_buf ); block_ChainAppend( out, p_block ); block_Release( p_audio_buf ); continue; error: if( p_audio_buf ) block_Release( p_audio_buf ); id->b_error = true; } while( p_audio_bufs ); end: /* Drain encoder */ if( unlikely( !id->b_error && in == NULL ) ) { if( id->p_encoder->p_module ) { block_t *p_block; do { p_block = id->p_encoder->pf_encode_audio(id->p_encoder, NULL ); block_ChainAppend( out, p_block ); } while( p_block ); } } return id->b_error ? VLC_EGENERIC : VLC_SUCCESS; } bool transcode_audio_add( sout_stream_t *p_stream, const es_format_t *p_fmt, sout_stream_id_sys_t *id ) { sout_stream_sys_t *p_sys = p_stream->p_sys; msg_Dbg( p_stream, "creating audio transcoding from fcc=`%4.4s' to fcc=`%4.4s'", (char*)&p_fmt->i_codec, (char*)&p_sys->i_acodec ); id->fifo.audio.first = NULL; id->fifo.audio.last = &id->fifo.audio.first; /* Complete destination format */ id->p_encoder->fmt_out.i_codec = p_sys->i_acodec; id->p_encoder->fmt_out.audio.i_rate = p_sys->i_sample_rate > 0 ? p_sys->i_sample_rate : p_fmt->audio.i_rate; id->p_encoder->fmt_out.i_bitrate = p_sys->i_abitrate; id->p_encoder->fmt_out.audio.i_bitspersample = p_fmt->audio.i_bitspersample; id->p_encoder->fmt_out.audio.i_channels = p_sys->i_channels > 0 ? p_sys->i_channels : p_fmt->audio.i_channels; if( id->p_encoder->fmt_out.audio.i_channels >= ARRAY_SIZE(pi_channels_maps) ) id->p_encoder->fmt_out.audio.i_channels = ARRAY_SIZE(pi_channels_maps) - 1; id->p_encoder->fmt_in.audio.i_physical_channels = id->p_encoder->fmt_out.audio.i_physical_channels = pi_channels_maps[id->p_encoder->fmt_out.audio.i_channels]; /* Build decoder -> filter -> encoder chain */ if( transcode_audio_new( p_stream, id ) == VLC_EGENERIC ) { msg_Err( p_stream, "cannot create audio chain" ); return false; } /* Open output stream */ id->b_transcode = true; /* Reinit encoder again later on, when all information from decoders * is available. */ if( id->p_encoder->p_module ) { module_unneed( id->p_encoder, id->p_encoder->p_module ); id->p_encoder->p_module = NULL; if( id->p_encoder->fmt_out.p_extra ) { free( id->p_encoder->fmt_out.p_extra ); id->p_encoder->fmt_out.p_extra = NULL; id->p_encoder->fmt_out.i_extra = 0; } if( id->p_af_chain != NULL ) aout_FiltersDelete( (vlc_object_t *)NULL, id->p_af_chain ); id->p_af_chain = NULL; } return true; }