[vlc-devel] [RFC PATCH] coreaudio: replace atomic circular buffer by locked block FIFO

Thomas Guillem thomas at gllm.fr
Mon Mar 12 19:30:01 CET 2018


Remove TPCircularBuffer.c and usage of multiple atomic variables that start to
make this code way too complicated. Replace it by a basic pthread_mutex and a
block FIFO.

RFC:

Theoretically, you should never block from the render callback. But pthread
mutexes on macOS/iOS are hybrid mutexes that will try to spinlock first, and
these locks are held for very fast operations (setting a pointer and a bool).

PS: I can't find anymore in the documentation the mention about forbiden usage
of blocking operations from audio callbacks (but I guess it's kind of
obvious?).
---
 modules/audio_output/Makefile.am        |   6 +-
 modules/audio_output/TPCircularBuffer.c | 115 ------------------
 modules/audio_output/TPCircularBuffer.h | 175 --------------------------
 modules/audio_output/coreaudio_common.c | 209 ++++++++++++++------------------
 modules/audio_output/coreaudio_common.h |  18 +--
 5 files changed, 105 insertions(+), 418 deletions(-)
 delete mode 100644 modules/audio_output/TPCircularBuffer.c
 delete mode 100644 modules/audio_output/TPCircularBuffer.h

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 0413dea570..e1fa48f667 100644
--- a/modules/audio_output/coreaudio_common.c
+++ b/modules/audio_output/coreaudio_common.c
@@ -42,16 +42,28 @@ 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;
+    p_sys->b_do_flush = false;
+}
+
 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);
-    vlc_sem_init(&p_sys->flush_sem, 0);
+    p_sys->p_out_chain = NULL;
+
     vlc_mutex_init(&p_sys->lock);
+    vlc_cond_init(&p_sys->wait);
 
     p_aout->play = ca_Play;
     p_aout->pause = ca_Pause;
@@ -64,8 +76,8 @@ 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);
+    vlc_cond_destroy(&p_sys->wait);
 }
 
 /* Called from render callbacks. No lock, wait, and IO here */
@@ -74,41 +86,54 @@ 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;
 
-    bool expected = true;
-    if (atomic_compare_exchange_weak(&p_sys->b_do_flush, &expected, false))
-    {
-        TPCircularBufferClear(&p_sys->circular_buffer);
-        /* Signal that the renderer is flushed */
-        vlc_sem_post(&p_sys->flush_sem);
-    }
+    vlc_mutex_lock(&p_sys->lock);
+    if (p_sys->b_do_flush)
+        ca_ClearOutBuffers(p_aout);
 
-    if (atomic_load_explicit(&p_sys->b_paused, memory_order_relaxed))
+    if (p_sys->b_paused)
     {
         memset(p_output, 0, i_requested);
+        vlc_mutex_unlock(&p_sys->lock);
         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;
-
-    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);
     }
+
+    vlc_mutex_unlock(&p_sys->lock);
+    vlc_cond_signal(&p_sys->wait);
 }
 
 int
@@ -116,12 +141,12 @@ ca_TimeGet(audio_output_t *p_aout, mtime_t *delay)
 {
     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
 
-    int32_t i_bytes;
-    TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes);
+    vlc_mutex_lock(&p_sys->lock);
 
-    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;
 
+    vlc_mutex_unlock(&p_sys->lock);
     return 0;
 }
 
@@ -130,47 +155,17 @@ ca_Flush(audio_output_t *p_aout, bool wait)
 {
     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
 
-    if (wait)
+    vlc_mutex_lock(&p_sys->lock);
+    if (!wait)
     {
-        int32_t i_bytes;
-
-        while (TPCircularBufferTail(&p_sys->circular_buffer, &i_bytes) != NULL)
-        {
-            if (atomic_load(&p_sys->b_paused))
-            {
-                TPCircularBufferClear(&p_sys->circular_buffer);
-                return;
-            }
-
-            /* 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);
-        }
+        assert(!&p_sys->b_do_flush);
+        p_sys->b_do_flush = true;
     }
-    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))
-        {
-            vlc_mutex_unlock(&p_sys->lock);
-            TPCircularBufferClear(&p_sys->circular_buffer);
-            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);
-    }
+    while (p_sys->i_out_size > 0)
+        vlc_cond_wait(&p_sys->wait, &p_sys->lock);
+
+    vlc_mutex_unlock(&p_sys->lock);
 }
 
 void
@@ -179,7 +174,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);
+    vlc_mutex_lock(&p_sys->lock);
+    p_sys->b_paused = pause;
+    vlc_mutex_unlock(&p_sys->lock);
 }
 
 void
@@ -193,43 +190,23 @@ 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))
-    {
-        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;
-        }
+    vlc_mutex_lock(&p_sys->lock);
 
-        /* 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);
+    if (likely(p_block->i_buffer <= p_sys->i_out_max_size))
+    {
+        while (p_sys->i_out_max_size - p_sys->i_out_size < p_block->i_buffer)
+            vlc_cond_wait(&p_sys->wait, &p_sys->lock);
     }
 
-    unsigned i_underrun_size = atomic_exchange(&p_sys->i_underrun_size, 0);
+    block_ChainLastAppend(&p_sys->pp_out_last, p_block);
+    p_sys->i_out_size += p_block->i_buffer;
+
+    size_t i_underrun_size = p_sys->i_underrun_size;
+    p_sys->i_underrun_size = 0;
+    vlc_mutex_unlock(&p_sys->lock);
+
     if (i_underrun_size > 0)
         msg_Warn(p_aout, "underrun of %u bytes", i_underrun_size);
-
-    block_Release(p_block);
 }
 
 int
@@ -238,8 +215,9 @@ 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);
+    p_sys->i_underrun_size = 0;
+    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;
@@ -272,8 +250,9 @@ ca_Initialize(audio_output_t *p_aout, const audio_sample_format_t *fmt,
         i_audiobuffer_size = i_audiobuffer_size * AOUT_MAX_ADVANCE_TIME
                            / CLOCK_FREQ;
     }
-    if (!TPCircularBufferInit(&p_sys->circular_buffer, i_audiobuffer_size))
-        return VLC_EGENERIC;
+
+    p_sys->i_out_max_size = i_audiobuffer_size;
+    ca_ClearOutBuffers(p_aout);
 
     return VLC_SUCCESS;
 }
@@ -282,8 +261,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
@@ -292,16 +271,14 @@ 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);
+    p_sys->b_paused = !alive;
+
+    if (!alive && p_sys->b_do_flush)
+        ca_ClearOutBuffers(p_aout);
 
-    bool expected = true;
-    if (!alive && atomic_compare_exchange_strong(&p_sys->b_do_flush, &expected, false))
-    {
-        TPCircularBufferClear(&p_sys->circular_buffer);
-        /* Signal that the renderer is flushed */
-        vlc_sem_post(&p_sys->flush_sem);
-    }
     vlc_mutex_unlock(&p_sys->lock);
+
+    vlc_cond_signal(&p_sys->wait);
 }
 
 AudioUnit
diff --git a/modules/audio_output/coreaudio_common.h b/modules/audio_output/coreaudio_common.h
index 4fb4e5fb64..79e8945240 100644
--- a/modules/audio_output/coreaudio_common.h
+++ b/modules/audio_output/coreaudio_common.h
@@ -27,13 +27,11 @@
 #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"
 
 #define STREAM_FORMAT_MSG(pre, sfm) \
     pre "[%f][%4.4s][%u][%u][%u][%u][%u][%u]", \
@@ -50,13 +48,17 @@ 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;
-    vlc_sem_t           flush_sem;
+    size_t              i_underrun_size;
+    bool                b_paused;
+    bool                b_do_flush;
+
+    size_t              i_out_max_size;
+    size_t              i_out_size;
+    block_t             *p_out_chain;
+    block_t             **pp_out_last;
+
     vlc_mutex_t         lock;
+    vlc_cond_t          wait;
     int                 i_rate;
     unsigned int        i_bytes_per_frame;
     unsigned int        i_frame_length;
-- 
2.11.0



More information about the vlc-devel mailing list