blob: 8c190621934fbb3926360972f75eee2e0d4b3c26 [file] [log] [blame]
Chung-yih Wang363c2ab2010-08-05 10:21:20 +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
17package android.net.rtp;
18
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080019import android.media.AudioManager;
20
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080021import java.util.HashMap;
22import java.util.Map;
23
24/**
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080025 * An AudioGroup is an audio hub for the speaker, the microphone, and
26 * {@link AudioStream}s. Each of these components can be logically turned on
27 * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}.
28 * The AudioGroup will go through these components and process them one by one
29 * within its execution loop. The loop consists of four steps. First, for each
30 * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
31 * packets and stores in its buffer. Then, if the microphone is enabled,
32 * processes the recorded audio and stores in its buffer. Third, if the speaker
33 * is enabled, mixes all AudioStream buffers and plays back. Finally, for each
34 * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
35 * buffers and sends back the encoded packets. An AudioGroup does nothing if
36 * there is no AudioStream in it.
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080037 *
38 * <p>Few things must be noticed before using these classes. The performance is
39 * highly related to the system load and the network bandwidth. Usually a
40 * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080041 * bandwidth, and vise versa. Using two AudioStreams at the same time doubles
42 * not only the load but also the bandwidth. The condition varies from one
43 * device to another, and developers should choose the right combination in
44 * order to get the best result.</p>
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080045 *
46 * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
47 * example, a Voice over IP (VoIP) application might want to put a conference
48 * call on hold in order to make a new call but still allow people in the
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080049 * conference call talking to each other. This can be done easily using two
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080050 * AudioGroups, but there are some limitations. Since the speaker and the
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080051 * microphone are globally shared resources, only one AudioGroup at a time is
52 * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will
53 * be unable to acquire these resources and fail silently.</p>
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +080054 *
55 * <p class="note">Using this class requires
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080056 * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers
57 * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION}
58 * using {@link AudioManager#setMode(int)} and change it back when none of
59 * the AudioGroups is in use.</p>
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +080060 *
61 * @see AudioStream
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080062 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080063public class AudioGroup {
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080064 /**
65 * This mode is similar to {@link #MODE_NORMAL} except the speaker and
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080066 * the microphone are both disabled.
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080067 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080068 public static final int MODE_ON_HOLD = 0;
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080069
70 /**
71 * This mode is similar to {@link #MODE_NORMAL} except the microphone is
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080072 * disabled.
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080073 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080074 public static final int MODE_MUTED = 1;
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080075
76 /**
77 * This mode indicates that the speaker, the microphone, and all
78 * {@link AudioStream}s in the group are enabled. First, the packets
79 * received from the streams are decoded and mixed with the audio recorded
80 * from the microphone. Then, the results are played back to the speaker,
81 * encoded and sent back to each stream.
82 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080083 public static final int MODE_NORMAL = 2;
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080084
85 /**
86 * This mode is similar to {@link #MODE_NORMAL} except the echo suppression
87 * is enabled. It should be only used when the speaker phone is on.
88 */
89 public static final int MODE_ECHO_SUPPRESSION = 3;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080090
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +080091 private static final int MODE_LAST = 3;
92
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080093 private final Map<AudioStream, Integer> mStreams;
94 private int mMode = MODE_ON_HOLD;
95
96 private int mNative;
97 static {
98 System.loadLibrary("rtp_jni");
99 }
100
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800101 /**
102 * Creates an empty AudioGroup.
103 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800104 public AudioGroup() {
105 mStreams = new HashMap<AudioStream, Integer>();
106 }
107
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800108 /**
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800109 * Returns the {@link AudioStream}s in this group.
110 */
111 public AudioStream[] getStreams() {
112 synchronized (this) {
113 return mStreams.keySet().toArray(new AudioStream[mStreams.size()]);
114 }
115 }
116
117 /**
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800118 * Returns the current mode.
119 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800120 public int getMode() {
121 return mMode;
122 }
123
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800124 /**
125 * Changes the current mode. It must be one of {@link #MODE_ON_HOLD},
126 * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and
127 * {@link #MODE_ECHO_SUPPRESSION}.
128 *
129 * @param mode The mode to change to.
130 * @throws IllegalArgumentException if the mode is invalid.
131 */
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800132 public void setMode(int mode) {
133 if (mode < 0 || mode > MODE_LAST) {
134 throw new IllegalArgumentException("Invalid mode");
135 }
136 synchronized (this) {
137 nativeSetMode(mode);
138 mMode = mode;
139 }
140 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800141
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800142 private native void nativeSetMode(int mode);
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800143
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800144 // Package-private method used by AudioStream.join().
Chia-chi Yehe6695052012-03-30 13:25:19 -0700145 synchronized void add(AudioStream stream) {
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800146 if (!mStreams.containsKey(stream)) {
147 try {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700148 AudioCodec codec = stream.getCodec();
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800149 String codecSpec = String.format("%d %s %s", codec.type,
150 codec.rtpmap, codec.fmtp);
Chia-chi Yehe6695052012-03-30 13:25:19 -0700151 int id = nativeAdd(stream.getMode(), stream.getSocket(),
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800152 stream.getRemoteAddress().getHostAddress(),
Chia-chi Yehe6695052012-03-30 13:25:19 -0700153 stream.getRemotePort(), codecSpec, stream.getDtmfType());
154 mStreams.put(stream, id);
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800155 } catch (NullPointerException e) {
156 throw new IllegalStateException(e);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800157 }
158 }
159 }
160
Chia-chi Yehe6695052012-03-30 13:25:19 -0700161 private native int nativeAdd(int mode, int socket, String remoteAddress,
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800162 int remotePort, String codecSpec, int dtmfType);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800163
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800164 // Package-private method used by AudioStream.join().
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800165 synchronized void remove(AudioStream stream) {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700166 Integer id = mStreams.remove(stream);
167 if (id != null) {
168 nativeRemove(id);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800169 }
170 }
171
Chia-chi Yehe6695052012-03-30 13:25:19 -0700172 private native void nativeRemove(int id);
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800173
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800174 /**
175 * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
176 * only event {@code 0} to {@code 15} are supported.
177 *
178 * @throws IllegalArgumentException if the event is invalid.
179 */
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800180 public void sendDtmf(int event) {
181 if (event < 0 || event > 15) {
182 throw new IllegalArgumentException("Invalid event");
183 }
184 synchronized (this) {
185 nativeSendDtmf(event);
186 }
187 }
188
189 private native void nativeSendDtmf(int event);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800190
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800191 /**
192 * Removes every {@link AudioStream} in this group.
193 */
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800194 public void clear() {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700195 for (AudioStream stream : getStreams()) {
196 stream.join(null);
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800197 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800198 }
199
200 @Override
201 protected void finalize() throws Throwable {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700202 nativeRemove(0);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800203 super.finalize();
204 }
205}