简介

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

功能介绍

1、注册流程

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

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

[java] view plaincopy
  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. PreferencesWrapper prefs = new PreferencesWrapper(getApplicationContext());
  8. account = wizard.buildAccount(account);
  9. account.wizard = wizardId;
  10. if (account.id == SipProfile.INVALID_ID) {
  11. // This account does not exists yet
  12. prefs.startEditing();
  13. wizard.setDefaultParams(prefs);
  14. prefs.endEditing();
  15. applyNewAccountDefault(account);
  16. Uri uri = getContentResolver().insert(SipProfile.ACCOUNT_URI, account.getDbContentValues());
  17. // After insert, add filters for this wizard
  18. account.id = ContentUris.parseId(uri);
  19. List<Filter> filters = wizard.getDefaultFilters(account);
  20. if (filters != null) {
  21. for (Filter filter : filters) {
  22. // Ensure the correct id if not done by the wizard
  23. filter.account = (int) account.id;
  24. getContentResolver().insert(SipManager.FILTER_URI, filter.getDbContentValues());
  25. }
  26. }
  27. // Check if we have to restart
  28. needRestart = wizard.needRestart();
  29. } else {
  30. // TODO : should not be done there but if not we should add an
  31. // option to re-apply default params
  32. prefs.startEditing();
  33. wizard.setDefaultParams(prefs);
  34. prefs.endEditing();
  35. getContentResolver().update(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, account.id), account.getDbContentValues(), null, null);
  36. }
  37. // Mainly if global preferences were changed, we have to restart sip stack
  38. if (needRestart) { //保存完毕后发送重新加载sip
  39. Intent intent = new Intent(SipManager.ACTION_SIP_REQUEST_RESTART);
  40. sendBroadcast(intent);
  41. }
  42. }

然后执行SipService中的

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

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

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

注册抓包信息如下:

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

2、电话拨打、电话监听

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

[java] view plaincopy
  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. @Override
  9. public void makeCallWithOptions(final String callee, final int accountId, final Bundle options)
  10. throws RemoteException {
  11. SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
  12. //We have to ensure service is properly started and not just binded
  13. SipService.this.startService(new Intent(SipService.this, SipService.class));//开启服务
  14. if(pjService == null) {
  15. Log.e(THIS_FILE, "Can't place call if service not started");
  16. // TODO - we should return a failing status here
  17. return;
  18. }
  19. if(!supportMultipleCalls) {
  20. // Check if there is no ongoing calls if so drop this request by alerting user
  21. SipCallSession activeCall = pjService.getActiveCallInProgress();//已有电话
  22. if(activeCall != null) {
  23. if(!CustomDistribution.forceNoMultipleCalls()) {
  24. notifyUserOfMessage(R.string.not_configured_multiple_calls);
  25. }
  26. return;
  27. }
  28. }
  29. getExecutor().execute(new SipRunnable() {
  30. @Override
  31. protected void doRun() throws SameThreadException {
  32. pjService.makeCall(callee, accountId, options);//底层拨打
  33. }
  34. });
  35. }
[java] view plaincopy
  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. final ToCall toCall = sanitizeSipUri(callee, accountId);// 构造对应sip地址
  14. if (toCall != null) {
  15. pj_str_t uri = pjsua.pj_str_copy(toCall.getCallee());
  16. // Nothing to do with this values
  17. byte[] userData = new byte[1];
  18. int[] callId = new int[1];
  19. pjsua_call_setting cs = new pjsua_call_setting();
  20. pjsua_msg_data msgData = new pjsua_msg_data();
  21. int pjsuaAccId = toCall.getPjsipAccountId();
  22. // Call settings to add video
  23. pjsua.call_setting_default(cs);// 添加电话配置信息
  24. cs.setAud_cnt(1);
  25. cs.setVid_cnt(0);
  26. if (b != null && b.getBoolean(SipCallSession.OPT_CALL_VIDEO, false)) {
  27. cs.setVid_cnt(1);
  28. }
  29. cs.setFlag(0);
  30. pj_pool_t pool = pjsua.pool_create("call_tmp", 512, 512);// 池
  31. // Msg data to add headers
  32. pjsua.msg_data_init(msgData); // 构造消息信息
  33. pjsua.csipsimple_init_acc_msg_data(pool, pjsuaAccId, msgData);
  34. if (b != null) {
  35. Bundle extraHeaders = b
  36. .getBundle(SipCallSession.OPT_CALL_EXTRA_HEADERS);
  37. if (extraHeaders != null) {
  38. for (String key : extraHeaders.keySet()) {
  39. try {
  40. String value = extraHeaders.getString(key);
  41. if (!TextUtils.isEmpty(value)) {
  42. int res = pjsua
  43. .csipsimple_msg_data_add_string_hdr(
  44. pool, msgData,
  45. pjsua.pj_str_copy(key),
  46. pjsua.pj_str_copy(value));
  47. if (res == pjsuaConstants.PJ_SUCCESS) {
  48. Log.e(THIS_FILE, "Failed to add Xtra hdr ("
  49. + key + " : " + value
  50. + ") probably not X- header");
  51. }
  52. }
  53. } catch (Exception e) {
  54. Log.e(THIS_FILE, "Invalid header value for key : "
  55. + key);
  56. }
  57. }
  58. }
  59. }
  60. // 拨打电话
  61. int status = pjsua.call_make_call(pjsuaAccId, uri, cs, userData,
  62. msgData, callId);
  63. if (status == pjsuaConstants.PJ_SUCCESS) {
  64. dtmfToAutoSend.put(callId[0], toCall.getDtmf());
  65. Log.d(THIS_FILE, "DTMF - Store for " + callId[0] + " - "
  66. + toCall.getDtmf());
  67. }
  68. pjsua.pj_pool_release(pool); // 释放
  69. return status;
  70. } else {
  71. service.notifyUserOfMessage(service
  72. .getString(R.string.invalid_sip_uri) + " : " + callee);
  73. }
  74. return -1;
  75. }

电话监听

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

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

3、音频视频编解码

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

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

[java] view plaincopy
  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. // Video implementation  加入视频插件
  16. if (prefsWrapper
  17. .getPreferenceBooleanValue(SipConfigManager.USE_VIDEO)) {
  18. // TODO :: Have plugins per capture / render / video codec /
  19. // converter
  20. Map<String, DynCodecInfos> videoPlugins = ExtraPlugins
  21. .getDynCodecPlugins(service,
  22. SipManager.ACTION_GET_VIDEO_PLUGIN);
  23. if (videoPlugins.size() > 0) {
  24. DynCodecInfos videoPlugin = videoPlugins.values()
  25. .iterator().next();
  26. pj_str_t pjVideoFile = pjsua
  27. .pj_str_copy(videoPlugin.libraryPath);
  28. Log.d(THIS_FILE, "Load video plugin at "
  29. + videoPlugin.libraryPath);
  30. // Render
  31. {
  32. dynamic_factory vidImpl = cssCfg
  33. .getVideo_render_implementation();
  34. vidImpl.setInit_factory_name(pjsua
  35. .pj_str_copy("pjmedia_webrtc_vid_render_factory"));
  36. vidImpl.setShared_lib_path(pjVideoFile);
  37. }
  38. // Capture
  39. {
  40. dynamic_factory vidImpl = cssCfg
  41. .getVideo_capture_implementation();
  42. vidImpl.setInit_factory_name(pjsua
  43. .pj_str_copy("pjmedia_webrtc_vid_capture_factory"));
  44. vidImpl.setShared_lib_path(pjVideoFile);
  45. /*
  46. * -- For testing video screen -- Not yet released
  47. * try { ComponentName cmp = new
  48. * ComponentName("com.csipsimple.plugins.video",
  49. * "com.csipsimple.plugins.video.CaptureReceiver");
  50. * DynCodecInfos screenCapt = new
  51. * ExtraPlugins.DynCodecInfos(service, cmp);
  52. * vidImpl.setInit_factory_name(pjsua
  53. * .pj_str_copy(screenCapt.factoryInitFunction));
  54. * vidImpl.setShared_lib_path(pjsua
  55. * .pj_str_copy(screenCapt.libraryPath)); } catch
  56. * (NameNotFoundException e) { Log.e(THIS_FILE,
  57. * "Not found capture plugin"); }
  58. */
  59. }
  60. // Video codecs  加入视频解码
  61. availableCodecs = ExtraPlugins.getDynCodecPlugins(
  62. service,
  63. SipManager.ACTION_GET_EXTRA_VIDEO_CODECS);
  64. cssCodecs = cssCfg.getExtra_vid_codecs();
  65. dynamic_factory[] cssCodecsDestroy = cssCfg
  66. .getExtra_vid_codecs_destroy();
  67. i = 0;
  68. for (Entry<String, DynCodecInfos> availableCodec : availableCodecs
  69. .entrySet()) {
  70. DynCodecInfos dyn = availableCodec.getValue();
  71. if (!TextUtils.isEmpty(dyn.libraryPath)) {
  72. // Create
  73. cssCodecs[i].setShared_lib_path(pjsua
  74. .pj_str_copy(dyn.libraryPath));
  75. cssCodecs[i].setInit_factory_name(pjsua
  76. .pj_str_copy(dyn.factoryInitFunction));
  77. // Destroy
  78. cssCodecsDestroy[i].setShared_lib_path(pjsua
  79. .pj_str_copy(dyn.libraryPath));
  80. cssCodecsDestroy[i]
  81. .setInit_factory_name(pjsua
  82. .pj_str_copy(dyn.factoryDeinitFunction));
  83. }
  84. i++;
  85. }
  86. cssCfg.setExtra_vid_codecs_cnt(i);
  87. // Converter
  88. dynamic_factory convertImpl = cssCfg.getVid_converter();
  89. convertImpl.setShared_lib_path(pjVideoFile);
  90. convertImpl
  91. .setInit_factory_name(pjsua
  92. .pj_str_copy("pjmedia_libswscale_converter_init"));
  93. }
  94. }

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

[java] view plaincopy
  1. <!-- Extra codecs 音频插件-->
  2. <receiver
  3. android:name="com.csipsimple.plugins.codecs.ReceiverSILK"
  4. android:exported="false" >
  5. <meta-data
  6. android:name="lib_name"
  7. android:value="libpj_silk_codec.so" />
  8. <meta-data
  9. android:name="init_factory"
  10. android:value="pjmedia_codec_silk_init" />
  11. <intent-filter>
  12. <action android:name="com.csipsimple.codecs.action.REGISTER_CODEC" />
  13. </intent-filter>
  14. </receiver>
[java] view plaincopy
  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. <meta-data
  7. android:name="lib_name"
  8. android:value="libpj_video_android.so" />
  9. <!-- For now it does not matter in the future we should have one per device, codec, and converter (if needed) -->
  10. <meta-data
  11. android:name="init_factory"
  12. android:value="pjmedia_webrtc_vid_render_factory" />
  13. </receiver>
  14. <!--
  15. Receiver for video capture
  16. <receiver android:name=".CaptureReceiver" >
  17. <intent-filter>
  18. <action android:name="com.csipsimple.plugins.action.REGISTER_CAPTURE_VIDEO" />
  19. </intent-filter>
  20. <meta-data
  21. android:name="lib_name"
  22. android:value="libpj_screen_capture_android.so" />
  23. <meta-data
  24. android:name="init_factory"
  25. android:value="pjmedia_webrtc_vid_capture_factory" />
  26. </receiver>
  27. -->
  28. <receiver android:name=".PluginReceiverFfmpeg" >
  29. <intent-filter>
  30. <action android:name="com.csipsimple.codecs.action.REGISTER_VIDEO_CODEC" />
  31. </intent-filter>
  32. <meta-data
  33. android:name="lib_name"
  34. android:value="libpj_video_android.so" />
  35. <meta-data
  36. android:name="init_factory"
  37. android:value="pjmedia_codec_ffmpeg_vid_init" />
  38. <meta-data
  39. android:name="deinit_factory"
  40. android:value="pjmedia_codec_ffmpeg_vid_deinit" />
  41. </receiver>
  42. <receiver android:name=".PluginReceiverVpx" >
  43. <intent-filter>
  44. <action android:name="com.csipsimple.codecs.action.REGISTER_VIDEO_CODEC" />
  45. </intent-filter>
  46. <meta-data
  47. android:name="lib_name"
  48. android:value="libpj_vpx.so" />
  49. <meta-data
  50. android:name="init_factory"
  51. android:value="pjmedia_codec_vpx_init" />
  52. <meta-data
  53. android:name="deinit_factory"
  54. android:value="pjmedia_codec_vpx_deinit" />
  55. </receiver>

它是如何消除回音的?

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

[java] view plaincopy
  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

转载于:https://www.cnblogs.com/John5/p/3978722.html

CSipSimple 简单分析相关推荐

  1. R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集、非线性:基函数展开和样条分析、你简单分析的不重要特征,可能只是线性不显著、而非线性是显著的

    R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集.非线性:基函数展开和样条分析.你简单分析的不重要特征,可能只是线性不显著.而非线性是显著的 目录

  2. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  3. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  4. howdoi 简单分析

    对howdoi的一个简单分析. 曾经看到过下面的这样一段js代码: try{doSth(); } catch (e){ask_url = "https://stackoverflow.com ...

  5. Mac与Phy组成原理的简单分析

    Mac与Phy组成原理的简单分析 2011-12-28 15:30:43 //http://blog.chinaunix.net/uid-20528014-id-3050217.html 本文乃fir ...

  6. python做数据可视化的代码_Python数据可视化正态分布简单分析及实现代码

    Python说来简单也简单,但是也不简单,尤其是再跟高数结合起来的时候... 正态分布(Normaldistribution),也称"常态分布",又名高斯分布(Gaussiandi ...

  7. ASIHTTPRequest源码简单分析

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比较底层的http库,功能比较少, ...

  8. Hessian 源码简单分析

    Hessian 源码简单分析 Hessian 是一个rpc框架, 我们需要先写一个服务端, 然后在客户端远程的调用它即可. 服务端: 服务端通常和spring 做集成. 首先写一个接口: public ...

  9. python预测股票价格tushare_用tushare对股票进行简单分析

    用tushare对股票进行简单分析(仅供交流学习) import numpy as np import pandas as pd import matplotlib.pyplot as plt imp ...

最新文章

  1. List查询排序删除泛型 应用
  2. python3之MongoDB
  3. 函数式编程笔记 01
  4. 2021年度训练联盟热身训练赛第五场F题Group Project
  5. struts+hibernate+oracle+easyui实现lazyout组件的简单案例——EmpDao层代码
  6. BOM(Browser Object Model)
  7. flask框架(三):flask配置文件
  8. 三级IP地址的子网划分
  9. PHP操控Excel视频教程
  10. 第九章第四节 流体压强与流速的关系
  11. dosbox基础使用
  12. win7计算机资源管理器卡住,如何解决win7系统资源管理器已停止工作的问题
  13. 小红书百万博主如何炼成?美妆博主专访
  14. ZUCC_计算机系统原理实验_大作业 bomb 破解
  15. 搭建vmware虚拟机,搭建docker环境
  16. android 恢复出厂,安卓(Android)手机恢复出厂设置的方法
  17. 在Latex论文下方添加copyright
  18. 机器学习,深度学习,环境搭建 #博学谷IT学习技术支持#
  19. 中文脏话识别的解决方案
  20. 【愚公系列】2022年02月 wireshark系列-数据抓包分析之ICMP协议

热门文章

  1. JS获取当前时间戳与时间戳转日期时间格式
  2. 计算机应用基础差错解释,计算机应用基础名词解释:差错
  3. java list控件_java Swing中对于JList控件的使用(一)
  4. QuantLib 金融计算——收益率曲线之构建曲线(1)
  5. 什么是CODECO 报文?
  6. 计算机二进制补位是什么,2、二进制
  7. 《计算机网络 自顶向下》第一章==计算机网络和因特网==随堂笔记
  8. (对抖音程序员隔空笔芯)抖音解析去水印iteminfo接口被封死,再放一个最新的去水印原理,无视频长度限制
  9. 立法保障交通权 日本交通政策基本法是如何制定的?
  10. IntelliJ IDEA下载