[jitsi-dev] Fwd: [jitsi~svn:9718] Fixes the switching of the playback device in calls upon hotplugging.


#1

Hi Lyubomir,

There is some problems with pulseaudio since commit #9718 (portaudio on Linux works well):
- No sounds are send or received.
- When hanging up the remote peer stays in connecting state forever.

The problem comes from the "PulseAudioRenderer#reset()" function which blocks at "open()" ----> "openWithMainloopLock()" ----> "pulseAudioSystem.waitForStreamState(stream,PA. STREAM_READY)".

To reproduce this problem, just make a XMPP or SIP call from a linux PC to another peer.

Regards,
Vincent

calls upon hotplugging.

From: lubomir_m@java.net

Project: jitsi
Repository: svn
Revision: 9718
Author: lubomir_m
Link:

Log Message:

···

-------- Original Message --------
Subject: [jitsi~svn:9718] Fixes the switching of the playback device in Date: Mon, 9 Jul 2012 14:22:17 +0000
Reply-To: commits@jitsi.java.net
To: commits@jitsi.java.net
Date: 2012-07-09 14:22:15 UTC
------------
Fixes the switching of the playback device in calls upon hotplugging.

Revisions:
----------
9718

Modified Paths:
---------------
trunk/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
trunk/lib/installer-exclude/libjitsi.jar
libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PulseAudioRenderer.java
libjitsi/src/org/jitsi/impl/neomedia/MediaServiceImpl.java
trunk/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java
libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java
libjitsi/src/org/jitsi/util/event/PropertyChangeNotifier.java
libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java
libjitsi/src/org/jitsi/impl/neomedia/device/AudioSystem.java
libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/AbstractRenderer.java

Added Paths:
------------
libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java

Diffs:
------
Index: trunk/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java

--- trunk/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java (revision 9717)
+++ trunk/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java (revision 9718)
@@ -1018,7 +1018,7 @@
                  if (oldValue != newValue)
                  {
                      stream.setDevice(newValue);
- registerAudioLevelListeners((AudioMediaStream)stream);
+ registerAudioLevelListeners((AudioMediaStream) stream);
                  }
              }
          }
Index: trunk/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java

--- trunk/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java (revision 9717)
+++ trunk/src/net/java/sip/communicator/service/protocol/media/MediaAwareCall.java (revision 9718)
@@ -170,6 +170,7 @@
      protected MediaAwareCall(U parentOpSet)
      {
          super(parentOpSet.getProtocolProvider());
+
          this.parentOpSet = parentOpSet;

          /*
@@ -483,29 +484,39 @@
      public MediaDevice getDefaultDevice(MediaType mediaType)
      {
          MediaDevice device;
- List<Call> callGroupCalls;

- if ((mediaType == MediaType.AUDIO)
- && (conferenceAudioMixer == null)
- && (callGroup != null)
- && ((callGroupCalls = callGroup.getCalls()).size() > 0))
- {
- conferenceAudioMixer
- = ((MediaAwareCall<?,?,?>) callGroupCalls.get(0))
- .conferenceAudioMixer;
- device = conferenceAudioMixer;
- }
-
          switch (mediaType)
          {
          case AUDIO:
- device = audioDevice;
+ {
+ /*
+ * TODO There must be something wrong related to the CallGroup
+ * functionality because the end product is that the local
+ * variable device gets assigned a CallGroup-dependent value and
+ * then it gets overwritten.
+ */
+ List<Call> callGroupCalls;
+
+ if ((conferenceAudioMixer == null)
+ && (callGroup != null)
+ && ((callGroupCalls = callGroup.getCalls()).size() > 0))
+ {
+ conferenceAudioMixer
+ = ((MediaAwareCall<?,?,?>) callGroupCalls.get(0))
+ .conferenceAudioMixer;
+ device = conferenceAudioMixer;
+ }
+
+ device = audioDevice;
+ }
              break;
          case VIDEO:
              device = videoDevice;
              break;
          default:
- /* no other type supported */
+ /*
+ * There is no other MediaType value (at the time of this writing).
+ */
              return null;
          }

@@ -984,9 +995,11 @@
      }

      /**
- * Set the <tt>MediaDevice</tt> used for the video.
+ * Sets the <tt>MediaDevice</tt> to be used by this <tt>Call</tt> for the
+ * video.
       *
- * @param dev video <tt>MediaDevice</tt>
+ * @param dev the <tt>MediaDevice</tt> to be used by this <tt>Call</tt> for
+ * the video
       */
      public void setVideoDevice(MediaDevice dev)
      {
@@ -994,45 +1007,38 @@
      }

      /**
- * Set the <tt>MediaDevice</tt> used for the audio.
+ * Sets the <tt>MediaDevice</tt> to be used by this <tt>Call</tt> for the
+ * audio.
       *
- * @param dev audio <tt>MediaDevice</tt>
+ * @param dev the <tt>MediaDevice</tt> to be used by this <tt>Call</tt> for
+ * the audio
       */
      public void setAudioDevice(MediaDevice dev)
      {
          if (audioDevice != dev)
          {
- MediaDevice oldAudioDevice = audioDevice;
+ /*
+ * XXX While we know the old and the new master/wrapped devices, we
+ * are not sure whether conferenceAudioMixer has been used. Anyway,
+ * we have to report different values in order to have
+ * PropertyChangeSupport really fire the event.
+ */
+ MediaDevice oldValue
+ = (conferenceAudioMixer instanceof MediaDeviceWrapper)
+ ? ((MediaDeviceWrapper) conferenceAudioMixer)
+ .getWrappedDevice()
+ : audioDevice;

              audioDevice = dev;

- if (conferenceAudioMixer instanceof MediaDeviceWrapper)
- {
- MediaDevice wrappedDevice
- = ((MediaDeviceWrapper) conferenceAudioMixer)
- .getWrappedDevice();
+ MediaDevice newValue = audioDevice;

- if (wrappedDevice != audioDevice)
- {
- conferenceAudioMixer = null;
-
- /*
- * XXX While we know the old and the new master/wrapped
- * devices, we are not sure whether conferenceAudioMixer has
- * been used. Anyway, we have to report different values in
- * order to have PropertyChangeSupport really fire the
- * event.
- */
- propertyChangeSupport.firePropertyChange(
- DEFAULT_DEVICE,
- wrappedDevice, audioDevice);
- }
- }
- else if (conferenceAudioMixer == null)
+ if (oldValue != newValue)
              {
+ conferenceAudioMixer = null;
                  propertyChangeSupport.firePropertyChange(
- DEFAULT_DEVICE,
- oldAudioDevice, audioDevice);
+ DEFAULT_DEVICE,
+ oldValue, newValue);
              }
          }
      }
@@ -1181,8 +1187,8 @@
      /**
       * Notifies this instance about a change of the value of a specific property
       * from a specific old value to a specific new value. At the time of this
- * writing, <tt>MediaAwareCall</tt> listeners to the property value changes
- * of <tt>MediaService</tt> in order to track the changes of the default
+ * writing, <tt>MediaAwareCall</tt> listens to the property value changes of
+ * <tt>MediaService</tt> in order to track the changes of the default
       * <tt>MediaDevice</tt>.
       *
       * @param event a <tt>PropertyChangeEvent</tt> which specifies the name of
@@ -1192,18 +1198,25 @@
      {
          if (MediaService.DEFAULT_DEVICE.equals(event.getPropertyName()))
          {
- // if we change the device, do it only for the first member
- if(getCallGroup() != null &&
- this != getCallGroup().getCalls().get(0))
- {
+ /*
+ * The first Call in a CallGroup will process the property change
+ * for all Calls in the CallGroup (because they share one and the
+ * same device).
+ */
+ CallGroup callGroup = getCallGroup();
+
+ if ((callGroup != null) && (this != callGroup.getCalls().get(0)))
                  return;
- }

              /*
               * XXX We only support changing the default audio device at the time
               * of this writing.
               */
- MediaDevice oldValue = null;
+ MediaDevice oldValue
+ = (conferenceAudioMixer instanceof MediaDeviceWrapper)
+ ? ((MediaDeviceWrapper) conferenceAudioMixer)
+ .getWrappedDevice()
+ : null;
              MediaDevice newValue
                  = (audioDevice == null)
                      ? ProtocolMediaActivator.getMediaService().getDefaultDevice(
@@ -1211,13 +1224,6 @@
                              mediaUseCase)
                      : audioDevice;

- if (conferenceAudioMixer instanceof MediaDeviceWrapper)
- {
- oldValue
- = ((MediaDeviceWrapper) conferenceAudioMixer)
- .getWrappedDevice();
- }
-
              /*
               * XXX If MediaService#getDefaultDevice(MediaType, MediaUseCase)
               * above returns null and its earlier return value was not null, we
@@ -1230,24 +1236,33 @@
                  propertyChangeSupport.firePropertyChange(
                          DEFAULT_DEVICE,
                          oldValue, newValue);
- }

- // now the first member of the group is configured so let's
- // configure the others
- if(getCallGroup() != null)
- {
- List<Call> calls = getCallGroup().getCalls();
-
- for(Call c : calls)
+ /*
+ * As previously stated, the first Call in a CallGroup will
+ * process the property change for all Calls in the CallGroup.
+ * Now that the first Call has responded to the change in the
+ * DEFAULT_DEVICE, the other Calls in the CallGroup can be
+ * notified about the change as well and they can consult the
+ * device configured by the first Call.
+ */
+ callGroup = getCallGroup();
+ if (callGroup != null)
                  {
- if(c == this)
- continue;
+ List<Call> calls = callGroup.getCalls();

- MediaAwareCall<?,?,?> call = (MediaAwareCall<?,?,?>)c;
+ for (Call c : calls)
+ {
+ if (c != this)
+ {
+ MediaAwareCall<?,?,?> call
+ = (MediaAwareCall<?,?,?>) c;

- call.conferenceAudioMixer = null;
- call.propertyChangeSupport.firePropertyChange(
- DEFAULT_DEVICE, oldValue, newValue);
+ call.conferenceAudioMixer = null;
+ call.propertyChangeSupport.firePropertyChange(
+ DEFAULT_DEVICE,
+ oldValue, newValue);
+ }
+ }
                  }
              }
          }
Index: trunk/lib/installer-exclude/libjitsi.jar

Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java

--- libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java (revision 9717)
+++ libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/protocol/portaudio/DataSource.java (revision 9718)
@@ -188,14 +188,17 @@
       *
       * @return the device index of a PortAudio device identified by the
       * <tt>MediaLocator</tt> of this <tt>DataSource</tt>
+ * @throws IllegalStateException if there is no <tt>MediaLocator</tt>
+ * associated with this <tt>DataSource</tt>
       */
      private int getDeviceIndex()
      {
          MediaLocator locator = getLocator();

          if (locator == null)
- throw new NullPointerException("locator");
- return getDeviceIndex(locator);
+ throw new IllegalStateException("locator");
+ else
+ return getDeviceIndex(locator);
      }

      /**
@@ -211,13 +214,9 @@
      {
          if (AudioSystem.LOCATOR_PROTOCOL_PORTAUDIO.equalsIgnoreCase(
                  locator.getProtocol()))
- {
              return Integer.parseInt(locator.getRemainder().replace("#", ""));
- }
          else
- {
              throw new IllegalArgumentException("locator.protocol");
- }
      }

      /**
Index: libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PulseAudioRenderer.java

--- libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PulseAudioRenderer.java (revision 9717)
+++ libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PulseAudioRenderer.java (revision 9718)
@@ -14,11 +14,15 @@

  import org.jitsi.impl.neomedia.*;
  import org.jitsi.impl.neomedia.device.*;
-import org.jitsi.impl.neomedia.jmfext.media.renderer.*;
  import org.jitsi.impl.neomedia.pulseaudio.*;

+/**
+ * Implements an audio <tt>Renderer</tt> which uses PulseAudio.
+ *
+ * @author Lyubomir Marinov
+ */
  public class PulseAudioRenderer
- extends AbstractRenderer<AudioFormat>
+ extends AbstractAudioRenderer
  {
      /**
       * The human-readable <tt>PlugIn</tt> name of the
@@ -53,10 +57,14 @@

      private float gainControlLevel;

- private MediaLocator locator;
-
      private final String mediaRole;

+ /*
+ * TODO The field pulseAudioSystem has been introduced prior to
+ * AbstractAudioSystem and its field audioSystem. It would be a good idea to
+ * remove the field pulseAudioSystem in order to reduce the memory footprint
+ * of the PulseAudioRenderer instances.
+ */
      private final PulseAudioSystem pulseAudioSystem;

      private long stream;
@@ -80,7 +88,9 @@

      public PulseAudioRenderer(String mediaRole)
      {
- pulseAudioSystem = PulseAudioSystem.getPulseAudioSystem();
+ super(PulseAudioSystem.getPulseAudioSystem());
+
+ pulseAudioSystem = (PulseAudioSystem) audioSystem;
          if (pulseAudioSystem == null)
              throw new IllegalStateException("pulseAudioSystem");

@@ -140,6 +150,8 @@
                      PA.stream_unref(stream);
                  }
              }
+
+ super.close();
          }
          finally
          {
@@ -164,21 +176,6 @@
          }
      }

- public MediaLocator getLocator()
- {
- MediaLocator locator = this.locator;
-
- if (locator == null)
- {
- CaptureDeviceInfo playbackDevice
- = pulseAudioSystem.getPlaybackDevice();
-
- if (playbackDevice != null)
- locator = playbackDevice.getLocator();
- }
- return locator;
- }
-
      private String getLocatorDev()
      {
          MediaLocator locator = getLocator();
@@ -212,6 +209,8 @@
          try
          {
              openWithMainloopLock();
+
+ super.open();
          }
          finally
          {
@@ -465,17 +464,43 @@
          return ret;
      }

- public void setLocator(MediaLocator locator)
+ /**
+ * Resets the state of this <tt>PlugIn</tt>.
+ */
+ public void reset()
      {
- if (this.locator == null)
+ pulseAudioSystem.lockMainloop();
+ try
          {
- if (locator == null)
- return;
+ boolean open = (this.stream != 0);
+
+ if (open)
+ {
+ /*
+ * The close method will stop this Renderer if it is currently
+ * started.
+ */
+ boolean start = !this.corked;
+
+ close();
+
+ try
+ {
+ open();
+ }
+ catch (ResourceUnavailableException rue)
+ {
+ throw new UndeclaredThrowableException(rue);
+ }
+
+ if (start)
+ start();
+ }
          }
- else if (this.locator.equals(locator))
- return;
-
- this.locator = locator;
+ finally
+ {
+ pulseAudioSystem.unlockMainloop();
+ }
      }

      private void setStreamVolume(long stream, float level)
Index: libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java

--- libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java (revision 0)
+++ libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/AbstractAudioRenderer.java (revision 9718)
@@ -0,0 +1,133 @@
+/*
+ * 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.renderer.audio;
+
+import java.beans.*;
+
+import javax.media.*;
+import javax.media.format.*;
+
+import org.jitsi.impl.neomedia.device.*;
+import org.jitsi.impl.neomedia.jmfext.media.renderer.*;
+
+/**
+ * Provides an abstract base implementation of <tt>Renderer</tt> which processes
+ * media in <tt>AudioFormat</tt> in order to facilitate extenders.
+ *
+ * @author Lyubomir Marinov
+ */
+public abstract class AbstractAudioRenderer
+ extends AbstractRenderer<AudioFormat>
+{
+ /**
+ * The <tt>AudioSystem</tt> which provides the playback deviced used by this
+ * <tt>Renderer</tt>.
+ */
+ protected final AudioSystem audioSystem;
+
+ /**
+ * The <tt>MediaLocator</tt> which specifies the playback device to be used
+ * by this <tt>Renderer</tt>.
+ */
+ private MediaLocator locator;
+
+ /**
+ * The <tt>PropertyChangeListener</tt> which listens to changes in the
+ * values of the properties of {@link #audioSystem}.
+ */
+ private final PropertyChangeListener propertyChangeListener
+ = new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ AbstractAudioRenderer.this.propertyChange(event);
+ }
+ };
+
+ protected AbstractAudioRenderer(AudioSystem audioSystem)
+ {
+ this.audioSystem = audioSystem;
+ }
+
+ protected AbstractAudioRenderer(String locatorProtocol)
+ {
+ this(AudioSystem.getAudioSystem(locatorProtocol));
+ }
+
+ public void close()
+ {
+ if (audioSystem != null)
+ audioSystem.removePropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Gets the <tt>MediaLocator</tt> which specifies the playback device to be
+ * used by this <tt>Renderer</tt>.
+ *
+ * @return the <tt>MediaLocator</tt> which specifies the playback device to
+ * be used by this <tt>Renderer</tt>
+ */
+ public MediaLocator getLocator()
+ {
+ MediaLocator locator = this.locator;
+
+ if ((locator == null) && (audioSystem != null))
+ {
+ CaptureDeviceInfo playbackDevice
+ = audioSystem.getPlaybackDevice();
+
+ if (playbackDevice != null)
+ locator = playbackDevice.getLocator();
+ }
+ return locator;
+ }
+
+ public void open()
+ throws ResourceUnavailableException
+ {
+ /*
+ * If this Renderer has not been forced to use a playback device with a
+ * specific MediaLocator, it will use the default playback device (of
+ * its associated AudioSystem). In the case of using the default
+ * playback device, change the playback device used by this instance
+ * upon changes of the default playback device.
+ */
+ if ((this.locator == null) && (audioSystem != null))
+ {
+ MediaLocator locator = getLocator();
+
+ if (locator != null)
+ audioSystem.addPropertyChangeListener(propertyChangeListener);
+ }
+ }
+
+ private void propertyChange(PropertyChangeEvent event)
+ {
+ if (AudioSystem.PROP_PLAYBACK_DEVICE.equals(event.getPropertyName()))
+ reset();
+ }
+
+ /**
+ * Sets the <tt>MediaLocator</tt> which specifies the playback device to be
+ * used by this <tt>Renderer</tt>.
+ *
+ * @param locator the <tt>MediaLocator</tt> which specifies the playback
+ * device to be used by this <tt>Renderer</tt>
+ */
+ public void setLocator(MediaLocator locator)
+ {
+ if (this.locator == null)
+ {
+ if (locator == null)
+ return;
+ }
+ else if (this.locator.equals(locator))
+ return;
+
+ this.locator = locator;
+ }
+}
Index: libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java

--- libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java (revision 9717)
+++ libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/audio/PortAudioRenderer.java (revision 9718)
@@ -6,6 +6,7 @@
   */
  package org.jitsi.impl.neomedia.jmfext.media.renderer.audio;

+import java.lang.reflect.*;
  import java.util.*;

  import javax.media.*;
@@ -16,7 +17,6 @@
  import org.jitsi.impl.neomedia.*;
  import org.jitsi.impl.neomedia.device.*;
  import org.jitsi.impl.neomedia.jmfext.media.protocol.portaudio.*;
-import org.jitsi.impl.neomedia.jmfext.media.renderer.*;
  import org.jitsi.util.*;

  /**
@@ -26,7 +26,7 @@
   * @author Lyubomir Marinov
   */
  public class PortAudioRenderer
- extends AbstractRenderer<AudioFormat>
+ extends AbstractAudioRenderer
  {
      /**
       * The <tt>Logger</tt> used by the <tt>PortAudioRenderer</tt> class and its
@@ -109,12 +109,6 @@
       */
      private int framesPerBuffer;

- /**
- * The <tt>MediaLocator</tt> which specifies the device index of the
- * PortAudio device used by this instance for rendering.
- */
- private MediaLocator locator;
-
      private long outputParameters = 0;

      /**
@@ -160,6 +154,8 @@
       */
      public PortAudioRenderer(boolean enableVolumeControl)
      {
+ super(AudioSystem.LOCATOR_PROTOCOL_PORTAUDIO);
+
          if (enableVolumeControl)
          {
              /*
@@ -209,6 +205,8 @@
                  PortAudio.PaStreamParameters_free(outputParameters);
                  outputParameters = 0;
              }
+
+ super.close();
          }
      }

@@ -226,35 +224,6 @@
      }

      /**
- * Gets the <tt>MediaLocator</tt> which specifies the device index of the
- * PortAudio device used by this instance for rendering.
- *
- * @return the <tt>MediaLocator</tt> which specifies the device index of the
- * PortAudio device used by this instance for rendering
- */
- public MediaLocator getLocator()
- {
- MediaLocator locator = this.locator;
-
- if (locator == null)
- {
- AudioSystem portAudioSystem
- = AudioSystem.getAudioSystem(
- AudioSystem.LOCATOR_PROTOCOL_PORTAUDIO);
-
- if (portAudioSystem != null)
- {
- CaptureDeviceInfo playbackDevice
- = portAudioSystem.getPlaybackDevice();
-
- if (playbackDevice != null)
- locator = playbackDevice.getLocator();
- }
- }
- return locator;
- }
-
- /**
       * Gets the descriptive/human-readable name of this JMF plug-in.
       *
       * @return the descriptive/human-readable name of this JMF plug-in
@@ -415,6 +384,8 @@
                  throw rue;
              }
          }
+
+ super.open();
      }

      /**
@@ -607,23 +578,50 @@
      }

      /**
+ * Resets the state of this <tt>PlugIn</tt>.
+ */
+ public synchronized void reset()
+ {
+ waitWhileStreamIsBusy();
+
+ boolean open = (this.stream != 0);
+
+ if (open)
+ {
+ /*
+ * The close method will stop this Renderer if it is currently
+ * started.
+ */
+ boolean start = this.started;
+
+ close();
+
+ try
+ {
+ open();
+ }
+ catch (ResourceUnavailableException rue)
+ {
+ throw new UndeclaredThrowableException(rue);
+ }
+
+ if (start)
+ start();
+ }
+ }
+
+ /**
       * Sets the <tt>MediaLocator</tt> which specifies the device index of the
       * PortAudio device to be used by this instance for rendering.
       *
       * @param locator a <tt>MediaLocator</tt> which specifies the device index
       * of the PortAudio device to be used by this instance for rendering
       */
+ @Override
      public void setLocator(MediaLocator locator)
      {
- if (this.locator == null)
- {
- if (locator == null)
- return;
- }
- else if (this.locator.equals(locator))
- return;
+ super.setLocator(locator);

- this.locator = locator;
          supportedInputFormats = null;
      }

@@ -653,6 +651,30 @@
       */
      public synchronized void stop()
      {
+ waitWhileStreamIsBusy();
+ if (started && (stream != 0))
+ {
+ try
+ {
+ PortAudio.Pa_StopStream(stream);
+ started = false;
+
+ bufferLeft = null;
+ }
+ catch (PortAudioException paex)
+ {
+ logger.error("Failed to close PortAudio stream.", paex);
+ }
+ }
+ }
+
+ /**
+ * Waits on this instance while {@link #streamIsBusy} is equal to
+ * <tt>true</tt> i.e. until it becomes <tt>false</tt>. The method should
+ * only be called by a thread that is the owner of this object's monitor.
+ */
+ private void waitWhileStreamIsBusy()
+ {
          boolean interrupted = false;

          while (streamIsBusy)
@@ -668,20 +690,5 @@
          }
          if (interrupted)
              Thread.currentThread().interrupt();
-
- if (started && (stream != 0))
- {
- try
- {
- PortAudio.Pa_StopStream(stream);
- started = false;
-
- bufferLeft = null;
- }
- catch (PortAudioException paex)
- {
- logger.error("Failed to close PortAudio stream.", paex);
- }
- }
      }
  }
Index: libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/AbstractRenderer.java

--- libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/AbstractRenderer.java (revision 9717)
+++ libjitsi/src/org/jitsi/impl/neomedia/jmfext/media/renderer/AbstractRenderer.java (revision 9718)
@@ -12,7 +12,7 @@

  /**
   * Provides an abstract base implementation of <tt>Renderer</tt> in order to
- * facilitate extenders
+ * facilitate extenders.
   *
   * @author Lyubomir Marinov
   *
Index: libjitsi/src/org/jitsi/impl/neomedia/device/AudioSystem.java

--- libjitsi/src/org/jitsi/impl/neomedia/device/AudioSystem.java (revision 9717)
+++ libjitsi/src/org/jitsi/impl/neomedia/device/AudioSystem.java (revision 9718)
@@ -308,12 +308,22 @@
              {
                  if (captureDevice != null)
                  {
- List<CaptureDeviceInfo> captureDevices = getCaptureDevices();
+ List<CaptureDeviceInfo> captureDevices
+ = getCaptureDevices();

                      if ((captureDevices == null)
                              >> !captureDevices.contains(captureDevice))
                          setCaptureDevice(null, false);
                  }
+ else
+ {
+ /*
+ * If captureDevice is null, it means that a device is to be
+ * used as the default. The default in question may have
+ * changed.
+ */
+ setCaptureDevice(null, false);
+ }
              }
              finally
              {
@@ -330,6 +340,15 @@
                                      >> !notifyDevices.contains(notifyDevice))
                                  setNotifyDevice(null, false);
                          }
+ else
+ {
+ /*
+ * If notifyDevice is null, it means that a device
+ * is to be used as the default. The default in
+ * question may have changed.
+ */
+ setNotifyDevice(null, false);
+ }
                      }
                      finally
                      {
@@ -343,6 +362,15 @@
                                              playbackDevice))
                                  setPlaybackDevice(null, false);
                          }
+ else
+ {
+ /*
+ * If playbackDevice is null, it means that a device
+ * is to be used as the default. The default in
+ * question may have changed.
+ */
+ setPlaybackDevice(null, false);
+ }
                      }
                  }
              }
Index: libjitsi/src/org/jitsi/impl/neomedia/MediaServiceImpl.java

--- libjitsi/src/org/jitsi/impl/neomedia/MediaServiceImpl.java (revision 9717)
+++ libjitsi/src/org/jitsi/impl/neomedia/MediaServiceImpl.java (revision 9718)
@@ -1386,7 +1386,20 @@
      {
          String propertyName = event.getPropertyName();

+ /*
+ * While AUDIO_CAPTURE_DEVICE is sure to affect the DEFAULT_DEVICE,
+ * AUDIO_PLAYBACK_DEVICE is not. Anyway, MediaDevice is supposed to
+ * represent the device to be used for capture AND playback (though its
+ * current implementation MediaDeviceImpl may be incomplete with respect
+ * to the playback representation). Since it is not clear at this point
+ * of the execution whether AUDIO_PLAYBACK_DEVICE really affects the
+ * DEFAULT_DEVICE and for the sake of completeness, throw in the changes
+ * to the AUDIO_NOTIFY_DEVICE as well.
+ */
          if (DeviceConfiguration.AUDIO_CAPTURE_DEVICE.equals(propertyName)
+ || DeviceConfiguration.AUDIO_NOTIFY_DEVICE.equals(propertyName)
+ || DeviceConfiguration.AUDIO_PLAYBACK_DEVICE.equals(
+ propertyName)
                  >> DeviceConfiguration.VIDEO_CAPTURE_DEVICE.equals(
                          propertyName))
          {
Index: libjitsi/src/org/jitsi/util/event/PropertyChangeNotifier.java

--- libjitsi/src/org/jitsi/util/event/PropertyChangeNotifier.java (revision 9717)
+++ libjitsi/src/org/jitsi/util/event/PropertyChangeNotifier.java (revision 9718)
@@ -70,7 +70,9 @@
       * <tt>PropertyChangeListener</tt>s registered with this
       * <tt>PropertyChangeNotifier</tt> in order to notify about a change in the
       * value of a specific property which had its old value modified to a
- * specific new value.
+ * specific new value. <tt>PropertyChangeNotifier</tt> does not check
+ * whether the specified <tt>oldValue</tt> and <tt>newValue</tt> are indeed
+ * different.
       *
       * @param property the name of the property of this
       * <tt>PropertyChangeNotifier</tt> which had its value changed
@@ -80,28 +82,31 @@
       * the change
       */
      protected void firePropertyChange(
- String property,
- Object oldValue,
- Object newValue)
+ String property,
+ Object oldValue,
+ Object newValue)
      {
          PropertyChangeListener[] listeners;
+
          synchronized (this.listeners)
          {
              listeners
- = this.listeners
- .toArray(
- new PropertyChangeListener[this.listeners.size()]);
+ = this.listeners.toArray(
+ new PropertyChangeListener[this.listeners.size()]);
          }

- PropertyChangeEvent event
- = new PropertyChangeEvent(
- getPropertyChangeSource(property, oldValue, newValue),
- property,
- oldValue,
- newValue);
+ if (listeners.length != 0)
+ {
+ PropertyChangeEvent event
+ = new PropertyChangeEvent(
+ getPropertyChangeSource(property, oldValue, newValue),
+ property,
+ oldValue,
+ newValue);

- for (PropertyChangeListener listener : listeners)
- listener.propertyChange(event);
+ for (PropertyChangeListener listener : listeners)
+ listener.propertyChange(event);
+ }
      }

      /**
@@ -126,9 +131,9 @@
       * specified new value
       */
      protected Object getPropertyChangeSource(
- String property,
- Object oldValue,
- Object newValue)
+ String property,
+ Object oldValue,
+ Object newValue)
      {
          return this;
      }


#2

Hi Vincent,

There is some problems with pulseaudio since commit #9718 (portaudio on
Linux works well):
- No sounds are send or received.
- When hanging up the remote peer stays in connecting state forever.

The problem comes from the "PulseAudioRenderer#reset()" function which
blocks at "open()" ----> "openWithMainloopLock()" ---->
"pulseAudioSystem.waitForStreamState(stream,PA. STREAM_READY)".

Thank you very much for the feedback!

To reproduce this problem, just make a XMPP or SIP call from a linux PC to
another peer.

Well, I've been doing that every day ever since committing revision
9718 without noticing a problem... Anyway, I'll try to fix the
problem.

Best regards,
Lyubomir

···

2012/7/12 Vincent Lucas <chenzo@jitsi.org>:


#3

Lyubomir Marinov wrote:

Hi Vincent,

There is some problems with pulseaudio since commit #9718 (portaudio on
Linux works well):
- No sounds are send or received.
- When hanging up the remote peer stays in connecting state forever.

The problem comes from the "PulseAudioRenderer#reset()" function which
blocks at "open()" ----> "openWithMainloopLock()" ---->
"pulseAudioSystem.waitForStreamState(stream,PA. STREAM_READY)".

Thank you very much for the feedback!

To reproduce this problem, just make a XMPP or SIP call from a linux PC to
another peer.

Well, I've been doing that every day ever since committing revision
9718 without noticing a problem... Anyway, I'll try to fix the
problem.

It happened to me too with the latest versions. I use Debian Wheezy
32-bit. No sound if i start a call (the ringing sounds play sometimes)
and afterwards if i go into the sound options menu, the UI freezes for
good and i have to kill java with -9.
PortAudio sort of works (crackling sound).

···

2012/7/12 Vincent Lucas <chenzo@jitsi.org>:

Best regards,
Lyubomir

--
O zi buna,
Kertesz Laszlo


#4

Lyubomir Marinov wrote:

Hi Vincent,

There is some problems with pulseaudio since commit #9718 (portaudio on
Linux works well):
- No sounds are send or received.
- When hanging up the remote peer stays in connecting state forever.

The problem comes from the "PulseAudioRenderer#reset()" function which
blocks at "open()" ----> "openWithMainloopLock()" ---->
"pulseAudioSystem.waitForStreamState(stream,PA. STREAM_READY)".

Thank you very much for the feedback!

To reproduce this problem, just make a XMPP or SIP call from a linux PC to
another peer.

Well, I've been doing that every day ever since committing revision
9718 without noticing a problem... Anyway, I'll try to fix the
problem.

I rolled back Jitsiversions to find when this issue begins.
The last working version for me is 4106 (dont have 4107 in apt's cache).
4108 and onward all have this no sound issue.
It has to do with revision 9718 (introduced in build 4107).

I use Debian Wheezy 32-bit with pulseaudio 2.0.

···

2012/7/12 Vincent Lucas <chenzo@jitsi.org>:

Best regards,
Lyubomir

--
O zi buna,
Kertesz Laszlo


#5

Test call to the echo service on Asterisk. No sound was received nor
sent, after closing the call and opening the audio preferences the UI
hangs forever. Nothing new in the logs.
Logs (nothing suspicious from my point of view):

00:18:58.696 INFO: org.jitsi.impl.neomedia.MediaStreamImpl.info() audio
codec/freq: PCMU/8000 Hz
00:18:58.697 INFO: org.jitsi.impl.neomedia.MediaStreamImpl.info() audio
remote IP/port: 192.168.3.4/11818
00:19:00.133 INFO:
org.jitsi.impl.neomedia.transform.rtcp.StatisticsEngine.info()
rtpstat:Sending a report for audio stream SSRC:831591937 [packet
count:22, bytes:3680, interarrival jitter:4, lost packets:0, time since
previous report:32767999ms ]
00:19:03.808 INFO: org.jitsi.impl.neomedia.MediaStreamImpl.info()
rtpstat:Received a report for audio stream SSRC:2098142192 [packet
count:236, bytes:37760, interarrival jitter:598, lost packets:0, time
since previous report:876ms ]
00:19:17.359 INFO:
org.jitsi.impl.neomedia.transform.rtcp.StatisticsEngine.info()
rtpstat:Sending a report for audio stream SSRC:831591937 [packet
count:22, bytes:3680, interarrival jitter:2, lost packets:0, time since
previous report:3552ms ]

···

--
O zi buna,
Kertesz Laszlo


#6

Vincent and Kertesz,

Thank you very much, guys, for the thorough feedback on the PulseAudio freezes!

While looking further for the exact details of the cause of the problem, the PulseAudio changes from r9718 have been commented out in r9736 in order to get you the working version back as soon as possible.

Best regards,
Lyubomir


#7

Lyubomir Marinov wrote:

Vincent and Kertesz,

Thank you very much, guys, for the thorough feedback on the PulseAudio
freezes!

While looking further for the exact details of the cause of the problem,
the PulseAudio changes from r9718 have been commented out in r9736 in
order to get you the working version back as soon as possible.

This fixed the problem in build 4119.
Thank you.

···

Best regards,
Lyubomir

--
O zi buna,
Kertesz Laszlo


#8

Hi Lyubomir, Kertesz

Thank you Lyubomir for commit #9742. I did a first couple of tests and I am able to make calls with sound capture/playback and to hang up correctly: which corrects the problem of revision #9718.

Moreover, I am able to hot-plug my headset during a call and the switch works well too.

However, I have 2 questions/remarks:

1) When connecting the headset the Options->Audio window is no more displayed. Is this normal? I do not remember, if we decided to keep this behavior or not.

2) After unplug-in and plug-in again my headest during a call, the following crash has appeared (cf. dump below). The crash seems to be related to Portaudio, but I have verified and I am using Pulseaudio.

*** glibc detected *** /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java: double free or corruption (out): 0x00007f4184014b70 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x75b46)[0x7f41a4002b46]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x6c)[0x7f41a400787c]
/home/lucas/softwares/clean_jitsi/trunk/lib/native/linux-64/libjnportaudio.so(+0x12e42)[0x7f418e273e42]
/home/lucas/softwares/clean_jitsi/trunk/lib/native/linux-64/libjnportaudio.so(Pa_UpdateAvailableDeviceList+0x15d)[0x7f418e26f2fd]
[0x7f419eb96d68]

Best Ragards,
Vincent

···

On 07/12/2012 11:55 PM, Lyubomir Marinov wrote:

Vincent and Kertesz,

Thank you very much, guys, for the thorough feedback on the PulseAudio
freezes!

While looking further for the exact details of the cause of the problem,
the PulseAudio changes from r9718 have been commented out in r9736 in
order to get you the working version back as soon as possible.

Best regards,
Lyubomir

--
Vincent Lucas, Ph.D. Jitsi developer
chenzo@jitsi.org http://jitsi.org