]> git.sesse.net Git - vlc/blobdiff - modules/audio_output/pulse.c
macosx: re-implemented time slider to fit the new style
[vlc] / modules / audio_output / pulse.c
index 25907d34fa8d8a0b3470136a15bd38d8d7962f80..e2ee41988a0770622a3a11ea2bd34e94354a858f 100644 (file)
@@ -47,6 +47,12 @@ vlc_module_end ()
 
 /* TODO: single static mainloop */
 
+/* NOTE:
+ * Be careful what you do when the PulseAudio mainloop is held, which is to say
+ * within PulseAudio callbacks, or after pa_threaded_mainloop_lock().
+ * In particular, a VLC variable callback cannot be triggered nor deleted with
+ * the PulseAudio mainloop lock held, if the callback acquires the lock. */
+
 struct aout_sys_t
 {
     pa_stream *stream; /**< PulseAudio playback stream object */
@@ -90,6 +96,23 @@ static void error(aout_instance_t *aout, const char *msg, pa_context *context)
 }
 
 /* Sink */
+static void sink_list_cb(pa_context *c, const pa_sink_info *i, int eol,
+                         void *userdata)
+{
+    aout_instance_t *aout = userdata;
+    vlc_value_t val, text;
+
+    if (eol)
+        return;
+    (void) c;
+
+    msg_Dbg(aout, "listing sink %s (%"PRIu32"): %s", i->name, i->index,
+            i->description);
+    val.i_int = i->index;
+    text.psz_string = (char *)i->description;
+    var_Change(aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text);
+}
+
 static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol,
                          void *userdata)
 {
@@ -140,6 +163,10 @@ static void stream_moved_cb(pa_stream *s, void *userdata)
                                            sink_info_cb, aout);
     if (likely(op != NULL))
         pa_operation_unref(op);
+
+    /* Update the variable if someone else moved our stream */
+    var_Change(aout, "audio-device", VLC_VAR_SETVALUE,
+               &(vlc_value_t){ .i_int = idx }, NULL);
 }
 
 static void stream_overflow_cb(pa_stream *s, void *userdata)
@@ -246,7 +273,7 @@ static void Play(aout_instance_t *aout)
         if (pa_context_errno(sys->context) != PA_ERR_NODATA)
             error(aout, "cannot determine latency", sys->context);
     } else {
-        mtime_t gap = aout_FifoFirstDate(aout, &aout->output.fifo) - mdate()
+        mtime_t gap = aout_FifoFirstDate(&aout->output.fifo) - mdate()
                 - latency;
 
         if (gap > AOUT_PTS_TOLERANCE)
@@ -268,7 +295,7 @@ static void Play(aout_instance_t *aout)
      * 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, &aout->output.fifo);
+        block_t *block = aout_FifoPop(&aout->output.fifo);
         if (block == NULL)
             break;
 
@@ -281,9 +308,8 @@ static void Play(aout_instance_t *aout)
 
         if (pa_stream_write(s, ptr, len, data_free, 0, PA_SEEK_RELATIVE) < 0)
         {
+            error(aout, "cannot write", sys->context);
             block_Release(block);
-            msg_Err(aout, "cannot write: %s",
-                    pa_strerror(pa_context_errno(sys->context)));
         }
     }
 
@@ -318,12 +344,39 @@ static int VolumeSet(aout_instance_t *aout, audio_volume_t vol, bool mute)
     return 0;
 }
 
+static int StreamMove(vlc_object_t *obj, const char *varname, vlc_value_t old,
+                      vlc_value_t val, void *userdata)
+{
+    aout_instance_t *aout = (aout_instance_t *)obj;
+    aout_sys_t *sys = aout->output.p_sys;
+    pa_stream *s = userdata;
+    pa_operation *op;
+    uint32_t idx = pa_stream_get_index(s);
+    uint32_t sink_idx = val.i_int;
+
+    (void) varname; (void) old;
+
+    pa_threaded_mainloop_lock(sys->mainloop);
+    op = pa_context_move_sink_input_by_index(sys->context, idx, sink_idx,
+                                             NULL, NULL);
+    if (likely(op != NULL)) {
+        pa_operation_unref(op);
+        msg_Dbg(aout, "moving to sink %"PRIu32, sink_idx);
+    } else
+        error(aout, "cannot move sink", sys->context);
+    pa_threaded_mainloop_unlock(sys->mainloop);
+
+    return (op != NULL) ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+
 /*****************************************************************************
  * Open: open the audio device
  *****************************************************************************/
 static int Open(vlc_object_t *obj)
 {
     aout_instance_t *aout = (aout_instance_t *)obj;
+    pa_operation *op;
 
     /* Sample format specification */
     struct pa_sample_spec ss;
@@ -507,7 +560,6 @@ static int Open(vlc_object_t *obj)
         error(aout, "cannot connect stream", ctx);
         goto fail;
     }
-    stream_moved_cb(s, aout);
 
     const struct pa_buffer_attr *pba = pa_stream_get_buffer_attr(s);
     msg_Dbg(aout, "using buffer metrics: maxlength=%u, tlength=%u, "
@@ -515,6 +567,17 @@ static int Open(vlc_object_t *obj)
             pba->maxlength, pba->tlength, pba->prebuf, pba->minreq);
 
     aout->output.i_nb_samples = pba->minreq / pa_frame_size(&ss);
+
+    var_Create(aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
+    var_Change(aout, "audio-device", VLC_VAR_SETTEXT,
+               &(vlc_value_t){ .psz_string = (char *)_("Audio device") },
+               NULL);
+    var_AddCallback (aout, "audio-device", StreamMove, s);
+    op = pa_context_get_sink_info_list(ctx, sink_list_cb, aout);
+    /* We may need to wait for completion... once LibVLC supports this */
+    if (op != NULL)
+        pa_operation_unref(op);
+    stream_moved_cb(s, aout);
     pa_threaded_mainloop_unlock(mainloop);
 
     aout->output.pf_play = Play;
@@ -538,6 +601,12 @@ static void Close (vlc_object_t *obj)
     pa_context *ctx = sys->context;
     pa_stream *s = sys->stream;
 
+    if (s != NULL) {
+        /* The callback takes mainloop lock, so it CANNOT be held here! */
+        var_DelCallback (aout, "audio-device", StreamMove, s);
+        var_Destroy (aout, "audio-device");
+    }
+
     pa_threaded_mainloop_lock(mainloop);
     if (s != NULL) {
         pa_operation *op;