[vlc-commits] [Git][videolan/vlc][master] 2 commits: alsa: add helper function that writes and recover

Thomas Guillem (@tguillem) gitlab at videolan.org
Wed May 27 15:15:13 UTC 2026



Thomas Guillem pushed to branch master at VideoLAN / VLC


Commits:
f18ee115 by Thomas Guillem at 2026-05-27T14:55:39+00:00
alsa: add helper function that writes and recover

- - - - -
792e135f by Thomas Guillem at 2026-05-27T14:55:39+00:00
alsa: implement aout_TimingReport + deferred start

Tested with a Sync-One2 device: A/V sync stays the same (-30ms). It's
coherent since the first silence write was handled by the core from a
hack in dec.c (that will be soon removed).

The clock is way more stable now (no reset).

Both additions interact with each others, hard to split in 2 commits.

- - - - -


1 changed file:

- modules/audio_output/alsa.c


Changes:

=====================================
modules/audio_output/alsa.c
=====================================
@@ -127,6 +127,14 @@ typedef struct
     vlc_frame_t *frame_chain;
     vlc_frame_t **frame_last;
     uint64_t queued_samples;
+
+    bool starting;
+    struct {
+        vlc_tick_t start_date;
+        vlc_tick_t first_pts;
+        vlc_tick_t next_update;
+        uint64_t injected_samples;
+    } time;
 } aout_sys_t;
 
 #include "audio_output/volume.h"
@@ -217,12 +225,43 @@ static int fill_pfds_from_state_locked(audio_output_t *aout, struct pollfd **pfd
     return -1;
 }
 
+static snd_pcm_sframes_t PcmWrite(audio_output_t *aout, snd_pcm_t *pcm,
+                                  const void *buffer, snd_pcm_sframes_t samples)
+{
+    snd_pcm_sframes_t frames = snd_pcm_writei(pcm, buffer, samples);
+    if (frames >= 0 || frames == -EAGAIN)
+        return frames;
+    int val = snd_pcm_recover(pcm, frames, 1);
+    if (val)
+    {
+        msg_Err(aout, "cannot recover playback stream: %s",
+                snd_strerror (val));
+        DumpDeviceStatus(aout->obj.logger, pcm);
+        return frames;
+    }
+    msg_Warn(aout, "cannot write samples: %s", snd_strerror(frames));
+    return -EAGAIN;
+}
+
 static void * InjectionThread(void * data)
 {
     audio_output_t *aout = (audio_output_t *) data;
     aout_sys_t *sys = aout->sys;
     snd_pcm_t *pcm = sys->pcm;
 
+    int64_t silence_buffer_frames =
+        samples_from_vlc_tick(AOUT_MIN_PREPARE_TIME, sys->rate);
+    size_t silence_buffer_bytes = snd_pcm_frames_to_bytes(pcm, silence_buffer_frames);
+
+    uint8_t *silence_buffer = malloc(silence_buffer_bytes);
+    if (silence_buffer == NULL)
+    {
+        sys->unrecoverable_error = true;
+        vlc_sem_post(&sys->init_sem);
+        return NULL;
+    }
+    memset(silence_buffer, 0, silence_buffer_bytes);
+
     /* We're expecting at least 2 fds:
      * - one for generic wakeup
      * - one or more for alsa
@@ -230,9 +269,10 @@ static void * InjectionThread(void * data)
     struct pollfd * pfds = calloc(2, sizeof(struct pollfd));
     if (pfds == NULL)
     {
-      sys->unrecoverable_error = true;
-      vlc_sem_post(&sys->init_sem);
-      return NULL;
+        free(silence_buffer);
+        sys->unrecoverable_error = true;
+        vlc_sem_post(&sys->init_sem);
+        return NULL;
     }
     int pfds_count = 2;
 
@@ -326,8 +366,47 @@ static void * InjectionThread(void * data)
             continue;
         }
 
+        if (sys->starting)
+        {
+            vlc_tick_t now = vlc_tick_now();
+            snd_pcm_sframes_t delay_frames;
+            int val = snd_pcm_delay(sys->pcm, &delay_frames);
+            vlc_tick_t delay_ticks =
+                (val == 0) ? vlc_tick_from_samples(delay_frames, sys->rate) : 0;
+            vlc_tick_t silence_ticks = sys->time.start_date - now - delay_ticks;
+            if (silence_ticks > 0)
+            {
+                vlc_tick_t write_silence_ticks =
+                    silence_ticks > AOUT_MIN_PREPARE_TIME ? AOUT_MIN_PREPARE_TIME
+                                                          : silence_ticks;
+                int64_t silence_frames = samples_from_vlc_tick(write_silence_ticks,
+                                                               sys->rate);
+
+                assert(silence_frames <= silence_buffer_frames);
+                snd_pcm_sframes_t frames = PcmWrite(aout, pcm, silence_buffer,
+                                                    silence_frames);
+                if (frames == -EAGAIN)
+                    continue;
+                if (frames < 0)
+                    break;
+
+                msg_Dbg(aout, "deferring start (%"PRId64" us)", silence_ticks);
+                if (write_silence_ticks == AOUT_MIN_PREPARE_TIME)
+                    continue;
+
+                sys->starting = false;
+            }
+            else
+            {
+                msg_Dbg(aout, "starting late (%"PRId64" us)", silence_ticks);
+                sys->starting = false;
+            }
+        }
+
+        assert(!sys->starting);
+
         vlc_frame_t * f = sys->frame_chain;
-        snd_pcm_sframes_t frames = snd_pcm_writei(pcm, f->p_buffer, f->i_nb_samples);
+        snd_pcm_sframes_t frames = PcmWrite(aout, pcm, f->p_buffer, f->i_nb_samples);
         if (frames >= 0)
         {
             size_t bytes = snd_pcm_frames_to_bytes(pcm, frames);
@@ -335,6 +414,7 @@ static void * InjectionThread(void * data)
             f->p_buffer += bytes;
             f->i_buffer -= bytes;
             sys->queued_samples -= frames;
+            sys->time.injected_samples += frames;
             // pts, length
             if (f->i_nb_samples == 0)
             {
@@ -347,19 +427,27 @@ static void * InjectionThread(void * data)
         else if (frames == -EAGAIN)
             continue;
         else
+            break;
+
+        vlc_tick_t now = vlc_tick_now();
+        if (sys->time.first_pts != VLC_TICK_INVALID && now > sys->time.next_update)
         {
-            int val = snd_pcm_recover(pcm, frames, 1);
-            if (val)
+            sys->time.next_update = now + VLC_TICK_FROM_SEC(1);
+            int val = snd_pcm_delay(sys->pcm, &frames);
+            if (val != 0)
             {
-                msg_Err(aout, "cannot recover playback stream: %s",
-                        snd_strerror (val));
-                DumpDeviceStatus(aout->obj.logger, pcm);
-                break;
+                msg_Err(aout, "cannot estimate delay: %s", snd_strerror(val));
+                continue;
             }
-            msg_Warn(aout, "cannot write samples: %s", snd_strerror(frames));
+            vlc_tick_t delay = vlc_tick_from_samples(frames, sys->rate);
+            vlc_tick_t injected_samples_ticks =
+                vlc_tick_from_samples(sys->time.injected_samples, sys->rate);
+            aout_TimingReport(aout, now + delay,
+                              sys->time.first_pts + injected_samples_ticks);
         }
     }
     free(pfds);
+    free(silence_buffer);
     if (sys->started && !sys->unrecoverable_error)
     {
         msg_Err(aout, "Unhandled error in injection thread, requesting aout restart");
@@ -369,25 +457,6 @@ static void * InjectionThread(void * data)
     return NULL;
 }
 
-static int TimeGet(audio_output_t *aout, vlc_tick_t *restrict delay)
-{
-    aout_sys_t *sys = aout->sys;
-    snd_pcm_sframes_t frames;
-
-    vlc_mutex_lock(&sys->lock);
-    int val = snd_pcm_delay(sys->pcm, &frames);
-    if (val)
-    {
-        msg_Err(aout, "cannot estimate delay: %s", snd_strerror(val));
-        vlc_mutex_unlock(&sys->lock);
-        return -1;
-    }
-    *delay = vlc_tick_from_samples(frames + sys->queued_samples, sys->rate);
-    vlc_mutex_unlock(&sys->lock);
-    return 0;
-}
-
-
 /**
  * Queues one audio buffer to the hardware.
  */
@@ -407,6 +476,15 @@ static void Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
         vlc_mutex_unlock(&sys->lock);
         return;
     }
+    if (sys->time.start_date == VLC_TICK_INVALID)
+    {
+        sys->starting = true;
+        sys->time.next_update = VLC_TICK_0;
+        sys->time.first_pts = block->i_pts;
+    }
+    if (sys->starting)
+        sys->time.start_date = date
+                             - vlc_tick_from_samples(sys->queued_samples, sys->rate);
     if (sys->frame_chain == NULL)
         wake_poll(sys);
     vlc_frame_ChainLastAppend(&sys->frame_last, block);
@@ -426,6 +504,11 @@ static void PauseDummy(audio_output_t *aout, bool pause, vlc_tick_t date)
     if (pause)
     {
         sys->state = PAUSED;
+        sys->starting = false;
+        sys->time.start_date = VLC_TICK_INVALID;
+        sys->time.first_pts = VLC_TICK_INVALID;
+        sys->time.next_update = VLC_TICK_0;
+        sys->time.injected_samples = 0;
         snd_pcm_drop(pcm);
     }
     else
@@ -473,6 +556,11 @@ static void Flush (audio_output_t *aout)
     sys->frame_last = &sys->frame_chain;
     sys->queued_samples = 0;
     sys->draining = false;
+    sys->starting = false;
+    sys->time.start_date = VLC_TICK_INVALID;
+    sys->time.first_pts = VLC_TICK_INVALID;
+    sys->time.next_update = VLC_TICK_0;
+    sys->time.injected_samples = 0;
 
     if (sys->state == IDLE)
         sys->state = PLAYING;
@@ -995,6 +1083,11 @@ static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
     sys->draining = false;
     sys->state = PLAYING;
     sys->unrecoverable_error = false;
+    sys->starting = false;
+    sys->time.start_date = VLC_TICK_INVALID;
+    sys->time.first_pts = VLC_TICK_INVALID;
+    sys->time.next_update = VLC_TICK_0;
+    sys->time.injected_samples = 0;
     if (vlc_clone(&sys->thread, InjectionThread, aout))
         goto error;
 
@@ -1140,7 +1233,7 @@ static int Open(vlc_object_t *obj)
     sys->frame_last = &sys->frame_chain;
     vlc_sem_init(&sys->init_sem, 0);
 
-    aout->time_get = TimeGet;
+    aout->time_get = NULL;
     aout->play = Play;
     aout->flush = Flush;
     aout->drain = Drain;



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0802bfb89b531e32a75dcfac419ae3382dd0570f...792e135f18ff134695c5f3b002f2a07481752931

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/0802bfb89b531e32a75dcfac419ae3382dd0570f...792e135f18ff134695c5f3b002f2a07481752931
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list