diff options
author | Chia-chi Yeh <chiachi@android.com> | 2010-09-30 08:51:59 +0800 |
---|---|---|
committer | Chia-chi Yeh <chiachi@android.com> | 2010-09-30 08:55:12 +0800 |
commit | f88fc1fa907f720df4a3e915509e688e9e4cf1f8 (patch) | |
tree | f60ea63675e44033619680bffd8bca5a3e01638b /voip/jni | |
parent | f4ae94229d736c7dbd3c5c36d484213d51545702 (diff) | |
download | frameworks_base-f88fc1fa907f720df4a3e915509e688e9e4cf1f8.zip frameworks_base-f88fc1fa907f720df4a3e915509e688e9e4cf1f8.tar.gz frameworks_base-f88fc1fa907f720df4a3e915509e688e9e4cf1f8.tar.bz2 |
RTP: Enable AMR codec.
Change-Id: I49e6bdc1b67306b44173f2f346f8372a50264870
Diffstat (limited to 'voip/jni')
-rw-r--r-- | voip/jni/rtp/AmrCodec.cpp | 171 | ||||
-rw-r--r-- | voip/jni/rtp/AudioCodec.cpp | 2 |
2 files changed, 173 insertions, 0 deletions
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}, }; |