/***************************************************************************** * Create: allocate ugly resampler *****************************************************************************/ static int Create( vlc_object_t *p_this ) { filter_t * p_filter = (filter_t *)p_this; if( p_filter->fmt_in.audio.i_rate == p_filter->fmt_out.audio.i_rate || p_filter->fmt_in.audio.i_format != p_filter->fmt_out.audio.i_format || p_filter->fmt_in.audio.i_physical_channels != p_filter->fmt_out.audio.i_physical_channels || p_filter->fmt_in.audio.i_original_channels != p_filter->fmt_out.audio.i_original_channels || !AOUT_FMT_LINEAR( &p_filter->fmt_in.audio ) ) return VLC_EGENERIC; p_filter->pf_audio_filter = DoWork; return VLC_SUCCESS; }
/** * Creates an audio output */ int aout_DecNew( audio_output_t *p_aout, const audio_sample_format_t *p_format, const audio_replay_gain_t *p_replay_gain, const aout_request_vout_t *p_request_vout ) { /* Sanitize audio format */ unsigned i_channels = aout_FormatNbChannels( p_format ); if( i_channels != p_format->i_channels && AOUT_FMT_LINEAR( p_format ) ) { msg_Err( p_aout, "incompatible audio channels count with layout mask" ); return -1; } if( p_format->i_rate > 352800 ) { msg_Err( p_aout, "excessive audio sample frequency (%u)", p_format->i_rate ); return -1; } if( p_format->i_rate < 4000 ) { msg_Err( p_aout, "too low audio sample frequency (%u)", p_format->i_rate ); return -1; } var_Create (p_aout, "stereo-mode", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT); vlc_value_t txt; txt.psz_string = _("Stereo audio mode"); var_Change (p_aout, "stereo-mode", VLC_VAR_SETTEXT, &txt, NULL); aout_owner_t *owner = aout_owner(p_aout); /* TODO: reduce lock scope depending on decoder's real need */ aout_OutputLock (p_aout); /* Create the audio output stream */ owner->volume = aout_volume_New (p_aout, p_replay_gain); atomic_store (&owner->restart, 0); owner->input_format = *p_format; owner->mixer_format = owner->input_format; owner->request_vout = *p_request_vout; if (aout_OutputNew (p_aout, &owner->mixer_format)) goto error; aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); /* Create the audio filtering "input" pipeline */ owner->filters = aout_FiltersNew (p_aout, p_format, &owner->mixer_format, &owner->request_vout); if (owner->filters == NULL) { aout_OutputDelete (p_aout); error: aout_volume_Delete (owner->volume); owner->volume = NULL; aout_OutputUnlock (p_aout); var_Destroy (p_aout, "stereo-mode"); return -1; } owner->sync.end = VLC_TS_INVALID; owner->sync.resamp_type = AOUT_RESAMPLING_NONE; owner->sync.discontinuity = true; aout_OutputUnlock (p_aout); atomic_init (&owner->buffers_lost, 0); atomic_init (&owner->buffers_played, 0); return 0; }
/***************************************************************************** * aout_OutputNew : allocate a new output and rework the filter pipeline ***************************************************************************** * This function is entered with the mixer lock. *****************************************************************************/ int aout_OutputNew( audio_output_t *p_aout, const audio_sample_format_t * p_format ) { aout_owner_t *owner = aout_owner (p_aout); aout_assert_locked( p_aout ); p_aout->format = *p_format; aout_FormatPrepare( &p_aout->format ); /* Find the best output plug-in. */ owner->module = module_need (p_aout, "audio output", "$aout", false); if (owner->module == NULL) { msg_Err( p_aout, "no suitable audio output module" ); return -1; } if ( var_Type( p_aout, "audio-channels" ) == (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) ) { /* The user may have selected a different channels configuration. */ switch( var_InheritInteger( p_aout, "audio-channels" ) ) { case AOUT_VAR_CHAN_RSTEREO: p_aout->format.i_original_channels |= AOUT_CHAN_REVERSESTEREO; break; case AOUT_VAR_CHAN_STEREO: p_aout->format.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; break; case AOUT_VAR_CHAN_LEFT: p_aout->format.i_original_channels = AOUT_CHAN_LEFT; break; case AOUT_VAR_CHAN_RIGHT: p_aout->format.i_original_channels = AOUT_CHAN_RIGHT; break; case AOUT_VAR_CHAN_DOLBYS: p_aout->format.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO; break; } } else if ( p_aout->format.i_physical_channels == AOUT_CHAN_CENTER && (p_aout->format.i_original_channels & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) ) { vlc_value_t val, text; /* Mono - create the audio-channels variable. */ var_Create( p_aout, "audio-channels", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Channels"); var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO ) { /* Go directly to the left channel. */ p_aout->format.i_original_channels = AOUT_CHAN_LEFT; var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT ); } var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, NULL ); } else if ( p_aout->format.i_physical_channels == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) && (p_aout->format.i_original_channels & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) { vlc_value_t val, text; /* Stereo - create the audio-channels variable. */ var_Create( p_aout, "audio-channels", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Channels"); var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); if ( p_aout->format.i_original_channels & AOUT_CHAN_DOLBYSTEREO ) { val.i_int = AOUT_VAR_CHAN_DOLBYS; text.psz_string = _("Dolby Surround"); } else { val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); } var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); if ( p_aout->format.i_original_channels & AOUT_CHAN_DUALMONO ) { /* Go directly to the left channel. */ p_aout->format.i_original_channels = AOUT_CHAN_LEFT; var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT ); } var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, NULL ); } var_TriggerCallback( p_aout, "intf-change" ); aout_FormatPrepare( &p_aout->format ); aout_FormatPrint( p_aout, "output", &p_aout->format ); /* Choose the mixer format. */ owner->mixer_format = p_aout->format; if (!AOUT_FMT_LINEAR(&p_aout->format)) owner->mixer_format.i_format = p_format->i_format; else /* Most audio filters can only deal with single-precision, * so lets always use that when hardware supports floating point. */ if( HAVE_FPU ) owner->mixer_format.i_format = VLC_CODEC_FL32; else /* Otherwise, audio filters will not work. Use fixed-point if the input has * more than 16-bits depth. */ if( p_format->i_bitspersample > 16 || !AOUT_FMT_LINEAR(p_format)) owner->mixer_format.i_format = VLC_CODEC_FI32; else /* Fallback to 16-bits. This avoids pointless conversion to and from * 32-bits samples for the sole purpose of software mixing. */ owner->mixer_format.i_format = VLC_CODEC_S16N; aout_FormatPrepare (&owner->mixer_format); aout_FormatPrint (p_aout, "mixer", &owner->mixer_format); /* Create filters. */ owner->nb_filters = 0; if (aout_FiltersCreatePipeline (p_aout, owner->filters, &owner->nb_filters, &owner->mixer_format, &p_aout->format) < 0) { msg_Err( p_aout, "couldn't create audio output pipeline" ); module_unneed (p_aout, owner->module); owner->module = NULL; return -1; } return 0; }
/** * 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; }