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