blob: 270b4949b39cbceeed73e07e2a9e49faadcdc919 [file] [log] [blame]
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdint.h>
19#include <string.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <sys/epoll.h>
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <sys/stat.h>
26#include <sys/time.h>
27#include <time.h>
28#include <arpa/inet.h>
29#include <netinet/in.h>
30
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -070031// #define LOG_NDEBUG 0
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080032#define LOG_TAG "AudioGroup"
33#include <cutils/atomic.h>
Eric Laurentd7a724e2011-03-29 18:22:57 -070034#include <cutils/properties.h>
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080035#include <utils/Log.h>
36#include <utils/Errors.h>
37#include <utils/RefBase.h>
38#include <utils/threads.h>
39#include <utils/SystemClock.h>
40#include <media/AudioSystem.h>
41#include <media/AudioRecord.h>
42#include <media/AudioTrack.h>
43#include <media/mediarecorder.h>
Eric Laurent5fb3ba62011-07-25 12:02:16 -070044#include <media/AudioEffect.h>
45#include <audio_effects/effect_aec.h>
Dima Zavin34bb4192011-05-11 14:15:23 -070046#include <system/audio.h>
Dima Zavin24fc2fb2011-04-19 22:30:36 -070047
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080048#include "jni.h"
49#include "JNIHelp.h"
50
51#include "AudioCodec.h"
Chia-chi Yeha8a10092010-10-05 01:17:13 +080052#include "EchoSuppressor.h"
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080053
54extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
55
56namespace {
57
58using namespace android;
59
60int gRandom = -1;
61
62// We use a circular array to implement jitter buffer. The simplest way is doing
63// a modulo operation on the index while accessing the array. However modulo can
64// be expensive on some platforms, such as ARM. Thus we round up the size of the
65// array to the nearest power of 2 and then use bitwise-and instead of modulo.
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -070066// Currently we make it 2048ms long and assume packet interval is 50ms or less.
67// The first 100ms is the place where samples get mixed. The rest is the real
68// jitter buffer. For a stream at 8000Hz it takes 32 kilobytes. These numbers
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080069// are chosen by experiments and each of them can be adjusted as needed.
70
Chia-chi Yeh3cf71372011-01-04 19:10:06 +080071// Originally a stream does not send packets when it is receive-only or there is
72// nothing to mix. However, this causes some problems with certain firewalls and
73// proxies. A firewall might remove a port mapping when there is no outgoing
74// packet for a preiod of time, and a proxy might wait for incoming packets from
75// both sides before start forwarding. To solve these problems, we send out a
76// silence packet on the stream for every second. It should be good enough to
77// keep the stream alive with relatively low resources.
78
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080079// Other notes:
80// + We use elapsedRealtime() to get the time. Since we use 32bit variables
81// instead of 64bit ones, comparison must be done by subtraction.
82// + Sampling rate must be multiple of 1000Hz, and packet length must be in
83// milliseconds. No floating points.
84// + If we cannot get enough CPU, we drop samples and simulate packet loss.
85// + Resampling is not done yet, so streams in one group must use the same rate.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +080086// For the first release only 8000Hz is supported.
87
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -070088#define BUFFER_SIZE 2048
89#define HISTORY_SIZE 100
90#define MEASURE_BASE 100
91#define MEASURE_PERIOD 5000
92#define DTMF_PERIOD 200
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080093
94class AudioStream
95{
96public:
97 AudioStream();
98 ~AudioStream();
99 bool set(int mode, int socket, sockaddr_storage *remote,
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800100 AudioCodec *codec, int sampleRate, int sampleCount,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800101 int codecType, int dtmfType);
102
103 void sendDtmf(int event);
104 bool mix(int32_t *output, int head, int tail, int sampleRate);
105 void encode(int tick, AudioStream *chain);
106 void decode(int tick);
107
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800108private:
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800109 enum {
110 NORMAL = 0,
111 SEND_ONLY = 1,
112 RECEIVE_ONLY = 2,
113 LAST_MODE = 2,
114 };
115
116 int mMode;
117 int mSocket;
118 sockaddr_storage mRemote;
119 AudioCodec *mCodec;
120 uint32_t mCodecMagic;
121 uint32_t mDtmfMagic;
Chia-chi Yehfe529892010-09-30 02:42:27 +0800122 bool mFixRemote;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800123
124 int mTick;
125 int mSampleRate;
126 int mSampleCount;
127 int mInterval;
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800128 int mKeepAlive;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800129
130 int16_t *mBuffer;
131 int mBufferMask;
132 int mBufferHead;
133 int mBufferTail;
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800134 int mLatencyTimer;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800135 int mLatencyScore;
136
137 uint16_t mSequence;
138 uint32_t mTimestamp;
139 uint32_t mSsrc;
140
141 int mDtmfEvent;
142 int mDtmfStart;
143
144 AudioStream *mNext;
145
146 friend class AudioGroup;
147};
148
149AudioStream::AudioStream()
150{
151 mSocket = -1;
152 mCodec = NULL;
153 mBuffer = NULL;
154 mNext = NULL;
155}
156
157AudioStream::~AudioStream()
158{
159 close(mSocket);
160 delete mCodec;
161 delete [] mBuffer;
Steve Block5baa3a62011-12-20 16:23:08 +0000162 ALOGD("stream[%d] is dead", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800163}
164
165bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800166 AudioCodec *codec, int sampleRate, int sampleCount,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800167 int codecType, int dtmfType)
168{
169 if (mode < 0 || mode > LAST_MODE) {
170 return false;
171 }
172 mMode = mode;
173
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800174 mCodecMagic = (0x8000 | codecType) << 16;
175 mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16;
176
177 mTick = elapsedRealtime();
178 mSampleRate = sampleRate / 1000;
179 mSampleCount = sampleCount;
180 mInterval = mSampleCount / mSampleRate;
181
182 // Allocate jitter buffer.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800183 for (mBufferMask = 8; mBufferMask < mSampleRate; mBufferMask <<= 1);
184 mBufferMask *= BUFFER_SIZE;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800185 mBuffer = new int16_t[mBufferMask];
186 --mBufferMask;
187 mBufferHead = 0;
188 mBufferTail = 0;
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800189 mLatencyTimer = 0;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800190 mLatencyScore = 0;
191
192 // Initialize random bits.
193 read(gRandom, &mSequence, sizeof(mSequence));
194 read(gRandom, &mTimestamp, sizeof(mTimestamp));
195 read(gRandom, &mSsrc, sizeof(mSsrc));
196
197 mDtmfEvent = -1;
198 mDtmfStart = 0;
199
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800200 // Only take over these things when succeeded.
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800201 mSocket = socket;
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800202 if (codec) {
203 mRemote = *remote;
204 mCodec = codec;
Chia-chi Yehfe529892010-09-30 02:42:27 +0800205
206 // Here we should never get an private address, but some buggy proxy
207 // servers do give us one. To solve this, we replace the address when
208 // the first time we successfully decode an incoming packet.
209 mFixRemote = false;
210 if (remote->ss_family == AF_INET) {
211 unsigned char *address =
212 (unsigned char *)&((sockaddr_in *)remote)->sin_addr;
213 if (address[0] == 10 ||
214 (address[0] == 172 && (address[1] >> 4) == 1) ||
215 (address[0] == 192 && address[1] == 168)) {
216 mFixRemote = true;
217 }
218 }
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800219 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800220
Steve Block5baa3a62011-12-20 16:23:08 +0000221 ALOGD("stream[%d] is configured as %s %dkHz %dms mode %d", mSocket,
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800222 (codec ? codec->name : "RAW"), mSampleRate, mInterval, mMode);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800223 return true;
224}
225
226void AudioStream::sendDtmf(int event)
227{
228 if (mDtmfMagic != 0) {
229 mDtmfEvent = event << 24;
230 mDtmfStart = mTimestamp + mSampleCount;
231 }
232}
233
234bool AudioStream::mix(int32_t *output, int head, int tail, int sampleRate)
235{
236 if (mMode == SEND_ONLY) {
237 return false;
238 }
239
240 if (head - mBufferHead < 0) {
241 head = mBufferHead;
242 }
243 if (tail - mBufferTail > 0) {
244 tail = mBufferTail;
245 }
246 if (tail - head <= 0) {
247 return false;
248 }
249
250 head *= mSampleRate;
251 tail *= mSampleRate;
252
253 if (sampleRate == mSampleRate) {
254 for (int i = head; i - tail < 0; ++i) {
255 output[i - head] += mBuffer[i & mBufferMask];
256 }
257 } else {
258 // TODO: implement resampling.
259 return false;
260 }
261 return true;
262}
263
264void AudioStream::encode(int tick, AudioStream *chain)
265{
266 if (tick - mTick >= mInterval) {
267 // We just missed the train. Pretend that packets in between are lost.
268 int skipped = (tick - mTick) / mInterval;
269 mTick += skipped * mInterval;
270 mSequence += skipped;
271 mTimestamp += skipped * mSampleCount;
Steve Block71f2cf12011-10-20 11:56:00 +0100272 ALOGV("stream[%d] skips %d packets", mSocket, skipped);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800273 }
274
275 tick = mTick;
276 mTick += mInterval;
277 ++mSequence;
278 mTimestamp += mSampleCount;
279
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800280 // If there is an ongoing DTMF event, send it now.
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800281 if (mMode != RECEIVE_ONLY && mDtmfEvent != -1) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800282 int duration = mTimestamp - mDtmfStart;
283 // Make sure duration is reasonable.
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700284 if (duration >= 0 && duration < mSampleRate * DTMF_PERIOD) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800285 duration += mSampleCount;
286 int32_t buffer[4] = {
287 htonl(mDtmfMagic | mSequence),
288 htonl(mDtmfStart),
289 mSsrc,
290 htonl(mDtmfEvent | duration),
291 };
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700292 if (duration >= mSampleRate * DTMF_PERIOD) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800293 buffer[3] |= htonl(1 << 23);
294 mDtmfEvent = -1;
295 }
296 sendto(mSocket, buffer, sizeof(buffer), MSG_DONTWAIT,
297 (sockaddr *)&mRemote, sizeof(mRemote));
298 return;
299 }
300 mDtmfEvent = -1;
301 }
302
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800303 int32_t buffer[mSampleCount + 3];
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700304 bool data = false;
305 if (mMode != RECEIVE_ONLY) {
306 // Mix all other streams.
307 memset(buffer, 0, sizeof(buffer));
308 while (chain) {
309 if (chain != this) {
310 data |= chain->mix(buffer, tick - mInterval, tick, mSampleRate);
311 }
312 chain = chain->mNext;
313 }
314 }
315
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800316 int16_t samples[mSampleCount];
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700317 if (data) {
318 // Saturate into 16 bits.
319 for (int i = 0; i < mSampleCount; ++i) {
320 int32_t sample = buffer[i];
321 if (sample < -32768) {
322 sample = -32768;
323 }
324 if (sample > 32767) {
325 sample = 32767;
326 }
327 samples[i] = sample;
328 }
329 } else {
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800330 if ((mTick ^ mKeepAlive) >> 10 == 0) {
331 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800332 }
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800333 mKeepAlive = mTick;
334 memset(samples, 0, sizeof(samples));
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800335
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700336 if (mMode != RECEIVE_ONLY) {
Steve Block71f2cf12011-10-20 11:56:00 +0100337 ALOGV("stream[%d] no data", mSocket);
repo sync7a69aef2010-09-23 05:46:01 +0800338 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800339 }
340
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800341 if (!mCodec) {
342 // Special case for device stream.
343 send(mSocket, samples, sizeof(samples), MSG_DONTWAIT);
344 return;
345 }
346
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800347 // Cook the packet and send it out.
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800348 buffer[0] = htonl(mCodecMagic | mSequence);
349 buffer[1] = htonl(mTimestamp);
350 buffer[2] = mSsrc;
351 int length = mCodec->encode(&buffer[3], samples);
352 if (length <= 0) {
Steve Block71f2cf12011-10-20 11:56:00 +0100353 ALOGV("stream[%d] encoder error", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800354 return;
355 }
356 sendto(mSocket, buffer, length + 12, MSG_DONTWAIT, (sockaddr *)&mRemote,
357 sizeof(mRemote));
358}
359
360void AudioStream::decode(int tick)
361{
362 char c;
363 if (mMode == SEND_ONLY) {
364 recv(mSocket, &c, 1, MSG_DONTWAIT);
365 return;
366 }
367
368 // Make sure mBufferHead and mBufferTail are reasonable.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800369 if ((unsigned int)(tick + BUFFER_SIZE - mBufferHead) > BUFFER_SIZE * 2) {
370 mBufferHead = tick - HISTORY_SIZE;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800371 mBufferTail = mBufferHead;
372 }
373
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800374 if (tick - mBufferHead > HISTORY_SIZE) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800375 // Throw away outdated samples.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800376 mBufferHead = tick - HISTORY_SIZE;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800377 if (mBufferTail - mBufferHead < 0) {
378 mBufferTail = mBufferHead;
379 }
380 }
381
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700382 // Adjust the jitter buffer if the latency keeps larger than the threshold
383 // in the measurement period.
384 int score = mBufferTail - tick - MEASURE_BASE;
385 if (mLatencyScore > score || mLatencyScore <= 0) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800386 mLatencyScore = score;
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800387 mLatencyTimer = tick;
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800388 } else if (tick - mLatencyTimer >= MEASURE_PERIOD) {
Steve Block71f2cf12011-10-20 11:56:00 +0100389 ALOGV("stream[%d] reduces latency of %dms", mSocket, mLatencyScore);
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800390 mBufferTail -= mLatencyScore;
Chia-chi Yehbe57bfe2011-09-06 23:45:17 -0700391 mLatencyScore = -1;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800392 }
393
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700394 int count = (BUFFER_SIZE - (mBufferTail - mBufferHead)) * mSampleRate;
395 if (count < mSampleCount) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800396 // Buffer overflow. Drop the packet.
Steve Block71f2cf12011-10-20 11:56:00 +0100397 ALOGV("stream[%d] buffer overflow", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800398 recv(mSocket, &c, 1, MSG_DONTWAIT);
399 return;
400 }
401
402 // Receive the packet and decode it.
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700403 int16_t samples[count];
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800404 if (!mCodec) {
405 // Special case for device stream.
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700406 count = recv(mSocket, samples, sizeof(samples),
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800407 MSG_TRUNC | MSG_DONTWAIT) >> 1;
408 } else {
409 __attribute__((aligned(4))) uint8_t buffer[2048];
Chia-chi Yehfe529892010-09-30 02:42:27 +0800410 sockaddr_storage remote;
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700411 socklen_t addrlen = sizeof(remote);
Chung-yih Wangbd229422010-09-23 23:23:11 +0800412
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700413 int length = recvfrom(mSocket, buffer, sizeof(buffer),
414 MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &addrlen);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800415
416 // Do we need to check SSRC, sequence, and timestamp? They are not
Chia-chi Yehb8790322010-08-19 18:26:53 +0800417 // reliable but at least they can be used to identify duplicates?
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800418 if (length < 12 || length > (int)sizeof(buffer) ||
419 (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
Steve Block71f2cf12011-10-20 11:56:00 +0100420 ALOGV("stream[%d] malformed packet", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800421 return;
422 }
423 int offset = 12 + ((buffer[0] & 0x0F) << 2);
424 if ((buffer[0] & 0x10) != 0) {
425 offset += 4 + (ntohs(*(uint16_t *)&buffer[offset + 2]) << 2);
426 }
427 if ((buffer[0] & 0x20) != 0) {
428 length -= buffer[length - 1];
429 }
430 length -= offset;
431 if (length >= 0) {
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700432 length = mCodec->decode(samples, count, &buffer[offset], length);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800433 }
Chia-chi Yehfe529892010-09-30 02:42:27 +0800434 if (length > 0 && mFixRemote) {
435 mRemote = remote;
436 mFixRemote = false;
437 }
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700438 count = length;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800439 }
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700440 if (count <= 0) {
Steve Block71f2cf12011-10-20 11:56:00 +0100441 ALOGV("stream[%d] decoder error", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800442 return;
443 }
444
445 if (tick - mBufferTail > 0) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800446 // Buffer underrun. Reset the jitter buffer.
Steve Block71f2cf12011-10-20 11:56:00 +0100447 ALOGV("stream[%d] buffer underrun", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800448 if (mBufferTail - mBufferHead <= 0) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800449 mBufferHead = tick + mInterval;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800450 mBufferTail = mBufferHead;
451 } else {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800452 int tail = (tick + mInterval) * mSampleRate;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800453 for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) {
454 mBuffer[i & mBufferMask] = 0;
455 }
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800456 mBufferTail = tick + mInterval;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800457 }
458 }
459
460 // Append to the jitter buffer.
461 int tail = mBufferTail * mSampleRate;
Chia-chi Yeh35d05dc2011-09-06 14:18:37 -0700462 for (int i = 0; i < count; ++i) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800463 mBuffer[tail & mBufferMask] = samples[i];
464 ++tail;
465 }
466 mBufferTail += mInterval;
467}
468
469//------------------------------------------------------------------------------
470
471class AudioGroup
472{
473public:
474 AudioGroup();
475 ~AudioGroup();
476 bool set(int sampleRate, int sampleCount);
477
478 bool setMode(int mode);
479 bool sendDtmf(int event);
480 bool add(AudioStream *stream);
481 bool remove(int socket);
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700482 bool platformHasAec() { return mPlatformHasAec; }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800483
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800484private:
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800485 enum {
486 ON_HOLD = 0,
487 MUTED = 1,
488 NORMAL = 2,
Chia-chi Yehd87be272011-01-06 17:43:24 +0800489 ECHO_SUPPRESSION = 3,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800490 LAST_MODE = 3,
491 };
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800492
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700493 bool checkPlatformAec();
494
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800495 AudioStream *mChain;
496 int mEventQueue;
497 volatile int mDtmfEvent;
498
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800499 int mMode;
500 int mSampleRate;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800501 int mSampleCount;
502 int mDeviceSocket;
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700503 bool mPlatformHasAec;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800504
505 class NetworkThread : public Thread
506 {
507 public:
508 NetworkThread(AudioGroup *group) : Thread(false), mGroup(group) {}
509
510 bool start()
511 {
512 if (run("Network", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
513 LOGE("cannot start network thread");
514 return false;
515 }
516 return true;
517 }
518
519 private:
520 AudioGroup *mGroup;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800521 bool threadLoop();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800522 };
523 sp<NetworkThread> mNetworkThread;
524
525 class DeviceThread : public Thread
526 {
527 public:
528 DeviceThread(AudioGroup *group) : Thread(false), mGroup(group) {}
529
530 bool start()
531 {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800532 if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
533 LOGE("cannot start device thread");
534 return false;
535 }
536 return true;
537 }
538
539 private:
540 AudioGroup *mGroup;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800541 bool threadLoop();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800542 };
543 sp<DeviceThread> mDeviceThread;
544};
545
546AudioGroup::AudioGroup()
547{
548 mMode = ON_HOLD;
549 mChain = NULL;
550 mEventQueue = -1;
551 mDtmfEvent = -1;
552 mDeviceSocket = -1;
553 mNetworkThread = new NetworkThread(this);
554 mDeviceThread = new DeviceThread(this);
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700555 mPlatformHasAec = checkPlatformAec();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800556}
557
558AudioGroup::~AudioGroup()
559{
560 mNetworkThread->requestExitAndWait();
561 mDeviceThread->requestExitAndWait();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800562 close(mEventQueue);
563 close(mDeviceSocket);
564 while (mChain) {
565 AudioStream *next = mChain->mNext;
566 delete mChain;
567 mChain = next;
568 }
Steve Block5baa3a62011-12-20 16:23:08 +0000569 ALOGD("group[%d] is dead", mDeviceSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800570}
571
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800572bool AudioGroup::set(int sampleRate, int sampleCount)
573{
574 mEventQueue = epoll_create(2);
575 if (mEventQueue == -1) {
576 LOGE("epoll_create: %s", strerror(errno));
577 return false;
578 }
579
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800580 mSampleRate = sampleRate;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800581 mSampleCount = sampleCount;
582
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800583 // Create device socket.
584 int pair[2];
585 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
586 LOGE("socketpair: %s", strerror(errno));
587 return false;
588 }
589 mDeviceSocket = pair[0];
590
591 // Create device stream.
592 mChain = new AudioStream;
593 if (!mChain->set(AudioStream::NORMAL, pair[1], NULL, NULL,
594 sampleRate, sampleCount, -1, -1)) {
595 close(pair[1]);
596 LOGE("cannot initialize device stream");
597 return false;
598 }
599
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800600 // Give device socket a reasonable timeout.
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800601 timeval tv;
602 tv.tv_sec = 0;
Chia-chi Yeh557b04d2010-09-08 09:56:02 +0800603 tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800604 if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800605 LOGE("setsockopt: %s", strerror(errno));
606 return false;
607 }
608
609 // Add device stream into event queue.
610 epoll_event event;
611 event.events = EPOLLIN;
612 event.data.ptr = mChain;
613 if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, pair[1], &event)) {
614 LOGE("epoll_ctl: %s", strerror(errno));
615 return false;
616 }
617
618 // Anything else?
Steve Block5baa3a62011-12-20 16:23:08 +0000619 ALOGD("stream[%d] joins group[%d]", pair[1], pair[0]);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800620 return true;
621}
622
623bool AudioGroup::setMode(int mode)
624{
625 if (mode < 0 || mode > LAST_MODE) {
626 return false;
627 }
Eric Laurent74e0a992011-08-29 14:24:31 -0700628 // FIXME: temporary code to overcome echo and mic gain issues on herring and tuna boards.
629 // Must be modified/removed when the root cause of the issue is fixed in the hardware or
630 // driver
Eric Laurentd7a724e2011-03-29 18:22:57 -0700631 char value[PROPERTY_VALUE_MAX];
632 property_get("ro.product.board", value, "");
Eric Laurent74e0a992011-08-29 14:24:31 -0700633 if (mode == NORMAL &&
634 (!strcmp(value, "herring") || !strcmp(value, "tuna"))) {
Eric Laurentd7a724e2011-03-29 18:22:57 -0700635 mode = ECHO_SUPPRESSION;
636 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800637 if (mMode == mode) {
638 return true;
639 }
640
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800641 mDeviceThread->requestExitAndWait();
Steve Block5baa3a62011-12-20 16:23:08 +0000642 ALOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800643 mMode = mode;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800644 return (mode == ON_HOLD) || mDeviceThread->start();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800645}
646
647bool AudioGroup::sendDtmf(int event)
648{
649 if (event < 0 || event > 15) {
650 return false;
651 }
652
653 // DTMF is rarely used, so we try to make it as lightweight as possible.
654 // Using volatile might be dodgy, but using a pipe or pthread primitives
655 // or stop-set-restart threads seems too heavy. Will investigate later.
656 timespec ts;
657 ts.tv_sec = 0;
658 ts.tv_nsec = 100000000;
659 for (int i = 0; mDtmfEvent != -1 && i < 20; ++i) {
660 nanosleep(&ts, NULL);
661 }
662 if (mDtmfEvent != -1) {
663 return false;
664 }
665 mDtmfEvent = event;
666 nanosleep(&ts, NULL);
667 return true;
668}
669
670bool AudioGroup::add(AudioStream *stream)
671{
672 mNetworkThread->requestExitAndWait();
673
674 epoll_event event;
675 event.events = EPOLLIN;
676 event.data.ptr = stream;
677 if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, stream->mSocket, &event)) {
678 LOGE("epoll_ctl: %s", strerror(errno));
679 return false;
680 }
681
682 stream->mNext = mChain->mNext;
683 mChain->mNext = stream;
684 if (!mNetworkThread->start()) {
685 // Only take over the stream when succeeded.
686 mChain->mNext = stream->mNext;
687 return false;
688 }
689
Steve Block5baa3a62011-12-20 16:23:08 +0000690 ALOGD("stream[%d] joins group[%d]", stream->mSocket, mDeviceSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800691 return true;
692}
693
694bool AudioGroup::remove(int socket)
695{
696 mNetworkThread->requestExitAndWait();
697
698 for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) {
699 AudioStream *target = stream->mNext;
700 if (target->mSocket == socket) {
Chia-chi Yehb8790322010-08-19 18:26:53 +0800701 if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, socket, NULL)) {
702 LOGE("epoll_ctl: %s", strerror(errno));
703 return false;
704 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800705 stream->mNext = target->mNext;
Steve Block5baa3a62011-12-20 16:23:08 +0000706 ALOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800707 delete target;
708 break;
709 }
710 }
711
712 // Do not start network thread if there is only one stream.
713 if (!mChain->mNext || !mNetworkThread->start()) {
714 return false;
715 }
716 return true;
717}
718
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800719bool AudioGroup::NetworkThread::threadLoop()
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800720{
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800721 AudioStream *chain = mGroup->mChain;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800722 int tick = elapsedRealtime();
723 int deadline = tick + 10;
724 int count = 0;
725
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800726 for (AudioStream *stream = chain; stream; stream = stream->mNext) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800727 if (tick - stream->mTick >= 0) {
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800728 stream->encode(tick, chain);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800729 }
730 if (deadline - stream->mTick > 0) {
731 deadline = stream->mTick;
732 }
733 ++count;
734 }
735
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800736 int event = mGroup->mDtmfEvent;
737 if (event != -1) {
738 for (AudioStream *stream = chain; stream; stream = stream->mNext) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800739 stream->sendDtmf(event);
740 }
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800741 mGroup->mDtmfEvent = -1;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800742 }
743
744 deadline -= tick;
745 if (deadline < 1) {
746 deadline = 1;
747 }
748
749 epoll_event events[count];
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800750 count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800751 if (count == -1) {
752 LOGE("epoll_wait: %s", strerror(errno));
753 return false;
754 }
755 for (int i = 0; i < count; ++i) {
756 ((AudioStream *)events[i].data.ptr)->decode(tick);
757 }
758
759 return true;
760}
761
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700762bool AudioGroup::checkPlatformAec()
763{
764 effect_descriptor_t fxDesc;
765 uint32_t numFx;
766
767 if (AudioEffect::queryNumberEffects(&numFx) != NO_ERROR) {
768 return false;
769 }
770 for (uint32_t i = 0; i < numFx; i++) {
771 if (AudioEffect::queryEffect(i, &fxDesc) != NO_ERROR) {
772 continue;
773 }
774 if (memcmp(&fxDesc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
775 return true;
776 }
777 }
778 return false;
779}
780
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800781bool AudioGroup::DeviceThread::threadLoop()
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800782{
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800783 int mode = mGroup->mMode;
784 int sampleRate = mGroup->mSampleRate;
785 int sampleCount = mGroup->mSampleCount;
786 int deviceSocket = mGroup->mDeviceSocket;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800787
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800788 // Find out the frame count for AudioTrack and AudioRecord.
789 int output = 0;
790 int input = 0;
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700791 if (AudioTrack::getMinFrameCount(&output, AUDIO_STREAM_VOICE_CALL,
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800792 sampleRate) != NO_ERROR || output <= 0 ||
793 AudioRecord::getMinFrameCount(&input, sampleRate,
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700794 AUDIO_FORMAT_PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800795 LOGE("cannot compute frame count");
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800796 return false;
797 }
Steve Block5baa3a62011-12-20 16:23:08 +0000798 ALOGD("reported frame count: output %d, input %d", output, input);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800799
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800800 if (output < sampleCount * 2) {
801 output = sampleCount * 2;
802 }
803 if (input < sampleCount * 2) {
804 input = sampleCount * 2;
805 }
Steve Block5baa3a62011-12-20 16:23:08 +0000806 ALOGD("adjusted frame count: output %d, input %d", output, input);
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800807
808 // Initialize AudioTrack and AudioRecord.
809 AudioTrack track;
810 AudioRecord record;
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700811 if (track.set(AUDIO_STREAM_VOICE_CALL, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
812 AUDIO_CHANNEL_OUT_MONO, output) != NO_ERROR || record.set(
813 AUDIO_SOURCE_VOICE_COMMUNICATION, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
814 AUDIO_CHANNEL_IN_MONO, input) != NO_ERROR) {
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800815 LOGE("cannot initialize audio device");
816 return false;
817 }
Steve Block5baa3a62011-12-20 16:23:08 +0000818 ALOGD("latency: output %d, input %d", track.latency(), record.latency());
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800819
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800820 // Give device socket a reasonable buffer size.
821 setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
822 setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
823
824 // Drain device socket.
825 char c;
826 while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
827
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700828 // check if platform supports echo cancellation and do not active local echo suppression in
829 // this case
830 EchoSuppressor *echo = NULL;
831 AudioEffect *aec = NULL;
832 if (mode == ECHO_SUPPRESSION) {
833 if (mGroup->platformHasAec()) {
834 aec = new AudioEffect(FX_IID_AEC,
835 NULL,
836 0,
837 0,
838 0,
839 record.getSessionId(),
840 record.getInput());
841 status_t status = aec->initCheck();
842 if (status == NO_ERROR || status == ALREADY_EXISTS) {
843 aec->setEnabled(true);
844 } else {
845 delete aec;
846 aec = NULL;
847 }
848 }
849 // Create local echo suppressor if platform AEC cannot be used.
850 if (aec == NULL) {
851 echo = new EchoSuppressor(sampleCount,
852 (track.latency() + record.latency()) * sampleRate / 1000);
853 }
854 }
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800855 // Start AudioRecord before AudioTrack. This prevents AudioTrack from being
856 // disabled due to buffer underrun while waiting for AudioRecord.
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800857 if (mode != MUTED) {
858 record.start();
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800859 int16_t one;
860 record.read(&one, sizeof(one));
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800861 }
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800862 track.start();
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800863
864 while (!exitPending()) {
865 int16_t output[sampleCount];
866 if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
867 memset(output, 0, sizeof(output));
868 }
869
870 int16_t input[sampleCount];
871 int toWrite = sampleCount;
872 int toRead = (mode == MUTED) ? 0 : sampleCount;
873 int chances = 100;
874
875 while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
876 if (toWrite > 0) {
877 AudioTrack::Buffer buffer;
878 buffer.frameCount = toWrite;
879
880 status_t status = track.obtainBuffer(&buffer, 1);
881 if (status == NO_ERROR) {
882 int offset = sampleCount - toWrite;
883 memcpy(buffer.i8, &output[offset], buffer.size);
884 toWrite -= buffer.frameCount;
885 track.releaseBuffer(&buffer);
886 } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
887 LOGE("cannot write to AudioTrack");
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700888 goto exit;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800889 }
890 }
891
892 if (toRead > 0) {
893 AudioRecord::Buffer buffer;
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800894 buffer.frameCount = toRead;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800895
896 status_t status = record.obtainBuffer(&buffer, 1);
897 if (status == NO_ERROR) {
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800898 int offset = sampleCount - toRead;
899 memcpy(&input[offset], buffer.i8, buffer.size);
900 toRead -= buffer.frameCount;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800901 record.releaseBuffer(&buffer);
902 } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
903 LOGE("cannot read from AudioRecord");
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700904 goto exit;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800905 }
906 }
907 }
908
909 if (chances <= 0) {
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800910 LOGW("device loop timeout");
911 while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800912 }
913
914 if (mode != MUTED) {
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700915 if (echo != NULL) {
Steve Block71f2cf12011-10-20 11:56:00 +0100916 ALOGV("echo->run()");
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700917 echo->run(output, input);
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800918 }
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700919 send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800920 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800921 }
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700922
923exit:
924 delete echo;
925 delete aec;
926 return true;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800927}
928
929//------------------------------------------------------------------------------
930
931static jfieldID gNative;
932static jfieldID gMode;
933
Chia-chi Yehb8790322010-08-19 18:26:53 +0800934void add(JNIEnv *env, jobject thiz, jint mode,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800935 jint socket, jstring jRemoteAddress, jint remotePort,
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800936 jstring jCodecSpec, jint dtmfType)
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800937{
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800938 AudioCodec *codec = NULL;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800939 AudioStream *stream = NULL;
940 AudioGroup *group = NULL;
941
942 // Sanity check.
943 sockaddr_storage remote;
944 if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
945 // Exception already thrown.
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800946 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800947 }
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800948 if (!jCodecSpec) {
949 jniThrowNullPointerException(env, "codecSpec");
950 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800951 }
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800952 const char *codecSpec = env->GetStringUTFChars(jCodecSpec, NULL);
953 if (!codecSpec) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800954 // Exception already thrown.
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800955 return;
956 }
957
958 // Create audio codec.
959 int codecType = -1;
960 char codecName[16];
961 int sampleRate = -1;
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800962 sscanf(codecSpec, "%d %15[^/]%*c%d", &codecType, codecName, &sampleRate);
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800963 codec = newAudioCodec(codecName);
964 int sampleCount = (codec ? codec->set(sampleRate, codecSpec) : -1);
965 env->ReleaseStringUTFChars(jCodecSpec, codecSpec);
966 if (sampleCount <= 0) {
967 jniThrowException(env, "java/lang/IllegalStateException",
968 "cannot initialize audio codec");
Chia-chi Yehb8790322010-08-19 18:26:53 +0800969 goto error;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800970 }
971
972 // Create audio stream.
973 stream = new AudioStream;
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800974 if (!stream->set(mode, socket, &remote, codec, sampleRate, sampleCount,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800975 codecType, dtmfType)) {
976 jniThrowException(env, "java/lang/IllegalStateException",
977 "cannot initialize audio stream");
978 goto error;
979 }
980 socket = -1;
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800981 codec = NULL;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800982
983 // Create audio group.
984 group = (AudioGroup *)env->GetIntField(thiz, gNative);
985 if (!group) {
986 int mode = env->GetIntField(thiz, gMode);
987 group = new AudioGroup;
988 if (!group->set(8000, 256) || !group->setMode(mode)) {
989 jniThrowException(env, "java/lang/IllegalStateException",
990 "cannot initialize audio group");
991 goto error;
992 }
993 }
994
995 // Add audio stream into audio group.
996 if (!group->add(stream)) {
997 jniThrowException(env, "java/lang/IllegalStateException",
998 "cannot add audio stream");
999 goto error;
1000 }
1001
1002 // Succeed.
1003 env->SetIntField(thiz, gNative, (int)group);
Chia-chi Yehb8790322010-08-19 18:26:53 +08001004 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001005
1006error:
1007 delete group;
1008 delete stream;
Chia-chi Yeh4033a672010-09-16 18:36:45 +08001009 delete codec;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001010 close(socket);
1011 env->SetIntField(thiz, gNative, NULL);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001012}
1013
1014void remove(JNIEnv *env, jobject thiz, jint socket)
1015{
1016 AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
1017 if (group) {
1018 if (socket == -1 || !group->remove(socket)) {
1019 delete group;
1020 env->SetIntField(thiz, gNative, NULL);
1021 }
1022 }
1023}
1024
1025void setMode(JNIEnv *env, jobject thiz, jint mode)
1026{
1027 AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
1028 if (group && !group->setMode(mode)) {
1029 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001030 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001031}
1032
1033void sendDtmf(JNIEnv *env, jobject thiz, jint event)
1034{
1035 AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
1036 if (group && !group->sendDtmf(event)) {
1037 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1038 }
1039}
1040
1041JNINativeMethod gMethods[] = {
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +08001042 {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add},
1043 {"nativeRemove", "(I)V", (void *)remove},
1044 {"nativeSetMode", "(I)V", (void *)setMode},
1045 {"nativeSendDtmf", "(I)V", (void *)sendDtmf},
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001046};
1047
1048} // namespace
1049
1050int registerAudioGroup(JNIEnv *env)
1051{
1052 gRandom = open("/dev/urandom", O_RDONLY);
1053 if (gRandom == -1) {
1054 LOGE("urandom: %s", strerror(errno));
1055 return -1;
1056 }
1057
1058 jclass clazz;
1059 if ((clazz = env->FindClass("android/net/rtp/AudioGroup")) == NULL ||
1060 (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL ||
1061 (gMode = env->GetFieldID(clazz, "mMode", "I")) == NULL ||
1062 env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
1063 LOGE("JNI registration failed");
1064 return -1;
1065 }
1066 return 0;
1067}