1
mirror of https://code.videolan.org/videolan/vlc synced 2024-07-21 07:24:15 +02:00

coreaudio: fix unlikely but possible deadlock on pause

The pause state need to be known by ca_Play in order to don't wait indefinitely
when the RenderCallback is paused. A deadlock could happen with asynchronous
audio decoders.
This commit is contained in:
Thomas Guillem 2017-02-24 17:40:33 +01:00
parent d04c135dcb
commit 61686a07d1
4 changed files with 37 additions and 36 deletions

View File

@ -103,7 +103,6 @@ avas_SetActive(audio_output_t *p_aout, bool active, NSUInteger options)
static void
Pause (audio_output_t *p_aout, bool pause, mtime_t date)
{
VLC_UNUSED(date);
struct aout_sys_t * p_sys = p_aout->sys;
/* We need to start / stop the audio unit here because otherwise the OS
@ -127,10 +126,16 @@ Pause (audio_output_t *p_aout, bool pause, mtime_t date)
{
err = AudioOutputUnitStart(p_sys->au_unit);
if (err != noErr)
{
msg_Err(p_aout, "AudioOutputUnitStart failed [%4.4s]",
(const char *) &err);
/* Do not un-pause, the Render Callback won't run, and next call
* of ca_Play will deadlock */
return;
}
}
}
ca_Pause(p_aout, pause, date);
}
static int

View File

@ -121,7 +121,6 @@ struct aout_sys_t
float f_volume;
bool b_mute;
atomic_bool b_paused;
bool b_ignore_streams_changed_callback;
};
@ -928,15 +927,8 @@ RenderCallbackAnalog(void *p_data, AudioUnitRenderActionFlags *ioActionFlags,
VLC_UNUSED(inBusNumber);
VLC_UNUSED(inNumberFrames);
audio_output_t * p_aout = p_data;
aout_sys_t *p_sys = p_aout->sys;
uint8_t *p_output = ioData->mBuffers[0].mData;
size_t i_size = ioData->mBuffers[0].mDataByteSize;
if (!atomic_load(&p_sys->b_paused))
ca_Render(p_aout, p_output, i_size);
else
memset(p_output, 0, i_size);
ca_Render(p_data, ioData->mBuffers[0].mData,
ioData->mBuffers[0].mDataByteSize);
return noErr;
}
@ -962,10 +954,7 @@ RenderCallbackSPDIF(AudioDeviceID inDevice, const AudioTimeStamp * inNow,
uint8_t *p_output = outOutputData->mBuffers[p_sys->i_stream_index].mData;
size_t i_size = outOutputData->mBuffers[p_sys->i_stream_index].mDataByteSize;
if (!atomic_load(&p_sys->b_paused))
ca_Render(p_data, p_output, i_size);
else
memset(p_output, 0, i_size);
ca_Render(p_aout, p_output, i_size);
return noErr;
}
@ -973,15 +962,6 @@ RenderCallbackSPDIF(AudioDeviceID inDevice, const AudioTimeStamp * inNow,
#pragma mark -
#pragma mark initialization
static void
Pause(audio_output_t *p_aout, bool pause, mtime_t date)
{
struct aout_sys_t * p_sys = p_aout->sys;
VLC_UNUSED(date);
atomic_store(&p_sys->b_paused, pause);
}
/*
* StartAnalog: open and setup a HAL AudioUnit to do PCM audio output
*/
@ -1811,7 +1791,6 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
p_sys->i_stream_index = -1;
p_sys->b_revert = false;
p_sys->b_changed_mixing = false;
p_sys->b_paused = false;
p_sys->c.i_device_latency = 0;
vlc_mutex_lock(&p_sys->selected_device_lock);
@ -1928,15 +1907,13 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
p_sys->c.i_device_latency = 0;
}
bool b_success = false;
/* Check for Digital mode or Analog output mode */
if (AOUT_FMT_SPDIF (fmt) && b_start_digital)
{
if (StartSPDIF (p_aout, fmt) == VLC_SUCCESS)
{
msg_Dbg(p_aout, "digital output successfully opened");
b_success = true;
return VLC_SUCCESS;
}
}
else
@ -1944,16 +1921,10 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
if (StartAnalog(p_aout, fmt) == VLC_SUCCESS)
{
msg_Dbg(p_aout, "analog output successfully opened");
b_success = true;
return VLC_SUCCESS;
}
}
if (b_success)
{
p_aout->pause = Pause;
return VLC_SUCCESS;
}
error:
/* If we reach this, this aout has failed */
msg_Err(p_aout, "opening auhal output failed");
@ -2031,7 +2002,6 @@ static int Open(vlc_object_t *obj)
p_sys->b_selected_dev_is_default = false;
memset(&p_sys->sfmt_revert, 0, sizeof(p_sys->sfmt_revert));
p_sys->i_stream_id = 0;
atomic_init(&p_sys->b_paused, false);
p_aout->sys = p_sys;
p_aout->start = Start;

View File

@ -42,6 +42,12 @@ ca_Render(audio_output_t *p_aout, uint8_t *p_output, size_t i_requested)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
{
memset(p_output, 0, i_requested);
return;
}
/* Pull audio from buffer */
int32_t i_available;
void *p_data = TPCircularBufferTail(&p_sys->circular_buffer,
@ -106,6 +112,15 @@ ca_Flush(audio_output_t *p_aout, bool wait)
}
}
void
ca_Pause(audio_output_t * p_aout, bool pause, mtime_t date)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
VLC_UNUSED(date);
atomic_store_explicit(&p_sys->b_paused, pause, memory_order_relaxed);
}
void
ca_Play(audio_output_t * p_aout, block_t * p_block)
{
@ -128,6 +143,12 @@ ca_Play(audio_output_t * p_aout, block_t * p_block)
assert(false);
break;
}
if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
{
msg_Warn(p_aout, "dropping block because the circular buffer is "
"full and paused");
break;
}
/* Wait for the render buffer to play the remaining data */
int32_t i_avalaible_bytes;
@ -162,12 +183,14 @@ ca_Init(audio_output_t *p_aout, const audio_sample_format_t *fmt,
return VLC_EGENERIC;
atomic_init(&p_sys->i_underrun_size, 0);
atomic_init(&p_sys->b_paused, false);
p_sys->i_rate = fmt->i_rate;
p_sys->i_bytes_per_frame = fmt->i_bytes_per_frame;
p_sys->i_frame_length = fmt->i_frame_length;
p_aout->play = ca_Play;
p_aout->pause = ca_Pause;
p_aout->flush = ca_Flush;
p_aout->time_get = ca_TimeGet;
return VLC_SUCCESS;

View File

@ -49,6 +49,7 @@ struct aout_sys_common
/* circular buffer to swap the audio data */
TPCircularBuffer circular_buffer;
atomic_uint i_underrun_size;
atomic_bool b_paused;
int i_rate;
unsigned int i_bytes_per_frame;
unsigned int i_frame_length;
@ -67,6 +68,8 @@ int ca_TimeGet(audio_output_t *p_aout, mtime_t *delay);
void ca_Flush(audio_output_t *p_aout, bool wait);
void ca_Pause(audio_output_t * p_aout, bool pause, mtime_t date);
void ca_Play(audio_output_t * p_aout, block_t * p_block);
int ca_Init(audio_output_t *p_aout, const audio_sample_format_t *fmt,