diff options
Diffstat (limited to 'voip/java')
-rw-r--r-- | voip/java/android/net/rtp/AudioGroup.java | 110 | ||||
-rw-r--r-- | voip/java/android/net/rtp/AudioStream.java | 28 | ||||
-rw-r--r-- | voip/java/android/net/rtp/RtpStream.java | 7 |
3 files changed, 100 insertions, 45 deletions
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java index 43a3827..a6b54d8 100644 --- a/voip/java/android/net/rtp/AudioGroup.java +++ b/voip/java/android/net/rtp/AudioGroup.java @@ -21,14 +21,14 @@ import java.util.Map; /** * An AudioGroup acts as a router connected to the speaker, the microphone, and - * {@link AudioStream}s. Its pipeline has four steps. First, for each - * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming - * packets and stores in its buffer. Then, if the microphone is enabled, - * processes the recorded audio and stores in its buffer. Third, if the speaker - * is enabled, mixes and playbacks buffers of all AudioStreams. Finally, for - * each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other - * buffers and sends back the encoded packets. An AudioGroup does nothing if - * there is no AudioStream in it. + * {@link AudioStream}s. Its execution loop consists of four steps. First, for + * each AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its + * incoming packets and stores in its buffer. Then, if the microphone is + * enabled, processes the recorded audio and stores in its buffer. Third, if the + * speaker is enabled, mixes and playbacks buffers of all AudioStreams. Finally, + * for each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all + * other buffers and sends back the encoded packets. An AudioGroup does nothing + * if there is no AudioStream in it. * * <p>Few things must be noticed before using these classes. The performance is * highly related to the system load and the network bandwidth. Usually a @@ -47,7 +47,12 @@ import java.util.Map; * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an * AudioStream into an AudioGroup, one should always put all other AudioGroups * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly - * initialized. + * initialized.</p> + * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#RECORD_AUDIO} permission.</p> + * + * @see AudioStream * @hide */ public class AudioGroup { @@ -78,6 +83,8 @@ public class AudioGroup { */ public static final int MODE_ECHO_SUPPRESSION = 3; + private static final int MODE_LAST = 3; + private final Map<AudioStream, Integer> mStreams; private int mMode = MODE_ON_HOLD; @@ -94,6 +101,15 @@ public class AudioGroup { } /** + * Returns the {@link AudioStream}s in this group. + */ + public AudioStream[] getStreams() { + synchronized (this) { + return mStreams.keySet().toArray(new AudioStream[mStreams.size()]); + } + } + + /** * Returns the current mode. */ public int getMode() { @@ -108,49 +124,77 @@ public class AudioGroup { * @param mode The mode to change to. * @throws IllegalArgumentException if the mode is invalid. */ - public synchronized native void setMode(int mode); - - private native void add(int mode, int socket, String remoteAddress, - int remotePort, String codecSpec, int dtmfType); + public void setMode(int mode) { + if (mode < 0 || mode > MODE_LAST) { + throw new IllegalArgumentException("Invalid mode"); + } + synchronized (this) { + nativeSetMode(mode); + mMode = mode; + } + } - synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) { - if (!mStreams.containsKey(stream)) { - try { - int socket = stream.dup(); - String codecSpec = String.format("%d %s %s", codec.type, - codec.rtpmap, codec.fmtp); - add(stream.getMode(), socket, - stream.getRemoteAddress().getHostAddress(), - stream.getRemotePort(), codecSpec, dtmfType); - mStreams.put(stream, socket); - } catch (NullPointerException e) { - throw new IllegalStateException(e); + private native void nativeSetMode(int mode); + + // Package-private method used by AudioStream.join(). + void add(AudioStream stream, AudioCodec codec, int dtmfType) { + synchronized (this) { + if (!mStreams.containsKey(stream)) { + try { + int socket = stream.dup(); + String codecSpec = String.format("%d %s %s", codec.type, + codec.rtpmap, codec.fmtp); + nativeAdd(stream.getMode(), socket, + stream.getRemoteAddress().getHostAddress(), + stream.getRemotePort(), codecSpec, dtmfType); + mStreams.put(stream, socket); + } catch (NullPointerException e) { + throw new IllegalStateException(e); + } } } } - private native void remove(int socket); + private native void nativeAdd(int mode, int socket, String remoteAddress, + int remotePort, String codecSpec, int dtmfType); - synchronized void remove(AudioStream stream) { - Integer socket = mStreams.remove(stream); - if (socket != null) { - remove(socket); + // Package-private method used by AudioStream.join(). + void remove(AudioStream stream) { + synchronized (this) { + Integer socket = mStreams.remove(stream); + if (socket != null) { + nativeRemove(socket); + } } } + private native void nativeRemove(int socket); + /** * Sends a DTMF digit to every {@link AudioStream} in this group. Currently * only event {@code 0} to {@code 15} are supported. * * @throws IllegalArgumentException if the event is invalid. */ - public native synchronized void sendDtmf(int event); + public void sendDtmf(int event) { + if (event < 0 || event > 15) { + throw new IllegalArgumentException("Invalid event"); + } + synchronized (this) { + nativeSendDtmf(event); + } + } + + private native void nativeSendDtmf(int event); /** * Removes every {@link AudioStream} in this group. */ - public synchronized void clear() { - remove(-1); + public void clear() { + synchronized (this) { + mStreams.clear(); + nativeRemove(-1); + } } @Override diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java index e5197ce..0edae6b 100644 --- a/voip/java/android/net/rtp/AudioStream.java +++ b/voip/java/android/net/rtp/AudioStream.java @@ -34,8 +34,12 @@ import java.net.SocketException; * of the setter methods are disabled. This is designed to ease the task of * managing native resources. One can always make an AudioStream leave its * AudioGroup by calling {@link #join(AudioGroup)} with {@code null} and put it - * back after the modification is done. + * back after the modification is done.</p> * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#INTERNET} permission.</p> + * + * @see RtpStream * @see AudioGroup * @hide */ @@ -82,16 +86,18 @@ public class AudioStream extends RtpStream { * @see AudioGroup */ public void join(AudioGroup group) { - if (mGroup == group) { - return; - } - if (mGroup != null) { - mGroup.remove(this); - mGroup = null; - } - if (group != null) { - group.add(this, mCodec, mDtmfType); - mGroup = group; + synchronized (this) { + if (mGroup == group) { + return; + } + if (mGroup != null) { + mGroup.remove(this); + mGroup = null; + } + if (group != null) { + group.add(this, mCodec, mDtmfType); + mGroup = group; + } } } diff --git a/voip/java/android/net/rtp/RtpStream.java b/voip/java/android/net/rtp/RtpStream.java index 23fb258..87d8bc6 100644 --- a/voip/java/android/net/rtp/RtpStream.java +++ b/voip/java/android/net/rtp/RtpStream.java @@ -24,6 +24,9 @@ import java.net.SocketException; /** * RtpStream represents the base class of streams which send and receive network * packets with media payloads over Real-time Transport Protocol (RTP). + * + * <p class="note">Using this class requires + * {@link android.Manifest.permission#INTERNET} permission.</p> * @hide */ public class RtpStream { @@ -43,6 +46,8 @@ public class RtpStream { */ public static final int MODE_RECEIVE_ONLY = 2; + private static final int MODE_LAST = 2; + private final InetAddress mLocalAddress; private final int mLocalPort; @@ -129,7 +134,7 @@ public class RtpStream { if (isBusy()) { throw new IllegalStateException("Busy"); } - if (mode != MODE_NORMAL && mode != MODE_SEND_ONLY && mode != MODE_RECEIVE_ONLY) { + if (mode < 0 || mode > MODE_LAST) { throw new IllegalArgumentException("Invalid mode"); } mMode = mode; |