研究了SipDroid2.7,自己对它的理解也渐渐的清晰了。
那它是怎样实现电话拨打以及电话监听的?它的音频接收以及发送是怎么实现的?它的视频又是怎么一回事?它在模拟器上的端口为什么总是变化的?它又是如何处理登陆超时以及通话出错的?
带着这些疑问进入它的代码思想境界!
使用yate搭配服务器,然后使用了一个yate与SipDroid客户端进行通话!~至于怎么搭配服务器以及SipDroid的配置设置,此处就不讨论了!~
登陆后的标识效果如图:
然后我使用了yate客户端程序拨打了SipDroid程序段上的帐号(banketree),如图:
接通后的效果图像如图:
好了,进入我们的主题了!~
它是怎样实现电话拨打以及电话监听的?
程序进入时会进行服务注册,如下:
- // 实例化一个引擎 注册模式 由登陆时进行……
- / 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;
- }
而StartEngine()里有开启监听!~
- //注册 在此向服务器发送请求。
- 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();
- }
向服务器发送信息(发送协议进行登陆以及获取对方信息等等)也是由该类完成的!~如下:
- /**
- * 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;
- }
那它的音频接收以及发送是怎么实现的?
管理音频有一个类,它是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()
- {
- //测试音频数据
- // 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();
- }
之后就进入到涉及视频的类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协议的类了!~涉及的类不多,感兴趣的童鞋就自己研究了,我累了!~
有些问题需要讨论!~~欢迎高手指点!~