/***************************************************************************** * aout_InputCheckAndRestart : restart an input ***************************************************************************** * This function must be entered with the input and mixer lock. *****************************************************************************/ void aout_InputCheckAndRestart( aout_instance_t * p_aout, aout_input_t * p_input ) { AOUT_ASSERT_MIXER_LOCKED; AOUT_ASSERT_INPUT_LOCKED; if( !p_input->b_restart ) return; aout_lock_input_fifos( p_aout ); /* A little trick to avoid loosing our input fifo and properties */ uint8_t *p_first_byte_to_mix = p_input->mixer.begin; aout_fifo_t fifo = p_input->mixer.fifo; bool b_paused = p_input->b_paused; mtime_t i_pause_date = p_input->i_pause_date; aout_FifoInit( p_aout, &p_input->mixer.fifo, p_aout->mixer_format.i_rate ); aout_InputDelete( p_aout, p_input ); aout_InputNew( p_aout, p_input, &p_input->request_vout ); p_input->mixer.begin = p_first_byte_to_mix; p_input->mixer.fifo = fifo; p_input->b_paused = b_paused; p_input->i_pause_date = i_pause_date; p_input->b_restart = false; aout_unlock_input_fifos( p_aout ); }
//#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_Restart : re-open the output device and rebuild the input and output * pipelines ***************************************************************************** * This function is used whenever the parameters of the output plug-in are * changed (eg. selecting S/PDIF or PCM). *****************************************************************************/ static int aout_Restart( aout_instance_t * p_aout ) { int i; bool b_error = 0; aout_lock_mixer( p_aout ); if ( p_aout->i_nb_inputs == 0 ) { aout_unlock_mixer( p_aout ); msg_Err( p_aout, "no decoder thread" ); return -1; } /* Lock all inputs. */ aout_lock_input_fifos( p_aout ); for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { aout_lock_input( p_aout, p_aout->pp_inputs[i] ); aout_InputDelete( p_aout, p_aout->pp_inputs[i] ); } aout_MixerDelete( p_aout ); /* Re-open the output plug-in. */ aout_OutputDelete( p_aout ); if ( aout_OutputNew( p_aout, &p_aout->pp_inputs[0]->input ) == -1 ) { /* Release all locks and report the error. */ for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock ); } aout_unlock_input_fifos( p_aout ); aout_unlock_mixer( p_aout ); return -1; } if ( aout_MixerNew( p_aout ) == -1 ) { aout_OutputDelete( p_aout ); for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { vlc_mutex_unlock( &p_aout->pp_inputs[i]->lock ); } aout_unlock_input_fifos( p_aout ); aout_unlock_mixer( p_aout ); return -1; } /* Re-open all inputs. */ for ( i = 0; i < p_aout->i_nb_inputs; i++ ) { aout_input_t * p_input = p_aout->pp_inputs[i]; b_error |= aout_InputNew( p_aout, p_input ); p_input->b_changed = 1; aout_unlock_input( p_aout, p_input ); } aout_unlock_input_fifos( p_aout ); aout_unlock_mixer( p_aout ); return b_error; }
/***************************************************************************** * doVolumeChanges : handle all volume changes. Internal use only to ease * variables locking. *****************************************************************************/ static int doVolumeChanges( unsigned action, vlc_object_t * p_object, int i_nb_steps, audio_volume_t i_volume, audio_volume_t * i_return_volume, bool b_mute ) { int i_result = VLC_SUCCESS; int i_volume_step = 1, i_new_volume = 0; bool b_var_mute = false; aout_instance_t *p_aout = findAout( p_object ); if ( p_aout ) aout_lock_volume( p_aout ); b_var_mute = var_GetBool( p_object, "volume-muted"); const bool b_unmute_condition = ( b_var_mute && ( /* Unmute: on increments */ ( action == INCREMENT_VOLUME ) || /* On explicit unmute */ ( ( action == SET_MUTE ) && !b_mute ) || /* On toggle from muted */ ( action == TOGGLE_MUTE ) )); const bool b_mute_condition = ( !b_var_mute && ( /* explicit */ ( ( action == SET_MUTE ) && b_mute ) || /* or toggle */ ( action == TOGGLE_MUTE ) )); /* If muting or unmuting when play hasn't started */ if ( action == SET_MUTE && !b_unmute_condition && !b_mute_condition ) { if ( p_aout ) { aout_unlock_volume( p_aout ); vlc_object_release( p_aout ); } return i_result; } /* On UnMute */ if ( b_unmute_condition ) { /* Restore saved volume */ i_volume = var_GetInteger( p_object, "saved-volume" ); var_SetBool( p_object, "volume-muted", false ); } else if ( b_mute_condition ) { /* We need an initial value to backup later */ i_volume = config_GetInt( p_object, "volume" ); } if ( action == INCREMENT_VOLUME ) { i_volume_step = var_InheritInteger( p_object, "volume-step" ); if ( !b_unmute_condition ) i_volume = config_GetInt( p_object, "volume" ); i_new_volume = (int) i_volume + i_volume_step * i_nb_steps; if ( i_new_volume > AOUT_VOLUME_MAX ) i_volume = AOUT_VOLUME_MAX; else if ( i_new_volume < AOUT_VOLUME_MIN ) i_volume = AOUT_VOLUME_MIN; else i_volume = i_new_volume; } var_SetInteger( p_object, "saved-volume" , i_volume ); /* On Mute */ if ( b_mute_condition ) { i_volume = AOUT_VOLUME_MIN; var_SetBool( p_object, "volume-muted", true ); } /* Commit volume changes */ config_PutInt( p_object, "volume", i_volume ); if ( p_aout ) { aout_lock_mixer( p_aout ); aout_lock_input_fifos( p_aout ); if ( p_aout->p_mixer ) i_result = p_aout->output.pf_volume_set( p_aout, i_volume ); aout_unlock_input_fifos( p_aout ); aout_unlock_mixer( p_aout ); } /* trigger callbacks */ var_TriggerCallback( p_object, "volume-change" ); if ( p_aout ) { var_SetBool( p_aout, "intf-change", true ); aout_unlock_volume( p_aout ); vlc_object_release( p_aout ); } if ( i_return_volume != NULL ) *i_return_volume = i_volume; return i_result; }