void AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { audio_output_t * p_aout = (audio_output_t *)inUserData; aout_buffer_t * p_buffer = NULL; if (p_aout) { struct aout_sys_t * p_sys = p_aout->sys; aout_packet_t * packet = &p_sys->packet; if (packet) { vlc_mutex_lock( &packet->lock ); p_buffer = aout_FifoPop2( &packet->fifo ); vlc_mutex_unlock( &packet->lock ); } } if ( p_buffer != NULL ) { vlc_memcpy( inBuffer->mAudioData, p_buffer->p_buffer, p_buffer->i_buffer ); inBuffer->mAudioDataByteSize = p_buffer->i_buffer; aout_BufferFree( p_buffer ); } else { vlc_memset( inBuffer->mAudioData, 0, inBuffer->mAudioDataBytesCapacity ); inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity; } AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); }
/***************************************************************************** * Play *****************************************************************************/ static void Play( void * _p_aout, void * _p_buffer, size_t i_size, const media_raw_audio_format &format ) { aout_instance_t * p_aout = (aout_instance_t*) _p_aout; float * p_buffer = (float*) _p_buffer; aout_sys_t * p_sys = (aout_sys_t*) p_aout->output.p_sys; aout_buffer_t * p_aout_buffer; p_aout_buffer = aout_OutputNextBuffer( p_aout, mdate() + p_sys->latency, VLC_FALSE ); if( p_aout_buffer != NULL ) { p_aout->p_vlc->pf_memcpy( p_buffer, p_aout_buffer->p_buffer, MIN( i_size, p_aout_buffer->i_nb_bytes ) ); if( p_aout_buffer->i_nb_bytes < i_size ) { p_aout->p_vlc->pf_memset( p_buffer + p_aout_buffer->i_nb_bytes, 0, i_size - p_aout_buffer->i_nb_bytes ); } aout_BufferFree( p_aout_buffer ); } else { p_aout->p_vlc->pf_memset( p_buffer, 0, i_size ); } }
/***************************************************************************** * DoWork: mix a new output buffer - this does nothing, indeed *****************************************************************************/ static void DoWork( aout_mixer_t * p_mixer, aout_buffer_t * p_buffer ) { VLC_UNUSED( p_buffer ); unsigned i = 0; aout_mixer_input_t * p_input = p_mixer->input[i]; while ( p_input->is_invalid ) p_input = p_mixer->input[++i]; aout_buffer_t * p_old_buffer = aout_FifoPop( NULL, &p_input->fifo ); /* We don't free the old buffer because, * The aout core use a hack to avoid useless memcpy: the buffer in which * to mix is the same as the one in the first active input fifo. * So the ownership of that buffer belongs to our caller */ assert( p_old_buffer == p_buffer ); /* Empty other FIFOs to avoid a memory leak. */ for ( i++; i < p_mixer->input_count; i++ ) { p_input = p_mixer->input[i]; if ( p_input->is_invalid ) continue; while ((p_old_buffer = aout_FifoPop( NULL, &p_input->fifo ))) aout_BufferFree( p_old_buffer ); } }
/***************************************************************************** * DoWork: mix a new output buffer - this does nothing, indeed *****************************************************************************/ static void DoWork( aout_instance_t * p_aout, aout_buffer_t * p_buffer ) { int i = 0; aout_input_t * p_input = p_aout->pp_inputs[i]; while ( p_input->b_error ) { p_input = p_aout->pp_inputs[++i]; } aout_FifoPop( p_aout, &p_input->fifo ); /* Empty other FIFOs to avoid a memory leak. */ for ( i++; i < p_aout->i_nb_inputs; i++ ) { aout_fifo_t * p_fifo; aout_buffer_t * p_deleted; p_input = p_aout->pp_inputs[i]; if ( p_input->b_error ) continue; p_fifo = &p_input->fifo; p_deleted = p_fifo->p_first; while ( p_deleted != NULL ) { aout_buffer_t * p_next = p_deleted->p_next; aout_BufferFree( p_deleted ); p_deleted = p_next; } p_fifo->p_first = NULL; p_fifo->pp_last = &p_fifo->p_first; } }
static void OSSThreadCleanup( void *data ) { oss_thread_ctx_t *p_ctx = data; if( p_ctx->p_buffer ) aout_BufferFree( p_ctx->p_buffer ); else free( p_ctx->p_bytes ); }
/***************************************************************************** * Process: callback for JACK *****************************************************************************/ int Process( jack_nframes_t i_frames, void *p_arg ) { unsigned int i, j, i_nb_samples = 0; audio_output_t *p_aout = (audio_output_t*) p_arg; struct aout_sys_t *p_sys = p_aout->sys; jack_sample_t *p_src = NULL; jack_nframes_t dframes = p_sys->latency - jack_frames_since_cycle_start( p_sys->p_jack_client ); jack_time_t dtime = dframes * 1000 * 1000 / jack_get_sample_rate( p_sys->p_jack_client ); mtime_t play_date = mdate() + (mtime_t) ( dtime ); /* Get the next audio data buffer */ aout_buffer_t *p_buffer = aout_PacketNext( p_aout, play_date ); if( p_buffer != NULL ) { p_src = (jack_sample_t *)p_buffer->p_buffer; i_nb_samples = p_buffer->i_nb_samples; } /* Get the JACK buffers to write to */ for( i = 0; i < p_sys->i_channels; i++ ) { p_sys->p_jack_buffers[i] = jack_port_get_buffer( p_sys->p_jack_ports[i], i_frames ); } /* Copy in the audio data */ for( j = 0; j < i_nb_samples; j++ ) { for( i = 0; i < p_sys->i_channels; i++ ) { jack_sample_t *p_dst = p_sys->p_jack_buffers[i]; p_dst[j] = *p_src; p_src++; } } /* Fill any remaining buffer with silence */ if( i_nb_samples < i_frames ) { for( i = 0; i < p_sys->i_channels; i++ ) { memset( p_sys->p_jack_buffers[i] + i_nb_samples, 0, sizeof( jack_sample_t ) * (i_frames - i_nb_samples) ); } } if( p_buffer ) { aout_BufferFree( p_buffer ); } return 0; }
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { VLC_UNUSED( s ); aout_instance_t *p_aout = (aout_instance_t *)userdata; struct aout_sys_t * p_sys = (struct aout_sys_t *) p_aout->output.p_sys; mtime_t next_date; assert(s); assert(p_sys); size_t buffer_size = p_sys->buffer_size; PULSE_DEBUG( "Pulse stream request %d", length); do { aout_buffer_t * p_buffer = NULL; if(p_sys->started) { pa_usec_t latency; int negative; if(pa_stream_get_latency(p_sys->stream, &latency, &negative)<0) { if (pa_context_errno(p_sys->context) != PA_ERR_NODATA) { msg_Err(p_aout, "pa_stream_get_latency() failed: %s", pa_strerror(pa_context_errno(p_sys->context))); } latency = 0; } PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency); next_date = mdate() + latency; if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ) { p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0); } } if ( p_buffer != NULL ) { PULSE_DEBUG( "Pulse stream request write buffer %d", p_buffer->i_buffer); pa_stream_write(p_sys->stream, p_buffer->p_buffer, p_buffer->i_buffer, NULL, 0, PA_SEEK_RELATIVE); length -= p_buffer->i_buffer; aout_BufferFree( p_buffer ); } else { PULSE_DEBUG( "Pulse stream request write zeroes"); void *data = pa_xmalloc(buffer_size); bzero(data, buffer_size); pa_stream_write(p_sys->stream, data, buffer_size, pa_xfree, 0, PA_SEEK_RELATIVE); length -= buffer_size; } } while(length > buffer_size); pa_threaded_mainloop_signal(p_sys->mainloop, 0); }
static void Play(aout_instance_t *p_aout) { struct aout_sys_t *p_sys = p_aout->output.p_sys; int length; aout_buffer_t *p_buffer; while ((p_buffer = aout_FifoPop(&p_aout->output.fifo)) != NULL) { length = 0; while (length < p_buffer->i_buffer) { length += at_write(p_sys->AudioTrack, (char*)(p_buffer->p_buffer) + length, p_buffer->i_buffer - length); } aout_BufferFree(p_buffer); } }
/***************************************************************************** * Process: callback for JACK *****************************************************************************/ int Process( jack_nframes_t i_frames, void *p_arg ) { unsigned int i, j, i_nb_samples = 0; aout_instance_t *p_aout = (aout_instance_t*) p_arg; struct aout_sys_t *p_sys = p_aout->output.p_sys; jack_sample_t *p_src = NULL; /* Get the next audio data buffer */ aout_buffer_t *p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); if( p_buffer != NULL ) { p_src = (jack_sample_t *)p_buffer->p_buffer; i_nb_samples = p_buffer->i_nb_samples; } /* Get the JACK buffers to write to */ for( i = 0; i < p_sys->i_channels; i++ ) { p_sys->p_jack_buffers[i] = jack_port_get_buffer( p_sys->p_jack_ports[i], i_frames ); } /* Copy in the audio data */ for( j = 0; j < i_nb_samples; j++ ) { for( i = 0; i < p_sys->i_channels; i++ ) { jack_sample_t *p_dst = p_sys->p_jack_buffers[i]; p_dst[j] = *p_src; p_src++; } } /* Fill any remaining buffer with silence */ if( i_nb_samples < i_frames ) { for( i = 0; i < p_sys->i_channels; i++ ) { memset( p_sys->p_jack_buffers[i] + i_nb_samples, 0, sizeof( jack_sample_t ) * (i_frames - i_nb_samples) ); } } if( p_buffer ) { aout_BufferFree( p_buffer ); } return 0; }
/***************************************************************************** * aout_FifoReset: trash all buffers *****************************************************************************/ void aout_FifoReset( aout_fifo_t * p_fifo ) { aout_buffer_t * p_buffer; date_Set( &p_fifo->end_date, VLC_TS_INVALID ); p_buffer = p_fifo->p_first; while ( p_buffer != NULL ) { aout_buffer_t * p_next = p_buffer->p_next; aout_BufferFree( p_buffer ); p_buffer = p_next; } p_fifo->p_first = NULL; p_fifo->pp_last = &p_fifo->p_first; }
/***************************************************************************** * aout_FifoSet : set end_date and trash all buffers (because they aren't * properly dated) *****************************************************************************/ void aout_FifoSet( aout_fifo_t * p_fifo, mtime_t date ) { aout_buffer_t * p_buffer; date_Set( &p_fifo->end_date, date ); p_buffer = p_fifo->p_first; while ( p_buffer != NULL ) { aout_buffer_t * p_next = p_buffer->p_next; aout_BufferFree( p_buffer ); p_buffer = p_next; } p_fifo->p_first = NULL; p_fifo->pp_last = &p_fifo->p_first; }
/***************************************************************************** * aout_FifoDestroy : destroy a FIFO and its buffers *****************************************************************************/ void aout_FifoDestroy( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) { aout_buffer_t * p_buffer; p_buffer = p_fifo->p_first; while ( p_buffer != NULL ) { aout_buffer_t * p_next = p_buffer->p_next; aout_BufferFree( p_buffer ); p_buffer = p_next; } p_fifo->p_first = NULL; p_fifo->pp_last = &p_fifo->p_first; }
/***************************************************************************** * 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; }
/***************************************************************************** * 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 ); }
/***************************************************************************** * aout_FifoDestroy : destroy a FIFO and its buffers *****************************************************************************/ void aout_FifoDestroy( aout_instance_t * p_aout, aout_fifo_t * p_fifo ) { aout_buffer_t * p_buffer; (void)p_aout; AOUT_ASSERT_FIFO_LOCKED; p_buffer = p_fifo->p_first; while ( p_buffer != NULL ) { aout_buffer_t * p_next = p_buffer->p_next; aout_BufferFree( p_buffer ); p_buffer = p_next; } p_fifo->p_first = NULL; p_fifo->pp_last = &p_fifo->p_first; }
/***************************************************************************** * aout_FifoSet : set end_date and trash all buffers (because they aren't * properly dated) *****************************************************************************/ void aout_FifoSet( aout_instance_t * p_aout, aout_fifo_t * p_fifo, mtime_t date ) { aout_buffer_t * p_buffer; (void)p_aout; AOUT_ASSERT_FIFO_LOCKED; date_Set( &p_fifo->end_date, date ); p_buffer = p_fifo->p_first; while ( p_buffer != NULL ) { aout_buffer_t * p_next = p_buffer->p_next; aout_BufferFree( p_buffer ); p_buffer = p_next; } p_fifo->p_first = NULL; p_fifo->pp_last = &p_fifo->p_first; }
/***************************************************************************** * 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; }
/* This routine will be called by the PortAudio engine when audio is needed. * It may called at interrupt level on some machines so don't do anything * that could mess up the system like calling malloc() or free(). */ static int paCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *paDate, PaStreamCallbackFlags statusFlags, void *p_cookie ) { VLC_UNUSED( inputBuffer ); VLC_UNUSED( statusFlags ); struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie; aout_instance_t *p_aout = p_sys->p_aout; aout_buffer_t *p_buffer; mtime_t out_date; out_date = mdate() + (mtime_t) ( 1000000 * ( paDate->outputBufferDacTime - paDate->currentTime ) ); p_buffer = aout_OutputNextBuffer( p_aout, out_date, true ); if ( p_buffer != NULL ) { if( p_sys->b_chan_reorder ) { /* Do the channel reordering here */ aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes, p_sys->i_channels, p_sys->pi_chan_table, p_sys->i_bits_per_sample ); } vlc_memcpy( outputBuffer, p_buffer->p_buffer, framesPerBuffer * p_sys->i_sample_size ); /* aout_BufferFree may be dangereous here, but then so is * aout_OutputNextBuffer (calls aout_BufferFree internally). * one solution would be to link the no longer useful buffers * in a second fifo (in aout_OutputNextBuffer too) and to * wait until we are in Play to do the actual free. */ aout_BufferFree( p_buffer ); } else /* Audio output buffer shortage -> stop the fill process and wait */ { vlc_memset( outputBuffer, 0, framesPerBuffer * p_sys->i_sample_size ); } return 0; }
/***************************************************************************** * Thread: thread used to DMA the data to the device *****************************************************************************/ static void* Thread( vlc_object_t *p_this ) { aout_instance_t * p_aout = (aout_instance_t*)p_this; aout_buffer_t * p_buffer; struct aout_sys_t * p_sys = p_aout->output.p_sys; PCMAudioPlayer * pPlayer = p_sys->pPlayer; int canc = vlc_savecancel (); while( vlc_object_alive (p_aout) ) { pPlayer->WaitForBuffer(); vlc_mutex_lock( &p_aout->output_fifo_lock ); p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); vlc_mutex_unlock( &p_aout->output_fifo_lock ); #define i p_sys->nNextBufferIndex if( p_buffer == NULL ) { vlc_memset( p_aout, p_sys->ppBuffers[ i ], 0, p_sys->nBufferSize ); } else { InterleaveS16( (int16_t *)p_buffer->p_buffer, (int16_t *)p_sys->ppBuffers[ i ] ); aout_BufferFree( p_buffer ); } if( !pPlayer->QueueBuffer( (s16 *)p_sys->ppBuffers[ i ], p_sys->nBufferSize / 2 ) ) { msg_Err( p_aout, "QueueBuffer failed" ); } i = (i + 1) % p_sys->nBuffers; #undef i } vlc_restorecancel (canc); return NULL; }
/***************************************************************************** * Play: pretend to play a sound *****************************************************************************/ static void Play( aout_instance_t * p_aout ) { aout_buffer_t * p_buffer; p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); if( fwrite( p_buffer->p_buffer, p_buffer->i_buffer, 1, p_aout->output.p_sys->p_file ) != 1 ) { msg_Err( p_aout, "write error (%m)" ); } if( p_aout->output.p_sys->b_add_wav_header ) { /* Update Wave Header */ p_aout->output.p_sys->waveh.DataLength += p_buffer->i_buffer; } aout_BufferFree( p_buffer ); }
void AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) { aout_instance_t * p_aout = (aout_instance_t *)inUserData; aout_buffer_t * p_buffer = NULL; if (p_aout) { vlc_mutex_lock( &p_aout->output_fifo_lock ); p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); vlc_mutex_unlock( &p_aout->output_fifo_lock ); } if ( p_buffer != NULL ) { vlc_memcpy( inBuffer->mAudioData, p_buffer->p_buffer, p_buffer->i_buffer ); inBuffer->mAudioDataByteSize = p_buffer->i_buffer; aout_BufferFree( p_buffer ); } else { vlc_memset( inBuffer->mAudioData, 0, inBuffer->mAudioDataBytesCapacity ); inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity; } AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL); }
/***************************************************************************** * SDLCallback: what to do once SDL has played sound samples *****************************************************************************/ static void SDLCallback( void * _p_aout, uint8_t * p_stream, int i_len ) { aout_instance_t * p_aout = (aout_instance_t *)_p_aout; aout_buffer_t * p_buffer; /* SDL is unable to call us at regular times, or tell us its current * hardware latency, or the buffer state. So we just pop data and throw * it at SDL's face. Nah. */ vlc_mutex_lock( &p_aout->output_fifo_lock ); p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); vlc_mutex_unlock( &p_aout->output_fifo_lock ); if ( p_buffer != NULL ) { vlc_memcpy( p_stream, p_buffer->p_buffer, i_len ); aout_BufferFree( p_buffer ); } else { vlc_memset( p_stream, 0, i_len ); } }
/***************************************************************************** * 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; }
/* This routine will be called by the PortAudio engine when audio is needed. * It may called at interrupt level on some machines so don't do anything * that could mess up the system like calling malloc() or free(). */ static int paCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *paDate, PaStreamCallbackFlags statusFlags, void *p_cookie ) { VLC_UNUSED( inputBuffer ); VLC_UNUSED( statusFlags ); struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie; audio_output_t *p_aout = p_sys->p_aout; aout_buffer_t *p_buffer; mtime_t out_date; out_date = mdate() + (mtime_t) ( 1000000 * ( paDate->outputBufferDacTime - paDate->currentTime ) ); p_buffer = aout_PacketNext( p_aout, out_date ); if ( p_buffer != NULL ) { if( p_sys->b_chan_reorder ) { /* Do the channel reordering here */ aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer, p_sys->i_channels, p_sys->pi_chan_table, p_sys->i_bits_per_sample ); } vlc_memcpy( outputBuffer, p_buffer->p_buffer, framesPerBuffer * p_sys->i_sample_size ); aout_BufferFree( p_buffer ); } else /* Audio output buffer shortage -> stop the fill process and wait */ { vlc_memset( outputBuffer, 0, framesPerBuffer * p_sys->i_sample_size ); } return 0; }
/***************************************************************************** * aout_FiltersPlay: play a buffer *****************************************************************************/ void aout_FiltersPlay( aout_instance_t * p_aout, aout_filter_t ** pp_filters, int i_nb_filters, aout_buffer_t ** pp_input_buffer ) { int i; for ( i = 0; i < i_nb_filters; i++ ) { aout_filter_t * p_filter = pp_filters[i]; aout_buffer_t * p_output_buffer; /* Resamplers can produce slightly more samples than (i_in_nb * * p_filter->output.i_rate / p_filter->input.i_rate) so we need * slightly bigger buffers. */ aout_BufferAlloc( &p_filter->output_alloc, ((mtime_t)(*pp_input_buffer)->i_nb_samples + 2) * 1000000 / p_filter->input.i_rate, *pp_input_buffer, p_output_buffer ); if ( p_output_buffer == NULL ) { msg_Err( p_aout, "out of memory" ); return; } /* Please note that p_output_buffer->i_nb_samples & i_nb_bytes * shall be set by the filter plug-in. */ p_filter->pf_do_work( p_aout, p_filter, *pp_input_buffer, p_output_buffer ); if ( !p_filter->b_in_place ) { aout_BufferFree( *pp_input_buffer ); *pp_input_buffer = p_output_buffer; } } }
/***************************************************************************** * Play: pretend to play a sound *****************************************************************************/ static void Play( aout_instance_t * p_aout ) { aout_buffer_t * p_buffer = aout_FifoPop( &p_aout->output.fifo ); aout_BufferFree( p_buffer ); }
/***************************************************************************** * DoWork: mix a new output buffer ***************************************************************************** * Terminology : in this function a word designates a single float32, eg. * a stereo sample is consituted of two words. *****************************************************************************/ static void DoWork( aout_instance_t * p_aout, aout_buffer_t * p_buffer ) { const int i_nb_inputs = p_aout->i_nb_inputs; const float f_multiplier_global = p_aout->mixer.f_multiplier; const int i_nb_channels = aout_FormatNbChannels( &p_aout->mixer.mixer ); int i_input; for ( i_input = 0; i_input < i_nb_inputs; i_input++ ) { int i_nb_words = p_buffer->i_nb_samples * i_nb_channels; aout_input_t * p_input = p_aout->pp_inputs[i_input]; float f_multiplier = f_multiplier_global * p_input->f_multiplier; float * p_out = (float *)p_buffer->p_buffer; float * p_in = (float *)p_input->p_first_byte_to_mix; if ( p_input->b_error ) continue; for ( ; ; ) { ptrdiff_t i_available_words = ( (float *)p_input->fifo.p_first->p_buffer - p_in) + p_input->fifo.p_first->i_nb_samples * i_nb_channels; if ( i_available_words < i_nb_words ) { aout_buffer_t * p_old_buffer; if ( i_available_words > 0 ) { if ( !i_input ) { ScaleWords( p_out, p_in, i_available_words, i_nb_inputs, f_multiplier ); } else { MeanWords( p_out, p_in, i_available_words, i_nb_inputs, f_multiplier ); } } i_nb_words -= i_available_words; p_out += i_available_words; /* Next buffer */ p_old_buffer = aout_FifoPop( p_aout, &p_input->fifo ); aout_BufferFree( p_old_buffer ); if ( p_input->fifo.p_first == NULL ) { msg_Err( p_aout, "internal amix error" ); return; } p_in = (float *)p_input->fifo.p_first->p_buffer; } else { if ( i_nb_words > 0 ) { if ( !i_input ) { ScaleWords( p_out, p_in, i_nb_words, i_nb_inputs, f_multiplier ); } else { MeanWords( p_out, p_in, i_nb_words, i_nb_inputs, f_multiplier ); } } p_input->p_first_byte_to_mix = (void *)(p_in + i_nb_words); break; } } } }
/***************************************************************************** * 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; }
/***************************************************************************** * QNXaoutThread: asynchronous thread used to DMA the data to the device *****************************************************************************/ static int QNXaoutThread( aout_instance_t * p_aout ) { struct aout_sys_t * p_sys = p_aout->output.p_sys; while ( !p_aout->b_die ) { aout_buffer_t * p_buffer; int i_tmp, i_size; byte_t * p_bytes; if ( p_aout->output.output.i_format != VLC_FOURCC('s','p','d','i') ) { mtime_t next_date = 0; /* Get the presentation date of the next write() operation. It * is equal to the current date + duration of buffered samples. * Order is important here, since GetBufInfo is believed to take * more time than mdate(). */ next_date = (mtime_t)GetBufInfo( p_aout ) * 1000000 / p_aout->output.output.i_bytes_per_frame / p_aout->output.output.i_rate * p_aout->output.output.i_frame_length; next_date += mdate(); p_buffer = aout_OutputNextBuffer( p_aout, next_date, VLC_FALSE ); } else { p_buffer = aout_OutputNextBuffer( p_aout, 0, VLC_TRUE ); } if ( p_buffer != NULL ) { p_bytes = p_buffer->p_buffer; i_size = p_buffer->i_nb_bytes; } else { i_size = DEFAULT_FRAME_SIZE / p_aout->output.output.i_frame_length * p_aout->output.output.i_bytes_per_frame; p_bytes = p_aout->output.p_sys->p_silent_buffer; memset( p_bytes, 0, i_size ); } i_tmp = snd_pcm_plugin_write( p_aout->output.p_sys->p_pcm_handle, (void *) p_bytes, (size_t) i_size ); if( i_tmp < 0 ) { msg_Err( p_aout, "write failed (%s)", strerror(errno) ); } if ( p_buffer != NULL ) { aout_BufferFree( p_buffer ); } } return 0; }
static void inputDrop( aout_input_t *p_input, aout_buffer_t *p_buffer ) { aout_BufferFree( p_buffer ); p_input->i_buffer_lost++; }