Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a77c9541 authored by Chia-chi Yeh's avatar Chia-chi Yeh Committed by Android Git Automerger
Browse files

am cbee6229: am 0a537b78: Merge "RTP: Enable AMR codec." into gingerbread

Merge commit 'cbee6229'

* commit 'cbee6229':
  RTP: Enable AMR codec.
parents d1614792 cbee6229
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -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;
+171 −0
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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},
};