269 lines
6.7 KiB
C++
269 lines
6.7 KiB
C++
|
/*
|
||
|
* 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"
|
||
|
|
||
|
#include "gsmamr_dec.h"
|
||
|
#include "gsmamr_enc.h"
|
||
|
|
||
|
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 = (char*)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:
|
||
|
GsmEfrCodec() {
|
||
|
if (AMREncodeInit(&mEncoder, &mSidSync, false)) {
|
||
|
mEncoder = NULL;
|
||
|
}
|
||
|
if (GSMInitDecode(&mDecoder, (Word8 *)"RTP")) {
|
||
|
mDecoder = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
~GsmEfrCodec() {
|
||
|
if (mEncoder) {
|
||
|
AMREncodeExit(&mEncoder, &mSidSync);
|
||
|
}
|
||
|
if (mDecoder) {
|
||
|
GSMDecodeFrameExit(&mDecoder);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int set(int sampleRate, const char *fmtp) {
|
||
|
return (sampleRate == 8000 && mEncoder && mDecoder) ? 160 : -1;
|
||
|
}
|
||
|
|
||
|
int encode(void *payload, int16_t *samples);
|
||
|
int decode(int16_t *samples, void *payload, int length);
|
||
|
|
||
|
private:
|
||
|
void *mEncoder;
|
||
|
void *mSidSync;
|
||
|
void *mDecoder;
|
||
|
};
|
||
|
|
||
|
int GsmEfrCodec::encode(void *payload, int16_t *samples)
|
||
|
{
|
||
|
unsigned char *bytes = (unsigned char *)payload;
|
||
|
Frame_Type_3GPP type;
|
||
|
|
||
|
int length = AMREncode(mEncoder, mSidSync, MR122,
|
||
|
samples, bytes, &type, AMR_TX_WMF);
|
||
|
|
||
|
if (type == AMR_122 && length == 32) {
|
||
|
bytes[0] = 0xC0 | (bytes[1] >> 4);
|
||
|
for (int i = 1; i < 31; ++i) {
|
||
|
bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
|
||
|
}
|
||
|
return 31;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int GsmEfrCodec::decode(int16_t *samples, void *payload, int length)
|
||
|
{
|
||
|
unsigned char *bytes = (unsigned char *)payload;
|
||
|
if (length == 31 && (bytes[0] >> 4) == 0x0C) {
|
||
|
for (int i = 0; i < 30; ++i) {
|
||
|
bytes[i] = (bytes[i] << 4) | (bytes[i + 1] >> 4);
|
||
|
}
|
||
|
bytes[30] <<= 4;
|
||
|
|
||
|
if (AMRDecode(mDecoder, AMR_122, bytes, samples, MIME_IETF) == 31) {
|
||
|
return 160;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
AudioCodec *newAmrCodec()
|
||
|
{
|
||
|
return new AmrCodec;
|
||
|
}
|
||
|
|
||
|
AudioCodec *newGsmEfrCodec()
|
||
|
{
|
||
|
return new GsmEfrCodec;
|
||
|
}
|