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