/***************************************************************************** * 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; }
/***************************************************************************** * aout_DecPlay : filter & mix the decoded buffer *****************************************************************************/ int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate) { aout_owner_t *owner = aout_owner (p_aout); aout_input_t *input; assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE && i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE ); assert( p_buffer->i_pts > 0 ); p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000 / owner->input_format.i_rate; aout_lock( p_aout ); aout_CheckRestart( p_aout ); input = owner->input; if (unlikely(input == NULL)) /* can happen due to restart */ { aout_unlock( p_aout ); aout_BufferFree( p_buffer ); return -1; } /* Input */ p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate, &owner->sync.date); if( p_buffer != NULL ) { date_Increment (&owner->sync.date, p_buffer->i_nb_samples); /* Mixer */ if (owner->volume.mixer != NULL) { float amp = owner->volume.multiplier * vlc_atomic_getf (&owner->gain.multiplier); aout_MixerRun (owner->volume.mixer, p_buffer, amp); } /* Output */ aout_OutputPlay( p_aout, p_buffer ); } aout_unlock( p_aout ); #ifdef __unix__ sched_yield(); #endif return 0; }
static void aout_DecSilence (audio_output_t *aout, mtime_t length, mtime_t pts) { aout_owner_t *owner = aout_owner (aout); const audio_sample_format_t *fmt = &owner->mixer_format; size_t frames = (fmt->i_rate * length) / CLOCK_FREQ; block_t *block = block_Alloc (frames * fmt->i_bytes_per_frame / fmt->i_frame_length); if (unlikely(block == NULL)) return; /* uho! */ msg_Dbg (aout, "inserting %zu zeroes", frames); memset (block->p_buffer, 0, block->i_buffer); block->i_nb_samples = frames; block->i_pts = pts; block->i_dts = pts; block->i_length = length; aout_OutputPlay (aout, block); }
/***************************************************************************** * 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; }