]> code.delx.au - pulseaudio/commitdiff
AC3 passthrough support
authorPierre-Louis Bossart <pierre-louis.bossart@intel.com>
Fri, 16 Jul 2010 21:46:28 +0000 (16:46 -0500)
committerColin Guthrie <cguthrie@mandriva.org>
Thu, 12 Aug 2010 17:20:27 +0000 (18:20 +0100)
Second version after Tanu's feedback

TODO:
    - notify client that volume control is disabled
    - change sink rate in passthrough mode if needed
    - automatic detection of passthrough mode instead of hard
    coded profile names

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
PROTOCOL
configure.ac
src/modules/alsa/alsa-sink.c
src/pulse/def.h
src/pulse/stream.c
src/pulsecore/protocol-native.c
src/pulsecore/sink-input.c
src/pulsecore/sink-input.h
src/pulsecore/sink.c
src/utils/pacat.c

index 883b8bb651898af319518d556261a14f9c04a0ca..f180b96d53d03b4dc429598fa5d5dc06c792b9c6 100644 (file)
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -194,3 +194,9 @@ new messages:
 new flag at end of CREATE_PLAYBACK_STREAM:
 
     bool relative_volume
+
+## v18, implemented by >= 0.9.22
+
+new flag at end of CREATE_PLAYBACK_STREAM:
+
+    bool passthrough
index abb8daeb2f69d0c30c02b33cc02349039f0ea803..a3551f0690c2aa086afca8b0640d1ea041a5850d 100644 (file)
@@ -40,7 +40,7 @@ AC_SUBST(PA_MAJORMINORMICRO, pa_major.pa_minor.pa_micro)
 AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
 
 AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 17)
+AC_SUBST(PA_PROTOCOL_VERSION, 18)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
index 2e54dbe732c763189af27c180c848ac21920edb9..80fdcaa2ee8463b32935e31b6852c96933b3f018 100644 (file)
@@ -1616,6 +1616,13 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
             return 0;
     }
 
+    /* FIXME: need automatic detection rather than hard-coded path */
+    if (!strcmp(u->mixer_path->name, "iec958-passthrough-output")) {
+        u->sink->flags |= PA_SINK_PASSTHROUGH;
+    } else {
+        u->sink->flags &= ~PA_SINK_PASSTHROUGH;
+    }
+
     if (!u->mixer_path->has_volume)
         pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
     else {
index 82106ef8d9c74e1de3ab8efde96abf737097c445..80d2a5084c67c03586140194cf8bc4d4a973a564 100644 (file)
@@ -289,6 +289,12 @@ typedef enum pa_stream_flags {
      * device volume. If this is not specified the volume will be
      * consider absolute when the sink is in flat volume mode,
      * relative otherwise. \since 0.9.20 */
+
+    PA_STREAM_PASSTHROUGH = 0x80000U
+    /**< Used to tag content that will be rendered by passthrough sinks.
+     * The data will be left as is and not reformatted, resampled.
+     * \since 0.9.22*/
+
 } pa_stream_flags_t;
 
 /** \cond fulldocs */
@@ -316,6 +322,7 @@ typedef enum pa_stream_flags {
 #define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
 #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
 #define PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
+#define PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
 
 /** \endcond */
 
@@ -729,9 +736,15 @@ typedef enum pa_sink_flags {
     /**< This sink is in flat volume mode, i.e. always the maximum of
      * the volume of all connected inputs. \since 0.9.15 */
 
-    PA_SINK_DYNAMIC_LATENCY = 0x0080U
+    PA_SINK_DYNAMIC_LATENCY = 0x0080U,
     /**< The latency can be adjusted dynamically depending on the
      * needs of the connected streams. \since 0.9.15 */
+
+    PA_SINK_PASSTHROUGH = 0x0100U
+    /**< This sink has support for passthrough mode. The data will be left
+     * as is and not reformatted, resampled, mixed.
+     * \since 0.9.22*/
+
 } pa_sink_flags_t;
 
 /** \cond fulldocs */
@@ -743,6 +756,8 @@ typedef enum pa_sink_flags {
 #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
 #define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
 #define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
+#define PA_SINK_PASSTHROUGH PA_SINK_PASSTHROUGH
+
 /** \endcond */
 
 /** Sink state. \since 0.9.15 */
index 36514e0e8f5750f4bf66ef0c9cae4c4916f37775..87c24ba83c187ad9c3d289d3e30684eade46e467 100644 (file)
@@ -1065,7 +1065,9 @@ static int create_stream(
                                               PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
                                               PA_STREAM_START_UNMUTED|
                                               PA_STREAM_FAIL_ON_SUSPEND|
-                                              PA_STREAM_RELATIVE_VOLUME)), PA_ERR_INVALID);
+                                              PA_STREAM_RELATIVE_VOLUME|
+                                              PA_STREAM_PASSTHROUGH)), PA_ERR_INVALID);
+
 
     PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
     PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
@@ -1206,6 +1208,12 @@ static int create_stream(
 
     }
 
+    if (s->context->version >= 18) {
+
+        if (s->direction == PA_STREAM_PLAYBACK)
+            pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
+    }
+
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
 
index f3d240b355024a18be06144a5382f649deb23b19..adb995f0c3d33f6a9683089fb48c2f18dce8fecf 100644 (file)
@@ -1873,7 +1873,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         dont_inhibit_auto_suspend = FALSE,
         muted_set = FALSE,
         fail_on_suspend = FALSE,
-        relative_volume = FALSE;
+        relative_volume = FALSE,
+        passthrough = FALSE;
+
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
@@ -1975,6 +1977,15 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         }
     }
 
+    if (c->version >= 18) {
+
+        if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);
         pa_proplist_free(p);
@@ -2008,7 +2019,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         (no_move ?  PA_SINK_INPUT_DONT_MOVE : 0) |
         (variable_rate ?  PA_SINK_INPUT_VARIABLE_RATE : 0) |
         (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
-        (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0);
+        (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) |
+        (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0);
 
     /* Only since protocol version 15 there's a seperate muted_set
      * flag. For older versions we synthesize it here */
index 395110b7fe412656dc35c52f622ab8ec4cfd833c..190e2d01d9e6699f0134a656e712747043513121 100644 (file)
@@ -55,6 +55,39 @@ static void sink_input_volume_ramping(pa_sink_input* i, pa_memchunk* chunk);
 static void sink_input_rewind_ramp_info(pa_sink_input *i, size_t nbytes);
 static void sink_input_release_envelope(pa_sink_input *i);
 
+static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *dest) {
+
+    if (dest->flags & PA_SINK_PASSTHROUGH) {
+
+        if (pa_idxset_size(dest->inputs) > 0) {
+
+            pa_sink_input *alt_i;
+            uint32_t idx;
+
+            alt_i = pa_idxset_first(dest->inputs, &idx);
+
+            /* only need to check the first input is not PASSTHROUGH */
+            if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
+                pa_log_warn("Sink is already connected to PASSTHROUGH input");
+                return -PA_ERR_BUSY;
+            }
+
+            /* Current inputs are PCM, check new input is not PASSTHROUGH */
+            if (flags & PA_SINK_INPUT_PASSTHROUGH) {
+                pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
+                return -PA_ERR_BUSY;
+            }
+        }
+
+    } else {
+         if (flags & PA_SINK_INPUT_PASSTHROUGH) {
+             pa_log_warn("Cannot connect PASSTHROUGH sink input to sink without PASSTHROUGH capabilities");
+             return -PA_ERR_INVALID;
+         }
+    }
+    return PA_OK;
+}
+
 pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
     pa_assert(data);
 
@@ -183,6 +216,9 @@ int pa_sink_input_new(
     pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
     pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
 
+    r = check_passthrough_connection(data->flags, data->sink);
+    pa_return_val_if_fail(r == PA_OK, r);
+
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;
 
@@ -1020,6 +1056,11 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
 
 /* Called from main context */
 void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
+
+    /* Do not allow for volume changes for non-audio types */
+    if (i->flags & PA_SINK_INPUT_PASSTHROUGH)
+        return;
+
     /* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */
     return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0);
 }
@@ -1162,6 +1203,9 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
         return FALSE;
     }
 
+    if (check_passthrough_connection(i->flags, dest) < 0)
+        return FALSE;
+
     if (i->may_move_to)
         if (!i->may_move_to(i, dest))
             return FALSE;
index 56ac3d606aa14d1621ca864b27a2ff55f6fa5ac9..4491d8a3d1158b8341a47f2e49c20dd6b823ad3f 100644 (file)
@@ -61,7 +61,8 @@ typedef enum pa_sink_input_flags {
     PA_SINK_INPUT_FIX_CHANNELS = 128,
     PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
     PA_SINK_INPUT_NO_CREATE_ON_SUSPEND = 512,
-    PA_SINK_INPUT_KILL_ON_SUSPEND = 1024
+    PA_SINK_INPUT_KILL_ON_SUSPEND = 1024,
+    PA_SINK_INPUT_PASSTHROUGH = 2048
 } pa_sink_input_flags_t;
 
 struct pa_sink_input {
index d69f03882201b07604b30bb3049cf823a289bd51..b68ad3aa8a557a9d0c0dfb2e2b3c10b2119b2964 100644 (file)
@@ -1393,6 +1393,24 @@ void pa_sink_set_volume(
     pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME));
     pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
 
+    /* make sure we don't change the volume when a PASSTHROUGH input is connected */
+    if (s->flags & PA_SINK_PASSTHROUGH) {
+        pa_sink_input *alt_i;
+        uint32_t idx;
+
+        /* one and only one PASSTHROUGH input can possibly be connected */
+        if (pa_idxset_size(s->inputs) == 1) {
+
+            alt_i = pa_idxset_first(s->inputs, &idx);
+
+            if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
+                /* FIXME: Need to notify client that volume control is disabled */
+                pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
+                return;
+            }
+        }
+    }
+
     /* As a special exception we accept mono volumes on all sinks --
      * even on those with more complex channel maps */
 
index 6cbff2e6fa7927f111417d89ceec7b16980fe933..79936fd70f58b311ccabcd09474850a047f74055 100644 (file)
@@ -659,6 +659,7 @@ static void help(const char *argv0) {
              "      --process-time-msec=MSEC          Request the specified process time per request in msec.\n"
              "      --property=PROPERTY=VALUE         Set the specified property to the specified value.\n"
              "      --raw                             Record/play raw PCM data.\n"
+             "      --passthrough                     passthrough data \n"
              "      --file-format[=FFORMAT]           Record/play formatted PCM data.\n"
              "      --list-file-formats               List available file formats.\n")
            , argv0);
@@ -680,6 +681,7 @@ enum {
     ARG_LATENCY,
     ARG_PROCESS_TIME,
     ARG_RAW,
+    ARG_PASSTHROUGH,
     ARG_PROPERTY,
     ARG_FILE_FORMAT,
     ARG_LIST_FILE_FORMATS,
@@ -718,6 +720,7 @@ int main(int argc, char *argv[]) {
         {"process-time", 1, NULL, ARG_PROCESS_TIME},
         {"property",     1, NULL, ARG_PROPERTY},
         {"raw",          0, NULL, ARG_RAW},
+        {"passthrough",  0, NULL, ARG_PASSTHROUGH},
         {"file-format",  2, NULL, ARG_FILE_FORMAT},
         {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
         {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
@@ -914,6 +917,10 @@ int main(int argc, char *argv[]) {
                 raw = TRUE;
                 break;
 
+            case ARG_PASSTHROUGH:
+                flags |= PA_STREAM_PASSTHROUGH;
+                break;
+
             case ARG_FILE_FORMAT:
                 raw = FALSE;