summaryrefslogtreecommitdiffstats
path: root/voip
diff options
context:
space:
mode:
authorChia-chi Yeh <chiachi@android.com>2010-09-30 08:51:59 +0800
committerChia-chi Yeh <chiachi@android.com>2010-09-30 08:55:12 +0800
commitf88fc1fa907f720df4a3e915509e688e9e4cf1f8 (patch)
treef60ea63675e44033619680bffd8bca5a3e01638b /voip
parentf4ae94229d736c7dbd3c5c36d484213d51545702 (diff)
downloadframeworks_base-f88fc1fa907f720df4a3e915509e688e9e4cf1f8.zip
frameworks_base-f88fc1fa907f720df4a3e915509e688e9e4cf1f8.tar.gz
frameworks_base-f88fc1fa907f720df4a3e915509e688e9e4cf1f8.tar.bz2
RTP: Enable AMR codec.
Change-Id: I49e6bdc1b67306b44173f2f346f8372a50264870
Diffstat (limited to 'voip')
-rw-r--r--voip/java/android/net/rtp/AudioCodec.java3
-rw-r--r--voip/jni/rtp/AmrCodec.cpp171
-rw-r--r--voip/jni/rtp/AudioCodec.cpp2
3 files changed, 174 insertions, 2 deletions
diff --git a/voip/java/android/net/rtp/AudioCodec.java b/voip/java/android/net/rtp/AudioCodec.java
index f171806..3877aeb 100644
--- a/voip/java/android/net/rtp/AudioCodec.java
+++ b/voip/java/android/net/rtp/AudioCodec.java
@@ -80,8 +80,7 @@ public class AudioCodec {
*/
public static final AudioCodec AMR = new AudioCodec(97, "AMR/8000", null);
- // TODO: add rest of the codecs when the native part is done.
- private static final AudioCodec[] sCodecs = {GSM_EFR, GSM, PCMU, PCMA};
+ private static final AudioCodec[] sCodecs = {GSM_EFR, AMR, GSM, PCMU, PCMA};
private AudioCodec(int type, String rtpmap, String fmtp) {
this.type = type;
diff --git a/voip/jni/rtp/AmrCodec.cpp b/voip/jni/rtp/AmrCodec.cpp
index 9a2227d..f3ecac2 100644
--- a/voip/jni/rtp/AmrCodec.cpp
+++ b/voip/jni/rtp/AmrCodec.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string.h>
+
#include "AudioCodec.h"
#include "gsmamr_dec.h"
@@ -21,6 +23,170 @@
namespace {
+const int gFrameBits[8] = {95, 103, 118, 134, 148, 159, 204, 244};
+
+//------------------------------------------------------------------------------
+
+// See RFC 4867 for the encoding details.
+
+class AmrCodec : public AudioCodec
+{
+public:
+ AmrCodec() {
+ if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
+ mEncoder = NULL;
+ }
+ if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
+ mDecoder = NULL;
+ }
+ }
+
+ ~AmrCodec() {
+ if (mEncoder) {
+ AMREncodeExit(&mEncoder, &mSidSync);
+ }
+ if (mDecoder) {
+ GSMDecodeFrameExit(&mDecoder);
+ }
+ }
+
+ int set(int sampleRate, const char *fmtp);
+ int encode(void *payload, int16_t *samples);
+ int decode(int16_t *samples, void *payload, int length);
+
+private:
+ void *mEncoder;
+ void *mSidSync;
+ void *mDecoder;
+
+ int mMode;
+ int mModeSet;
+ bool mOctetAligned;
+};
+
+int AmrCodec::set(int sampleRate, const char *fmtp)
+{
+ // These parameters are not supported.
+ if (strcasestr(fmtp, "crc=1") || strcasestr(fmtp, "robust-sorting=1") ||
+ strcasestr(fmtp, "interleaving=")) {
+ return -1;
+ }
+
+ // Handle mode-set and octet-align.
+ char *modes = strcasestr(fmtp, "mode-set=");
+ if (modes) {
+ mMode = 0;
+ mModeSet = 0;
+ for (char c = *modes; c && c != ' '; c = *++modes) {
+ if (c >= '0' && c <= '7') {
+ int mode = c - '0';
+ if (mode > mMode) {
+ mMode = mode;
+ }
+ mModeSet |= 1 << mode;
+ }
+ }
+ } else {
+ mMode = 7;
+ mModeSet = 0xFF;
+ }
+ mOctetAligned = (strcasestr(fmtp, "octet-align=1") != NULL);
+
+ // TODO: handle mode-change-*.
+
+ return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
+}
+
+int AmrCodec::encode(void *payload, int16_t *samples)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+
+ int length = AMREncode(mEncoder, mSidSync, (Mode)mMode,
+ samples, bytes + 1, &type, AMR_TX_WMF);
+
+ if (type != mMode || length != (8 + gFrameBits[mMode] + 7) >> 3) {
+ return -1;
+ }
+
+ if (mOctetAligned) {
+ bytes[0] = 0xF0;
+ bytes[1] = (mMode << 3) | 0x04;
+ ++length;
+ } else {
+ // CMR = 15 (4-bit), F = 0 (1-bit), FT = mMode (4-bit), Q = 1 (1-bit).
+ bytes[0] = 0xFF;
+ bytes[1] = 0xC0 | (mMode << 1) | 1;
+
+ // Shift left 6 bits and update the length.
+ bytes[length + 1] = 0;
+ for (int i = 0; i <= length; ++i) {
+ bytes[i] = (bytes[i] << 6) | (bytes[i + 1] >> 2);
+ }
+ length = (10 + gFrameBits[mMode] + 7) >> 3;
+ }
+ return length;
+}
+
+int AmrCodec::decode(int16_t *samples, void *payload, int length)
+{
+ unsigned char *bytes = (unsigned char *)payload;
+ Frame_Type_3GPP type;
+ if (length < 2) {
+ return -1;
+ }
+ int request = bytes[0] >> 4;
+
+ if (mOctetAligned) {
+ if ((bytes[1] & 0xC4) != 0x04) {
+ return -1;
+ }
+ type = (Frame_Type_3GPP)(bytes[1] >> 3);
+ if (length != (16 + gFrameBits[type] + 7) >> 3) {
+ return -1;
+ }
+ length -= 2;
+ bytes += 2;
+ } else {
+ if ((bytes[0] & 0x0C) || !(bytes[1] & 0x40)) {
+ return -1;
+ }
+ type = (Frame_Type_3GPP)((bytes[0] << 1 | bytes[1] >> 7) & 0x07);
+ if (length != (10 + gFrameBits[type] + 7) >> 3) {
+ return -1;
+ }
+
+ // Shift left 2 bits and update the length.
+ --length;
+ for (int i = 1; i < length; ++i) {
+ bytes[i] = (bytes[i] << 2) | (bytes[i + 1] >> 6);
+ }
+ bytes[length] <<= 2;
+ length = (gFrameBits[type] + 7) >> 3;
+ ++bytes;
+ }
+
+ if (AMRDecode(mDecoder, type, bytes, samples, MIME_IETF) != length) {
+ return -1;
+ }
+
+ // Handle CMR
+ if (request < 8 && request != mMode) {
+ for (int i = request; i >= 0; --i) {
+ if (mModeSet & (1 << i)) {
+ mMode = request;
+ break;
+ }
+ }
+ }
+
+ return 160;
+}
+
+//------------------------------------------------------------------------------
+
+// See RFC 3551 for the encoding details.
+
class GsmEfrCodec : public AudioCodec
{
public:
@@ -91,6 +257,11 @@ int GsmEfrCodec::decode(int16_t *samples, void *payload, int length)
} // namespace
+AudioCodec *newAmrCodec()
+{
+ return new AmrCodec;
+}
+
AudioCodec *newGsmEfrCodec()
{
return new GsmEfrCodec;
diff --git a/voip/jni/rtp/AudioCodec.cpp b/voip/jni/rtp/AudioCodec.cpp
index afc193c..2267ea0 100644
--- a/voip/jni/rtp/AudioCodec.cpp
+++ b/voip/jni/rtp/AudioCodec.cpp
@@ -21,6 +21,7 @@
extern AudioCodec *newAlawCodec();
extern AudioCodec *newUlawCodec();
extern AudioCodec *newGsmCodec();
+extern AudioCodec *newAmrCodec();
extern AudioCodec *newGsmEfrCodec();
struct AudioCodecType {
@@ -30,6 +31,7 @@ struct AudioCodecType {
{"PCMA", newAlawCodec},
{"PCMU", newUlawCodec},
{"GSM", newGsmCodec},
+ {"AMR", newAmrCodec},
{"GSM-EFR", newGsmEfrCodec},
{NULL, NULL},
};