From 32a05f329170b982cd6a2a9f61a59fae9812e9cc Mon Sep 17 00:00:00 2001
From: Lyubomir Marinov <lyubomir.marinov@jitsi.org>
Date: Thu, 18 Jul 2013 18:27:00 +0300
Subject: [PATCH] Fixes an issue in the Windows Audio Session API (WASAPI)
 integration which could cause the audio capture to fail on certain devices if
 acoustic echo cancellation is enabled.

---
 .../impl/neomedia/device/WASAPISystem.java    |  5 +-
 .../media/protocol/wasapi/DataSource.java     | 67 ++++++-------------
 .../wasapi/NativelySupportedAudioFormat.java  | 53 +++++++++++++++
 .../media/protocol/wasapi/WASAPIStream.java   | 33 ++++++---
 4 files changed, 100 insertions(+), 58 deletions(-)
 create mode 100644 src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/NativelySupportedAudioFormat.java

diff --git a/src/org/jitsi/impl/neomedia/device/WASAPISystem.java b/src/org/jitsi/impl/neomedia/device/WASAPISystem.java
index 68b10979..9626cfb2 100644
--- a/src/org/jitsi/impl/neomedia/device/WASAPISystem.java
+++ b/src/org/jitsi/impl/neomedia/device/WASAPISystem.java
@@ -753,7 +753,7 @@ private List<AudioFormat> getIAudioClientSupportedFormats(long iAudioClient)
                         if (nChannels == 2)
                         {
                             supportedFormat
-                                = new AudioFormat(
+                                = new NativelySupportedAudioFormat(
                                         AudioFormat.LINEAR,
                                         nSamplesPerSec,
                                         wBitsPerSample,
@@ -767,9 +767,8 @@ private List<AudioFormat> getIAudioClientSupportedFormats(long iAudioClient)
                             if (!supportedFormats.contains(supportedFormat))
                                 supportedFormats.add(supportedFormat);
                         }
-
                         supportedFormat
-                            = new AudioFormat(
+                            = new NativelySupportedAudioFormat(
                                     AudioFormat.LINEAR,
                                     nSamplesPerSec,
                                     wBitsPerSample,
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/DataSource.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/DataSource.java
index 098acb83..1bbd889d 100644
--- a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/DataSource.java
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/DataSource.java
@@ -140,10 +140,7 @@ protected void doDisconnect()
      */
     Format[] getIAudioClientSupportedFormats()
     {
-        return
-            getIAudioClientSupportedFormats(
-                    /* streamIndex */ 0,
-                    audioSystem.getAECSupportedFormats());
+        return getIAudioClientSupportedFormats(/* streamIndex */ 0);
     }
 
     /**
@@ -153,14 +150,10 @@ Format[] getIAudioClientSupportedFormats()
      * @param streamIndex the index of the <tt>SourceStream</tt> within the list
      * of <tt>SourceStream</tt>s of this <tt>DataSource</tt> on behalf of which
      * the query is being made
-     * @param aecSupportedFormats the list of <tt>AudioFormat</tt>s supported by
-     * the voice capture DMO implementing acoustic echo cancellation
      * @return the <tt>Format</tt>s of media data supported by the audio
      * endpoint device associated with this instance
      */
-    private Format[] getIAudioClientSupportedFormats(
-            int streamIndex,
-            List<AudioFormat> aecSupportedFormats)
+    private Format[] getIAudioClientSupportedFormats(int streamIndex)
     {
         Format[] superSupportedFormats = super.getSupportedFormats(streamIndex);
 
@@ -172,42 +165,25 @@ private Format[] getIAudioClientSupportedFormats(
                 || (superSupportedFormats.length == 0))
             return superSupportedFormats;
 
-        Format[] array;
+        // Return the NativelySupportedAudioFormat instances only.
+        List<Format> supportedFormats
+            = new ArrayList<Format>(superSupportedFormats.length);
 
-        if (aecSupportedFormats.isEmpty())
-            array = superSupportedFormats;
-        else
+        for (Format format : superSupportedFormats)
         {
-            /*
-             * Filter out the Formats added to the list of supported because of
-             * the voice capture DMO alone.
-             */
-            List<Format> list
-                = new ArrayList<Format>(superSupportedFormats.length);
-
-            for (Format superSupportedFormat : superSupportedFormats)
+            if ((format instanceof NativelySupportedAudioFormat)
+                    && !supportedFormats.contains(format))
             {
-                /*
-                 * Reference equality to an aecSupportedFormat signals that the
-                 * superSupportedFormat is not supported by the capture endpoint
-                 * device and is supported by the voice capture DMO.
-                 */
-                boolean equals = false;
-
-                for (Format aecSupportedFormat : aecSupportedFormats)
-                {
-                    if (superSupportedFormat == aecSupportedFormat)
-                    {
-                        equals = true;
-                        break;
-                    }
-                }
-                if (!equals)
-                    list.add(superSupportedFormat);
+                supportedFormats.add(format);
             }
-            array = list.toArray(new Format[list.size()]);
         }
-        return array;
+
+        int supportedFormatCount = supportedFormats.size();
+
+        return
+            (supportedFormatCount == superSupportedFormats.length)
+                ? superSupportedFormats
+                : supportedFormats.toArray(new Format[supportedFormatCount]);
     }
 
     /**
@@ -222,21 +198,18 @@ private Format[] getIAudioClientSupportedFormats(
     @Override
     protected Format[] getSupportedFormats(int streamIndex)
     {
-        List<AudioFormat> aecSupportedFormats
-            = audioSystem.getAECSupportedFormats();
-
         if (aec)
         {
+            List<AudioFormat> aecSupportedFormats
+                = audioSystem.getAECSupportedFormats();
+
             return
                 aecSupportedFormats.toArray(
                         new Format[aecSupportedFormats.size()]);
         }
         else
         {
-            return
-                getIAudioClientSupportedFormats(
-                        streamIndex,
-                        aecSupportedFormats);
+            return getIAudioClientSupportedFormats(streamIndex);
         }
     }
 }
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/NativelySupportedAudioFormat.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/NativelySupportedAudioFormat.java
new file mode 100644
index 00000000..6ed2102d
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/NativelySupportedAudioFormat.java
@@ -0,0 +1,53 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.jmfext.media.protocol.wasapi;
+
+import javax.media.format.*;
+
+/**
+ * Represents an <tt>AudioFormat</tt> which is natively supported by the entity
+ * which supports it. The <tt>NativelySupportedAudioFormat</tt> class is used
+ * purely as a flag/indicator/marker. In the context of the Windows Audio
+ * Session API (WASAPI) integration, it signals that the endpoint device
+ * represented by an associated <tt>CaptureDeviceInfo2</tt> supports the format
+ * either directly or with built-in converstion between mono and stereo.
+ *
+ * @author Lyubomir Marinov
+ */
+public class NativelySupportedAudioFormat
+    extends AudioFormat
+{
+    public NativelySupportedAudioFormat(
+            String encoding,
+            double sampleRate,
+            int sampleSizeInBits,
+            int channels,
+            int endian,
+            int signed,
+            int frameSizeInBits,
+            double frameRate,
+            Class<?> dataType)
+    {
+        super(
+                encoding,
+                sampleRate,
+                sampleSizeInBits,
+                channels,
+                endian,
+                signed,
+                frameSizeInBits,
+                frameRate,
+                dataType);
+
+        /*
+         * The NativelySupportedAudioFormat class is used purely as a
+         * flag/indicator/marker and, consequently, needs to not affect value
+         * equality.
+         */
+        clz = AudioFormat.class;
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/WASAPIStream.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/WASAPIStream.java
index 58edcaad..279c66bd 100644
--- a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/WASAPIStream.java
+++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/WASAPIStream.java
@@ -64,16 +64,19 @@ public class WASAPIStream
      * possible is to be found 
      * @param format the <tt>AudioFormat</tt> for which a similar
      * <tt>AudioFormat</tt> is to be found in <tt>formats</tt>
+     * @param clazz the runtime type of the matches to be considered or
+     * <tt>null</tt> if the runtime type is to not be limited
      * @return an <tt>AudioFormat</tt> which is an element of <tt>formats</tt>
      * and is as similar to the specified <tt>format</tt> as possible or
      * <tt>null</tt> if no similarity could be established
      */
     private static AudioFormat findClosestMatch(
             Format[] formats,
-            AudioFormat format)
+            AudioFormat format,
+            Class<? extends AudioFormat> clazz)
     {
         // Try to find the very specified format.
-        AudioFormat match = findFirstMatch(formats, format);
+        AudioFormat match = findFirstMatch(formats, format, clazz);
 
         if (match == null)
         {
@@ -93,7 +96,8 @@ private static AudioFormat findClosestMatch(
                                 format.getSigned(),
                                 /* frameSizeInBits */ Format.NOT_SPECIFIED,
                                 /* frameRate */ Format.NOT_SPECIFIED,
-                                format.getDataType()));
+                                format.getDataType()),
+                        clazz);
             if (match == null)
             {
                 /*
@@ -113,7 +117,8 @@ private static AudioFormat findClosestMatch(
                                     format.getSigned(),
                                     /* frameSizeInBits */ Format.NOT_SPECIFIED,
                                     /* frameRate */ Format.NOT_SPECIFIED,
-                                    format.getDataType()));
+                                    format.getDataType()),
+                            clazz);
             }
         }
         return match;
@@ -127,16 +132,24 @@ private static AudioFormat findClosestMatch(
      * @param formats the array of <tt>Format</tt>s which si to be searched
      * @param format the <tt>AudioFormat</tt> for which a match is to be found
      * in the specified <tt>formats</tt>
+     * @param clazz the runtime type of the matches to be considered or
+     * <tt>null</tt> if the runtime type is to not be limited
      * @return the first element of <tt>formats</tt> which matches the specified
      * <tt>format</tt> or <tt>null</tt> if no match could be found
      */
     private static AudioFormat findFirstMatch(
             Format[] formats,
-            AudioFormat format)
+            AudioFormat format,
+            Class<? extends AudioFormat> clazz)
     {
         for (Format aFormat : formats)
-            if (aFormat.matches(format))
+        {
+            if (aFormat.matches(format)
+                    && ((clazz == null) || clazz.isInstance(aFormat)))
+            {
                 return (AudioFormat) aFormat.intersects(format);
+            }
+        }
         return null;
     }
 
@@ -757,7 +770,10 @@ private void doConnect()
             }
 
             AudioFormat renderFormat
-                = findClosestMatch(renderDeviceInfo.getFormats(), thisFormat);
+                = findClosestMatch(
+                        renderDeviceInfo.getFormats(),
+                        thisFormat,
+                        NativelySupportedAudioFormat.class);
 
             if (renderFormat == null)
             {
@@ -812,7 +828,8 @@ private AudioFormat findClosestMatchCaptureSupportedFormat(
         return
             findClosestMatch(
                     dataSource.getIAudioClientSupportedFormats(),
-                    format);
+                    format,
+                    NativelySupportedAudioFormat.class);
     }
 
     /**
-- 
GitLab