/***************************************************************************** * aout_OutputNextBuffer : give the audio output plug-in the right buffer ***************************************************************************** * If b_can_sleek is 1, the aout core functions won't try to resample * new buffers to catch up - that is we suppose that the output plug-in can * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1. * This function is entered with no lock at all :-). *****************************************************************************/ aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout, mtime_t start_date, bool b_can_sleek ) { aout_buffer_t * p_buffer; aout_lock_output_fifo( p_aout ); p_buffer = p_aout->output.fifo.p_first; /* Drop the audio sample if the audio output is really late. * In the case of b_can_sleek, we don't use a resampler so we need to be * a lot more severe. */ while ( p_buffer && p_buffer->i_pts < (b_can_sleek ? start_date : mdate()) - AOUT_PTS_TOLERANCE ) { msg_Dbg( p_aout, "audio output is too slow (%"PRId64"), " "trashing %"PRId64"us", mdate() - p_buffer->i_pts, p_buffer->i_length ); p_buffer = p_buffer->p_next; aout_BufferFree( p_aout->output.fifo.p_first ); p_aout->output.fifo.p_first = p_buffer; } if ( p_buffer == NULL ) { p_aout->output.fifo.pp_last = &p_aout->output.fifo.p_first; #if 0 /* This is bad because the audio output might just be trying to fill * in its internal buffers. And anyway, it's up to the audio output * to deal with this kind of starvation. */ /* Set date to 0, to allow the mixer to send a new buffer ASAP */ aout_FifoSet( p_aout, &p_aout->output.fifo, 0 ); if ( !p_aout->output.b_starving ) msg_Dbg( p_aout, "audio output is starving (no input), playing silence" ); p_aout->output.b_starving = 1; #endif aout_unlock_output_fifo( p_aout ); return NULL; }
/***************************************************************************** * aout_OutputNextBuffer : give the audio output plug-in the right buffer ***************************************************************************** * If b_can_sleek is 1, the aout core functions won't try to resample * new buffers to catch up - that is we suppose that the output plug-in can * compensate it by itself. S/PDIF outputs should always set b_can_sleek = 1. * This function is entered with no lock at all :-). *****************************************************************************/ aout_buffer_t * aout_OutputNextBuffer( aout_instance_t * p_aout, mtime_t start_date, bool b_can_sleek ) { aout_fifo_t *p_fifo = &p_aout->output.fifo; aout_buffer_t * p_buffer; mtime_t now = mdate(); aout_lock( p_aout ); /* Drop the audio sample if the audio output is really late. * In the case of b_can_sleek, we don't use a resampler so we need to be * a lot more severe. */ while( ((p_buffer = p_fifo->p_first) != NULL) && p_buffer->i_pts < (b_can_sleek ? start_date : now) - AOUT_MAX_PTS_DELAY ) { msg_Dbg( p_aout, "audio output is too slow (%"PRId64"), " "trashing %"PRId64"us", now - p_buffer->i_pts, p_buffer->i_length ); aout_BufferFree( aout_FifoPop( p_fifo ) ); } if( p_buffer == NULL ) { #if 0 /* This is bad because the audio output might just be trying to fill * in its internal buffers. And anyway, it's up to the audio output * to deal with this kind of starvation. */ /* Set date to 0, to allow the mixer to send a new buffer ASAP */ aout_FifoSet( &p_aout->output.fifo, 0 ); if ( !p_aout->output.b_starving ) msg_Dbg( p_aout, "audio output is starving (no input), playing silence" ); p_aout->output.b_starving = true; #endif goto out; }
/***************************************************************************** * MixBuffer: try to prepare one output buffer ***************************************************************************** * Please note that you must hold the mixer lock. *****************************************************************************/ static int MixBuffer( aout_instance_t * p_aout ) { int i, i_first_input = 0; aout_buffer_t * p_output_buffer; mtime_t start_date, end_date; audio_date_t exact_start_date; if ( p_aout->mixer.b_error ) { /* Free all incoming buffers. */ vlc_mutex_lock( &p_aout->input_fifos_lock ); for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { aout_input_t * p_input = p_aout->pp_inputs[i]; aout_buffer_t * p_buffer = p_input->fifo.p_first; if ( p_input->b_error ) continue; while ( p_buffer != NULL ) { aout_buffer_t * p_next = p_buffer->p_next; aout_BufferFree( p_buffer ); p_buffer = p_next; } } vlc_mutex_unlock( &p_aout->input_fifos_lock ); return -1; } vlc_mutex_lock( &p_aout->output_fifo_lock ); vlc_mutex_lock( &p_aout->input_fifos_lock ); /* Retrieve the date of the next buffer. */ memcpy( &exact_start_date, &p_aout->output.fifo.end_date, sizeof(audio_date_t) ); start_date = aout_DateGet( &exact_start_date ); if ( start_date != 0 && start_date < mdate() ) { /* The output 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, "output PTS is out of range ("I64Fd"), clearing out", mdate() - start_date ); aout_FifoSet( p_aout, &p_aout->output.fifo, 0 ); aout_DateSet( &exact_start_date, 0 ); start_date = 0; } vlc_mutex_unlock( &p_aout->output_fifo_lock ); /* See if we have enough data to prepare a new buffer for the audio * output. First : start date. */ if ( !start_date ) { /* Find the latest start date available. */ for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { aout_input_t * p_input = p_aout->pp_inputs[i]; aout_fifo_t * p_fifo = &p_input->fifo; aout_buffer_t * p_buffer; if ( p_input->b_error ) continue; p_buffer = p_fifo->p_first; while ( p_buffer != NULL && p_buffer->start_date < mdate() ) { msg_Warn( p_aout, "input PTS is out of range ("I64Fd"), " "trashing", mdate() - p_buffer->start_date ); p_buffer = aout_FifoPop( p_aout, p_fifo ); aout_BufferFree( p_buffer ); p_buffer = p_fifo->p_first; p_input->p_first_byte_to_mix = NULL; } if ( p_buffer == NULL ) { break; } if ( !start_date || start_date < p_buffer->start_date ) { aout_DateSet( &exact_start_date, p_buffer->start_date ); start_date = p_buffer->start_date; } } if ( i < p_aout->i_nb_inputs ) { /* Interrupted before the end... We can't run. */ vlc_mutex_unlock( &p_aout->input_fifos_lock ); return -1; } } aout_DateIncrement( &exact_start_date, p_aout->output.i_nb_samples ); end_date = aout_DateGet( &exact_start_date ); /* Check that start_date and end_date are available for all input * streams. */ for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { aout_input_t * p_input = p_aout->pp_inputs[i]; aout_fifo_t * p_fifo = &p_input->fifo; aout_buffer_t * p_buffer; mtime_t prev_date; vlc_bool_t b_drop_buffers; if ( p_input->b_error ) { if ( i_first_input == i ) i_first_input++; continue; } p_buffer = p_fifo->p_first; if ( p_buffer == NULL ) { break; } /* Check for the continuity of start_date */ while ( p_buffer != NULL && p_buffer->end_date < start_date - 1 ) { /* We authorize a +-1 because rounding errors get compensated * regularly. */ aout_buffer_t * p_next = p_buffer->p_next; msg_Warn( p_aout, "the mixer got a packet in the past ("I64Fd")", start_date - p_buffer->end_date ); aout_BufferFree( p_buffer ); p_fifo->p_first = p_buffer = p_next; p_input->p_first_byte_to_mix = NULL; } if ( p_buffer == NULL ) { p_fifo->pp_last = &p_fifo->p_first; break; } /* Check that we have enough samples. */ for ( ; ; ) { p_buffer = p_fifo->p_first; if ( p_buffer == NULL ) break; if ( p_buffer->end_date >= end_date ) break; /* Check that all buffers are contiguous. */ prev_date = p_fifo->p_first->end_date; p_buffer = p_buffer->p_next; b_drop_buffers = 0; for ( ; p_buffer != NULL; p_buffer = p_buffer->p_next ) { if ( prev_date != p_buffer->start_date ) { msg_Warn( p_aout, "buffer hole, dropping packets ("I64Fd")", p_buffer->start_date - prev_date ); b_drop_buffers = 1; break; } if ( p_buffer->end_date >= end_date ) break; prev_date = p_buffer->end_date; } if ( b_drop_buffers ) { aout_buffer_t * p_deleted = p_fifo->p_first; while ( p_deleted != NULL && p_deleted != p_buffer ) { aout_buffer_t * p_next = p_deleted->p_next; aout_BufferFree( p_deleted ); p_deleted = p_next; } p_fifo->p_first = p_deleted; /* == p_buffer */ } else break; } if ( p_buffer == NULL ) break; p_buffer = p_fifo->p_first; if ( !AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) ) { /* Additionally check that p_first_byte_to_mix is well * located. */ mtime_t i_nb_bytes = (start_date - p_buffer->start_date) * p_aout->mixer.mixer.i_bytes_per_frame * p_aout->mixer.mixer.i_rate / p_aout->mixer.mixer.i_frame_length / 1000000; ptrdiff_t mixer_nb_bytes; if ( p_input->p_first_byte_to_mix == NULL ) { p_input->p_first_byte_to_mix = p_buffer->p_buffer; } mixer_nb_bytes = p_input->p_first_byte_to_mix - p_buffer->p_buffer; if ( !((i_nb_bytes + p_aout->mixer.mixer.i_bytes_per_frame > mixer_nb_bytes) && (i_nb_bytes < p_aout->mixer.mixer.i_bytes_per_frame + mixer_nb_bytes)) ) { msg_Warn( p_aout, "mixer start isn't output start ("I64Fd")", i_nb_bytes - mixer_nb_bytes ); /* Round to the nearest multiple */ i_nb_bytes /= p_aout->mixer.mixer.i_bytes_per_frame; i_nb_bytes *= p_aout->mixer.mixer.i_bytes_per_frame; if( i_nb_bytes < 0 ) { /* Is it really the best way to do it ? */ aout_FifoSet( p_aout, &p_aout->output.fifo, 0 ); aout_DateSet( &exact_start_date, 0 ); break; } p_input->p_first_byte_to_mix = p_buffer->p_buffer + i_nb_bytes; } } } if ( i < p_aout->i_nb_inputs || i_first_input == p_aout->i_nb_inputs ) { /* Interrupted before the end... We can't run. */ vlc_mutex_unlock( &p_aout->input_fifos_lock ); return -1; } /* Run the mixer. */ aout_BufferAlloc( &p_aout->mixer.output_alloc, ((uint64_t)p_aout->output.i_nb_samples * 1000000) / p_aout->output.output.i_rate, /* This is a bit kludgy, but is actually only used * for the S/PDIF dummy mixer : */ p_aout->pp_inputs[i_first_input]->fifo.p_first, p_output_buffer ); if ( p_output_buffer == NULL ) { msg_Err( p_aout, "out of memory" ); vlc_mutex_unlock( &p_aout->input_fifos_lock ); return -1; } /* This is again a bit kludgy - for the S/PDIF mixer. */ if ( p_aout->mixer.output_alloc.i_alloc_type != AOUT_ALLOC_NONE ) { p_output_buffer->i_nb_samples = p_aout->output.i_nb_samples; p_output_buffer->i_nb_bytes = p_aout->output.i_nb_samples * p_aout->mixer.mixer.i_bytes_per_frame / p_aout->mixer.mixer.i_frame_length; } p_output_buffer->start_date = start_date; p_output_buffer->end_date = end_date; p_aout->mixer.pf_do_work( p_aout, p_output_buffer ); vlc_mutex_unlock( &p_aout->input_fifos_lock ); aout_OutputPlay( p_aout, p_output_buffer ); return 0; }
//#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; }