首页 > 代码库 > CSipSimple 简单分析

CSipSimple 简单分析

简介

CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话。连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果才好。下面简单分析一下其。

 

功能介绍

1、注册流程

用户首先选择使用哪国哪个类型,这是由com.csipsimple.wizards.impl包下完成的。该包下实现接口WizardIface,接口方法中有
SipProfile buildAccount(SipProfile account);产生一个帐号文件。

然后在BasePrefsWizard类下保存帐号,代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /** 
  2.  * Save the account with given wizard id 
  3.  * @param wizardId the wizard to use for account entry 
  4.  */  
  5. private void saveAccount(String wizardId) { //保存帐号(帐号保存到共享数据库中)  
  6.     boolean needRestart = false;  
  7.   
  8.   
  9.     PreferencesWrapper prefs = new PreferencesWrapper(getApplicationContext());  
  10.     account = wizard.buildAccount(account);  
  11.     account.wizard = wizardId;  
  12.     if (account.id == SipProfile.INVALID_ID) {  
  13.         // This account does not exists yet  
  14.         prefs.startEditing();  
  15.         wizard.setDefaultParams(prefs);  
  16.         prefs.endEditing();  
  17.         applyNewAccountDefault(account);  
  18.         Uri uri = getContentResolver().insert(SipProfile.ACCOUNT_URI, account.getDbContentValues());  
  19.           
  20.         // After insert, add filters for this wizard   
  21.         account.id = ContentUris.parseId(uri);  
  22.         List<Filter> filters = wizard.getDefaultFilters(account);  
  23.         if (filters != null) {  
  24.             for (Filter filter : filters) {  
  25.                 // Ensure the correct id if not done by the wizard  
  26.                 filter.account = (int) account.id;  
  27.                 getContentResolver().insert(SipManager.FILTER_URI, filter.getDbContentValues());  
  28.             }  
  29.         }  
  30.         // Check if we have to restart  
  31.         needRestart = wizard.needRestart();  
  32.   
  33.   
  34.     } else {  
  35.         // TODO : should not be done there but if not we should add an  
  36.         // option to re-apply default params  
  37.            prefs.startEditing();  
  38.         wizard.setDefaultParams(prefs);  
  39.            prefs.endEditing();  
  40.         getContentResolver().update(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, account.id), account.getDbContentValues(), null, null);  
  41.     }  
  42.   
  43.   
  44.     // Mainly if global preferences were changed, we have to restart sip stack   
  45.     if (needRestart) { //保存完毕后发送重新加载sip  
  46.         Intent intent = new Intent(SipManager.ACTION_SIP_REQUEST_RESTART);  
  47.         sendBroadcast(intent);  
  48.     }  
  49. }  


然后执行SipService中的
  

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1.  public void restartSipStack() throws SameThreadException {  
  2.        if(stopSipStack()) {  
  3.            startSipStack();  
  4.        }else {  
  5.            Log.e(THIS_FILE, "Can‘t stop ... so do not restart ! ");  
  6.        }  
  7.    }  
  8.   
  9.   
  10. //private KeepAliveTimer kaAlarm;  
  11. // This is always done in SipExecutor thread  
  12. private void startSipStack() throws SameThreadException {  
  13.     //Cache some prefs  
  14.     supportMultipleCalls = prefsWrapper.getPreferenceBooleanValue(SipConfigManager.SUPPORT_MULTIPLE_CALLS);  
  15.       
  16.     if(!isConnectivityValid()) {  
  17.         notifyUserOfMessage(R.string.connection_not_valid);  
  18.         Log.e(THIS_FILE, "No need to start sip");  
  19.         return;  
  20.     }  
  21.     Log.d(THIS_FILE, "Start was asked and we should actually start now");  
  22.     if(pjService == null) {  
  23.         Log.d(THIS_FILE, "Start was asked and pjService in not there");  
  24.         if(!loadStack()) {  
  25.             Log.e(THIS_FILE, "Unable to load SIP stack !! ");  
  26.             return;  
  27.         }  
  28.     }  
  29.     Log.d(THIS_FILE, "Ask pjservice to start itself");  
  30.       
  31.   
  32.   
  33.        //presenceMgr.startMonitoring(this);  
  34.     if(pjService.sipStart()) {  
  35.         // This should be done after in acquire resource  
  36.         // But due to http://code.google.com/p/android/issues/detail?id=21635  
  37.         // not a good idea  
  38.         applyComponentEnablingState(true);  
  39.           
  40.         registerBroadcasts();  
  41.         Log.d(THIS_FILE, "Add all accounts");  
  42.         addAllAccounts(); //关键添加帐户  
  43.     }  
  44. }  
  45.   
  46.   
  47.   
  48.   
  49. /** 
  50.  * Add accounts from database 
  51.  */  
  52. private void addAllAccounts() throws SameThreadException {//从数据库中读取所有的帐户信息  
  53.     Log.d(THIS_FILE, "We are adding all accounts right now....");  
  54.   
  55.   
  56.     boolean hasSomeSuccess = false;  
  57.     Cursor c = getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION,   
  58.             SipProfile.FIELD_ACTIVE + "=?", new String[] {"1"}, null);  
  59.     if (c != null) {  
  60.         try {  
  61.             int index = 0;  
  62.             if(c.getCount() > 0) {  
  63.                 c.moveToFirst();  
  64.                 do {  
  65.                     SipProfile account = new SipProfile(c);  
  66.                     if (pjService != null && pjService.addAccount(account) ) {//加入到pjsip  
  67.                         hasSomeSuccess = true;  
  68.                     }  
  69.                     index ++;  
  70.                 } while (c.moveToNext() && index < 10);  
  71.             }  
  72.         } catch (Exception e) {  
  73.             Log.e(THIS_FILE, "Error on looping over sip profiles", e);  
  74.         } finally {  
  75.             c.close();  
  76.         }  
  77.     }  
  78.       
  79.     hasSomeActiveAccount = hasSomeSuccess;  
  80.   
  81.   
  82.     if (hasSomeSuccess) {  
  83.         acquireResources();  
  84.           
  85.     } else {  
  86.         releaseResources();  
  87.         if (notificationManager != null) {  
  88.             notificationManager.cancelRegisters();  
  89.         }  
  90.     }  
  91. }  
  92.   
  93.   
  94.   
  95. //设置帐户注册状态信息  
  96. public boolean setAccountRegistration(SipProfile account, int renew, boolean forceReAdd) throws SameThreadException {  
  97.     boolean status = false;  
  98.     if(pjService != null) {  
  99.         status = pjService.setAccountRegistration(account, renew, forceReAdd);  
  100.     }         
  101.       
  102.     return status;  
  103. }  
  104.   
  105.   
  106. /** 
  107.  * Remove accounts from database  从数据库中移除帐号信息 
  108.  */  
  109. private void unregisterAllAccounts(boolean cancelNotification) throws SameThreadException {  
  110.   
  111.   
  112.     releaseResources();  
  113.       
  114.     Log.d(THIS_FILE, "Remove all accounts");  
  115.       
  116.     Cursor c = getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION, null, null, null);  
  117.     if (c != null) {  
  118.         try {  
  119.             c.moveToFirst();  
  120.             do {  
  121.                 SipProfile account = new SipProfile(c);  
  122.                 setAccountRegistration(account, 0, false);  
  123.             } while (c.moveToNext() );  
  124.         } catch (Exception e) {  
  125.             Log.e(THIS_FILE, "Error on looping over sip profiles", e);  
  126.         } finally {  
  127.             c.close();  
  128.         }  
  129.     }  
  130.   
  131.   
  132.   
  133.   
  134.     if (notificationManager != null && cancelNotification) {  
  135.         notificationManager.cancelRegisters();  
  136.     }  
  137. }  
  138.   
  139.   
  140. //重新加载帐户数据库  
  141. private void reAddAllAccounts() throws SameThreadException {  
  142.     Log.d(THIS_FILE, "RE REGISTER ALL ACCOUNTS");  
  143.     unregisterAllAccounts(false);  
  144.     addAllAccounts();  
  145. }  


真正实现注册的是在PjSipService中,关键代码如下:

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1.  public boolean addAccount(SipProfile profile) throws SameThreadException {//底层注册  
  2.         int status = pjsuaConstants.PJ_FALSE;  
  3.         if (!created) { //是否已创建  
  4.             Log.e(THIS_FILE, "PJSIP is not started here, nothing can be done");  
  5.             return status == pjsuaConstants.PJ_SUCCESS;  
  6.   
  7.         }  
  8.         PjSipAccount account = new PjSipAccount(profile); //帐户信息  
  9.         account.applyExtraParams(service);  
  10.   
  11.         // Force the use of a transport  
  12.         /* 
  13.          * switch (account.transport) { case SipProfile.TRANSPORT_UDP: if 
  14.          * (udpTranportId != null) { 
  15.          * //account.cfg.setTransport_id(udpTranportId); } break; case 
  16.          * SipProfile.TRANSPORT_TCP: if (tcpTranportId != null) { // 
  17.          * account.cfg.setTransport_id(tcpTranportId); } break; case 
  18.          * SipProfile.TRANSPORT_TLS: if (tlsTransportId != null) { // 
  19.          * account.cfg.setTransport_id(tlsTransportId); } break; default: break; 
  20.          * } 
  21.          */  
  22.   
  23.         SipProfileState currentAccountStatus = getProfileState(profile);  
  24.         account.cfg.setRegister_on_acc_add(pjsuaConstants.PJ_FALSE);//注册  
  25.   
  26.         if (currentAccountStatus.isAddedToStack()) {//是否加入到堆栈  
  27.             pjsua.csipsimple_set_acc_user_data(currentAccountStatus.getPjsuaId(), account.css_cfg);//设置帐户信息  
  28.             status = pjsua.acc_modify(currentAccountStatus.getPjsuaId(), account.cfg);//修改配置信息  
  29.             beforeAccountRegistration(currentAccountStatus.getPjsuaId(), profile);//调用注册前函数  
  30.             ContentValues cv = new ContentValues();  
  31.             cv.put(SipProfileState.ADDED_STATUS, status);  
  32.             service.getContentResolver().update(  
  33.                     ContentUris.withAppendedId(SipProfile.ACCOUNT_STATUS_ID_URI_BASE, profile.id),  
  34.                     cv, null, null); //更新帐户信息  
  35.   
  36.             if (!account.wizard.equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {  
  37.                 // Re register  
  38.                 if (status == pjsuaConstants.PJ_SUCCESS) {  
  39.                     status = pjsua.acc_set_registration(currentAccountStatus.getPjsuaId(), 1);  
  40.                     if (status == pjsuaConstants.PJ_SUCCESS) {  
  41.                         pjsua.acc_set_online_status(currentAccountStatus.getPjsuaId(), 1);//更新帐户状态  
  42.                     }  
  43.                 }  
  44.             }  
  45.         } else {  
  46.             int[] accId = new int[1];  
  47.             if (account.wizard.equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {  
  48.                 // We already have local account by default  
  49.                 // For now consider we are talking about UDP one  
  50.                 // In the future local account should be set per transport  
  51.                 switch (account.transport) { //选择穿透方式  
  52.                     case SipProfile.TRANSPORT_UDP:  
  53.                         accId[0] = prefsWrapper.useIPv6() ? localUdp6AccPjId : localUdpAccPjId;  
  54.                         break;  
  55.                     case SipProfile.TRANSPORT_TCP:  
  56.                         accId[0] = prefsWrapper.useIPv6() ? localTcp6AccPjId : localTcpAccPjId;  
  57.                         break;  
  58.                     case SipProfile.TRANSPORT_TLS:  
  59.                         accId[0] = prefsWrapper.useIPv6() ? localTls6AccPjId : localTlsAccPjId;  
  60.                         break;  
  61.                     default:  
  62.                         // By default use UDP  
  63.                         accId[0] = localUdpAccPjId;  
  64.                         break;  
  65.                 }  
  66.   
  67.                 pjsua.csipsimple_set_acc_user_data(accId[0], account.css_cfg);//设置用户配置信息  
  68.                 // TODO : use video cfg here  
  69. //                nCfg.setVid_in_auto_show(pjsuaConstants.PJ_TRUE);  
  70. //                nCfg.setVid_out_auto_transmit(pjsuaConstants.PJ_TRUE);  
  71. //                status = pjsua.acc_modify(accId[0], nCfg);  
  72.             } else {  
  73.                 // Cause of standard account different from local account :)  
  74.                 status = pjsua.acc_add(account.cfg, pjsuaConstants.PJ_FALSE, accId);  
  75.                 pjsua.csipsimple_set_acc_user_data(accId[0], account.css_cfg);  
  76.                 beforeAccountRegistration(accId[0], profile);  
  77.                 pjsua.acc_set_registration(accId[0], 1);  
  78.             }  
  79.   
  80.             if (status == pjsuaConstants.PJ_SUCCESS) {//成功设置状态信息  
  81.                 SipProfileState ps = new SipProfileState(profile);  
  82.                 ps.setAddedStatus(status);  
  83.                 ps.setPjsuaId(accId[0]);  
  84.                 service.getContentResolver().insert(  
  85.                         ContentUris.withAppendedId(SipProfile.ACCOUNT_STATUS_ID_URI_BASE,  
  86.                                 account.id), ps.getAsContentValue());  
  87.   
  88.                 pjsua.acc_set_online_status(accId[0], 1);  
  89.             }  
  90.         }  
  91.   
  92.         return status == pjsuaConstants.PJ_SUCCESS;  
  93.     }  
  94.   
  95.     void beforeAccountRegistration(int pjId, SipProfile profile) { //注册前触发  
  96.         for (PjsipModule mod : pjsipModules.values()) {  
  97.             mod.onBeforeAccountStartRegistration(pjId, profile);  
  98.         }  
  99.     }  


注册抓包信息如下:

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. REGISTER sip:www.**.net:5060 SIP/2.0  
  2. Via: SIP/2.0/UDP 10.0.2.15:60591;rport;branch=z9hG4bKPjfvTjKWT5urgwc2nwez3BgasaQYYDLpTj  
  3. Route: <sip:www.**.net:5060;transport=udp;lr>  
  4. Max-Forwards: 70  
  5. From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx  
  6. To: <sip:1001@www.**.net>  
  7. Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB  
  8. CSeq: 63463 REGISTER  
  9. User-Agent: CSipSimple_generic-8/r2353  
  10. Contact: <sip:1001@10.0.2.15:60591;ob>  
  11. Expires: 900  
  12. Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS  
  13. Content-Length:  0  
  14.   
  15.   
  16. SIP/2.0 401 Unauthorized  
  17. Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjfvTjKWT5urgwc2nwez3BgasaQYYDLpTj;received=192.168.1.154  
  18. From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx  
  19. To: <sip:1001@www.**.net>;tag=4f5e0299ccbb80ebb6598255e669265c.a5b5  
  20. Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB  
  21. CSeq: 63463 REGISTER  
  22. WWW-Authenticate: Digest realm="www.**.net", nonce="Ux5l0lMeZKaFznRge1gZtxoYW//UWjA2"  
  23. Server: kamailio (4.0.3 (x86_64/linux))  
  24. Content-Length: 0  
  25.   
  26.   
  27. REGISTER sip:www.**.net:5060 SIP/2.0  
  28. Via: SIP/2.0/UDP 10.0.2.15:60591;rport;branch=z9hG4bKPjrw3ueP2qlwf7pE6T2eM.b..-AeoqKmdc  
  29. Route: <sip:www.**.net:5060;transport=udp;lr>  
  30. Max-Forwards: 70  
  31. From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx  
  32. To: <sip:1001@www.**.net>  
  33. Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB  
  34. CSeq: 63464 REGISTER  
  35. User-Agent: CSipSimple_generic-8/r2353  
  36. Contact: <sip:1001@10.0.2.15:60591;ob>  
  37. Expires: 900  
  38. Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS  
  39. Authorization: Digest username="1001", realm="www.**.net", nonce="Ux5l0lMeZKaFznRge1gZtxoYW//UWjA2", uri="sip:www.**.net:5060", response="8a006ec04c954b1533a5a895d77929c5"  
  40. Content-Length:  0  
  41.   
  42.   
  43. SIP/2.0 200 OK  
  44. Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjrw3ueP2qlwf7pE6T2eM.b..-AeoqKmdc;received=192.168.1.154  
  45. From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx  
  46. To: <sip:1001@www.**.net>;tag=4f5e0299ccbb80ebb6598255e669265c.eb21  
  47. Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB  
  48. CSeq: 63464 REGISTER  
  49. Contact: <sip:1001@10.0.2.15:60591;ob>;expires=600;received="sip:192.168.1.154:52571"  
  50. Server: kamailio (4.0.3 (x86_64/linux))  
  51. Content-Length: 0  
  52.   
  53.   
  54. SUBSCRIBE sip:1001@www.**.net SIP/2.0  
  55. Via: SIP/2.0/UDP 10.0.2.15:60591;rport;branch=z9hG4bKPjgXYRCe5ny..u9iYYumroyVEWYE8V0hYS  
  56. Max-Forwards: 70  
  57. From: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb  
  58. To: <sip:1001@www.**.net>  
  59. Contact: <sip:1001@192.168.1.154:52571;ob>  
  60. Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp  
  61. CSeq: 24382 SUBSCRIBE  
  62. Route: <sip:www.**.net:5060;transport=udp;lr>  
  63. Event: message-summary  
  64. Expires: 3600  
  65. Supported: replaces, 100rel, timer, norefersub  
  66. Accept: application/simple-message-summary  
  67. Allow-Events: presence, message-summary, refer  
  68. User-Agent: CSipSimple_generic-8/r2353  
  69. Content-Length:  0  
  70.   
  71.   
  72. SIP/2.0 407 Proxy Authentication Required  
  73. Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjgXYRCe5ny..u9iYYumroyVEWYE8V0hYS;received=192.168.1.154  
  74. From: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb  
  75. To: <sip:1001@www.**.net>;tag=4f5e0299ccbb80ebb6598255e669265c.e4b6  
  76. Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp  
  77. CSeq: 24382 SUBSCRIBE  
  78. Proxy-Authenticate: Digest realm="www.**.net", nonce="Ux5l0lMeZKaFznRge1gZtxoYW//UWjA2"  
  79. Server: kamailio (4.0.3 (x86_64/linux))  
  80. Content-Length: 0  
  81.   
  82.   
  83. SIP/2.0 202 OK  
  84. Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjRhk4xHkOkvy82g1N2n3I-d0m2CHWwlJT;received=192.168.1.154  
  85. From: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb  
  86. To: <sip:1001@www.**.net>;tag=afbf025b308b45bb32e1b93911cf7810-9f85  
  87. Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp  
  88. CSeq: 24383 SUBSCRIBE  
  89. Expires: 3600  
  90. Contact: <sip:113.195.206.200:5060;transport=udp>  
  91. Server: kamailio (4.0.3 (x86_64/linux))  
  92. Content-Length: 0  
  93.   
  94.   
  95. SIP/2.0 200 OK  
  96. Via: SIP/2.0/UDP 113.195.206.200;received=113.195.206.200;branch=z9hG4bKa281.981eed9.0  
  97. Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp  
  98. From: <sip:1001@www.**.net>;tag=afbf025b308b45bb32e1b93911cf7810-9f85  
  99. To: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb  
  100. CSeq: 2 NOTIFY  
  101. Contact: <sip:1001@192.168.1.154:52571;ob>  
  102. Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS  
  103. Supported: replaces, 100rel, timer, norefersub  
  104. Content-Length:  0  


2、电话拨打、电话监听

 

电话的拨打在SipService代码中,代码如下:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1.       /** 
  2.        * {@inheritDoc} 
  3.        */  
  4. @Override  
  5. public void makeCall(final String callee, final int accountId) throws RemoteException {  
  6.     makeCallWithOptions(callee, accountId, null);  
  7. }  
  8.   
  9.   
  10.       @Override  
  11.       public void makeCallWithOptions(final String callee, final int accountId, final Bundle options)  
  12.               throws RemoteException {  
  13.           SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);  
  14.           //We have to ensure service is properly started and not just binded  
  15.           SipService.this.startService(new Intent(SipService.this, SipService.class));//开启服务  
  16.             
  17.           if(pjService == null) {  
  18.               Log.e(THIS_FILE, "Can‘t place call if service not started");  
  19.               // TODO - we should return a failing status here  
  20.               return;  
  21.           }  
  22.             
  23.           if(!supportMultipleCalls) {  
  24.               // Check if there is no ongoing calls if so drop this request by alerting user  
  25.               SipCallSession activeCall = pjService.getActiveCallInProgress();//已有电话  
  26.               if(activeCall != null) {  
  27.                   if(!CustomDistribution.forceNoMultipleCalls()) {  
  28.                       notifyUserOfMessage(R.string.not_configured_multiple_calls);  
  29.                   }  
  30.                   return;  
  31.               }  
  32.           }  
  33.           getExecutor().execute(new SipRunnable() {  
  34.               @Override  
  35.               protected void doRun() throws SameThreadException {  
  36.                   pjService.makeCall(callee, accountId, options);//底层拨打  
  37.               }  
  38.           });  
  39.       }  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /** 
  2.      * Make a call 
  3.      *  
  4.      * @param callee 
  5.      *            remote contact ot call If not well formated we try to add 
  6.      *            domain name of the default account 
  7.      */  
  8.     public int makeCall(String callee, int accountId, Bundle b)  
  9.             throws SameThreadException {  
  10.         if (!created) { // 未创建  
  11.             return -1;  
  12.         }  
  13.   
  14.         final ToCall toCall = sanitizeSipUri(callee, accountId);// 构造对应sip地址  
  15.         if (toCall != null) {  
  16.             pj_str_t uri = pjsua.pj_str_copy(toCall.getCallee());  
  17.   
  18.             // Nothing to do with this values  
  19.             byte[] userData = new byte[1];  
  20.             int[] callId = new int[1];  
  21.             pjsua_call_setting cs = new pjsua_call_setting();  
  22.             pjsua_msg_data msgData = new pjsua_msg_data();  
  23.             int pjsuaAccId = toCall.getPjsipAccountId();  
  24.   
  25.             // Call settings to add video  
  26.             pjsua.call_setting_default(cs);// 添加电话配置信息  
  27.             cs.setAud_cnt(1);  
  28.             cs.setVid_cnt(0);  
  29.             if (b != null && b.getBoolean(SipCallSession.OPT_CALL_VIDEO, false)) {  
  30.                 cs.setVid_cnt(1);  
  31.             }  
  32.             cs.setFlag(0);  
  33.   
  34.             pj_pool_t pool = pjsua.pool_create("call_tmp", 512, 512);// 池  
  35.   
  36.             // Msg data to add headers  
  37.             pjsua.msg_data_init(msgData); // 构造消息信息  
  38.             pjsua.csipsimple_init_acc_msg_data(pool, pjsuaAccId, msgData);  
  39.             if (b != null) {  
  40.                 Bundle extraHeaders = b  
  41.                         .getBundle(SipCallSession.OPT_CALL_EXTRA_HEADERS);  
  42.                 if (extraHeaders != null) {  
  43.                     for (String key : extraHeaders.keySet()) {  
  44.                         try {  
  45.                             String value = extraHeaders.getString(key);  
  46.                             if (!TextUtils.isEmpty(value)) {  
  47.                                 int res = pjsua  
  48.                                         .csipsimple_msg_data_add_string_hdr(  
  49.                                                 pool, msgData,  
  50.                                                 pjsua.pj_str_copy(key),  
  51.                                                 pjsua.pj_str_copy(value));  
  52.                                 if (res == pjsuaConstants.PJ_SUCCESS) {  
  53.                                     Log.e(THIS_FILE, "Failed to add Xtra hdr ("  
  54.                                             + key + " : " + value  
  55.                                             + ") probably not X- header");  
  56.                                 }  
  57.                             }  
  58.                         } catch (Exception e) {  
  59.                             Log.e(THIS_FILE, "Invalid header value for key : "  
  60.                                     + key);  
  61.                         }  
  62.                     }  
  63.                 }  
  64.             }  
  65.             // 拨打电话  
  66.             int status = pjsua.call_make_call(pjsuaAccId, uri, cs, userData,  
  67.                     msgData, callId);  
  68.             if (status == pjsuaConstants.PJ_SUCCESS) {  
  69.                 dtmfToAutoSend.put(callId[0], toCall.getDtmf());  
  70.                 Log.d(THIS_FILE, "DTMF - Store for " + callId[0] + " - "  
  71.                         + toCall.getDtmf());  
  72.             }  
  73.             pjsua.pj_pool_release(pool); // 释放  
  74.             return status;  
  75.         } else {  
  76.             service.notifyUserOfMessage(service  
  77.                     .getString(R.string.invalid_sip_uri) + " : " + callee);  
  78.         }  
  79.         return -1;  
  80.     }  


电话监听

 

电话监听在UAStateReceiver中,该类是继承Callback的,Callback是调用jni的类,关键代码如下:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /* 
  2.     * private class IncomingCallInfos { public SipCallSession callInfo; public 
  3.     * Integer accId; } 
  4.     */  
  5.    @Override  
  6.    public void on_incoming_call(final int accId, final int callId, SWIGTYPE_p_pjsip_rx_data rdata) {  
  7.        lockCpu();  
  8.   
  9.        // Check if we have not already an ongoing call  
  10.        boolean hasOngoingSipCall = false;  
  11.        if (pjService != null && pjService.service != null) {  
  12.            SipCallSessionImpl[] calls = getCalls();  
  13.            if (calls != null) {  
  14.                for (SipCallSessionImpl existingCall : calls) {  
  15.                    if (!existingCall.isAfterEnded() && existingCall.getCallId() != callId) {  
  16.                        if (!pjService.service.supportMultipleCalls) {  
  17.                            Log.e(THIS_FILE,  
  18.                                    "Settings to not support two call at the same time !!!");  
  19.                            // If there is an ongoing call and we do not support  
  20.                            // multiple calls  
  21.                            // Send busy here  
  22.                            pjsua.call_hangup(callId, StatusCode.BUSY_HERE, null, null);  
  23.                            unlockCpu();  
  24.                            return;  
  25.                        } else {  
  26.                            hasOngoingSipCall = true;  
  27.                        }  
  28.                    }  
  29.                }  
  30.            }  
  31.        }  
  32.   
  33.        try {  
  34.            SipCallSessionImpl callInfo = updateCallInfoFromStack(callId, null);  
  35.            Log.d(THIS_FILE, "Incoming call << for account " + accId);  
  36.   
  37.            // Extra check if set reference counted is false ???  
  38.            if (!ongoingCallLock.isHeld()) {  
  39.                ongoingCallLock.acquire();  
  40.            }  
  41.   
  42.            final String remContact = callInfo.getRemoteContact();  
  43.            callInfo.setIncoming(true);  
  44.            notificationManager.showNotificationForCall(callInfo);  
  45.   
  46.            // Auto answer feature  
  47.            SipProfile acc = pjService.getAccountForPjsipId(accId);  
  48.            Bundle extraHdr = new Bundle();  
  49.            fillRDataHeader("Call-Info", rdata, extraHdr);  
  50.            final int shouldAutoAnswer = pjService.service.shouldAutoAnswer(remContact, acc,  
  51.                    extraHdr);  
  52.            Log.d(THIS_FILE, "Should I anto answer ? " + shouldAutoAnswer);  
  53.            if (shouldAutoAnswer >= 200) {  
  54.                // Automatically answer incoming calls with 200 or higher final  
  55.                // code  
  56.                pjService.callAnswer(callId, shouldAutoAnswer);  
  57.            } else {  
  58.                // Ring and inform remote about ringing with 180/RINGING  
  59.                pjService.callAnswer(callId, 180);  
  60.   
  61.                if (pjService.mediaManager != null) {  
  62.                    if (pjService.service.getGSMCallState() == TelephonyManager.CALL_STATE_IDLE  
  63.                            && !hasOngoingSipCall) {  
  64.                        pjService.mediaManager.startRing(remContact);  
  65.                    } else {  
  66.                        pjService.mediaManager.playInCallTone(MediaManager.TONE_CALL_WAITING);  
  67.                    }  
  68.                }  
  69.                broadCastAndroidCallState("RINGING", remContact);  
  70.            }  
  71.            if (shouldAutoAnswer < 300) {  
  72.                // Or by api  
  73.                launchCallHandler(callInfo);  
  74.                Log.d(THIS_FILE, "Incoming call >>");  
  75.            }  
  76.        } catch (SameThreadException e) {  
  77.            // That‘s fine we are in a pjsip thread  
  78.        } finally {  
  79.            unlockCpu();  
  80.        }  
  81.   
  82.    }  


3、音频视频编解码

 

我们知道CSipsimple中的音频编解码、视频编解码是以插件的形式加入的。我们先看下它是如何加入的。

在PjSipService中sipStart函数中有如下代码:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. // Audio implementation  加入音频插件  
  2. int implementation = prefsWrapper  
  3.         .getPreferenceIntegerValue(SipConfigManager.AUDIO_IMPLEMENTATION);  
  4. if (implementation == SipConfigManager.AUDIO_IMPLEMENTATION_OPENSLES) {  
  5.     dynamic_factory audImp = cssCfg.getAudio_implementation();  
  6.     audImp.setInit_factory_name(pjsua  
  7.             .pj_str_copy("pjmedia_opensl_factory"));  
  8.     File openslLib = NativeLibManager.getBundledStackLibFile(  
  9.             service, "libpj_opensl_dev.so");  
  10.     audImp.setShared_lib_path(pjsua.pj_str_copy(openslLib  
  11.             .getAbsolutePath()));  
  12.     cssCfg.setAudio_implementation(audImp);  
  13.     Log.d(THIS_FILE, "Use OpenSL-ES implementation");  
  14. }  
  15.   
  16. // Video implementation  加入视频插件  
  17. if (prefsWrapper  
  18.         .getPreferenceBooleanValue(SipConfigManager.USE_VIDEO)) {  
  19.     // TODO :: Have plugins per capture / render / video codec /  
  20.     // converter  
  21.     Map<String, DynCodecInfos> videoPlugins = ExtraPlugins  
  22.             .getDynCodecPlugins(service,  
  23.                     SipManager.ACTION_GET_VIDEO_PLUGIN);  
  24.   
  25.     if (videoPlugins.size() > 0) {  
  26.         DynCodecInfos videoPlugin = videoPlugins.values()  
  27.                 .iterator().next();  
  28.         pj_str_t pjVideoFile = pjsua  
  29.                 .pj_str_copy(videoPlugin.libraryPath);  
  30.         Log.d(THIS_FILE, "Load video plugin at "  
  31.                 + videoPlugin.libraryPath);  
  32.         // Render  
  33.         {  
  34.             dynamic_factory vidImpl = cssCfg  
  35.                     .getVideo_render_implementation();  
  36.             vidImpl.setInit_factory_name(pjsua  
  37.                     .pj_str_copy("pjmedia_webrtc_vid_render_factory"));  
  38.             vidImpl.setShared_lib_path(pjVideoFile);  
  39.         }  
  40.         // Capture  
  41.         {  
  42.             dynamic_factory vidImpl = cssCfg  
  43.                     .getVideo_capture_implementation();  
  44.             vidImpl.setInit_factory_name(pjsua  
  45.                     .pj_str_copy("pjmedia_webrtc_vid_capture_factory"));  
  46.             vidImpl.setShared_lib_path(pjVideoFile);  
  47.             /* 
  48.              * -- For testing video screen -- Not yet released 
  49.              * try { ComponentName cmp = new 
  50.              * ComponentName("com.csipsimple.plugins.video", 
  51.              * "com.csipsimple.plugins.video.CaptureReceiver"); 
  52.              * DynCodecInfos screenCapt = new 
  53.              * ExtraPlugins.DynCodecInfos(service, cmp); 
  54.              * vidImpl.setInit_factory_name(pjsua 
  55.              * .pj_str_copy(screenCapt.factoryInitFunction)); 
  56.              * vidImpl.setShared_lib_path(pjsua 
  57.              * .pj_str_copy(screenCapt.libraryPath)); } catch 
  58.              * (NameNotFoundException e) { Log.e(THIS_FILE, 
  59.              * "Not found capture plugin"); } 
  60.              */  
  61.         }  
  62.         // Video codecs  加入视频解码  
  63.         availableCodecs = ExtraPlugins.getDynCodecPlugins(  
  64.                 service,  
  65.                 SipManager.ACTION_GET_EXTRA_VIDEO_CODECS);  
  66.         cssCodecs = cssCfg.getExtra_vid_codecs();  
  67.         dynamic_factory[] cssCodecsDestroy = cssCfg  
  68.                 .getExtra_vid_codecs_destroy();  
  69.         i = 0;  
  70.         for (Entry<String, DynCodecInfos> availableCodec : availableCodecs  
  71.                 .entrySet()) {  
  72.             DynCodecInfos dyn = availableCodec.getValue();  
  73.             if (!TextUtils.isEmpty(dyn.libraryPath)) {  
  74.                 // Create  
  75.                 cssCodecs[i].setShared_lib_path(pjsua  
  76.                         .pj_str_copy(dyn.libraryPath));  
  77.                 cssCodecs[i].setInit_factory_name(pjsua  
  78.                         .pj_str_copy(dyn.factoryInitFunction));  
  79.                 // Destroy  
  80.                 cssCodecsDestroy[i].setShared_lib_path(pjsua  
  81.                         .pj_str_copy(dyn.libraryPath));  
  82.                 cssCodecsDestroy[i]  
  83.                         .setInit_factory_name(pjsua  
  84.                                 .pj_str_copy(dyn.factoryDeinitFunction));  
  85.             }  
  86.             i++;  
  87.         }  
  88.         cssCfg.setExtra_vid_codecs_cnt(i);  
  89.   
  90.         // Converter  
  91.         dynamic_factory convertImpl = cssCfg.getVid_converter();  
  92.         convertImpl.setShared_lib_path(pjVideoFile);  
  93.         convertImpl  
  94.                 .setInit_factory_name(pjsua  
  95.                         .pj_str_copy("pjmedia_libswscale_converter_init"));  
  96.     }  
  97. }  


这只是对音频、视频编解码信息的读取,那具体是如何加入的呢?看一下AndroidManifest.xml文件,原来是通过广播添加的,代码如下:

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. <!-- Extra codecs 音频插件-->  
  2.   
  3.   <receiver  
  4.       android:name="com.csipsimple.plugins.codecs.ReceiverSILK"  
  5.       android:exported="false" >  
  6.       <meta-data  
  7.           android:name="lib_name"  
  8.           android:value=http://www.mamicode.com/"libpj_silk_codec.so" />  
  9.       <meta-data  
  10.           android:name="init_factory"  
  11.           android:value=http://www.mamicode.com/"pjmedia_codec_silk_init" />  
  12.   
  13.       <intent-filter>  
  14.           <action android:name="com.csipsimple.codecs.action.REGISTER_CODEC" />  
  15.       </intent-filter>  
  16.   </receiver>  

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. <!-- Receiver for standard video 视频插件 -->  
  2.      <receiver android:name=".PluginReceiver" >  
  3.          <intent-filter>  
  4.              <action android:name="com.csipsimple.plugins.action.REGISTER_VIDEO" />  
  5.          </intent-filter>  
  6.   
  7.          <meta-data  
  8.              android:name="lib_name"  
  9.              android:value=http://www.mamicode.com/"libpj_video_android.so" />  
  10.          <!-- For now it does not matter in the future we should have one per device, codec, and converter (if needed) -->  
  11.          <meta-data  
  12.              android:name="init_factory"  
  13.              android:value=http://www.mamicode.com/"pjmedia_webrtc_vid_render_factory" />  
  14.      </receiver>  
  15.   
  16.      <!--  
  17.      Receiver for video capture  
  18.      <receiver android:name=".CaptureReceiver" >  
  19.          <intent-filter>  
  20.              <action android:name="com.csipsimple.plugins.action.REGISTER_CAPTURE_VIDEO" />  
  21.          </intent-filter>  
  22.   
  23.          <meta-data  
  24.              android:name="lib_name"  
  25.              android:value=http://www.mamicode.com/"libpj_screen_capture_android.so" />  
  26.          <meta-data  
  27.              android:name="init_factory"  
  28.              android:value=http://www.mamicode.com/"pjmedia_webrtc_vid_capture_factory" />  
  29.      </receiver>  
  30.      -->  
  31.      <receiver android:name=".PluginReceiverFfmpeg" >  
  32.          <intent-filter>  
  33.              <action android:name="com.csipsimple.codecs.action.REGISTER_VIDEO_CODEC" />  
  34.          </intent-filter>  
  35.   
  36.          <meta-data  
  37.              android:name="lib_name"  
  38.              android:value=http://www.mamicode.com/"libpj_video_android.so" />  
  39.          <meta-data  
  40.              android:name="init_factory"  
  41.              android:value=http://www.mamicode.com/"pjmedia_codec_ffmpeg_vid_init" />  
  42.          <meta-data  
  43.              android:name="deinit_factory"  
  44.              android:value=http://www.mamicode.com/"pjmedia_codec_ffmpeg_vid_deinit" />  
  45.      </receiver>  
  46.      <receiver android:name=".PluginReceiverVpx" >  
  47.          <intent-filter>  
  48.              <action android:name="com.csipsimple.codecs.action.REGISTER_VIDEO_CODEC" />  
  49.          </intent-filter>  
  50.   
  51.          <meta-data  
  52.              android:name="lib_name"  
  53.              android:value=http://www.mamicode.com/"libpj_vpx.so" />  
  54.          <meta-data  
  55.              android:name="init_factory"  
  56.              android:value=http://www.mamicode.com/"pjmedia_codec_vpx_init" />  
  57.          <meta-data  
  58.              android:name="deinit_factory"  
  59.              android:value=http://www.mamicode.com/"pjmedia_codec_vpx_deinit" />  
  60.      </receiver>  

 

它是如何消除回音的?

在PjSipService文件中,有如下函数:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. //消除回音  
  2. public void setEchoCancellation(boolean on) throws SameThreadException {  
  3.     if (created && userAgentReceiver != null) {  
  4.         Log.d(THIS_FILE, "set echo cancelation " + on);  
  5.         pjsua.set_ec(  
  6.                 on ? prefsWrapper.getEchoCancellationTail() : 0,  
  7.                 prefsWrapper  
  8.                         .getPreferenceIntegerValue(SipConfigManager.ECHO_MODE));  
  9.     }  
  10. }  


原来是通过底层进行回音消除的。

 

结束

简单的分析一下CSipSimple,对sip的认识又进了一步,近期将对它进行再封装。

转:http://blog.csdn.net/banketree/article/details/20990997

CSipSimple 简单分析