Skip to content

Commit

Permalink
audiounit: poll latency when updating timings
Browse files Browse the repository at this point in the history
Because AVAudioSessionRouteChangeNotification is not triggered when
switching Speaker <-> Airplay2 (but it is triggered for BT <-> Anything).

Fixes A/V sync with AirPlay2 when Airplay is changed during playback.
  • Loading branch information
tguillem authored and jbkempf committed Dec 2, 2022
1 parent 8cf10ff commit 9793d06
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 26 deletions.
34 changes: 24 additions & 10 deletions modules/audio_output/audiounit_ios.m
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ - (NSInteger)removeAoutInstance:(AoutWrapper *)wrapperInstance
bool b_spatial_audio_supported;
enum au_dev au_dev;

/* For debug purpose, to print when specific latency changed */
vlc_tick_t output_latency_ticks;
vlc_tick_t io_buffer_duration_ticks;

/* sw gain */
float soft_gain;
bool soft_mute;
Expand All @@ -161,17 +165,29 @@ - (NSInteger)removeAoutInstance:(AoutWrapper *)wrapperInstance

Float64 unit_s;
vlc_tick_t latency_us = 0, us;
bool changed = false;

us = vlc_tick_from_sec([p_sys->avInstance outputLatency]);
msg_Dbg(p_aout, "Current device has a outputLatency of %" PRId64 "us", us);
if (us != p_sys->output_latency_ticks)
{
msg_Dbg(p_aout, "Current device has a new outputLatency of %" PRId64 "us", us);
p_sys->output_latency_ticks = us;
changed = true;
}
latency_us += us;

us = vlc_tick_from_sec([p_sys->avInstance IOBufferDuration]);
msg_Dbg(p_aout, "Current device has a IOBufferDuration of %" PRId64 "us", us);
if (us != p_sys->io_buffer_duration_ticks)
{
msg_Dbg(p_aout, "Current device has a new IOBufferDuration of %" PRId64 "us", us);
p_sys->io_buffer_duration_ticks = us;
changed = true;
}
latency_us += us;

msg_Dbg(p_aout, "Current device has a total latency of %" PRId64 "us",
latency_us);
if (changed)
msg_Dbg(p_aout, "Current device has a new total latency of %" PRId64 "us",
latency_us);
return latency_us;
}

Expand Down Expand Up @@ -202,10 +218,7 @@ - (void)audioSessionRouteChange:(NSNotification *)notification
|| routeChangeReason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable)
aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
else
{
const vlc_tick_t latency_us = GetLatency(p_aout);
ca_SetDeviceLatency(p_aout, latency_us);
}
ca_ResetDeviceLatency(p_aout);
}

- (void)handleInterruption:(NSNotification *)notification
Expand Down Expand Up @@ -539,6 +552,8 @@ static int role2policy_cmp(const void *key, const void *val)
aout_FormatPrint(p_aout, "VLC is looking for:", fmt);

p_sys->au_unit = NULL;
p_sys->output_latency_ticks = 0;
p_sys->io_buffer_duration_ticks = 0;

NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:p_sys->aoutWrapper
Expand Down Expand Up @@ -599,8 +614,7 @@ static int role2policy_cmp(const void *key, const void *val)
if (err != noErr)
ca_LogWarn("failed to set IO mode");

const vlc_tick_t latency_us = GetLatency(p_aout);
ret = au_Initialize(p_aout, p_sys->au_unit, fmt, NULL, latency_us, NULL);
ret = au_Initialize(p_aout, p_sys->au_unit, fmt, NULL, 0, GetLatency, NULL);
if (ret != VLC_SUCCESS)
goto error;

Expand Down
4 changes: 2 additions & 2 deletions modules/audio_output/auhal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt,
/* Do the last VLC aout setups */
bool warn_configuration;
int ret = au_Initialize(p_aout, p_sys->au_unit, fmt, layout, latency_us,
&warn_configuration);
NULL, &warn_configuration);
if (ret != VLC_SUCCESS)
goto error;

Expand Down Expand Up @@ -1365,7 +1365,7 @@ StartSPDIF(audio_output_t * p_aout, audio_sample_format_t *fmt)
return VLC_EGENERIC;
}

ret = ca_Initialize(p_aout, fmt, 0);
ret = ca_Initialize(p_aout, fmt, 0, NULL);
if (ret != VLC_SUCCESS)
{
AudioDeviceDestroyIOProcID(p_sys->i_selected_dev, p_sys->i_procID);
Expand Down
32 changes: 21 additions & 11 deletions modules/audio_output/coreaudio_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,14 @@ ca_Open(audio_output_t *p_aout)
return VLC_SUCCESS;
}

static vlc_tick_t
GetLatency(audio_output_t *p_aout)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
return p_sys->get_latency != NULL ? p_sys->get_latency(p_aout)
: p_sys->i_dev_latency_ticks;
}

/* Called from render callbacks. No lock, wait, and IO here */
void
ca_Render(audio_output_t *p_aout, uint64_t host_time,
Expand All @@ -148,9 +156,7 @@ ca_Render(audio_output_t *p_aout, uint64_t host_time,
const vlc_tick_t bytes_ticks = BytesToTicks(p_sys, bytes);

const vlc_tick_t now_ticks = vlc_tick_now();
const vlc_tick_t end_ticks = now_ticks + bytes_ticks
+ host_delay_ticks
+ p_sys->i_dev_latency_ticks;
const vlc_tick_t end_ticks = now_ticks + bytes_ticks + host_delay_ticks;

lock_lock(p_sys);

Expand All @@ -168,7 +174,7 @@ ca_Render(audio_output_t *p_aout, uint64_t host_time,
{
/* Write silence to reach the first play date */
vlc_tick_t silence_ticks = p_sys->first_play_date - end_ticks
+ bytes_ticks;
- GetLatency(p_aout) + bytes_ticks;
if (silence_ticks > 0)
{
tocopy = TicksToBytes(p_sys, silence_ticks);
Expand Down Expand Up @@ -208,7 +214,7 @@ ca_Render(audio_output_t *p_aout, uint64_t host_time,
{
p_sys->timing_report_last_written_bytes = 0;
vlc_tick_t pos_ticks = BytesToTicks(p_sys, p_sys->i_total_bytes);
aout_TimingReport(p_aout, end_ticks, pos_ticks);
aout_TimingReport(p_aout, end_ticks + GetLatency(p_aout), pos_ticks);
}
else
p_sys->timing_report_last_written_bytes += tocopy;
Expand Down Expand Up @@ -329,7 +335,7 @@ ca_Play(audio_output_t * p_aout, block_t * p_block, vlc_tick_t date)

int
ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
vlc_tick_t i_dev_latency_ticks)
vlc_tick_t i_dev_latency_ticks, get_latency_cb get_latency)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;

Expand All @@ -346,7 +352,10 @@ ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame;
p_sys->i_frame_length = fmt->i_frame_length;

p_sys->i_dev_latency_ticks = i_dev_latency_ticks;
if (get_latency != NULL)
p_sys->get_latency = get_latency;
else
p_sys->i_dev_latency_ticks = i_dev_latency_ticks;
p_sys->timing_report_delay_bytes = TicksToBytes(p_sys, TIMING_REPORT_DELAY_TICKS);

ca_ClearOutBuffers(p_aout);
Expand All @@ -373,12 +382,13 @@ ca_SetAliveState(audio_output_t *p_aout, bool alive)
lock_unlock(p_sys);
}

void ca_SetDeviceLatency(audio_output_t *p_aout, vlc_tick_t i_dev_latency_ticks)
void ca_ResetDeviceLatency(audio_output_t *p_aout)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;

lock_lock(p_sys);
p_sys->i_dev_latency_ticks = i_dev_latency_ticks;
/* Trigger aout_TimingReport() to be called from the next render callback */
p_sys->timing_report_last_written_bytes = p_sys->timing_report_delay_bytes;
lock_unlock(p_sys);
}

Expand Down Expand Up @@ -713,7 +723,7 @@ MapInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
int
au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
const AudioChannelLayout *outlayout, vlc_tick_t i_dev_latency_ticks,
bool *warn_configuration)
get_latency_cb get_latency, bool *warn_configuration)
{
int ret;
AudioChannelLayout *inlayout_buf = NULL;
Expand Down Expand Up @@ -836,7 +846,7 @@ au_Initialize(audio_output_t *p_aout, AudioUnit au, audio_sample_format_t *fmt,
return VLC_EGENERIC;
}

ret = ca_Initialize(p_aout, fmt, i_dev_latency_ticks);
ret = ca_Initialize(p_aout, fmt, i_dev_latency_ticks, get_latency);
if (ret != VLC_SUCCESS)
{
AudioUnitUninitialize(au);
Expand Down
9 changes: 6 additions & 3 deletions modules/audio_output/coreaudio_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#define ca_LogErr(fmt) msg_Err(p_aout, fmt ", OSStatus: %d", (int) err)
#define ca_LogWarn(fmt) msg_Warn(p_aout, fmt ", OSStatus: %d", (int) err)

typedef vlc_tick_t (*get_latency_cb)(audio_output_t *);

struct aout_sys_common
{
/* The following is owned by common.c (initialized from ca_Open) */
Expand Down Expand Up @@ -86,6 +88,7 @@ struct aout_sys_common
uint8_t chan_table[AOUT_CHAN_MAX];
/* ca_TimeGet extra latency, in vlc ticks */
vlc_tick_t i_dev_latency_ticks;
get_latency_cb get_latency;
};

int ca_Open(audio_output_t *p_aout);
Expand All @@ -104,20 +107,20 @@ void ca_MuteSet(audio_output_t * p_aout, bool mute);
void ca_Play(audio_output_t * p_aout, block_t * p_block, vlc_tick_t date);

int ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
vlc_tick_t i_dev_latency_ticks);
vlc_tick_t i_dev_latency_ticks, get_latency_cb get_latency);

void ca_Uninitialize(audio_output_t *p_aout);

void ca_SetAliveState(audio_output_t *p_aout, bool alive);

void ca_SetDeviceLatency(audio_output_t *p_aout, vlc_tick_t i_dev_latency_ticks);
void ca_ResetDeviceLatency(audio_output_t *p_aout);

AudioUnit au_NewOutputInstance(audio_output_t *p_aout, OSType comp_sub_type);

int au_Initialize(audio_output_t *p_aout, AudioUnit au,
audio_sample_format_t *fmt,
const AudioChannelLayout *outlayout, vlc_tick_t i_dev_latency_ticks,
bool *warn_configuration);
get_latency_cb get_latency, bool *warn_configuration);

void au_Uninitialize(audio_output_t *p_aout, AudioUnit au);

Expand Down

0 comments on commit 9793d06

Please sign in to comment.