Home | History | Annotate | Download | only in rtp
      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      */
     92     public AudioGroup() {
     93         mStreams = new HashMap<AudioStream, Integer>();
     94     }
     95 
     96     /**
     97      * Returns the current mode.
     98      */
     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      */
    111     public synchronized native void setMode(int mode);
    112 
    113     private native void add(int mode, int socket, String remoteAddress,
    114             int remotePort, String codecSpec, int dtmfType);
    115 
    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 
    132     private native void remove(int socket);
    133 
    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      */
    147     public native synchronized void sendDtmf(int event);
    148 
    149     /**
    150      * Removes every {@link AudioStream} in this group.
    151      */
    152     public synchronized void clear() {
    153         remove(-1);
    154     }
    155 
    156     @Override
    157     protected void finalize() throws Throwable {
    158         clear();
    159         super.finalize();
    160     }
    161 }
    162