blob: 8faeb88db6e95920c2490eb43d9538b524410f21 [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;
Johan Redestig9b17d0a2012-08-28 09:37:23 +020022import java.util.Locale;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080023import java.util.Map;
24
25/**
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080026 * An AudioGroup is an audio hub for the speaker, the microphone, and
27 * {@link AudioStream}s. Each of these components can be logically turned on
28 * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}.
29 * The AudioGroup will go through these components and process them one by one
30 * within its execution loop. The loop consists of four steps. First, for each
31 * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
32 * packets and stores in its buffer. Then, if the microphone is enabled,
33 * processes the recorded audio and stores in its buffer. Third, if the speaker
34 * is enabled, mixes all AudioStream buffers and plays back. Finally, for each
35 * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
36 * buffers and sends back the encoded packets. An AudioGroup does nothing if
37 * there is no AudioStream in it.
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080038 *
39 * <p>Few things must be noticed before using these classes. The performance is
40 * highly related to the system load and the network bandwidth. Usually a
41 * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080042 * bandwidth, and vise versa. Using two AudioStreams at the same time doubles
43 * not only the load but also the bandwidth. The condition varies from one
44 * device to another, and developers should choose the right combination in
45 * order to get the best result.</p>
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080046 *
47 * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
48 * example, a Voice over IP (VoIP) application might want to put a conference
49 * call on hold in order to make a new call but still allow people in the
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080050 * conference call talking to each other. This can be done easily using two
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080051 * AudioGroups, but there are some limitations. Since the speaker and the
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080052 * microphone are globally shared resources, only one AudioGroup at a time is
53 * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will
54 * be unable to acquire these resources and fail silently.</p>
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +080055 *
56 * <p class="note">Using this class requires
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080057 * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers
58 * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION}
59 * using {@link AudioManager#setMode(int)} and change it back when none of
60 * the AudioGroups is in use.</p>
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +080061 *
62 * @see AudioStream
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080063 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080064public class AudioGroup {
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080065 /**
66 * This mode is similar to {@link #MODE_NORMAL} except the speaker and
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080067 * the microphone are both disabled.
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080068 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080069 public static final int MODE_ON_HOLD = 0;
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080070
71 /**
72 * This mode is similar to {@link #MODE_NORMAL} except the microphone is
Chia-chi Yehc52f5b22011-03-03 07:59:00 +080073 * disabled.
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080074 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080075 public static final int MODE_MUTED = 1;
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080076
77 /**
78 * This mode indicates that the speaker, the microphone, and all
79 * {@link AudioStream}s in the group are enabled. First, the packets
80 * received from the streams are decoded and mixed with the audio recorded
81 * from the microphone. Then, the results are played back to the speaker,
82 * encoded and sent back to each stream.
83 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080084 public static final int MODE_NORMAL = 2;
Chia-chi Yeh32e106b2010-09-16 09:56:38 +080085
86 /**
87 * This mode is similar to {@link #MODE_NORMAL} except the echo suppression
88 * is enabled. It should be only used when the speaker phone is on.
89 */
90 public static final int MODE_ECHO_SUPPRESSION = 3;
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080091
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +080092 private static final int MODE_LAST = 3;
93
Chung-yih Wang363c2ab2010-08-05 10:21:20 +080094 private final Map<AudioStream, Integer> mStreams;
95 private int mMode = MODE_ON_HOLD;
96
97 private int mNative;
98 static {
99 System.loadLibrary("rtp_jni");
100 }
101
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800102 /**
103 * Creates an empty AudioGroup.
104 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800105 public AudioGroup() {
106 mStreams = new HashMap<AudioStream, Integer>();
107 }
108
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800109 /**
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800110 * Returns the {@link AudioStream}s in this group.
111 */
112 public AudioStream[] getStreams() {
113 synchronized (this) {
114 return mStreams.keySet().toArray(new AudioStream[mStreams.size()]);
115 }
116 }
117
118 /**
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800119 * Returns the current mode.
120 */
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800121 public int getMode() {
122 return mMode;
123 }
124
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800125 /**
126 * Changes the current mode. It must be one of {@link #MODE_ON_HOLD},
127 * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and
128 * {@link #MODE_ECHO_SUPPRESSION}.
129 *
130 * @param mode The mode to change to.
131 * @throws IllegalArgumentException if the mode is invalid.
132 */
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800133 public void setMode(int mode) {
134 if (mode < 0 || mode > MODE_LAST) {
135 throw new IllegalArgumentException("Invalid mode");
136 }
137 synchronized (this) {
138 nativeSetMode(mode);
139 mMode = mode;
140 }
141 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800142
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800143 private native void nativeSetMode(int mode);
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800144
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800145 // Package-private method used by AudioStream.join().
Chia-chi Yehe6695052012-03-30 13:25:19 -0700146 synchronized void add(AudioStream stream) {
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800147 if (!mStreams.containsKey(stream)) {
148 try {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700149 AudioCodec codec = stream.getCodec();
Johan Redestig9b17d0a2012-08-28 09:37:23 +0200150 String codecSpec = String.format(Locale.US, "%d %s %s", codec.type,
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800151 codec.rtpmap, codec.fmtp);
Chia-chi Yehe6695052012-03-30 13:25:19 -0700152 int id = nativeAdd(stream.getMode(), stream.getSocket(),
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800153 stream.getRemoteAddress().getHostAddress(),
Chia-chi Yehe6695052012-03-30 13:25:19 -0700154 stream.getRemotePort(), codecSpec, stream.getDtmfType());
155 mStreams.put(stream, id);
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800156 } catch (NullPointerException e) {
157 throw new IllegalStateException(e);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800158 }
159 }
160 }
161
Chia-chi Yehe6695052012-03-30 13:25:19 -0700162 private native int nativeAdd(int mode, int socket, String remoteAddress,
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800163 int remotePort, String codecSpec, int dtmfType);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800164
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800165 // Package-private method used by AudioStream.join().
Chia-chi Yehc52f5b22011-03-03 07:59:00 +0800166 synchronized void remove(AudioStream stream) {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700167 Integer id = mStreams.remove(stream);
168 if (id != null) {
169 nativeRemove(id);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800170 }
171 }
172
Chia-chi Yehe6695052012-03-30 13:25:19 -0700173 private native void nativeRemove(int id);
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800174
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800175 /**
176 * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
177 * only event {@code 0} to {@code 15} are supported.
178 *
179 * @throws IllegalArgumentException if the event is invalid.
180 */
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800181 public void sendDtmf(int event) {
182 if (event < 0 || event > 15) {
183 throw new IllegalArgumentException("Invalid event");
184 }
185 synchronized (this) {
186 nativeSendDtmf(event);
187 }
188 }
189
190 private native void nativeSendDtmf(int event);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800191
Chia-chi Yeh32e106b2010-09-16 09:56:38 +0800192 /**
193 * Removes every {@link AudioStream} in this group.
194 */
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800195 public void clear() {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700196 for (AudioStream stream : getStreams()) {
197 stream.join(null);
Chia-chi Yeh53aa6ef2010-11-30 13:10:31 +0800198 }
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800199 }
200
201 @Override
202 protected void finalize() throws Throwable {
Chia-chi Yehe6695052012-03-30 13:25:19 -0700203 nativeRemove(0);
Chung-yih Wang363c2ab2010-08-05 10:21:20 +0800204 super.finalize();
205 }
206}