@ -39,13 +39,6 @@
# include "clock/clock.h"
# include "libvlc.h"
# define MAX_TIMING_POINT 16
struct timing_point
{
vlc_tick_t system_ts ;
vlc_tick_t audio_ts ;
} ;
struct vlc_aout_stream
{
aout_instance_t * instance ;
@ -70,25 +63,17 @@ struct vlc_aout_stream
struct
{
vlc_mutex_t lock ; /* Guard data, count and head */
/* Circular array */
struct timing_point data [ MAX_TIMING_POINT ] ;
/* Number of points in the array */
size_t count ;
/* Index of the next point to write */
size_t head ;
vlc_mutex_t lock ; /* Guard first_pts, last_drift, rate_system_ts and
rate_audio_ts */
bool running ;
vlc_tick_t last_drift ;
vlc_tick_t first_pts ;
vlc_tick_t last_pts ; /* Used for stream_TimeGet() emulation */
struct timing_point rate_point ;
vlc_tick_t rate_system_ts ;
vlc_tick_t rate_audio_ts ;
float rate ;
void ( * notify_latency_cb ) ( void * data ) ;
void * notify_latency_data ;
} timing_points ;
const char * str_id ;
@ -157,13 +142,10 @@ static void stream_Discontinuity(vlc_aout_stream *stream)
stream - > original_pts = VLC_TICK_INVALID ;
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
stream - > timing_points . head = stream - > timing_points . count = 0 ;
stream - > timing_points . first_pts = VLC_TICK_INVALID ;
stream - > timing_points . last_drift = VLC_TICK_INVALID ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
stream - > timing_points . first_pts =
stream - > timing_points . last_pts = VLC_TICK_INVALID ;
stream - > timing_points . running = false ;
}
static void stream_Reset ( vlc_aout_stream * stream )
@ -197,7 +179,7 @@ static void stream_Reset(vlc_aout_stream *stream)
}
}
stream - > timing_points . rate_point . audio_ts = VLC_TICK_INVALID ;
stream - > timing_points . rate_audio_ts = VLC_TICK_INVALID ;
stream - > timing_points . rate = 1.0 ;
atomic_store_explicit ( & stream - > drained , false , memory_order_relaxed ) ;
@ -262,12 +244,10 @@ vlc_aout_stream * vlc_aout_stream_New(audio_output_t *p_aout,
stream - > sync . clock = cfg - > clock ;
stream - > str_id = cfg - > str_id ;
stream - > timing_points . rate_point . audio_ts = VLC_TICK_INVALID ;
stream - > timing_points . rate_audio_ts = VLC_TICK_INVALID ;
stream - > timing_points . rate = 1.f ;
vlc_mutex_init ( & stream - > timing_points . lock ) ;
stream - > timing_points . notify_latency_cb = cfg - > notify_latency_cb ;
stream - > timing_points . notify_latency_data = cfg - > notify_latency_data ;
stream - > sync . rate = 1.f ;
stream - > sync . resamp_type = AOUT_RESAMPLING_NONE ;
@ -637,99 +617,30 @@ static void stream_Synchronize(vlc_aout_stream *stream, vlc_tick_t system_now,
void vlc_aout_stream_NotifyTiming ( vlc_aout_stream * stream , vlc_tick_t system_ts ,
vlc_tick_t audio_ts )
{
/* This function might be called from high priority audio threads (so, no
* mutexes , allocation , IO , debug , wait . . . ) . That is why we use a circular
* buffer of points . The vlc_aout_stream user will read these points and
* update the clock from vlc_aout_stream_Play ( ) and
* vlc_aout_stream_UpdateLatency ( ) . */
/* VLC mutexes use atomic and the reader will only do very fast
* operations ( copy of the timing_point data ) . */
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
size_t write_idx ;
if ( stream - > timing_points . count = = MAX_TIMING_POINT )
{
write_idx = stream - > timing_points . head ;
stream - > timing_points . head = ( stream - > timing_points . head + 1 )
% MAX_TIMING_POINT ;
}
else
write_idx = stream - > timing_points . count + + ;
struct timing_point * p = & stream - > timing_points . data [ write_idx ] ;
p - > system_ts = system_ts ;
p - > audio_ts = audio_ts ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
if ( stream - > timing_points . notify_latency_cb ! = NULL )
stream - > timing_points . notify_latency_cb (
stream - > timing_points . notify_latency_data ) ;
}
static vlc_tick_t
stream_ReadTimingPoints ( vlc_aout_stream * stream )
{
struct timing_point points [ MAX_TIMING_POINT ] ;
size_t count = 0 ;
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
assert ( stream - > timing_points . count < = MAX_TIMING_POINT
& & stream - > timing_points . head < MAX_TIMING_POINT ) ;
vlc_tick_t rate_audio_ts = stream - > timing_points . rate_audio_ts ;
vlc_tick_t rate_system_ts = stream - > timing_points . rate_system_ts ;
size_t initial_read = stream - > timing_points . head ;
for ( size_t read = stream - > timing_points . head ;
count < stream - > timing_points . count ; + + read , + + count )
{
points [ count ] = stream - > timing_points . data [ read % MAX_TIMING_POINT ] ;
points [ count ] . audio_ts + = stream - > timing_points . first_pts ;
}
stream - > timing_points . count = stream - > timing_points . head = 0 ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
if ( count = = 0 )
return 0 ;
audio_ts + = stream - > timing_points . first_pts ;
if ( initial_read > 0 )
if ( rate_audio_ts ! = VLC_TICK_INVALID )
{
/* This is not critical to miss timing points, the more important is
* to get the last ones . Log it anyway since it can help identifying
* buggy audio outputs spamming timing points . */
audio_output_t * aout = aout_stream_aout ( stream ) ;
msg_Dbg ( aout , " Missed %zu timing points " , initial_read ) ;
}
vlc_tick_t drift = 0 ; /* Use only the last updated drift */
for ( size_t i = 0 ; i < count ; + + i )
{
struct timing_point * tp = & points [ i ] ;
struct timing_point * rp = & stream - > timing_points . rate_point ;
if ( rp - > audio_ts ! = VLC_TICK_INVALID )
/* Drop timing updates that comes before the rate change */
if ( system_ts < rate_system_ts )
{
/* Drop timing updates that comes before the rate change */
if ( tp - > system_ts < rp - > system_ts )
continue ;
/* Fix the audio timestamp with the rate */
tp - > audio_ts = rp - > audio_ts + ( tp - > system_ts - rp - > system_ts )
* stream - > timing_points . rate ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
return ;
}
drift = vlc_clock_Update ( stream - > sync . clock , points [ i ] . system_ts ,
points [ i ] . audio_ts , stream - > timing_points . rate ) ;
/* Fix the audio timestamp with the rate */
audio_ts = rate_audio_ts + ( system_ts - rate_system_ts )
* stream - > timing_points . rate ;
}
return drift ;
}
void vlc_aout_stream_UpdateLatency ( vlc_aout_stream * stream )
{
if ( stream - > timing_points . running )
stream_ReadTimingPoints ( stream ) ;
stream - > timing_points . last_drift =
vlc_clock_Update ( stream - > sync . clock , system_ts ,
audio_ts , stream - > timing_points . rate ) ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
}
/*****************************************************************************
@ -800,7 +711,10 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
stream_Synchronize ( stream , system_now , original_pts ) ;
else
{
vlc_tick_t drift = stream_ReadTimingPoints ( stream ) ;
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
vlc_tick_t drift = stream - > timing_points . last_drift ;
stream - > timing_points . last_drift = VLC_TICK_INVALID ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
stream_HandleDrift ( stream , drift , original_pts ) ;
}
@ -817,36 +731,29 @@ int vlc_aout_stream_Play(vlc_aout_stream *stream, block_t *block)
vlc_audio_meter_Process ( & owner - > meter , block , play_date ) ;
if ( ! stream - > timing_points . running )
{
stream - > timing_points . running = true ;
/* Assert that no timing points are updated between flush and play */
# ifndef NDEBUG
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
assert ( stream - > timing_points . count = = 0 ) ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
# endif
}
if ( aout - > time_get = = NULL
& & stream - > sync . rate ! = stream - > timing_points . rate )
{
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
/* Save the first timing point seeing a rate change */
stream - > timing_points . rate_point = ( struct timing_point ) {
. system_ts = play_date ,
. audio_ts = original_pts ,
} ;
stream - > timing_points . rate_system_ts = play_date ;
stream - > timing_points . rate_audio_ts = original_pts ;
stream - > timing_points . rate = stream - > sync . rate ;
/* Update the clock immediately with the new rate, instead of waiting
* for a timing update that could come too late ( after 1 second ) . */
vlc_clock_Update ( stream - > sync . clock , play_date ,
original_pts , stream - > sync . rate ) ;
stream - > timing_points . last_drift =
vlc_clock_Update ( stream - > sync . clock , play_date , original_pts ,
stream - > sync . rate ) ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
}
if ( stream - > timing_points . first_pts = = VLC_TICK_INVALID )
{
vlc_mutex_lock ( & stream - > timing_points . lock ) ;
stream - > timing_points . first_pts = original_pts ;
vlc_mutex_unlock ( & stream - > timing_points . lock ) ;
}
stream - > timing_points . last_pts = original_pts ;
/* Output */
@ -889,13 +796,13 @@ void vlc_aout_stream_ChangePause(vlc_aout_stream *stream, bool paused, vlc_tick_
/* Update the rate point after the pause */
if ( aout - > time_get = = NULL & & ! paused
& & stream - > timing_points . rate_point . audio_ts ! = VLC_TICK_INVALID )
& & stream - > timing_points . rate_audio_ts ! = VLC_TICK_INVALID )
{
vlc_tick_t play_date =
vlc_clock_ConvertToSystem ( stream - > sync . clock , date ,
stream - > timing_points . rate_point . audio_ts ,
stream - > timing_points . rate_audio_ts ,
stream - > sync . rate ) ;
stream - > timing_points . rate_point . system_ts = play_date ;
stream - > timing_points . rate_system_ts = play_date ;
}
}
}