[vlc-commits] [Git][videolan/vlc][master] 14 commits: coreaudio: add missing chain initialisation

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Fri Dec 2 18:27:12 UTC 2022



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
27009027 by Thomas Guillem at 2022-12-02T18:15:23+00:00
coreaudio: add missing chain initialisation

- - - - -
370f785e by Thomas Guillem at 2022-12-02T18:15:23+00:00
coreaudio: rework the render callback

Code written by me few years ago but quite hard to understand (even by
the author), rework it by taking inspiration from the AAudio plugin.

Don't use both mach host time and vlc_tick_t but transform immediatly
the host_time to a delay (in ticks) and always use ticks.

- - - - -
c41a3554 by Thomas Guillem at 2022-12-02T18:15:23+00:00
coreaudio: simplify flush

The semaphore was needed by the previous atomic circular buffer
implementation.

- - - - -
db64c396 by Thomas Guillem at 2022-12-02T18:15:23+00:00
coreaudio: assert that frames match bytes

- - - - -
ad57dc09 by Thomas Guillem at 2022-12-02T18:15:23+00:00
auhal: use the latency offset

Fixes delay with bluetooth devices.

Pretty sure I tested it in the past and it was producing worst results.

Now we match the QuickTime player regarding A/V sync (tested with a
Sync-One2 device).

Fixes #27512

- - - - -
3e39b3a4 by Thomas Guillem at 2022-12-02T18:15:23+00:00
auhal: move latency retrieval in a new function

- - - - -
1bb8234a by Thomas Guillem at 2022-12-02T18:15:23+00:00
auhal: rework GetLatency

Add logs in case of failure, prepare for the next commit.

- - - - -
a6662433 by Thomas Guillem at 2022-12-02T18:15:23+00:00
auhal: add kAudioStreamPropertyLatency to the latency

Fixes delay with AirPlay devices.

Internal and bluetooth devices have a valid Device Latency.
Airplay 2 devices has a valid Stream Latency.

- - - - -
90201063 by Thomas Guillem at 2022-12-02T18:15:23+00:00
auhal: add kAudioDevicePropertySafetyOffset to the latency

- - - - -
621effd5 by Thomas Guillem at 2022-12-02T18:15:23+00:00
audiounit: refactor, add GetLatency()

No functional changes.

- - - - -
8cf10ffd by Thomas Guillem at 2022-12-02T18:15:23+00:00
audiounit: add IOBufferDuration + kAudioUnitProperty_Latency

Still not happy with the A/V sync when using external devices, even with
this commit.

- - - - -
9793d068 by Thomas Guillem at 2022-12-02T18:15:23+00:00
audiounit: poll latency when updating timings

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.

- - - - -
88fd1e68 by Thomas Guillem at 2022-12-02T18:15:23+00:00
coreaudio: add the AudioUnit latency to the device latency

- - - - -
f55b5b53 by Thomas Guillem at 2022-12-02T18:15:23+00:00
coreaudio: remove unused variables

- - - - -


4 changed files:

- modules/audio_output/audiounit_ios.m
- modules/audio_output/auhal.c
- modules/audio_output/coreaudio_common.c
- modules/audio_output/coreaudio_common.h


Changes:

=====================================
modules/audio_output/audiounit_ios.m
=====================================
@@ -138,6 +138,10 @@ typedef struct
     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;
@@ -154,6 +158,39 @@ enum port_type
     PORT_TYPE_HEADPHONES
 };
 
+static vlc_tick_t
+GetLatency(audio_output_t *p_aout)
+{
+    aout_sys_t *p_sys = p_aout->sys;
+
+    Float64 unit_s;
+    vlc_tick_t latency_us = 0, us;
+    bool changed = false;
+
+    us = vlc_tick_from_sec([p_sys->avInstance outputLatency]);
+    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]);
+    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;
+
+    if (changed)
+        msg_Dbg(p_aout, "Current device has a new total latency of %" PRId64 "us",
+                latency_us);
+    return latency_us;
+}
+
 #pragma mark -
 #pragma mark AVAudioSession route and output handling
 
@@ -181,12 +218,7 @@ enum port_type
      || routeChangeReason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable)
         aout_RestartRequest(p_aout, AOUT_RESTART_OUTPUT);
     else
-    {
-        const vlc_tick_t latency_us =
-            vlc_tick_from_sec([p_sys->avInstance outputLatency]);
-        ca_SetDeviceLatency(p_aout, latency_us);
-        msg_Dbg(p_aout, "Current device has a new latency of %lld us", latency_us);
-    }
+        ca_ResetDeviceLatency(p_aout);
 }
 
 - (void)handleInterruption:(NSNotification *)notification
@@ -520,6 +552,8 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
     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
@@ -580,11 +614,7 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
     if (err != noErr)
         ca_LogWarn("failed to set IO mode");
 
-    const vlc_tick_t latency_us =
-        vlc_tick_from_sec([p_sys->avInstance outputLatency]);
-    msg_Dbg(p_aout, "Current device has a latency of %lld us", latency_us);
-
-    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;
 


=====================================
modules/audio_output/auhal.c
=====================================
@@ -1036,7 +1036,8 @@ WarnConfiguration(audio_output_t *p_aout)
  * StartAnalog: open and setup a HAL AudioUnit to do PCM audio output
  */
 static int
-StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt)
+StartAnalog(audio_output_t *p_aout, audio_sample_format_t *fmt,
+            vlc_tick_t latency_us)
 {
     aout_sys_t                  *p_sys = p_aout->sys;
     OSStatus                    err = noErr;
@@ -1090,8 +1091,8 @@ 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, 0,
-                            &warn_configuration);
+    int ret = au_Initialize(p_aout, p_sys->au_unit, fmt, layout, latency_us,
+                            NULL, &warn_configuration);
     if (ret != VLC_SUCCESS)
         goto error;
 
@@ -1364,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);
@@ -1475,6 +1476,58 @@ Stop(audio_output_t *p_aout)
                       kAudioObjectPropertyScopeGlobal);
 }
 
+static vlc_tick_t
+GetLatency(audio_output_t *p_aout, const audio_sample_format_t *fmt)
+{
+    aout_sys_t *p_sys = p_aout->sys;
+
+    UInt32 i_latency_samples;
+    vlc_tick_t i_latency_us, i_device_latency_us = 0, i_device_offset_us = 0,
+               i_stream_latency_us = 0;
+    /* Get device latency */
+    int ret = AO_GET1PROP(p_sys->i_selected_dev, UInt32, &i_latency_samples,
+                          kAudioDevicePropertyLatency,
+                          kAudioObjectPropertyScopeOutput);
+    if (ret == VLC_SUCCESS)
+        i_device_latency_us = vlc_tick_from_samples(i_latency_samples, fmt->i_rate);
+    else
+        msg_Warn(p_aout, "failed to get kAudioDevicePropertyLatency");
+
+    /* Get device safety offset */
+    ret = AO_GET1PROP(p_sys->i_selected_dev, UInt32, &i_latency_samples,
+                      kAudioDevicePropertySafetyOffset,
+                      kAudioObjectPropertyScopeOutput);
+    if (ret == VLC_SUCCESS)
+        i_device_offset_us = vlc_tick_from_samples(i_latency_samples, fmt->i_rate);
+    else
+        msg_Warn(p_aout, "failed to get kAudioDevicePropertySafetyOffset");
+
+    AudioStreamID sid;
+    /* Get stream latency */
+    ret = AO_GET1PROP(p_sys->i_selected_dev, AudioStreamID, &sid,
+                      kAudioDevicePropertyStreams, kAudioObjectPropertyScopeOutput);
+    if (ret == VLC_SUCCESS)
+    {
+        ret = AO_GET1PROP(sid, UInt32, &i_latency_samples,
+                          kAudioStreamPropertyLatency,
+                          kAudioObjectPropertyScopeOutput);
+        if (ret == VLC_SUCCESS)
+            i_stream_latency_us = vlc_tick_from_samples(i_latency_samples, fmt->i_rate);
+        else
+            msg_Warn(p_aout, "failed to get kAudioStreamPropertyLatency");
+    }
+    else
+        msg_Warn(p_aout, "failed to get kAudioDevicePropertyStreams");
+
+    i_latency_us = i_device_latency_us + i_device_offset_us + i_stream_latency_us;
+
+    msg_Dbg(p_aout, "Current device has a latency of %" PRId64 " us "
+            "(device: %" PRId64 " us, offset: %" PRId64 " us, stream: %" PRId64 " us)", i_latency_us,
+            i_device_latency_us, i_device_offset_us, i_stream_latency_us);
+
+    return i_latency_us;
+}
+
 static int
 Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
 {
@@ -1573,16 +1626,7 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
                       kAudioDevicePropertyDeviceIsAlive,
                       kAudioObjectPropertyScopeGlobal);
 
-    /* get device latency */
-    UInt32 i_latency_samples;
-    vlc_tick_t i_latency_us = 0;
-    int ret = AO_GET1PROP(p_sys->i_selected_dev, UInt32, &i_latency_samples,
-                          kAudioDevicePropertyLatency,
-                          kAudioObjectPropertyScopeOutput);
-    if (ret == VLC_SUCCESS)
-        i_latency_us += vlc_tick_from_samples(i_latency_samples, fmt->i_rate);
-
-    msg_Dbg(p_aout, "Current device has a latency of %lld us", i_latency_us);
+    vlc_tick_t i_latency_us = GetLatency(p_aout, fmt);
 
     /* Check for Digital mode or Analog output mode */
     if (do_spdif)
@@ -1595,7 +1639,7 @@ Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
     }
     else
     {
-        if (StartAnalog(p_aout, fmt) == VLC_SUCCESS)
+        if (StartAnalog(p_aout, fmt, i_latency_us) == VLC_SUCCESS)
         {
             msg_Dbg(p_aout, "analog output successfully opened");
             fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;


=====================================
modules/audio_output/coreaudio_common.c
=====================================
@@ -25,6 +25,8 @@
 #include "coreaudio_common.h"
 #include <CoreAudio/CoreAudioTypes.h>
 
+#define TIMING_REPORT_DELAY_TICKS VLC_TICK_FROM_MS(1000)
+
 static inline uint64_t
 BytesToFrames(struct aout_sys_common *p_sys, size_t i_bytes)
 {
@@ -37,6 +39,12 @@ FramesToTicks(struct aout_sys_common *p_sys, int64_t i_nb_frames)
     return vlc_tick_from_samples(i_nb_frames, p_sys->i_rate);
 }
 
+static inline vlc_tick_t
+BytesToTicks(struct aout_sys_common *p_sys, size_t i_bytes)
+{
+    return FramesToTicks(p_sys, BytesToFrames(p_sys, i_bytes));
+}
+
 static inline size_t
 FramesToBytes(struct aout_sys_common *p_sys, uint64_t i_frames)
 {
@@ -49,6 +57,12 @@ TicksToFrames(struct aout_sys_common *p_sys, vlc_tick_t i_ticks)
     return samples_from_vlc_tick(i_ticks, p_sys->i_rate);
 }
 
+static inline size_t
+TicksToBytes(struct aout_sys_common *p_sys, vlc_tick_t i_ticks)
+{
+    return FramesToBytes(p_sys, TicksToFrames(p_sys, i_ticks));
+}
+
 /**
  * Convert a relative audio host time to vlc_ticks
  *
@@ -62,19 +76,6 @@ HostTimeToTick(struct aout_sys_common *p_sys, int64_t i_host_time)
     return VLC_TICK_FROM_NS(i_host_time * p_sys->tinfo.numer / p_sys->tinfo.denom);
 }
 
-/**
- * Convert relative vlc_ticks to an audio host time
- *
- * \warning  This function may only be used to convert relative
- *           vlc_ticks, as vlc_ticks do not have the same origin
- *           as the audio host clock!
- */
-static inline int64_t
-TickToHostTime(struct aout_sys_common *p_sys, vlc_tick_t i_ticks)
-{
-    return NS_FROM_VLC_TICK(i_ticks * p_sys->tinfo.denom / p_sys->tinfo.numer);
-}
-
 static void
 ca_ClearOutBuffers(audio_output_t *p_aout)
 {
@@ -83,8 +84,6 @@ ca_ClearOutBuffers(audio_output_t *p_aout)
     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 inline void
@@ -124,9 +123,9 @@ ca_Open(audio_output_t *p_aout)
 
     assert(p_sys->tinfo.denom != 0 && p_sys->tinfo.numer != 0);
 
-    vlc_sem_init(&p_sys->flush_sem, 0);
     lock_init(p_sys);
     p_sys->p_out_chain = NULL;
+    p_sys->pp_out_last = &p_sys->p_out_chain;
     p_sys->chans_to_reorder = 0;
 
     p_aout->play = ca_Play;
@@ -137,126 +136,128 @@ 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;
+
+    vlc_tick_t dev_latency_ticks =
+        p_sys->get_latency != NULL ? p_sys->get_latency(p_aout)
+                                   : p_sys->i_dev_latency_ticks;
+
+    /* Add the AudioUnit latency to the auhal/audiounit_ios latency */
+    if (p_sys->au != NULL)
+    {
+        Float64 unit_s;
+        if (AudioUnitGetProperty(p_sys->au, kAudioUnitProperty_Latency,
+                                 kAudioUnitScope_Global, 0, &unit_s,
+                                 &(UInt32) { sizeof(unit_s) }) != noErr)
+            unit_s = 0;
+        vlc_tick_t us = vlc_tick_from_sec(unit_s);
+        if (us != p_sys->au_latency_ticks)
+        {
+            msg_Dbg(p_aout, "Adding AudioUnit latency: %" PRId64 "us", us);
+            p_sys->au_latency_ticks = us;
+        }
+
+        dev_latency_ticks += us;
+    }
+
+    return dev_latency_ticks;
+}
+
 /* Called from render callbacks. No lock, wait, and IO here */
 void
-ca_Render(audio_output_t *p_aout, uint64_t i_host_time,
-          uint8_t *p_output, size_t i_requested, bool *is_silence)
+ca_Render(audio_output_t *p_aout, uint64_t host_time,
+          uint8_t *data, size_t bytes, bool *is_silence)
 {
     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
 
-    vlc_tick_t i_now_ticks = vlc_tick_now();
+    const vlc_tick_t host_delay_ticks = host_time == 0 ? 0
+                                      : HostTimeToTick(p_sys, host_time - mach_absolute_time());
+    const vlc_tick_t bytes_ticks = BytesToTicks(p_sys, bytes);
 
-    lock_lock(p_sys);
+    const vlc_tick_t now_ticks = vlc_tick_now();
+    const vlc_tick_t end_ticks = now_ticks + bytes_ticks + host_delay_ticks;
 
-    if (p_sys->b_do_flush)
-    {
-        ca_ClearOutBuffers(p_aout);
-        /* Signal that the renderer is flushed */
-        p_sys->b_do_flush = false;
-        vlc_sem_post(&p_sys->flush_sem);
-    }
+    lock_lock(p_sys);
 
-    if (unlikely(p_sys->i_first_render_host_time == 0))
-        goto drop;
 
     if (p_sys->b_paused)
-    {
-        p_sys->i_render_host_time = i_host_time;
         goto drop;
-    }
 
-    /* Start deferred: write silence (zeros) until we reach the first render
-     * host time. */
-    if (unlikely(p_sys->i_first_render_host_time > i_host_time ))
+    if (!p_sys->started)
     {
-        /* Convert the requested bytes into host time and check that it does
-         * not overlap between the first_render host time and the current one.
-         * */
-        const vlc_tick_t i_requested_ticks =
-            FramesToTicks(p_sys, BytesToFrames(p_sys, i_requested));
-        const int64_t i_requested_host_time =
-            TickToHostTime(p_sys, i_requested_ticks);
-        if (p_sys->i_first_render_host_time >= i_host_time + i_requested_host_time)
+        size_t tocopy;
+
+        if (p_sys->first_play_date == VLC_TICK_INVALID)
+            tocopy = bytes;
+        else
         {
-            /* Fill the buffer with silence */
-            goto drop;
+            /* Write silence to reach the first play date */
+            vlc_tick_t silence_ticks = p_sys->first_play_date - end_ticks
+                                     - GetLatency(p_aout) + bytes_ticks;
+            if (silence_ticks > 0)
+            {
+                tocopy = TicksToBytes(p_sys, silence_ticks);
+                if (tocopy > bytes)
+                    tocopy = bytes;
+            }
+            else
+                tocopy = 0;
         }
 
-        /* Write silence to reach the first_render host time */
-        const vlc_tick_t i_silence_ticks =
-            HostTimeToTick(p_sys, p_sys->i_first_render_host_time - i_host_time);
-
-        const size_t i_silence_bytes =
-            FramesToBytes(p_sys, TicksToFrames(p_sys, i_silence_ticks));
-        assert(i_silence_bytes <= i_requested);
-        memset(p_output, 0, i_silence_bytes);
+        if (tocopy > 0)
+        {
+            memset(data, 0, tocopy);
 
-        i_requested -= i_silence_bytes;
-        p_output += i_silence_bytes;
+            data += tocopy;
+            bytes -= tocopy;
 
-        /* Start the first rendering */
+            if (bytes == 0 && is_silence != NULL)
+                *is_silence = true;
+        }
     }
-    p_sys->i_render_host_time = i_host_time;
 
-    size_t i_copied = 0;
-    block_t *p_block = p_sys->p_out_chain;
-    while (p_block != NULL && i_requested != 0)
+    while (bytes > 0)
     {
-        size_t i_tocopy = __MIN(i_requested, p_block->i_buffer);
-        if (unlikely(p_sys->b_muted))
-            memset(p_output, 0, i_tocopy);
-        else
-            memcpy(p_output, p_block->p_buffer, i_tocopy);
-        i_requested -= i_tocopy;
-        i_copied += i_tocopy;
-        p_output += i_tocopy;
+        vlc_frame_t *f = p_sys->p_out_chain;
+        if (f == NULL)
+            goto drop;
+
+        size_t tocopy = f->i_buffer > bytes ? bytes : f->i_buffer;
+
+        p_sys->i_out_size -= tocopy;
+        p_sys->i_total_bytes += tocopy;
 
-        if (i_tocopy == p_block->i_buffer)
+        if (!p_sys->started
+          || (p_sys->timing_report_last_written_bytes >=
+             p_sys->timing_report_delay_bytes))
         {
-            block_t *p_release = p_block;
-            p_block = p_block->p_next;
-            block_Release(p_release);
+            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 + GetLatency(p_aout), pos_ticks);
         }
         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;
-
-    p_sys->i_total_bytes += i_copied;
+            p_sys->timing_report_last_written_bytes += tocopy;
 
-    /* Pad with 0 */
-    if (i_requested > 0)
-    {
-        assert(p_sys->i_out_size == 0);
-        p_sys->i_underrun_size += i_requested;
-        memset(p_output, 0, i_requested);
-    }
+        p_sys->started = true;
 
-    if (is_silence != NULL)
-        *is_silence = p_sys->b_muted;
+        memcpy(data, f->p_buffer, tocopy);
 
-    /* Convert host time to ticks */
-    vlc_tick_t i_host_ticks = HostTimeToTick(p_sys, i_host_time - mach_absolute_time())
-                            + i_now_ticks;
+        data += tocopy;
+        bytes -= tocopy;
+        f->i_buffer -= tocopy;
+        f->p_buffer += tocopy;
 
-    /* Report a timing every seconds */
-    if (p_sys->i_last_latency_ticks == VLC_TICK_INVALID
-     || i_host_ticks - p_sys->i_last_latency_ticks >= VLC_TICK_FROM_SEC(1))
-    {
-        vlc_tick_t frames_tick = FramesToTicks(p_sys,
-                                    BytesToFrames(p_sys, p_sys->i_total_bytes))
-                               + p_sys->i_dev_latency_ticks;
+        if (f->i_buffer == 0)
+        {
+            p_sys->p_out_chain = f->p_next;
+            if (p_sys->p_out_chain == NULL)
+                p_sys->pp_out_last = &p_sys->p_out_chain;
 
-        aout_TimingReport(p_aout, i_host_ticks, frames_tick);
-        p_sys->i_last_latency_ticks = i_host_ticks;
+            block_Release(f);
+        }
     }
 
     lock_unlock(p_sys);
@@ -264,43 +265,27 @@ ca_Render(audio_output_t *p_aout, uint64_t i_host_time,
     return;
 
 drop:
-    memset(p_output, 0, i_requested);
+    memset(data, 0, bytes);
     if (is_silence != NULL)
         *is_silence = true;
     lock_unlock(p_sys);
 }
 
-static vlc_tick_t
-ca_GetLatencyLocked(audio_output_t *p_aout)
-{
-    struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
-
-    const int64_t i_out_frames = BytesToFrames(p_sys, p_sys->i_out_size);
-    return FramesToTicks(p_sys, i_out_frames)
-           + p_sys->i_dev_latency_ticks;
-}
-
 void
 ca_Flush(audio_output_t *p_aout)
 {
     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
 
-    lock_lock(p_sys);
 
-    assert(!p_sys->b_do_flush);
-    if (p_sys->b_paused)
-        ca_ClearOutBuffers(p_aout);
-    else
-    {
-        p_sys->b_do_flush = true;
-        lock_unlock(p_sys);
-        vlc_sem_wait(&p_sys->flush_sem);
-        lock_lock(p_sys);
-    }
+    lock_lock(p_sys);
 
-    p_sys->i_render_host_time = p_sys->i_first_render_host_time = 0;
-    p_sys->i_last_latency_ticks = VLC_TICK_INVALID;
+    p_sys->started = false;
+    p_sys->i_out_size = 0;
     p_sys->i_total_bytes = 0;
+    p_sys->first_play_date = VLC_TICK_INVALID;
+    p_sys->timing_report_last_written_bytes = 0;
+
+    ca_ClearOutBuffers(p_aout);
     lock_unlock(p_sys);
 
     p_sys->b_played = false;
@@ -314,6 +299,7 @@ ca_Pause(audio_output_t * p_aout, bool pause, vlc_tick_t date)
 
     lock_lock(p_sys);
     p_sys->b_paused = pause;
+    p_sys->started = false;
     lock_unlock(p_sys);
 }
 
@@ -340,20 +326,17 @@ ca_Play(audio_output_t * p_aout, block_t * p_block, vlc_tick_t date)
 
     lock_lock(p_sys);
 
-    if (p_sys->i_render_host_time == 0)
+    if (!p_sys->started)
     {
-        /* Setup the first render time, this date must be updated until the
-         * first (non-silence/zero) frame is rendered by the render callback.
-         * Once the rendering is truly started, the date can be ignored. */
-
-        /* We can't convert date to host time directly since the clock source
-         * may be different (MONOTONIC vs continue during sleep). The solution
-         * is to convert it to a relative time and then add it to
-         * mach_absolute_time() */
-        const vlc_tick_t first_render_delay = date - vlc_tick_now()
-                                            - ca_GetLatencyLocked(p_aout);
-        p_sys->i_first_render_host_time
-            = mach_absolute_time() + TickToHostTime(p_sys, first_render_delay);
+        vlc_tick_t now = vlc_tick_now();
+        p_sys->first_play_date = date - BytesToTicks(p_sys, p_sys->i_out_size);
+
+        if (p_sys->first_play_date > now)
+            msg_Dbg(p_aout, "deferring start (%"PRId64" us)",
+                    p_sys->first_play_date - now);
+        else
+            msg_Dbg(p_aout, "starting late (%"PRId64" us)",
+                    p_sys->first_play_date - now);
     }
 
     p_sys->i_out_size += p_block->i_buffer;
@@ -374,22 +357,30 @@ 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;
 
+    p_sys->au = NULL;
+    p_sys->au_latency_ticks = 0;
     p_sys->i_underrun_size = 0;
     p_sys->b_paused = false;
+    p_sys->started = false;
     p_sys->b_muted = false;
-    p_sys->i_render_host_time = p_sys->i_first_render_host_time = 0;
-    p_sys->i_last_latency_ticks = VLC_TICK_INVALID;
+    p_sys->i_out_size = 0;
     p_sys->i_total_bytes = 0;
+    p_sys->first_play_date = VLC_TICK_INVALID;
+    p_sys->timing_report_last_written_bytes = 0;
 
     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_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);
     p_sys->b_played = false;
@@ -411,29 +402,17 @@ ca_SetAliveState(audio_output_t *p_aout, bool alive)
     struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
 
     lock_lock(p_sys);
-
-    bool b_sem_post = false;
     p_sys->b_paused = !alive;
-    if (!alive && p_sys->b_do_flush)
-    {
-        ca_ClearOutBuffers(p_aout);
-        p_sys->b_played = false;
-        p_sys->b_do_flush = false;
-        b_sem_post = true;
-    }
-
     lock_unlock(p_sys);
-
-    if (b_sem_post)
-        vlc_sem_post(&p_sys->flush_sem);
 }
 
-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);
 }
 
@@ -480,13 +459,18 @@ RenderCallback(void *p_data, AudioUnitRenderActionFlags *ioActionFlags,
     VLC_UNUSED(ioActionFlags);
     VLC_UNUSED(inTimeStamp);
     VLC_UNUSED(inBusNumber);
+
+    audio_output_t * p_aout = p_data;
+    struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
+
+    assert(inNumberFrames == BytesToFrames(p_sys, ioData->mBuffers[0].mDataByteSize));
     VLC_UNUSED(inNumberFrames);
 
     uint64_t i_host_time = (inTimeStamp->mFlags & kAudioTimeStampHostTimeValid)
                          ? inTimeStamp->mHostTime : 0;
 
     bool is_silence;
-    ca_Render(p_data, i_host_time, ioData->mBuffers[0].mData,
+    ca_Render(p_aout, i_host_time, ioData->mBuffers[0].mData,
               ioData->mBuffers[0].mDataByteSize, &is_silence);
     if (is_silence)
         *ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
@@ -718,9 +702,6 @@ static int
 MapInputLayout(audio_output_t *p_aout, const audio_sample_format_t *fmt,
                AudioChannelLayout **inlayoutp, size_t *inlayout_size)
 {
-    struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
-    uint32_t chans_out[AOUT_CHAN_MAX] = { 0, };
-
     unsigned channels = aout_FormatNbChannels(fmt);
 
     size_t size;
@@ -763,8 +744,9 @@ 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)
 {
+    struct aout_sys_common *p_sys = (struct aout_sys_common *) p_aout->sys;
     int ret;
     AudioChannelLayout *inlayout_buf = NULL;
     const AudioChannelLayout *inlayout = NULL;
@@ -886,12 +868,13 @@ 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);
         return VLC_EGENERIC;
     }
+    p_sys->au = au;
 
     return VLC_SUCCESS;
 }


=====================================
modules/audio_output/coreaudio_common.h
=====================================
@@ -45,27 +45,36 @@
 #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) */
 
+    AudioUnit au; /* Can be NULL (pass-through) */
+
     mach_timebase_info_data_t tinfo;
 
     size_t              i_underrun_size;
+    bool                started;
     bool                b_paused;
     bool                b_muted;
-    bool                b_do_flush;
 
-    size_t              i_out_size;
     bool                b_played;
     block_t             *p_out_chain;
     block_t             **pp_out_last;
-    uint64_t            i_render_host_time;
-    uint64_t            i_first_render_host_time;
-    vlc_tick_t          i_last_latency_ticks;
+    /* Size of the frame FIFO */
+    size_t              i_out_size;
+    /* Size written via the render callback */
     uint64_t            i_total_bytes;
-
-    vlc_sem_t           flush_sem;
+    /* Date when the data callback should start to process audio */
+    vlc_tick_t first_play_date;
+    /* Bytes written since the last timing report */
+    size_t timing_report_last_written_bytes;
+    /* Number of bytes to write before sending a timing report */
+    size_t timing_report_delay_bytes;
+    /* Last AudioUnit Latency, for debug/log purpose */
+    vlc_tick_t au_latency_ticks;
 
     union lock
     {
@@ -83,6 +92,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);
@@ -101,20 +111,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);
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/eb5bc46d21b479e6389a7f0205c65035e200c9da...f55b5b53bb998711b9f7d4cabcf70bca7326576e

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/eb5bc46d21b479e6389a7f0205c65035e200c9da...f55b5b53bb998711b9f7d4cabcf70bca7326576e
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list