PulseAudio: sync at start
authorRémi Denis-Courmont <remi@remlab.net>
Mon, 18 Jul 2011 15:32:31 +0000 (18:32 +0300)
committerRémi Denis-Courmont <remi@remlab.net>
Mon, 18 Jul 2011 16:50:10 +0000 (19:50 +0300)
modules/audio_output/pulse.c

index 5113637..4927fa3 100644 (file)
@@ -203,9 +203,12 @@ static void stream_suspended_cb(pa_stream *s, void *userdata)
 static void stream_underflow_cb(pa_stream *s, void *userdata)
 {
     aout_instance_t *aout = userdata;
+    pa_operation *op;
 
-    msg_Dbg(aout, "underflow");
-    (void) s;
+    msg_Warn(aout, "underflow");
+    op = pa_stream_cork(s, 1, NULL, NULL);
+    if (op != NULL)
+        pa_operation_unref(op);
 }
 
 static int stream_wait(pa_threaded_mainloop *mainloop, pa_stream *stream)
@@ -252,6 +255,17 @@ static void Play(aout_instance_t *aout)
     aout_sys_t *sys = aout->output.p_sys;
     pa_stream *s = sys->stream;
 
+    /* This function is called exactly once per block in the output FIFO. */
+    block_t *block = aout_FifoPop(&aout->output.fifo);
+    assert (block != NULL);
+
+    const void *ptr = data_convert(&block);
+    if (unlikely(ptr == NULL))
+        return;
+
+    size_t len = block->i_buffer;
+    mtime_t pts = block->i_pts + block->i_length;
+
     /* Note: The core already holds the output FIFO lock at this point.
      * Therefore we must not under any circumstances (try to) acquire the
      * output FIFO lock while the PulseAudio threaded main loop lock is held
@@ -260,7 +274,41 @@ static void Play(aout_instance_t *aout)
     pa_threaded_mainloop_lock(sys->mainloop);
 
     if (pa_stream_is_corked(s) > 0) {
-        pa_operation *op = pa_stream_cork(s, 0, NULL, NULL);
+        /* Start or resume the stream. Zeroes are prepended to sync.
+         * This does not really work because PulseAudio latency measurement is
+         * garbage at start. */
+        pa_operation *op;
+        pa_usec_t latency;
+        int negative;
+
+        if (pa_stream_get_latency(s, &latency, &negative) == 0)
+            msg_Dbg(aout, "starting with %c%"PRIu64" us latency",
+                    negative ? '-' : '+', latency);
+        else
+            latency = negative = 0;
+
+        mtime_t advance = block->i_pts - mdate();
+        if (negative)
+            advance += latency;
+        else
+            advance -= latency;
+
+        if (advance > 0) {
+            size_t nb = (advance * aout->output.output.i_rate) / CLOCK_FREQ;
+            size_t size = aout->output.output.i_bytes_per_frame;
+            float *zeroes = calloc (nb, size);
+
+            msg_Dbg(aout, "prepending %zu zeroes", nb);
+            if (likely(zeroes != NULL))
+                if (pa_stream_write(s, zeroes, nb * size, free, 0,
+                                    PA_SEEK_RELATIVE) < 0)
+                    free(zeroes);
+        }
+
+        op = pa_stream_cork(s, 0, NULL, NULL);
+        if (op != NULL)
+            pa_operation_unref(op);
+        op = pa_stream_trigger(s, NULL, NULL);
         if (op != NULL)
             pa_operation_unref(op);
         msg_Dbg(aout, "uncorking");
@@ -274,27 +322,9 @@ static void Play(aout_instance_t *aout)
     }
 #endif
 
-    /* This function is called exactly once per block in the output FIFO, so
-     * this for-loop is not necessary.
-     * If this function is changed to not always dequeue blocks, be sure to
-     * limit the queue size to a reasonable limit to avoid huge leaks. */
-    for (;;) {
-        block_t *block = aout_FifoPop(&aout->output.fifo);
-        if (block == NULL)
-            break;
-
-        const void *ptr = data_convert(&block);
-        if (unlikely(ptr == NULL))
-            break;
-
-        size_t len = block->i_buffer;
-        //mtime_t pts = block->i_pts, duration = block->i_length;
-
-        if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0)
-        {
-            error(aout, "cannot write", sys->context);
-            block_Release(block);
-        }
+    if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0) {
+        error(aout, "cannot write", sys->context);
+        block_Release(block);
     }
 
     pa_threaded_mainloop_unlock(sys->mainloop);
@@ -308,14 +338,12 @@ static void Pause(aout_instance_t *aout, bool b_paused, mtime_t i_date)
     aout_sys_t *sys = aout->output.p_sys;
     pa_stream *s = sys->stream;
 
-    /* Note: The core already holds the output FIFO lock at this point.
-     * Therefore we must not under any circumstances (try to) acquire the
-     * output FIFO lock while the PulseAudio threaded main loop lock is held
-     * (including from PulseAudio stream callbacks). Otherwise lock inversion
-     * will take place, and sooner or later a deadlock. */
+    if (!b_paused)
+        return; /* nothing to do - yet */
+
     pa_threaded_mainloop_lock(sys->mainloop);
 
-    pa_operation *op = pa_stream_cork(s, b_paused ? 1 : 0, NULL, NULL);
+    pa_operation *op = pa_stream_cork(s, 1, NULL, NULL);
     if (op != NULL)
         pa_operation_unref(op);
 
@@ -490,15 +518,15 @@ static int Open(vlc_object_t *obj)
     }
 
     /* Stream parameters */
-    const pa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING
-                                  | PA_STREAM_AUTO_TIMING_UPDATE
-                                  | PA_STREAM_START_CORKED;
+    const pa_stream_flags_t flags = PA_STREAM_START_CORKED
+                                  //| PA_STREAM_INTERPOLATE_TIMING
+                                  | PA_STREAM_AUTO_TIMING_UPDATE;
 
     const uint32_t byterate = pa_bytes_per_second(&ss);
     struct pa_buffer_attr attr;
     attr.maxlength = -1;
     attr.tlength = byterate * AOUT_MAX_PREPARE_TIME / CLOCK_FREQ;
-    attr.prebuf = -1;
+    attr.prebuf = 0; /* trigger manually */
     attr.minreq = -1;
     attr.fragsize = 0; /* not used for output */