198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/* 298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Copyright (C) 2010 The Android Open Source Project 398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Licensed under the Apache License, Version 2.0 (the "License"); 598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * you may not use this file except in compliance with the License. 698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * You may obtain a copy of the License at 798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * http://www.apache.org/licenses/LICENSE-2.0 998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 1098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Unless required by applicable law or agreed to in writing, software 1198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * distributed under the License is distributed on an "AS IS" BASIS, 1298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * See the License for the specific language governing permissions and 1498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * limitations under the License. 1598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */ 1698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangpackage android.net.rtp; 1898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 190a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yehimport android.media.AudioManager; 200a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh 2198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.util.HashMap; 22fe2164c76a5715e0a8106aacfd78a372d510bc04Johan Redestigimport java.util.Locale; 2398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangimport java.util.Map; 2498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 2598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang/** 260a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * An AudioGroup is an audio hub for the speaker, the microphone, and 270a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * {@link AudioStream}s. Each of these components can be logically turned on 280a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}. 290a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * The AudioGroup will go through these components and process them one by one 300a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * within its execution loop. The loop consists of four steps. First, for each 310a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming 320a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * packets and stores in its buffer. Then, if the microphone is enabled, 330a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * processes the recorded audio and stores in its buffer. Third, if the speaker 340a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * is enabled, mixes all AudioStream buffers and plays back. Finally, for each 350a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other 360a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * buffers and sends back the encoded packets. An AudioGroup does nothing if 370a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * there is no AudioStream in it. 387f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * 397f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * <p>Few things must be noticed before using these classes. The performance is 407f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * highly related to the system load and the network bandwidth. Usually a 417f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network 420a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * bandwidth, and vise versa. Using two AudioStreams at the same time doubles 430a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * not only the load but also the bandwidth. The condition varies from one 440a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * device to another, and developers should choose the right combination in 450a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * order to get the best result.</p> 467f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * 477f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For 487f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * example, a Voice over IP (VoIP) application might want to put a conference 497f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * call on hold in order to make a new call but still allow people in the 500a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * conference call talking to each other. This can be done easily using two 517f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * AudioGroups, but there are some limitations. Since the speaker and the 520a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * microphone are globally shared resources, only one AudioGroup at a time is 530a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will 540a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * be unable to acquire these resources and fail silently.</p> 55a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh * 56a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh * <p class="note">Using this class requires 570a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers 580a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION} 590a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * using {@link AudioManager#setMode(int)} and change it back when none of 600a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * the AudioGroups is in use.</p> 61a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh * 62a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh * @see AudioStream 6398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */ 6498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wangpublic class AudioGroup { 657f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 667f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * This mode is similar to {@link #MODE_NORMAL} except the speaker and 670a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * the microphone are both disabled. 687f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 6998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public static final int MODE_ON_HOLD = 0; 707f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh 717f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 727f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * This mode is similar to {@link #MODE_NORMAL} except the microphone is 730a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh * disabled. 747f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 7598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public static final int MODE_MUTED = 1; 767f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh 777f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 787f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * This mode indicates that the speaker, the microphone, and all 797f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * {@link AudioStream}s in the group are enabled. First, the packets 807f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * received from the streams are decoded and mixed with the audio recorded 817f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * from the microphone. Then, the results are played back to the speaker, 827f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * encoded and sent back to each stream. 837f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 8498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public static final int MODE_NORMAL = 2; 857f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh 867f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 877f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * This mode is similar to {@link #MODE_NORMAL} except the echo suppression 887f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * is enabled. It should be only used when the speaker phone is on. 897f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 907f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh public static final int MODE_ECHO_SUPPRESSION = 3; 9198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 92a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh private static final int MODE_LAST = 3; 93a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh 94ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat private final Map<AudioStream, Long> mStreams; 9598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang private int mMode = MODE_ON_HOLD; 9698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 97ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat private long mNative; 9898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang static { 9998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang System.loadLibrary("rtp_jni"); 10098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 10198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1027f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 1037f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * Creates an empty AudioGroup. 1047f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 10598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public AudioGroup() { 106ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat mStreams = new HashMap<AudioStream, Long>(); 10798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 10898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1097f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 110a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh * Returns the {@link AudioStream}s in this group. 111a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh */ 112a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh public AudioStream[] getStreams() { 113a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh synchronized (this) { 114a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh return mStreams.keySet().toArray(new AudioStream[mStreams.size()]); 115a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 116a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 117a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh 118a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh /** 1197f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * Returns the current mode. 1207f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 12198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang public int getMode() { 12298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang return mMode; 12398cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 12498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1257f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 1267f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * Changes the current mode. It must be one of {@link #MODE_ON_HOLD}, 1277f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * {@link #MODE_MUTED}, {@link #MODE_NORMAL}, and 1287f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * {@link #MODE_ECHO_SUPPRESSION}. 1297f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * 1307f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * @param mode The mode to change to. 1317f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * @throws IllegalArgumentException if the mode is invalid. 1327f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 133a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh public void setMode(int mode) { 134a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh if (mode < 0 || mode > MODE_LAST) { 135a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh throw new IllegalArgumentException("Invalid mode"); 136a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 137a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh synchronized (this) { 138a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh nativeSetMode(mode); 139a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh mMode = mode; 140a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 141a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 1427f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh 143a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh private native void nativeSetMode(int mode); 144a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh 145a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh // Package-private method used by AudioStream.join(). 1462bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh synchronized void add(AudioStream stream) { 1470a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh if (!mStreams.containsKey(stream)) { 1480a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh try { 1492bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh AudioCodec codec = stream.getCodec(); 150fe2164c76a5715e0a8106aacfd78a372d510bc04Johan Redestig String codecSpec = String.format(Locale.US, "%d %s %s", codec.type, 1510a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh codec.rtpmap, codec.fmtp); 152ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat long id = nativeAdd(stream.getMode(), stream.getSocket(), 1530a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh stream.getRemoteAddress().getHostAddress(), 1542bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh stream.getRemotePort(), codecSpec, stream.getDtmfType()); 1552bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh mStreams.put(stream, id); 1560a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh } catch (NullPointerException e) { 1570a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh throw new IllegalStateException(e); 15898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 15998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 16098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 16198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 162ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat private native long nativeAdd(int mode, int socket, String remoteAddress, 163a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh int remotePort, String codecSpec, int dtmfType); 16498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 165a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh // Package-private method used by AudioStream.join(). 1660a3e1f1851b26657b865ae3c91200c59dfc411feChia-chi Yeh synchronized void remove(AudioStream stream) { 167ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat Long id = mStreams.remove(stream); 1682bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh if (id != null) { 1692bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh nativeRemove(id); 17098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 17198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 17298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 173ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat private native void nativeRemove(long id); 174a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh 17598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang /** 17698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * Sends a DTMF digit to every {@link AudioStream} in this group. Currently 17798cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * only event {@code 0} to {@code 15} are supported. 17898cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * 17998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang * @throws IllegalArgumentException if the event is invalid. 18098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang */ 181a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh public void sendDtmf(int event) { 182a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh if (event < 0 || event > 15) { 183a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh throw new IllegalArgumentException("Invalid event"); 184a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 185a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh synchronized (this) { 186a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh nativeSendDtmf(event); 187a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 188a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 189a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh 190a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh private native void nativeSendDtmf(int event); 19198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 1927f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh /** 1937f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh * Removes every {@link AudioStream} in this group. 1947f383063fafc54a41f91540a41bf987003fd2502Chia-chi Yeh */ 195a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh public void clear() { 1962bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh for (AudioStream stream : getStreams()) { 1972bf2e642d061e7b48dd71927752e9151a5126fb2Chia-chi Yeh stream.join(null); 198a54e36694b3909ef367867eb7516b2d6f6031261Chia-chi Yeh } 19998cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 20098cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang 20198cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang @Override 20298cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang protected void finalize() throws Throwable { 203ae710cd10213c0a89e9daea945f3efa9f90b188eAshok Bhat nativeRemove(0L); 20498cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang super.finalize(); 20598cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang } 20698cee0ce2354234e72bafb836864ec10a490ea4dChung-yih Wang} 207