Merge "RTP: Delay the initialization of AudioTrack and AudioRecord." into gingerbread
This commit is contained in:
@ -461,18 +461,15 @@ private:
|
|||||||
EC_ENABLED = 3,
|
EC_ENABLED = 3,
|
||||||
LAST_MODE = 3,
|
LAST_MODE = 3,
|
||||||
};
|
};
|
||||||
int mMode;
|
|
||||||
AudioStream *mChain;
|
AudioStream *mChain;
|
||||||
int mEventQueue;
|
int mEventQueue;
|
||||||
volatile int mDtmfEvent;
|
volatile int mDtmfEvent;
|
||||||
|
|
||||||
|
int mMode;
|
||||||
|
int mSampleRate;
|
||||||
int mSampleCount;
|
int mSampleCount;
|
||||||
int mDeviceSocket;
|
int mDeviceSocket;
|
||||||
AudioTrack mTrack;
|
|
||||||
AudioRecord mRecord;
|
|
||||||
|
|
||||||
bool networkLoop();
|
|
||||||
bool deviceLoop();
|
|
||||||
|
|
||||||
class NetworkThread : public Thread
|
class NetworkThread : public Thread
|
||||||
{
|
{
|
||||||
@ -490,10 +487,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AudioGroup *mGroup;
|
AudioGroup *mGroup;
|
||||||
bool threadLoop()
|
bool threadLoop();
|
||||||
{
|
|
||||||
return mGroup->networkLoop();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
sp<NetworkThread> mNetworkThread;
|
sp<NetworkThread> mNetworkThread;
|
||||||
|
|
||||||
@ -504,9 +498,6 @@ private:
|
|||||||
|
|
||||||
bool start()
|
bool start()
|
||||||
{
|
{
|
||||||
char c;
|
|
||||||
while (recv(mGroup->mDeviceSocket, &c, 1, MSG_DONTWAIT) == 1);
|
|
||||||
|
|
||||||
if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
|
if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
|
||||||
LOGE("cannot start device thread");
|
LOGE("cannot start device thread");
|
||||||
return false;
|
return false;
|
||||||
@ -516,10 +507,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AudioGroup *mGroup;
|
AudioGroup *mGroup;
|
||||||
bool threadLoop()
|
bool threadLoop();
|
||||||
{
|
|
||||||
return mGroup->deviceLoop();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
sp<DeviceThread> mDeviceThread;
|
sp<DeviceThread> mDeviceThread;
|
||||||
};
|
};
|
||||||
@ -539,8 +527,6 @@ AudioGroup::~AudioGroup()
|
|||||||
{
|
{
|
||||||
mNetworkThread->requestExitAndWait();
|
mNetworkThread->requestExitAndWait();
|
||||||
mDeviceThread->requestExitAndWait();
|
mDeviceThread->requestExitAndWait();
|
||||||
mTrack.stop();
|
|
||||||
mRecord.stop();
|
|
||||||
close(mEventQueue);
|
close(mEventQueue);
|
||||||
close(mDeviceSocket);
|
close(mDeviceSocket);
|
||||||
while (mChain) {
|
while (mChain) {
|
||||||
@ -559,40 +545,9 @@ bool AudioGroup::set(int sampleRate, int sampleCount)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSampleRate = sampleRate;
|
||||||
mSampleCount = sampleCount;
|
mSampleCount = sampleCount;
|
||||||
|
|
||||||
// Find out the frame count for AudioTrack and AudioRecord.
|
|
||||||
int output = 0;
|
|
||||||
int input = 0;
|
|
||||||
if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
|
|
||||||
sampleRate) != NO_ERROR || output <= 0 ||
|
|
||||||
AudioRecord::getMinFrameCount(&input, sampleRate,
|
|
||||||
AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
|
|
||||||
LOGE("cannot compute frame count");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOGD("reported frame count: output %d, input %d", output, input);
|
|
||||||
|
|
||||||
if (output < sampleCount * 2) {
|
|
||||||
output = sampleCount * 2;
|
|
||||||
}
|
|
||||||
if (input < sampleCount * 2) {
|
|
||||||
input = sampleCount * 2;
|
|
||||||
}
|
|
||||||
LOGD("adjusted frame count: output %d, input %d", output, input);
|
|
||||||
|
|
||||||
// Initialize AudioTrack and AudioRecord.
|
|
||||||
if (mTrack.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT,
|
|
||||||
AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR ||
|
|
||||||
mRecord.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT,
|
|
||||||
AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) {
|
|
||||||
LOGE("cannot initialize audio device");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
LOGD("latency: output %d, input %d", mTrack.latency(), mRecord.latency());
|
|
||||||
|
|
||||||
// TODO: initialize echo canceler here.
|
|
||||||
|
|
||||||
// Create device socket.
|
// Create device socket.
|
||||||
int pair[2];
|
int pair[2];
|
||||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
|
||||||
@ -610,13 +565,11 @@ bool AudioGroup::set(int sampleRate, int sampleCount)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give device socket a reasonable timeout and buffer size.
|
// Give device socket a reasonable timeout.
|
||||||
timeval tv;
|
timeval tv;
|
||||||
tv.tv_sec = 0;
|
tv.tv_sec = 0;
|
||||||
tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
|
tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
|
||||||
if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) ||
|
if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
|
||||||
setsockopt(pair[0], SOL_SOCKET, SO_RCVBUF, &output, sizeof(output)) ||
|
|
||||||
setsockopt(pair[1], SOL_SOCKET, SO_SNDBUF, &output, sizeof(output))) {
|
|
||||||
LOGE("setsockopt: %s", strerror(errno));
|
LOGE("setsockopt: %s", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -644,29 +597,10 @@ bool AudioGroup::setMode(int mode)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mDeviceThread->requestExitAndWait();
|
||||||
LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
|
LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
|
||||||
mMode = mode;
|
mMode = mode;
|
||||||
|
return (mode == ON_HOLD) || mDeviceThread->start();
|
||||||
mDeviceThread->requestExitAndWait();
|
|
||||||
if (mode == ON_HOLD) {
|
|
||||||
mTrack.stop();
|
|
||||||
mRecord.stop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mTrack.start();
|
|
||||||
if (mode == MUTED) {
|
|
||||||
mRecord.stop();
|
|
||||||
} else {
|
|
||||||
mRecord.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mDeviceThread->start()) {
|
|
||||||
mTrack.stop();
|
|
||||||
mRecord.stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroup::sendDtmf(int event)
|
bool AudioGroup::sendDtmf(int event)
|
||||||
@ -741,15 +675,16 @@ bool AudioGroup::remove(int socket)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroup::networkLoop()
|
bool AudioGroup::NetworkThread::threadLoop()
|
||||||
{
|
{
|
||||||
|
AudioStream *chain = mGroup->mChain;
|
||||||
int tick = elapsedRealtime();
|
int tick = elapsedRealtime();
|
||||||
int deadline = tick + 10;
|
int deadline = tick + 10;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
for (AudioStream *stream = mChain; stream; stream = stream->mNext) {
|
for (AudioStream *stream = chain; stream; stream = stream->mNext) {
|
||||||
if (!stream->mTick || tick - stream->mTick >= 0) {
|
if (!stream->mTick || tick - stream->mTick >= 0) {
|
||||||
stream->encode(tick, mChain);
|
stream->encode(tick, chain);
|
||||||
}
|
}
|
||||||
if (deadline - stream->mTick > 0) {
|
if (deadline - stream->mTick > 0) {
|
||||||
deadline = stream->mTick;
|
deadline = stream->mTick;
|
||||||
@ -757,12 +692,12 @@ bool AudioGroup::networkLoop()
|
|||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDtmfEvent != -1) {
|
int event = mGroup->mDtmfEvent;
|
||||||
int event = mDtmfEvent;
|
if (event != -1) {
|
||||||
for (AudioStream *stream = mChain; stream; stream = stream->mNext) {
|
for (AudioStream *stream = chain; stream; stream = stream->mNext) {
|
||||||
stream->sendDtmf(event);
|
stream->sendDtmf(event);
|
||||||
}
|
}
|
||||||
mDtmfEvent = -1;
|
mGroup->mDtmfEvent = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
deadline -= tick;
|
deadline -= tick;
|
||||||
@ -771,7 +706,7 @@ bool AudioGroup::networkLoop()
|
|||||||
}
|
}
|
||||||
|
|
||||||
epoll_event events[count];
|
epoll_event events[count];
|
||||||
count = epoll_wait(mEventQueue, events, count, deadline);
|
count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
|
||||||
if (count == -1) {
|
if (count == -1) {
|
||||||
LOGE("epoll_wait: %s", strerror(errno));
|
LOGE("epoll_wait: %s", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
@ -783,70 +718,125 @@ bool AudioGroup::networkLoop()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioGroup::deviceLoop()
|
bool AudioGroup::DeviceThread::threadLoop()
|
||||||
{
|
{
|
||||||
int16_t output[mSampleCount];
|
int mode = mGroup->mMode;
|
||||||
|
int sampleRate = mGroup->mSampleRate;
|
||||||
|
int sampleCount = mGroup->mSampleCount;
|
||||||
|
int deviceSocket = mGroup->mDeviceSocket;
|
||||||
|
|
||||||
if (recv(mDeviceSocket, output, sizeof(output), 0) <= 0) {
|
// Find out the frame count for AudioTrack and AudioRecord.
|
||||||
memset(output, 0, sizeof(output));
|
int output = 0;
|
||||||
}
|
int input = 0;
|
||||||
|
if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
|
||||||
int16_t input[mSampleCount];
|
sampleRate) != NO_ERROR || output <= 0 ||
|
||||||
int toWrite = mSampleCount;
|
AudioRecord::getMinFrameCount(&input, sampleRate,
|
||||||
int toRead = (mMode == MUTED) ? 0 : mSampleCount;
|
AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
|
||||||
int chances = 100;
|
LOGE("cannot compute frame count");
|
||||||
|
|
||||||
while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
|
|
||||||
if (toWrite > 0) {
|
|
||||||
AudioTrack::Buffer buffer;
|
|
||||||
buffer.frameCount = toWrite;
|
|
||||||
|
|
||||||
status_t status = mTrack.obtainBuffer(&buffer, 1);
|
|
||||||
if (status == NO_ERROR) {
|
|
||||||
memcpy(buffer.i8, &output[mSampleCount - toWrite], buffer.size);
|
|
||||||
toWrite -= buffer.frameCount;
|
|
||||||
mTrack.releaseBuffer(&buffer);
|
|
||||||
} else if (status != TIMED_OUT && status != WOULD_BLOCK) {
|
|
||||||
LOGE("cannot write to AudioTrack");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toRead > 0) {
|
|
||||||
AudioRecord::Buffer buffer;
|
|
||||||
buffer.frameCount = mRecord.frameCount();
|
|
||||||
|
|
||||||
status_t status = mRecord.obtainBuffer(&buffer, 1);
|
|
||||||
if (status == NO_ERROR) {
|
|
||||||
int count = ((int)buffer.frameCount < toRead) ?
|
|
||||||
buffer.frameCount : toRead;
|
|
||||||
memcpy(&input[mSampleCount - toRead], buffer.i8, count * 2);
|
|
||||||
toRead -= count;
|
|
||||||
if (buffer.frameCount < mRecord.frameCount()) {
|
|
||||||
buffer.frameCount = count;
|
|
||||||
}
|
|
||||||
mRecord.releaseBuffer(&buffer);
|
|
||||||
} else if (status != TIMED_OUT && status != WOULD_BLOCK) {
|
|
||||||
LOGE("cannot read from AudioRecord");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chances) {
|
|
||||||
LOGE("device loop timeout");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
LOGD("reported frame count: output %d, input %d", output, input);
|
||||||
|
|
||||||
if (mMode != MUTED) {
|
if (output < sampleCount * 2) {
|
||||||
if (mMode == NORMAL) {
|
output = sampleCount * 2;
|
||||||
send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
}
|
||||||
} else {
|
if (input < sampleCount * 2) {
|
||||||
// TODO: Echo canceller runs here.
|
input = sampleCount * 2;
|
||||||
send(mDeviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
}
|
||||||
|
LOGD("adjusted frame count: output %d, input %d", output, input);
|
||||||
|
|
||||||
|
// Initialize AudioTrack and AudioRecord.
|
||||||
|
AudioTrack track;
|
||||||
|
AudioRecord record;
|
||||||
|
if (track.set(AudioSystem::VOICE_CALL, sampleRate, AudioSystem::PCM_16_BIT,
|
||||||
|
AudioSystem::CHANNEL_OUT_MONO, output) != NO_ERROR ||
|
||||||
|
record.set(AUDIO_SOURCE_MIC, sampleRate, AudioSystem::PCM_16_BIT,
|
||||||
|
AudioSystem::CHANNEL_IN_MONO, input) != NO_ERROR) {
|
||||||
|
LOGE("cannot initialize audio device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOGD("latency: output %d, input %d", track.latency(), record.latency());
|
||||||
|
|
||||||
|
// TODO: initialize echo canceler here.
|
||||||
|
|
||||||
|
// Give device socket a reasonable buffer size.
|
||||||
|
setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
|
||||||
|
setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
|
||||||
|
|
||||||
|
// Drain device socket.
|
||||||
|
char c;
|
||||||
|
while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
|
||||||
|
|
||||||
|
// Start your engine!
|
||||||
|
track.start();
|
||||||
|
if (mode != MUTED) {
|
||||||
|
record.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!exitPending()) {
|
||||||
|
int16_t output[sampleCount];
|
||||||
|
if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
|
||||||
|
memset(output, 0, sizeof(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t input[sampleCount];
|
||||||
|
int toWrite = sampleCount;
|
||||||
|
int toRead = (mode == MUTED) ? 0 : sampleCount;
|
||||||
|
int chances = 100;
|
||||||
|
|
||||||
|
while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
|
||||||
|
if (toWrite > 0) {
|
||||||
|
AudioTrack::Buffer buffer;
|
||||||
|
buffer.frameCount = toWrite;
|
||||||
|
|
||||||
|
status_t status = track.obtainBuffer(&buffer, 1);
|
||||||
|
if (status == NO_ERROR) {
|
||||||
|
int offset = sampleCount - toWrite;
|
||||||
|
memcpy(buffer.i8, &output[offset], buffer.size);
|
||||||
|
toWrite -= buffer.frameCount;
|
||||||
|
track.releaseBuffer(&buffer);
|
||||||
|
} else if (status != TIMED_OUT && status != WOULD_BLOCK) {
|
||||||
|
LOGE("cannot write to AudioTrack");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toRead > 0) {
|
||||||
|
AudioRecord::Buffer buffer;
|
||||||
|
buffer.frameCount = record.frameCount();
|
||||||
|
|
||||||
|
status_t status = record.obtainBuffer(&buffer, 1);
|
||||||
|
if (status == NO_ERROR) {
|
||||||
|
int count = ((int)buffer.frameCount < toRead) ?
|
||||||
|
buffer.frameCount : toRead;
|
||||||
|
memcpy(&input[sampleCount - toRead], buffer.i8, count * 2);
|
||||||
|
toRead -= count;
|
||||||
|
if (buffer.frameCount < record.frameCount()) {
|
||||||
|
buffer.frameCount = count;
|
||||||
|
}
|
||||||
|
record.releaseBuffer(&buffer);
|
||||||
|
} else if (status != TIMED_OUT && status != WOULD_BLOCK) {
|
||||||
|
LOGE("cannot read from AudioRecord");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chances <= 0) {
|
||||||
|
LOGE("device loop timeout");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode != MUTED) {
|
||||||
|
if (mode == NORMAL) {
|
||||||
|
send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
||||||
|
} else {
|
||||||
|
// TODO: Echo canceller runs here.
|
||||||
|
send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user