研究了SipDroid2.7,自己对它的理解也渐渐的清晰了。
那它是怎样实现电话拨打以及电话监听的?它的音频接收以及发送是怎么实现的?它的视频又是怎么一回事?它在模拟器上的端口为什么总是变化的?它又是如何处理登陆超时以及通话出错的?
带着这些疑问进入它的代码思想境界!
使用yate搭配服务器,然后使用了一个yate与SipDroid客户端进行通话!~至于怎么搭配服务器以及SipDroid的配置设置,此处就不讨论了!~
登陆后的标识效果如图:
然后我使用了yate客户端程序拨打了SipDroid程序段上的帐号(banketree),如图:
接通后的效果图像如图:
好了,进入我们的主题了!~
它是怎样实现电话拨打以及电话监听的?
程序进入时会进行服务注册,如下:
- // 实例化一个引擎 注册模式 由登陆时进行……
- / Receiver.engine(this).registerMore();
// 实例化一个引擎 注册模式 由登陆时进行……
// Receiver.engine(this).registerMore();
我们知道Receiver.engine(this) 进行了SipUA引擎的实例化,并开启SipUA引擎,关键就SipUA引擎了!~
- // 构造引擎
- public static synchronized SipdroidEngine engine(Context context)
- {
- // 构造引擎的条件
- if (mContext == null
- || !context.getClass().getName()
- .contains("ReceiverRestrictedContext"))
- {
- mContext = context;
- }
- // 为空则构造
- if (mSipdroidEngine == null)
- {
- mSipdroidEngine = new SipdroidEngine();
- // 开始
- mSipdroidEngine.StartEngine();
- // 开启蓝牙服务
- if (Integer.parseInt(Build.VERSION.SDK) >= 8)
- {
- Bluetooth.init();
- }
- } else
- {
- // 引擎已经存在
- mSipdroidEngine.CheckEngine();
- }
- // 开启服务
- context.startService(new Intent(context, RegisterService.class));
- return mSipdroidEngine;
- }
// 构造引擎
public static synchronized SipdroidEngine engine(Context context)
{
// 构造引擎的条件
if (mContext == null
|| !context.getClass().getName()
.contains("ReceiverRestrictedContext"))
{
mContext = context;
}
// 为空则构造
if (mSipdroidEngine == null)
{
mSipdroidEngine = new SipdroidEngine();
// 开始
mSipdroidEngine.StartEngine();
// 开启蓝牙服务
if (Integer.parseInt(Build.VERSION.SDK) >= 8)
{
Bluetooth.init();
}
} else
{
// 引擎已经存在
mSipdroidEngine.CheckEngine();
}
// 开启服务
context.startService(new Intent(context, RegisterService.class));
return mSipdroidEngine;
}
而StartEngine()里有开启监听!~
- //注册 在此向服务器发送请求。
- register();
- //实例化一个ExtendedCall 循环监听指定端口
- listen();
//注册 在此向服务器发送请求。
register();
//实例化一个ExtendedCall 循环监听指定端口
listen();
至于里面是怎么实现的,那就涉及到了SipUA封装的一些类以及消息了!~
消息以及协议的通信都是SipProvider类由完成的!~
- public void onReceivedMessage(Transport transport, Message msg)
- {
- if (pm == null)
- {
- pm = (PowerManager) Receiver.mContext.getSystemService(Context.POWER_SERVICE);
- wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Sipdroid.SipProvider");
- }
- wl.acquire(); // modified
- //处理接收消息 当程序进行了监听,就会向此类添加一个监听事件标识
- //处理消息就是根据标识进行区分的,例如来电、接听、视频等等!~~~
- processReceivedMessage(msg);
- wl.release();
- }
public void onReceivedMessage(Transport transport, Message msg)
{
if (pm == null)
{
pm = (PowerManager) Receiver.mContext.getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Sipdroid.SipProvider");
}
wl.acquire(); // modified
//处理接收消息 当程序进行了监听,就会向此类添加一个监听事件标识
//处理消息就是根据标识进行区分的,例如来电、接听、视频等等!~~~
processReceivedMessage(msg);
wl.release();
}
向服务器发送信息(发送协议进行登陆以及获取对方信息等等)也是由该类完成的!~如下:
- /**
- * Sends a Message, specifing the transport portocol, nexthop address and
- * port.
- * 发送一条消息,指定传输协议、地址、以及端口。
- */
- private ConnectionIdentifier sendMessage(Message msg, String proto, IpAddress dest_ipaddr, int dest_port, int ttl)
- {
- if(bDebug)
- {
- android.util.Log.i("SipProvider发送消息", "msg:"+msg.toString());
- }
- ConnectionIdentifier conn_id = new ConnectionIdentifier(proto, dest_ipaddr, dest_port);
- if (log_all_packets || msg.getLength() > MIN_MESSAGE_LENGTH)
- printLog("Sending message to " + conn_id, LogLevel.MEDIUM);
- if (transport_udp && proto.equals(PROTO_UDP))
- {
- // UDP
- // printLog("using UDP",LogLevel.LOW);
- conn_id = null;
- try
- {
- // if (ttl>0 && multicast_address) do something?
- udp.sendMessage(msg, dest_ipaddr, dest_port);
- } catch (IOException e)
- {
- printException(e, LogLevel.HIGH);
- return null;
- }
- } else if (transport_tcp && proto.equals(PROTO_TCP))
- {
- // TCP
- // printLog("using TCP",LogLevel.LOW);
- if (connections == null || !connections.containsKey(conn_id))
- {
- // modified
- printLog("no active connection found matching " + conn_id, LogLevel.MEDIUM);
- printLog("open " + proto + " connection to " + dest_ipaddr + ":" + dest_port, LogLevel.MEDIUM);
- TcpTransport conn = null;
- try
- {
- conn = new TcpTransport(dest_ipaddr, dest_port, this);
- } catch (Exception e)
- {
- printLog("connection setup FAILED", LogLevel.HIGH);
- return null;
- }
- printLog("connection " + conn + " opened", LogLevel.HIGH);
- addConnection(conn);
- if (!msg.isRegister())
- Receiver.engine(Receiver.mContext).register(); // modified
- } else
- {
- printLog("active connection found matching " + conn_id, LogLevel.MEDIUM);
- }
- ConnectedTransport conn = (ConnectedTransport) connections.get(conn_id);
- if (conn != null)
- {
- printLog("sending data through conn " + conn, LogLevel.MEDIUM);
- try
- {
- conn.sendMessage(msg);
- conn_id = new ConnectionIdentifier(conn);
- } catch (IOException e)
- {
- printException(e, LogLevel.HIGH);
- return null;
- }
- } else
- {
- // this point has not to be reached这一点还没有达到
- printLog("ERROR: conn " + conn_id + " not found: abort.", LogLevel.MEDIUM);
- return null;
- }
- } else
- { // otherwise
- printWarning("Unsupported protocol (" + proto + "): Message discarded", LogLevel.HIGH);
- return null;
- }
- // logs
- String dest_addr = dest_ipaddr.toString();
- printMessageLog(proto, dest_addr, dest_port, msg.getLength(), msg, "sent");
- return conn_id;
- }
/**
* Sends a Message, specifing the transport portocol, nexthop address and
* port.
* 发送一条消息,指定传输协议、地址、以及端口。
*/
private ConnectionIdentifier sendMessage(Message msg, String proto, IpAddress dest_ipaddr, int dest_port, int ttl)
{
if(bDebug)
{
android.util.Log.i("SipProvider发送消息", "msg:"+msg.toString());
}
ConnectionIdentifier conn_id = new ConnectionIdentifier(proto, dest_ipaddr, dest_port);
if (log_all_packets || msg.getLength() > MIN_MESSAGE_LENGTH)
printLog("Sending message to " + conn_id, LogLevel.MEDIUM);
if (transport_udp && proto.equals(PROTO_UDP))
{
// UDP
// printLog("using UDP",LogLevel.LOW);
conn_id = null;
try
{
// if (ttl>0 && multicast_address) do something?
udp.sendMessage(msg, dest_ipaddr, dest_port);
} catch (IOException e)
{
printException(e, LogLevel.HIGH);
return null;
}
} else if (transport_tcp && proto.equals(PROTO_TCP))
{
// TCP
// printLog("using TCP",LogLevel.LOW);
if (connections == null || !connections.containsKey(conn_id))
{
// modified
printLog("no active connection found matching " + conn_id, LogLevel.MEDIUM);
printLog("open " + proto + " connection to " + dest_ipaddr + ":" + dest_port, LogLevel.MEDIUM);
TcpTransport conn = null;
try
{
conn = new TcpTransport(dest_ipaddr, dest_port, this);
} catch (Exception e)
{
printLog("connection setup FAILED", LogLevel.HIGH);
return null;
}
printLog("connection " + conn + " opened", LogLevel.HIGH);
addConnection(conn);
if (!msg.isRegister())
Receiver.engine(Receiver.mContext).register(); // modified
} else
{
printLog("active connection found matching " + conn_id, LogLevel.MEDIUM);
}
ConnectedTransport conn = (ConnectedTransport) connections.get(conn_id);
if (conn != null)
{
printLog("sending data through conn " + conn, LogLevel.MEDIUM);
try
{
conn.sendMessage(msg);
conn_id = new ConnectionIdentifier(conn);
} catch (IOException e)
{
printException(e, LogLevel.HIGH);
return null;
}
} else
{
// this point has not to be reached这一点还没有达到
printLog("ERROR: conn " + conn_id + " not found: abort.", LogLevel.MEDIUM);
return null;
}
} else
{ // otherwise
printWarning("Unsupported protocol (" + proto + "): Message discarded", LogLevel.HIGH);
return null;
}
// logs
String dest_addr = dest_ipaddr.toString();
printMessageLog(proto, dest_addr, dest_port, msg.getLength(), msg, "sent");
return conn_id;
}
消息的信息格式如下:
……
回到问题中来,监听电话已经明白了,那是如何实现拨打的?
当用户登陆后就会把自己的信息发送给服务端,服务端记录下来,等用户需要时就返回给他,比如我打banketree电话,我没有他的信息我怎么打呀,是吧!~
拨打电话的关键在UserAgent中,其它的都是封装好处理信息的类!~
- public boolean call(String target_url, boolean send_anonymous)
- {
- if (Receiver.call_state != UA_STATE_IDLE)
- {
- // We can initiate or terminate a call only when
- // we are in an idle state
- //只有当我们处于闲置状态,我们可以开始或结束通话
- printLog("Call attempted in state" + this.getSessionDescriptor()
- + " : Failing Request", LogLevel.HIGH);
- return false;
- }
- //挂断
- hangup(); // modified
- //改变状态
- changeStatus(UA_STATE_OUTGOING_CALL, target_url);
- String from_url;
- if (!send_anonymous)
- {
- from_url = user_profile.from_url;
- } else
- {
- from_url = "sip:anonymous@anonymous.com";
- }
- // change start multi codecs 更改启动多编解码器
- createOffer();
- // change end
- call = new ExtendedCall(sip_provider, from_url,
- user_profile.contact_url, user_profile.username,
- user_profile.realm, user_profile.passwd, this);
- // in case of incomplete url (e.g. only 'user' is present), try to
- // complete it
- //在不完整的URL(例如,“用户”是存在的话)的情况下,尽量去完成它
- if (target_url.indexOf("@") < 0)
- {
- if (user_profile.realm.equals(Settings.DEFAULT_SERVER))
- target_url = "&" + target_url;
- target_url = target_url + "@" + realm; // modified
- }
- // MMTel addition to define MMTel ICSI to be included in INVITE (added
- // by mandrajg) 要包含在INVITE MMTel除了定义MMTel ICSI
- String icsi = null;
- if (user_profile.mmtel == true)
- {
- icsi = "\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
- }
- //从服务器中获得对方的地址
- target_url = sip_provider.completeNameAddress(target_url).toString();
- //真的拨打电话
- if (user_profile.no_offer)
- {
- call.call(target_url);
- } else
- {
- call.call(target_url, local_session, icsi); // modified by mandrajg
- }
- return true;
- }
public boolean call(String target_url, boolean send_anonymous)
{
if (Receiver.call_state != UA_STATE_IDLE)
{
// We can initiate or terminate a call only when
// we are in an idle state
//只有当我们处于闲置状态,我们可以开始或结束通话
printLog("Call attempted in state" + this.getSessionDescriptor()
+ " : Failing Request", LogLevel.HIGH);
return false;
}
//挂断
hangup(); // modified
//改变状态
changeStatus(UA_STATE_OUTGOING_CALL, target_url);
String from_url;
if (!send_anonymous)
{
from_url = user_profile.from_url;
} else
{
from_url = "sip:anonymous@anonymous.com";
}
// change start multi codecs 更改启动多编解码器
createOffer();
// change end
call = new ExtendedCall(sip_provider, from_url,
user_profile.contact_url, user_profile.username,
user_profile.realm, user_profile.passwd, this);
// in case of incomplete url (e.g. only 'user' is present), try to
// complete it
//在不完整的URL(例如,“用户”是存在的话)的情况下,尽量去完成它
if (target_url.indexOf("@") < 0)
{
if (user_profile.realm.equals(Settings.DEFAULT_SERVER))
target_url = "&" + target_url;
target_url = target_url + "@" + realm; // modified
}
// MMTel addition to define MMTel ICSI to be included in INVITE (added
// by mandrajg) 要包含在INVITE MMTel除了定义MMTel ICSI
String icsi = null;
if (user_profile.mmtel == true)
{
icsi = "\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";
}
//从服务器中获得对方的地址
target_url = sip_provider.completeNameAddress(target_url).toString();
//真的拨打电话
if (user_profile.no_offer)
{
call.call(target_url);
} else
{
call.call(target_url, local_session, icsi); // modified by mandrajg
}
return true;
}
那它的音频接收以及发送是怎么实现的?
管理音频有一个类,它是JAudioLauncher,当实例化它的时候,它会开启两个线程,一个是RtpStreamReceiver,另一个是RtpStreamReceiver!~
看标题就知道它们一个是发送的,一个是接收的!~
先来看下接收是怎么实现的!~
- public void run()
- {
- boolean nodata = PreferenceManager.getDefaultSharedPreferences(
- Receiver.mContext).getBoolean(
- org.sipdroid.sipua.ui.Settings.PREF_NODATA,
- org.sipdroid.sipua.ui.Settings.DEFAULT_NODATA);
- keepon = PreferenceManager.getDefaultSharedPreferences(
- Receiver.mContext).getBoolean(
- org.sipdroid.sipua.ui.Settings.PREF_KEEPON,
- org.sipdroid.sipua.ui.Settings.DEFAULT_KEEPON);
- if (rtp_socket == null)
- {
- if (DEBUG)
- {
- println("ERROR: RTP socket is null(出错:rtp接受套接字出错!)");
- }
- return;
- }
- //发送包 缓冲区
- byte[] buffer = new byte[BUFFER_SIZE + 12];
- //构建包实例
- rtp_packet = new RtpPacket(buffer, 0);
- if (DEBUG)
- {
- println("Reading blocks of max (读取快的最大值) = " + buffer.length + " bytes");
- }
- running = true;
- //开启蓝牙
- enableBluetooth(PreferenceManager.getDefaultSharedPreferences(
- Receiver.mContext).getBoolean(
- org.sipdroid.sipua.ui.Settings.PREF_BLUETOOTH,
- org.sipdroid.sipua.ui.Settings.DEFAULT_BLUETOOTH));
- restored = false;
- //设置线程权限
- android.os.Process
- .setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
- am = (AudioManager) Receiver.mContext
- .getSystemService(Context.AUDIO_SERVICE);
- cr = Receiver.mContext.getContentResolver();
- //保存设置
- saveSettings();
- Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY,
- Settings.System.WIFI_SLEEP_POLICY_NEVER);
- am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,
- AudioManager.VIBRATE_SETTING_OFF);
- am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,
- AudioManager.VIBRATE_SETTING_OFF);
- if (oldvol == -1)
- {
- oldvol = am.getStreamVolume(AudioManager.STREAM_MUSIC);
- }
- //初始化模式
- initMode();
- //设置音频编解码器
- setCodec();
- //编辑音频 由发送包解码后放到此处 然后调整后放入到声音播放器中
- short lin[] = new short[BUFFER_SIZE];
- //它是负责清空lin的
- short lin2[] = new short[BUFFER_SIZE];
- int server, headroom, todo, len = 0, m = 1, expseq, getseq, vm = 1, gap, gseq;
- ToneGenerator tg = new ToneGenerator(
- AudioManager.STREAM_VOICE_CALL,
- (int) (ToneGenerator.MAX_VOLUME * 2 * org.sipdroid.sipua.ui.Settings
- .getEarGain()));
- //播放
- track.play();
- //指示虚拟机运行垃圾收集器,这将是一个好时机。
- //请注意,这仅是一个提示。有没有保证,垃圾收集器将实际运行。
- System.gc();
- //清空
- empty();
- lockFirst = true;
- while (running)
- {
- lock(true);
- if (Receiver.call_state == UserAgent.UA_STATE_HOLD)
- {
- lock(false);
- //停止
- tg.stopTone();
- //暂停
- track.pause();
- while (running
- && Receiver.call_state == UserAgent.UA_STATE_HOLD)
- {
- try
- {
- sleep(1000);
- } catch (InterruptedException e1)
- {
- }
- }
- track.play();
- System.gc();
- timeout = 1;
- luser = luser2 = -8000 * mu;
- }
- try
- {
- // 接受数据
- rtp_socket.receive(rtp_packet);
- //超时
- if (timeout != 0)
- {
- //停止音乐
- tg.stopTone();
- //暂停声音
- track.pause();
- //清空
- for (int i = maxjitter * 2; i > 0; i -= BUFFER_SIZE)
- {
- write(lin2, 0, i > BUFFER_SIZE ? BUFFER_SIZE : i);
- }
- cnt += maxjitter * 2;
- //声音播放
- track.play();
- empty();
- }
- timeout = 0;
- } catch (IOException e)
- {
- if (timeout == 0 && nodata)
- {
- tg.startTone(ToneGenerator.TONE_SUP_RINGTONE);
- }
- //异常者 断开
- rtp_socket.getDatagramSocket().disconnect();
- if (++timeout > 60)
- {
- Receiver.engine(Receiver.mContext).rejectcall();
- break;
- }
- }
- //在运行 且未超时
- if (running && timeout == 0)
- {
- //得到数字字符
- gseq = rtp_packet.getSequenceNumber();
- //得到当前接受包的大小
- if (seq == gseq)
- {
- m++;
- continue;
- }
- gap = (gseq - seq) & 0xff;
- if (gap > 240)
- {
- continue;
- }
- //返回帧中表示播放头位置。
- server = track.getPlaybackHeadPosition();
- //接受包的总大小 - 当前播放的位置
- headroom = user - server;
- if (headroom > 2 * jitter)
- {
- cnt += len;
- } else
- {
- cnt = 0;
- }
- if (lserver == server)
- {
- cnt2++;
- } else
- {
- cnt2 = 0;
- }
- if (cnt <= 500 * mu || cnt2 >= 2 || headroom - jitter < len
- || p_type.codec.number() != 8
- || p_type.codec.number() != 0)
- {
- //有效负荷类型||改变类型
- if (rtp_packet.getPayloadType() != p_type.number
- && p_type.change(rtp_packet.getPayloadType()))
- {
- //保留声量
- saveVolume();
- //设置编解码器
- setCodec();
- //恢复声量
- restoreVolume();
- //头信息
- codec = p_type.codec.getTitle();
- }
- //得到有效负荷长度
- len = p_type.codec.decode(buffer, lin,
- rtp_packet.getPayloadLength());
- // Call recording: Save incoming.Data is in buffer lin, from 0 to len.
- //通话记录:保存传入。数据是在缓冲区林,从0到LEN。
- if (call_recorder != null)
- {
- //写入
- call_recorder.writeIncoming(lin, 0, len);
- }
- //声音改变效果显示
- if (speakermode == AudioManager.MODE_NORMAL)
- {
- calc(lin, 0, len);
- } else if (gain > 1)
- {
- calc2(lin, 0, len);
- }
- }
- //
- avgheadroom = avgheadroom * 0.99 + (double) headroom * 0.01;
- if (avgcnt++ > 300)
- {
- devheadroom = devheadroom * 0.999
- + Math.pow(Math.abs(headroom - avgheadroom), 2)
- * 0.001;
- }
- //头大小
- if (headroom < 250 * mu)
- {
- late++;
- newjitter(true);
- // System.out.println("RTP:underflow "
- // + (int) Math.sqrt(devheadroom));
- todo = jitter - headroom;
- write(lin2, 0, todo > BUFFER_SIZE ? BUFFER_SIZE : todo);
- }
- //
- if (cnt > 500 * mu && cnt2 < 2)
- {
- todo = headroom - jitter;
- if (todo < len)
- {
- write(lin, todo, len - todo);
- }
- } else
- {
- write(lin, 0, len);
- }
- //丢失包统计
- if (seq != 0)
- {
- getseq = gseq & 0xff;
- expseq = ++seq & 0xff;
- if (m == RtpStreamSender.m)
- {
- vm = m;
- }
- gap = (getseq - expseq) & 0xff;
- if (gap > 0)
- {
- // System.out.println("RTP:lost");
- if (gap > 100)
- {
- gap = 1;
- }
- loss += gap;
- lost += gap;
- good += gap - 1;
- loss2++;
- } else
- {
- if (m < vm)
- {
- loss++;
- loss2++;
- }
- }
- good++;
- if (good > 110)
- {
- good *= 0.99;
- lost *= 0.99;
- loss *= 0.99;
- loss2 *= 0.99;
- late *= 0.99;
- }
- }
- m = 1;
- seq = gseq;
- if (user >= luser + 8000 * mu
- && (Receiver.call_state == UserAgent.UA_STATE_INCALL || Receiver.call_state == UserAgent.UA_STATE_OUTGOING_CALL))
- {
- if (luser == -8000 * mu || getMode() != speakermode)
- {
- //保留声量
- saveVolume();
- //设置模式
- setMode(speakermode);
- //恢复声量
- restoreVolume();
- }
- luser = user;
- if (user >= luser2 + 160000 * mu)
- {
- //抖动
- newjitter(false);
- }
- }
- lserver = server;
- }
- }
public void run()
{
boolean nodata = PreferenceManager.getDefaultSharedPreferences(
Receiver.mContext).getBoolean(
org.sipdroid.sipua.ui.Settings.PREF_NODATA,
org.sipdroid.sipua.ui.Settings.DEFAULT_NODATA);
keepon = PreferenceManager.getDefaultSharedPreferences(
Receiver.mContext).getBoolean(
org.sipdroid.sipua.ui.Settings.PREF_KEEPON,
org.sipdroid.sipua.ui.Settings.DEFAULT_KEEPON);
if (rtp_socket == null)
{
if (DEBUG)
{
println("ERROR: RTP socket is null(出错:rtp接受套接字出错!)");
}
return;
}
//发送包 缓冲区
byte[] buffer = new byte[BUFFER_SIZE + 12];
//构建包实例
rtp_packet = new RtpPacket(buffer, 0);
if (DEBUG)
{
println("Reading blocks of max (读取快的最大值) = " + buffer.length + " bytes");
}
running = true;
//开启蓝牙
enableBluetooth(PreferenceManager.getDefaultSharedPreferences(
Receiver.mContext).getBoolean(
org.sipdroid.sipua.ui.Settings.PREF_BLUETOOTH,
org.sipdroid.sipua.ui.Settings.DEFAULT_BLUETOOTH));
restored = false;
//设置线程权限
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
am = (AudioManager) Receiver.mContext
.getSystemService(Context.AUDIO_SERVICE);
cr = Receiver.mContext.getContentResolver();
//保存设置
saveSettings();
Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY,
Settings.System.WIFI_SLEEP_POLICY_NEVER);
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,
AudioManager.VIBRATE_SETTING_OFF);
am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,
AudioManager.VIBRATE_SETTING_OFF);
if (oldvol == -1)
{
oldvol = am.getStreamVolume(AudioManager.STREAM_MUSIC);
}
//初始化模式
initMode();
//设置音频编解码器
setCodec();
//编辑音频 由发送包解码后放到此处 然后调整后放入到声音播放器中
short lin[] = new short[BUFFER_SIZE];
//它是负责清空lin的
short lin2[] = new short[BUFFER_SIZE];
int server, headroom, todo, len = 0, m = 1, expseq, getseq, vm = 1, gap, gseq;
ToneGenerator tg = new ToneGenerator(
AudioManager.STREAM_VOICE_CALL,
(int) (ToneGenerator.MAX_VOLUME * 2 * org.sipdroid.sipua.ui.Settings
.getEarGain()));
//播放
track.play();
//指示虚拟机运行垃圾收集器,这将是一个好时机。
//请注意,这仅是一个提示。有没有保证,垃圾收集器将实际运行。
System.gc();
//清空
empty();
lockFirst = true;
while (running)
{
lock(true);
if (Receiver.call_state == UserAgent.UA_STATE_HOLD)
{
lock(false);
//停止
tg.stopTone();
//暂停
track.pause();
while (running
&& Receiver.call_state == UserAgent.UA_STATE_HOLD)
{
try
{
sleep(1000);
} catch (InterruptedException e1)
{
}
}
track.play();
System.gc();
timeout = 1;
luser = luser2 = -8000 * mu;
}
try
{
// 接受数据
rtp_socket.receive(rtp_packet);
//超时
if (timeout != 0)
{
//停止音乐
tg.stopTone();
//暂停声音
track.pause();
//清空
for (int i = maxjitter * 2; i > 0; i -= BUFFER_SIZE)
{
write(lin2, 0, i > BUFFER_SIZE ? BUFFER_SIZE : i);
}
cnt += maxjitter * 2;
//声音播放
track.play();
empty();
}
timeout = 0;
} catch (IOException e)
{
if (timeout == 0 && nodata)
{
tg.startTone(ToneGenerator.TONE_SUP_RINGTONE);
}
//异常者 断开
rtp_socket.getDatagramSocket().disconnect();
if (++timeout > 60)
{
Receiver.engine(Receiver.mContext).rejectcall();
break;
}
}
//在运行 且未超时
if (running && timeout == 0)
{
//得到数字字符
gseq = rtp_packet.getSequenceNumber();
//得到当前接受包的大小
if (seq == gseq)
{
m++;
continue;
}
gap = (gseq - seq) & 0xff;
if (gap > 240)
{
continue;
}
//返回帧中表示播放头位置。
server = track.getPlaybackHeadPosition();
//接受包的总大小 - 当前播放的位置
headroom = user - server;
if (headroom > 2 * jitter)
{
cnt += len;
} else
{
cnt = 0;
}
if (lserver == server)
{
cnt2++;
} else
{
cnt2 = 0;
}
if (cnt <= 500 * mu || cnt2 >= 2 || headroom - jitter < len
|| p_type.codec.number() != 8
|| p_type.codec.number() != 0)
{
//有效负荷类型||改变类型
if (rtp_packet.getPayloadType() != p_type.number
&& p_type.change(rtp_packet.getPayloadType()))
{
//保留声量
saveVolume();
//设置编解码器
setCodec();
//恢复声量
restoreVolume();
//头信息
codec = p_type.codec.getTitle();
}
//得到有效负荷长度
len = p_type.codec.decode(buffer, lin,
rtp_packet.getPayloadLength());
// Call recording: Save incoming.Data is in buffer lin, from 0 to len.
//通话记录:保存传入。数据是在缓冲区林,从0到LEN。
if (call_recorder != null)
{
//写入
call_recorder.writeIncoming(lin, 0, len);
}
//声音改变效果显示
if (speakermode == AudioManager.MODE_NORMAL)
{
calc(lin, 0, len);
} else if (gain > 1)
{
calc2(lin, 0, len);
}
}
//
avgheadroom = avgheadroom * 0.99 + (double) headroom * 0.01;
if (avgcnt++ > 300)
{
devheadroom = devheadroom * 0.999
+ Math.pow(Math.abs(headroom - avgheadroom), 2)
* 0.001;
}
//头大小
if (headroom < 250 * mu)
{
late++;
newjitter(true);
// System.out.println("RTP:underflow "
// + (int) Math.sqrt(devheadroom));
todo = jitter - headroom;
write(lin2, 0, todo > BUFFER_SIZE ? BUFFER_SIZE : todo);
}
//
if (cnt > 500 * mu && cnt2 < 2)
{
todo = headroom - jitter;
if (todo < len)
{
write(lin, todo, len - todo);
}
} else
{
write(lin, 0, len);
}
//丢失包统计
if (seq != 0)
{
getseq = gseq & 0xff;
expseq = ++seq & 0xff;
if (m == RtpStreamSender.m)
{
vm = m;
}
gap = (getseq - expseq) & 0xff;
if (gap > 0)
{
// System.out.println("RTP:lost");
if (gap > 100)
{
gap = 1;
}
loss += gap;
lost += gap;
good += gap - 1;
loss2++;
} else
{
if (m < vm)
{
loss++;
loss2++;
}
}
good++;
if (good > 110)
{
good *= 0.99;
lost *= 0.99;
loss *= 0.99;
loss2 *= 0.99;
late *= 0.99;
}
}
m = 1;
seq = gseq;
if (user >= luser + 8000 * mu
&& (Receiver.call_state == UserAgent.UA_STATE_INCALL || Receiver.call_state == UserAgent.UA_STATE_OUTGOING_CALL))
{
if (luser == -8000 * mu || getMode() != speakermode)
{
//保留声量
saveVolume();
//设置模式
setMode(speakermode);
//恢复声量
restoreVolume();
}
luser = user;
if (user >= luser2 + 160000 * mu)
{
//抖动
newjitter(false);
}
}
lserver = server;
}
}
再看下发送包是怎么实现的,发送声音肯定是先录制,后读取,再发送!~如下:
- public void run()
- {
- //测试音频数据
- // testVoiceFile mvoiceFile = new testVoiceFile("voice");
- // testVoiceFile mvoiceRtpFile = new testVoiceFile("rtpdate");
- //wifi管理
- WifiManager wm = (WifiManager) Receiver.mContext
- .getSystemService(Context.WIFI_SERVICE);
- long lastscan = 0, lastsent = 0;
- if (rtp_socket == null)
- return;
- int seqn = 0;
- long time = 0;
- double p = 0;
- //改善
- boolean improve = PreferenceManager.getDefaultSharedPreferences(
- Receiver.mContext).getBoolean(Settings.PREF_IMPROVE,
- Settings.DEFAULT_IMPROVE);
- //选择wifi
- boolean selectWifi = PreferenceManager.getDefaultSharedPreferences(
- Receiver.mContext).getBoolean(
- org.sipdroid.sipua.ui.Settings.PREF_SELECTWIFI,
- org.sipdroid.sipua.ui.Settings.DEFAULT_SELECTWIFI);
- int micgain = 0;
- long last_tx_time = 0;
- long next_tx_delay;
- long now;
- running = true;
- m = 1;
- int dtframesize = 4;
- //改变线程权限
- android.os.Process
- .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
- //解码速率
- mu = p_type.codec.samp_rate() / 8000;
- int min = AudioRecord.getMinBufferSize(p_type.codec.samp_rate(),
- AudioFormat.CHANNEL_CONFIGURATION_MONO,
- AudioFormat.ENCODING_PCM_16BIT); //最小缓冲大小
- if (min == 640)
- {
- if (frame_size == 960)
- frame_size = 320;
- if (frame_size == 1024)
- frame_size = 160;
- min = 4096 * 3 / 2;
- } else if (min < 4096)
- {
- if (min <= 2048 && frame_size == 1024)
- frame_size /= 2;
- min = 4096 * 3 / 2;
- } else if (min == 4096)
- {
- min *= 3 / 2;
- if (frame_size == 960)
- frame_size = 320;
- } else
- {
- if (frame_size == 960)
- frame_size = 320;
- if (frame_size == 1024)
- frame_size *= 2;
- }
- //速率
- frame_rate = p_type.codec.samp_rate() / frame_size;
- long frame_period = 1000 / frame_rate;
- frame_rate *= 1.5;
- //发送包缓冲区
- byte[] buffer = new byte[frame_size + 12]; //
- //实例化包
- RtpPacket rtp_packet = new RtpPacket(buffer, 0);
- //设置有效复合
- rtp_packet.setPayloadType(p_type.number);
- //调式输出信息
- if (DEBUG)
- println("Reading blocks of (读取块大小)" + buffer.length + " bytes");
- println("Sample rate (采样率) = " + p_type.codec.samp_rate());
- println("Buffer size(缓冲区大小) = " + min);
- //录制
- AudioRecord record = null;
- //获得声音内容变量
- short[] lin = new short[frame_size * (frame_rate + 1)];
- int num, ring = 0, pos;
- //随机数
- random = new Random();
- //输入流
- InputStream alerting = null;
- try
- {
- //接受
- alerting = Receiver.mContext.getAssets().open("alerting");
- } catch (IOException e2)
- {
- if (!Sipdroid.release)
- e2.printStackTrace();
- }
- //初始化音频编码
- p_type.codec.init();
- if(Sipdroid.VoiceDebug)
- {
- Log.i("RtpStreamSender", "音频发送开始啦");
- }
- //循环发送 先录制 然后发送
- while (running)
- {
- //录制没有 或已经改变
- if (changed || record == null)
- {
- if (record != null)
- {
- //释放操作
- record.stop();
- record.release();
- if (RtpStreamReceiver.samsung)
- {
- AudioManager am = (AudioManager) Receiver.mContext
- .getSystemService(Context.AUDIO_SERVICE);
- am.setMode(AudioManager.MODE_IN_CALL);
- am.setMode(AudioManager.MODE_NORMAL);
- }
- }
- //未改变
- changed = false;
- //实例化录制
- record = new AudioRecord(MediaRecorder.AudioSource.MIC,
- p_type.codec.samp_rate(),
- AudioFormat.CHANNEL_CONFIGURATION_MONO,
- AudioFormat.ENCODING_PCM_16BIT, min);
- if(Sipdroid.VoiceDebug)
- {
- Log.i("RtpStreamSender", "构造音频录制实例");
- }
- //得到录制类型
- if (record.getState() != AudioRecord.STATE_INITIALIZED)
- {
- //拒接
- Receiver.engine(Receiver.mContext).rejectcall();
- record = null;
- break;
- }
- //开始录制
- record.startRecording();
- //计算
- micgain = (int) (Settings.getMicGain() * 10);
- }
- //静音或挂断 则停止
- if (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD)
- {
- //挂断
- if (Receiver.call_state == UserAgent.UA_STATE_HOLD)
- {
- //恢复模式
- RtpStreamReceiver.restoreMode();
- }
- if(Sipdroid.VoiceDebug)
- {
- Log.i("RtpStreamSender", "挂断电话则停止录制");
- }
- //停止录音
- record.stop();
- //延时
- while (running
- && (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD))
- {
- try
- {
- sleep(1000);
- } catch (InterruptedException e1)
- {
- }
- }
- //开始录制
- record.startRecording();
- }
- // DTMF change start
- if (dtmf.length() != 0)
- {
- //构造包
- byte[] dtmfbuf = new byte[dtframesize + 12];
- RtpPacket dt_packet = new RtpPacket(dtmfbuf, 0);
- //设置有效负荷
- dt_packet.setPayloadType(dtmf_payload_type);
- //设置大小
- dt_packet.setPayloadLength(dtframesize);
- dt_packet.setSscr(rtp_packet.getSscr());
- long dttime = time;
- int duration;
- for (int i = 0; i < 6; i++)
- {
- time += 160;
- duration = (int) (time - dttime);
- dt_packet.setSequenceNumber(seqn++);
- dt_packet.setTimestamp(dttime);
- dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));
- dtmfbuf[13] = (byte) 0x0a;
- dtmfbuf[14] = (byte) (duration >> 8);
- dtmfbuf[15] = (byte) duration;
- try
- {
- //发送包 声音包
- rtp_socket.send(dt_packet);
- if (Sipdroid.VoiceDebug)
- {
- Log.i("RtpStreamSender",
- "第一次发送声音包 大小" + dt_packet.getLength()
- + " " + dt_packet.getPacket());
- }
- if(bShowVoiceDecodeData)
- {
- // mvoiceRtpFile.write(dt_packet.getPacket());
- }
- sleep(20);
- } catch (Exception e1)
- {
- }
- }
- //发送回音?
- for (int i = 0; i < 3; i++)
- {
- duration = (int) (time - dttime);
- dt_packet.setSequenceNumber(seqn);
- dt_packet.setTimestamp(dttime);
- dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));
- dtmfbuf[13] = (byte) 0x8a;
- dtmfbuf[14] = (byte) (duration >> 8);
- dtmfbuf[15] = (byte) duration;
- try
- {
- //发送包
- rtp_socket.send(dt_packet);
- if (Sipdroid.VoiceDebug)
- {
- Log.i("RtpStreamSender",
- "第二次发送声音包 大小" + dt_packet.getLength()
- + " " + dt_packet.getPacket());
- }
- if(bShowVoiceDecodeData)
- {
- // mvoiceRtpFile.write(dt_packet.getPacket());
- }
- } catch (Exception e1)
- {
- }
- }
- time += 160;
- seqn++;
- dtmf = dtmf.substring(1);
- }
- // DTMF change end
- if (frame_size < 480)
- {
- now = System.currentTimeMillis();
- next_tx_delay = frame_period - (now - last_tx_time);
- last_tx_time = now;
- if (next_tx_delay > 0)
- {
- try
- {
- sleep(next_tx_delay);
- } catch (InterruptedException e1)
- {
- }
- last_tx_time += next_tx_delay - sync_adj;
- }
- }
- //获得发送的位置
- pos = (ring + delay * frame_rate * frame_size)
- % (frame_size * (frame_rate + 1));
- //得到大小
- num = record.read(lin, pos, frame_size);
- if (num <= 0)
- continue;
- //是否有效
- if (!p_type.codec.isValid())
- continue;
- // Call recording: Save the frame to the CallRecorder.
- //通话记录:框架保存的CallRecorder的。 新的录制
- if (call_recorder != null)
- {
- //写入 输出
- call_recorder.writeOutgoing(lin, pos, num);
- }
- if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL)
- {
- calc(lin, pos, num);
- if (RtpStreamReceiver.nearend != 0
- && RtpStreamReceiver.down_time == 0)
- {
- noise(lin, pos, num, p / 2);
- }
- else if (nearend == 0)
- {
- p = 0.9 * p + 0.1 * s;
- }
- } else
- {
- switch (micgain)
- {
- case 1:
- calc1(lin, pos, num);
- break;
- case 2:
- calc2(lin, pos, num);
- break;
- case 10:
- calc10(lin, pos, num);
- break;
- }
- }
- iCount++;
- //通话中
- if (Receiver.call_state != UserAgent.UA_STATE_INCALL
- && Receiver.call_state != UserAgent.UA_STATE_OUTGOING_CALL
- && alerting != null)
- {
- try
- {
- if (alerting.available() < num / mu)
- {
- alerting.reset();
- }
- alerting.read(buffer, 12, num / mu);
- } catch (IOException e)
- {
- if (!Sipdroid.release)
- {
- e.printStackTrace();
- }
- }
- if (p_type.codec.number() != 8)
- {
- G711.alaw2linear(buffer, lin, num, mu);
- num = p_type.codec.encode(lin, 0, buffer, num);
- // if(bShowVoiceDecodeData)
- // {
- // byte[] sdf = lin.toString().getBytes();
- // mvoiceFile.write(sdf);
- //
- // String string = "";
- //
- // for(short i:buffer)
- // string+=i;
- //
- // Log.i("p_type.codec.encode",iCount +"编码后的数据(buffer):"+string);
- // }
- }
- } else
- {
- num = p_type.codec.encode(lin, ring
- % (frame_size * (frame_rate + 1)), buffer, num); //进行了编码
- // if(bShowVoiceDecodeData)
- // {
- // mvoiceFile.write(buffer);
- //
- // String string = "";
- //
- // for(short i:buffer)
- // string+=i;
- //
- // Log.i("p_type.codec.encode",iCount +"编码后的数据(buffer):"+string);
- // }
- }
- //大小
- ring += frame_size;
- rtp_packet.setSequenceNumber(seqn++);
- rtp_packet.setTimestamp(time);
- rtp_packet.setPayloadLength(num);
- //记录时间
- now = SystemClock.elapsedRealtime();
- if (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan
- || now - lastsent > 500)
- {
- try
- {
- lastsent = now;
- rtp_socket.send(rtp_packet);
- if (m > 1
- && (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan))
- {
- for (int i = 1; i < m; i++)
- rtp_socket.send(rtp_packet);
- }
- if (Sipdroid.VoiceDebug)
- {
- Log.i("RtpStreamSender",
- "第三次发送声音包 大小" + rtp_packet.getLength()
- + " " + rtp_packet.getPacket());
- }
- // if(bShowVoiceDecodeData)
- // {
- // mvoiceRtpFile.write(rtp_packet.getPacket());
- // }
- } catch (Exception e)
- {
- }
- }
- //编码数
- if (p_type.codec.number() == 9)
- {
- time += frame_size / 2;
- }
- else
- {
- time += frame_size;
- }
- if (RtpStreamReceiver.good != 0
- && RtpStreamReceiver.loss2 / RtpStreamReceiver.good > 0.01)
- {
- if (selectWifi && Receiver.on_wlan && now - lastscan > 10000)
- {
- wm.startScan();
- lastscan = now;
- }
- if (improve
- && delay == 0
- && (p_type.codec.number() == 0
- || p_type.codec.number() == 8 || p_type.codec
- .number() == 9))
- {
- m = 2;
- }
- else
- {
- m = 1;
- }
- } else
- {
- m = 1;
- }
- }
public void run()
{
//测试音频数据
// testVoiceFile mvoiceFile = new testVoiceFile("voice");
// testVoiceFile mvoiceRtpFile = new testVoiceFile("rtpdate");
//wifi管理
WifiManager wm = (WifiManager) Receiver.mContext
.getSystemService(Context.WIFI_SERVICE);
long lastscan = 0, lastsent = 0;
if (rtp_socket == null)
return;
int seqn = 0;
long time = 0;
double p = 0;
//改善
boolean improve = PreferenceManager.getDefaultSharedPreferences(
Receiver.mContext).getBoolean(Settings.PREF_IMPROVE,
Settings.DEFAULT_IMPROVE);
//选择wifi
boolean selectWifi = PreferenceManager.getDefaultSharedPreferences(
Receiver.mContext).getBoolean(
org.sipdroid.sipua.ui.Settings.PREF_SELECTWIFI,
org.sipdroid.sipua.ui.Settings.DEFAULT_SELECTWIFI);
int micgain = 0;
long last_tx_time = 0;
long next_tx_delay;
long now;
running = true;
m = 1;
int dtframesize = 4;
//改变线程权限
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
//解码速率
mu = p_type.codec.samp_rate() / 8000;
int min = AudioRecord.getMinBufferSize(p_type.codec.samp_rate(),
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT); //最小缓冲大小
if (min == 640)
{
if (frame_size == 960)
frame_size = 320;
if (frame_size == 1024)
frame_size = 160;
min = 4096 * 3 / 2;
} else if (min < 4096)
{
if (min <= 2048 && frame_size == 1024)
frame_size /= 2;
min = 4096 * 3 / 2;
} else if (min == 4096)
{
min *= 3 / 2;
if (frame_size == 960)
frame_size = 320;
} else
{
if (frame_size == 960)
frame_size = 320;
if (frame_size == 1024)
frame_size *= 2;
}
//速率
frame_rate = p_type.codec.samp_rate() / frame_size;
long frame_period = 1000 / frame_rate;
frame_rate *= 1.5;
//发送包缓冲区
byte[] buffer = new byte[frame_size + 12]; //
//实例化包
RtpPacket rtp_packet = new RtpPacket(buffer, 0);
//设置有效复合
rtp_packet.setPayloadType(p_type.number);
//调式输出信息
if (DEBUG)
println("Reading blocks of (读取块大小)" + buffer.length + " bytes");
println("Sample rate (采样率) = " + p_type.codec.samp_rate());
println("Buffer size(缓冲区大小) = " + min);
//录制
AudioRecord record = null;
//获得声音内容变量
short[] lin = new short[frame_size * (frame_rate + 1)];
int num, ring = 0, pos;
//随机数
random = new Random();
//输入流
InputStream alerting = null;
try
{
//接受
alerting = Receiver.mContext.getAssets().open("alerting");
} catch (IOException e2)
{
if (!Sipdroid.release)
e2.printStackTrace();
}
//初始化音频编码
p_type.codec.init();
if(Sipdroid.VoiceDebug)
{
Log.i("RtpStreamSender", "音频发送开始啦");
}
//循环发送 先录制 然后发送
while (running)
{
//录制没有 或已经改变
if (changed || record == null)
{
if (record != null)
{
//释放操作
record.stop();
record.release();
if (RtpStreamReceiver.samsung)
{
AudioManager am = (AudioManager) Receiver.mContext
.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_CALL);
am.setMode(AudioManager.MODE_NORMAL);
}
}
//未改变
changed = false;
//实例化录制
record = new AudioRecord(MediaRecorder.AudioSource.MIC,
p_type.codec.samp_rate(),
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, min);
if(Sipdroid.VoiceDebug)
{
Log.i("RtpStreamSender", "构造音频录制实例");
}
//得到录制类型
if (record.getState() != AudioRecord.STATE_INITIALIZED)
{
//拒接
Receiver.engine(Receiver.mContext).rejectcall();
record = null;
break;
}
//开始录制
record.startRecording();
//计算
micgain = (int) (Settings.getMicGain() * 10);
}
//静音或挂断 则停止
if (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD)
{
//挂断
if (Receiver.call_state == UserAgent.UA_STATE_HOLD)
{
//恢复模式
RtpStreamReceiver.restoreMode();
}
if(Sipdroid.VoiceDebug)
{
Log.i("RtpStreamSender", "挂断电话则停止录制");
}
//停止录音
record.stop();
//延时
while (running
&& (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD))
{
try
{
sleep(1000);
} catch (InterruptedException e1)
{
}
}
//开始录制
record.startRecording();
}
// DTMF change start
if (dtmf.length() != 0)
{
//构造包
byte[] dtmfbuf = new byte[dtframesize + 12];
RtpPacket dt_packet = new RtpPacket(dtmfbuf, 0);
//设置有效负荷
dt_packet.setPayloadType(dtmf_payload_type);
//设置大小
dt_packet.setPayloadLength(dtframesize);
dt_packet.setSscr(rtp_packet.getSscr());
long dttime = time;
int duration;
for (int i = 0; i < 6; i++)
{
time += 160;
duration = (int) (time - dttime);
dt_packet.setSequenceNumber(seqn++);
dt_packet.setTimestamp(dttime);
dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));
dtmfbuf[13] = (byte) 0x0a;
dtmfbuf[14] = (byte) (duration >> 8);
dtmfbuf[15] = (byte) duration;
try
{
//发送包 声音包
rtp_socket.send(dt_packet);
if (Sipdroid.VoiceDebug)
{
Log.i("RtpStreamSender",
"第一次发送声音包 大小" + dt_packet.getLength()
+ " " + dt_packet.getPacket());
}
if(bShowVoiceDecodeData)
{
// mvoiceRtpFile.write(dt_packet.getPacket());
}
sleep(20);
} catch (Exception e1)
{
}
}
//发送回音?
for (int i = 0; i < 3; i++)
{
duration = (int) (time - dttime);
dt_packet.setSequenceNumber(seqn);
dt_packet.setTimestamp(dttime);
dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));
dtmfbuf[13] = (byte) 0x8a;
dtmfbuf[14] = (byte) (duration >> 8);
dtmfbuf[15] = (byte) duration;
try
{
//发送包
rtp_socket.send(dt_packet);
if (Sipdroid.VoiceDebug)
{
Log.i("RtpStreamSender",
"第二次发送声音包 大小" + dt_packet.getLength()
+ " " + dt_packet.getPacket());
}
if(bShowVoiceDecodeData)
{
// mvoiceRtpFile.write(dt_packet.getPacket());
}
} catch (Exception e1)
{
}
}
time += 160;
seqn++;
dtmf = dtmf.substring(1);
}
// DTMF change end
if (frame_size < 480)
{
now = System.currentTimeMillis();
next_tx_delay = frame_period - (now - last_tx_time);
last_tx_time = now;
if (next_tx_delay > 0)
{
try
{
sleep(next_tx_delay);
} catch (InterruptedException e1)
{
}
last_tx_time += next_tx_delay - sync_adj;
}
}
//获得发送的位置
pos = (ring + delay * frame_rate * frame_size)
% (frame_size * (frame_rate + 1));
//得到大小
num = record.read(lin, pos, frame_size);
if (num <= 0)
continue;
//是否有效
if (!p_type.codec.isValid())
continue;
// Call recording: Save the frame to the CallRecorder.
//通话记录:框架保存的CallRecorder的。 新的录制
if (call_recorder != null)
{
//写入 输出
call_recorder.writeOutgoing(lin, pos, num);
}
if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL)
{
calc(lin, pos, num);
if (RtpStreamReceiver.nearend != 0
&& RtpStreamReceiver.down_time == 0)
{
noise(lin, pos, num, p / 2);
}
else if (nearend == 0)
{
p = 0.9 * p + 0.1 * s;
}
} else
{
switch (micgain)
{
case 1:
calc1(lin, pos, num);
break;
case 2:
calc2(lin, pos, num);
break;
case 10:
calc10(lin, pos, num);
break;
}
}
iCount++;
//通话中
if (Receiver.call_state != UserAgent.UA_STATE_INCALL
&& Receiver.call_state != UserAgent.UA_STATE_OUTGOING_CALL
&& alerting != null)
{
try
{
if (alerting.available() < num / mu)
{
alerting.reset();
}
alerting.read(buffer, 12, num / mu);
} catch (IOException e)
{
if (!Sipdroid.release)
{
e.printStackTrace();
}
}
if (p_type.codec.number() != 8)
{
G711.alaw2linear(buffer, lin, num, mu);
num = p_type.codec.encode(lin, 0, buffer, num);
// if(bShowVoiceDecodeData)
// {
// byte[] sdf = lin.toString().getBytes();
// mvoiceFile.write(sdf);
//
// String string = "";
//
// for(short i:buffer)
// string+=i;
//
// Log.i("p_type.codec.encode",iCount +"编码后的数据(buffer):"+string);
// }
}
} else
{
num = p_type.codec.encode(lin, ring
% (frame_size * (frame_rate + 1)), buffer, num); //进行了编码
// if(bShowVoiceDecodeData)
// {
// mvoiceFile.write(buffer);
//
// String string = "";
//
// for(short i:buffer)
// string+=i;
//
// Log.i("p_type.codec.encode",iCount +"编码后的数据(buffer):"+string);
// }
}
//大小
ring += frame_size;
rtp_packet.setSequenceNumber(seqn++);
rtp_packet.setTimestamp(time);
rtp_packet.setPayloadLength(num);
//记录时间
now = SystemClock.elapsedRealtime();
if (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan
|| now - lastsent > 500)
{
try
{
lastsent = now;
rtp_socket.send(rtp_packet);
if (m > 1
&& (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan))
{
for (int i = 1; i < m; i++)
rtp_socket.send(rtp_packet);
}
if (Sipdroid.VoiceDebug)
{
Log.i("RtpStreamSender",
"第三次发送声音包 大小" + rtp_packet.getLength()
+ " " + rtp_packet.getPacket());
}
// if(bShowVoiceDecodeData)
// {
// mvoiceRtpFile.write(rtp_packet.getPacket());
// }
} catch (Exception e)
{
}
}
//编码数
if (p_type.codec.number() == 9)
{
time += frame_size / 2;
}
else
{
time += frame_size;
}
if (RtpStreamReceiver.good != 0
&& RtpStreamReceiver.loss2 / RtpStreamReceiver.good > 0.01)
{
if (selectWifi && Receiver.on_wlan && now - lastscan > 10000)
{
wm.startScan();
lastscan = now;
}
if (improve
&& delay == 0
&& (p_type.codec.number() == 0
|| p_type.codec.number() == 8 || p_type.codec
.number() == 9))
{
m = 2;
}
else
{
m = 1;
}
} else
{
m = 1;
}
}
好了,进入下一个问题,它的视频又是怎么一回事?此处不谈它的视频编码,直接介绍涉及它的流程!~
涉及视频的类有:VideoCamera、VideoCameraNew、VideoCameraNew2、VideoPreview!~ 而是否使用视频,关键在CallScreen类,CallScreen类是通话的界面!~
如下是否使用视频的代码,该代码在CallScreen中!~
- //是否开启视频流程关键地方 必须是在 电话中并且端口等设置正确
- if (Receiver.call_state == UserAgent.UA_STATE_INCALL
- && socket == null
- && Receiver.engine(mContext).getLocalVideo() != 0
- && Receiver.engine(mContext).getRemoteVideo() != 0
- && PreferenceManager
- .getDefaultSharedPreferences(this)
- .getString(org.sipdroid.sipua.ui.Settings.PREF_SERVER,
- org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER)
- .equals(org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER))
- {
- (new Thread()
- {
- public void run()
- {
- // 视频包 在线包
- RtpPacket keepalive = new RtpPacket(new byte[12], 0);
- RtpPacket videopacket = new RtpPacket(new byte[1000], 0);
- try
- {
- if (intent == null || rtp_socket == null)
- {
- // 新建一个套接字
- rtp_socket = new RtpSocket(
- // 初始化套接字
- socket = new SipdroidSocket(Receiver
- .engine(mContext).getLocalVideo()),
- // 设置网络地址
- InetAddress.getByName(Receiver.engine(
- mContext).getRemoteAddr()),
- // 接受远程视频
- Receiver.engine(mContext).getRemoteVideo());
- sleep(3000);
- } else
- {
- // 接受数据
- socket = rtp_socket.getDatagramSocket();
- }
- // 接受数据
- rtp_socket.getDatagramSocket().setSoTimeout(15000);
- } catch (Exception e)
- {
- if (!Sipdroid.release)
- {
- e.printStackTrace();
- }
- return;
- }
- // 设置有效载荷类型
- keepalive.setPayloadType(126);
- try
- {
- // 发送数据
- rtp_socket.send(keepalive);
- } catch (Exception e1)
- {
- return;
- }
- for (;;)
- {
- try
- {
- // 循环接收数据
- rtp_socket.receive(videopacket);
- } catch (IOException e)
- {
- // 异常则断开
- rtp_socket.getDatagramSocket().disconnect();
- try
- {
- // 发送在线包
- rtp_socket.send(keepalive);
- } catch (IOException e1)
- {
- return;
- }
- }
- // 得到有效负荷长度
- if (videopacket.getPayloadLength() > 200)
- {
- if (intent != null)
- {
- // 发送数据
- intent.putExtra("justplay", true);
- mHandler.sendEmptyMessage(0);
- } else
- {
- // 否则播放
- Intent i = new Intent(mContext,
- org.sipdroid.sipua.ui.VideoCamera.class);
- i.putExtra("justplay", true);
- startActivity(i);
- }
- return;
- }
- }
- }
- }).start();
- }
//是否开启视频流程关键地方 必须是在 电话中并且端口等设置正确
if (Receiver.call_state == UserAgent.UA_STATE_INCALL
&& socket == null
&& Receiver.engine(mContext).getLocalVideo() != 0
&& Receiver.engine(mContext).getRemoteVideo() != 0
&& PreferenceManager
.getDefaultSharedPreferences(this)
.getString(org.sipdroid.sipua.ui.Settings.PREF_SERVER,
org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER)
.equals(org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER))
{
(new Thread()
{
public void run()
{
// 视频包 在线包
RtpPacket keepalive = new RtpPacket(new byte[12], 0);
RtpPacket videopacket = new RtpPacket(new byte[1000], 0);
try
{
if (intent == null || rtp_socket == null)
{
// 新建一个套接字
rtp_socket = new RtpSocket(
// 初始化套接字
socket = new SipdroidSocket(Receiver
.engine(mContext).getLocalVideo()),
// 设置网络地址
InetAddress.getByName(Receiver.engine(
mContext).getRemoteAddr()),
// 接受远程视频
Receiver.engine(mContext).getRemoteVideo());
sleep(3000);
} else
{
// 接受数据
socket = rtp_socket.getDatagramSocket();
}
// 接受数据
rtp_socket.getDatagramSocket().setSoTimeout(15000);
} catch (Exception e)
{
if (!Sipdroid.release)
{
e.printStackTrace();
}
return;
}
// 设置有效载荷类型
keepalive.setPayloadType(126);
try
{
// 发送数据
rtp_socket.send(keepalive);
} catch (Exception e1)
{
return;
}
for (;;)
{
try
{
// 循环接收数据
rtp_socket.receive(videopacket);
} catch (IOException e)
{
// 异常则断开
rtp_socket.getDatagramSocket().disconnect();
try
{
// 发送在线包
rtp_socket.send(keepalive);
} catch (IOException e1)
{
return;
}
}
// 得到有效负荷长度
if (videopacket.getPayloadLength() > 200)
{
if (intent != null)
{
// 发送数据
intent.putExtra("justplay", true);
mHandler.sendEmptyMessage(0);
} else
{
// 否则播放
Intent i = new Intent(mContext,
org.sipdroid.sipua.ui.VideoCamera.class);
i.putExtra("justplay", true);
startActivity(i);
}
return;
}
}
}
}).start();
}
之后就进入到涉及视频的类VideoCamera中了!~
它在模拟器上的端口为什么总是变化的?
抓包分析下,如图:
yate服务端第一次记录了我的rport为1849,从图中发现它是与服务器通信的端口!~服务器中也把它当做端口记录!~而SipUA客户端是使用了UDP套接字自定义了一个37850端口,这个端口一直到退出才改变,而yate服务端第一次记录了我的rport为2251,也就是说服务器记录的rport的端口是一直在发生改变的!~所以下次用户拨打对方,向服务器索取对方信息时出错,就有可能会直接挂掉!~
rport在Sip中的定义是rport方式主要是对sip信令中Via字头的扩展,不过同时也要求SIP Proxy支持该功能。NAT之后的sip client在发送请求的时候在via字头中添加rport字段,该消息经发出后路由到SIP Proxy,SIP Proxy通过检查消息的源地址和Via字段中的地址,得知该client处于NAT之后,并且基于已有的rport,将消息的真实地址即公网上的地址通过received和rport字段返回给client端,这样client就知道自己真实的公网地址,可以解决信令穿越的问题。
而有网友提出,使用Android模拟器通过路由器时端口会发生变化!~ 不知道这是不是真的!
它又是如何处理登陆超时以及通话出错的?
这就涉及到封装处理Sip协议的类了!~涉及的类不多,感兴趣的童鞋就自己研究了,我累了!~
有些问题需要讨论!~~欢迎高手指点!~