From efe321046242e71c557abe6b9a1be7c6915e2e32 Mon Sep 17 00:00:00 2001
From: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
Date: Wed, 7 Jul 2010 19:20:25 -0500
Subject: [PATCH 2/2] AC3 passthrough support

Add new NONAUDIO_IEC958 flag for pa_stream and propagate it to sink_input.
Add similar flag PASSTHROUGH flag for sink
Handle connections, prevent mixing and volume control

TODO: fix protocol version, prevent remap/resampling,
adjust sink sample rate if needed
---
 src/modules/alsa/alsa-mixer.h   |    1 +
 src/modules/alsa/alsa-sink.c    |    6 +++++
 src/pulse/def.h                 |   18 +++++++++++++++-
 src/pulse/stream.c              |    9 +++++++-
 src/pulsecore/protocol-native.c |   16 +++++++++++++-
 src/pulsecore/sink-input.c      |   41 +++++++++++++++++++++++++++++++++++++++
 src/pulsecore/sink-input.h      |    3 +-
 src/pulsecore/sink.c            |   17 ++++++++++++++++
 src/utils/pacat.c               |    7 ++++++
 9 files changed, 112 insertions(+), 6 deletions(-)

diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index a0d4fcb..d696d5f 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -163,6 +163,7 @@ struct pa_alsa_path {
     pa_bool_t has_mute:1;
     pa_bool_t has_volume:1;
     pa_bool_t has_dB:1;
+    pa_bool_t is_passthrough:1;
 
     long min_volume, max_volume;
     double min_dB, max_dB;
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 02b5a02..dde4b0a 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1596,6 +1596,12 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
             return 0;
     }
 
+    if (!strcmp(u->mixer_path->name, "iec958-passthrough-output")) {
+        u->sink->flags |= PA_SINK_PASSTHROUGH_IEC958;
+    } else {
+        u->sink->flags &= ~PA_SINK_PASSTHROUGH_IEC958;
+    }
+
     if (!u->mixer_path->has_volume)
         pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
     else {
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 5d0a0b4..6d9b089 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -276,11 +276,17 @@ typedef enum pa_stream_flags {
      * whether to create the stream in muted or in unmuted
      * state. \since 0.9.15 */
 
-    PA_STREAM_FAIL_ON_SUSPEND = 0x20000U
+    PA_STREAM_FAIL_ON_SUSPEND = 0x20000U,
     /**< If the sink/source this stream is connected to is suspended
      * during the creation of this stream, cause it to fail. If the
      * sink/source is being suspended during creation of this stream,
      * make sure this stream is terminated. \since 0.9.15 */
+
+    PA_STREAM_NONAUDIO_IEC958 = 0x40000U
+    /**< 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.21*/
+
 } pa_stream_flags_t;
 
 /** \cond fulldocs */
@@ -307,6 +313,7 @@ typedef enum pa_stream_flags {
 #define PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
 #define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
 #define PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
+#define PA_STREAM_NONAUDIO_IEC958 PA_STREAM_NONAUDIO_IEC958
 
 /** \endcond */
 
@@ -720,9 +727,14 @@ 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_IEC958 = 0x0100U
+    /**< The latency can be adjusted dynamically depending on the
+     * needs of the connected streams. \since 0.9.21 */
+
 } pa_sink_flags_t;
 
 /** \cond fulldocs */
@@ -734,6 +746,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_IEC958 PA_SINK_PASSTHROUGH_IEC958
+
 /** \endcond */
 
 /** Sink state. \since 0.9.15 */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 8da40ec..4f48f76 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -1064,7 +1064,8 @@ static int create_stream(
                                               PA_STREAM_EARLY_REQUESTS|
                                               PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
                                               PA_STREAM_START_UNMUTED|
-                                              PA_STREAM_FAIL_ON_SUSPEND)), PA_ERR_INVALID);
+                                              PA_STREAM_FAIL_ON_SUSPEND|
+                                              PA_STREAM_NONAUDIO_IEC958)), 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);
@@ -1198,6 +1199,12 @@ static int create_stream(
         pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
     }
 
+    if (s->context->version >= 1) { // PASSTHROUGH FIXME: protocol versions!!
+
+        if (s->direction == PA_STREAM_PLAYBACK)
+            pa_tagstruct_put_boolean(t, flags & (PA_STREAM_NONAUDIO_IEC958));
+    }
+
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 337869d..3ae3074 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1871,7 +1871,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
         muted_set = FALSE,
-        fail_on_suspend = FALSE;
+        fail_on_suspend = FALSE,
+        nonaudio_iec958 = FALSE;
+
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
@@ -1964,6 +1966,15 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         }
     }
 
+    if (c->version >= 1) { // PASSTHROUGH FIXME: protocol versions
+
+        if (pa_tagstruct_get_boolean(t, &nonaudio_iec958) < 0 ) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);
         pa_proplist_free(p);
@@ -1997,7 +2008,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) |
+        (nonaudio_iec958 ? PA_SINK_INPUT_NONAUDIO_IEC958 : 0);
 
     /* Only since protocol version 15 there's a seperate muted_set
      * flag. For older versions we synthesize it here */
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 3c957f1..d687328 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -49,6 +49,38 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
 static void sink_input_free(pa_object *o);
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
 
+static pa_bool_t check_iec958_connection(pa_sink_input_flags_t flags, pa_sink *dest) {
+
+    if (dest->flags&PA_SINK_PASSTHROUGH_IEC958) {
+
+        /* make sure none of the inputs is NONAUDIO_IEC958 */
+        pa_sink_input *alt_i;
+        uint32_t idx;
+
+        for (alt_i = pa_idxset_first(dest->inputs, &idx); alt_i; alt_i=pa_idxset_next(dest->inputs, &idx)) {
+
+            if (alt_i->flags&PA_SINK_INPUT_NONAUDIO_IEC958) {
+                pa_log_warn("Sink is already connected to NONAUDIO_IEC958 input");
+                return FALSE;
+            }
+        }
+
+        if (flags & PA_SINK_INPUT_NONAUDIO_IEC958) {
+            /* make sure the sink is not connected yet */
+            if (pa_idxset_size(dest->inputs) > 0) {
+                pa_log_warn("Cannot connect NONAUDIO_IEC958 sink input: sink is already connected");
+                return FALSE;
+            }
+        }
+    } else {
+         if (flags & PA_SINK_INPUT_NONAUDIO_IEC958) {
+             pa_log_warn("Cannot connect NONAUDIO_IEC958 sink input to sink without PASSTHROUGH_IEC958 capabilities");
+             return FALSE;
+         }
+    }
+    return TRUE;
+}
+
 pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {
     pa_assert(data);
 
@@ -177,6 +209,8 @@ 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);
 
+    pa_return_val_if_fail(check_iec958_connection(data->flags,data->sink), -PA_ERR_INVALID);
+
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;
 
@@ -988,6 +1022,10 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
     pa_assert(pa_cvolume_valid(volume));
     pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
 
+    /* Do not allow for volume changes for non-audio types */
+    if (i->flags & PA_SINK_INPUT_NONAUDIO_IEC958)
+        return;
+
     if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) {
         v = i->sink->reference_volume;
         pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
@@ -1187,6 +1225,9 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
         return FALSE;
     }
 
+    if(!check_iec958_connection(i->flags,dest))
+        return FALSE;
+
     if (i->may_move_to)
         if (!i->may_move_to(i, dest))
             return FALSE;
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 415a801..cd9deb4 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -60,7 +60,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_NONAUDIO_IEC958 = 2048
 } pa_sink_input_flags_t;
 
 struct pa_sink_input {
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 3a92f67..abe2a4f 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1393,6 +1393,23 @@ 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 NONAUDIO_IEC958 input is connected */
+     if (s->flags&PA_SINK_PASSTHROUGH_IEC958) {
+
+        /* make sure none of the inputs is NONAUDIO_IEC958 */
+        pa_sink_input *alt_i;
+        uint32_t idx;
+
+        for (alt_i = pa_idxset_first(s->inputs, &idx); alt_i; alt_i=pa_idxset_next(s->inputs, &idx)) {
+
+            if (alt_i->flags&PA_SINK_INPUT_NONAUDIO_IEC958) {
+                pa_log_warn("Cannot change volume, Sink is already connected to NONAUDIO_IEC958 input");
+                return;
+            }
+        }
+     }
+
+
     /* As a special exception we accept mono volumes on all sinks --
      * even on those with more complex channel maps */
 
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index 394cfbf..9d9d5ac 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -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"
+             "      --iec958                          IEC958 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_IEC958,
     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},
+        {"iec958",       0, NULL, ARG_IEC958},
         {"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_IEC958:
+                flags |= PA_STREAM_NONAUDIO_IEC958;
+                break;
+
             case ARG_FILE_FORMAT:
                 raw = FALSE;
 
-- 
1.7.0.1

