From be914dbf8f715ca7ee35b76ad94ae01d3b3886b2 Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov <lyubomir.marinov@jitsi.org> Date: Tue, 9 Jul 2013 16:47:49 +0300 Subject: [PATCH] Works on improving the acoustic echo cancellation (AEC) of Windows Audio Session API (WASAPI). --- .../protocol/wasapi/AudioCaptureClient.java | 65 +++++++++++++++---- .../media/protocol/wasapi/WASAPIStream.java | 14 ++-- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/AudioCaptureClient.java b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/AudioCaptureClient.java index 06722ef2..1778a82d 100644 --- a/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/AudioCaptureClient.java +++ b/src/org/jitsi/impl/neomedia/jmfext/media/protocol/wasapi/AudioCaptureClient.java @@ -40,6 +40,24 @@ public class AudioCaptureClient private static final Logger logger = Logger.getLogger(AudioCaptureClient.class); + private static int maybeIAudioCaptureClientGetNextPacketSize( + long iAudioCaptureClient) + { + int numFramesInNextPacket; + + try + { + numFramesInNextPacket + = IAudioCaptureClient_GetNextPacketSize(iAudioCaptureClient); + } + catch (HResultException hre) + { + numFramesInNextPacket = 0; // Silence the compiler. + logger.error("IAudioCaptureClient_GetNextPacketSize", hre); + } + return numFramesInNextPacket; + } + /** * The internal buffer of this instance in which audio data is read from the * associated <tt>IAudioCaptureClient</tt> by the instance and awaits to be @@ -142,6 +160,17 @@ public class AudioCaptureClient */ final AudioFormat outFormat; + /** + * The indicator which reports whether audio samples have been read out of + * this instance since the indicator has been set to <tt>false</tt>. For + * example, determines whether audio samples have been read out of this + * instance since this instance has given control to its associated + * <tt>BufferTransferHandler</tt> (if any) and thus resolves a condition + * similar to a busy wait which arises if the associated + * <tt>BufferTransferHandler</tt> does not read any data. + */ + private boolean read; + /** * The number of channels with which {@link #iAudioClient} has been * initialized. @@ -505,6 +534,8 @@ else if (!started) { read = doRead(iMediaBuffer, buffer, offset, length); cause = null; + if (read > 0) + this.read = true; } catch (Throwable t) { @@ -556,18 +587,9 @@ private BufferTransferHandler readInEventHandleCmd() * Determine the size in bytes of the next data packet in the capture * endpoint buffer. */ - int numFramesInNextPacket; + int numFramesInNextPacket + = maybeIAudioCaptureClientGetNextPacketSize(iAudioCaptureClient); - try - { - numFramesInNextPacket - = IAudioCaptureClient_GetNextPacketSize(iAudioCaptureClient); - } - catch (HResultException hre) - { - numFramesInNextPacket = 0; // Silence the compiler. - logger.error("IAudioCaptureClient_GetNextPacketSize", hre); - } if (numFramesInNextPacket != 0) { int toRead = numFramesInNextPacket * dstFrameSize; @@ -619,6 +641,7 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) { long eventHandle; BufferTransferHandler transferHandler; + int numFramesInNextPacket; synchronized (this) { @@ -648,6 +671,17 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) try { transferHandler = readInEventHandleCmd(); + if (transferHandler != null) + read = false; + /* + * If the audio engine has more samples to deliver to the + * application, deliver them as soon as the transferHandler, + * if any, has been given a chance to read (from) the + * available samples. + */ + numFramesInNextPacket + = maybeIAudioCaptureClientGetNextPacketSize( + iAudioCaptureClient); } finally { @@ -668,7 +702,8 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) * exception, we will WaitForSingleObject in order to * give the application time to recover. */ - continue; + if (read) + continue; } catch (Throwable t) { @@ -682,6 +717,12 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) } } } + /* + * The audio engine may already have more samples to deliver to + * the application. + */ + if (numFramesInNextPacket != 0) + continue; int wfso; 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 51b50143..bdbb3d85 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 @@ -1014,7 +1014,14 @@ private void initializeRender(final MediaLocator locator, AudioFormat format) * at this time. If the transferHandler (which will normally invoke * transferRenderData) was non-null, it would cause excessive CPU use. */ - BufferTransferHandler transferHandler = null; + BufferTransferHandler transferHandler + = new BufferTransferHandler() + { + public void transferData(PushBufferStream stream) + { + transferRenderData(); + } + }; render = new AudioCaptureClient( @@ -1022,7 +1029,7 @@ private void initializeRender(final MediaLocator locator, AudioFormat format) locator, AudioSystem.DataFlow.PLAYBACK, WASAPI.AUDCLNT_STREAMFLAGS_LOOPBACK, - /* hnsBufferDuration */ Format.NOT_SPECIFIED, + WASAPISystem.DEFAULT_BUFFER_DURATION, format, transferHandler); replenishRender = true; @@ -1115,7 +1122,7 @@ private void processInput(int dwInputStreamIndex, int maxLength) if ((dwInputStreamIndex == RENDER_INPUT_STREAM_INDEX) && replenishRender) { - int replenishThreshold = renderBufferMaxLength; + int replenishThreshold = (3 * renderBufferMaxLength / 2); if (audioCaptureClient.getAvailableLength() < replenishThreshold) @@ -1698,7 +1705,6 @@ private void transferCaptureData() * Notifies this instance that audio data has been made available in * {@link #render}. */ - @SuppressWarnings("unused") private void transferRenderData() { /* -- GitLab