再谈SipDroid

研究了SipDroid2.7,自己对它的理解也渐渐的清晰了。

那它是怎样实现电话拨打以及电话监听的?它的音频接收以及发送是怎么实现的?它的视频又是怎么一回事?它在模拟器上的端口为什么总是变化的?它又是如何处理登陆超时以及通话出错的?

带着这些疑问进入它的代码思想境界!

使用yate搭配服务器,然后使用了一个yate与SipDroid客户端进行通话!~至于怎么搭配服务器以及SipDroid的配置设置,此处就不讨论了!~ 

登陆后的标识效果如图:


然后我使用了yate客户端程序拨打了SipDroid程序段上的帐号(banketree),如图:



接通后的效果图像如图:


好了,进入我们的主题了!~

它是怎样实现电话拨打以及电话监听的?

程序进入时会进行服务注册,如下:

[java]  view plain copy
  1. // 实例化一个引擎 注册模式    由登陆时进行……  
  2. /   Receiver.engine(this).registerMore();  

我们知道Receiver.engine(this) 进行了SipUA引擎的实例化,并开启SipUA引擎,关键就SipUA引擎了!~

[java]  view plain copy
  1. // 构造引擎  
  2. public static synchronized SipdroidEngine engine(Context context)  
  3. {  
  4.     // 构造引擎的条件  
  5.     if (mContext == null  
  6.             || !context.getClass().getName()  
  7.                     .contains("ReceiverRestrictedContext"))  
  8.     {  
  9.         mContext = context;  
  10.     }  
  11.   
  12.     // 为空则构造  
  13.     if (mSipdroidEngine == null)  
  14.     {  
  15.         mSipdroidEngine = new SipdroidEngine();  
  16.   
  17.         // 开始   
  18.         mSipdroidEngine.StartEngine();  
  19.   
  20.         // 开启蓝牙服务  
  21.         if (Integer.parseInt(Build.VERSION.SDK) >= 8)  
  22.         {  
  23.             Bluetooth.init();  
  24.         }  
  25.     } else  
  26.     {  
  27.         // 引擎已经存在  
  28.         mSipdroidEngine.CheckEngine();  
  29.     }  
  30.   
  31.     // 开启服务  
  32.     context.startService(new Intent(context, RegisterService.class));  
  33.   
  34.     return mSipdroidEngine;  
  35. }  

而StartEngine()里有开启监听!~

[java]  view plain copy
  1. //注册  在此向服务器发送请求。  
  2. register();  
  3.   
  4. //实例化一个ExtendedCall 循环监听指定端口   
  5. listen();  

至于里面是怎么实现的,那就涉及到了SipUA封装的一些类以及消息了!~ 

消息以及协议的通信都是SipProvider类由完成的!~

[java]  view plain copy
  1. public void onReceivedMessage(Transport transport, Message msg)  
  2. {  
  3.     if (pm == null)  
  4.     {  
  5.         pm = (PowerManager) Receiver.mContext.getSystemService(Context.POWER_SERVICE);  
  6.         wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Sipdroid.SipProvider");  
  7.     }  
  8.     wl.acquire(); // modified  
  9.     //处理接收消息   当程序进行了监听,就会向此类添加一个监听事件标识   
  10.     //处理消息就是根据标识进行区分的,例如来电、接听、视频等等!~~~  
  11.     processReceivedMessage(msg);    
  12.     wl.release();  
  13. }  

向服务器发送信息(发送协议进行登陆以及获取对方信息等等)也是由该类完成的!~如下:

[java]  view plain copy
  1. /** 
  2.      * Sends a Message, specifing the transport portocol, nexthop address and 
  3.      * port. 
  4.      * 发送一条消息,指定传输协议、地址、以及端口。 
  5.      */  
  6.     private ConnectionIdentifier sendMessage(Message msg, String proto, IpAddress dest_ipaddr, int dest_port, int ttl)  
  7.     {  
  8.         if(bDebug)  
  9.         {  
  10.             android.util.Log.i("SipProvider发送消息""msg:"+msg.toString());  
  11.         }  
  12.           
  13.         ConnectionIdentifier conn_id = new ConnectionIdentifier(proto, dest_ipaddr, dest_port);  
  14.         if (log_all_packets || msg.getLength() > MIN_MESSAGE_LENGTH)  
  15.             printLog("Sending message to " + conn_id, LogLevel.MEDIUM);  
  16.   
  17.         if (transport_udp && proto.equals(PROTO_UDP))  
  18.         {   
  19.             // UDP  
  20.             // printLog("using UDP",LogLevel.LOW);  
  21.             conn_id = null;  
  22.             try  
  23.             {   
  24.                 // if (ttl>0 && multicast_address) do something?  
  25.                 udp.sendMessage(msg, dest_ipaddr, dest_port);  
  26.             } catch (IOException e)  
  27.             {  
  28.                 printException(e, LogLevel.HIGH);  
  29.                 return null;  
  30.             }  
  31.         } else if (transport_tcp && proto.equals(PROTO_TCP))  
  32.         {   
  33.             // TCP  
  34.             // printLog("using TCP",LogLevel.LOW);  
  35.             if (connections == null || !connections.containsKey(conn_id))  
  36.             {   
  37.                 // modified  
  38.                 printLog("no active connection found matching " + conn_id, LogLevel.MEDIUM);  
  39.                 printLog("open " + proto + " connection to " + dest_ipaddr + ":" + dest_port, LogLevel.MEDIUM);  
  40.                 TcpTransport conn = null;  
  41.                 try  
  42.                 {  
  43.                     conn = new TcpTransport(dest_ipaddr, dest_port, this);  
  44.                 } catch (Exception e)  
  45.                 {  
  46.                     printLog("connection setup FAILED", LogLevel.HIGH);  
  47.                     return null;  
  48.                 }  
  49.                   
  50.                 printLog("connection " + conn + " opened", LogLevel.HIGH);  
  51.                 addConnection(conn);  
  52.                 if (!msg.isRegister())  
  53.                     Receiver.engine(Receiver.mContext).register(); // modified  
  54.             } else  
  55.             {  
  56.                 printLog("active connection found matching " + conn_id, LogLevel.MEDIUM);  
  57.             }  
  58.             ConnectedTransport conn = (ConnectedTransport) connections.get(conn_id);  
  59.             if (conn != null)  
  60.             {  
  61.                 printLog("sending data through conn " + conn, LogLevel.MEDIUM);  
  62.                 try  
  63.                 {  
  64.                     conn.sendMessage(msg);  
  65.                     conn_id = new ConnectionIdentifier(conn);  
  66.                 } catch (IOException e)  
  67.                 {  
  68.                     printException(e, LogLevel.HIGH);  
  69.                     return null;  
  70.                 }  
  71.             } else  
  72.             {   
  73.                 // this point has not to be reached这一点还没有达到  
  74.                 printLog("ERROR: conn " + conn_id + " not found: abort.", LogLevel.MEDIUM);  
  75.                 return null;  
  76.             }  
  77.         } else  
  78.         { // otherwise  
  79.             printWarning("Unsupported protocol (" + proto + "): Message discarded", LogLevel.HIGH);  
  80.             return null;  
  81.         }  
  82.         // logs  
  83.         String dest_addr = dest_ipaddr.toString();  
  84.         printMessageLog(proto, dest_addr, dest_port, msg.getLength(), msg, "sent");  
  85.         return conn_id;  
  86.     }  

消息的信息格式如下:


……

回到问题中来,监听电话已经明白了,那是如何实现拨打的?

当用户登陆后就会把自己的信息发送给服务端,服务端记录下来,等用户需要时就返回给他,比如我打banketree电话,我没有他的信息我怎么打呀,是吧!~

拨打电话的关键在UserAgent中,其它的都是封装好处理信息的类!~

[java]  view plain copy
  1. public boolean call(String target_url, boolean send_anonymous)  
  2. {  
  3.   
  4.     if (Receiver.call_state != UA_STATE_IDLE)  
  5.     {  
  6.         // We can initiate or terminate a call only when  
  7.         // we are in an idle state  
  8.         //只有当我们处于闲置状态,我们可以开始或结束通话  
  9.         printLog("Call attempted in state" + this.getSessionDescriptor()  
  10.                 + " : Failing Request", LogLevel.HIGH);  
  11.         return false;  
  12.     }  
  13.       
  14.     //挂断  
  15.     hangup(); // modified  
  16.       
  17.     //改变状态  
  18.     changeStatus(UA_STATE_OUTGOING_CALL, target_url);  
  19.   
  20.     String from_url;  
  21.   
  22.     if (!send_anonymous)  
  23.     {  
  24.         from_url = user_profile.from_url;  
  25.     } else  
  26.     {  
  27.         from_url = "sip:anonymous@anonymous.com";  
  28.     }  
  29.   
  30.     // change start multi codecs  更改启动多编解码器  
  31.     createOffer();  
  32.     // change end  
  33.       
  34.     call = new ExtendedCall(sip_provider, from_url,  
  35.             user_profile.contact_url, user_profile.username,  
  36.             user_profile.realm, user_profile.passwd, this);  
  37.   
  38.     // in case of incomplete url (e.g. only 'user' is present), try to  
  39.     // complete it  
  40.     //在不完整的URL(例如,“用户”是存在的话)的情况下,尽量去完成它  
  41.     if (target_url.indexOf("@") < 0)  
  42.     {  
  43.         if (user_profile.realm.equals(Settings.DEFAULT_SERVER))  
  44.             target_url = "&" + target_url;  
  45.           
  46.         target_url = target_url + "@" + realm; // modified  
  47.     }  
  48.   
  49.     // MMTel addition to define MMTel ICSI to be included in INVITE (added  
  50.     // by mandrajg)  要包含在INVITE MMTel除了定义MMTel ICSI  
  51.     String icsi = null;  
  52.     if (user_profile.mmtel == true)  
  53.     {  
  54.         icsi = "\"urn%3Aurn-7%3A3gpp-service.ims.icsi.mmtel\"";  
  55.     }  
  56.   
  57.     //从服务器中获得对方的地址  
  58.     target_url = sip_provider.completeNameAddress(target_url).toString();  
  59.       
  60.     //真的拨打电话  
  61.     if (user_profile.no_offer)  
  62.     {  
  63.         call.call(target_url);  
  64.     } else  
  65.     {  
  66.         call.call(target_url, local_session, icsi); // modified by mandrajg  
  67.     }  
  68.   
  69.     return true;  
  70. }  

那它的音频接收以及发送是怎么实现的?

管理音频有一个类,它是JAudioLauncher,当实例化它的时候,它会开启两个线程,一个是RtpStreamReceiver,另一个是RtpStreamReceiver!~

看标题就知道它们一个是发送的,一个是接收的!~

先来看下接收是怎么实现的!~

[java]  view plain copy
  1.     public void run()  
  2.     {  
  3.         boolean nodata = PreferenceManager.getDefaultSharedPreferences(  
  4.                 Receiver.mContext).getBoolean(  
  5.                 org.sipdroid.sipua.ui.Settings.PREF_NODATA,  
  6.                 org.sipdroid.sipua.ui.Settings.DEFAULT_NODATA);  
  7.         keepon = PreferenceManager.getDefaultSharedPreferences(  
  8.                 Receiver.mContext).getBoolean(  
  9.                 org.sipdroid.sipua.ui.Settings.PREF_KEEPON,  
  10.                 org.sipdroid.sipua.ui.Settings.DEFAULT_KEEPON);  
  11.   
  12.         if (rtp_socket == null)  
  13.         {  
  14.             if (DEBUG)  
  15.             {  
  16.                 println("ERROR: RTP socket is null(出错:rtp接受套接字出错!)");  
  17.             }  
  18.             return;  
  19.         }  
  20.   
  21.         //发送包 缓冲区  
  22.         byte[] buffer = new byte[BUFFER_SIZE + 12];  
  23.           
  24.         //构建包实例  
  25.         rtp_packet = new RtpPacket(buffer, 0);  
  26.   
  27.         if (DEBUG)  
  28.         {  
  29.             println("Reading blocks of max (读取快的最大值) = " + buffer.length + " bytes");  
  30.         }  
  31.   
  32.         running = true;  
  33.           
  34.         //开启蓝牙  
  35.         enableBluetooth(PreferenceManager.getDefaultSharedPreferences(  
  36.                 Receiver.mContext).getBoolean(  
  37.                 org.sipdroid.sipua.ui.Settings.PREF_BLUETOOTH,  
  38.                 org.sipdroid.sipua.ui.Settings.DEFAULT_BLUETOOTH));  
  39.           
  40.         restored = false;  
  41.   
  42.         //设置线程权限  
  43.         android.os.Process  
  44.                 .setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);  
  45.           
  46.         am = (AudioManager) Receiver.mContext  
  47.                 .getSystemService(Context.AUDIO_SERVICE);  
  48.         cr = Receiver.mContext.getContentResolver();  
  49.           
  50.         //保存设置  
  51.         saveSettings();  
  52.         Settings.System.putInt(cr, Settings.System.WIFI_SLEEP_POLICY,  
  53.                 Settings.System.WIFI_SLEEP_POLICY_NEVER);  
  54.         am.setVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER,  
  55.                 AudioManager.VIBRATE_SETTING_OFF);  
  56.         am.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,  
  57.                 AudioManager.VIBRATE_SETTING_OFF);  
  58.         if (oldvol == -1)  
  59.         {  
  60.             oldvol = am.getStreamVolume(AudioManager.STREAM_MUSIC);  
  61.         }  
  62.   
  63.         //初始化模式  
  64.         initMode();  
  65.           
  66.         //设置音频编解码器  
  67.         setCodec();  
  68.   
  69.         //编辑音频  由发送包解码后放到此处 然后调整后放入到声音播放器中  
  70.         short lin[] = new short[BUFFER_SIZE];  
  71.         //它是负责清空lin的  
  72.         short lin2[] = new short[BUFFER_SIZE];  
  73.         int server, headroom, todo, len = 0, m = 1, expseq, getseq, vm = 1, gap, gseq;  
  74.           
  75.         ToneGenerator tg = new ToneGenerator(  
  76.                 AudioManager.STREAM_VOICE_CALL,  
  77.                 (int) (ToneGenerator.MAX_VOLUME * 2 * org.sipdroid.sipua.ui.Settings  
  78.                         .getEarGain()));  
  79.           
  80.         //播放  
  81.         track.play();  
  82.           
  83.         //指示虚拟机运行垃圾收集器,这将是一个好时机。  
  84.         //请注意,这仅是一个提示。有没有保证,垃圾收集器将实际运行。  
  85.         System.gc();  
  86.           
  87.         //清空  
  88.         empty();  
  89.           
  90.         lockFirst = true;  
  91.   
  92.         while (running)  
  93.         {  
  94.             lock(true);  
  95.               
  96.             if (Receiver.call_state == UserAgent.UA_STATE_HOLD)  
  97.             {  
  98.                 lock(false);  
  99.                   
  100.                 //停止  
  101.                 tg.stopTone();  
  102.                   
  103.                 //暂停  
  104.                 track.pause();  
  105.                   
  106.                 while (running  
  107.                         && Receiver.call_state == UserAgent.UA_STATE_HOLD)  
  108.                 {  
  109.                     try  
  110.                     {  
  111.                         sleep(1000);  
  112.                     } catch (InterruptedException e1)  
  113.                     {  
  114.                     }  
  115.                 }  
  116.   
  117.                 track.play();  
  118.                 System.gc();  
  119.                 timeout = 1;  
  120.                 luser = luser2 = -8000 * mu;  
  121.             }  
  122.               
  123.             try  
  124.             {  
  125.                 // 接受数据  
  126.                 rtp_socket.receive(rtp_packet);  
  127.   
  128.                 //超时  
  129.                 if (timeout != 0)  
  130.                 {  
  131.                     //停止音乐  
  132.                     tg.stopTone();  
  133.                       
  134.                     //暂停声音  
  135.                     track.pause();  
  136.                       
  137.                     //清空  
  138.                     for (int i = maxjitter * 2; i > 0; i -= BUFFER_SIZE)  
  139.                     {  
  140.                         write(lin2, 0, i > BUFFER_SIZE ? BUFFER_SIZE : i);  
  141.                     }  
  142.   
  143.                     cnt += maxjitter * 2;  
  144.                       
  145.                     //声音播放  
  146.                     track.play();  
  147.                     empty();  
  148.                 }  
  149.                 timeout = 0;  
  150.             } catch (IOException e)  
  151.             {  
  152.                 if (timeout == 0 && nodata)  
  153.                 {  
  154.                     tg.startTone(ToneGenerator.TONE_SUP_RINGTONE);  
  155.                 }  
  156.   
  157.                 //异常者 断开  
  158.                 rtp_socket.getDatagramSocket().disconnect();  
  159.                   
  160.                 if (++timeout > 60)  
  161.                 {  
  162.                     Receiver.engine(Receiver.mContext).rejectcall();  
  163.                     break;  
  164.                 }  
  165.             }  
  166.               
  167.             //在运行 且未超时  
  168.             if (running && timeout == 0)  
  169.             {  
  170.                 //得到数字字符  
  171.                 gseq = rtp_packet.getSequenceNumber();  
  172.                   
  173.                 //得到当前接受包的大小  
  174.                 if (seq == gseq)  
  175.                 {  
  176.                     m++;  
  177.                     continue;  
  178.                 }  
  179.                   
  180.                   
  181.                 gap = (gseq - seq) & 0xff;  
  182.                   
  183.                 if (gap > 240)  
  184.                 {  
  185.                     continue;  
  186.                 }  
  187.                   
  188.                 //返回帧中表示播放头位置。  
  189.                 server = track.getPlaybackHeadPosition();  
  190.                   
  191.                 //接受包的总大小 - 当前播放的位置  
  192.                 headroom = user - server;  
  193.   
  194.                 if (headroom > 2 * jitter)  
  195.                 {  
  196.                     cnt += len;  
  197.                 } else  
  198.                 {  
  199.                     cnt = 0;  
  200.                 }  
  201.   
  202.                 if (lserver == server)  
  203.                 {  
  204.                     cnt2++;  
  205.                 } else  
  206.                 {  
  207.                     cnt2 = 0;  
  208.                 }  
  209.   
  210.                 if (cnt <= 500 * mu || cnt2 >= 2 || headroom - jitter < len  
  211.                         || p_type.codec.number() != 8  
  212.                         || p_type.codec.number() != 0)  
  213.                 {  
  214.                     //有效负荷类型||改变类型  
  215.                     if (rtp_packet.getPayloadType() != p_type.number  
  216.                             && p_type.change(rtp_packet.getPayloadType()))  
  217.                     {  
  218.                         //保留声量  
  219.                         saveVolume();  
  220.                           
  221.                         //设置编解码器  
  222.                         setCodec();  
  223.                           
  224.                         //恢复声量  
  225.                         restoreVolume();  
  226.                           
  227.                         //头信息  
  228.                         codec = p_type.codec.getTitle();  
  229.                     }  
  230.                       
  231.                     //得到有效负荷长度  
  232.                     len = p_type.codec.decode(buffer, lin,  
  233.                             rtp_packet.getPayloadLength());  
  234.   
  235.                     // Call recording: Save incoming.Data is in buffer lin, from 0 to len.  
  236.                     //通话记录:保存传入。数据是在缓冲区林,从0到LEN。  
  237.                     if (call_recorder != null)  
  238.                     {  
  239.                         //写入  
  240.                         call_recorder.writeIncoming(lin, 0, len);  
  241.                     }  
  242.   
  243.                     //声音改变效果显示  
  244.                     if (speakermode == AudioManager.MODE_NORMAL)  
  245.                     {  
  246.                         calc(lin, 0, len);  
  247.                     } else if (gain > 1)  
  248.                     {  
  249.                         calc2(lin, 0, len);  
  250.                     }  
  251.                 }  
  252.   
  253.                 //  
  254.                 avgheadroom = avgheadroom * 0.99 + (double) headroom * 0.01;  
  255.                 if (avgcnt++ > 300)  
  256.                 {  
  257.                     devheadroom = devheadroom * 0.999  
  258.                             + Math.pow(Math.abs(headroom - avgheadroom), 2)  
  259.                             * 0.001;  
  260.                 }  
  261.                   
  262.                 //头大小  
  263.                 if (headroom < 250 * mu)  
  264.                 {  
  265.                     late++;  
  266.                     newjitter(true);  
  267. //                  System.out.println("RTP:underflow "  
  268. //                          + (int) Math.sqrt(devheadroom));  
  269.                     todo = jitter - headroom;  
  270.                     write(lin2, 0, todo > BUFFER_SIZE ? BUFFER_SIZE : todo);  
  271.                 }  
  272.   
  273.                 //  
  274.                 if (cnt > 500 * mu && cnt2 < 2)  
  275.                 {  
  276.                     todo = headroom - jitter;  
  277.                     if (todo < len)  
  278.                     {  
  279.                         write(lin, todo, len - todo);  
  280.                     }  
  281.                 } else  
  282.                 {  
  283.                     write(lin, 0, len);  
  284.                 }  
  285.   
  286.                 //丢失包统计  
  287.                 if (seq != 0)  
  288.                 {  
  289.                     getseq = gseq & 0xff;  
  290.                     expseq = ++seq & 0xff;  
  291.                     if (m == RtpStreamSender.m)  
  292.                     {  
  293.                         vm = m;  
  294.                     }  
  295.                       
  296.                     gap = (getseq - expseq) & 0xff;  
  297.                     if (gap > 0)  
  298.                     {  
  299.                     //  System.out.println("RTP:lost");  
  300.                         if (gap > 100)  
  301.                         {  
  302.                             gap = 1;  
  303.                         }  
  304.                         loss += gap;  
  305.                         lost += gap;  
  306.                         good += gap - 1;  
  307.                         loss2++;  
  308.                     } else  
  309.                     {  
  310.                         if (m < vm)  
  311.                         {  
  312.                             loss++;  
  313.                             loss2++;  
  314.                         }  
  315.                     }  
  316.                     good++;  
  317.                     if (good > 110)  
  318.                     {  
  319.                         good *= 0.99;  
  320.                         lost *= 0.99;  
  321.                         loss *= 0.99;  
  322.                         loss2 *= 0.99;  
  323.                         late *= 0.99;  
  324.                     }  
  325.                 }  
  326.                 m = 1;  
  327.                 seq = gseq;  
  328.   
  329.                 if (user >= luser + 8000 * mu  
  330.                         && (Receiver.call_state == UserAgent.UA_STATE_INCALL || Receiver.call_state == UserAgent.UA_STATE_OUTGOING_CALL))  
  331.                 {  
  332.                     if (luser == -8000 * mu || getMode() != speakermode)  
  333.                     {  
  334.                         //保留声量  
  335.                         saveVolume();  
  336.                           
  337.                         //设置模式  
  338.                         setMode(speakermode);  
  339.                           
  340.                         //恢复声量  
  341.                         restoreVolume();  
  342.                     }  
  343.                   
  344.                     luser = user;  
  345.                       
  346.                     if (user >= luser2 + 160000 * mu)  
  347.                     {  
  348.                         //抖动  
  349.                         newjitter(false);  
  350.                     }  
  351.                 }  
  352.                 lserver = server;  
  353.             }  
  354.         }  

再看下发送包是怎么实现的,发送声音肯定是先录制,后读取,再发送!~如下:

[java]  view plain copy
  1.     public void run()  
  2.     {  
  3.         //测试音频数据  
  4. //      testVoiceFile mvoiceFile = new testVoiceFile("voice");  
  5. //      testVoiceFile mvoiceRtpFile = new testVoiceFile("rtpdate");  
  6.           
  7.         //wifi管理  
  8.         WifiManager wm = (WifiManager) Receiver.mContext  
  9.                 .getSystemService(Context.WIFI_SERVICE);  
  10.         long lastscan = 0, lastsent = 0;  
  11.   
  12.         if (rtp_socket == null)  
  13.             return;  
  14.         int seqn = 0;  
  15.         long time = 0;  
  16.         double p = 0;  
  17.           
  18.         //改善  
  19.         boolean improve = PreferenceManager.getDefaultSharedPreferences(  
  20.                 Receiver.mContext).getBoolean(Settings.PREF_IMPROVE,  
  21.                 Settings.DEFAULT_IMPROVE);  
  22.           
  23.         //选择wifi  
  24.         boolean selectWifi = PreferenceManager.getDefaultSharedPreferences(  
  25.                 Receiver.mContext).getBoolean(  
  26.                 org.sipdroid.sipua.ui.Settings.PREF_SELECTWIFI,  
  27.                 org.sipdroid.sipua.ui.Settings.DEFAULT_SELECTWIFI);  
  28.           
  29.         int micgain = 0;  
  30.         long last_tx_time = 0;  
  31.         long next_tx_delay;  
  32.         long now;  
  33.         running = true;  
  34.         m = 1;  
  35.         int dtframesize = 4;  
  36.   
  37.         //改变线程权限  
  38.         android.os.Process  
  39.                 .setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);  
  40.           
  41.         //解码速率  
  42.         mu = p_type.codec.samp_rate() / 8000;  
  43.         int min = AudioRecord.getMinBufferSize(p_type.codec.samp_rate(),  
  44.                 AudioFormat.CHANNEL_CONFIGURATION_MONO,  
  45.                 AudioFormat.ENCODING_PCM_16BIT);  //最小缓冲大小  
  46.           
  47.         if (min == 640)  
  48.         {  
  49.             if (frame_size == 960)  
  50.                 frame_size = 320;  
  51.             if (frame_size == 1024)  
  52.                 frame_size = 160;  
  53.             min = 4096 * 3 / 2;  
  54.         } else if (min < 4096)  
  55.         {  
  56.             if (min <= 2048 && frame_size == 1024)  
  57.                 frame_size /= 2;  
  58.             min = 4096 * 3 / 2;  
  59.         } else if (min == 4096)  
  60.         {  
  61.             min *= 3 / 2;  
  62.             if (frame_size == 960)  
  63.                 frame_size = 320;  
  64.         } else  
  65.         {  
  66.             if (frame_size == 960)  
  67.                 frame_size = 320;  
  68.             if (frame_size == 1024)  
  69.                 frame_size *= 2;  
  70.         }  
  71.           
  72.         //速率  
  73.         frame_rate = p_type.codec.samp_rate() / frame_size;  
  74.         long frame_period = 1000 / frame_rate;  
  75.         frame_rate *= 1.5;  
  76.           
  77.         //发送包缓冲区  
  78.         byte[] buffer = new byte[frame_size + 12];  //  
  79.           
  80.         //实例化包  
  81.         RtpPacket rtp_packet = new RtpPacket(buffer, 0);  
  82.           
  83.         //设置有效复合  
  84.         rtp_packet.setPayloadType(p_type.number);  
  85.           
  86.         //调式输出信息  
  87.         if (DEBUG)  
  88.             println("Reading blocks of (读取块大小)" + buffer.length + " bytes");  
  89.   
  90.         println("Sample rate (采样率) = " + p_type.codec.samp_rate());  
  91.         println("Buffer size(缓冲区大小) = " + min);  
  92.   
  93.         //录制  
  94.         AudioRecord record = null;  
  95.   
  96.         //获得声音内容变量  
  97.         short[] lin = new short[frame_size * (frame_rate + 1)];  
  98.         int num, ring = 0, pos;  
  99.           
  100.         //随机数  
  101.         random = new Random();  
  102.           
  103.         //输入流  
  104.         InputStream alerting = null;  
  105.         try  
  106.         {  
  107.             //接受  
  108.             alerting = Receiver.mContext.getAssets().open("alerting");  
  109.         } catch (IOException e2)  
  110.         {  
  111.             if (!Sipdroid.release)  
  112.                 e2.printStackTrace();  
  113.         }  
  114.           
  115.         //初始化音频编码  
  116.         p_type.codec.init();  
  117.           
  118.         if(Sipdroid.VoiceDebug)  
  119.         {  
  120.             Log.i("RtpStreamSender""音频发送开始啦");  
  121.         }  
  122.           
  123.         //循环发送  先录制 然后发送  
  124.         while (running)  
  125.         {  
  126.             //录制没有 或已经改变  
  127.             if (changed || record == null)  
  128.             {  
  129.                 if (record != null)  
  130.                 {  
  131.                     //释放操作  
  132.                     record.stop();  
  133.                     record.release();  
  134.                     if (RtpStreamReceiver.samsung)  
  135.                     {  
  136.                         AudioManager am = (AudioManager) Receiver.mContext  
  137.                                 .getSystemService(Context.AUDIO_SERVICE);  
  138.                         am.setMode(AudioManager.MODE_IN_CALL);  
  139.                         am.setMode(AudioManager.MODE_NORMAL);  
  140.                     }  
  141.                 }  
  142.                   
  143.                 //未改变  
  144.                 changed = false;  
  145.                   
  146.                 //实例化录制  
  147.                 record = new AudioRecord(MediaRecorder.AudioSource.MIC,  
  148.                         p_type.codec.samp_rate(),  
  149.                         AudioFormat.CHANNEL_CONFIGURATION_MONO,  
  150.                         AudioFormat.ENCODING_PCM_16BIT, min);  
  151.                   
  152.                 if(Sipdroid.VoiceDebug)  
  153.                 {  
  154.                     Log.i("RtpStreamSender""构造音频录制实例");  
  155.                 }  
  156.                   
  157.                 //得到录制类型  
  158.                 if (record.getState() != AudioRecord.STATE_INITIALIZED)  
  159.                 {  
  160.                     //拒接  
  161.                     Receiver.engine(Receiver.mContext).rejectcall();  
  162.                     record = null;  
  163.                     break;  
  164.                 }  
  165.                   
  166.                 //开始录制  
  167.                 record.startRecording();  
  168.                   
  169.                 //计算  
  170.                 micgain = (int) (Settings.getMicGain() * 10);  
  171.             }  
  172.               
  173.               
  174.             //静音或挂断 则停止  
  175.             if (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD)  
  176.             {  
  177.                 //挂断  
  178.                 if (Receiver.call_state == UserAgent.UA_STATE_HOLD)  
  179.                 {  
  180.                     //恢复模式  
  181.                     RtpStreamReceiver.restoreMode();  
  182.                 }  
  183.                   
  184.                 if(Sipdroid.VoiceDebug)  
  185.                 {  
  186.                     Log.i("RtpStreamSender""挂断电话则停止录制");  
  187.                 }  
  188.                   
  189.                 //停止录音  
  190.                 record.stop();  
  191.               
  192.                 //延时  
  193.                 while (running  
  194.                         && (muted || Receiver.call_state == UserAgent.UA_STATE_HOLD))  
  195.                 {  
  196.                     try  
  197.                     {  
  198.                         sleep(1000);  
  199.                     } catch (InterruptedException e1)  
  200.                     {  
  201.                     }  
  202.                 }  
  203.                   
  204.                 //开始录制  
  205.                 record.startRecording();  
  206.             }  
  207.               
  208.             // DTMF change start  
  209.             if (dtmf.length() != 0)  
  210.             {  
  211.                 //构造包  
  212.                 byte[] dtmfbuf = new byte[dtframesize + 12];  
  213.                 RtpPacket dt_packet = new RtpPacket(dtmfbuf, 0);  
  214.                   
  215.                 //设置有效负荷  
  216.                 dt_packet.setPayloadType(dtmf_payload_type);  
  217.                   
  218.                 //设置大小  
  219.                 dt_packet.setPayloadLength(dtframesize);  
  220.                 dt_packet.setSscr(rtp_packet.getSscr());  
  221.                 long dttime = time;  
  222.                 int duration;  
  223.   
  224.                 for (int i = 0; i < 6; i++)  
  225.                 {  
  226.                     time += 160;  
  227.                     duration = (int) (time - dttime);  
  228.                     dt_packet.setSequenceNumber(seqn++);  
  229.                     dt_packet.setTimestamp(dttime);  
  230.                     dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));  
  231.                     dtmfbuf[13] = (byte0x0a;  
  232.                     dtmfbuf[14] = (byte) (duration >> 8);  
  233.                     dtmfbuf[15] = (byte) duration;  
  234.                     try  
  235.                     {  
  236.                         //发送包 声音包  
  237.                         rtp_socket.send(dt_packet);  
  238.                           
  239.                         if (Sipdroid.VoiceDebug)  
  240.                         {  
  241.                             Log.i("RtpStreamSender",  
  242.                                     "第一次发送声音包  大小" + dt_packet.getLength()  
  243.                                             + " " + dt_packet.getPacket());  
  244.                         }  
  245.                           
  246.                         if(bShowVoiceDecodeData)  
  247.                         {  
  248. //                          mvoiceRtpFile.write(dt_packet.getPacket());  
  249.                         }  
  250.   
  251.                         sleep(20);  
  252.                     } catch (Exception e1)  
  253.                     {  
  254.                     }  
  255.                 }  
  256.                   
  257.                 //发送回音?  
  258.                 for (int i = 0; i < 3; i++)  
  259.                 {  
  260.                     duration = (int) (time - dttime);  
  261.                     dt_packet.setSequenceNumber(seqn);  
  262.                     dt_packet.setTimestamp(dttime);  
  263.                     dtmfbuf[12] = rtpEventMap.get(dtmf.charAt(0));  
  264.                     dtmfbuf[13] = (byte0x8a;  
  265.                     dtmfbuf[14] = (byte) (duration >> 8);  
  266.                     dtmfbuf[15] = (byte) duration;  
  267.                     try  
  268.                     {  
  269.                         //发送包  
  270.                         rtp_socket.send(dt_packet);  
  271.                           
  272.                         if (Sipdroid.VoiceDebug)  
  273.                         {  
  274.                             Log.i("RtpStreamSender",  
  275.                                     "第二次发送声音包  大小" + dt_packet.getLength()  
  276.                                             + " " + dt_packet.getPacket());  
  277.                         }  
  278.                           
  279.                         if(bShowVoiceDecodeData)  
  280.                         {  
  281. //                          mvoiceRtpFile.write(dt_packet.getPacket());  
  282.                         }  
  283.                     } catch (Exception e1)  
  284.                     {  
  285.                     }  
  286.                 }  
  287.                 time += 160;  
  288.                 seqn++;  
  289.                 dtmf = dtmf.substring(1);  
  290.             }  
  291.             // DTMF change end  
  292.   
  293.             if (frame_size < 480)  
  294.             {  
  295.                 now = System.currentTimeMillis();  
  296.                 next_tx_delay = frame_period - (now - last_tx_time);  
  297.                 last_tx_time = now;  
  298.                 if (next_tx_delay > 0)  
  299.                 {  
  300.                     try  
  301.                     {  
  302.                         sleep(next_tx_delay);  
  303.                     } catch (InterruptedException e1)  
  304.                     {  
  305.                     }  
  306.                     last_tx_time += next_tx_delay - sync_adj;  
  307.                 }  
  308.             }  
  309.               
  310.             //获得发送的位置  
  311.             pos = (ring + delay * frame_rate * frame_size)  
  312.                     % (frame_size * (frame_rate + 1));  
  313.               
  314.             //得到大小  
  315.             num = record.read(lin, pos, frame_size);  
  316.               
  317.             if (num <= 0)  
  318.                 continue;  
  319.               
  320.             //是否有效  
  321.             if (!p_type.codec.isValid())  
  322.                 continue;  
  323.   
  324.             // Call recording: Save the frame to the CallRecorder.  
  325.             //通话记录:框架保存的CallRecorder的。  新的录制   
  326.             if (call_recorder != null)  
  327.             {  
  328.                 //写入 输出  
  329.                 call_recorder.writeOutgoing(lin, pos, num);  
  330.             }  
  331.   
  332.             if (RtpStreamReceiver.speakermode == AudioManager.MODE_NORMAL)  
  333.             {  
  334.                 calc(lin, pos, num);  
  335.                 if (RtpStreamReceiver.nearend != 0  
  336.                         && RtpStreamReceiver.down_time == 0)  
  337.                 {  
  338.                     noise(lin, pos, num, p / 2);  
  339.                 }  
  340.                 else if (nearend == 0)  
  341.                 {  
  342.                     p = 0.9 * p + 0.1 * s;  
  343.                 }  
  344.             } else  
  345.             {  
  346.                 switch (micgain)  
  347.                 {  
  348.                 case 1:  
  349.                     calc1(lin, pos, num);  
  350.                     break;  
  351.                 case 2:  
  352.                     calc2(lin, pos, num);  
  353.                     break;  
  354.                 case 10:  
  355.                     calc10(lin, pos, num);  
  356.                     break;  
  357.                 }  
  358.             }  
  359.               
  360.             iCount++;  
  361.               
  362.             //通话中  
  363.             if (Receiver.call_state != UserAgent.UA_STATE_INCALL  
  364.                     && Receiver.call_state != UserAgent.UA_STATE_OUTGOING_CALL  
  365.                     && alerting != null)  
  366.             {  
  367.                 try  
  368.                 {  
  369.                     if (alerting.available() < num / mu)  
  370.                     {  
  371.                         alerting.reset();  
  372.                     }  
  373.                     alerting.read(buffer, 12, num / mu);  
  374.                 } catch (IOException e)  
  375.                 {  
  376.                     if (!Sipdroid.release)  
  377.                     {  
  378.                         e.printStackTrace();  
  379.                     }  
  380.                 }  
  381.                 if (p_type.codec.number() != 8)  
  382.                 {  
  383.                     G711.alaw2linear(buffer, lin, num, mu);  
  384.                     num = p_type.codec.encode(lin, 0, buffer, num);  
  385.                       
  386. //                  if(bShowVoiceDecodeData)  
  387. //                  {  
  388. //                      byte[] sdf = lin.toString().getBytes();  
  389. //                      mvoiceFile.write(sdf);  
  390. //                        
  391. //                      String string = "";  
  392. //                        
  393. //                      for(short i:buffer)  
  394. //                          string+=i;  
  395. //                                
  396. //                      Log.i("p_type.codec.encode",iCount +"编码后的数据(buffer):"+string);  
  397. //                  }  
  398.                 }  
  399.             } else  
  400.             {  
  401.                 num = p_type.codec.encode(lin, ring  
  402.                         % (frame_size * (frame_rate + 1)), buffer, num); //进行了编码  
  403.                   
  404. //              if(bShowVoiceDecodeData)  
  405. //              {  
  406. //                  mvoiceFile.write(buffer);  
  407. //                    
  408. //                  String string = "";  
  409. //                    
  410. //                  for(short i:buffer)  
  411. //                      string+=i;  
  412. //                            
  413. //                  Log.i("p_type.codec.encode",iCount +"编码后的数据(buffer):"+string);  
  414. //              }  
  415.             }  
  416.               
  417.               
  418.   
  419.             //大小  
  420.             ring += frame_size;  
  421.             rtp_packet.setSequenceNumber(seqn++);  
  422.             rtp_packet.setTimestamp(time);  
  423.             rtp_packet.setPayloadLength(num);  
  424.               
  425.             //记录时间  
  426.             now = SystemClock.elapsedRealtime();              
  427.               
  428.             if (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan  
  429.                     || now - lastsent > 500)  
  430.             {  
  431.                 try  
  432.                 {  
  433.                     lastsent = now;  
  434.                     rtp_socket.send(rtp_packet);  
  435.                   
  436.                     if (m > 1  
  437.                             && (RtpStreamReceiver.timeout == 0 || Receiver.on_wlan))  
  438.                     {  
  439.                         for (int i = 1; i < m; i++)  
  440.                             rtp_socket.send(rtp_packet);  
  441.                     }  
  442.                       
  443.                     if (Sipdroid.VoiceDebug)  
  444.                     {  
  445.                         Log.i("RtpStreamSender",  
  446.                                 "第三次发送声音包  大小" + rtp_packet.getLength()  
  447.                                         + " " + rtp_packet.getPacket());  
  448.                     }  
  449.                       
  450. //                  if(bShowVoiceDecodeData)  
  451. //                  {  
  452. //                      mvoiceRtpFile.write(rtp_packet.getPacket());  
  453. //                  }  
  454.                 } catch (Exception e)  
  455.                 {  
  456.                 }  
  457.             }  
  458.               
  459.             //编码数  
  460.             if (p_type.codec.number() == 9)  
  461.             {  
  462.                 time += frame_size / 2;  
  463.             }  
  464.             else  
  465.             {  
  466.                 time += frame_size;  
  467.             }  
  468.               
  469.             if (RtpStreamReceiver.good != 0  
  470.                     && RtpStreamReceiver.loss2 / RtpStreamReceiver.good > 0.01)  
  471.             {  
  472.                 if (selectWifi && Receiver.on_wlan && now - lastscan > 10000)  
  473.                 {  
  474.                     wm.startScan();  
  475.                     lastscan = now;  
  476.                 }  
  477.                 if (improve  
  478.                         && delay == 0  
  479.                         && (p_type.codec.number() == 0  
  480.                                 || p_type.codec.number() == 8 || p_type.codec  
  481.                                 .number() == 9))  
  482.                 {  
  483.                     m = 2;  
  484.                 }  
  485.                 else  
  486.                 {  
  487.                     m = 1;  
  488.                 }  
  489.             } else  
  490.             {  
  491.                 m = 1;  
  492.             }  
  493.         }  

好了,进入下一个问题,它的视频又是怎么一回事?此处不谈它的视频编码,直接介绍涉及它的流程!~

涉及视频的类有:VideoCamera、VideoCameraNew、VideoCameraNew2、VideoPreview!~ 而是否使用视频,关键在CallScreen类,CallScreen类是通话的界面!~

如下是否使用视频的代码,该代码在CallScreen中!~

[java]  view plain copy
  1. //是否开启视频流程关键地方     必须是在 电话中并且端口等设置正确   
  2. if (Receiver.call_state == UserAgent.UA_STATE_INCALL  
  3.         && socket == null  
  4.         && Receiver.engine(mContext).getLocalVideo() != 0  
  5.         && Receiver.engine(mContext).getRemoteVideo() != 0  
  6.         && PreferenceManager  
  7.                 .getDefaultSharedPreferences(this)  
  8.                 .getString(org.sipdroid.sipua.ui.Settings.PREF_SERVER,  
  9.                         org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER)  
  10.                 .equals(org.sipdroid.sipua.ui.Settings.DEFAULT_SERVER))  
  11. {  
  12.     (new Thread()  
  13.     {  
  14.         public void run()  
  15.         {                     
  16.             // 视频包 在线包  
  17.             RtpPacket keepalive = new RtpPacket(new byte[12], 0);  
  18.             RtpPacket videopacket = new RtpPacket(new byte[1000], 0);  
  19.   
  20.             try  
  21.             {  
  22.                 if (intent == null || rtp_socket == null)  
  23.                 {  
  24.                     // 新建一个套接字  
  25.                     rtp_socket = new RtpSocket(  
  26.   
  27.                     // 初始化套接字  
  28.                             socket = new SipdroidSocket(Receiver  
  29.                                     .engine(mContext).getLocalVideo()),  
  30.   
  31.                             // 设置网络地址  
  32.                             InetAddress.getByName(Receiver.engine(  
  33.                                     mContext).getRemoteAddr()),  
  34.   
  35.                             // 接受远程视频  
  36.                             Receiver.engine(mContext).getRemoteVideo());  
  37.                     sleep(3000);  
  38.                 } else  
  39.                 {  
  40.                     // 接受数据  
  41.                     socket = rtp_socket.getDatagramSocket();  
  42.                 }  
  43.                 // 接受数据  
  44.                 rtp_socket.getDatagramSocket().setSoTimeout(15000);  
  45.             } catch (Exception e)  
  46.             {  
  47.                 if (!Sipdroid.release)  
  48.                 {  
  49.                     e.printStackTrace();  
  50.                 }  
  51.                 return;  
  52.             }  
  53.   
  54.             // 设置有效载荷类型  
  55.             keepalive.setPayloadType(126);  
  56.   
  57.             try  
  58.             {  
  59.                 // 发送数据  
  60.                 rtp_socket.send(keepalive);  
  61.             } catch (Exception e1)  
  62.             {  
  63.                 return;  
  64.             }  
  65.             for (;;)  
  66.             {  
  67.                 try  
  68.                 {  
  69.                     // 循环接收数据  
  70.                     rtp_socket.receive(videopacket);  
  71.                 } catch (IOException e)  
  72.                 {  
  73.                     // 异常则断开  
  74.                     rtp_socket.getDatagramSocket().disconnect();  
  75.                     try  
  76.                     {  
  77.                         // 发送在线包  
  78.                         rtp_socket.send(keepalive);  
  79.                     } catch (IOException e1)  
  80.                     {  
  81.                         return;  
  82.                     }  
  83.                 }  
  84.   
  85.                 // 得到有效负荷长度  
  86.                 if (videopacket.getPayloadLength() > 200)  
  87.                 {  
  88.                     if (intent != null)  
  89.                     {  
  90.                         // 发送数据  
  91.                         intent.putExtra("justplay"true);  
  92.                         mHandler.sendEmptyMessage(0);  
  93.                     } else  
  94.                     {  
  95.                         // 否则播放  
  96.                         Intent i = new Intent(mContext,  
  97.                                 org.sipdroid.sipua.ui.VideoCamera.class);  
  98.                         i.putExtra("justplay"true);  
  99.                         startActivity(i);  
  100.                     }  
  101.                     return;  
  102.                 }  
  103.             }  
  104.         }  
  105.     }).start();  
  106. }  

之后就进入到涉及视频的类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协议的类了!~涉及的类不多,感兴趣的童鞋就自己研究了,我累了!~

有些问题需要讨论!~~欢迎高手指点!~

Sipdroid APK是一款用于Android系统的一款允许用户进行VoIP(Voice over Internet Protocol)通话的应用程序。VoIP是一种通过互联网而不是传统的电话线进行语音通信的技术。Sipdroid APK允许用户通过Wi-Fi或数据网络连接到其他使用相同协议的用户进行语音通话。 Sipdroid APK具有许多功能和优点。首先,它可以帮助用户节省通信费用,特别是在国际长途通话方面。通过使用互联网连接,用户可以享受更低廉的通话费用,并避免传统电话公司的高额长途费用。 此外,Sipdroid APK提供了高质量的语音通话。它使用了先进的音频编解码技术,确保用户可以在通话过程中享受清晰、无杂音的声音质量。这对于商务通话或重要的电话会议非常关键。 Sipdroid APK还具有很好的灵活性和可扩展性。它兼容许多不同的VoIP服务提供商和协议,例如Asterisk、FreePBX、Google Voice等。这意味着用户可以根据自己的喜好和需求选择适合自己的服务。 此外,Sipdroid APK还具有用户友好的界面和简单的设置过程。用户只需下载并安装该应用程序,然后按照指示进行简单的设置即可开始使用。这使得它非常适合普通用户和那些对技术要求不高的人。 总的来说,Sipdroid APK是一款功能强大、灵活易用的VoIP通话应用程序。它可以帮助用户节省通信费用,并提供高质量的语音通话体验。无论是个人用户还是商务用户,Sipdroid APK都是一款优秀的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值