/***************************************************************************** * 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 Play (aout_instance_t *aout) { aout_sys_t *sys = aout->output.p_sys; block_t *block; while ((block = aout_FifoPop(&aout->output.fifo)) != NULL) { sys->play (sys->opaque, block->p_buffer, block->i_nb_samples, block->i_pts); block_Release (block); } }
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; }
/***************************************************************************** * 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; }
/***************************************************************************** * 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; }
/***************************************************************************** * 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; } } } }
/** * Dequeues the next audio packet (a.k.a. audio fragment). * The audio output plugin must first call aout_PacketPlay() to queue the * decoded audio samples. Typically, audio_output_t.pf_play is set to, or calls * aout_PacketPlay(). * @note This function is considered legacy. Please do not use this function in * new audio output plugins. * @param p_aout audio output instance * @param start_date expected PTS of the audio packet */ block_t *aout_PacketNext (audio_output_t *p_aout, mtime_t start_date) { aout_packet_t *p = aout_packet (p_aout); aout_fifo_t *p_fifo = &p->fifo; block_t *p_buffer; const bool b_can_sleek = !AOUT_FMT_LINEAR(&p_aout->format); const mtime_t now = mdate (); const mtime_t threshold = (b_can_sleek ? start_date : now) - AOUT_MAX_PTS_DELAY; vlc_mutex_lock( &p->lock ); if( p->pause_date != VLC_TS_INVALID ) goto out; /* paused: do not dequeue buffers */ for (;;) { p_buffer = p_fifo->p_first; if (p_buffer == NULL) goto out; /* nothing to play */ if (p_buffer->i_pts >= threshold) break; /* 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. */ msg_Dbg (p_aout, "audio output is too slow (%"PRId64" us): " " trashing %"PRId64" us", threshold - p_buffer->i_pts, p_buffer->i_length); block_Release (aout_FifoPop (p_fifo)); } mtime_t delta = start_date - p_buffer->i_pts; /* This assumes that all buffers have the same duration. This is true * since aout_PacketPlay() (aout_OutputSlice()) is used. */ if (0 >= delta + p_buffer->i_length) { if (!p->starving) { msg_Dbg (p_aout, "audio output is starving (%"PRId64"), " "playing silence", delta); p->starving = true; } goto out; /* nothing to play _yet_ */ } p->starving = false; p_buffer = aout_FifoPop( p_fifo ); if (!b_can_sleek && (delta < -AOUT_MAX_PTS_ADVANCE || AOUT_MAX_PTS_DELAY < delta)) { msg_Warn (p_aout, "audio output out of sync, " "adjusting dates (%"PRId64" us)", delta); aout_FifoMoveDates (&p->partial, delta); aout_FifoMoveDates (p_fifo, delta); p->time_report = delta; } vlc_mutex_unlock( &p->lock ); return p_buffer; out: vlc_mutex_unlock( &p->lock ); return NULL; }
/** * Rearranges audio blocks in correct number of samples. * @note (FIXME) This is left here for historical reasons. It belongs in the * output code. Besides, this operation should be avoided if possible. */ static block_t *aout_OutputSlice (audio_output_t *p_aout) { aout_packet_t *p = aout_packet (p_aout); aout_fifo_t *p_fifo = &p->partial; const unsigned samples = p->samples; assert( samples > 0 ); vlc_assert_locked( &p->lock ); /* Retrieve the date of the next buffer. */ date_t exact_start_date = p->fifo.end_date; mtime_t start_date = date_Get( &exact_start_date ); /* See if we have enough data to prepare a new buffer for the audio output. */ aout_buffer_t *p_buffer = p_fifo->p_first; if( p_buffer == NULL ) return NULL; /* Find the earliest start date available. */ if ( start_date == VLC_TS_INVALID ) { start_date = p_buffer->i_pts; date_Set( &exact_start_date, start_date ); } /* Compute the end date for the new buffer. */ mtime_t end_date = date_Increment( &exact_start_date, samples ); /* Check that we have enough samples (TODO merge with next loop). */ for( unsigned available = 0; available < samples; ) { p_buffer = p_buffer->p_next; if( p_buffer == NULL ) return NULL; available += p_buffer->i_nb_samples; } if( AOUT_FMT_LINEAR( &p_aout->format ) ) { const unsigned framesize = p_aout->format.i_bytes_per_frame; /* Build packet with adequate number of samples */ unsigned needed = samples * framesize; p_buffer = block_Alloc( needed ); if( unlikely(p_buffer == NULL) ) /* XXX: should free input buffers */ return NULL; p_buffer->i_nb_samples = samples; for( uint8_t *p_out = p_buffer->p_buffer; needed > 0; ) { aout_buffer_t *p_inbuf = p_fifo->p_first; if( unlikely(p_inbuf == NULL) ) { msg_Err( p_aout, "packetization error" ); vlc_memset( p_out, 0, needed ); break; } const uint8_t *p_in = p_inbuf->p_buffer; size_t avail = p_inbuf->i_nb_samples * framesize; if( avail > needed ) { vlc_memcpy( p_out, p_in, needed ); p_fifo->p_first->p_buffer += needed; p_fifo->p_first->i_buffer -= needed; needed /= framesize; p_fifo->p_first->i_nb_samples -= needed; mtime_t t = needed * CLOCK_FREQ / p_aout->format.i_rate; p_fifo->p_first->i_pts += t; p_fifo->p_first->i_length -= t; break; } vlc_memcpy( p_out, p_in, avail ); needed -= avail; p_out += avail; /* Next buffer */ aout_BufferFree( aout_FifoPop( p_fifo ) ); } } else p_buffer = aout_FifoPop( p_fifo ); p_buffer->i_pts = start_date; p_buffer->i_length = end_date - start_date; return p_buffer; }
/***************************************************************************** * DoWork: mix a new output buffer *****************************************************************************/ 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]; int i_nb_channels = aout_FormatNbChannels( &p_aout->mixer.mixer ); int i_nb_bytes = p_buffer->i_nb_samples * sizeof(int32_t) * i_nb_channels; uint8_t * p_in; uint8_t * p_out; while ( p_input->b_error ) { p_input = p_aout->pp_inputs[++i]; /* This can't crash because if no input has b_error == 0, the * audio mixer cannot run and we can't be here. */ } p_in = p_input->p_first_byte_to_mix; p_out = p_buffer->p_buffer; for ( ; ; ) { ptrdiff_t i_available_bytes = (p_input->fifo.p_first->p_buffer - p_in) + p_input->fifo.p_first->i_nb_samples * sizeof(int32_t) * i_nb_channels; if ( i_available_bytes < i_nb_bytes ) { aout_buffer_t * p_old_buffer; vlc_memcpy( p_out, p_in, i_available_bytes ); i_nb_bytes -= i_available_bytes; p_out += i_available_bytes; /* 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 = p_input->fifo.p_first->p_buffer; } else { vlc_memcpy( p_out, p_in, i_nb_bytes ); p_input->p_first_byte_to_mix = p_in + i_nb_bytes; break; } } /* 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; } }