1 /* 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 package android.net.rtp; 18 19 import java.util.HashMap; 20 import java.util.Map; 21 22 /** 23 * An AudioGroup acts as a router connected to the speaker, the microphone, and 24 * {@link AudioStream}s. Its pipeline has four steps. First, for each 25 * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming 26 * packets and stores in its buffer. Then, if the microphone is enabled, 27 * processes the recorded audio and stores in its buffer. Third, if the speaker 28 * is enabled, mixes and playbacks buffers of all AudioStreams. Finally, for 29 * each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other 30 * buffers and sends back the encoded packets. An AudioGroup does nothing if 31 * there is no AudioStream in it. 32 * 33 * <p>Few things must be noticed before using these classes. The performance is 34 * highly related to the system load and the network bandwidth. Usually a 35 * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network 36 * bandwidth, and vise versa. Using two AudioStreams at the same time not only 37 * doubles the load but also the bandwidth. The condition varies from one device 38 * to another, and developers must choose the right combination in order to get 39 * the best result. 40 * 41 * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For 42 * example, a Voice over IP (VoIP) application might want to put a conference 43 * call on hold in order to make a new call but still allow people in the 44 * previous call to talk to each other. This can be done easily using two 45 * AudioGroups, but there are some limitations. Since the speaker and the 46 * microphone are shared globally, only one AudioGroup is allowed to run in 47 * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an 48 * AudioStream into an AudioGroup, one should always put all other AudioGroups 49 * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly 50 * initialized. 51 * @hide 52 */ 53 public class AudioGroup { 54 /** 55 * This mode is similar to {@link #MODE_NORMAL} except the speaker and 56 * the microphone are disabled. 57 */ 58 public static final int MODE_ON_HOLD = 0; 59 60 /** 61 * This mode is similar to {@link #MODE_NORMAL} except the microphone is 62 * muted. 63 */ 64 public static final int MODE_MUTED = 1; 65 66 /** 67 * This mode indicates that the speaker, the microphone, and all 68 * {@link AudioStream}s in the group are enabled. First, the packets 69 * received from the streams are decoded and mixed with the audio recorded 70 * from the microphone. Then, the results are played back to the speaker, 71 * encoded and sent back to each stream. 72 */ 73 public static final int MODE_NORMAL = 2; 74 75 /** 76 * This mode is similar to {@link #MODE_NORMAL} except the echo suppression 77 * is enabled. It should be only used when the speaker phone is on. 78 */ 79 public static final int MODE_ECHO_SUPPRESSION = 3; 80 81 private final Map<AudioStream, Integer> mStreams; 82 private int mMode = MODE_ON_HOLD; 83 84 private int mNative; 85 static { 86 System.loadLibrary("rtp_jni"); 87 } 88 89 /** 90 * Creates an empty AudioGroup. 91 */ AudioGroup()92 public AudioGroup() { 93 mStreams = new HashMap<AudioStream, Integer>(); 94 } 95 96 /** 97 * Returns the current mode. 98 */ getMode()99 public int getMode() { 100 return mMode; 101 } 102 103 /** 104 * Changes the current mode. It must be one of {@link #MODE_ON_HOLD}, 105 * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and 106 * {@link #MODE_ECHO_SUPPRESSION}. 107 * 108 * @param mode The mode to change to. 109 * @throws IllegalArgumentException if the mode is invalid. 110 */ setMode(int mode)111 public synchronized native void setMode(int mode); 112 add(int mode, int socket, String remoteAddress, int remotePort, String codecSpec, int dtmfType)113 private native void add(int mode, int socket, String remoteAddress, 114 int remotePort, String codecSpec, int dtmfType); 115 add(AudioStream stream, AudioCodec codec, int dtmfType)116 synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) { 117 if (!mStreams.containsKey(stream)) { 118 try { 119 int socket = stream.dup(); 120 String codecSpec = String.format("%d %s %s", codec.type, 121 codec.rtpmap, codec.fmtp); 122 add(stream.getMode(), socket, 123 stream.getRemoteAddress().getHostAddress(), 124 stream.getRemotePort(), codecSpec, dtmfType); 125 mStreams.put(stream, socket); 126 } catch (NullPointerException e) { 127 throw new IllegalStateException(e); 128 } 129 } 130 } 131 remove(int socket)132 private native void remove(int socket); 133 remove(AudioStream stream)134 synchronized void remove(AudioStream stream) { 135 Integer socket = mStreams.remove(stream); 136 if (socket != null) { 137 remove(socket); 138 } 139 } 140 141 /** 142 * Sends a DTMF digit to every {@link AudioStream} in this group. Currently 143 * only event {@code 0} to {@code 15} are supported. 144 * 145 * @throws IllegalArgumentException if the event is invalid. 146 */ sendDtmf(int event)147 public native synchronized void sendDtmf(int event); 148 149 /** 150 * Removes every {@link AudioStream} in this group. 151 */ clear()152 public synchronized void clear() { 153 remove(-1); 154 } 155 156 @Override finalize()157 protected void finalize() throws Throwable { 158 clear(); 159 super.finalize(); 160 } 161 } 162