概述
Android的voip功能支持位于目录frameworks/base/voip中。它包括支持rtp功能的package
RTP支持
RTP支持包位于目录frameworks/base/voip/java/android/net/rtp下,主要包含四个Java类:代表着基于RTP协议的流RtpStream、基于RTP协议的语音流AudioStream、描述了语音Codec信息的AudioCodec和语音会话组的AudioGroup、。
RTP流:RtpStream
它是基于RTP(Real-time Transport Protocol)协议的数据流。Java层的API类是android.net.rtp.RtpStream,代表着一个通过RTP协议发送和接收网络多媒体数据包的流。一个流主要包括本机网络地址和端口号、远程主机网络地址和端口号、socket号和流模式。
RtpStream支持三种流模式,可由setMode函数设定:
MODE_NORMAL:正常模式,接收和发送数据包
MODE_SEND_ONLY: 只发送数据包
MODE_RECEIVE_ONLY:只接收数据包
本地主机IP地址(InetAddress,支持IPv4和IPv6)由调用构造函数时传入。在构造函数中,会调用native层实现的create函数获取一个本地主机端口号(依据RFC 3550);同时,native层的create函数还会得到一个socket连接号,socket号会在native层中更新到该Java类实例中。
远程主机地址和端口号由函数associate指定:
public void associate(InetAddress address, int port)
获取socket号的过程如下:
android.net.rtp.RtpStream的一个私有成员整型变量mNative存放的是socket号:
private int mNative;
它由JNI层在调用socket函数后得到一个socket号后存入里面。具体如下:在JNI层(frameworks/base/voip/jni/rtp/RtpStream.cpp)中标识它的变量是:
jfieldID gNative;
在函数registerRtpStream中获取具体的值:
(gNative = env->GetFieldID(clazz, “mNative”, “I”)) == NULL ||
在JNI层的create函数中,会调用socket函数得到一个socket号:
int socket = ::socket(ss.ss_family, SOCK_DGRAM, 0);
这个socket号会被指定给Java层的mNative变量:
env->SetIntField(thiz, gNative, socket);
端口号port则由create函数直接返回。create函数已经支持IPv6。
语音流:AudioStream
android.net.rtp.AudioStream继承自RtpStream,代表着一个建立在RTP协议之上的与对方通话的语音流。语音流需要使用一个语音Codec来描述其对应的编解码信息。在建立通话之前,语音流需加入(join)到会话组android.net.rtp.AudioGroup中。因此,它包含了:所在的语音组、语音Codec以及DTMF(Dual-Tone Multi-Frequency)类型(RFC 2833)等信息。
语音Codec:AudioCodec
一个android.net.rtp.AudioStream需要有一个android.net.rtp.AudioCodec为其编解码。Java层的AudioCodec只是描述了Codec信息的类,主要包含了三样信息:
public final int type; //The RTP payload type of the encoding.
public final String rtpmap; //The encoding parameters to be used in the corresponding SDP attribute.
public final String fmtp; //The format parameters to be used in the corresponding SDP attribute.
可以使用AudioCodec.getCodec轻松得到一个Codec:
public static AudioCodec getCodec(int type, String rtpmap, String fmtp)
为方便使用,在AudioCodec中Android定义了常用的几个Codec:PCMU、PCMA、GSM、GSM_EFR和AMR。
语音组:AudioGroup
android.net.rtp.AudioGroup代表的是一个会话,可能只是两人通话,也可能是多于两人的电话会议。可以同时有多组会话,因为麦克和扬声器只能是排他性使用,故只能有一组会话为活动的,其它必须是HOLD状态。
语音组通过一个映射表来维护加入它里面的语音流:
private final Map<AudioStream, Integer> mStreams;
一个AudioStream加入到AudioGroup的流程如下:
首先是AudioStream调用join加入到某个AudioGroup中:
public void join(AudioGroup group)
然后调用AudioGroup.add,接着调用:
private native void