/***************************************************************************** * aout_DecPlay : filter & mix the decoded buffer *****************************************************************************/ int aout_DecPlay (audio_output_t *aout, block_t *block, int input_rate) { aout_owner_t *owner = aout_owner (aout); assert (input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE); assert (input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE); assert (block->i_pts >= VLC_TS_0); block->i_length = CLOCK_FREQ * block->i_nb_samples / owner->input_format.i_rate; aout_OutputLock (aout); int ret = aout_CheckReady (aout); if (unlikely(ret == AOUT_DEC_FAILED)) goto drop; /* Pipeline is unrecoverably broken :-( */ const mtime_t now = mdate (), advance = block->i_pts - now; if (advance < -AOUT_MAX_PTS_DELAY) { /* Late buffer can be caused by bugs in the decoder, by scheduling * latency spikes (excessive load, SIGSTOP, etc.) or if buffering is * insufficient. We assume the PTS is wrong and play the buffer anyway: * Hopefully video has encountered a similar PTS problem as audio. */ msg_Warn (aout, "buffer too late (%"PRId64" us): dropped", advance); goto drop; } if (advance > AOUT_MAX_ADVANCE_TIME) { /* Early buffers can only be caused by bugs in the decoder. */ msg_Err (aout, "buffer too early (%"PRId64" us): dropped", advance); goto drop; } if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) owner->sync.discontinuity = true; block = aout_FiltersPlay (owner->filters, block, input_rate); if (block == NULL) goto lost; /* Software volume */ aout_volume_Amplify (owner->volume, block); /* Drift correction */ aout_DecSynchronize (aout, block->i_pts, input_rate); /* Output */ owner->sync.end = block->i_pts + block->i_length + 1; owner->sync.discontinuity = false; aout_OutputPlay (aout, block); atomic_fetch_add(&owner->buffers_played, 1); out: aout_OutputUnlock (aout); return ret; drop: owner->sync.discontinuity = true; block_Release (block); lost: atomic_fetch_add(&owner->buffers_lost, 1); goto out; }
/** * Plays a decoded audio buffer. */ void aout_OutputPlay (audio_output_t *aout, block_t *block) { aout_owner_t *owner = aout_owner (aout); aout_assert_locked (aout); aout_FiltersPlay (owner->filters, owner->nb_filters, &block); if (block == NULL) return; if (block->i_buffer == 0) { block_Release (block); return; } aout->pf_play (aout, block); }
/***************************************************************************** * aout_OutputPlay : play a buffer ***************************************************************************** * This function is entered with the mixer lock. *****************************************************************************/ void aout_OutputPlay( aout_instance_t * p_aout, aout_buffer_t * p_buffer ) { vlc_assert_locked( &p_aout->lock ); aout_FiltersPlay( p_aout->output.pp_filters, p_aout->output.i_nb_filters, &p_buffer ); if( !p_buffer ) return; if( p_buffer->i_buffer == 0 ) { block_Release( p_buffer ); return; } aout_FifoPush( &p_aout->output.fifo, p_buffer ); p_aout->output.pf_play( p_aout ); }
/***************************************************************************** * aout_OutputPlay : play a buffer ***************************************************************************** * This function is entered with the mixer lock. *****************************************************************************/ void aout_OutputPlay( aout_instance_t * p_aout, aout_buffer_t * p_buffer ) { aout_FiltersPlay( p_aout, p_aout->output.pp_filters, p_aout->output.i_nb_filters, &p_buffer ); if( p_buffer->i_nb_bytes == 0 ) { aout_BufferFree( p_buffer ); return; } aout_lock_output_fifo( p_aout ); aout_FifoPush( p_aout, &p_aout->output.fifo, p_buffer ); p_aout->output.pf_play( p_aout ); aout_unlock_output_fifo( p_aout ); }
int transcode_audio_process( sout_stream_t *p_stream, sout_stream_id_t *id, block_t *in, block_t **out ) { sout_stream_sys_t *p_sys = p_stream->p_sys; block_t *p_block, *p_audio_buf; *out = NULL; if( unlikely( in == NULL ) ) { 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 VLC_SUCCESS; } while( (p_audio_buf = id->p_decoder->pf_decode_audio( id->p_decoder, &in )) ) { if( p_sys->b_master_sync ) { mtime_t i_pts = date_Get( &id->interpolated_pts ) + 1; mtime_t i_drift = 0; if( likely( p_audio_buf->i_pts != VLC_TS_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->interpolated_pts, p_audio_buf->i_pts ); i_pts = p_audio_buf->i_pts + 1; } if( likely(p_audio_buf->i_pts != VLC_TS_INVALID ) ) p_sys->i_master_drift = p_audio_buf->i_pts - i_pts; date_Increment( &id->interpolated_pts, p_audio_buf->i_nb_samples ); p_audio_buf->i_pts = i_pts; } p_audio_buf->i_dts = p_audio_buf->i_pts; /* Check if audio format has changed, and filters need reinit */ if( unlikely( ( id->p_decoder->fmt_out.audio.i_rate != p_sys->fmt_audio.i_rate ) || ( id->p_decoder->fmt_out.audio.i_physical_channels != p_sys->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 ); /* decoders don't set audio.i_format, but audio filters use it */ id->p_decoder->fmt_out.audio.i_format = id->p_decoder->fmt_out.i_codec; aout_FormatPrepare( &id->p_decoder->fmt_out.audio ); if( transcode_audio_initialize_filters( p_stream, id, p_sys, &id->p_decoder->fmt_out.audio ) != VLC_SUCCESS ) return VLC_EGENERIC; } /* Run filter chain */ p_audio_buf = aout_FiltersPlay( id->p_af_chain, p_audio_buf, INPUT_RATE_DEFAULT ); if( !p_audio_buf ) abort(); p_audio_buf->i_dts = p_audio_buf->i_pts; p_block = id->p_encoder->pf_encode_audio( id->p_encoder, p_audio_buf ); block_ChainAppend( out, p_block ); block_Release( p_audio_buf ); } return VLC_SUCCESS; }
//#define AOUT_PROCESS_BEFORE_CHEKS int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input, aout_buffer_t * p_buffer, int i_input_rate ) { mtime_t start_date; AOUT_ASSERT_INPUT_LOCKED; if( i_input_rate != INPUT_RATE_DEFAULT && p_input->p_playback_rate_filter == NULL ) { inputDrop( p_input, p_buffer ); return 0; } #ifdef AOUT_PROCESS_BEFORE_CHEKS /* Run pre-filters. */ aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters, &p_buffer ); if( !p_buffer ) return 0; /* Actually run the resampler now. */ if ( p_input->i_nb_resamplers > 0 ) { const mtime_t i_date = p_buffer->i_pts; aout_FiltersPlay( p_aout, p_input->pp_resamplers, p_input->i_nb_resamplers, &p_buffer ); } if( !p_buffer ) return 0; if( p_buffer->i_nb_samples <= 0 ) { block_Release( p_buffer ); return 0; } #endif /* Handle input rate change, but keep drift correction */ if( i_input_rate != p_input->i_last_input_rate ) { unsigned int * const pi_rate = &p_input->p_playback_rate_filter->fmt_in.audio.i_rate; #define F(r,ir) ( INPUT_RATE_DEFAULT * (r) / (ir) ) const int i_delta = *pi_rate - F(p_input->input.i_rate,p_input->i_last_input_rate); *pi_rate = F(p_input->input.i_rate + i_delta, i_input_rate); #undef F p_input->i_last_input_rate = i_input_rate; } /* We don't care if someone changes the start date behind our back after * this. We'll deal with that when pushing the buffer, and compensate * with the next incoming buffer. */ aout_lock_input_fifos( p_aout ); start_date = aout_FifoNextStart( p_aout, &p_input->mixer.fifo ); aout_unlock_input_fifos( p_aout ); if ( start_date != 0 && start_date < mdate() ) { /* The decoder is _very_ late. This can only happen if the user * pauses the stream (or if the decoder is buggy, which cannot * happen :). */ msg_Warn( p_aout, "computed PTS is out of range (%"PRId64"), " "clearing out", mdate() - start_date ); aout_lock_input_fifos( p_aout ); aout_FifoSet( p_aout, &p_input->mixer.fifo, 0 ); p_input->mixer.begin = NULL; aout_unlock_input_fifos( p_aout ); if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) msg_Warn( p_aout, "timing screwed, stopping resampling" ); inputResamplingStop( p_input ); p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY; start_date = 0; } if ( p_buffer->i_pts < mdate() + AOUT_MIN_PREPARE_TIME ) { /* The decoder gives us f*cked up PTS. It's its business, but we * can't present it anyway, so drop the buffer. */ msg_Warn( p_aout, "PTS is out of range (%"PRId64"), dropping buffer", mdate() - p_buffer->i_pts ); inputDrop( p_input, p_buffer ); inputResamplingStop( p_input ); return 0; } /* If the audio drift is too big then it's not worth trying to resample * the audio. */ mtime_t i_pts_tolerance = 3 * AOUT_PTS_TOLERANCE * i_input_rate / INPUT_RATE_DEFAULT; if ( start_date != 0 && ( start_date < p_buffer->i_pts - i_pts_tolerance ) ) { msg_Warn( p_aout, "audio drift is too big (%"PRId64"), clearing out", start_date - p_buffer->i_pts ); aout_lock_input_fifos( p_aout ); aout_FifoSet( p_aout, &p_input->mixer.fifo, 0 ); p_input->mixer.begin = NULL; aout_unlock_input_fifos( p_aout ); if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) msg_Warn( p_aout, "timing screwed, stopping resampling" ); inputResamplingStop( p_input ); p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY; start_date = 0; } else if ( start_date != 0 && ( start_date > p_buffer->i_pts + i_pts_tolerance) ) { msg_Warn( p_aout, "audio drift is too big (%"PRId64"), dropping buffer", start_date - p_buffer->i_pts ); inputDrop( p_input, p_buffer ); return 0; } if ( start_date == 0 ) start_date = p_buffer->i_pts; #ifndef AOUT_PROCESS_BEFORE_CHEKS /* Run pre-filters. */ aout_FiltersPlay( p_input->pp_filters, p_input->i_nb_filters, &p_buffer ); if( !p_buffer ) return 0; #endif /* Run the resampler if needed. * We first need to calculate the output rate of this resampler. */ if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) && ( start_date < p_buffer->i_pts - AOUT_PTS_TOLERANCE || start_date > p_buffer->i_pts + AOUT_PTS_TOLERANCE ) && p_input->i_nb_resamplers > 0 ) { /* Can happen in several circumstances : * 1. A problem at the input (clock drift) * 2. A small pause triggered by the user * 3. Some delay in the output stage, causing a loss of lip * synchronization * Solution : resample the buffer to avoid a scratch. */ mtime_t drift = p_buffer->i_pts - start_date; p_input->i_resamp_start_date = mdate(); p_input->i_resamp_start_drift = (int)drift; if ( drift > 0 ) p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; else p_input->i_resampling_type = AOUT_RESAMPLING_UP; msg_Warn( p_aout, "buffer is %"PRId64" %s, triggering %ssampling", drift > 0 ? drift : -drift, drift > 0 ? "in advance" : "late", drift > 0 ? "down" : "up"); } if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) { /* Resampling has been triggered previously (because of dates * mismatch). We want the resampling to happen progressively so * it isn't too audible to the listener. */ if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) { p_input->pp_resamplers[0]->fmt_in.audio.i_rate += 2; /* Hz */ } else { p_input->pp_resamplers[0]->fmt_in.audio.i_rate -= 2; /* Hz */ } /* Check if everything is back to normal, in which case we can stop the * resampling */ unsigned int i_nominal_rate = (p_input->pp_resamplers[0] == p_input->p_playback_rate_filter) ? INPUT_RATE_DEFAULT * p_input->input.i_rate / i_input_rate : p_input->input.i_rate; if( p_input->pp_resamplers[0]->fmt_in.audio.i_rate == i_nominal_rate ) { p_input->i_resampling_type = AOUT_RESAMPLING_NONE; msg_Warn( p_aout, "resampling stopped after %"PRIi64" usec " "(drift: %"PRIi64")", mdate() - p_input->i_resamp_start_date, p_buffer->i_pts - start_date); } else if( abs( (int)(p_buffer->i_pts - start_date) ) < abs( p_input->i_resamp_start_drift ) / 2 ) { /* if we reduced the drift from half, then it is time to switch * back the resampling direction. */ if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; else p_input->i_resampling_type = AOUT_RESAMPLING_UP; p_input->i_resamp_start_drift = 0; } else if( p_input->i_resamp_start_drift && ( abs( (int)(p_buffer->i_pts - start_date) ) > abs( p_input->i_resamp_start_drift ) * 3 / 2 ) ) { /* If the drift is increasing and not decreasing, than something * is bad. We'd better stop the resampling right now. */ msg_Warn( p_aout, "timing screwed, stopping resampling" ); inputResamplingStop( p_input ); p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY; } } #ifndef AOUT_PROCESS_BEFORE_CHEKS /* Actually run the resampler now. */ if ( p_input->i_nb_resamplers > 0 ) { aout_FiltersPlay( p_input->pp_resamplers, p_input->i_nb_resamplers, &p_buffer ); } if( !p_buffer ) return 0; if( p_buffer->i_nb_samples <= 0 ) { block_Release( p_buffer ); return 0; } #endif /* Adding the start date will be managed by aout_FifoPush(). */ p_buffer->i_pts = start_date; aout_lock_input_fifos( p_aout ); aout_FifoPush( p_aout, &p_input->mixer.fifo, p_buffer ); aout_unlock_input_fifos( p_aout ); return 0; }
/***************************************************************************** * aout_InputPlay : play a buffer ***************************************************************************** * This function must be entered with the input lock. *****************************************************************************/ int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input, aout_buffer_t * p_buffer ) { mtime_t start_date; if( p_input->b_restart ) { aout_fifo_t fifo, dummy_fifo; byte_t *p_first_byte_to_mix; vlc_mutex_lock( &p_aout->mixer_lock ); /* A little trick to avoid loosing our input fifo */ aout_FifoInit( p_aout, &dummy_fifo, p_aout->mixer.mixer.i_rate ); p_first_byte_to_mix = p_input->p_first_byte_to_mix; fifo = p_input->fifo; p_input->fifo = dummy_fifo; aout_InputDelete( p_aout, p_input ); aout_InputNew( p_aout, p_input ); p_input->p_first_byte_to_mix = p_first_byte_to_mix; p_input->fifo = fifo; vlc_mutex_unlock( &p_aout->mixer_lock ); } /* We don't care if someone changes the start date behind our back after * this. We'll deal with that when pushing the buffer, and compensate * with the next incoming buffer. */ vlc_mutex_lock( &p_aout->input_fifos_lock ); start_date = aout_FifoNextStart( p_aout, &p_input->fifo ); vlc_mutex_unlock( &p_aout->input_fifos_lock ); if ( start_date != 0 && start_date < mdate() ) { /* The decoder is _very_ late. This can only happen if the user * pauses the stream (or if the decoder is buggy, which cannot * happen :). */ msg_Warn( p_aout, "computed PTS is out of range ("I64Fd"), " "clearing out", mdate() - start_date ); vlc_mutex_lock( &p_aout->input_fifos_lock ); aout_FifoSet( p_aout, &p_input->fifo, 0 ); p_input->p_first_byte_to_mix = NULL; vlc_mutex_unlock( &p_aout->input_fifos_lock ); if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) msg_Warn( p_aout, "timing screwed, stopping resampling" ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if ( p_input->i_nb_resamplers != 0 ) { p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; p_input->pp_resamplers[0]->b_continuity = VLC_FALSE; } start_date = 0; if( p_input->p_input_thread ) { stats_UpdateInteger( p_input->p_input_thread, STATS_LOST_ABUFFERS, 1, NULL ); } } if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME ) { /* The decoder gives us f*cked up PTS. It's its business, but we * can't present it anyway, so drop the buffer. */ msg_Warn( p_aout, "PTS is out of range ("I64Fd"), dropping buffer", mdate() - p_buffer->start_date ); if( p_input->p_input_thread ) { stats_UpdateInteger( p_input->p_input_thread, STATS_LOST_ABUFFERS, 1, NULL ); } aout_BufferFree( p_buffer ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if ( p_input->i_nb_resamplers != 0 ) { p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; p_input->pp_resamplers[0]->b_continuity = VLC_FALSE; } return 0; } /* If the audio drift is too big then it's not worth trying to resample * the audio. */ if ( start_date != 0 && ( start_date < p_buffer->start_date - 3 * AOUT_PTS_TOLERANCE ) ) { msg_Warn( p_aout, "audio drift is too big ("I64Fd"), clearing out", start_date - p_buffer->start_date ); vlc_mutex_lock( &p_aout->input_fifos_lock ); aout_FifoSet( p_aout, &p_input->fifo, 0 ); p_input->p_first_byte_to_mix = NULL; vlc_mutex_unlock( &p_aout->input_fifos_lock ); if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) msg_Warn( p_aout, "timing screwed, stopping resampling" ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if ( p_input->i_nb_resamplers != 0 ) { p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; p_input->pp_resamplers[0]->b_continuity = VLC_FALSE; } start_date = 0; } else if ( start_date != 0 && ( start_date > p_buffer->start_date + 3 * AOUT_PTS_TOLERANCE ) ) { msg_Warn( p_aout, "audio drift is too big ("I64Fd"), dropping buffer", start_date - p_buffer->start_date ); aout_BufferFree( p_buffer ); if( p_input->p_input_thread ) { stats_UpdateInteger( p_input->p_input_thread, STATS_LOST_ABUFFERS, 1, NULL ); } return 0; } if ( start_date == 0 ) start_date = p_buffer->start_date; /* Run pre-filters. */ aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters, &p_buffer ); /* Run the resampler if needed. * We first need to calculate the output rate of this resampler. */ if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) && ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE ) && p_input->i_nb_resamplers > 0 ) { /* Can happen in several circumstances : * 1. A problem at the input (clock drift) * 2. A small pause triggered by the user * 3. Some delay in the output stage, causing a loss of lip * synchronization * Solution : resample the buffer to avoid a scratch. */ mtime_t drift = p_buffer->start_date - start_date; p_input->i_resamp_start_date = mdate(); p_input->i_resamp_start_drift = (int)drift; if ( drift > 0 ) p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; else p_input->i_resampling_type = AOUT_RESAMPLING_UP; msg_Warn( p_aout, "buffer is "I64Fd" %s, triggering %ssampling", drift > 0 ? drift : -drift, drift > 0 ? "in advance" : "late", drift > 0 ? "down" : "up"); } if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) { /* Resampling has been triggered previously (because of dates * mismatch). We want the resampling to happen progressively so * it isn't too audible to the listener. */ if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) { p_input->pp_resamplers[0]->input.i_rate += 2; /* Hz */ } else { p_input->pp_resamplers[0]->input.i_rate -= 2; /* Hz */ } /* Check if everything is back to normal, in which case we can stop the * resampling */ if( p_input->pp_resamplers[0]->input.i_rate == p_input->input.i_rate ) { p_input->i_resampling_type = AOUT_RESAMPLING_NONE; msg_Warn( p_aout, "resampling stopped after "I64Fi" usec " "(drift: "I64Fi")", mdate() - p_input->i_resamp_start_date, p_buffer->start_date - start_date); } else if( abs( (int)(p_buffer->start_date - start_date) ) < abs( p_input->i_resamp_start_drift ) / 2 ) { /* if we reduced the drift from half, then it is time to switch * back the resampling direction. */ if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; else p_input->i_resampling_type = AOUT_RESAMPLING_UP; p_input->i_resamp_start_drift = 0; } else if( p_input->i_resamp_start_drift && ( abs( (int)(p_buffer->start_date - start_date) ) > abs( p_input->i_resamp_start_drift ) * 3 / 2 ) ) { /* If the drift is increasing and not decreasing, than something * is bad. We'd better stop the resampling right now. */ msg_Warn( p_aout, "timing screwed, stopping resampling" ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; } } /* Adding the start date will be managed by aout_FifoPush(). */ p_buffer->end_date = start_date + (p_buffer->end_date - p_buffer->start_date); p_buffer->start_date = start_date; /* Actually run the resampler now. */ if ( p_input->i_nb_resamplers > 0 ) { aout_FiltersPlay( p_aout, p_input->pp_resamplers, p_input->i_nb_resamplers, &p_buffer ); } vlc_mutex_lock( &p_aout->input_fifos_lock ); aout_FifoPush( p_aout, &p_input->fifo, p_buffer ); vlc_mutex_unlock( &p_aout->input_fifos_lock ); return 0; }