您的位置:首页 > 其它

CSIPSIMPLE 简单分析

2014-11-12 16:59 197 查看
简介

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

功能介绍

1、注册流程

用户首先选择使用哪国哪个类型,这是由com.csipsimple.wizards.impl包下完成的。该包下实现接口WizardIface,接口方法中有

SipProfile buildAccount(SipProfile account);产生一个帐号文件。

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

[java] view
plaincopy





/**

* 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中的

[java] view
plaincopy





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中,关键代码如下:

[java] view
plaincopy





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);

}

}

注册抓包信息如下:

[java] view
plaincopy





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代码中,代码如下:

[java] view
plaincopy





/**

* {@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);//底层拨打

}

});

}

[java] view
plaincopy





/**

* 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的类,关键代码如下:

[java] view
plaincopy





/*

* 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函数中有如下代码:

[java] view
plaincopy





// 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文件,原来是通过广播添加的,代码如下:

[java] view
plaincopy





<!-- Extra codecs 音频插件-->

<receiver

android:name="com.csipsimple.plugins.codecs.ReceiverSILK"

android:exported="false" >

<meta-data

android:name="lib_name"

android:value="libpj_silk_codec.so" />

<meta-data

android:name="init_factory"

android:value="pjmedia_codec_silk_init" />

<intent-filter>

<action android:name="com.csipsimple.codecs.action.REGISTER_CODEC" />

</intent-filter>

</receiver>

[java] view
plaincopy





<!-- 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="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="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="libpj_screen_capture_android.so" />

<meta-data

android:name="init_factory"

android:value="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="libpj_video_android.so" />

<meta-data

android:name="init_factory"

android:value="pjmedia_codec_ffmpeg_vid_init" />

<meta-data

android:name="deinit_factory"

android:value="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="libpj_vpx.so" />

<meta-data

android:name="init_factory"

android:value="pjmedia_codec_vpx_init" />

<meta-data

android:name="deinit_factory"

android:value="pjmedia_codec_vpx_deinit" />

</receiver>

它是如何消除回音的?

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

[java] view
plaincopy





//消除回音

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: