首页 > 代码库 > CSipSimple 简单分析
CSipSimple 简单分析
简介
CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话。连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果才好。下面简单分析一下其。
功能介绍
1、注册流程
用户首先选择使用哪国哪个类型,这是由com.csipsimple.wizards.impl包下完成的。该包下实现接口WizardIface,接口方法中有
SipProfile buildAccount(SipProfile account);产生一个帐号文件。
然后在BasePrefsWizard类下保存帐号,代码如下:
- /**
- * Save the account with given wizard id
- * @param wizardId the wizard to use for account entry
- */
- private void saveAccount(String wizardId) { //保存帐号(帐号保存到共享数据库中)
- boolean needRestart = false;
- PreferencesWrapper prefs = new PreferencesWrapper(getApplicationContext());
- account = wizard.buildAccount(account);
- account.wizard = wizardId;
- if (account.id == SipProfile.INVALID_ID) {
- // This account does not exists yet
- prefs.startEditing();
- wizard.setDefaultParams(prefs);
- prefs.endEditing();
- applyNewAccountDefault(account);
- Uri uri = getContentResolver().insert(SipProfile.ACCOUNT_URI, account.getDbContentValues());
- // After insert, add filters for this wizard
- account.id = ContentUris.parseId(uri);
- List<Filter> filters = wizard.getDefaultFilters(account);
- if (filters != null) {
- for (Filter filter : filters) {
- // Ensure the correct id if not done by the wizard
- filter.account = (int) account.id;
- getContentResolver().insert(SipManager.FILTER_URI, filter.getDbContentValues());
- }
- }
- // Check if we have to restart
- needRestart = wizard.needRestart();
- } else {
- // TODO : should not be done there but if not we should add an
- // option to re-apply default params
- prefs.startEditing();
- wizard.setDefaultParams(prefs);
- prefs.endEditing();
- getContentResolver().update(ContentUris.withAppendedId(SipProfile.ACCOUNT_ID_URI_BASE, account.id), account.getDbContentValues(), null, null);
- }
- // Mainly if global preferences were changed, we have to restart sip stack
- if (needRestart) { //保存完毕后发送重新加载sip
- Intent intent = new Intent(SipManager.ACTION_SIP_REQUEST_RESTART);
- sendBroadcast(intent);
- }
- }
然后执行SipService中的
- public void restartSipStack() throws SameThreadException {
- if(stopSipStack()) {
- startSipStack();
- }else {
- Log.e(THIS_FILE, "Can‘t stop ... so do not restart ! ");
- }
- }
- //private KeepAliveTimer kaAlarm;
- // This is always done in SipExecutor thread
- private void startSipStack() throws SameThreadException {
- //Cache some prefs
- supportMultipleCalls = prefsWrapper.getPreferenceBooleanValue(SipConfigManager.SUPPORT_MULTIPLE_CALLS);
- if(!isConnectivityValid()) {
- notifyUserOfMessage(R.string.connection_not_valid);
- Log.e(THIS_FILE, "No need to start sip");
- return;
- }
- Log.d(THIS_FILE, "Start was asked and we should actually start now");
- if(pjService == null) {
- Log.d(THIS_FILE, "Start was asked and pjService in not there");
- if(!loadStack()) {
- Log.e(THIS_FILE, "Unable to load SIP stack !! ");
- return;
- }
- }
- Log.d(THIS_FILE, "Ask pjservice to start itself");
- //presenceMgr.startMonitoring(this);
- if(pjService.sipStart()) {
- // This should be done after in acquire resource
- // But due to http://code.google.com/p/android/issues/detail?id=21635
- // not a good idea
- applyComponentEnablingState(true);
- registerBroadcasts();
- Log.d(THIS_FILE, "Add all accounts");
- addAllAccounts(); //关键添加帐户
- }
- }
- /**
- * Add accounts from database
- */
- private void addAllAccounts() throws SameThreadException {//从数据库中读取所有的帐户信息
- Log.d(THIS_FILE, "We are adding all accounts right now....");
- boolean hasSomeSuccess = false;
- Cursor c = getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION,
- SipProfile.FIELD_ACTIVE + "=?", new String[] {"1"}, null);
- if (c != null) {
- try {
- int index = 0;
- if(c.getCount() > 0) {
- c.moveToFirst();
- do {
- SipProfile account = new SipProfile(c);
- if (pjService != null && pjService.addAccount(account) ) {//加入到pjsip
- hasSomeSuccess = true;
- }
- index ++;
- } while (c.moveToNext() && index < 10);
- }
- } catch (Exception e) {
- Log.e(THIS_FILE, "Error on looping over sip profiles", e);
- } finally {
- c.close();
- }
- }
- hasSomeActiveAccount = hasSomeSuccess;
- if (hasSomeSuccess) {
- acquireResources();
- } else {
- releaseResources();
- if (notificationManager != null) {
- notificationManager.cancelRegisters();
- }
- }
- }
- //设置帐户注册状态信息
- public boolean setAccountRegistration(SipProfile account, int renew, boolean forceReAdd) throws SameThreadException {
- boolean status = false;
- if(pjService != null) {
- status = pjService.setAccountRegistration(account, renew, forceReAdd);
- }
- return status;
- }
- /**
- * Remove accounts from database 从数据库中移除帐号信息
- */
- private void unregisterAllAccounts(boolean cancelNotification) throws SameThreadException {
- releaseResources();
- Log.d(THIS_FILE, "Remove all accounts");
- Cursor c = getContentResolver().query(SipProfile.ACCOUNT_URI, DBProvider.ACCOUNT_FULL_PROJECTION, null, null, null);
- if (c != null) {
- try {
- c.moveToFirst();
- do {
- SipProfile account = new SipProfile(c);
- setAccountRegistration(account, 0, false);
- } while (c.moveToNext() );
- } catch (Exception e) {
- Log.e(THIS_FILE, "Error on looping over sip profiles", e);
- } finally {
- c.close();
- }
- }
- if (notificationManager != null && cancelNotification) {
- notificationManager.cancelRegisters();
- }
- }
- //重新加载帐户数据库
- private void reAddAllAccounts() throws SameThreadException {
- Log.d(THIS_FILE, "RE REGISTER ALL ACCOUNTS");
- unregisterAllAccounts(false);
- addAllAccounts();
- }
真正实现注册的是在PjSipService中,关键代码如下:
- public boolean addAccount(SipProfile profile) throws SameThreadException {//底层注册
- int status = pjsuaConstants.PJ_FALSE;
- if (!created) { //是否已创建
- Log.e(THIS_FILE, "PJSIP is not started here, nothing can be done");
- return status == pjsuaConstants.PJ_SUCCESS;
- }
- PjSipAccount account = new PjSipAccount(profile); //帐户信息
- account.applyExtraParams(service);
- // Force the use of a transport
- /*
- * switch (account.transport) { case SipProfile.TRANSPORT_UDP: if
- * (udpTranportId != null) {
- * //account.cfg.setTransport_id(udpTranportId); } break; case
- * SipProfile.TRANSPORT_TCP: if (tcpTranportId != null) { //
- * account.cfg.setTransport_id(tcpTranportId); } break; case
- * SipProfile.TRANSPORT_TLS: if (tlsTransportId != null) { //
- * account.cfg.setTransport_id(tlsTransportId); } break; default: break;
- * }
- */
- SipProfileState currentAccountStatus = getProfileState(profile);
- account.cfg.setRegister_on_acc_add(pjsuaConstants.PJ_FALSE);//注册
- if (currentAccountStatus.isAddedToStack()) {//是否加入到堆栈
- pjsua.csipsimple_set_acc_user_data(currentAccountStatus.getPjsuaId(), account.css_cfg);//设置帐户信息
- status = pjsua.acc_modify(currentAccountStatus.getPjsuaId(), account.cfg);//修改配置信息
- beforeAccountRegistration(currentAccountStatus.getPjsuaId(), profile);//调用注册前函数
- ContentValues cv = new ContentValues();
- cv.put(SipProfileState.ADDED_STATUS, status);
- service.getContentResolver().update(
- ContentUris.withAppendedId(SipProfile.ACCOUNT_STATUS_ID_URI_BASE, profile.id),
- cv, null, null); //更新帐户信息
- if (!account.wizard.equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {
- // Re register
- if (status == pjsuaConstants.PJ_SUCCESS) {
- status = pjsua.acc_set_registration(currentAccountStatus.getPjsuaId(), 1);
- if (status == pjsuaConstants.PJ_SUCCESS) {
- pjsua.acc_set_online_status(currentAccountStatus.getPjsuaId(), 1);//更新帐户状态
- }
- }
- }
- } else {
- int[] accId = new int[1];
- if (account.wizard.equalsIgnoreCase(WizardUtils.LOCAL_WIZARD_TAG)) {
- // We already have local account by default
- // For now consider we are talking about UDP one
- // In the future local account should be set per transport
- switch (account.transport) { //选择穿透方式
- case SipProfile.TRANSPORT_UDP:
- accId[0] = prefsWrapper.useIPv6() ? localUdp6AccPjId : localUdpAccPjId;
- break;
- case SipProfile.TRANSPORT_TCP:
- accId[0] = prefsWrapper.useIPv6() ? localTcp6AccPjId : localTcpAccPjId;
- break;
- case SipProfile.TRANSPORT_TLS:
- accId[0] = prefsWrapper.useIPv6() ? localTls6AccPjId : localTlsAccPjId;
- break;
- default:
- // By default use UDP
- accId[0] = localUdpAccPjId;
- break;
- }
- pjsua.csipsimple_set_acc_user_data(accId[0], account.css_cfg);//设置用户配置信息
- // TODO : use video cfg here
- // nCfg.setVid_in_auto_show(pjsuaConstants.PJ_TRUE);
- // nCfg.setVid_out_auto_transmit(pjsuaConstants.PJ_TRUE);
- // status = pjsua.acc_modify(accId[0], nCfg);
- } else {
- // Cause of standard account different from local account :)
- status = pjsua.acc_add(account.cfg, pjsuaConstants.PJ_FALSE, accId);
- pjsua.csipsimple_set_acc_user_data(accId[0], account.css_cfg);
- beforeAccountRegistration(accId[0], profile);
- pjsua.acc_set_registration(accId[0], 1);
- }
- if (status == pjsuaConstants.PJ_SUCCESS) {//成功设置状态信息
- SipProfileState ps = new SipProfileState(profile);
- ps.setAddedStatus(status);
- ps.setPjsuaId(accId[0]);
- service.getContentResolver().insert(
- ContentUris.withAppendedId(SipProfile.ACCOUNT_STATUS_ID_URI_BASE,
- account.id), ps.getAsContentValue());
- pjsua.acc_set_online_status(accId[0], 1);
- }
- }
- return status == pjsuaConstants.PJ_SUCCESS;
- }
- void beforeAccountRegistration(int pjId, SipProfile profile) { //注册前触发
- for (PjsipModule mod : pjsipModules.values()) {
- mod.onBeforeAccountStartRegistration(pjId, profile);
- }
- }
注册抓包信息如下:
- REGISTER sip:www.**.net:5060 SIP/2.0
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport;branch=z9hG4bKPjfvTjKWT5urgwc2nwez3BgasaQYYDLpTj
- Route: <sip:www.**.net:5060;transport=udp;lr>
- Max-Forwards: 70
- From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx
- To: <sip:1001@www.**.net>
- Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB
- CSeq: 63463 REGISTER
- User-Agent: CSipSimple_generic-8/r2353
- Contact: <sip:1001@10.0.2.15:60591;ob>
- Expires: 900
- Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
- Content-Length: 0
- SIP/2.0 401 Unauthorized
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjfvTjKWT5urgwc2nwez3BgasaQYYDLpTj;received=192.168.1.154
- From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx
- To: <sip:1001@www.**.net>;tag=4f5e0299ccbb80ebb6598255e669265c.a5b5
- Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB
- CSeq: 63463 REGISTER
- WWW-Authenticate: Digest realm="www.**.net", nonce="Ux5l0lMeZKaFznRge1gZtxoYW//UWjA2"
- Server: kamailio (4.0.3 (x86_64/linux))
- Content-Length: 0
- REGISTER sip:www.**.net:5060 SIP/2.0
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport;branch=z9hG4bKPjrw3ueP2qlwf7pE6T2eM.b..-AeoqKmdc
- Route: <sip:www.**.net:5060;transport=udp;lr>
- Max-Forwards: 70
- From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx
- To: <sip:1001@www.**.net>
- Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB
- CSeq: 63464 REGISTER
- User-Agent: CSipSimple_generic-8/r2353
- Contact: <sip:1001@10.0.2.15:60591;ob>
- Expires: 900
- Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
- Authorization: Digest username="1001", realm="www.**.net", nonce="Ux5l0lMeZKaFznRge1gZtxoYW//UWjA2", uri="sip:www.**.net:5060", response="8a006ec04c954b1533a5a895d77929c5"
- Content-Length: 0
- SIP/2.0 200 OK
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjrw3ueP2qlwf7pE6T2eM.b..-AeoqKmdc;received=192.168.1.154
- From: <sip:1001@www.**.net>;tag=M3IMkYd2B3u30Nto9ctSUMerD.7ya2kx
- To: <sip:1001@www.**.net>;tag=4f5e0299ccbb80ebb6598255e669265c.eb21
- Call-ID: srV7dgEt90L2cGD7t--2OVnQGFhPXcbB
- CSeq: 63464 REGISTER
- Contact: <sip:1001@10.0.2.15:60591;ob>;expires=600;received="sip:192.168.1.154:52571"
- Server: kamailio (4.0.3 (x86_64/linux))
- Content-Length: 0
- SUBSCRIBE sip:1001@www.**.net SIP/2.0
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport;branch=z9hG4bKPjgXYRCe5ny..u9iYYumroyVEWYE8V0hYS
- Max-Forwards: 70
- From: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb
- To: <sip:1001@www.**.net>
- Contact: <sip:1001@192.168.1.154:52571;ob>
- Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp
- CSeq: 24382 SUBSCRIBE
- Route: <sip:www.**.net:5060;transport=udp;lr>
- Event: message-summary
- Expires: 3600
- Supported: replaces, 100rel, timer, norefersub
- Accept: application/simple-message-summary
- Allow-Events: presence, message-summary, refer
- User-Agent: CSipSimple_generic-8/r2353
- Content-Length: 0
- SIP/2.0 407 Proxy Authentication Required
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjgXYRCe5ny..u9iYYumroyVEWYE8V0hYS;received=192.168.1.154
- From: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb
- To: <sip:1001@www.**.net>;tag=4f5e0299ccbb80ebb6598255e669265c.e4b6
- Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp
- CSeq: 24382 SUBSCRIBE
- Proxy-Authenticate: Digest realm="www.**.net", nonce="Ux5l0lMeZKaFznRge1gZtxoYW//UWjA2"
- Server: kamailio (4.0.3 (x86_64/linux))
- Content-Length: 0
- SIP/2.0 202 OK
- Via: SIP/2.0/UDP 10.0.2.15:60591;rport=52571;branch=z9hG4bKPjRhk4xHkOkvy82g1N2n3I-d0m2CHWwlJT;received=192.168.1.154
- From: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb
- To: <sip:1001@www.**.net>;tag=afbf025b308b45bb32e1b93911cf7810-9f85
- Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp
- CSeq: 24383 SUBSCRIBE
- Expires: 3600
- Contact: <sip:113.195.206.200:5060;transport=udp>
- Server: kamailio (4.0.3 (x86_64/linux))
- Content-Length: 0
- SIP/2.0 200 OK
- Via: SIP/2.0/UDP 113.195.206.200;received=113.195.206.200;branch=z9hG4bKa281.981eed9.0
- Call-ID: AEzcRXNFhpLpFEJDhAJ-qtZxbZASJmfp
- From: <sip:1001@www.**.net>;tag=afbf025b308b45bb32e1b93911cf7810-9f85
- To: <sip:1001@www.**.net>;tag=ciLmE1XieFD0ZkO0CblHPwCRMiQyL8Vb
- CSeq: 2 NOTIFY
- Contact: <sip:1001@192.168.1.154:52571;ob>
- Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
- Supported: replaces, 100rel, timer, norefersub
- Content-Length: 0
2、电话拨打、电话监听
电话的拨打在SipService代码中,代码如下:
- /**
- * {@inheritDoc}
- */
- @Override
- public void makeCall(final String callee, final int accountId) throws RemoteException {
- makeCallWithOptions(callee, accountId, null);
- }
- @Override
- public void makeCallWithOptions(final String callee, final int accountId, final Bundle options)
- throws RemoteException {
- SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
- //We have to ensure service is properly started and not just binded
- SipService.this.startService(new Intent(SipService.this, SipService.class));//开启服务
- if(pjService == null) {
- Log.e(THIS_FILE, "Can‘t place call if service not started");
- // TODO - we should return a failing status here
- return;
- }
- if(!supportMultipleCalls) {
- // Check if there is no ongoing calls if so drop this request by alerting user
- SipCallSession activeCall = pjService.getActiveCallInProgress();//已有电话
- if(activeCall != null) {
- if(!CustomDistribution.forceNoMultipleCalls()) {
- notifyUserOfMessage(R.string.not_configured_multiple_calls);
- }
- return;
- }
- }
- getExecutor().execute(new SipRunnable() {
- @Override
- protected void doRun() throws SameThreadException {
- pjService.makeCall(callee, accountId, options);//底层拨打
- }
- });
- }
- /**
- * Make a call
- *
- * @param callee
- * remote contact ot call If not well formated we try to add
- * domain name of the default account
- */
- public int makeCall(String callee, int accountId, Bundle b)
- throws SameThreadException {
- if (!created) { // 未创建
- return -1;
- }
- final ToCall toCall = sanitizeSipUri(callee, accountId);// 构造对应sip地址
- if (toCall != null) {
- pj_str_t uri = pjsua.pj_str_copy(toCall.getCallee());
- // Nothing to do with this values
- byte[] userData = new byte[1];
- int[] callId = new int[1];
- pjsua_call_setting cs = new pjsua_call_setting();
- pjsua_msg_data msgData = new pjsua_msg_data();
- int pjsuaAccId = toCall.getPjsipAccountId();
- // Call settings to add video
- pjsua.call_setting_default(cs);// 添加电话配置信息
- cs.setAud_cnt(1);
- cs.setVid_cnt(0);
- if (b != null && b.getBoolean(SipCallSession.OPT_CALL_VIDEO, false)) {
- cs.setVid_cnt(1);
- }
- cs.setFlag(0);
- pj_pool_t pool = pjsua.pool_create("call_tmp", 512, 512);// 池
- // Msg data to add headers
- pjsua.msg_data_init(msgData); // 构造消息信息
- pjsua.csipsimple_init_acc_msg_data(pool, pjsuaAccId, msgData);
- if (b != null) {
- Bundle extraHeaders = b
- .getBundle(SipCallSession.OPT_CALL_EXTRA_HEADERS);
- if (extraHeaders != null) {
- for (String key : extraHeaders.keySet()) {
- try {
- String value = extraHeaders.getString(key);
- if (!TextUtils.isEmpty(value)) {
- int res = pjsua
- .csipsimple_msg_data_add_string_hdr(
- pool, msgData,
- pjsua.pj_str_copy(key),
- pjsua.pj_str_copy(value));
- if (res == pjsuaConstants.PJ_SUCCESS) {
- Log.e(THIS_FILE, "Failed to add Xtra hdr ("
- + key + " : " + value
- + ") probably not X- header");
- }
- }
- } catch (Exception e) {
- Log.e(THIS_FILE, "Invalid header value for key : "
- + key);
- }
- }
- }
- }
- // 拨打电话
- int status = pjsua.call_make_call(pjsuaAccId, uri, cs, userData,
- msgData, callId);
- if (status == pjsuaConstants.PJ_SUCCESS) {
- dtmfToAutoSend.put(callId[0], toCall.getDtmf());
- Log.d(THIS_FILE, "DTMF - Store for " + callId[0] + " - "
- + toCall.getDtmf());
- }
- pjsua.pj_pool_release(pool); // 释放
- return status;
- } else {
- service.notifyUserOfMessage(service
- .getString(R.string.invalid_sip_uri) + " : " + callee);
- }
- return -1;
- }
电话监听
电话监听在UAStateReceiver中,该类是继承Callback的,Callback是调用jni的类,关键代码如下:
- /*
- * private class IncomingCallInfos { public SipCallSession callInfo; public
- * Integer accId; }
- */
- @Override
- public void on_incoming_call(final int accId, final int callId, SWIGTYPE_p_pjsip_rx_data rdata) {
- lockCpu();
- // Check if we have not already an ongoing call
- boolean hasOngoingSipCall = false;
- if (pjService != null && pjService.service != null) {
- SipCallSessionImpl[] calls = getCalls();
- if (calls != null) {
- for (SipCallSessionImpl existingCall : calls) {
- if (!existingCall.isAfterEnded() && existingCall.getCallId() != callId) {
- if (!pjService.service.supportMultipleCalls) {
- Log.e(THIS_FILE,
- "Settings to not support two call at the same time !!!");
- // If there is an ongoing call and we do not support
- // multiple calls
- // Send busy here
- pjsua.call_hangup(callId, StatusCode.BUSY_HERE, null, null);
- unlockCpu();
- return;
- } else {
- hasOngoingSipCall = true;
- }
- }
- }
- }
- }
- try {
- SipCallSessionImpl callInfo = updateCallInfoFromStack(callId, null);
- Log.d(THIS_FILE, "Incoming call << for account " + accId);
- // Extra check if set reference counted is false ???
- if (!ongoingCallLock.isHeld()) {
- ongoingCallLock.acquire();
- }
- final String remContact = callInfo.getRemoteContact();
- callInfo.setIncoming(true);
- notificationManager.showNotificationForCall(callInfo);
- // Auto answer feature
- SipProfile acc = pjService.getAccountForPjsipId(accId);
- Bundle extraHdr = new Bundle();
- fillRDataHeader("Call-Info", rdata, extraHdr);
- final int shouldAutoAnswer = pjService.service.shouldAutoAnswer(remContact, acc,
- extraHdr);
- Log.d(THIS_FILE, "Should I anto answer ? " + shouldAutoAnswer);
- if (shouldAutoAnswer >= 200) {
- // Automatically answer incoming calls with 200 or higher final
- // code
- pjService.callAnswer(callId, shouldAutoAnswer);
- } else {
- // Ring and inform remote about ringing with 180/RINGING
- pjService.callAnswer(callId, 180);
- if (pjService.mediaManager != null) {
- if (pjService.service.getGSMCallState() == TelephonyManager.CALL_STATE_IDLE
- && !hasOngoingSipCall) {
- pjService.mediaManager.startRing(remContact);
- } else {
- pjService.mediaManager.playInCallTone(MediaManager.TONE_CALL_WAITING);
- }
- }
- broadCastAndroidCallState("RINGING", remContact);
- }
- if (shouldAutoAnswer < 300) {
- // Or by api
- launchCallHandler(callInfo);
- Log.d(THIS_FILE, "Incoming call >>");
- }
- } catch (SameThreadException e) {
- // That‘s fine we are in a pjsip thread
- } finally {
- unlockCpu();
- }
- }
3、音频视频编解码
我们知道CSipsimple中的音频编解码、视频编解码是以插件的形式加入的。我们先看下它是如何加入的。
在PjSipService中sipStart函数中有如下代码:
- // Audio implementation 加入音频插件
- int implementation = prefsWrapper
- .getPreferenceIntegerValue(SipConfigManager.AUDIO_IMPLEMENTATION);
- if (implementation == SipConfigManager.AUDIO_IMPLEMENTATION_OPENSLES) {
- dynamic_factory audImp = cssCfg.getAudio_implementation();
- audImp.setInit_factory_name(pjsua
- .pj_str_copy("pjmedia_opensl_factory"));
- File openslLib = NativeLibManager.getBundledStackLibFile(
- service, "libpj_opensl_dev.so");
- audImp.setShared_lib_path(pjsua.pj_str_copy(openslLib
- .getAbsolutePath()));
- cssCfg.setAudio_implementation(audImp);
- Log.d(THIS_FILE, "Use OpenSL-ES implementation");
- }
- // Video implementation 加入视频插件
- if (prefsWrapper
- .getPreferenceBooleanValue(SipConfigManager.USE_VIDEO)) {
- // TODO :: Have plugins per capture / render / video codec /
- // converter
- Map<String, DynCodecInfos> videoPlugins = ExtraPlugins
- .getDynCodecPlugins(service,
- SipManager.ACTION_GET_VIDEO_PLUGIN);
- if (videoPlugins.size() > 0) {
- DynCodecInfos videoPlugin = videoPlugins.values()
- .iterator().next();
- pj_str_t pjVideoFile = pjsua
- .pj_str_copy(videoPlugin.libraryPath);
- Log.d(THIS_FILE, "Load video plugin at "
- + videoPlugin.libraryPath);
- // Render
- {
- dynamic_factory vidImpl = cssCfg
- .getVideo_render_implementation();
- vidImpl.setInit_factory_name(pjsua
- .pj_str_copy("pjmedia_webrtc_vid_render_factory"));
- vidImpl.setShared_lib_path(pjVideoFile);
- }
- // Capture
- {
- dynamic_factory vidImpl = cssCfg
- .getVideo_capture_implementation();
- vidImpl.setInit_factory_name(pjsua
- .pj_str_copy("pjmedia_webrtc_vid_capture_factory"));
- vidImpl.setShared_lib_path(pjVideoFile);
- /*
- * -- For testing video screen -- Not yet released
- * try { ComponentName cmp = new
- * ComponentName("com.csipsimple.plugins.video",
- * "com.csipsimple.plugins.video.CaptureReceiver");
- * DynCodecInfos screenCapt = new
- * ExtraPlugins.DynCodecInfos(service, cmp);
- * vidImpl.setInit_factory_name(pjsua
- * .pj_str_copy(screenCapt.factoryInitFunction));
- * vidImpl.setShared_lib_path(pjsua
- * .pj_str_copy(screenCapt.libraryPath)); } catch
- * (NameNotFoundException e) { Log.e(THIS_FILE,
- * "Not found capture plugin"); }
- */
- }
- // Video codecs 加入视频解码
- availableCodecs = ExtraPlugins.getDynCodecPlugins(
- service,
- SipManager.ACTION_GET_EXTRA_VIDEO_CODECS);
- cssCodecs = cssCfg.getExtra_vid_codecs();
- dynamic_factory[] cssCodecsDestroy = cssCfg
- .getExtra_vid_codecs_destroy();
- i = 0;
- for (Entry<String, DynCodecInfos> availableCodec : availableCodecs
- .entrySet()) {
- DynCodecInfos dyn = availableCodec.getValue();
- if (!TextUtils.isEmpty(dyn.libraryPath)) {
- // Create
- cssCodecs[i].setShared_lib_path(pjsua
- .pj_str_copy(dyn.libraryPath));
- cssCodecs[i].setInit_factory_name(pjsua
- .pj_str_copy(dyn.factoryInitFunction));
- // Destroy
- cssCodecsDestroy[i].setShared_lib_path(pjsua
- .pj_str_copy(dyn.libraryPath));
- cssCodecsDestroy[i]
- .setInit_factory_name(pjsua
- .pj_str_copy(dyn.factoryDeinitFunction));
- }
- i++;
- }
- cssCfg.setExtra_vid_codecs_cnt(i);
- // Converter
- dynamic_factory convertImpl = cssCfg.getVid_converter();
- convertImpl.setShared_lib_path(pjVideoFile);
- convertImpl
- .setInit_factory_name(pjsua
- .pj_str_copy("pjmedia_libswscale_converter_init"));
- }
- }
这只是对音频、视频编解码信息的读取,那具体是如何加入的呢?看一下AndroidManifest.xml文件,原来是通过广播添加的,代码如下:
- <!-- Extra codecs 音频插件-->
- <receiver
- android:name="com.csipsimple.plugins.codecs.ReceiverSILK"
- android:exported="false" >
- <meta-data
- android:name="lib_name"
- android:value=http://www.mamicode.com/"libpj_silk_codec.so" />
- <meta-data
- android:name="init_factory"
- android:value=http://www.mamicode.com/"pjmedia_codec_silk_init" />
- <intent-filter>
- <action android:name="com.csipsimple.codecs.action.REGISTER_CODEC" />
- </intent-filter>
- </receiver>
- <!-- Receiver for standard video 视频插件 -->
- <receiver android:name=".PluginReceiver" >
- <intent-filter>
- <action android:name="com.csipsimple.plugins.action.REGISTER_VIDEO" />
- </intent-filter>
- <meta-data
- android:name="lib_name"
- android:value=http://www.mamicode.com/"libpj_video_android.so" />
- <!-- For now it does not matter in the future we should have one per device, codec, and converter (if needed) -->
- <meta-data
- android:name="init_factory"
- android:value=http://www.mamicode.com/"pjmedia_webrtc_vid_render_factory" />
- </receiver>
- <!--
- Receiver for video capture
- <receiver android:name=".CaptureReceiver" >
- <intent-filter>
- <action android:name="com.csipsimple.plugins.action.REGISTER_CAPTURE_VIDEO" />
- </intent-filter>
- <meta-data
- android:name="lib_name"
- android:value=http://www.mamicode.com/"libpj_screen_capture_android.so" />
- <meta-data
- android:name="init_factory"
- android:value=http://www.mamicode.com/"pjmedia_webrtc_vid_capture_factory" />
- </receiver>
- -->
- <receiver android:name=".PluginReceiverFfmpeg" >
- <intent-filter>
- <action android:name="com.csipsimple.codecs.action.REGISTER_VIDEO_CODEC" />
- </intent-filter>
- <meta-data
- android:name="lib_name"
- android:value=http://www.mamicode.com/"libpj_video_android.so" />
- <meta-data
- android:name="init_factory"
- android:value=http://www.mamicode.com/"pjmedia_codec_ffmpeg_vid_init" />
- <meta-data
- android:name="deinit_factory"
- android:value=http://www.mamicode.com/"pjmedia_codec_ffmpeg_vid_deinit" />
- </receiver>
- <receiver android:name=".PluginReceiverVpx" >
- <intent-filter>
- <action android:name="com.csipsimple.codecs.action.REGISTER_VIDEO_CODEC" />
- </intent-filter>
- <meta-data
- android:name="lib_name"
- android:value=http://www.mamicode.com/"libpj_vpx.so" />
- <meta-data
- android:name="init_factory"
- android:value=http://www.mamicode.com/"pjmedia_codec_vpx_init" />
- <meta-data
- android:name="deinit_factory"
- android:value=http://www.mamicode.com/"pjmedia_codec_vpx_deinit" />
- </receiver>
它是如何消除回音的?
在PjSipService文件中,有如下函数:
- //消除回音
- public void setEchoCancellation(boolean on) throws SameThreadException {
- if (created && userAgentReceiver != null) {
- Log.d(THIS_FILE, "set echo cancelation " + on);
- pjsua.set_ec(
- on ? prefsWrapper.getEchoCancellationTail() : 0,
- prefsWrapper
- .getPreferenceIntegerValue(SipConfigManager.ECHO_MODE));
- }
- }
原来是通过底层进行回音消除的。
结束
简单的分析一下CSipSimple,对sip的认识又进了一步,近期将对它进行再封装。
转:http://blog.csdn.net/banketree/article/details/20990997
CSipSimple 简单分析