M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
+268
View File
@@ -0,0 +1,268 @@
/*
* 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;
}
+58
View File
@@ -0,0 +1,58 @@
#
# 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 \
EchoSuppressor.cpp \
RtpStream.cpp \
util.cpp \
rtp_jni.cpp
LOCAL_SRC_FILES += \
AmrCodec.cpp \
G711Codec.cpp \
GsmCodec.cpp
LOCAL_SHARED_LIBRARIES := \
libnativehelper \
libcutils \
libutils \
libmedia \
libstagefright
LOCAL_STATIC_LIBRARIES := libgsm
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
external/libgsm/inc \
frameworks/base/media/libstagefright/codecs/amrnb/common/include \
frameworks/base/media/libstagefright/codecs/amrnb/common/ \
frameworks/base/media/libstagefright/codecs/amrnb/enc/include \
frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
frameworks/base/media/libstagefright/codecs/amrnb/dec/include \
frameworks/base/media/libstagefright/codecs/amrnb/dec/src
LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
+51
View File
@@ -0,0 +1,51 @@
/*
* 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"
extern AudioCodec *newAlawCodec();
extern AudioCodec *newUlawCodec();
extern AudioCodec *newGsmCodec();
extern AudioCodec *newAmrCodec();
extern AudioCodec *newGsmEfrCodec();
struct AudioCodecType {
const char *name;
AudioCodec *(*create)();
} gAudioCodecTypes[] = {
{"PCMA", newAlawCodec},
{"PCMU", newUlawCodec},
{"GSM", newGsmCodec},
{"AMR", newAmrCodec},
{"GSM-EFR", newGsmEfrCodec},
{NULL, NULL},
};
AudioCodec *newAudioCodec(const char *codecName)
{
AudioCodecType *type = gAudioCodecTypes;
while (type->name != NULL) {
if (strcasecmp(codecName, type->name) == 0) {
AudioCodec *codec = type->create();
codec->name = type->name;
return codec;
}
++type;
}
return NULL;
}
+38
View File
@@ -0,0 +1,38 @@
/*
* 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:
const char *name;
// Needed by destruction through base class pointers.
virtual ~AudioCodec() {}
// Returns sampleCount or non-positive value if unsupported.
virtual int set(int sampleRate, const char *fmtp) = 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
File diff suppressed because it is too large Load Diff
+195
View File
@@ -0,0 +1,195 @@
/*
* 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 <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#define LOG_TAG "Echo"
#include <utils/Log.h>
#include "EchoSuppressor.h"
// It is very difficult to do echo cancellation at this level due to the lack of
// the timing information of the samples being played and recorded. Therefore,
// for the first release only echo suppression is implemented.
// The algorithm is derived from the "previous works" summarized in
// A new class of doubletalk detectors based on cross-correlation,
// J Benesty, DR Morgan, JH Cho, IEEE Trans. on Speech and Audio Processing.
// The method proposed in that paper is not used because of its high complexity.
// It is well known that cross-correlation can be computed using convolution,
// but unfortunately not every mobile processor has a (fast enough) FPU. Thus
// we use integer arithmetic as much as possible and do lots of bookkeeping.
// Again, parameters and thresholds are chosen by experiments.
EchoSuppressor::EchoSuppressor(int sampleCount, int tailLength)
{
tailLength += sampleCount * 4;
int shift = 0;
while ((sampleCount >> shift) > 1 && (tailLength >> shift) > 256) {
++shift;
}
mShift = shift + 4;
mScale = 1 << shift;
mSampleCount = sampleCount;
mWindowSize = sampleCount >> shift;
mTailLength = tailLength >> shift;
mRecordLength = tailLength * 2 / sampleCount;
mRecordOffset = 0;
mXs = new uint16_t[mTailLength + mWindowSize];
memset(mXs, 0, sizeof(*mXs) * (mTailLength + mWindowSize));
mXSums = new uint32_t[mTailLength];
memset(mXSums, 0, sizeof(*mXSums) * mTailLength);
mX2Sums = new uint32_t[mTailLength];
memset(mX2Sums, 0, sizeof(*mX2Sums) * mTailLength);
mXRecords = new uint16_t[mRecordLength * mWindowSize];
memset(mXRecords, 0, sizeof(*mXRecords) * mRecordLength * mWindowSize);
mYSum = 0;
mY2Sum = 0;
mYRecords = new uint32_t[mRecordLength];
memset(mYRecords, 0, sizeof(*mYRecords) * mRecordLength);
mY2Records = new uint32_t[mRecordLength];
memset(mY2Records, 0, sizeof(*mY2Records) * mRecordLength);
mXYSums = new uint32_t[mTailLength];
memset(mXYSums, 0, sizeof(*mXYSums) * mTailLength);
mXYRecords = new uint32_t[mRecordLength * mTailLength];
memset(mXYRecords, 0, sizeof(*mXYRecords) * mRecordLength * mTailLength);
mLastX = 0;
mLastY = 0;
mWeight = 1.0f / (mRecordLength * mWindowSize);
}
EchoSuppressor::~EchoSuppressor()
{
delete [] mXs;
delete [] mXSums;
delete [] mX2Sums;
delete [] mXRecords;
delete [] mYRecords;
delete [] mY2Records;
delete [] mXYSums;
delete [] mXYRecords;
}
void EchoSuppressor::run(int16_t *playbacked, int16_t *recorded)
{
// Update Xs.
for (int i = mTailLength - 1; i >= 0; --i) {
mXs[i + mWindowSize] = mXs[i];
}
for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
uint32_t sum = 0;
for (int k = 0; k < mScale; ++k) {
int32_t x = playbacked[j + k] << 15;
mLastX += x;
sum += ((mLastX >= 0) ? mLastX : -mLastX) >> 15;
mLastX -= (mLastX >> 10) + x;
}
mXs[i] = sum >> mShift;
}
// Update XSums, X2Sums, and XRecords.
for (int i = mTailLength - mWindowSize - 1; i >= 0; --i) {
mXSums[i + mWindowSize] = mXSums[i];
mX2Sums[i + mWindowSize] = mX2Sums[i];
}
uint16_t *xRecords = &mXRecords[mRecordOffset * mWindowSize];
for (int i = mWindowSize - 1; i >= 0; --i) {
uint16_t x = mXs[i];
mXSums[i] = mXSums[i + 1] + x - xRecords[i];
mX2Sums[i] = mX2Sums[i + 1] + x * x - xRecords[i] * xRecords[i];
xRecords[i] = x;
}
// Compute Ys.
uint16_t ys[mWindowSize];
for (int i = mWindowSize - 1, j = 0; i >= 0; --i, j += mScale) {
uint32_t sum = 0;
for (int k = 0; k < mScale; ++k) {
int32_t y = recorded[j + k] << 15;
mLastY += y;
sum += ((mLastY >= 0) ? mLastY : -mLastY) >> 15;
mLastY -= (mLastY >> 10) + y;
}
ys[i] = sum >> mShift;
}
// Update YSum, Y2Sum, YRecords, and Y2Records.
uint32_t ySum = 0;
uint32_t y2Sum = 0;
for (int i = mWindowSize - 1; i >= 0; --i) {
ySum += ys[i];
y2Sum += ys[i] * ys[i];
}
mYSum += ySum - mYRecords[mRecordOffset];
mY2Sum += y2Sum - mY2Records[mRecordOffset];
mYRecords[mRecordOffset] = ySum;
mY2Records[mRecordOffset] = y2Sum;
// Update XYSums and XYRecords.
uint32_t *xyRecords = &mXYRecords[mRecordOffset * mTailLength];
for (int i = mTailLength - 1; i >= 0; --i) {
uint32_t xySum = 0;
for (int j = mWindowSize - 1; j >= 0; --j) {
xySum += mXs[i + j] * ys[j];
}
mXYSums[i] += xySum - xyRecords[i];
xyRecords[i] = xySum;
}
// Compute correlations.
int latency = 0;
float corr2 = 0.0f;
float varX = 0.0f;
float varY = mY2Sum - mWeight * mYSum * mYSum;
for (int i = mTailLength - 1; i >= 0; --i) {
float cov = mXYSums[i] - mWeight * mXSums[i] * mYSum;
if (cov > 0.0f) {
float varXi = mX2Sums[i] - mWeight * mXSums[i] * mXSums[i];
float corr2i = cov * cov / (varXi * varY + 1);
if (corr2i > corr2) {
varX = varXi;
corr2 = corr2i;
latency = i;
}
}
}
//LOGI("corr^2 %.5f, var %8.0f %8.0f, latency %d", corr2, varX, varY,
// latency * mScale);
// Do echo suppression.
if (corr2 > 0.1f && varX > 10000.0f) {
int factor = (corr2 > 1.0f) ? 0 : (1.0f - sqrtf(corr2)) * 4096;
for (int i = 0; i < mSampleCount; ++i) {
recorded[i] = recorded[i] * factor >> 16;
}
}
// Increase RecordOffset.
++mRecordOffset;
if (mRecordOffset == mRecordLength) {
mRecordOffset = 0;
}
}
+58
View File
@@ -0,0 +1,58 @@
/*
* 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 __ECHO_SUPPRESSOR_H__
#define __ECHO_SUPPRESSOR_H__
#include <stdint.h>
class EchoSuppressor
{
public:
// The sampleCount must be power of 2.
EchoSuppressor(int sampleCount, int tailLength);
~EchoSuppressor();
void run(int16_t *playbacked, int16_t *recorded);
private:
int mShift;
int mScale;
int mSampleCount;
int mWindowSize;
int mTailLength;
int mRecordLength;
int mRecordOffset;
uint16_t *mXs;
uint32_t *mXSums;
uint32_t *mX2Sums;
uint16_t *mXRecords;
uint32_t mYSum;
uint32_t mY2Sum;
uint32_t *mYRecords;
uint32_t *mY2Records;
uint32_t *mXYSums;
uint32_t *mXYRecords;
int32_t mLastX;
int32_t mLastY;
float mWeight;
};
#endif
+138
View File
@@ -0,0 +1,138 @@
/*
* 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 "AudioCodec.h"
namespace {
const 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:
int set(int sampleRate, const char *fmtp) {
mSampleCount = sampleRate / 50;
return mSampleCount;
}
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;
}
//------------------------------------------------------------------------------
class AlawCodec : public AudioCodec
{
public:
int set(int sampleRate, const char *fmtp) {
mSampleCount = sampleRate / 50;
return mSampleCount;
}
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;
}
} // namespace
AudioCodec *newUlawCodec()
{
return new UlawCodec;
}
AudioCodec *newAlawCodec()
{
return new AlawCodec;
}
+74
View File
@@ -0,0 +1,74 @@
/*
* 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 "AudioCodec.h"
extern "C" {
#include "gsm.h"
}
namespace {
class GsmCodec : public AudioCodec
{
public:
GsmCodec() {
mEncode = gsm_create();
mDecode = gsm_create();
}
~GsmCodec() {
if (mEncode) {
gsm_destroy(mEncode);
}
if (mDecode) {
gsm_destroy(mDecode);
}
}
int set(int sampleRate, const char *fmtp) {
return (sampleRate == 8000 && mEncode && mDecode) ? 160 : -1;
}
int encode(void *payload, int16_t *samples);
int decode(int16_t *samples, void *payload, int length);
private:
gsm mEncode;
gsm mDecode;
};
int GsmCodec::encode(void *payload, int16_t *samples)
{
gsm_encode(mEncode, samples, (unsigned char *)payload);
return 33;
}
int GsmCodec::decode(int16_t *samples, void *payload, int length)
{
if (length == 33 &&
gsm_decode(mDecode, (unsigned char *)payload, samples) == 0) {
return 160;
}
return -1;
}
} // namespace
AudioCodec *newGsmCodec()
{
return new GsmCodec;
}
+123
View File
@@ -0,0 +1,123 @@
/*
* 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 socket = ::dup(env->GetIntField(thiz, gNative));
if (socket == -1) {
jniThrowException(env, "java/lang/IllegalStateException", strerror(errno));
}
return socket;
}
void close(JNIEnv *env, jobject thiz)
{
int socket = env->GetIntField(thiz, gNative);
::close(socket);
env->SetIntField(thiz, gNative, -1);
}
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;
}
+32
View File
@@ -0,0 +1,32 @@
/*
* 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;
}
+61
View File
@@ -0,0 +1,61 @@
/*
* 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;
}