• 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="libpj_silk_codec.so" />  
    9.       <meta-data  
    10.           android:name="init_factory"  
    11.           android:value="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="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="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="libpj_screen_capture_android.so" />  
    26.          <meta-data  
    27.              android:name="init_factory"  
    28.              android:value="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="libpj_video_android.so" />  
    39.          <meta-data  
    40.              android:name="init_factory"  
    41.              android:value="pjmedia_codec_ffmpeg_vid_init" />  
    42.          <meta-data  
    43.              android:name="deinit_factory"  
    44.              android:value="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="libpj_vpx.so" />  
    54.          <meta-data  
    55.              android:name="init_factory"  
    56.              android:value="pjmedia_codec_vpx_init" />  
    57.          <meta-data  
    58.              android:name="deinit_factory"  
    59.              android:value="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

  • 相关阅读:
    android webview内存泄露解决方法
    使用adb安装遇到的一些坑
    androidstudio在创建new project时,窗口太大,看不到下面确定按钮的解决方法
    android切换前后台状态监听
    android设置系统横屏方案
    android判断adb调试是否打开及代码跳转到开发者选项界面
    xml布局解析报错的可能原因
    Android:防止过快点击造成多次事件 问题
    android Information:Gradle tasks [:dl_version:generateDebugSources, :dl_version:generateDebugAndroidTestSources导致无法实现Preview功能
    Android定位服务关闭和定位(悬浮)等权限拒绝的判断
  • 原文地址:https://www.cnblogs.com/John5/p/3978722.html
Copyright © 2020-2023  润新知