Commit 5138b463 authored by Rémi Denis-Courmont's avatar Rémi Denis-Courmont

aout: inline synchronization code into dec.c

parent c96fa88a
...@@ -66,7 +66,6 @@ src/audio_output/aout_internal.h ...@@ -66,7 +66,6 @@ src/audio_output/aout_internal.h
src/audio_output/common.c src/audio_output/common.c
src/audio_output/dec.c src/audio_output/dec.c
src/audio_output/filters.c src/audio_output/filters.c
src/audio_output/input.c
src/audio_output/output.c src/audio_output/output.c
src/config/chain.c src/config/chain.c
src/config/cmdline.c src/config/cmdline.c
......
...@@ -413,7 +413,6 @@ SOURCES_libvlc_common = \ ...@@ -413,7 +413,6 @@ SOURCES_libvlc_common = \
audio_output/common.c \ audio_output/common.c \
audio_output/dec.c \ audio_output/dec.c \
audio_output/filters.c \ audio_output/filters.c \
audio_output/input.c \
audio_output/output.c \ audio_output/output.c \
audio_output/volume.c \ audio_output/volume.c \
network/getaddrinfo.c \ network/getaddrinfo.c \
......
...@@ -46,26 +46,17 @@ typedef struct ...@@ -46,26 +46,17 @@ typedef struct
typedef struct aout_volume aout_volume_t; typedef struct aout_volume aout_volume_t;
/** an input stream for the audio output */
struct aout_input_t
{
int i_resampling_type;
int i_resamp_start_drift;
/* */
int i_buffer_lost;
};
typedef struct typedef struct
{ {
vlc_mutex_t lock; vlc_mutex_t lock;
module_t *module; /**< Output plugin (or NULL if inactive) */ module_t *module; /**< Output plugin (or NULL if inactive) */
aout_input_t *input;
aout_volume_t *volume; aout_volume_t *volume;
struct struct
{ {
date_t date; date_t date;
int resamp_type;
int resamp_start_drift;
} sync; } sync;
audio_sample_format_t input_format; audio_sample_format_t input_format;
...@@ -84,6 +75,8 @@ typedef struct ...@@ -84,6 +75,8 @@ typedef struct
aout_request_vout_t request_vout; aout_request_vout_t request_vout;
bool recycle_vout; bool recycle_vout;
unsigned buffers_lost;
vlc_atomic_t restart; vlc_atomic_t restart;
} aout_owner_t; } aout_owner_t;
...@@ -102,12 +95,6 @@ static inline aout_owner_t *aout_owner (audio_output_t *aout) ...@@ -102,12 +95,6 @@ static inline aout_owner_t *aout_owner (audio_output_t *aout)
* Prototypes * Prototypes
*****************************************************************************/ *****************************************************************************/
/* From input.c : */
aout_input_t *aout_InputNew(void);
void aout_InputDelete(aout_input_t *);
block_t *aout_InputPlay( audio_output_t *p_aout, aout_input_t *p_input,
block_t *p_buffer, int i_input_rate, date_t * );
/* From filters.c : */ /* From filters.c : */
int aout_FiltersPipelineCreate(vlc_object_t *, filter_t **, unsigned *, int aout_FiltersPipelineCreate(vlc_object_t *, filter_t **, unsigned *,
unsigned, const audio_sample_format_t *, const audio_sample_format_t *); unsigned, const audio_sample_format_t *, const audio_sample_format_t *);
......
...@@ -95,9 +95,10 @@ error: ...@@ -95,9 +95,10 @@ error:
date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1); date_Init (&owner->sync.date, owner->mixer_format.i_rate, 1);
date_Set (&owner->sync.date, VLC_TS_INVALID); date_Set (&owner->sync.date, VLC_TS_INVALID);
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
owner->buffers_lost = 0;
assert (owner->input == NULL);
owner->input = aout_InputNew ();
aout_unlock( p_aout ); aout_unlock( p_aout );
return ret; return ret;
} }
...@@ -108,14 +109,8 @@ error: ...@@ -108,14 +109,8 @@ error:
void aout_DecDelete (audio_output_t *p_aout) void aout_DecDelete (audio_output_t *p_aout)
{ {
aout_owner_t *owner = aout_owner (p_aout); aout_owner_t *owner = aout_owner (p_aout);
aout_input_t *input;
aout_lock( p_aout ); aout_lock( p_aout );
/* Remove the input. */
input = owner->input;
aout_InputDelete (input);
owner->input = NULL;
aout_FiltersDelete (p_aout); aout_FiltersDelete (p_aout);
aout_OutputDelete( p_aout ); aout_OutputDelete( p_aout );
aout_volume_Delete (owner->volume); aout_volume_Delete (owner->volume);
...@@ -127,7 +122,7 @@ void aout_DecDelete (audio_output_t *p_aout) ...@@ -127,7 +122,7 @@ void aout_DecDelete (audio_output_t *p_aout)
#define AOUT_RESTART_OUTPUT 1 #define AOUT_RESTART_OUTPUT 1
#define AOUT_RESTART_INPUT 2 #define AOUT_RESTART_INPUT 2
static void aout_CheckRestart (audio_output_t *aout) static int aout_CheckRestart (audio_output_t *aout)
{ {
aout_owner_t *owner = aout_owner (aout); aout_owner_t *owner = aout_owner (aout);
...@@ -135,15 +130,12 @@ static void aout_CheckRestart (audio_output_t *aout) ...@@ -135,15 +130,12 @@ static void aout_CheckRestart (audio_output_t *aout)
int restart = vlc_atomic_swap (&owner->restart, 0); int restart = vlc_atomic_swap (&owner->restart, 0);
if (likely(restart == 0)) if (likely(restart == 0))
return; return 0;
assert (restart & AOUT_RESTART_INPUT); assert (restart & AOUT_RESTART_INPUT);
const aout_request_vout_t request_vout = owner->request_vout; const aout_request_vout_t request_vout = owner->request_vout;
aout_InputDelete (owner->input);
owner->input = NULL;
aout_FiltersDelete (aout); aout_FiltersDelete (aout);
/* Reinitializes the output */ /* Reinitializes the output */
...@@ -151,16 +143,18 @@ static void aout_CheckRestart (audio_output_t *aout) ...@@ -151,16 +143,18 @@ static void aout_CheckRestart (audio_output_t *aout)
{ {
aout_OutputDelete (aout); aout_OutputDelete (aout);
if (aout_OutputNew (aout, &owner->input_format)) if (aout_OutputNew (aout, &owner->input_format))
{ abort (); /* FIXME we are officially screwed */
aout_volume_Delete (owner->volume);
return; /* we are officially screwed */
}
aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format); aout_volume_SetFormat (owner->volume, owner->mixer_format.i_format);
} }
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
if (aout_FiltersNew (aout, &owner->input_format, &owner->mixer_format, if (aout_FiltersNew (aout, &owner->input_format, &owner->mixer_format,
&request_vout) == 0) &request_vout))
owner->input = aout_InputNew (); {
abort (); /* FIXME */
}
return 0;
} }
/** /**
...@@ -235,64 +229,175 @@ void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block) ...@@ -235,64 +229,175 @@ void aout_DecDeleteBuffer (audio_output_t *aout, block_t *block)
block_Release (block); block_Release (block);
} }
static void aout_StopResampling (audio_output_t *aout)
{
aout_owner_t *owner = aout_owner (aout);
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
aout_FiltersAdjustResampling (aout, 0);
}
/***************************************************************************** /*****************************************************************************
* aout_DecPlay : filter & mix the decoded buffer * aout_DecPlay : filter & mix the decoded buffer
*****************************************************************************/ *****************************************************************************/
int aout_DecPlay (audio_output_t *p_aout, block_t *p_buffer, int i_input_rate) int aout_DecPlay (audio_output_t *aout, block_t *block, int input_rate)
{ {
aout_owner_t *owner = aout_owner (p_aout); aout_owner_t *owner = aout_owner (aout);
aout_input_t *input;
assert( i_input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE && assert (input_rate >= INPUT_RATE_DEFAULT / AOUT_MAX_INPUT_RATE);
i_input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE ); assert (input_rate <= INPUT_RATE_DEFAULT * AOUT_MAX_INPUT_RATE);
assert( p_buffer->i_pts > 0 ); assert (block->i_pts >= VLC_TS_0);
p_buffer->i_length = (mtime_t)p_buffer->i_nb_samples * 1000000 block->i_length = CLOCK_FREQ * block->i_nb_samples
/ owner->input_format.i_rate; / owner->input_format.i_rate;
aout_lock( p_aout ); aout_lock (aout);
aout_CheckRestart( p_aout ); if (unlikely(aout_CheckRestart (aout)))
goto drop; /* Pipeline is unrecoverably broken :-( */
/* 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. */
mtime_t start_date = date_Get (&owner->sync.date);
const mtime_t now = mdate ();
if (start_date != VLC_TS_INVALID && start_date < now)
{ /* 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 (aout, "computed PTS is out of range (%"PRId64"), "
"clearing out", now - start_date);
aout_OutputFlush (aout, false);
if (owner->sync.resamp_type != AOUT_RESAMPLING_NONE)
msg_Warn (aout, "timing screwed, stopping resampling");
aout_StopResampling (aout);
block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
start_date = VLC_TS_INVALID;
}
input = owner->input; if (block->i_pts < now + AOUT_MIN_PREPARE_TIME)
if (unlikely(input == NULL)) /* can happen due to restart */ { /* 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 (aout, "PTS is out of range (%"PRId64"), dropping buffer",
now - block->i_pts);
aout_StopResampling (aout);
goto drop;
}
/* If the audio drift is too big then it's not worth trying to resample
* the audio. */
if (start_date == VLC_TS_INVALID)
{ {
aout_unlock( p_aout ); start_date = block->i_pts;
block_Release( p_buffer ); date_Set (&owner->sync.date, start_date);
return -1;
} }
/* Input */ mtime_t drift = start_date - block->i_pts;
p_buffer = aout_InputPlay (p_aout, input, p_buffer, i_input_rate, if (drift < -input_rate * 3 * AOUT_MAX_PTS_ADVANCE / INPUT_RATE_DEFAULT)
&owner->sync.date);
if( p_buffer != NULL )
{ {
date_Increment (&owner->sync.date, p_buffer->i_nb_samples); msg_Warn (aout, "buffer way too early (%"PRId64"), clearing queue",
drift);
aout_OutputFlush (aout, false);
if (owner->sync.resamp_type != AOUT_RESAMPLING_NONE)
msg_Warn (aout, "timing screwed, stopping resampling");
aout_StopResampling (aout);
block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
start_date = block->i_pts;
date_Set (&owner->sync.date, start_date);
drift = 0;
}
else
if (drift > +input_rate * 3 * AOUT_MAX_PTS_DELAY / INPUT_RATE_DEFAULT)
{
msg_Warn (aout, "buffer way too late (%"PRId64"), dropping buffer",
drift);
goto drop;
}
/* Mixer */ block = aout_FiltersPlay (aout, block, input_rate);
aout_volume_Amplify (owner->volume, p_buffer); if (block == NULL)
{
owner->buffers_lost++;
goto out;
}
/* Output */ /* Adjust the resampler if needed.
aout_OutputPlay( p_aout, p_buffer ); * We first need to calculate the output rate of this resampler. */
if ((owner->sync.resamp_type == AOUT_RESAMPLING_NONE)
&& (drift < -AOUT_MAX_PTS_ADVANCE || drift > +AOUT_MAX_PTS_DELAY))
{ /* 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.
*/
owner->sync.resamp_start_drift = (int)-drift;
owner->sync.resamp_type = (drift < 0) ? AOUT_RESAMPLING_DOWN
: AOUT_RESAMPLING_UP;
msg_Warn (aout, (drift < 0)
? "buffer too early (%"PRId64"), down-sampling"
: "buffer too late (%"PRId64"), up-sampling", drift);
}
if (owner->sync.resamp_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. */
const int adjust = (owner->sync.resamp_type == AOUT_RESAMPLING_UP)
? +2 : -2;
/* Check if everything is back to normal, then stop resampling. */
if (!aout_FiltersAdjustResampling (aout, adjust))
{
owner->sync.resamp_type = AOUT_RESAMPLING_NONE;
msg_Warn (aout, "resampling stopped (drift: %"PRIi64")",
block->i_pts - start_date);
}
else if (abs ((int)(block->i_pts - start_date))
< abs (owner->sync.resamp_start_drift) / 2)
{ /* If we reduced the drift from half, then it is time to switch
* back the resampling direction. */
if (owner->sync.resamp_type == AOUT_RESAMPLING_UP)
owner->sync.resamp_type = AOUT_RESAMPLING_DOWN;
else
owner->sync.resamp_type = AOUT_RESAMPLING_UP;
owner->sync.resamp_start_drift = 0;
}
else if (owner->sync.resamp_start_drift
&& (abs ((int)(block->i_pts - start_date))
> abs (owner->sync.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 (aout, "timing screwed, stopping resampling");
aout_StopResampling (aout);
block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
}
} }
aout_unlock( p_aout ); block->i_pts = start_date;
date_Increment (&owner->sync.date, block->i_nb_samples);
/* Software volume */
aout_volume_Amplify (owner->volume, block);
/* Output */
aout_OutputPlay (aout, block);
out:
aout_unlock (aout);
return 0; return 0;
drop:
block_Release (block);
owner->buffers_lost++;
goto out;
} }
int aout_DecGetResetLost (audio_output_t *aout) int aout_DecGetResetLost (audio_output_t *aout)
{ {
aout_owner_t *owner = aout_owner (aout); aout_owner_t *owner = aout_owner (aout);
aout_input_t *input = owner->input; unsigned val;
int val;
aout_lock (aout); aout_lock (aout);
if (likely(input != NULL)) val = owner->buffers_lost;
{ owner->buffers_lost = 0;
val = input->i_buffer_lost;
input->i_buffer_lost = 0;
}
else
val = 0; /* if aout_CheckRestart() failed */
aout_unlock (aout); aout_unlock (aout);
return val; return val;
......
/*****************************************************************************
* input.c : internal management of input streams for the audio output
*****************************************************************************
* Copyright (C) 2002-2007 VLC authors and VideoLAN
* $Id$
*
* Authors: Christophe Massiot <massiot@via.ecp.fr>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_input.h>
#include <vlc_aout.h>
#include <libvlc.h>
#include "aout_internal.h"
static void inputDrop( aout_input_t *, block_t * );
static void inputResamplingStop( audio_output_t *, aout_input_t * );
/*****************************************************************************
* aout_InputNew : allocate a new input and rework the filter pipeline
*****************************************************************************/
aout_input_t *aout_InputNew (void)
{
aout_input_t *p_input = xmalloc (sizeof (*p_input));
p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
p_input->i_buffer_lost = 0;
return p_input;
}
/*****************************************************************************
* aout_InputDelete : delete an input
*****************************************************************************
* This function must be entered with the mixer lock.
*****************************************************************************/
void aout_InputDelete (aout_input_t * p_input )
{
free (p_input);
}
/*****************************************************************************
* aout_InputPlay : play a buffer
*****************************************************************************
* This function must be entered with the input lock.
*****************************************************************************/
block_t *aout_InputPlay(audio_output_t *p_aout, aout_input_t *p_input,
block_t *p_buffer, int i_input_rate, date_t *date )
{
mtime_t start_date;
aout_assert_locked( p_aout );
mtime_t now = mdate();
/* 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. */
start_date = date_Get (date);
if ( start_date != VLC_TS_INVALID && start_date < now )
{
/* 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", now - start_date );
aout_OutputFlush( p_aout, false );
if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
msg_Warn( p_aout, "timing screwed, stopping resampling" );
inputResamplingStop( p_aout, p_input );
p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
start_date = VLC_TS_INVALID;
}
if ( p_buffer->i_pts < now + 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",
now - p_buffer->i_pts );
inputDrop( p_input, p_buffer );
inputResamplingStop( p_aout, p_input );
return NULL;
}
/* If the audio drift is too big then it's not worth trying to resample
* the audio. */
if( start_date == VLC_TS_INVALID )
{
start_date = p_buffer->i_pts;
date_Set (date, start_date);
}
mtime_t drift = start_date - p_buffer->i_pts;
if( drift < -i_input_rate * 3 * AOUT_MAX_PTS_ADVANCE / INPUT_RATE_DEFAULT )
{
msg_Warn( p_aout, "buffer way too early (%"PRId64"), clearing queue",
drift );
aout_OutputFlush( p_aout, false );
if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE )
msg_Warn( p_aout, "timing screwed, stopping resampling" );
inputResamplingStop( p_aout, p_input );
p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
start_date = p_buffer->i_pts;
date_Set (date, start_date);
drift = 0;
}
else
if( drift > +i_input_rate * 3 * AOUT_MAX_PTS_DELAY / INPUT_RATE_DEFAULT )
{
msg_Warn( p_aout, "buffer way too late (%"PRId64"), dropping buffer",
drift );
inputDrop( p_input, p_buffer );
return NULL;
}
p_buffer = aout_FiltersPlay( p_aout, p_buffer, i_input_rate );
if( !p_buffer )
return NULL;
/* 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 ) &&
( drift < -AOUT_MAX_PTS_ADVANCE || drift > +AOUT_MAX_PTS_DELAY ) )
{
/* 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.
*/
p_input->i_resamp_start_drift = (int)-drift;
p_input->i_resampling_type = (drift < 0) ? AOUT_RESAMPLING_DOWN
: AOUT_RESAMPLING_UP;
msg_Warn( p_aout, (drift < 0)
? "buffer too early (%"PRId64"), down-sampling"
: "buffer too late (%"PRId64"), up-sampling", drift );
}
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. */
const int adjust = ( p_input->i_resampling_type == AOUT_RESAMPLING_UP )
? +2 : -2;
/* Check if everything is back to normal, in which case we can stop the
* resampling */
if( !aout_FiltersAdjustResampling( p_aout, adjust ) )
{
p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
msg_Warn( p_aout, "resampling stopped (drift: %"PRIi64")",
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_aout, p_input );
p_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY;
}
}
p_buffer->i_pts = start_date;
return p_buffer;
}
/*****************************************************************************
* static functions
*****************************************************************************/
static void inputDrop( aout_input_t *p_input, block_t *p_buffer )
{
block_Release( p_buffer );
p_input->i_buffer_lost++;
}
static void inputResamplingStop( audio_output_t *p_aout, aout_input_t *p_input )
{
p_input->i_resampling_type = AOUT_RESAMPLING_NONE;
aout_FiltersAdjustResampling( p_aout, 0 );
}
...@@ -96,8 +96,6 @@ audio_output_t *aout_New (vlc_object_t *parent) ...@@ -96,8 +96,6 @@ audio_output_t *aout_New (vlc_object_t *parent)
vlc_mutex_init (&owner->lock); vlc_mutex_init (&owner->lock);
vlc_object_set_destructor (aout, aout_Destructor); vlc_object_set_destructor (aout, aout_Destructor);
owner->input = NULL;
/* Audio output module callbacks */ /* Audio output module callbacks */
var_Create (aout, "volume", VLC_VAR_FLOAT); var_Create (aout, "volume", VLC_VAR_FLOAT);
var_AddCallback (aout, "volume", var_Copy, parent); var_AddCallback (aout, "volume", var_Copy, parent);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment