diff options
author | Chia-chi Yeh <chiachi@android.com> | 2010-08-06 17:24:20 +0800 |
---|---|---|
committer | Chia-chi Yeh <chiachi@android.com> | 2010-08-06 17:24:20 +0800 |
commit | 9c0e76beae15ce3ef27507b6b32a75ab78c6b836 (patch) | |
tree | 00702c7c5d6e9dbbe9664d2d927fad1fc02ed330 | |
parent | 29c26d07eb1c66891e6ad4f0396712f0a1273c92 (diff) | |
download | android_external_nist-sip-9c0e76beae15ce3ef27507b6b32a75ab78c6b836.tar.gz android_external_nist-sip-9c0e76beae15ce3ef27507b6b32a75ab78c6b836.tar.bz2 android_external_nist-sip-9c0e76beae15ce3ef27507b6b32a75ab78c6b836.zip |
RTP: remove dead code. Now we are officially in the frameworks.
Change-Id: I772d47805b982ead28cec53dda7a8acdd308a407
-rw-r--r-- | src/android/net/rtp/AudioCodec.java | 55 | ||||
-rw-r--r-- | src/android/net/rtp/AudioGroup.java | 90 | ||||
-rw-r--r-- | src/android/net/rtp/AudioStream.java | 135 | ||||
-rw-r--r-- | src/android/net/rtp/RtpStream.java | 172 | ||||
-rw-r--r-- | src/com/android/sip/rtp/AudioCodec.java | 55 | ||||
-rw-r--r-- | src/com/android/sip/rtp/AudioStream.java | 188 | ||||
-rw-r--r-- | src/com/android/sip/rtp/RtpSocket.java | 118 | ||||
-rw-r--r-- | src/jni/rtp/Android.mk | 43 | ||||
-rw-r--r-- | src/jni/rtp/AudioCodec.h | 58 | ||||
-rw-r--r-- | src/jni/rtp/AudioStream.cpp | 660 | ||||
-rw-r--r-- | src/jni/rtp/G711Codec.cpp | 96 | ||||
-rw-r--r-- | src/jni/rtp/RtpSocket.cpp | 254 | ||||
-rw-r--r-- | src/jni/rtp/RtpSocket.h | 35 | ||||
-rw-r--r-- | src/jni/rtp/libsiprtp.cpp | 33 | ||||
-rw-r--r-- | src/jni/rtp_jni/Android.mk | 44 | ||||
-rw-r--r-- | src/jni/rtp_jni/AudioCodec.cpp | 161 | ||||
-rw-r--r-- | src/jni/rtp_jni/AudioCodec.h | 36 | ||||
-rw-r--r-- | src/jni/rtp_jni/AudioGroup.cpp | 1004 | ||||
-rw-r--r-- | src/jni/rtp_jni/RtpStream.cpp | 126 | ||||
-rw-r--r-- | src/jni/rtp_jni/rtp_jni.cpp | 32 | ||||
-rw-r--r-- | src/jni/rtp_jni/util.cpp | 61 |
21 files changed, 0 insertions, 3456 deletions
diff --git a/src/android/net/rtp/AudioCodec.java b/src/android/net/rtp/AudioCodec.java deleted file mode 100644 index 242ad58..0000000 --- a/src/android/net/rtp/AudioCodec.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.rtp; - -public class AudioCodec { - public static final AudioCodec ULAW = new AudioCodec("PCMU", 8000, 160, 0); - public static final AudioCodec ALAW = new AudioCodec("PCMA", 8000, 160, 8); - - /** - * Returns system supported codecs. - */ - public static AudioCodec[] getSystemSupportedCodecs() { - return new AudioCodec[] {AudioCodec.ULAW, AudioCodec.ALAW}; - } - - /** - * Returns the codec instance if it is supported by the system. - * - * @param name name of the codec - * @return the matched codec or null if the codec name is not supported by - * the system - */ - public static AudioCodec getSystemSupportedCodec(String name) { - for (AudioCodec codec : getSystemSupportedCodecs()) { - if (codec.name.equals(name)) return codec; - } - return null; - } - - public final String name; - public final int sampleRate; - public final int sampleCount; - public final int defaultType; - - private AudioCodec(String name, int sampleRate, int sampleCount, int defaultType) { - this.name = name; - this.sampleRate = sampleRate; - this.sampleCount = sampleCount; - this.defaultType = defaultType; - } -} diff --git a/src/android/net/rtp/AudioGroup.java b/src/android/net/rtp/AudioGroup.java deleted file mode 100644 index 0708613..0000000 --- a/src/android/net/rtp/AudioGroup.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.rtp; - -import java.util.HashMap; -import java.util.Map; - -/** - */ -public class AudioGroup { - public static final int MODE_ON_HOLD = 0; - public static final int MODE_MUTED = 1; - public static final int MODE_NORMAL = 2; - public static final int MODE_EC_ENABLED = 3; - - private final Map<AudioStream, Integer> mStreams; - private int mMode = MODE_ON_HOLD; - - private int mNative; - static { - System.loadLibrary("rtp_jni"); - } - - public AudioGroup() { - mStreams = new HashMap<AudioStream, Integer>(); - } - - public int getMode() { - return mMode; - } - - public synchronized native void setMode(int mode); - - synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) { - if (!mStreams.containsKey(stream)) { - try { - int id = add(stream.getMode(), stream.dup(), - stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(), - codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType); - mStreams.put(stream, id); - } catch (NullPointerException e) { - throw new IllegalStateException(e); - } - } - } - - private native int add(int mode, int socket, String remoteAddress, int remotePort, - String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType); - - synchronized void remove(AudioStream stream) { - Integer id = mStreams.remove(stream); - if (id != null) { - remove(id); - } - } - - private native void remove(int id); - - /** - * 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 synchronized void reset() { - remove(-1); - } - - @Override - protected void finalize() throws Throwable { - reset(); - super.finalize(); - } -} diff --git a/src/android/net/rtp/AudioStream.java b/src/android/net/rtp/AudioStream.java deleted file mode 100644 index c1da7ba..0000000 --- a/src/android/net/rtp/AudioStream.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.rtp; - -import java.net.InetAddress; -import java.net.SocketException; - -/** - * AudioStream represents a RTP stream carrying audio payloads. - */ -public class AudioStream extends RtpStream { - private AudioCodec mCodec; - private int mCodecType = -1; - private int mDtmfType = -1; - private AudioGroup mGroup; - - /** - * Creates an AudioStream on the given local address. Note that the local - * port is assigned automatically to conform with RFC 3550. - * - * @param address The network address of the local host to bind to. - * @throws SocketException if the address cannot be bound or a problem - * occurs during binding. - */ - public AudioStream(InetAddress address) throws SocketException { - super(address); - } - - /** - * Returns {@code true} if the stream already joined an {@link AudioGroup}. - */ - @Override - public final boolean isBusy() { - return mGroup != null; - } - - /** - * Returns the joined {@link AudioGroup}. - */ - public AudioGroup getAudioGroup() { - return mGroup; - } - - /** - * Joins an {@link AudioGroup}. Each stream can join only one group at a - * time. The group can be changed by passing a different one or removed - * by calling this method with {@code null}. - * - * @param group The AudioGroup to join or {@code null} to leave. - * @throws IllegalStateException if the stream is not properly configured. - * @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, mCodecType, mDtmfType); - mGroup = group; - } - } - - /** - * Sets the {@link AudioCodec} and its RTP payload type. According to RFC - * 3551, the type must be in the range of 0 and 127, where 96 and above are - * dynamic types. For codecs with static mappings (non-negative - * {@link AudioCodec#defaultType}), assigning a different non-dynamic type - * is disallowed. - * - * @param codec The AudioCodec to be used. - * @param type The RTP payload type. - * @throws IllegalArgumentException if the type is invalid or used by DTMF. - * @throws IllegalStateException if the stream is busy. - */ - public void setCodec(AudioCodec codec, int type) { - if (isBusy()) { - throw new IllegalStateException("Busy"); - } - if (type < 0 || type > 127 || (type != codec.defaultType && type < 96)) { - throw new IllegalArgumentException("Invalid type"); - } - if (type == mDtmfType) { - throw new IllegalArgumentException("The type is used by DTMF"); - } - mCodec = codec; - mCodecType = type; - } - - /** - * Sets the RTP payload type for dual-tone multi-frequency (DTMF) digits. - * The primary usage is to send digits to the remote gateway to perform - * certain tasks, such as second-stage dialing. According to RFC 2833, the - * RTP payload type for DTMF is assigned dynamically, so it must be in the - * range of 96 and 127. One can use {@code -1} to disable DTMF and free up - * the previous assigned value. This method cannot be called when the stream - * already joined an {@link AudioGroup}. - * - * @param type The RTP payload type to be used or {@code -1} to disable it. - * @throws IllegalArgumentException if the type is invalid or used by codec. - * @throws IllegalStateException if the stream is busy. - * @see AudioGroup#sendDtmf(int) - */ - public void setDtmfType(int type) { - if (isBusy()) { - throw new IllegalStateException("Busy"); - } - if (type != -1) { - if (type < 96 || type > 127) { - throw new IllegalArgumentException("Invalid type"); - } - if (type == mCodecType) { - throw new IllegalArgumentException("The type is used by codec"); - } - } - mDtmfType = type; - } -} diff --git a/src/android/net/rtp/RtpStream.java b/src/android/net/rtp/RtpStream.java deleted file mode 100644 index b542ca7..0000000 --- a/src/android/net/rtp/RtpStream.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.rtp; - -import java.net.InetAddress; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.SocketException; - -/** - * RtpStream represents a base class of media streams running over - * Real-time Transport Protocol (RTP). - */ -public class RtpStream { - public static final int MODE_NORMAL = 0; - public static final int MODE_SEND_ONLY = 1; - public static final int MODE_RECEIVE_ONLY = 2; - - private final InetAddress mLocalAddress; - private final int mLocalPort; - - private InetAddress mRemoteAddress; - private int mRemotePort = -1; - private int mMode = MODE_NORMAL; - - private int mNative; - static { - System.loadLibrary("rtp_jni"); - } - - /** - * Creates a RtpStream on the given local address. Note that the local - * port is assigned automatically to conform with RFC 3550. - * - * @param address The network address of the local host to bind to. - * @throws SocketException if the address cannot be bound or a problem - * occurs during binding. - */ - RtpStream(InetAddress address) throws SocketException { - mLocalPort = create(address.getHostAddress()); - mLocalAddress = address; - } - - private native int create(String address) throws SocketException; - - /** - * Returns the network address of the local host. - */ - public InetAddress getLocalAddress() { - return mLocalAddress; - } - - /** - * Returns the network port of the local host. - */ - public int getLocalPort() { - return mLocalPort; - } - - /** - * Returns the network address of the remote host or {@code null} if the - * stream is not associated. - */ - public InetAddress getRemoteAddress() { - return mRemoteAddress; - } - - /** - * Returns the network port of the remote host or {@code -1} if the stream - * is not associated. - */ - public int getRemotePort() { - return mRemotePort; - } - - /** - * Returns {@code true} if the stream is busy. This method is intended to be - * overridden by subclasses. - */ - public boolean isBusy() { - return false; - } - - /** - * Returns the current mode. The initial mode is {@link #MODE_NORMAL}. - */ - public int getMode() { - return mMode; - } - - /** - * Changes the current mode. It must be one of {@link #MODE_NORMAL}, - * {@link #MODE_SEND_ONLY}, and {@link #MODE_RECEIVE_ONLY}. - * - * @param mode The mode to change to. - * @throws IllegalArgumentException if the mode is invalid. - * @throws IllegalStateException if the stream is busy. - * @see #isBusy() - */ - public void setMode(int mode) { - if (isBusy()) { - throw new IllegalStateException("Busy"); - } - if (mode != MODE_NORMAL && mode != MODE_SEND_ONLY && mode != MODE_RECEIVE_ONLY) { - throw new IllegalArgumentException("Invalid mode"); - } - mMode = mode; - } - - /** - * Associates with a remote host. - * - * @param address The network address of the remote host. - * @param port The network port of the remote host. - * @throws IllegalArgumentException if the address is not supported or the - * port is invalid. - * @throws IllegalStateException if the stream is busy. - * @see #isBusy() - */ - public void associate(InetAddress address, int port) { - if (isBusy()) { - throw new IllegalStateException("Busy"); - } - if (!(address instanceof Inet4Address && mLocalAddress instanceof Inet4Address) && - !(address instanceof Inet6Address && mLocalAddress instanceof Inet6Address)) { - throw new IllegalArgumentException("Unsupported address"); - } - if (port < 0 || port > 65535) { - throw new IllegalArgumentException("Invalid port"); - } - mRemoteAddress = address; - mRemotePort = port; - } - - synchronized native int dup(); - - /** - * Releases allocated resources. The stream becomes inoperable after calling - * this method. - * - * @throws IllegalStateException if the stream is busy. - * @see #isBusy() - */ - public void release() { - if (isBusy()) { - throw new IllegalStateException("Busy"); - } - close(); - } - - private synchronized native void close(); - - @Override - protected void finalize() throws Throwable { - close(); - super.finalize(); - } -} diff --git a/src/com/android/sip/rtp/AudioCodec.java b/src/com/android/sip/rtp/AudioCodec.java deleted file mode 100644 index 1e298b2..0000000 --- a/src/com/android/sip/rtp/AudioCodec.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.sip.rtp; - -public class AudioCodec { - public static final AudioCodec ULAW = new AudioCodec("PCMU", 8000, 160, 0); - public static final AudioCodec ALAW = new AudioCodec("PCMA", 8000, 160, 8); - - /** - * Returns system supported codecs. - */ - public static AudioCodec[] getSystemSupportedCodecs() { - return new AudioCodec[] {AudioCodec.ULAW, AudioCodec.ALAW}; - } - - /** - * Returns the codec instance if it is supported by the system. - * - * @param name name of the codec - * @return the matched codec or null if the codec name is not supported by - * the system - */ - public static AudioCodec getSystemSupportedCodec(String name) { - for (AudioCodec codec : getSystemSupportedCodecs()) { - if (codec.name.equals(name)) return codec; - } - return null; - } - - public final String name; - public final int sampleRate; - public final int sampleCount; - public final int defaultType; - - private AudioCodec(String name, int sampleRate, int sampleCount, int defaultType) { - this.name = name; - this.sampleRate = sampleRate; - this.sampleCount = sampleCount; - this.defaultType = defaultType; - } -} diff --git a/src/com/android/sip/rtp/AudioStream.java b/src/com/android/sip/rtp/AudioStream.java deleted file mode 100644 index e4a98aa..0000000 --- a/src/com/android/sip/rtp/AudioStream.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.sip.rtp; - - -/** - * The AudioStream class represents a RTP stream carrying audio payloads. - */ -public class AudioStream { - private int mNative; - private final RtpSocket mRtpSocket; - - private AudioCodec mCodec; - private int mCodecType = -1; - private int mDtmfType = -1; - - static { - System.loadLibrary("siprtp"); - } - - /** - * Creates an AudioStream on a given {@link RtpSocket}. - * - * @param rtpSocket The socket to send and receive RTP packets. - * @throws IllegalStateException if the socket is not associated or is - * already used by another stream. - */ - public AudioStream(RtpSocket rtpSocket) { - rtpSocket.occupy(); - mRtpSocket = rtpSocket; - } - - /** - * Closes the stream. Once closed, the stream stops sending and receiving, - * and relinquishes the ownership of the {@link RtpSocket}. - */ - public void close() { - stopSending(); - stopReceiving(); - mRtpSocket.vacate(); - } - - /** - * Returns the bound {@link RtpSocket}. - */ - public RtpSocket getRtpSocket() { - return mRtpSocket; - } - - /** - * Sets the {@link AudioCodec} and the RTP payload type. According to RFC - * 3551, the type must be in the range of 0 and 127, where 96 and above are - * used by dynamic types. For codecs with static mappings (non-negative - * {@link AudioCodec#defaultType}), assigning a different non-dynamic type - * is disallowed. This method can be called at any time but only takes - * effect after the next call to {@link #prepare()}. - * - * @param codec The audio codec to be used. - * @param type The RTP payload type. - * @throws IllegalArgumentException if the type is out of range or - * disallowed for this codec. - * @throws IllegalStateException if the type is already used by DTMF. - */ - public synchronized void setCodec(AudioCodec codec, int type) { - if (type < 0 || type > 127) { - throw new IllegalArgumentException("The type is out of range"); - } - if (type != codec.defaultType && type < 96) { - throw new IllegalArgumentException( - "Assigning a different non-dynamic type is not allowed"); - } - if (type == mDtmfType) { - throw new IllegalStateException("Codec and DTMF cannot use the same type"); - } - mCodec = codec; - mCodecType = type; - } - - /** - * Sets the RTP payload type for dual-tone multi-frequency (DTMF) digits. - * The primary usage is to send digits to the remote gateway to perform - * certain tasks, such as second-stage dialing. According to RFC 2833, the - * RTP payload type for DTMF is assigned dynamically, so it must be in the - * range of 96 and 127. One can use {@code -1} to disable DTMF and free up - * the previous assigned mapping. This method can be called at any time but - * only takes effect after the next call to {@link #prepare()}. - * - * @param type The RTP payload type or {@code -1} to disable DTMF. - * @throws IllegalArgumentException if the type is out of range. - * @throws IllegalStateException if the type is already used by the codec. - * @see #sendDtmf(int) - */ - public synchronized void setDtmf(int type) { - if (type != -1) { - if (type < 96 || type > 127) { - throw new IllegalArgumentException("The type is out of range"); - } - if (type == mCodecType) { - throw new IllegalStateException("Codec and DTMF cannot use the same type"); - } - } - mDtmfType = type; - } - - /** - * Allocates native resources for the current configuration. - * - * @throws IllegalStateException if the codec is not set or the stream is - * already prepared. - */ - public synchronized void prepare() { - if (mCodec == null) { - throw new IllegalStateException("Codec is not set"); - } - prepare(mRtpSocket, mCodec.name, mCodec.sampleRate, - mCodec.sampleCount, mCodecType, mDtmfType); - } - private native void prepare(RtpSocket rtpSocket, String codecName, - int sampleRate, int sampleCount, int codecType, int dtmfType); - - /** - * Returns {@code true} if the stream is already prepared. - */ - public native synchronized boolean isPrepared(); - - /** - * Starts recording and sending encoded audio to the remote host. - * - * @throws IllegalStateException if the stream is not prepared. - */ - public native synchronized void startSending(); - - /** - * Starts receiving and playing decoded audio from the remote host. - * - * @throws IllegalStateException if the stream is not prepared. - */ - public native synchronized void startReceiving(); - - /** - * Sends a DTMF digit to the remote host. One must set the RTP payload type - * for DTMF before calling this method. Currently only events {@code 0} - * through {@code 15} are supported. - * - * @throws IllegalArgumentException if the event is out of range. - * @throws IllegalStateException if the stream is not sending or DTMF is - * disabled. - * @see #setDtmf(int) - */ - public native synchronized void sendDtmf(int event); - - /** - * Stops sending. - */ - public native synchronized void stopSending(); - - /** - * Stops receiving. - */ - public native synchronized void stopReceiving(); - - /** - * Releases native resources. It also stops sending and receiving. After - * calling this method, one can call {@link #prepare()} again with or - * without changing the configuration. - */ - public native synchronized void release(); - - @Override - protected void finalize() throws Throwable { - release(); - super.finalize(); - } -} diff --git a/src/com/android/sip/rtp/RtpSocket.java b/src/com/android/sip/rtp/RtpSocket.java deleted file mode 100644 index 1f29f21..0000000 --- a/src/com/android/sip/rtp/RtpSocket.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.sip.rtp; - -import java.net.InetAddress; -import java.net.SocketException; - -/** - * The RtpSocket class represents a UDP socket carrying RTP packets. The network - * port of the local host is assigned automatically to conform with RFC 3550. - */ -public class RtpSocket { - private int mNative; - private final InetAddress mLocalAddress; - private final int mLocalPort; - - private InetAddress mRemoteAddress; - private int mRemotePort = -1; - private boolean mInUse = false; - - static { - System.loadLibrary("siprtp"); - } - - /** - * Creates a RtpSocket by specifying the network address of the local host. - * - * @param address The network address of the local host to bind to. - * @throws SocketException if the address cannot be bound or a problem - * occurs during binding. - */ - public RtpSocket(InetAddress address) throws SocketException { - mLocalPort = create(address.getHostAddress()); - mLocalAddress = address; - } - private native int create(String address) throws SocketException; - - synchronized void occupy() { - if (mRemoteAddress == null) { - throw new IllegalStateException("RtpSocket is not associated"); - } - if (mInUse) { - throw new IllegalStateException("RtpSocket is already in use"); - } - mInUse = true; - } - - synchronized void vacate() { - mInUse = false; - } - - /** - * Returns the network address of the local host. - */ - public InetAddress getLocalAddress() { - return mLocalAddress; - } - - /** - * Returns the network port of the local host. - */ - public int getLocalPort() { - return mLocalPort; - } - - /** - * Returns the network address of the remote host or {@code null} if the - * socket is not associated. - */ - public InetAddress getRemoteAddress() { - return mRemoteAddress; - } - - /** - * Returns the network port of the remote host or {@code -1} if the socket - * is not associated. - */ - public int getRemotePort() { - return mRemotePort; - } - - /** - * Associates with a remote host. - * - * @param address The network address of the remote host. - * @param port The network port of the remote host. - * @throws SocketException if the address family is different or the socket - * is already associated. - */ - public synchronized void associate(InetAddress address, int port) - throws SocketException { - associate(address.getHostAddress(), port); - mRemoteAddress = address; - mRemotePort = port; - } - private native void associate(String address, int port) throws SocketException; - - @Override - protected void finalize() throws Throwable { - release(); - super.finalize(); - } - private native void release(); -} diff --git a/src/jni/rtp/Android.mk b/src/jni/rtp/Android.mk deleted file mode 100644 index fb1fdb8..0000000 --- a/src/jni/rtp/Android.mk +++ /dev/null @@ -1,43 +0,0 @@ -# -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := libsiprtp - -LOCAL_SRC_FILES := \ - RtpSocket.cpp \ - AudioStream.cpp \ - G711Codec.cpp \ - libsiprtp.cpp - -LOCAL_SHARED_LIBRARIES := \ - libnativehelper \ - libutils \ - libcutils \ - libmedia - -LOCAL_STATIC_LIBRARIES := - -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) - -LOCAL_CFLAGS += -fvisibility=hidden - -LOCAL_PRELINK_MODULE := false - -include $(BUILD_SHARED_LIBRARY) diff --git a/src/jni/rtp/AudioCodec.h b/src/jni/rtp/AudioCodec.h deleted file mode 100644 index f9aee94..0000000 --- a/src/jni/rtp/AudioCodec.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyrightm (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __AUDIO_CODEC_H__ -#define __AUDIO_CODEC_H__ - -class AudioCodec -{ -public: - virtual ~AudioCodec() {} - // Returns true if sampleCount is acceptable. - virtual bool set(int sampleCount) = 0; - // Returns the length of payload in bytes. - virtual int encode(void *payload, int16_t *samples) = 0; - // Returns the number of decoded samples. - virtual int decode(int16_t *samples, void *payload, int length) = 0; -}; - -class UlawCodec : public AudioCodec -{ -public: - virtual bool set(int sampleCount) { - mSampleCount = sampleCount; - return true; - } - virtual int encode(void *payload, int16_t *samples); - virtual int decode(int16_t *samples, void *payload, int length); -private: - int mSampleCount; -}; - -class AlawCodec : public AudioCodec -{ -public: - virtual bool set(int sampleCount) { - mSampleCount = sampleCount; - return true; - } - virtual int encode(void *payload, int16_t *samples); - virtual int decode(int16_t *samples, void *payload, int length); -private: - int mSampleCount; -}; - -#endif diff --git a/src/jni/rtp/AudioStream.cpp b/src/jni/rtp/AudioStream.cpp deleted file mode 100644 index 8093414..0000000 --- a/src/jni/rtp/AudioStream.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/time.h> -#include <time.h> -#include <arpa/inet.h> -#include <pthread.h> - -#define LOG_TAG "AudioStream" -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/RefBase.h> -#include <utils/threads.h> -#include <media/AudioRecord.h> -#include <media/AudioTrack.h> -#include <media/mediarecorder.h> - -#include "jni.h" -#include "JNIHelp.h" - -#include "RtpSocket.h" -#include "AudioCodec.h" - -using namespace android; - -static int gRandom = -1; - -class JitterBuffer { - static const int SIZE = 6; - uint32_t mBufferSize; - uint8_t *mBuffer[SIZE]; - uint16_t *mLength; - uint16_t mHead; - uint16_t mTail; - - public: - JitterBuffer(int sampleCount) { - mHead = mTail = 0; - mBufferSize = 2048 + (sizeof(int16_t) * sampleCount); - for (int i = 0; i < SIZE ; ++i) { - mBuffer[i] = (uint8_t*) malloc(mBufferSize); - } - mLength = (uint16_t*) malloc(SIZE * sizeof(uint16_t)); - } - - ~JitterBuffer() { - for (int i = 0; i < SIZE ; ++i) { - free(mBuffer[i]); - } - free(mLength); - } - - int getBufferSize() { - return mBufferSize; - } - - uint8_t* obtainBuffer() { - // we need extra buffer to save one memcpy. - int reservedHead = ((mHead == 0) ? (SIZE - 1) : (mHead - 1)); - if (mTail == reservedHead) return NULL; - return mBuffer[mTail]; - } - - void pushBack(int length) { - mLength[mTail] = length; - if (++mTail == SIZE) mTail = 0; - } - - unsigned int popFront(uint8_t **packet) { - int length = mLength[mHead]; - *packet = mBuffer[mHead]; - if (++mHead == SIZE) mHead = 0; - return length; - } - - bool empty() { - return (mHead == mTail); - } -}; - -class AudioStream -{ -public: - AudioStream(); - ~AudioStream(); - - bool set(RtpSocket *rtpSocket, const char *codecName, - int sampleRate, int sampleCount, int codecType, int dtmfType); - - bool startSending(); - bool startReceiving(); - bool sendDtmf(int event); - void stopSending(); - void stopReceiving(); - -private: - RtpSocket *mSocket; - AudioCodec *mCodec; - AudioRecord mRecord; - AudioTrack mTrack; - pthread_mutex_t mDtmfLock; - - uint16_t mLocalSequence; - uint32_t mLocalTimestamp; - uint32_t mLocalSsrc; - uint32_t mRemoteTimestamp; - uint32_t mRemoteSsrc; - uint32_t mCodecMagic; - uint32_t mDtmfMagic; - - int mSampleRate; - int mSampleCount; - int mInterval; - int mTimer; - - JitterBuffer *mJitterBuffer; - - volatile int32_t mNextDtmfEvent; - int mDtmfEvent; - int mDtmfDuration; - - bool encode(); - bool decode(); - - void adjustMicGain(int16_t *buf, int len, int factor); - - int getPacketFromJB(RtpSocket *rtpSocket, uint8_t **buffer, timeval *deadline); - - class Sender : public Thread - { - public: - Sender(AudioStream *stream) : Thread(false), mStream(stream) {} - private: - virtual bool threadLoop() - { - if (!mStream->encode()) { - mStream->mRecord.stop(); - return false; - } - return true; - } - AudioStream *mStream; - }; - sp<Sender> mSender; - - class Receiver : public Thread - { - public: - Receiver(AudioStream *stream) : Thread(false), mStream(stream) {} - private: - virtual bool threadLoop() - { - if (!mStream->decode()) { - mStream->mTrack.stop(); - return false; - } - return true; - } - AudioStream *mStream; - }; - sp<Receiver> mReceiver; -}; - -AudioStream::AudioStream() -{ - mSender = new Sender(this); - mReceiver = new Receiver(this); - mCodec = NULL; - mJitterBuffer = NULL; - pthread_mutex_init(&mDtmfLock, NULL); -} - -AudioStream::~AudioStream() -{ - stopSending(); - stopReceiving(); - mSender.clear(); - mReceiver.clear(); - delete mCodec; - delete mJitterBuffer; -} - -bool AudioStream::set(RtpSocket *rtpSocket, const char *codecName, - int sampleRate, int sampleCount, int codecType, int dtmfType) -{ - mSocket = rtpSocket; - - // One frame per second is just not reasonable. - if (sampleRate <= 0 || sampleCount <= 0 || sampleRate <= sampleCount) { - return false; - } - - // Find AudioCodec and configure it. - if (strcmp("PCMU", codecName) == 0) { - mCodec = new UlawCodec; - } else if (strcmp("PCMA", codecName) == 0) { - mCodec = new AlawCodec; - } else { - mCodec = NULL; - } - if (mCodec == NULL || !mCodec->set(sampleCount)) { - return false; - } - - // Set AudioRecord with double buffer. Otherwise try system default. - if (mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_IN_MONO, sampleCount * 2) != NO_ERROR && - mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_IN_MONO) != NO_ERROR) { - return false; - } - - // Set AudioTrack with double buffer. Otherwise try system default. - if (mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_OUT_MONO, sampleCount * 2) != NO_ERROR && - mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_OUT_MONO) != NO_ERROR) { - return false; - } - - // Only initialize these random bits once for the maximum compatibility. - read(gRandom, &mLocalSequence, sizeof(mLocalSequence)); - read(gRandom, &mLocalTimestamp, sizeof(mLocalTimestamp)); - read(gRandom, &mLocalSsrc, sizeof(mLocalSsrc)); - - mCodecMagic = (0x8000 | codecType) << 16; - mDtmfMagic = (dtmfType == -1 ? -1 : (0x8000 | dtmfType) << 16); - - mSampleRate = sampleRate; - mSampleCount = sampleCount; - - if (mJitterBuffer == NULL) { - mJitterBuffer = new JitterBuffer(sampleCount); - } - - // mInterval is a threshold for jitter control in microseconds. To avoid - // introducing more latencies, here we use 0.8 times of the real interval. - mInterval = 1000 * sampleCount / sampleRate * 800; - - return true; -} - -bool AudioStream::startSending() -{ - if (mRecord.stopped()) { - mTimer = 0; - mNextDtmfEvent = -1; - mDtmfEvent = -1; - - if (mRecord.start() != NO_ERROR || - mSender->run("Sender", ANDROID_PRIORITY_AUDIO) != NO_ERROR) { - mRecord.stop(); - return false; - } - } - return true; -} - -bool AudioStream::startReceiving() -{ - if (mTrack.stopped()) { - mRemoteTimestamp = 0; - mRemoteSsrc = 0; - - mTrack.start(); - if (mReceiver->run("Receiver", ANDROID_PRIORITY_AUDIO) != NO_ERROR) { - mTrack.stop(); - return false; - } - } - return true; -} - -bool AudioStream::sendDtmf(int event) -{ - if (mRecord.stopped() || ~mDtmfMagic == 0) { - return false; - } - if (pthread_mutex_trylock(&mDtmfLock) != 0) { - usleep(mInterval * 2); - if (pthread_mutex_trylock(&mDtmfLock) != 0) return false; - } - mNextDtmfEvent = event; - pthread_mutex_unlock(&mDtmfLock); - return true; -} - -void AudioStream::stopSending() -{ - if (!mRecord.stopped()) { - mSender->requestExitAndWait(); - mRecord.stop(); - } -} - -void AudioStream::stopReceiving() -{ - if (!mTrack.stopped()) { - mReceiver->requestExitAndWait(); - mTrack.stop(); - } -} - -// TODO: remove this function after the mic level issue was fixed in driver. -void AudioStream::adjustMicGain(int16_t *buf, int len, int factor) -{ - int i, j; - int bound = 32768/factor; - for (i = 0; i < len; i++) { - j = buf[i]; - if (j > bound) { - buf[i] = 32767; - } else if (j < -bound) { - buf[i] = -32767; - } else { - buf[i] = (int16_t)(factor*j); - } - } -} - -// ----------------------------------------------------------------------------- - -bool AudioStream::encode() -{ - int16_t samples[mSampleCount]; - - // Read samples from AudioRecord. Since AudioRecord itself has fault - // recovery mechanism, we just return false if the length is wrong. - int length = mRecord.read(samples, sizeof(samples)); - if (length - sizeof(samples) != 0) { - LOGD("read"); - return false; - } - - adjustMicGain(samples, length/sizeof(int16_t), 8); - - mLocalSequence++; - mLocalTimestamp += mSampleCount; - - // If we have a DTMF event to send, send it now. - pthread_mutex_lock(&mDtmfLock); - int32_t event = mNextDtmfEvent; - mNextDtmfEvent = -1; - pthread_mutex_unlock(&mDtmfLock); - if (event != -1) { - mDtmfEvent = event << 24; - mDtmfDuration = 0; - } - if (mDtmfEvent != -1) { - mDtmfDuration += mSampleCount; - uint32_t packet[4] = { - htonl(mDtmfMagic | mLocalSequence), - htonl(mLocalTimestamp - mDtmfDuration), - mLocalSsrc, - htonl(mDtmfEvent | mDtmfDuration), - }; - // Make the DTMF event roughly 200 millisecond long. - if (mDtmfDuration * 5 >= mSampleRate) { - packet[3] |= 1 << 24; - mDtmfEvent = -1; - } - send(mSocket, packet, sizeof(packet)); - return true; - } - - // Otherwise encode the samples and prepare the packet. - __attribute__((aligned(4))) uint8_t packet[12 + sizeof(samples)]; - - uint32_t *header = (uint32_t *)packet; - header[0] = htonl(mCodecMagic | mLocalSequence); - header[1] = htonl(mLocalTimestamp); - header[2] = mLocalSsrc; - - length = mCodec->encode(&packet[12], samples); - if (length <= 0) { - LOGD("encode"); - return false; - } - length += 12; - - // Here we implement a simple jitter control for the outgoing packets. - // Ideally we should send out packets at a constant rate, but in practice - // every component in the pipeline might delay or speed up a little. To - // avoid making things worse, we only delay the packet which comes early. - // Note that interval is less than the real one, so the delay will be - // converged. - timeval now; - if (gettimeofday(&now, NULL) != 0) { - LOGD("gettimeofday"); - return false; - } - int interval = now.tv_sec * 1000000 + now.tv_usec - mTimer; - if (interval > 0 && interval < mInterval) { - usleep(mInterval - interval); - interval = mInterval; - } - mTimer += interval; - - send(mSocket, packet, length); - return true; -} - -bool AudioStream::decode() -{ - timeval deadline; - - if (gettimeofday(&deadline, NULL) != 0) { - LOGD("gettimeofday"); - return false; - } - - // mInterval is always less than 1000000. - deadline.tv_usec += mInterval; - if (deadline.tv_usec > 1000000) { - deadline.tv_usec -= 1000000; - deadline.tv_sec++; - } - - int16_t samples[mSampleCount]; - uint8_t *packet; - - while (1) { - int length = getPacketFromJB(mSocket, &packet, &deadline); - if (length <= 0) { - return true; - } - if (length < 12) { - continue; - } - - // Here we check all the fields in the standard RTP header. Some - // restrictions might be too tight and could be removed in the future. - int offset = 12 + (packet[0] & 0x0F) * 4; - if ((packet[0] & 0x10) != 0) { - offset += 4 + (packet[offset + 2] << 8 | packet[offset + 3]) * 4; - } - if ((packet[0] & 0x20) != 0 && length - sizeof(packet) <= 0) { - length -= packet[length - 1]; - } - length -= offset; - if (length < 0) { - continue; - } - - uint32_t *header = (uint32_t *)packet; - header[0] = ntohl(header[0]); - header[1] = ntohl(header[1]); - - if ((header[0] & 0xC07F0000) != mCodecMagic) { - LOGD("wrong magic (%X != %X)", mCodecMagic, header[0] & 0xC07F0000); - continue; - } - - mRemoteTimestamp = header[1]; - mRemoteSsrc = header[2]; - - length = mCodec->decode(samples, &packet[offset], length) * 2; - if (length <= 0) { - LOGD("decode"); - continue; - } - - // Write samples to AudioTrack. Again, since AudioTrack itself has fault - // recovery mechanism, we just return false if the length is wrong. - return mTrack.write(samples, length) == length; - } -} - -int AudioStream::getPacketFromJB(RtpSocket *rtpSocket, uint8_t **buffer, - timeval *deadline) -{ - // Here we implement a simple jitter control for the incoming packets. - // Ideally there should be only one packet every time we try to read - // from the socket. If any packets are late, we must drop incoming packets - // if the jitter buffer is full already. - - int result, count = 0; - if (mJitterBuffer->empty()) { - *buffer = mJitterBuffer->obtainBuffer(); - result = receive(rtpSocket, *buffer, mJitterBuffer->getBufferSize(), - deadline); - if (result <= 0) return result; - mJitterBuffer->pushBack(result); - } - result = mJitterBuffer->popFront(buffer); - while (1) { - void *fillBuffer = (void*) mJitterBuffer->obtainBuffer(); - int length = receive(mSocket, fillBuffer, ((fillBuffer == NULL) ? - 0 : mJitterBuffer->getBufferSize()), NULL); - if (length <= 0) break; - if (fillBuffer != NULL) { - mJitterBuffer->pushBack(length); - } else { - count++; - } - } - if (count > 0) { - LOGD("Drop %d packet(s), jitter buffer is full!", count); - } - return result; -} -// ----------------------------------------------------------------------------- - -static jfieldID gNative; - -static void throwIllegalStateException(JNIEnv *env, const char *message) -{ - jniThrowException(env, "java/lang/IllegalStateException", message); -} - -// All these JNI methods are synchronized in java class, so we implement them -// without using any mutex locks. Simple is best! - -static void prepare(JNIEnv *env, jobject thiz, jobject jRtpSocket, - jstring jCodecName, jint sampleRate, jint sampleCount, jint codecType, - jint dtmfType) -{ - AudioStream *stream = (AudioStream *)env->GetIntField(thiz, gNative); - if (stream != NULL) { - throwIllegalStateException(env, "Already prepared"); - return; - } - - RtpSocket *rtpSocket = getRtpSocket(env, jRtpSocket, true); - if (rtpSocket == NULL) { - // Exception already thrown. - return; - } - - if (jCodecName == NULL) { - jniThrowNullPointerException(env, "codecName"); - return; - } - - const char *codecName = env->GetStringUTFChars(jCodecName, NULL); - stream = new AudioStream; - if (!stream->set(rtpSocket, codecName, sampleRate, sampleCount, codecType, - dtmfType)) { - delete stream; - stream = NULL; - } - env->ReleaseStringUTFChars(jCodecName, codecName); - - if (stream == NULL) { - throwIllegalStateException(env, "Failed to create native AudioStream"); - } - env->SetIntField(thiz, gNative, (int)stream); -} - -static jboolean isPrepared(JNIEnv *env, jobject thiz) -{ - return env->GetIntField(thiz, gNative) != 0; -} - -static void startSending(JNIEnv *env, jobject thiz) -{ - AudioStream *stream = (AudioStream *)env->GetIntField(thiz, gNative); - if (stream == NULL) { - throwIllegalStateException(env, "Not prepared"); - } else if (!stream->startSending()) { - throwIllegalStateException(env, "Failed to start native AudioRecord"); - } -} - -static void startReceiving(JNIEnv *env, jobject thiz) -{ - AudioStream *stream = (AudioStream *)env->GetIntField(thiz, gNative); - if (stream == NULL) { - throwIllegalStateException(env, "Not prepared"); - } else if (!stream->startReceiving()) { - throwIllegalStateException(env, "Failed to start native AudioTrack"); - } -} - -static void sendDtmf(JNIEnv *env, jobject thiz, jint event) -{ - AudioStream *stream = (AudioStream *)env->GetIntField(thiz, gNative); - if (stream == NULL) { - throwIllegalStateException(env, "Not prepared"); - } else if (event < 0 || event > 15) { - jniThrowException(env, "java/lang/IllegalArgumentException", "event"); - } else if (!stream->sendDtmf(event)) { - throwIllegalStateException(env, "Failed to send DTMF"); - } -} - -static void stopSending(JNIEnv *env, jobject thiz) -{ - AudioStream *stream = (AudioStream *)env->GetIntField(thiz, gNative); - if (stream != NULL) { - stream->stopSending(); - } -} - -static void stopReceiving(JNIEnv *env, jobject thiz) -{ - AudioStream *stream = (AudioStream *)env->GetIntField(thiz, gNative); - if (stream != NULL) { - stream->stopReceiving(); - } -} - -static void release(JNIEnv *env, jobject thiz) -{ - delete (AudioStream *)env->GetIntField(thiz, gNative); - env->SetIntField(thiz, gNative, NULL); -} - -//------------------------------------------------------------------------------ - -static JNINativeMethod gMethods[] = { - {"prepare", "(Lcom/android/sip/rtp/RtpSocket;Ljava/lang/String;IIII)V", - (void *)prepare}, - {"isPrepared", "()Z", (void *)isPrepared}, - {"startSending", "()V", (void *)startSending}, - {"startReceiving", "()V", (void *)startReceiving}, - {"sendDtmf", "(I)V", (void *)sendDtmf}, - {"stopSending", "()V", (void *)stopSending}, - {"stopReceiving", "()V", (void *)stopReceiving}, - {"release", "()V", (void *)release}, -}; - -int registerAudioStream(JNIEnv *env) -{ - gRandom = open("/dev/urandom", O_RDONLY); - if (gRandom == -1) { - LOGE("urandom: %s", strerror(errno)); - return -1; - } - - jclass clazz; - if ((clazz = env->FindClass("com/android/sip/rtp/AudioStream")) == NULL || - (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL || - env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) { - LOGE("JNI registration failed"); - return -1; - } - return 0; -} diff --git a/src/jni/rtp/G711Codec.cpp b/src/jni/rtp/G711Codec.cpp deleted file mode 100644 index 2226850..0000000 --- a/src/jni/rtp/G711Codec.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyrightm (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> - -#include "AudioCodec.h" - -static int8_t gExponents[128] = { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, -}; - -int UlawCodec::encode(void *payload, int16_t *samples) -{ - int8_t *ulaws = (int8_t *)payload; - for (int i = 0; i < mSampleCount; ++i) { - int sample = samples[i]; - int sign = (sample >> 8) & 0x80; - if (sample < 0) { - sample = -sample; - } - sample += 132; - if (sample > 32767) { - sample = 32767; - } - int exponent = gExponents[sample >> 8]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - ulaws[i] = ~(sign | (exponent << 4) | mantissa); - } - return mSampleCount; -} - -int UlawCodec::decode(int16_t *samples, void *payload, int length) -{ - int8_t *ulaws = (int8_t *)payload; - for (int i = 0; i < length; ++i) { - int ulaw = ~ulaws[i]; - int exponent = (ulaw >> 4) & 0x07; - int mantissa = ulaw & 0x0F; - int sample = (((mantissa << 3) + 132) << exponent) - 132; - samples[i] = (ulaw < 0 ? -sample : sample); - } - return length; -} - -int AlawCodec::encode(void *payload, int16_t *samples) -{ - int8_t *alaws = (int8_t *)payload; - for (int i = 0; i < mSampleCount; ++i) { - int sample = samples[i]; - int sign = (sample >> 8) & 0x80; - if (sample < 0) { - sample = -sample; - } - if (sample > 32767) { - sample = 32767; - } - int exponent = gExponents[sample >> 8]; - int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F; - alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5; - } - return mSampleCount; -} - -int AlawCodec::decode(int16_t *samples, void *payload, int length) -{ - int8_t *alaws = (int8_t *)payload; - for (int i = 0; i < length; ++i) { - int alaw = alaws[i] ^ 0x55; - int exponent = (alaw >> 4) & 0x07; - int mantissa = alaw & 0x0F; - int sample = (exponent == 0 ? (mantissa << 4) + 8 : - ((mantissa << 3) + 132) << exponent); - samples[i] = (alaw < 0 ? sample : -sample); - } - return length; -} diff --git a/src/jni/rtp/RtpSocket.cpp b/src/jni/rtp/RtpSocket.cpp deleted file mode 100644 index cc63e3f..0000000 --- a/src/jni/rtp/RtpSocket.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <arpa/inet.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <time.h> - -#define LOG_TAG "RtpSocket" -#include <utils/Log.h> - -#include "jni.h" -#include "JNIHelp.h" - -#include "RtpSocket.h" - -static jfieldID gNative; - -struct RtpSocket -{ - int mFd; - int mFamily; - sockaddr_storage mRemote; - - RtpSocket(int fd, sockaddr_storage *local) - { - mFd = fd; - mFamily = local->ss_family; - mRemote.ss_family = ~mFamily; - } - - ~RtpSocket() { close(mFd); } -}; - -//------------------------------------------------------------------------------ - -RtpSocket *getRtpSocket(JNIEnv *env, jobject jRtpSocket, bool associated) -{ - if (jRtpSocket == NULL) { - jniThrowNullPointerException(env, "rtpSocket"); - return NULL; - } - RtpSocket *rtpSocket = (RtpSocket *)env->GetIntField(jRtpSocket, gNative); - if (rtpSocket == NULL) { - jniThrowException(env, "java/lang/IllegalStateException", "native"); - LOGE("native is NULL"); - return NULL; - } - if (associated && (rtpSocket->mRemote.ss_family != rtpSocket->mFamily)) { - jniThrowException(env, "java/lang/IllegalStateException", - strerror(ENOTCONN)); - return NULL; - } - return rtpSocket; -} - -int send(RtpSocket *rtpSocket, void *buffer, int length) -{ - return sendto(rtpSocket->mFd, buffer, length, MSG_NOSIGNAL, - (sockaddr *)&rtpSocket->mRemote, sizeof(rtpSocket->mRemote)); -} - -int receive(RtpSocket *rtpSocket, void *buffer, int length, timeval *deadline) -{ - int flag = MSG_TRUNC | MSG_DONTWAIT; - if (deadline != NULL) { - timeval timeout; - if (gettimeofday(&timeout, NULL) != 0) { - return -1; - } - - int remain = (deadline->tv_sec - timeout.tv_sec) * 1000000 + - deadline->tv_usec - timeout.tv_usec; - if (remain <= 0) { - return 0; - } - - if (remain < 1000000) { - timeout.tv_sec = 0; - timeout.tv_usec = remain; - } else { - timeout.tv_sec = remain / 1000000; - timeout.tv_usec = remain - timeout.tv_sec * 1000000; - } - if (setsockopt(rtpSocket->mFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, - sizeof(timeout)) != 0) { - return -1; - } - flag ^= MSG_DONTWAIT; - } - - length = recv(rtpSocket->mFd, buffer, length, flag); - if (length == -1 && (errno == EAGAIN || errno == EINTR)) { - return 0; - } - return length; -} - -//------------------------------------------------------------------------------ - -static void throwSocketException(JNIEnv *env, int error) -{ - jniThrowException(env, "java/net/SocketException", strerror(error)); -} - -static int parse(JNIEnv *env, jstring jAddress, jint port, sockaddr_storage *ss) -{ - if (jAddress == NULL) { - jniThrowNullPointerException(env, "address"); - return -1; - } - if (port < 0 || port > 65535) { - jniThrowException(env, "java/lang/IllegalArgumentException", "port"); - return -1; - } - const char *address = env->GetStringUTFChars(jAddress, NULL); - if (address == NULL) { - // Exception already thrown. - return -1; - } - memset(ss, 0, sizeof(*ss)); - - sockaddr_in *sin = (sockaddr_in *)ss; - if (inet_pton(AF_INET, address, &(sin->sin_addr)) > 0) { - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - env->ReleaseStringUTFChars(jAddress, address); - return 0; - } - - sockaddr_in6 *sin6 = (sockaddr_in6 *)ss; - if (inet_pton(AF_INET6, address, &(sin6->sin6_addr)) > 0) { - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(port); - env->ReleaseStringUTFChars(jAddress, address); - return 0; - } - - env->ReleaseStringUTFChars(jAddress, address); - jniThrowException(env, "java/lang/IllegalArgumentException", "address"); - return -1; -} - -static jint create(JNIEnv *env, jobject thiz, jstring jAddress) -{ - sockaddr_storage ss; - if (parse(env, jAddress, 0, &ss) < 0) { - // Exception already thrown. - return -1; - } - - int fd = socket(ss.ss_family, SOCK_DGRAM, 0); - socklen_t len = sizeof(ss); - if (fd == -1 || bind(fd, (sockaddr *)&ss, sizeof(ss)) != 0 || - getsockname(fd, (sockaddr *)&ss, &len) != 0) { - throwSocketException(env, errno); - close(fd); - return -1; - } - - uint16_t *p = (ss.ss_family == AF_INET ? - &((sockaddr_in *)&ss)->sin_port : &((sockaddr_in6 *)&ss)->sin6_port); - uint16_t port = ntohs(*p); - if ((port & 1) == 0) { - env->SetIntField(thiz, gNative, (int)new RtpSocket(fd, &ss)); - return port; - } - close(fd); - - fd = socket(ss.ss_family, SOCK_DGRAM, 0); - if (fd != -1) { - uint16_t delta = port << 1; - ++port; - - for (int i = 0; i < 1000; ++i) { - do { - port += delta; - } while (port < 1024); - *p = htons(port); - - if (bind(fd, (sockaddr *)&ss, sizeof(ss)) == 0) { - env->SetIntField(thiz, gNative, (int)new RtpSocket(fd, &ss)); - return port; - } - } - } - - throwSocketException(env, errno); - close(fd); - return -1; -} - -static void associate(JNIEnv *env, jobject thiz, jstring jAddress, jint port) -{ - RtpSocket *rtpSocket = getRtpSocket(env, thiz, false); - if (rtpSocket == NULL) { - // Exception already thrown. - return; - } - sockaddr_storage ss; - if (parse(env, jAddress, port, &ss) < 0) { - // Exception already thrown. - return; - } - if (rtpSocket->mFamily != ss.ss_family) { - throwSocketException(env, EAFNOSUPPORT); - return; - } - rtpSocket->mRemote = ss; -} - -static void release(JNIEnv *env, jobject thiz) -{ - delete (RtpSocket *)env->GetIntField(thiz, gNative); -} - -//------------------------------------------------------------------------------ - -static JNINativeMethod gMethods[] = { - {"create", "(Ljava/lang/String;)I", (void *)create}, - {"associate", "(Ljava/lang/String;I)V", (void *)associate}, - {"release", "()V", (void *)release}, -}; - -int registerRtpSocket(JNIEnv *env) -{ - jclass clazz; - if ((clazz = env->FindClass("com/android/sip/rtp/RtpSocket")) == NULL || - (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL || - env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) { - LOGE("JNI registration failed"); - return -1; - } - return 0; -} diff --git a/src/jni/rtp/RtpSocket.h b/src/jni/rtp/RtpSocket.h deleted file mode 100644 index af1e558..0000000 --- a/src/jni/rtp/RtpSocket.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __RTP_SOCKET_H__ -#define __RTP_SOCKET_H__ - -#include "jni.h" - -struct RtpSocket; - -// Returns NULL and throws an exception if an error occurs. -RtpSocket *getRtpSocket(JNIEnv *env, jobject jRtpSocket, bool associated); - -// Returns the number of bytes sent or -1 if an error occurs. The error code -// can be found in errno. -int send(RtpSocket *rtpSocket, void *buffer, int length); - -// Returns the REAL LENGTH of the packet received, 0 if deadline is reached, -// or -1 if an error occurs. The error code can be found in errno. -int receive(RtpSocket *rtpSocket, void *buffer, int length, timeval *deadline); - -#endif diff --git a/src/jni/rtp/libsiprtp.cpp b/src/jni/rtp/libsiprtp.cpp deleted file mode 100644 index 46b7f98..0000000 --- a/src/jni/rtp/libsiprtp.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> - -#include "jni.h" - -extern int registerRtpSocket(JNIEnv *env); -extern int registerAudioStream(JNIEnv *env); - -__attribute__((visibility("default"))) -jint JNI_OnLoad(JavaVM *vm, void *reserved) -{ - JNIEnv *env = NULL; - if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK || - registerRtpSocket(env) < 0 || registerAudioStream(env) < 0) { - return -1; - } - return JNI_VERSION_1_4; -} diff --git a/src/jni/rtp_jni/Android.mk b/src/jni/rtp_jni/Android.mk deleted file mode 100644 index 46ac19d..0000000 --- a/src/jni/rtp_jni/Android.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := librtp_jni - -LOCAL_SRC_FILES := \ - AudioCodec.cpp \ - AudioGroup.cpp \ - RtpStream.cpp \ - util.cpp \ - rtp_jni.cpp - -LOCAL_SHARED_LIBRARIES := \ - libnativehelper \ - libcutils \ - libutils \ - libmedia - -LOCAL_STATIC_LIBRARIES := - -LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) - -LOCAL_CFLAGS += -fvisibility=hidden - -LOCAL_PRELINK_MODULE := false - -# include $(BUILD_SHARED_LIBRARY) diff --git a/src/jni/rtp_jni/AudioCodec.cpp b/src/jni/rtp_jni/AudioCodec.cpp deleted file mode 100644 index ddd07fc..0000000 --- a/src/jni/rtp_jni/AudioCodec.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyrightm (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <string.h> - -#include "AudioCodec.h" - -namespace { - -int8_t gExponents[128] = { - 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, -}; - -//------------------------------------------------------------------------------ - -class UlawCodec : public AudioCodec -{ -public: - bool set(int sampleRate, int sampleCount) { - mSampleCount = sampleCount; - return sampleCount > 0; - } - int encode(void *payload, int16_t *samples); - int decode(int16_t *samples, void *payload, int length); -private: - int mSampleCount; -}; - -int UlawCodec::encode(void *payload, int16_t *samples) -{ - int8_t *ulaws = (int8_t *)payload; - for (int i = 0; i < mSampleCount; ++i) { - int sample = samples[i]; - int sign = (sample >> 8) & 0x80; - if (sample < 0) { - sample = -sample; - } - sample += 132; - if (sample > 32767) { - sample = 32767; - } - int exponent = gExponents[sample >> 8]; - int mantissa = (sample >> (exponent + 3)) & 0x0F; - ulaws[i] = ~(sign | (exponent << 4) | mantissa); - } - return mSampleCount; -} - -int UlawCodec::decode(int16_t *samples, void *payload, int length) -{ - int8_t *ulaws = (int8_t *)payload; - for (int i = 0; i < length; ++i) { - int ulaw = ~ulaws[i]; - int exponent = (ulaw >> 4) & 0x07; - int mantissa = ulaw & 0x0F; - int sample = (((mantissa << 3) + 132) << exponent) - 132; - samples[i] = (ulaw < 0 ? -sample : sample); - } - return length; -} - -AudioCodec *newUlawCodec() -{ - return new UlawCodec; -} - -//------------------------------------------------------------------------------ - -class AlawCodec : public AudioCodec -{ -public: - bool set(int sampleRate, int sampleCount) { - mSampleCount = sampleCount; - return sampleCount > 0; - } - int encode(void *payload, int16_t *samples); - int decode(int16_t *samples, void *payload, int length); -private: - int mSampleCount; -}; - -int AlawCodec::encode(void *payload, int16_t *samples) -{ - int8_t *alaws = (int8_t *)payload; - for (int i = 0; i < mSampleCount; ++i) { - int sample = samples[i]; - int sign = (sample >> 8) & 0x80; - if (sample < 0) { - sample = -sample; - } - if (sample > 32767) { - sample = 32767; - } - int exponent = gExponents[sample >> 8]; - int mantissa = (sample >> (exponent == 0 ? 4 : exponent + 3)) & 0x0F; - alaws[i] = (sign | (exponent << 4) | mantissa) ^ 0xD5; - } - return mSampleCount; -} - -int AlawCodec::decode(int16_t *samples, void *payload, int length) -{ - int8_t *alaws = (int8_t *)payload; - for (int i = 0; i < length; ++i) { - int alaw = alaws[i] ^ 0x55; - int exponent = (alaw >> 4) & 0x07; - int mantissa = alaw & 0x0F; - int sample = (exponent == 0 ? (mantissa << 4) + 8 : - ((mantissa << 3) + 132) << exponent); - samples[i] = (alaw < 0 ? sample : -sample); - } - return length; -} - -AudioCodec *newAlawCodec() -{ - return new AlawCodec; -} - -struct AudioCodecType { - const char *name; - AudioCodec *(*create)(); -} gAudioCodecTypes[] = { - {"PCMA", newAlawCodec}, - {"PCMU", newUlawCodec}, - {NULL, NULL}, -}; - -} // namespace - -AudioCodec *newAudioCodec(const char *codecName) -{ - AudioCodecType *type = gAudioCodecTypes; - while (type->name != NULL) { - if (strcmp(codecName, type->name) == 0) { - return type->create(); - } - ++type; - } - return NULL; -} diff --git a/src/jni/rtp_jni/AudioCodec.h b/src/jni/rtp_jni/AudioCodec.h deleted file mode 100644 index 797494c..0000000 --- a/src/jni/rtp_jni/AudioCodec.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyrightm (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> - -#ifndef __AUDIO_CODEC_H__ -#define __AUDIO_CODEC_H__ - -class AudioCodec -{ -public: - virtual ~AudioCodec() {} - // Returns true if initialization succeeds. - virtual bool set(int sampleRate, int sampleCount) = 0; - // Returns the length of payload in bytes. - virtual int encode(void *payload, int16_t *samples) = 0; - // Returns the number of decoded samples. - virtual int decode(int16_t *samples, void *payload, int length) = 0; -}; - -AudioCodec *newAudioCodec(const char *codecName); - -#endif diff --git a/src/jni/rtp_jni/AudioGroup.cpp b/src/jni/rtp_jni/AudioGroup.cpp deleted file mode 100644 index fc1ed9b..0000000 --- a/src/jni/rtp_jni/AudioGroup.cpp +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/epoll.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <time.h> -#include <arpa/inet.h> -#include <netinet/in.h> - -#define LOG_TAG "AudioGroup" -#include <cutils/atomic.h> -#include <utils/Log.h> -#include <utils/Errors.h> -#include <utils/RefBase.h> -#include <utils/threads.h> -#include <utils/SystemClock.h> -#include <media/AudioSystem.h> -#include <media/AudioRecord.h> -#include <media/AudioTrack.h> -#include <media/mediarecorder.h> - -#include "jni.h" -#include "JNIHelp.h" - -#include "AudioCodec.h" - -extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss); - -namespace { - -using namespace android; - -int gRandom = -1; - -// We use a circular array to implement jitter buffer. The simplest way is doing -// a modulo operation on the index while accessing the array. However modulo can -// be expensive on some platforms, such as ARM. Thus we round up the size of the -// array to the nearest power of 2 and then use bitwise-and instead of modulo. -// Currently we make it 256ms long and assume packet interval is 32ms or less. -// The first 64ms is the place where samples get mixed. The rest 192ms is the -// real jitter buffer. For a stream at 8000Hz it takes 4096 bytes. These numbers -// are chosen by experiments and each of them can be adjusted as needed. - -// Other notes: -// + We use elapsedRealtime() to get the time. Since we use 32bit variables -// instead of 64bit ones, comparison must be done by subtraction. -// + Sampling rate must be multiple of 1000Hz, and packet length must be in -// milliseconds. No floating points. -// + If we cannot get enough CPU, we drop samples and simulate packet loss. -// + Resampling is not done yet, so streams in one group must use the same rate. -// For the first release we might only support 8kHz and 16kHz. - -class AudioStream -{ -public: - AudioStream(); - ~AudioStream(); - bool set(int mode, int socket, sockaddr_storage *remote, - const char *codecName, int sampleRate, int sampleCount, - int codecType, int dtmfType); - - void sendDtmf(int event); - bool mix(int32_t *output, int head, int tail, int sampleRate); - void encode(int tick, AudioStream *chain); - void decode(int tick); - -private: - enum { - NORMAL = 0, - SEND_ONLY = 1, - RECEIVE_ONLY = 2, - LAST_MODE = 2, - }; - - int mMode; - int mSocket; - sockaddr_storage mRemote; - AudioCodec *mCodec; - uint32_t mCodecMagic; - uint32_t mDtmfMagic; - - int mTick; - int mSampleRate; - int mSampleCount; - int mInterval; - - int16_t *mBuffer; - int mBufferMask; - int mBufferHead; - int mBufferTail; - int mLatencyScore; - - uint16_t mSequence; - uint32_t mTimestamp; - uint32_t mSsrc; - - int mDtmfEvent; - int mDtmfStart; - - AudioStream *mNext; - - friend class AudioGroup; -}; - -AudioStream::AudioStream() -{ - mSocket = -1; - mCodec = NULL; - mBuffer = NULL; - mNext = NULL; -} - -AudioStream::~AudioStream() -{ - close(mSocket); - delete mCodec; - delete [] mBuffer; - LOGD("stream[%d] is dead", mSocket); -} - -bool AudioStream::set(int mode, int socket, sockaddr_storage *remote, - const char *codecName, int sampleRate, int sampleCount, - int codecType, int dtmfType) -{ - if (mode < 0 || mode > LAST_MODE) { - return false; - } - mMode = mode; - - if (codecName) { - mRemote = *remote; - mCodec = newAudioCodec(codecName); - if (!mCodec || !mCodec->set(sampleRate, sampleCount)) { - return false; - } - } - - mCodecMagic = (0x8000 | codecType) << 16; - mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16; - - mTick = elapsedRealtime(); - mSampleRate = sampleRate / 1000; - mSampleCount = sampleCount; - mInterval = mSampleCount / mSampleRate; - - // Allocate jitter buffer. - for (mBufferMask = 8192; mBufferMask < sampleRate; mBufferMask <<= 1); - mBufferMask >>= 2; - mBuffer = new int16_t[mBufferMask]; - --mBufferMask; - mBufferHead = 0; - mBufferTail = 0; - mLatencyScore = 0; - - // Initialize random bits. - read(gRandom, &mSequence, sizeof(mSequence)); - read(gRandom, &mTimestamp, sizeof(mTimestamp)); - read(gRandom, &mSsrc, sizeof(mSsrc)); - - mDtmfEvent = -1; - mDtmfStart = 0; - - // Only take over the socket when succeeded. - mSocket = socket; - - LOGD("stream[%d] is configured as %s %dkHz %dms", mSocket, - (codecName ? codecName : "RAW"), mSampleRate, mInterval); - return true; -} - -void AudioStream::sendDtmf(int event) -{ - if (mDtmfMagic != 0) { - mDtmfEvent = event << 24; - mDtmfStart = mTimestamp + mSampleCount; - } -} - -bool AudioStream::mix(int32_t *output, int head, int tail, int sampleRate) -{ - if (mMode == SEND_ONLY) { - return false; - } - - if (head - mBufferHead < 0) { - head = mBufferHead; - } - if (tail - mBufferTail > 0) { - tail = mBufferTail; - } - if (tail - head <= 0) { - return false; - } - - head *= mSampleRate; - tail *= mSampleRate; - - if (sampleRate == mSampleRate) { - for (int i = head; i - tail < 0; ++i) { - output[i - head] += mBuffer[i & mBufferMask]; - } - } else { - // TODO: implement resampling. - return false; - } - return true; -} - -void AudioStream::encode(int tick, AudioStream *chain) -{ - if (tick - mTick >= mInterval) { - // We just missed the train. Pretend that packets in between are lost. - int skipped = (tick - mTick) / mInterval; - mTick += skipped * mInterval; - mSequence += skipped; - mTimestamp += skipped * mSampleCount; - LOGD("stream[%d] skips %d packets", mSocket, skipped); - } - - tick = mTick; - mTick += mInterval; - ++mSequence; - mTimestamp += mSampleCount; - - if (mMode == RECEIVE_ONLY) { - return; - } - - // If there is an ongoing DTMF event, send it now. - if (mDtmfEvent != -1) { - int duration = mTimestamp - mDtmfStart; - // Make sure duration is reasonable. - if (duration >= 0 && duration < mSampleRate * 100) { - duration += mSampleCount; - int32_t buffer[4] = { - htonl(mDtmfMagic | mSequence), - htonl(mDtmfStart), - mSsrc, - htonl(mDtmfEvent | duration), - }; - if (duration >= mSampleRate * 100) { - buffer[3] |= htonl(1 << 23); - mDtmfEvent = -1; - } - sendto(mSocket, buffer, sizeof(buffer), MSG_DONTWAIT, - (sockaddr *)&mRemote, sizeof(mRemote)); - return; - } - mDtmfEvent = -1; - } - - // It is time to mix streams. - bool mixed = false; - int32_t buffer[mSampleCount + 3]; - memset(buffer, 0, sizeof(buffer)); - while (chain) { - if (chain != this && - chain->mix(buffer, tick - mInterval, tick, mSampleRate)) { - mixed = true; - } - chain = chain->mNext; - } - if (!mixed) { - LOGD("stream[%d] no data", mSocket); - return; - } - - // Cook the packet and send it out. - int16_t samples[mSampleCount]; - for (int i = 0; i < mSampleCount; ++i) { - int32_t sample = buffer[i]; - if (sample < -32768) { - sample = -32768; - } - if (sample > 32767) { - sample = 32767; - } - samples[i] = sample; - } - if (!mCodec) { - // Special case for device stream. - send(mSocket, samples, sizeof(samples), MSG_DONTWAIT); - return; - } - - buffer[0] = htonl(mCodecMagic | mSequence); - buffer[1] = htonl(mTimestamp); - buffer[2] = mSsrc; - int length = mCodec->encode(&buffer[3], samples); - if (length <= 0) { - LOGD("stream[%d] encoder error", mSocket); - return; - } - sendto(mSocket, buffer, length + 12, MSG_DONTWAIT, (sockaddr *)&mRemote, - sizeof(mRemote)); -} - -void AudioStream::decode(int tick) -{ - char c; - if (mMode == SEND_ONLY) { - recv(mSocket, &c, 1, MSG_DONTWAIT); - return; - } - - // Make sure mBufferHead and mBufferTail are reasonable. - if ((unsigned int)(tick + 256 - mBufferHead) > 1024) { - mBufferHead = tick - 64; - mBufferTail = mBufferHead; - } - - if (tick - mBufferHead > 64) { - // Throw away outdated samples. - mBufferHead = tick - 64; - if (mBufferTail - mBufferHead < 0) { - mBufferTail = mBufferHead; - } - } - - if (mBufferTail - tick <= 80) { - mLatencyScore = tick; - } else if (tick - mLatencyScore >= 5000) { - // Reset the jitter buffer to 40ms if the latency keeps larger than 80ms - // in the past 5s. This rarely happens, so let us just keep it simple. - LOGD("stream[%d] latency control", mSocket); - mBufferTail = tick + 40; - } - - if (mBufferTail - mBufferHead > 256 - mInterval) { - // Buffer overflow. Drop the packet. - LOGD("stream[%d] buffer overflow", mSocket); - recv(mSocket, &c, 1, MSG_DONTWAIT); - return; - } - - // Receive the packet and decode it. - int16_t samples[mSampleCount]; - int length = 0; - if (!mCodec) { - // Special case for device stream. - length = recv(mSocket, samples, sizeof(samples), - MSG_TRUNC | MSG_DONTWAIT) >> 1; - } else { - __attribute__((aligned(4))) uint8_t buffer[2048]; - length = recv(mSocket, buffer, sizeof(buffer), - MSG_TRUNC | MSG_DONTWAIT); - - // Do we need to check SSRC, sequence, and timestamp? They are not - // reliable but at least they can be used to identity duplicates? - if (length < 12 || length > (int)sizeof(buffer) || - (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) { - LOGD("stream[%d] malformed packet", mSocket); - return; - } - int offset = 12 + ((buffer[0] & 0x0F) << 2); - if ((buffer[0] & 0x10) != 0) { - offset += 4 + (ntohs(*(uint16_t *)&buffer[offset + 2]) << 2); - } - if ((buffer[0] & 0x20) != 0) { - length -= buffer[length - 1]; - } - length -= offset; - if (length >= 0) { - length = mCodec->decode(samples, &buffer[offset], length); - } - } - if (length != mSampleCount) { - LOGD("stream[%d] decoder error", mSocket); - return; - } - - if (tick - mBufferTail > 0) { - // Buffer underrun. Reset the jitter buffer to 40ms. - LOGD("stream[%d] buffer underrun", mSocket); - if (mBufferTail - mBufferHead <= 0) { - mBufferHead = tick + 40; - mBufferTail = mBufferHead; - } else { - int tail = (tick + 40) * mSampleRate; - for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) { - mBuffer[i & mBufferMask] = 0; - } - mBufferTail = tick + 40; - } - } - - // Append to the jitter buffer. - int tail = mBufferTail * mSampleRate; - for (int i = 0; i < mSampleCount; ++i) { - mBuffer[tail & mBufferMask] = samples[i]; - ++tail; - } - mBufferTail += mInterval; -} - -//------------------------------------------------------------------------------ - -class AudioGroup -{ -public: - AudioGroup(); - ~AudioGroup(); - bool set(int sampleRate, int sampleCount); - - bool setMode(int mode); - bool sendDtmf(int event); - bool add(AudioStream *stream); - bool remove(int socket); - -private: - enum { - ON_HOLD = 0, - MUTED = 1, - NORMAL = 2, - EC_ENABLED = 3, - LAST_MODE = 3, - }; - int mMode; - AudioStream *mChain; - int mEventQueue; - volatile int mDtmfEvent; - - int mSampleCount; - int mDeviceSocket; - AudioTrack mTrack; - AudioRecord mRecord; - - bool networkLoop(); - bool deviceLoop(); - - class NetworkThread : public Thread - { - public: - NetworkThread(AudioGroup *group) : Thread(false), mGroup(group) {} - - bool start() - { - if (run("Network", ANDROID_PRIORITY_AUDIO) != NO_ERROR) { - LOGE("cannot start network thread"); - return false; - } - return true; - } - - private: - AudioGroup *mGroup; - bool threadLoop() - { - return mGroup->networkLoop(); - } - }; - sp<NetworkThread> mNetworkThread; - - class DeviceThread : public Thread - { - public: - DeviceThread(AudioGroup *group) : Thread(false), mGroup(group) {} - - bool start() - { - char c; - while (recv(mGroup->mDeviceSocket, &c, 1, MSG_DONTWAIT) == 1); - - if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) { - LOGE("cannot start device thread"); - return false; - } - return true; - } - - private: - AudioGroup *mGroup; - bool threadLoop() - { - return mGroup->deviceLoop(); - } - }; - sp<DeviceThread> mDeviceThread; -}; - -AudioGroup::AudioGroup() -{ - mMode = ON_HOLD; - mChain = NULL; - mEventQueue = -1; - mDtmfEvent = -1; - mDeviceSocket = -1; - mNetworkThread = new NetworkThread(this); - mDeviceThread = new DeviceThread(this); -} - -AudioGroup::~AudioGroup() -{ - mNetworkThread->requestExitAndWait(); - mDeviceThread->requestExitAndWait(); - mTrack.stop(); - mRecord.stop(); - close(mEventQueue); - close(mDeviceSocket); - while (mChain) { - AudioStream *next = mChain->mNext; - delete mChain; - mChain = next; - } - LOGD("group[%d] is dead", mDeviceSocket); -} - -#define FROYO_COMPATIBLE -#ifdef FROYO_COMPATIBLE - -// Copied from AudioRecord.cpp. -status_t AudioRecord_getMinFrameCount( - int* frameCount, - uint32_t sampleRate, - int format, - int channelCount) -{ - size_t size = 0; - if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &size) - != NO_ERROR) { - LOGE("AudioSystem could not query the input buffer size."); - return NO_INIT; - } - - if (size == 0) { - LOGE("Unsupported configuration: sampleRate %d, format %d, channelCount %d", - sampleRate, format, channelCount); - return BAD_VALUE; - } - - // We double the size of input buffer for ping pong use of record buffer. - size <<= 1; - - if (AudioSystem::isLinearPCM(format)) { - size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1); - } - - *frameCount = size; - return NO_ERROR; -} - -// Copied from AudioTrack.cpp. -status_t AudioTrack_getMinFrameCount( - int* frameCount, - int streamType, - uint32_t sampleRate) -{ - int afSampleRate; - if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { - return NO_INIT; - } - int afFrameCount; - if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { - return NO_INIT; - } - uint32_t afLatency; - if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) { - return NO_INIT; - } - - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate); - if (minBufCount < 2) minBufCount = 2; - - *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount : - afFrameCount * minBufCount * sampleRate / afSampleRate; - return NO_ERROR; -} - -#endif - -bool AudioGroup::set(int sampleRate, int sampleCount) -{ - mEventQueue = epoll_create(2); - if (mEventQueue == -1) { - LOGE("epoll_create: %s", strerror(errno)); - return false; - } - - mSampleCount = sampleCount; - - // Find out the frame count for AudioTrack and AudioRecord. - int output = 0; - int input = 0; -#ifdef FROYO_COMPATIBLE - if (AudioTrack_getMinFrameCount(&output, AudioSystem::VOICE_CALL, - sampleRate) != NO_ERROR || output <= 0 || - AudioRecord_getMinFrameCount(&input, sampleRate, - AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) { - LOGE("cannot compute frame count"); - return false; - } -#else - if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL, - sampleRate) != NO_ERROR || output <= 0 || - AudioRecord::getMinFrameCount(&input, sampleRate, - AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) { - LOGE("cannot compute frame count"); - return false; - } -#endif - LOGD("reported frame count: output %d, input %d", output, input); - - output = (output + sampleCount - 1) / sampleCount * sampleCount; - input = (input + sampleCount - 1) / sampleCount * sampleCount; - if (input < output * 2) { - input = output * 2; - } - LOGD("adjusted frame count: output %d, input %d", output, input); - - // Initialize AudioTrack and AudioRecord. - if (mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR || - mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT, - AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) { - LOGE("cannot initialize audio device"); - return false; - } - LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency()); - - // TODO: initialize echo canceler here. - - // Create device socket. - int pair[2]; - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) { - LOGE("socketpair: %s", strerror(errno)); - return false; - } - mDeviceSocket = pair[0]; - - // Create device stream. - mChain = new AudioStream; - if (!mChain->set(AudioStream::NORMAL, pair[1], NULL, NULL, - sampleRate, sampleCount, -1, -1)) { - close(pair[1]); - LOGE("cannot initialize device stream"); - return false; - } - - // Give device socket a reasonable timeout and buffer size. - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 1000 * sampleCount / sampleRate * 500; - if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) || - setsockopt(pair[0], SOL_SOCKET, SO_RCVBUF, &output, sizeof(output)) || - setsockopt(pair[1], SOL_SOCKET, SO_SNDBUF, &output, sizeof(output))) { - LOGE("setsockopt: %s", strerror(errno)); - return false; - } - - // Add device stream into event queue. - epoll_event event; - event.events = EPOLLIN; - event.data.ptr = mChain; - if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, pair[1], &event)) { - LOGE("epoll_ctl: %s", strerror(errno)); - return false; - } - - // Anything else? - LOGD("stream[%d] joins group[%d]", pair[1], pair[0]); - return true; -} - -bool AudioGroup::setMode(int mode) -{ - if (mode < 0 || mode > LAST_MODE) { - return false; - } - if (mMode == mode) { - return true; - } - - LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode); - mMode = mode; - - mDeviceThread->requestExitAndWait(); - if (mode == ON_HOLD) { - mTrack.stop(); - mRecord.stop(); - return true; - } - - mTrack.start(); - if (mode == MUTED) { - mRecord.stop(); - } else { - mRecord.start(); - } - - if (!mDeviceThread->start()) { - mTrack.stop(); - mRecord.stop(); - return false; - } - return true; -} - -bool AudioGroup::sendDtmf(int event) -{ - if (event < 0 || event > 15) { - return false; - } - - // DTMF is rarely used, so we try to make it as lightweight as possible. - // Using volatile might be dodgy, but using a pipe or pthread primitives - // or stop-set-restart threads seems too heavy. Will investigate later. - timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = 100000000; - for (int i = 0; mDtmfEvent != -1 && i < 20; ++i) { - nanosleep(&ts, NULL); - } - if (mDtmfEvent != -1) { - return false; - } - mDtmfEvent = event; - nanosleep(&ts, NULL); - return true; -} - -bool AudioGroup::add(AudioStream *stream) -{ - mNetworkThread->requestExitAndWait(); - - epoll_event event; - event.events = EPOLLIN; - event.data.ptr = stream; - if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, stream->mSocket, &event)) { - LOGE("epoll_ctl: %s", strerror(errno)); - return false; - } - - stream->mNext = mChain->mNext; - mChain->mNext = stream; - if (!mNetworkThread->start()) { - // Only take over the stream when succeeded. - mChain->mNext = stream->mNext; - return false; - } - - LOGD("stream[%d] joins group[%d]", stream->mSocket, mDeviceSocket); - return true; -} - -bool AudioGroup::remove(int socket) -{ - mNetworkThread->requestExitAndWait(); - - for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) { - AudioStream *target = stream->mNext; - if (target->mSocket == socket) { - stream->mNext = target->mNext; - LOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket); - delete target; - break; - } - } - - // Do not start network thread if there is only one stream. - if (!mChain->mNext || !mNetworkThread->start()) { - return false; - } - return true; -} - -bool AudioGroup::networkLoop() -{ - int tick = elapsedRealtime(); - int deadline = tick + 10; - int count = 0; - - for (AudioStream *stream = mChain; stream; stream = stream->mNext) { - if (!stream->mTick || tick - stream->mTick >= 0) { - stream->encode(tick, mChain); - } - if (deadline - stream->mTick > 0) { - deadline = stream->mTick; - } - ++count; - } - - if (mDtmfEvent != -1) { - int event = mDtmfEvent; - for (AudioStream *stream = mChain; stream; stream = stream->mNext) { - stream->sendDtmf(event); - } - mDtmfEvent = -1; - } - - deadline -= tick; - if (deadline < 1) { - deadline = 1; - } - - epoll_event events[count]; - count = epoll_wait(mEventQueue, events, count, deadline); - if (count == -1) { - LOGE("epoll_wait: %s", strerror(errno)); - return false; - } - for (int i = 0; i < count; ++i) { - ((AudioStream *)events[i].data.ptr)->decode(tick); - } - - return true; -} - -bool AudioGroup::deviceLoop() -{ - int16_t output[mSampleCount]; - - if (recv(mDeviceSocket, output, sizeof(output), 0) <= 0) { - memset(output, 0, sizeof(output)); - } - if (mTrack.write(output, sizeof(output)) != (int)sizeof(output)) { - LOGE("cannot write to AudioTrack"); - return false; - } - - if (mMode != MUTED) { - uint32_t frameCount = mRecord.frameCount(); - AudioRecord::Buffer input; - input.frameCount = frameCount; - - if (mRecord.obtainBuffer(&input, -1) != NO_ERROR) { - LOGE("cannot read from AudioRecord"); - return false; - } - - if (input.frameCount < (uint32_t)mSampleCount) { - input.frameCount = 0; - } else { - if (mMode == NORMAL) { - send(mDeviceSocket, input.i8, sizeof(output), MSG_DONTWAIT); - } else { - // TODO: Echo canceller runs here. - send(mDeviceSocket, input.i8, sizeof(output), MSG_DONTWAIT); - } - if (input.frameCount < frameCount) { - input.frameCount = mSampleCount; - } - } - - mRecord.releaseBuffer(&input); - } - - return true; -} - -//------------------------------------------------------------------------------ - -static jfieldID gNative; -static jfieldID gMode; - -jint add(JNIEnv *env, jobject thiz, jint mode, - jint socket, jstring jRemoteAddress, jint remotePort, - jstring jCodecName, jint sampleRate, jint sampleCount, - jint codecType, jint dtmfType) -{ - const char *codecName = NULL; - AudioStream *stream = NULL; - AudioGroup *group = NULL; - - // Sanity check. - sockaddr_storage remote; - if (parse(env, jRemoteAddress, remotePort, &remote) < 0) { - // Exception already thrown. - return -1; - } - if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - goto error; - } - if (!jCodecName) { - jniThrowNullPointerException(env, "codecName"); - return -1; - } - codecName = env->GetStringUTFChars(jCodecName, NULL); - if (!codecName) { - // Exception already thrown. - return -1; - } - - // Create audio stream. - stream = new AudioStream; - if (!stream->set(mode, socket, &remote, codecName, sampleRate, sampleCount, - codecType, dtmfType)) { - jniThrowException(env, "java/lang/IllegalStateException", - "cannot initialize audio stream"); - goto error; - } - socket = -1; - - // Create audio group. - group = (AudioGroup *)env->GetIntField(thiz, gNative); - if (!group) { - int mode = env->GetIntField(thiz, gMode); - group = new AudioGroup; - if (!group->set(8000, 256) || !group->setMode(mode)) { - jniThrowException(env, "java/lang/IllegalStateException", - "cannot initialize audio group"); - goto error; - } - } - - // Add audio stream into audio group. - if (!group->add(stream)) { - jniThrowException(env, "java/lang/IllegalStateException", - "cannot add audio stream"); - goto error; - } - - // Succeed. - env->SetIntField(thiz, gNative, (int)group); - env->ReleaseStringUTFChars(jCodecName, codecName); - return socket; - -error: - delete group; - delete stream; - close(socket); - env->SetIntField(thiz, gNative, NULL); - env->ReleaseStringUTFChars(jCodecName, codecName); - return -1; -} - -void remove(JNIEnv *env, jobject thiz, jint socket) -{ - AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative); - if (group) { - if (socket == -1 || !group->remove(socket)) { - delete group; - env->SetIntField(thiz, gNative, NULL); - } - } -} - -void setMode(JNIEnv *env, jobject thiz, jint mode) -{ - AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative); - if (group && !group->setMode(mode)) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return; - } - env->SetIntField(thiz, gMode, mode); -} - -void sendDtmf(JNIEnv *env, jobject thiz, jint event) -{ - AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative); - if (group && !group->sendDtmf(event)) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - } -} - -JNINativeMethod gMethods[] = { - {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)I", (void *)add}, - {"remove", "(I)V", (void *)remove}, - {"setMode", "(I)V", (void *)setMode}, - {"sendDtmf", "(I)V", (void *)sendDtmf}, -}; - -} // namespace - -int registerAudioGroup(JNIEnv *env) -{ - gRandom = open("/dev/urandom", O_RDONLY); - if (gRandom == -1) { - LOGE("urandom: %s", strerror(errno)); - return -1; - } - - jclass clazz; - if ((clazz = env->FindClass("android/net/rtp/AudioGroup")) == NULL || - (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL || - (gMode = env->GetFieldID(clazz, "mMode", "I")) == NULL || - env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) { - LOGE("JNI registration failed"); - return -1; - } - return 0; -} diff --git a/src/jni/rtp_jni/RtpStream.cpp b/src/jni/rtp_jni/RtpStream.cpp deleted file mode 100644 index 33b88e4..0000000 --- a/src/jni/rtp_jni/RtpStream.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#include <netinet/in.h> - -#define LOG_TAG "RtpStream" -#include <utils/Log.h> - -#include "jni.h" -#include "JNIHelp.h" - -extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss); - -namespace { - -jfieldID gNative; - -jint create(JNIEnv *env, jobject thiz, jstring jAddress) -{ - env->SetIntField(thiz, gNative, -1); - - sockaddr_storage ss; - if (parse(env, jAddress, 0, &ss) < 0) { - // Exception already thrown. - return -1; - } - - int socket = ::socket(ss.ss_family, SOCK_DGRAM, 0); - socklen_t len = sizeof(ss); - if (socket == -1 || bind(socket, (sockaddr *)&ss, sizeof(ss)) != 0 || - getsockname(socket, (sockaddr *)&ss, &len) != 0) { - jniThrowException(env, "java/net/SocketException", strerror(errno)); - ::close(socket); - return -1; - } - - uint16_t *p = (ss.ss_family == AF_INET) ? - &((sockaddr_in *)&ss)->sin_port : &((sockaddr_in6 *)&ss)->sin6_port; - uint16_t port = ntohs(*p); - if ((port & 1) == 0) { - env->SetIntField(thiz, gNative, socket); - return port; - } - ::close(socket); - - socket = ::socket(ss.ss_family, SOCK_DGRAM, 0); - if (socket != -1) { - uint16_t delta = port << 1; - ++port; - - for (int i = 0; i < 1000; ++i) { - do { - port += delta; - } while (port < 1024); - *p = htons(port); - - if (bind(socket, (sockaddr *)&ss, sizeof(ss)) == 0) { - env->SetIntField(thiz, gNative, socket); - return port; - } - } - } - - jniThrowException(env, "java/net/SocketException", strerror(errno)); - ::close(socket); - return -1; -} - -jint dup(JNIEnv *env, jobject thiz) -{ - int socket1 = env->GetIntField(thiz, gNative); - int socket2 = ::dup(socket1); - if (socket2 == -1) { - jniThrowException(env, "java/lang/IllegalStateException", strerror(errno)); - } - LOGD("dup %d to %d", socket1, socket2); - return socket2; -} - -void close(JNIEnv *env, jobject thiz) -{ - int socket = env->GetIntField(thiz, gNative); - ::close(socket); - env->SetIntField(thiz, gNative, -1); - LOGD("close %d", socket); -} - -JNINativeMethod gMethods[] = { - {"create", "(Ljava/lang/String;)I", (void *)create}, - {"dup", "()I", (void *)dup}, - {"close", "()V", (void *)close}, -}; - -} // namespace - -int registerRtpStream(JNIEnv *env) -{ - jclass clazz; - if ((clazz = env->FindClass("android/net/rtp/RtpStream")) == NULL || - (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL || - env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) { - LOGE("JNI registration failed"); - return -1; - } - return 0; -} diff --git a/src/jni/rtp_jni/rtp_jni.cpp b/src/jni/rtp_jni/rtp_jni.cpp deleted file mode 100644 index 9f4bff9..0000000 --- a/src/jni/rtp_jni/rtp_jni.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> - -#include "jni.h" - -extern int registerRtpStream(JNIEnv *env); -extern int registerAudioGroup(JNIEnv *env); - -__attribute__((visibility("default"))) jint JNI_OnLoad(JavaVM *vm, void *unused) -{ - JNIEnv *env = NULL; - if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK || - registerRtpStream(env) < 0 || registerAudioGroup(env) < 0) { - return -1; - } - return JNI_VERSION_1_4; -} diff --git a/src/jni/rtp_jni/util.cpp b/src/jni/rtp_jni/util.cpp deleted file mode 100644 index 1d702fc..0000000 --- a/src/jni/rtp_jni/util.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <string.h> -#include <arpa/inet.h> -#include <netinet/in.h> - -#include "jni.h" -#include "JNIHelp.h" - -int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss) -{ - if (!jAddress) { - jniThrowNullPointerException(env, "address"); - return -1; - } - if (port < 0 || port > 65535) { - jniThrowException(env, "java/lang/IllegalArgumentException", "port"); - return -1; - } - const char *address = env->GetStringUTFChars(jAddress, NULL); - if (!address) { - // Exception already thrown. - return -1; - } - memset(ss, 0, sizeof(*ss)); - - sockaddr_in *sin = (sockaddr_in *)ss; - if (inet_pton(AF_INET, address, &(sin->sin_addr)) > 0) { - sin->sin_family = AF_INET; - sin->sin_port = htons(port); - env->ReleaseStringUTFChars(jAddress, address); - return 0; - } - - sockaddr_in6 *sin6 = (sockaddr_in6 *)ss; - if (inet_pton(AF_INET6, address, &(sin6->sin6_addr)) > 0) { - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(port); - env->ReleaseStringUTFChars(jAddress, address); - return 0; - } - - env->ReleaseStringUTFChars(jAddress, address); - jniThrowException(env, "java/lang/IllegalArgumentException", "address"); - return -1; -} |