blob: 5f07bb59074c8877c9d658dc79fe276cb50d70a6 [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
31#define LOG_TAG "AudioGroup"
32#include <cutils/atomic.h>
Eric Laurentd7a724e2011-03-29 18:22:57 -070033#include <cutils/properties.h>
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080034#include <utils/Log.h>
35#include <utils/Errors.h>
36#include <utils/RefBase.h>
37#include <utils/threads.h>
38#include <utils/SystemClock.h>
39#include <media/AudioSystem.h>
40#include <media/AudioRecord.h>
41#include <media/AudioTrack.h>
42#include <media/mediarecorder.h>
Eric Laurent5fb3ba62011-07-25 12:02:16 -070043#include <media/AudioEffect.h>
44#include <audio_effects/effect_aec.h>
Dima Zavin34bb4192011-05-11 14:15:23 -070045#include <system/audio.h>
Dima Zavin24fc2fb2011-04-19 22:30:36 -070046
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080047#include "jni.h"
48#include "JNIHelp.h"
49
50#include "AudioCodec.h"
Chia-chi Yeha8a10092010-10-05 01:17:13 +080051#include "EchoSuppressor.h"
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080052
53extern int parse(JNIEnv *env, jstring jAddress, int port, sockaddr_storage *ss);
54
55namespace {
56
57using namespace android;
58
59int gRandom = -1;
60
61// We use a circular array to implement jitter buffer. The simplest way is doing
62// a modulo operation on the index while accessing the array. However modulo can
63// be expensive on some platforms, such as ARM. Thus we round up the size of the
64// array to the nearest power of 2 and then use bitwise-and instead of modulo.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +080065// Currently we make it 512ms long and assume packet interval is 40ms or less.
66// The first 80ms is the place where samples get mixed. The rest 432ms is the
67// real jitter buffer. For a stream at 8000Hz it takes 8192 bytes. These numbers
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080068// are chosen by experiments and each of them can be adjusted as needed.
69
Chia-chi Yeh3cf71372011-01-04 19:10:06 +080070// Originally a stream does not send packets when it is receive-only or there is
71// nothing to mix. However, this causes some problems with certain firewalls and
72// proxies. A firewall might remove a port mapping when there is no outgoing
73// packet for a preiod of time, and a proxy might wait for incoming packets from
74// both sides before start forwarding. To solve these problems, we send out a
75// silence packet on the stream for every second. It should be good enough to
76// keep the stream alive with relatively low resources.
77
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080078// Other notes:
79// + We use elapsedRealtime() to get the time. Since we use 32bit variables
80// instead of 64bit ones, comparison must be done by subtraction.
81// + Sampling rate must be multiple of 1000Hz, and packet length must be in
82// milliseconds. No floating points.
83// + If we cannot get enough CPU, we drop samples and simulate packet loss.
84// + Resampling is not done yet, so streams in one group must use the same rate.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +080085// For the first release only 8000Hz is supported.
86
87#define BUFFER_SIZE 512
88#define HISTORY_SIZE 80
89#define MEASURE_PERIOD 2000
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080090
91class AudioStream
92{
93public:
94 AudioStream();
95 ~AudioStream();
96 bool set(int mode, int socket, sockaddr_storage *remote,
Chia-chi Yeh4033a672010-09-16 18:36:45 +080097 AudioCodec *codec, int sampleRate, int sampleCount,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +080098 int codecType, int dtmfType);
99
100 void sendDtmf(int event);
101 bool mix(int32_t *output, int head, int tail, int sampleRate);
102 void encode(int tick, AudioStream *chain);
103 void decode(int tick);
104
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800105private:
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800106 enum {
107 NORMAL = 0,
108 SEND_ONLY = 1,
109 RECEIVE_ONLY = 2,
110 LAST_MODE = 2,
111 };
112
113 int mMode;
114 int mSocket;
115 sockaddr_storage mRemote;
116 AudioCodec *mCodec;
117 uint32_t mCodecMagic;
118 uint32_t mDtmfMagic;
Chia-chi Yehfe529892010-09-30 02:42:27 +0800119 bool mFixRemote;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800120
121 int mTick;
122 int mSampleRate;
123 int mSampleCount;
124 int mInterval;
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800125 int mKeepAlive;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800126
127 int16_t *mBuffer;
128 int mBufferMask;
129 int mBufferHead;
130 int mBufferTail;
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800131 int mLatencyTimer;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800132 int mLatencyScore;
133
134 uint16_t mSequence;
135 uint32_t mTimestamp;
136 uint32_t mSsrc;
137
138 int mDtmfEvent;
139 int mDtmfStart;
140
141 AudioStream *mNext;
142
143 friend class AudioGroup;
144};
145
146AudioStream::AudioStream()
147{
148 mSocket = -1;
149 mCodec = NULL;
150 mBuffer = NULL;
151 mNext = NULL;
152}
153
154AudioStream::~AudioStream()
155{
156 close(mSocket);
157 delete mCodec;
158 delete [] mBuffer;
159 LOGD("stream[%d] is dead", mSocket);
160}
161
162bool AudioStream::set(int mode, int socket, sockaddr_storage *remote,
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800163 AudioCodec *codec, int sampleRate, int sampleCount,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800164 int codecType, int dtmfType)
165{
166 if (mode < 0 || mode > LAST_MODE) {
167 return false;
168 }
169 mMode = mode;
170
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800171 mCodecMagic = (0x8000 | codecType) << 16;
172 mDtmfMagic = (dtmfType == -1) ? 0 : (0x8000 | dtmfType) << 16;
173
174 mTick = elapsedRealtime();
175 mSampleRate = sampleRate / 1000;
176 mSampleCount = sampleCount;
177 mInterval = mSampleCount / mSampleRate;
178
179 // Allocate jitter buffer.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800180 for (mBufferMask = 8; mBufferMask < mSampleRate; mBufferMask <<= 1);
181 mBufferMask *= BUFFER_SIZE;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800182 mBuffer = new int16_t[mBufferMask];
183 --mBufferMask;
184 mBufferHead = 0;
185 mBufferTail = 0;
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800186 mLatencyTimer = 0;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800187 mLatencyScore = 0;
188
189 // Initialize random bits.
190 read(gRandom, &mSequence, sizeof(mSequence));
191 read(gRandom, &mTimestamp, sizeof(mTimestamp));
192 read(gRandom, &mSsrc, sizeof(mSsrc));
193
194 mDtmfEvent = -1;
195 mDtmfStart = 0;
196
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800197 // Only take over these things when succeeded.
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800198 mSocket = socket;
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800199 if (codec) {
200 mRemote = *remote;
201 mCodec = codec;
Chia-chi Yehfe529892010-09-30 02:42:27 +0800202
203 // Here we should never get an private address, but some buggy proxy
204 // servers do give us one. To solve this, we replace the address when
205 // the first time we successfully decode an incoming packet.
206 mFixRemote = false;
207 if (remote->ss_family == AF_INET) {
208 unsigned char *address =
209 (unsigned char *)&((sockaddr_in *)remote)->sin_addr;
210 if (address[0] == 10 ||
211 (address[0] == 172 && (address[1] >> 4) == 1) ||
212 (address[0] == 192 && address[1] == 168)) {
213 mFixRemote = true;
214 }
215 }
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800216 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800217
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800218 LOGD("stream[%d] is configured as %s %dkHz %dms mode %d", mSocket,
219 (codec ? codec->name : "RAW"), mSampleRate, mInterval, mMode);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800220 return true;
221}
222
223void AudioStream::sendDtmf(int event)
224{
225 if (mDtmfMagic != 0) {
226 mDtmfEvent = event << 24;
227 mDtmfStart = mTimestamp + mSampleCount;
228 }
229}
230
231bool AudioStream::mix(int32_t *output, int head, int tail, int sampleRate)
232{
233 if (mMode == SEND_ONLY) {
234 return false;
235 }
236
237 if (head - mBufferHead < 0) {
238 head = mBufferHead;
239 }
240 if (tail - mBufferTail > 0) {
241 tail = mBufferTail;
242 }
243 if (tail - head <= 0) {
244 return false;
245 }
246
247 head *= mSampleRate;
248 tail *= mSampleRate;
249
250 if (sampleRate == mSampleRate) {
251 for (int i = head; i - tail < 0; ++i) {
252 output[i - head] += mBuffer[i & mBufferMask];
253 }
254 } else {
255 // TODO: implement resampling.
256 return false;
257 }
258 return true;
259}
260
261void AudioStream::encode(int tick, AudioStream *chain)
262{
263 if (tick - mTick >= mInterval) {
264 // We just missed the train. Pretend that packets in between are lost.
265 int skipped = (tick - mTick) / mInterval;
266 mTick += skipped * mInterval;
267 mSequence += skipped;
268 mTimestamp += skipped * mSampleCount;
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800269 LOGV("stream[%d] skips %d packets", mSocket, skipped);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800270 }
271
272 tick = mTick;
273 mTick += mInterval;
274 ++mSequence;
275 mTimestamp += mSampleCount;
276
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800277 // If there is an ongoing DTMF event, send it now.
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800278 if (mMode != RECEIVE_ONLY && mDtmfEvent != -1) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800279 int duration = mTimestamp - mDtmfStart;
280 // Make sure duration is reasonable.
281 if (duration >= 0 && duration < mSampleRate * 100) {
282 duration += mSampleCount;
283 int32_t buffer[4] = {
284 htonl(mDtmfMagic | mSequence),
285 htonl(mDtmfStart),
286 mSsrc,
287 htonl(mDtmfEvent | duration),
288 };
289 if (duration >= mSampleRate * 100) {
290 buffer[3] |= htonl(1 << 23);
291 mDtmfEvent = -1;
292 }
293 sendto(mSocket, buffer, sizeof(buffer), MSG_DONTWAIT,
294 (sockaddr *)&mRemote, sizeof(mRemote));
295 return;
296 }
297 mDtmfEvent = -1;
298 }
299
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800300 int32_t buffer[mSampleCount + 3];
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800301 int16_t samples[mSampleCount];
302 if (mMode == RECEIVE_ONLY) {
303 if ((mTick ^ mKeepAlive) >> 10 == 0) {
304 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800305 }
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800306 mKeepAlive = mTick;
307 memset(samples, 0, sizeof(samples));
308 } else {
309 // Mix all other streams.
310 bool mixed = false;
311 memset(buffer, 0, sizeof(buffer));
312 while (chain) {
313 if (chain != this &&
314 chain->mix(buffer, tick - mInterval, tick, mSampleRate)) {
315 mixed = true;
316 }
317 chain = chain->mNext;
318 }
319
320 if (mixed) {
321 // Saturate into 16 bits.
322 for (int i = 0; i < mSampleCount; ++i) {
323 int32_t sample = buffer[i];
324 if (sample < -32768) {
325 sample = -32768;
326 }
327 if (sample > 32767) {
328 sample = 32767;
329 }
330 samples[i] = sample;
331 }
332 } else {
333 if ((mTick ^ mKeepAlive) >> 10 == 0) {
334 return;
335 }
336 mKeepAlive = mTick;
337 memset(samples, 0, sizeof(samples));
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800338 LOGV("stream[%d] no data", mSocket);
repo sync7a69aef2010-09-23 05:46:01 +0800339 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800340 }
341
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800342 if (!mCodec) {
343 // Special case for device stream.
344 send(mSocket, samples, sizeof(samples), MSG_DONTWAIT);
345 return;
346 }
347
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800348 // Cook the packet and send it out.
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800349 buffer[0] = htonl(mCodecMagic | mSequence);
350 buffer[1] = htonl(mTimestamp);
351 buffer[2] = mSsrc;
352 int length = mCodec->encode(&buffer[3], samples);
353 if (length <= 0) {
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800354 LOGV("stream[%d] encoder error", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800355 return;
356 }
357 sendto(mSocket, buffer, length + 12, MSG_DONTWAIT, (sockaddr *)&mRemote,
358 sizeof(mRemote));
359}
360
361void AudioStream::decode(int tick)
362{
363 char c;
364 if (mMode == SEND_ONLY) {
365 recv(mSocket, &c, 1, MSG_DONTWAIT);
366 return;
367 }
368
369 // Make sure mBufferHead and mBufferTail are reasonable.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800370 if ((unsigned int)(tick + BUFFER_SIZE - mBufferHead) > BUFFER_SIZE * 2) {
371 mBufferHead = tick - HISTORY_SIZE;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800372 mBufferTail = mBufferHead;
373 }
374
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800375 if (tick - mBufferHead > HISTORY_SIZE) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800376 // Throw away outdated samples.
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800377 mBufferHead = tick - HISTORY_SIZE;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800378 if (mBufferTail - mBufferHead < 0) {
379 mBufferTail = mBufferHead;
380 }
381 }
382
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800383 // Adjust the jitter buffer if the latency keeps larger than two times of the
384 // packet interval in the past two seconds.
385 int score = mBufferTail - tick - mInterval * 2;
386 if (mLatencyScore > score) {
387 mLatencyScore = score;
388 }
389 if (mLatencyScore <= 0) {
390 mLatencyTimer = tick;
391 mLatencyScore = score;
392 } else if (tick - mLatencyTimer >= MEASURE_PERIOD) {
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800393 LOGV("stream[%d] reduces latency of %dms", mSocket, mLatencyScore);
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800394 mBufferTail -= mLatencyScore;
395 mLatencyTimer = tick;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800396 }
397
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800398 if (mBufferTail - mBufferHead > BUFFER_SIZE - mInterval) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800399 // Buffer overflow. Drop the packet.
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800400 LOGV("stream[%d] buffer overflow", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800401 recv(mSocket, &c, 1, MSG_DONTWAIT);
402 return;
403 }
404
405 // Receive the packet and decode it.
406 int16_t samples[mSampleCount];
407 int length = 0;
408 if (!mCodec) {
409 // Special case for device stream.
410 length = recv(mSocket, samples, sizeof(samples),
411 MSG_TRUNC | MSG_DONTWAIT) >> 1;
412 } else {
413 __attribute__((aligned(4))) uint8_t buffer[2048];
Chia-chi Yehfe529892010-09-30 02:42:27 +0800414 sockaddr_storage remote;
415 socklen_t len = sizeof(remote);
Chung-yih Wangbd229422010-09-23 23:23:11 +0800416
Chia-chi Yehfe529892010-09-30 02:42:27 +0800417 length = recvfrom(mSocket, buffer, sizeof(buffer),
418 MSG_TRUNC | MSG_DONTWAIT, (sockaddr *)&remote, &len);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800419
420 // Do we need to check SSRC, sequence, and timestamp? They are not
Chia-chi Yehb8790322010-08-19 18:26:53 +0800421 // reliable but at least they can be used to identify duplicates?
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800422 if (length < 12 || length > (int)sizeof(buffer) ||
423 (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800424 LOGV("stream[%d] malformed packet", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800425 return;
426 }
427 int offset = 12 + ((buffer[0] & 0x0F) << 2);
428 if ((buffer[0] & 0x10) != 0) {
429 offset += 4 + (ntohs(*(uint16_t *)&buffer[offset + 2]) << 2);
430 }
431 if ((buffer[0] & 0x20) != 0) {
432 length -= buffer[length - 1];
433 }
434 length -= offset;
435 if (length >= 0) {
436 length = mCodec->decode(samples, &buffer[offset], length);
437 }
Chia-chi Yehfe529892010-09-30 02:42:27 +0800438 if (length > 0 && mFixRemote) {
439 mRemote = remote;
440 mFixRemote = false;
441 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800442 }
Chia-chi Yehfe529892010-09-30 02:42:27 +0800443 if (length <= 0) {
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800444 LOGV("stream[%d] decoder error", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800445 return;
446 }
447
448 if (tick - mBufferTail > 0) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800449 // Buffer underrun. Reset the jitter buffer.
Chia-chi Yeh21ae1ad2010-09-30 16:07:44 +0800450 LOGV("stream[%d] buffer underrun", mSocket);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800451 if (mBufferTail - mBufferHead <= 0) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800452 mBufferHead = tick + mInterval;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800453 mBufferTail = mBufferHead;
454 } else {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800455 int tail = (tick + mInterval) * mSampleRate;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800456 for (int i = mBufferTail * mSampleRate; i - tail < 0; ++i) {
457 mBuffer[i & mBufferMask] = 0;
458 }
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800459 mBufferTail = tick + mInterval;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800460 }
461 }
462
463 // Append to the jitter buffer.
464 int tail = mBufferTail * mSampleRate;
465 for (int i = 0; i < mSampleCount; ++i) {
466 mBuffer[tail & mBufferMask] = samples[i];
467 ++tail;
468 }
469 mBufferTail += mInterval;
470}
471
472//------------------------------------------------------------------------------
473
474class AudioGroup
475{
476public:
477 AudioGroup();
478 ~AudioGroup();
479 bool set(int sampleRate, int sampleCount);
480
481 bool setMode(int mode);
482 bool sendDtmf(int event);
483 bool add(AudioStream *stream);
484 bool remove(int socket);
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700485 bool platformHasAec() { return mPlatformHasAec; }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800486
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800487private:
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800488 enum {
489 ON_HOLD = 0,
490 MUTED = 1,
491 NORMAL = 2,
Chia-chi Yehd87be272011-01-06 17:43:24 +0800492 ECHO_SUPPRESSION = 3,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800493 LAST_MODE = 3,
494 };
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800495
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700496 bool checkPlatformAec();
497
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800498 AudioStream *mChain;
499 int mEventQueue;
500 volatile int mDtmfEvent;
501
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800502 int mMode;
503 int mSampleRate;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800504 int mSampleCount;
505 int mDeviceSocket;
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700506 bool mPlatformHasAec;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800507
508 class NetworkThread : public Thread
509 {
510 public:
511 NetworkThread(AudioGroup *group) : Thread(false), mGroup(group) {}
512
513 bool start()
514 {
515 if (run("Network", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
516 LOGE("cannot start network thread");
517 return false;
518 }
519 return true;
520 }
521
522 private:
523 AudioGroup *mGroup;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800524 bool threadLoop();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800525 };
526 sp<NetworkThread> mNetworkThread;
527
528 class DeviceThread : public Thread
529 {
530 public:
531 DeviceThread(AudioGroup *group) : Thread(false), mGroup(group) {}
532
533 bool start()
534 {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800535 if (run("Device", ANDROID_PRIORITY_AUDIO) != NO_ERROR) {
536 LOGE("cannot start device thread");
537 return false;
538 }
539 return true;
540 }
541
542 private:
543 AudioGroup *mGroup;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800544 bool threadLoop();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800545 };
546 sp<DeviceThread> mDeviceThread;
547};
548
549AudioGroup::AudioGroup()
550{
551 mMode = ON_HOLD;
552 mChain = NULL;
553 mEventQueue = -1;
554 mDtmfEvent = -1;
555 mDeviceSocket = -1;
556 mNetworkThread = new NetworkThread(this);
557 mDeviceThread = new DeviceThread(this);
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700558 mPlatformHasAec = checkPlatformAec();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800559}
560
561AudioGroup::~AudioGroup()
562{
563 mNetworkThread->requestExitAndWait();
564 mDeviceThread->requestExitAndWait();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800565 close(mEventQueue);
566 close(mDeviceSocket);
567 while (mChain) {
568 AudioStream *next = mChain->mNext;
569 delete mChain;
570 mChain = next;
571 }
572 LOGD("group[%d] is dead", mDeviceSocket);
573}
574
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800575bool AudioGroup::set(int sampleRate, int sampleCount)
576{
577 mEventQueue = epoll_create(2);
578 if (mEventQueue == -1) {
579 LOGE("epoll_create: %s", strerror(errno));
580 return false;
581 }
582
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800583 mSampleRate = sampleRate;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800584 mSampleCount = sampleCount;
585
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800586 // Create device socket.
587 int pair[2];
588 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair)) {
589 LOGE("socketpair: %s", strerror(errno));
590 return false;
591 }
592 mDeviceSocket = pair[0];
593
594 // Create device stream.
595 mChain = new AudioStream;
596 if (!mChain->set(AudioStream::NORMAL, pair[1], NULL, NULL,
597 sampleRate, sampleCount, -1, -1)) {
598 close(pair[1]);
599 LOGE("cannot initialize device stream");
600 return false;
601 }
602
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800603 // Give device socket a reasonable timeout.
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800604 timeval tv;
605 tv.tv_sec = 0;
Chia-chi Yeh557b04d2010-09-08 09:56:02 +0800606 tv.tv_usec = 1000 * sampleCount / sampleRate * 500;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800607 if (setsockopt(pair[0], SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800608 LOGE("setsockopt: %s", strerror(errno));
609 return false;
610 }
611
612 // Add device stream into event queue.
613 epoll_event event;
614 event.events = EPOLLIN;
615 event.data.ptr = mChain;
616 if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, pair[1], &event)) {
617 LOGE("epoll_ctl: %s", strerror(errno));
618 return false;
619 }
620
621 // Anything else?
622 LOGD("stream[%d] joins group[%d]", pair[1], pair[0]);
623 return true;
624}
625
626bool AudioGroup::setMode(int mode)
627{
628 if (mode < 0 || mode > LAST_MODE) {
629 return false;
630 }
Eric Laurent74e0a992011-08-29 14:24:31 -0700631 // FIXME: temporary code to overcome echo and mic gain issues on herring and tuna boards.
632 // Must be modified/removed when the root cause of the issue is fixed in the hardware or
633 // driver
Eric Laurentd7a724e2011-03-29 18:22:57 -0700634 char value[PROPERTY_VALUE_MAX];
635 property_get("ro.product.board", value, "");
Eric Laurent74e0a992011-08-29 14:24:31 -0700636 if (mode == NORMAL &&
637 (!strcmp(value, "herring") || !strcmp(value, "tuna"))) {
Eric Laurentd7a724e2011-03-29 18:22:57 -0700638 mode = ECHO_SUPPRESSION;
639 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800640 if (mMode == mode) {
641 return true;
642 }
643
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800644 mDeviceThread->requestExitAndWait();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800645 LOGD("group[%d] switches from mode %d to %d", mDeviceSocket, mMode, mode);
646 mMode = mode;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800647 return (mode == ON_HOLD) || mDeviceThread->start();
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800648}
649
650bool AudioGroup::sendDtmf(int event)
651{
652 if (event < 0 || event > 15) {
653 return false;
654 }
655
656 // DTMF is rarely used, so we try to make it as lightweight as possible.
657 // Using volatile might be dodgy, but using a pipe or pthread primitives
658 // or stop-set-restart threads seems too heavy. Will investigate later.
659 timespec ts;
660 ts.tv_sec = 0;
661 ts.tv_nsec = 100000000;
662 for (int i = 0; mDtmfEvent != -1 && i < 20; ++i) {
663 nanosleep(&ts, NULL);
664 }
665 if (mDtmfEvent != -1) {
666 return false;
667 }
668 mDtmfEvent = event;
669 nanosleep(&ts, NULL);
670 return true;
671}
672
673bool AudioGroup::add(AudioStream *stream)
674{
675 mNetworkThread->requestExitAndWait();
676
677 epoll_event event;
678 event.events = EPOLLIN;
679 event.data.ptr = stream;
680 if (epoll_ctl(mEventQueue, EPOLL_CTL_ADD, stream->mSocket, &event)) {
681 LOGE("epoll_ctl: %s", strerror(errno));
682 return false;
683 }
684
685 stream->mNext = mChain->mNext;
686 mChain->mNext = stream;
687 if (!mNetworkThread->start()) {
688 // Only take over the stream when succeeded.
689 mChain->mNext = stream->mNext;
690 return false;
691 }
692
693 LOGD("stream[%d] joins group[%d]", stream->mSocket, mDeviceSocket);
694 return true;
695}
696
697bool AudioGroup::remove(int socket)
698{
699 mNetworkThread->requestExitAndWait();
700
701 for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) {
702 AudioStream *target = stream->mNext;
703 if (target->mSocket == socket) {
Chia-chi Yehb8790322010-08-19 18:26:53 +0800704 if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, socket, NULL)) {
705 LOGE("epoll_ctl: %s", strerror(errno));
706 return false;
707 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800708 stream->mNext = target->mNext;
709 LOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket);
710 delete target;
711 break;
712 }
713 }
714
715 // Do not start network thread if there is only one stream.
716 if (!mChain->mNext || !mNetworkThread->start()) {
717 return false;
718 }
719 return true;
720}
721
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800722bool AudioGroup::NetworkThread::threadLoop()
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800723{
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800724 AudioStream *chain = mGroup->mChain;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800725 int tick = elapsedRealtime();
726 int deadline = tick + 10;
727 int count = 0;
728
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800729 for (AudioStream *stream = chain; stream; stream = stream->mNext) {
Chia-chi Yeh3520bd42010-09-30 13:48:07 +0800730 if (tick - stream->mTick >= 0) {
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800731 stream->encode(tick, chain);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800732 }
733 if (deadline - stream->mTick > 0) {
734 deadline = stream->mTick;
735 }
736 ++count;
737 }
738
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800739 int event = mGroup->mDtmfEvent;
740 if (event != -1) {
741 for (AudioStream *stream = chain; stream; stream = stream->mNext) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800742 stream->sendDtmf(event);
743 }
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800744 mGroup->mDtmfEvent = -1;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800745 }
746
747 deadline -= tick;
748 if (deadline < 1) {
749 deadline = 1;
750 }
751
752 epoll_event events[count];
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800753 count = epoll_wait(mGroup->mEventQueue, events, count, deadline);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800754 if (count == -1) {
755 LOGE("epoll_wait: %s", strerror(errno));
756 return false;
757 }
758 for (int i = 0; i < count; ++i) {
759 ((AudioStream *)events[i].data.ptr)->decode(tick);
760 }
761
762 return true;
763}
764
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700765bool AudioGroup::checkPlatformAec()
766{
767 effect_descriptor_t fxDesc;
768 uint32_t numFx;
769
770 if (AudioEffect::queryNumberEffects(&numFx) != NO_ERROR) {
771 return false;
772 }
773 for (uint32_t i = 0; i < numFx; i++) {
774 if (AudioEffect::queryEffect(i, &fxDesc) != NO_ERROR) {
775 continue;
776 }
777 if (memcmp(&fxDesc.type, FX_IID_AEC, sizeof(effect_uuid_t)) == 0) {
778 return true;
779 }
780 }
781 return false;
782}
783
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800784bool AudioGroup::DeviceThread::threadLoop()
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800785{
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800786 int mode = mGroup->mMode;
787 int sampleRate = mGroup->mSampleRate;
788 int sampleCount = mGroup->mSampleCount;
789 int deviceSocket = mGroup->mDeviceSocket;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800790
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800791 // Find out the frame count for AudioTrack and AudioRecord.
792 int output = 0;
793 int input = 0;
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700794 if (AudioTrack::getMinFrameCount(&output, AUDIO_STREAM_VOICE_CALL,
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800795 sampleRate) != NO_ERROR || output <= 0 ||
796 AudioRecord::getMinFrameCount(&input, sampleRate,
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700797 AUDIO_FORMAT_PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800798 LOGE("cannot compute frame count");
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800799 return false;
800 }
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800801 LOGD("reported frame count: output %d, input %d", output, input);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800802
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800803 if (output < sampleCount * 2) {
804 output = sampleCount * 2;
805 }
806 if (input < sampleCount * 2) {
807 input = sampleCount * 2;
808 }
809 LOGD("adjusted frame count: output %d, input %d", output, input);
810
811 // Initialize AudioTrack and AudioRecord.
812 AudioTrack track;
813 AudioRecord record;
Dima Zavin24fc2fb2011-04-19 22:30:36 -0700814 if (track.set(AUDIO_STREAM_VOICE_CALL, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
815 AUDIO_CHANNEL_OUT_MONO, output) != NO_ERROR || record.set(
816 AUDIO_SOURCE_VOICE_COMMUNICATION, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
817 AUDIO_CHANNEL_IN_MONO, input) != NO_ERROR) {
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800818 LOGE("cannot initialize audio device");
819 return false;
820 }
821 LOGD("latency: output %d, input %d", track.latency(), record.latency());
822
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800823 // Give device socket a reasonable buffer size.
824 setsockopt(deviceSocket, SOL_SOCKET, SO_RCVBUF, &output, sizeof(output));
825 setsockopt(deviceSocket, SOL_SOCKET, SO_SNDBUF, &output, sizeof(output));
826
827 // Drain device socket.
828 char c;
829 while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
830
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700831 // check if platform supports echo cancellation and do not active local echo suppression in
832 // this case
833 EchoSuppressor *echo = NULL;
834 AudioEffect *aec = NULL;
835 if (mode == ECHO_SUPPRESSION) {
836 if (mGroup->platformHasAec()) {
837 aec = new AudioEffect(FX_IID_AEC,
838 NULL,
839 0,
840 0,
841 0,
842 record.getSessionId(),
843 record.getInput());
844 status_t status = aec->initCheck();
845 if (status == NO_ERROR || status == ALREADY_EXISTS) {
846 aec->setEnabled(true);
847 } else {
848 delete aec;
849 aec = NULL;
850 }
851 }
852 // Create local echo suppressor if platform AEC cannot be used.
853 if (aec == NULL) {
854 echo = new EchoSuppressor(sampleCount,
855 (track.latency() + record.latency()) * sampleRate / 1000);
856 }
857 }
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800858 // Start AudioRecord before AudioTrack. This prevents AudioTrack from being
859 // disabled due to buffer underrun while waiting for AudioRecord.
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800860 if (mode != MUTED) {
861 record.start();
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800862 int16_t one;
863 record.read(&one, sizeof(one));
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800864 }
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800865 track.start();
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800866
867 while (!exitPending()) {
868 int16_t output[sampleCount];
869 if (recv(deviceSocket, output, sizeof(output), 0) <= 0) {
870 memset(output, 0, sizeof(output));
871 }
872
873 int16_t input[sampleCount];
874 int toWrite = sampleCount;
875 int toRead = (mode == MUTED) ? 0 : sampleCount;
876 int chances = 100;
877
878 while (--chances > 0 && (toWrite > 0 || toRead > 0)) {
879 if (toWrite > 0) {
880 AudioTrack::Buffer buffer;
881 buffer.frameCount = toWrite;
882
883 status_t status = track.obtainBuffer(&buffer, 1);
884 if (status == NO_ERROR) {
885 int offset = sampleCount - toWrite;
886 memcpy(buffer.i8, &output[offset], buffer.size);
887 toWrite -= buffer.frameCount;
888 track.releaseBuffer(&buffer);
889 } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
890 LOGE("cannot write to AudioTrack");
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700891 goto exit;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800892 }
893 }
894
895 if (toRead > 0) {
896 AudioRecord::Buffer buffer;
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800897 buffer.frameCount = toRead;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800898
899 status_t status = record.obtainBuffer(&buffer, 1);
900 if (status == NO_ERROR) {
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800901 int offset = sampleCount - toRead;
902 memcpy(&input[offset], buffer.i8, buffer.size);
903 toRead -= buffer.frameCount;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800904 record.releaseBuffer(&buffer);
905 } else if (status != TIMED_OUT && status != WOULD_BLOCK) {
906 LOGE("cannot read from AudioRecord");
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700907 goto exit;
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800908 }
909 }
910 }
911
912 if (chances <= 0) {
Chia-chi Yeh67ecb5b2010-10-01 08:20:09 +0800913 LOGW("device loop timeout");
914 while (recv(deviceSocket, &c, 1, MSG_DONTWAIT) == 1);
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800915 }
916
917 if (mode != MUTED) {
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700918 if (echo != NULL) {
919 LOGV("echo->run()");
920 echo->run(output, input);
Chia-chi Yeh9083c842010-09-29 05:19:44 +0800921 }
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700922 send(deviceSocket, input, sizeof(input), MSG_DONTWAIT);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800923 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800924 }
Eric Laurent5fb3ba62011-07-25 12:02:16 -0700925
926exit:
927 delete echo;
928 delete aec;
929 return true;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800930}
931
932//------------------------------------------------------------------------------
933
934static jfieldID gNative;
935static jfieldID gMode;
936
Chia-chi Yehb8790322010-08-19 18:26:53 +0800937void add(JNIEnv *env, jobject thiz, jint mode,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800938 jint socket, jstring jRemoteAddress, jint remotePort,
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800939 jstring jCodecSpec, jint dtmfType)
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800940{
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800941 AudioCodec *codec = NULL;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800942 AudioStream *stream = NULL;
943 AudioGroup *group = NULL;
944
945 // Sanity check.
946 sockaddr_storage remote;
947 if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
948 // Exception already thrown.
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800949 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800950 }
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800951 if (!jCodecSpec) {
952 jniThrowNullPointerException(env, "codecSpec");
953 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800954 }
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800955 const char *codecSpec = env->GetStringUTFChars(jCodecSpec, NULL);
956 if (!codecSpec) {
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800957 // Exception already thrown.
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800958 return;
959 }
960
961 // Create audio codec.
962 int codecType = -1;
963 char codecName[16];
964 int sampleRate = -1;
Chia-chi Yeh3cf71372011-01-04 19:10:06 +0800965 sscanf(codecSpec, "%d %15[^/]%*c%d", &codecType, codecName, &sampleRate);
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800966 codec = newAudioCodec(codecName);
967 int sampleCount = (codec ? codec->set(sampleRate, codecSpec) : -1);
968 env->ReleaseStringUTFChars(jCodecSpec, codecSpec);
969 if (sampleCount <= 0) {
970 jniThrowException(env, "java/lang/IllegalStateException",
971 "cannot initialize audio codec");
Chia-chi Yehb8790322010-08-19 18:26:53 +0800972 goto error;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800973 }
974
975 // Create audio stream.
976 stream = new AudioStream;
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800977 if (!stream->set(mode, socket, &remote, codec, sampleRate, sampleCount,
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800978 codecType, dtmfType)) {
979 jniThrowException(env, "java/lang/IllegalStateException",
980 "cannot initialize audio stream");
981 goto error;
982 }
983 socket = -1;
Chia-chi Yeh4033a672010-09-16 18:36:45 +0800984 codec = NULL;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +0800985
986 // Create audio group.
987 group = (AudioGroup *)env->GetIntField(thiz, gNative);
988 if (!group) {
989 int mode = env->GetIntField(thiz, gMode);
990 group = new AudioGroup;
991 if (!group->set(8000, 256) || !group->setMode(mode)) {
992 jniThrowException(env, "java/lang/IllegalStateException",
993 "cannot initialize audio group");
994 goto error;
995 }
996 }
997
998 // Add audio stream into audio group.
999 if (!group->add(stream)) {
1000 jniThrowException(env, "java/lang/IllegalStateException",
1001 "cannot add audio stream");
1002 goto error;
1003 }
1004
1005 // Succeed.
1006 env->SetIntField(thiz, gNative, (int)group);
Chia-chi Yehb8790322010-08-19 18:26:53 +08001007 return;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001008
1009error:
1010 delete group;
1011 delete stream;
Chia-chi Yeh4033a672010-09-16 18:36:45 +08001012 delete codec;
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001013 close(socket);
1014 env->SetIntField(thiz, gNative, NULL);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001015}
1016
1017void remove(JNIEnv *env, jobject thiz, jint socket)
1018{
1019 AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
1020 if (group) {
1021 if (socket == -1 || !group->remove(socket)) {
1022 delete group;
1023 env->SetIntField(thiz, gNative, NULL);
1024 }
1025 }
1026}
1027
1028void setMode(JNIEnv *env, jobject thiz, jint mode)
1029{
1030 AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
1031 if (group && !group->setMode(mode)) {
1032 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001033 }
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001034}
1035
1036void sendDtmf(JNIEnv *env, jobject thiz, jint event)
1037{
1038 AudioGroup *group = (AudioGroup *)env->GetIntField(thiz, gNative);
1039 if (group && !group->sendDtmf(event)) {
1040 jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1041 }
1042}
1043
1044JNINativeMethod gMethods[] = {
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +08001045 {"nativeAdd", "(IILjava/lang/String;ILjava/lang/String;I)V", (void *)add},
1046 {"nativeRemove", "(I)V", (void *)remove},
1047 {"nativeSetMode", "(I)V", (void *)setMode},
1048 {"nativeSendDtmf", "(I)V", (void *)sendDtmf},
Chia-chi Yeh4c5d28c2010-08-06 14:12:05 +08001049};
1050
1051} // namespace
1052
1053int registerAudioGroup(JNIEnv *env)
1054{
1055 gRandom = open("/dev/urandom", O_RDONLY);
1056 if (gRandom == -1) {
1057 LOGE("urandom: %s", strerror(errno));
1058 return -1;
1059 }
1060
1061 jclass clazz;
1062 if ((clazz = env->FindClass("android/net/rtp/AudioGroup")) == NULL ||
1063 (gNative = env->GetFieldID(clazz, "mNative", "I")) == NULL ||
1064 (gMode = env->GetFieldID(clazz, "mMode", "I")) == NULL ||
1065 env->RegisterNatives(clazz, gMethods, NELEM(gMethods)) < 0) {
1066 LOGE("JNI registration failed");
1067 return -1;
1068 }
1069 return 0;
1070}