[vlc-commits] coreaudio: replace TPCircularBuffer by os_unfair_lock and a block chain
Thomas Guillem
git at videolan.org
Wed Mar 14 14:50:49 CET 2018
vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Mon Mar 12 13:58:28 2018 +0100| [9bb14edac43e3bf7c4a96b02e3f920f7f7efeb6c] | committer: Thomas Guillem
coreaudio: replace TPCircularBuffer by os_unfair_lock and a block chain
Remove the usage of TPCircularBuffer and multiple atomic variables that start
to make this code way too complicated. Replace it by os_unfair_lock and a block
chain.
os_unfair_lock is a safe spinlock that waits in the kernel in case of thread
contention.
Fallback to pthread_mutex_t if os_unfair_lock is not availaible (before macOS
10.12 / iOS 10.0).
The unfairness of this new lock is not an issue here since both locking threads
(the render callback and the VLC DecoderThread calling aout_DecPlay) will be
automatically paced (and will let the other thread take the lock). Indeed, the
render thread need a sample every 22 or 88ms, and the DecoderThread will wait
for the decoder, wait in the decoder lock, or wait from the aout if the FIFO is
full.
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=9bb14edac43e3bf7c4a96b02e3f920f7f7efeb6c
---
modules/audio_output/Makefile.am | 6 +-
modules/audio_output/TPCircularBuffer.c | 115 -----------
modules/audio_output/TPCircularBuffer.h | 175 -----------------
modules/audio_output/coreaudio_common.c | 336 ++++++++++++++++++++------------
modules/audio_output/coreaudio_common.h | 30 ++-
5 files changed, 238 insertions(+), 424 deletions(-)
diff --git a/modules/audio_output/Makefile.am b/modules/audio_output/Makefile.am
index d7766af7e9..48c19d79ea 100644
--- a/modules/audio_output/Makefile.am
+++ b/modules/audio_output/Makefile.am
@@ -101,16 +101,14 @@ aout_LTLIBRARIES += libwaveout_plugin.la
endif
libauhal_plugin_la_SOURCES = audio_output/auhal.c \
- audio_output/coreaudio_common.c audio_output/coreaudio_common.h \
- audio_output/TPCircularBuffer.h audio_output/TPCircularBuffer.c
+ audio_output/coreaudio_common.c audio_output/coreaudio_common.h
libauhal_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(aoutdir)' \
-Wl,-framework,CoreAudio,-framework,AudioUnit,-framework,AudioToolbox,-framework,CoreServices
if HAVE_OSX
aout_LTLIBRARIES += libauhal_plugin.la
endif
libaudiounit_ios_plugin_la_SOURCES = audio_output/audiounit_ios.m \
- audio_output/coreaudio_common.c audio_output/coreaudio_common.h \
- audio_output/TPCircularBuffer.h audio_output/TPCircularBuffer.c
+ audio_output/coreaudio_common.c audio_output/coreaudio_common.h
libaudiounit_ios_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(aoutdir)' \
-Wl,-framework,CoreAudio,-framework,AudioUnit,-framework,AudioToolbox,-framework,CoreServices,-framework,UIKit,-framework,AVFoundation
if HAVE_IOS
diff --git a/modules/audio_output/TPCircularBuffer.c b/modules/audio_output/TPCircularBuffer.c
deleted file mode 100644
index 1f7f05fa8c..0000000000
--- a/modules/audio_output/TPCircularBuffer.c
+++ /dev/null
@@ -1,115 +0,0 @@
-//
-// TPCircularBuffer.c
-// Circular/Ring buffer implementation
-//
-// Created by Michael Tyson on 10/12/2011.
-// Copyright 2011-2012 A Tasty Pixel. All rights reserved.
-
-
-#include "audio_output/TPCircularBuffer.h"
-#include <mach/mach.h>
-#include <stdio.h>
-
-#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
-static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) {
- if ( result != ERR_SUCCESS ) {
- printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
- return false;
- }
- return true;
-}
-
-bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
-
- // Keep trying until we get our buffer, needed to handle race conditions
- int retries = 3;
- while ( true ) {
-
- buffer->length = round_page(length); // We need whole page sizes
-
- // Temporarily allocate twice the length, so we have the contiguous address space to
- // support a second instance of the buffer directly after
- vm_address_t bufferAddress;
- kern_return_t result = vm_allocate(mach_task_self(),
- &bufferAddress,
- buffer->length * 2,
- VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
- if ( result != ERR_SUCCESS ) {
- if ( retries-- == 0 ) {
- reportResult(result, "Buffer allocation");
- return false;
- }
- // Try again if we fail
- continue;
- }
-
- // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half...
- result = vm_deallocate(mach_task_self(),
- bufferAddress + buffer->length,
- buffer->length);
- if ( result != ERR_SUCCESS ) {
- if ( retries-- == 0 ) {
- reportResult(result, "Buffer deallocation");
- return false;
- }
- // If this fails somehow, deallocate the whole region and try again
- vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
- continue;
- }
-
- // Re-map the buffer to the address space immediately after the buffer
- vm_address_t virtualAddress = bufferAddress + buffer->length;
- vm_prot_t cur_prot, max_prot;
- result = vm_remap(mach_task_self(),
- &virtualAddress, // mirror target
- buffer->length, // size of mirror
- 0, // auto alignment
- 0, // force remapping to virtualAddress
- mach_task_self(), // same task
- bufferAddress, // mirror source
- 0, // MAP READ-WRITE, NOT COPY
- &cur_prot, // unused protection struct
- &max_prot, // unused protection struct
- VM_INHERIT_DEFAULT);
- if ( result != ERR_SUCCESS ) {
- if ( retries-- == 0 ) {
- reportResult(result, "Remap buffer memory");
- return false;
- }
- // If this remap failed, we hit a race condition, so deallocate and try again
- vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
- continue;
- }
-
- if ( virtualAddress != bufferAddress+buffer->length ) {
- // If the memory is not contiguous, clean up both allocated buffers and try again
- if ( retries-- == 0 ) {
- printf("Couldn't map buffer memory to end of buffer\n");
- return false;
- }
-
- vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
- vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
- continue;
- }
-
- buffer->buffer = (void*)bufferAddress;
- buffer->fillCount = 0;
- buffer->head = buffer->tail = 0;
-
- return true;
- }
- return false;
-}
-
-void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
- vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
- memset(buffer, 0, sizeof(TPCircularBuffer));
-}
-
-void TPCircularBufferClear(TPCircularBuffer *buffer) {
- int32_t fillCount;
- if ( TPCircularBufferTail(buffer, &fillCount) ) {
- TPCircularBufferConsume(buffer, fillCount);
- }
-}
diff --git a/modules/audio_output/TPCircularBuffer.h b/modules/audio_output/TPCircularBuffer.h
deleted file mode 100644
index b4932edeb2..0000000000
--- a/modules/audio_output/TPCircularBuffer.h
+++ /dev/null
@@ -1,175 +0,0 @@
-//
-// TPCircularBuffer.h
-// Circular/Ring buffer implementation
-//
-// https://github.com/michaeltyson/TPCircularBuffer
-//
-// Created by Michael Tyson on 10/12/2011.
-// Copyright 2011-2012 A Tasty Pixel. All rights reserved.
-//
-//
-// This implementation makes use of a virtual memory mapping technique that inserts a virtual copy
-// of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around
-// logic. Clients can simply use the returned memory address as if it were contiguous space.
-//
-// The implementation is thread-safe in the case of a single producer and single consumer.
-//
-// Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and
-// adapted to Darwin by Kurt Revis (http://www.snoize.com,
-// http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz)
-//
-
-#ifndef TPCircularBuffer_h
-#define TPCircularBuffer_h
-
-#include <libkern/OSAtomic.h>
-#include <string.h>
-#include <assert.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct {
- void *buffer;
- int32_t length;
- int32_t tail;
- int32_t head;
- volatile int32_t fillCount;
-} TPCircularBuffer;
-
-/*!
- * Initialise buffer
- *
- * Note that the length is advisory only: Because of the way the
- * memory mirroring technique works, the true buffer length will
- * be multiples of the device page size (e.g. 4096 bytes)
- *
- * @param buffer Circular buffer
- * @param length Length of buffer
- */
-bool TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length);
-
-/*!
- * Cleanup buffer
- *
- * Releases buffer resources.
- */
-void TPCircularBufferCleanup(TPCircularBuffer *buffer);
-
-/*!
- * Clear buffer
- *
- * Resets buffer to original, empty state.
- *
- * This is safe for use by consumer while producer is accessing
- * buffer.
- */
-void TPCircularBufferClear(TPCircularBuffer *buffer);
-
-// Reading (consuming)
-
-/*!
- * Access end of buffer
- *
- * This gives you a pointer to the end of the buffer, ready
- * for reading, and the number of available bytes to read.
- *
- * @param buffer Circular buffer
- * @param availableBytes On output, the number of bytes ready for reading
- * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty
- */
-static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
- *availableBytes = buffer->fillCount;
- if ( *availableBytes == 0 ) return NULL;
- return (void*)((char*)buffer->buffer + buffer->tail);
-}
-
-/*!
- * Consume bytes in buffer
- *
- * This frees up the just-read bytes, ready for writing again.
- *
- * @param buffer Circular buffer
- * @param amount Number of bytes to consume
- */
-static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) {
- buffer->tail = (buffer->tail + amount) % buffer->length;
- OSAtomicAdd32Barrier(-amount, &buffer->fillCount);
- assert(buffer->fillCount >= 0);
-}
-
-/*!
- * Version of TPCircularBufferConsume without the memory barrier, for more optimal use in single-threaded contexts
- */
-static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) {
- buffer->tail = (buffer->tail + amount) % buffer->length;
- buffer->fillCount -= amount;
- assert(buffer->fillCount >= 0);
-}
-
-/*!
- * Access front of buffer
- *
- * This gives you a pointer to the front of the buffer, ready
- * for writing, and the number of available bytes to write.
- *
- * @param buffer Circular buffer
- * @param availableBytes On output, the number of bytes ready for writing
- * @return Pointer to the first bytes ready for writing, or NULL if buffer is full
- */
-static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
- *availableBytes = (buffer->length - buffer->fillCount);
- if ( *availableBytes == 0 ) return NULL;
- return (void*)((char*)buffer->buffer + buffer->head);
-}
-
-// Writing (producing)
-
-/*!
- * Produce bytes in buffer
- *
- * This marks the given section of the buffer ready for reading.
- *
- * @param buffer Circular buffer
- * @param amount Number of bytes to produce
- */
-static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int amount) {
- buffer->head = (buffer->head + amount) % buffer->length;
- OSAtomicAdd32Barrier(amount, &buffer->fillCount);
- assert(buffer->fillCount <= buffer->length);
-}
-
-/*!
- * Version of TPCircularBufferProduce without the memory barrier, for more optimal use in single-threaded contexts
- */
-static __inline__ __attribute__((always_inline)) void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int amount) {
- buffer->head = (buffer->head + amount) % buffer->length;
- buffer->fillCount += amount;
- assert(buffer->fillCount <= buffer->length);
-}
-
-/*!
- * Helper routine to copy bytes to buffer
- *
- * This copies the given bytes to the buffer, and marks them ready for writing.
- *
- * @param buffer Circular buffer
- * @param src Source buffer
- * @param len Number of bytes in source buffer
- * @return true if bytes copied, false if there was insufficient space
- */
-static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
- int32_t space;
- void *ptr = TPCircularBufferHead(buffer, &space);
- if ( space < len ) return false;
- memcpy(ptr, src, len);
- TPCircularBufferProduce(buffer, len);
- return true;
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/modules/audio_output/coreaudio_common.c b/modules/audio_output/coreaudio_common.c
index 292146f28c..1dd80812c8 100644
--- a/modules/audio_output/coreaudio_common.c
+++ b/modules/audio_output/coreaudio_common.c
@@ -25,10 +25,13 @@
#import "coreaudio_common.h"
#import <CoreAudio/CoreAudioTypes.h>
-#if !TARGET_OS_IPHONE
-#import <CoreServices/CoreServices.h>
-#import <vlc_dialog.h>
-#endif
+#import <dlfcn.h>
+
+static struct
+{
+ void (*lock)(os_unfair_lock *lock);
+ void (*unlock)(os_unfair_lock *lock);
+} unfair_lock;
static inline uint64_t
BytesToFrames(struct aout_sys_common *p_sys, size_t i_bytes)
@@ -42,17 +45,74 @@ FramesToUs(struct aout_sys_common *p_sys, uint64_t i_nb_frames)
return i_nb_frames * CLOCK_FREQ / p_sys->i_rate;
}
+static void
+ca_ClearOutBuffers(audio_output_t *p_aout)
+{
+ struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
+
+ block_ChainRelease(p_sys->p_out_chain);
+ p_sys->p_out_chain = NULL;
+ p_sys->pp_out_last = &p_sys->p_out_chain;
+
+ p_sys->i_out_size = 0;
+}
+
+static void
+ca_init_once(void)
+{
+ unfair_lock.lock = dlsym(RTLD_DEFAULT, "os_unfair_lock_lock");
+ if (!unfair_lock.lock)
+ return;
+ unfair_lock.unlock = dlsym(RTLD_DEFAULT, "os_unfair_lock_unlock");
+ if (!unfair_lock.unlock)
+ unfair_lock.lock = NULL;
+}
+
+static void
+lock_init(struct aout_sys_common *p_sys)
+{
+ if (unfair_lock.lock)
+ p_sys->lock.unfair = OS_UNFAIR_LOCK_INIT;
+ else
+ vlc_mutex_init(&p_sys->lock.mutex);
+}
+
+static void
+lock_destroy(struct aout_sys_common *p_sys)
+{
+ if (!unfair_lock.lock)
+ vlc_mutex_destroy(&p_sys->lock.mutex);
+}
+
+static void
+lock_lock(struct aout_sys_common *p_sys)
+{
+ if (unfair_lock.lock)
+ unfair_lock.lock(&p_sys->lock.unfair);
+ else
+ vlc_mutex_lock(&p_sys->lock.mutex);
+}
+
+static void
+lock_unlock(struct aout_sys_common *p_sys)
+{
+ if (unfair_lock.lock)
+ unfair_lock.unlock(&p_sys->lock.unfair);
+ else
+ vlc_mutex_unlock(&p_sys->lock.mutex);
+}
+
void
ca_Open(audio_output_t *p_aout)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
- atomic_init(&p_sys->i_underrun_size, 0);
- atomic_init(&p_sys->b_paused, false);
- atomic_init(&p_sys->b_do_flush, false);
- atomic_init(&p_sys->b_highlatency, true);
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, ca_init_once);
+
vlc_sem_init(&p_sys->flush_sem, 0);
- vlc_mutex_init(&p_sys->lock);
+ lock_init(p_sys);
+ p_sys->p_out_chain = NULL;
p_aout->play = ca_Play;
p_aout->pause = ca_Pause;
@@ -66,7 +126,7 @@ ca_Close(audio_output_t *p_aout)
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
vlc_sem_destroy(&p_sys->flush_sem);
- vlc_mutex_destroy(&p_sys->lock);
+ lock_destroy(p_sys);
}
/* Called from render callbacks. No lock, wait, and IO here */
@@ -76,52 +136,63 @@ ca_Render(audio_output_t *p_aout, uint32_t i_nb_samples, uint8_t *p_output,
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
- const bool b_highlatency = CLOCK_FREQ * (uint64_t)i_nb_samples / p_sys->i_rate
- > AOUT_MAX_PTS_DELAY;
+ lock_lock(p_sys);
- if (b_highlatency)
- atomic_store(&p_sys->b_highlatency, true);
+ p_sys->b_highlatency = CLOCK_FREQ * (uint64_t)i_nb_samples / p_sys->i_rate
+ > AOUT_MAX_PTS_DELAY;
- bool expected = true;
- if (atomic_compare_exchange_weak(&p_sys->b_do_flush, &expected, false))
+ if (p_sys->b_do_flush)
{
- TPCircularBufferClear(&p_sys->circular_buffer);
+ ca_ClearOutBuffers(p_aout);
/* Signal that the renderer is flushed */
+ p_sys->b_do_flush = false;
vlc_sem_post(&p_sys->flush_sem);
}
- 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,
- &i_available);
- if (i_available < 0)
- i_available = 0;
+ if (p_sys->b_paused)
+ goto drop;
- size_t i_tocopy = __MIN(i_requested, (size_t) i_available);
-
- if (i_tocopy > 0)
+ size_t i_copied = 0;
+ block_t *p_block = p_sys->p_out_chain;
+ while (p_block != NULL && i_requested != 0)
{
- memcpy(p_output, p_data, i_tocopy);
- TPCircularBufferConsume(&p_sys->circular_buffer, i_tocopy);
+ size_t i_tocopy = __MIN(i_requested, p_block->i_buffer);
+ memcpy(&p_output[i_copied], p_block->p_buffer, i_tocopy);
+ i_requested -= i_tocopy;
+ i_copied += i_tocopy;
+ if (i_tocopy == p_block->i_buffer)
+ {
+ block_t *p_release = p_block;
+ p_block = p_block->p_next;
+ block_Release(p_release);
+ }
+ else
+ {
+ assert(i_requested == 0);
+
+ p_block->p_buffer += i_tocopy;
+ p_block->i_buffer -= i_tocopy;
+ }
}
+ p_sys->p_out_chain = p_block;
+ if (!p_sys->p_out_chain)
+ p_sys->pp_out_last = &p_sys->p_out_chain;
+ p_sys->i_out_size -= i_copied;
/* Pad with 0 */
- if (i_requested > i_tocopy)
+ if (i_requested > 0)
{
- atomic_fetch_add(&p_sys->i_underrun_size, i_requested - i_tocopy);
- memset(&p_output[i_tocopy], 0, i_requested - i_tocopy);
+ assert(p_sys->i_out_size == 0);
+ p_sys->i_underrun_size += i_requested;
+ memset(&p_output[i_copied], 0, i_requested);
}
- /* Set high delay to false (re-enabling ca_Timeget) after consuming the
- * circular buffer */
- if (!b_highlatency)
- atomic_store(&p_sys->b_highlatency, false);
+ lock_unlock(p_sys);
+ return;
+
+drop:
+ memset(p_output, 0, i_requested);
+ lock_unlock(p_sys);
}
int
@@ -129,16 +200,17 @@ ca_TimeGet(audio_output_t *p_aout, mtime_t *delay)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
- /* Too high delay: TimeGet will be too imprecise */
- if (atomic_load(&p_sys->b_highlatency))
+ lock_lock(p_sys);
+ if (p_sys->b_highlatency)
+ {
+ lock_unlock(p_sys);
return -1;
+ }
- int32_t i_bytes;
- TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes);
-
- int64_t i_frames = BytesToFrames(p_sys, i_bytes);
+ int64_t i_frames = BytesToFrames(p_sys, p_sys->i_out_size);
*delay = FramesToUs(p_sys, i_frames) + p_sys->i_dev_latency_us;
+ lock_unlock(p_sys);
return 0;
}
@@ -147,47 +219,41 @@ ca_Flush(audio_output_t *p_aout, bool wait)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
+ lock_lock(p_sys);
if (wait)
{
- int32_t i_bytes;
-
- while (TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes) != NULL)
+ while (p_sys->i_out_size > 0)
{
- if (atomic_load(&p_sys->b_paused))
+ if (p_sys->b_paused)
{
- TPCircularBufferClear(&p_sys->circular_buffer);
- return;
+ ca_ClearOutBuffers(p_aout);
+ break;
}
/* Calculate the duration of the circular buffer, in order to wait
* for the render thread to play it all */
const mtime_t i_frame_us =
- FramesToUs(p_sys, BytesToFrames(p_sys, i_bytes)) + 10000;
-
- msleep(i_frame_us / 2);
+ FramesToUs(p_sys, BytesToFrames(p_sys, p_sys->i_out_size)) + 10000;
+ lock_unlock(p_sys);
+ msleep(i_frame_us);
+ lock_lock(p_sys);
}
}
else
{
- /* Request the renderer to flush, and wait for an ACK.
- * b_do_flush and b_paused need to be locked together in order to not
- * get stuck here when b_paused is being set after reading. This can
- * happen when setAliveState() is called from any thread through an
- * interrupt notification */
-
- vlc_mutex_lock(&p_sys->lock);
- assert(!atomic_load(&p_sys->b_do_flush));
- if (atomic_load(&p_sys->b_paused))
+ assert(!p_sys->b_do_flush);
+ if (p_sys->b_paused)
+ ca_ClearOutBuffers(p_aout);
+ else
{
- vlc_mutex_unlock(&p_sys->lock);
- TPCircularBufferClear(&p_sys->circular_buffer);
+ p_sys->b_do_flush = true;
+ lock_unlock(p_sys);
+ vlc_sem_wait(&p_sys->flush_sem);
return;
}
-
- atomic_store_explicit(&p_sys->b_do_flush, true, memory_order_release);
- vlc_mutex_unlock(&p_sys->lock);
- vlc_sem_wait(&p_sys->flush_sem);
}
+
+ lock_unlock(p_sys);
}
void
@@ -196,7 +262,9 @@ 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);
+ lock_lock(p_sys);
+ p_sys->b_paused = pause;
+ lock_unlock(p_sys);
}
void
@@ -210,43 +278,65 @@ ca_Play(audio_output_t * p_aout, block_t * p_block)
p_sys->chans_to_reorder, p_sys->chan_table,
VLC_CODEC_FL32);
- /* move data to buffer */
- while (!TPCircularBufferProduceBytes(&p_sys->circular_buffer,
- p_block->p_buffer, p_block->i_buffer))
+ lock_lock(p_sys);
+ do
{
- if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
+ const size_t i_avalaible_bytes =
+ __MIN(p_block->i_buffer, p_sys->i_out_max_size - p_sys->i_out_size);
+
+ if (unlikely(i_avalaible_bytes != p_block->i_buffer))
{
- msg_Warn(p_aout, "dropping block because the circular buffer is "
- "full and paused");
- break;
+ /* Not optimal but unlikely code path. */
+
+ lock_unlock(p_sys);
+
+ block_t *p_new = block_Alloc(i_avalaible_bytes);
+ if (!p_new)
+ {
+ block_Release(p_block);
+ return;
+ }
+
+ memcpy(p_new->p_buffer, p_block->p_buffer, i_avalaible_bytes);
+
+ p_block->p_buffer += i_avalaible_bytes;
+ p_block->i_buffer -= i_avalaible_bytes;
+
+ lock_lock(p_sys);
+
+ block_ChainLastAppend(&p_sys->pp_out_last, p_new);
+ p_sys->i_out_size += i_avalaible_bytes;
+
+ if (p_sys->b_paused)
+ {
+ lock_unlock(p_sys);
+ block_Release(p_block);
+ return;
+ }
+
+ const mtime_t i_frame_us =
+ FramesToUs(p_sys, BytesToFrames(p_sys, p_block->i_buffer));
+
+ /* Wait for the render buffer to play the remaining data */
+ lock_unlock(p_sys);
+ msleep(i_frame_us);
+ lock_lock(p_sys);
+ }
+ else
+ {
+ block_ChainLastAppend(&p_sys->pp_out_last, p_block);
+ p_sys->i_out_size += i_avalaible_bytes;
+ p_block = NULL;
}
+ } while (p_block != NULL);
- /* Try to play what we can */
- int32_t i_avalaible_bytes;
- TPCircularBufferHead(&p_sys->circular_buffer, &i_avalaible_bytes);
- assert(i_avalaible_bytes >= 0);
- if (unlikely((size_t) i_avalaible_bytes >= p_block->i_buffer))
- continue;
-
- bool ret =
- TPCircularBufferProduceBytes(&p_sys->circular_buffer,
- p_block->p_buffer, i_avalaible_bytes);
- VLC_UNUSED(ret);
- assert(ret == true);
- p_block->p_buffer += i_avalaible_bytes;
- p_block->i_buffer -= i_avalaible_bytes;
-
- /* Wait for the render buffer to play the remaining data */
- const mtime_t i_frame_us =
- FramesToUs(p_sys, BytesToFrames(p_sys, p_block->i_buffer));
- msleep(i_frame_us / 2);
- }
+ size_t i_underrun_size = p_sys->i_underrun_size;
+ p_sys->i_underrun_size = 0;
- unsigned i_underrun_size = atomic_exchange(&p_sys->i_underrun_size, 0);
- if (i_underrun_size > 0)
- msg_Warn(p_aout, "underrun of %u bytes", i_underrun_size);
+ lock_unlock(p_sys);
- block_Release(p_block);
+ if (i_underrun_size > 0)
+ msg_Warn(p_aout, "underrun of %zu bytes", i_underrun_size);
}
int
@@ -255,9 +345,10 @@ ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
- atomic_store(&p_sys->i_underrun_size, 0);
- atomic_store(&p_sys->b_paused, false);
- atomic_store(&p_sys->b_highlatency, true);
+ p_sys->i_underrun_size = 0;
+ p_sys->b_paused = false;
+ p_sys->b_highlatency = true;
+
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;
@@ -282,16 +373,15 @@ ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
{
/* lower latency: 200 ms of buffering. XXX: Decrease when VLC's core
* can handle lower audio latency */
- i_audiobuffer_size = i_audiobuffer_size / 5;
+ p_sys->i_out_max_size = i_audiobuffer_size / 5;
}
else
{
/* 2 seconds of buffering */
- i_audiobuffer_size = i_audiobuffer_size * AOUT_MAX_ADVANCE_TIME
- / CLOCK_FREQ;
+ p_sys->i_out_max_size = i_audiobuffer_size * 2;
}
- if (!TPCircularBufferInit(&p_sys->circular_buffer, i_audiobuffer_size))
- return VLC_EGENERIC;
+
+ ca_ClearOutBuffers(p_aout);
return VLC_SUCCESS;
}
@@ -300,8 +390,8 @@ void
ca_Uninitialize(audio_output_t *p_aout)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
- /* clean-up circular buffer */
- TPCircularBufferCleanup(&p_sys->circular_buffer);
+ ca_ClearOutBuffers(p_aout);
+ p_sys->i_out_max_size = 0;
}
void
@@ -309,17 +399,21 @@ ca_SetAliveState(audio_output_t *p_aout, bool alive)
{
struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
- vlc_mutex_lock(&p_sys->lock);
- atomic_store(&p_sys->b_paused, !alive);
+ lock_lock(p_sys);
- bool expected = true;
- if (!alive && atomic_compare_exchange_strong(&p_sys->b_do_flush, &expected, false))
+ bool b_sem_post = false;
+ p_sys->b_paused = !alive;
+ if (!alive && p_sys->b_do_flush)
{
- TPCircularBufferClear(&p_sys->circular_buffer);
- /* Signal that the renderer is flushed */
- vlc_sem_post(&p_sys->flush_sem);
+ ca_ClearOutBuffers(p_aout);
+ p_sys->b_do_flush = false;
+ b_sem_post = true;
}
- vlc_mutex_unlock(&p_sys->lock);
+
+ lock_unlock(p_sys);
+
+ if (b_sem_post)
+ vlc_sem_post(&p_sys->flush_sem);
}
AudioUnit
diff --git a/modules/audio_output/coreaudio_common.h b/modules/audio_output/coreaudio_common.h
index 21cdcb6518..d78b1f43ae 100644
--- a/modules/audio_output/coreaudio_common.h
+++ b/modules/audio_output/coreaudio_common.h
@@ -27,13 +27,12 @@
#endif
#import <vlc_common.h>
-#import <stdatomic.h>
#import <vlc_aout.h>
#import <vlc_threads.h>
#import <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
-#import "TPCircularBuffer.h"
+#import <os/lock.h>
#define STREAM_FORMAT_MSG(pre, sfm) \
pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
@@ -50,14 +49,27 @@ struct aout_sys_common
/* The following is owned by common.c (initialized from ca_Init, cleaned
* from ca_Clean) */
- /* circular buffer to swap the audio data */
- TPCircularBuffer circular_buffer;
- atomic_uint i_underrun_size;
- atomic_bool b_paused;
- atomic_bool b_do_flush;
- atomic_bool b_highlatency;
+ size_t i_underrun_size;
+ bool b_paused;
+ bool b_do_flush;
+ bool b_highlatency;
+
+ size_t i_out_max_size;
+ size_t i_out_size;
+ block_t *p_out_chain;
+ block_t **pp_out_last;
+
vlc_sem_t flush_sem;
- vlc_mutex_t lock;
+
+ union lock
+ {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpartial-availability"
+ os_unfair_lock unfair;
+#pragma clang diagnostic pop
+ pthread_mutex_t mutex;
+ } lock;
+
int i_rate;
unsigned int i_bytes_per_frame;
unsigned int i_frame_length;
More information about the vlc-commits
mailing list