--- oss4-sink.h-orig	2010-04-13 15:20:09.129598637 -0500
+++ oss4-sink.h	2010-04-13 14:09:35.128107679 -0500
@@ -44,6 +44,7 @@ struct _GstOss4Sink {
   gchar       * device_name;        /* set if the device is open */
   gint          fd;                 /* -1 if not open            */
   gint          bytes_per_sample;
+  gint          mute_volume;
 
   GstCaps     * probed_caps;
 
--- oss4-sink.c-orig	2010-04-13 13:38:38.894482133 -0500
+++ oss4-sink.c	2010-04-13 15:27:29.084461729 -0500
@@ -59,6 +59,7 @@
 #include <string.h>
 
 #include <gst/gst-i18n-plugin.h>
+#include <gst/interfaces/streamvolume.h>
 
 #define NO_LEGACY_MIXER
 #include "oss4-audio.h"
@@ -93,12 +94,18 @@ static void gst_oss4_sink_reset (GstAudi
 
 #define DEFAULT_DEVICE      NULL
 #define DEFAULT_DEVICE_NAME NULL
+#define DEFAULT_MUTE        FALSE
+#define DEFAULT_VOLUME      1.0
+#define MAX_VOLUME          10.0
 
 enum
 {
   PROP_0,
   PROP_DEVICE,
-  PROP_DEVICE_NAME
+  PROP_DEVICE_NAME,
+  PROP_VOLUME,
+  PROP_MUTE,
+  PROP_LAST
 };
 
 GST_BOILERPLATE_FULL (GstOss4Sink, gst_oss4_sink, GstAudioSink,
@@ -156,6 +163,18 @@ gst_oss4_sink_class_init (GstOss4SinkCla
           "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
           G_PARAM_READABLE));
 
+  g_object_class_install_property (gobject_class,
+      PROP_VOLUME,
+      g_param_spec_double ("volume", "Volume",
+          "Linear volume of this stream, 1.0=100%", 0.0, MAX_VOLUME,
+          DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class,
+      PROP_MUTE,
+      g_param_spec_boolean ("mute", "Mute",
+          "Mute state of this stream", DEFAULT_MUTE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_oss4_sink_getcaps);
 
   audiosink_class->open = GST_DEBUG_FUNCPTR (gst_oss4_sink_open_func);
@@ -180,6 +199,7 @@ gst_oss4_sink_init (GstOss4Sink * osssin
   osssink->bytes_per_sample = 0;
   osssink->probed_caps = NULL;
   osssink->device_name = NULL;
+  osssink->mute_volume = 100 | (100 << 8);
 }
 
 static void
@@ -201,6 +221,8 @@ gst_oss4_sink_set_property (GObject * ob
     const GValue * value, GParamSpec * pspec)
 {
   GstOss4Sink *oss = GST_OSS4_SINK (object);
+  gdouble      dvol;
+  int          ivol;
 
   switch (prop_id) {
     case PROP_DEVICE:
@@ -220,6 +242,41 @@ gst_oss4_sink_set_property (GObject * ob
       }
       GST_OBJECT_UNLOCK (oss);
       break;
+    case PROP_VOLUME:
+      dvol = g_value_get_double (value) * 100.0;
+      ivol = (int)dvol | ((int)dvol << 8);
+      if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
+         GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
+      }
+      break;
+    case PROP_MUTE:
+      if (g_value_get_boolean (value)) {
+         /*
+          * OSSv4 does not have a per-channel mute, so simulate by setting
+          * the value to 0.  Save the volume before doing a mute so we can
+          * reset the value when the user un-mutes.
+          */
+         if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &oss->mute_volume) < 0) {
+            GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
+         }
+         ivol = 0;
+         if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &ivol) < 0) {
+            GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
+         }
+      } else {
+         /*
+          * If the saved volume is 0, then reset it to 100.  Otherwise the mute
+          * can get stuck.  This can happen, for example, due to rounding
+          * errors in converting from the float to an integer.
+          */
+         if (oss->mute_volume == 0) {
+            oss->mute_volume = 100 | (100 << 8);
+         }
+         if (ioctl (oss->fd, SNDCTL_DSP_SETPLAYVOL, &oss->mute_volume) < 0) {
+            GST_LOG_OBJECT (oss, "SETPLAYVOL failed");
+         }
+      }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -231,6 +288,8 @@ gst_oss4_sink_get_property (GObject * ob
     GValue * value, GParamSpec * pspec)
 {
   GstOss4Sink *oss = GST_OSS4_SINK (object);
+  int          ivol, lvol, rvol;
+  gdouble      dvol;
 
   switch (prop_id) {
     case PROP_DEVICE:
@@ -258,6 +317,34 @@ gst_oss4_sink_get_property (GObject * ob
       }
       GST_OBJECT_UNLOCK (oss);
       break;
+    case PROP_VOLUME:
+      if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
+         GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
+      }
+
+      /* Return the higher of the two volume channels, if different */
+      lvol = ivol & 0xff;
+      rvol = (ivol >> 8) & 0xff;
+      if (lvol >= rvol) {
+          dvol = lvol / 100.0;
+      } else {
+          dvol = rvol / 100.0;
+      }
+      g_value_set_double (value, dvol);
+      break;
+    case PROP_MUTE:
+      if (ioctl (oss->fd, SNDCTL_DSP_GETPLAYVOL, &ivol) < 0) {
+         GST_LOG_OBJECT (oss, "GETPLAYVOL failed");
+      }
+      lvol = ivol & 0xff;
+      rvol = (ivol >> 8) & 0xff;
+ 
+      if (lvol == 0 && rvol == 0) {
+         g_value_set_boolean (value, TRUE);
+      } else {
+         g_value_set_boolean (value, FALSE);
+      }
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -566,8 +653,40 @@ gst_oss4_sink_reset (GstAudioSink * asin
    * other ways if we try an ioctl SNDCTL_DSP_HALT or similar */
 }
 
+static gboolean
+gst_oss4_sink_interface_supported (GstImplementsInterface *
+    iface, GType interface_type)
+{
+  GstOss4Sink *this = GST_OSS4_SINK_CAST (iface);
+
+  if (interface_type == GST_TYPE_PROPERTY_PROBE)
+    return TRUE;
+  if (interface_type == GST_TYPE_STREAM_VOLUME)
+    return TRUE;
+
+  return FALSE;
+}
+
+static void
+gst_oss4_sink_implements_interface_init (GstImplementsInterfaceClass * klass)
+{
+  klass->supported = gst_oss4_sink_interface_supported;
+}
+
 static void
 gst_oss4_sink_init_interfaces (GType type)
 {
+  static const GInterfaceInfo implements_iface_info = {
+    (GInterfaceInitFunc) gst_oss4_sink_implements_interface_init,
+    NULL,
+    NULL,
+  };
+
+  static const GInterfaceInfo svol_iface_info = {
+    NULL, NULL, NULL
+  };
+
+  g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_iface_info);
+
   gst_oss4_add_property_probe_interface (type);
 }
