[转]【linphone】android--ip电话
1. 如何初始化参数?
2.如何打电话?
3.如何接电话?
4.上层和下层如何调用?
5.声音如何接上
==============
android2.3部分的sip实现
ap部分:
1.初始化参数
public void initializeManager() {
if(manager == null) {
manager = SipManager.newInstance(this); //初始化call manager.
}
initializeLocalProfile(); //初始化profile,也即是参数
}
…………
public void initializeLocalProfile() {
if (manager == null) {
return;
}
if (me != null) {
closeLocalProfile();
}
//得到username,pwd, domain
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
String username = prefs.getString("namePref", "");
String domain = prefs.getString("domainPref", "");
String password = prefs.getString("passPref", "");
//check 参数
if (username.length() == 0 || domain.length() == 0 || password.length() == 0) {
showDialog(UPDATE_SETTINGS_DIALOG);
return;
}
try {
//构建profile类
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
me = builder.build();
//构建profile类的包装,并通过manager启动profile的运用
Intent i = new Intent();
i.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
// This listener must be added AFTER manager.open is called,
// Otherwise the methods aren't guaranteed to fire.
//注册回调,监控注册的情况,并在UI上响应。
manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
updateStatus("Registering with SIP Server...");
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
updateStatus("Ready");
}
public void onRegistrationFailed(String localProfileUri, int errorCode,
String errorMessage) {
updateStatus("Registration failed. Please check settings.");
}
});
} catch (ParseException pe) {
updateStatus("Connection Error.");
} catch (SipException se) {
updateStatus("Connection error.");
}
}
2.打电话
public void initiateCall() {
//更新UI,显示被叫方的sip地址
updateStatus(sipAddress);
try {
//设置call过程中的回调
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// Much of the client's interaction with the SIP Stack will
// happen via listeners. Even making an outgoing call, don't
// forget to set up a listener to set things up once the call is established.
@Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
call.toggleMute();
updateStatus(call);
}
@Override
public void onCallEnded(SipAudioCall call) {
updateStatus("Ready.");
}
};
//打出call。
call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);
}
catch (Exception e) {
Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity/InitiateCall",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
3.接电话
构建一个broadcast receiver
public void onReceive(Context context, Intent intent) {
SipAudioCall incomingCall = null;
try {
//call的监听,回调实现
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
@Override
public void onRinging(SipAudioCall call, SipProfile caller) {
try {
call.answerCall(30);
} catch (Exception e) {
e.printStackTrace();
}
}
};
WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;
//注册监听
incomingCall = wtActivity.manager.takeAudioCall(intent, listener);
incomingCall.answerCall(30);
incomingCall.startAudio();
incomingCall.setSpeakerMode(true);
if(incomingCall.isMuted()) {
incomingCall.toggleMute();
}
wtActivity.call = incomingCall;
wtActivity.updateStatus(incomingCall);
} catch (Exception e) {
if (incomingCall != null) {
incomingCall.close();
}
}
}
4. 上下层的调用
这个层次的问题在framework中,看framework是如何同协议栈等的交互。
linphone的app
1.注册参数
[email protected] 启动linphone的lib,同时对proxy进行注册。关键的在于iterate。
private synchronized void startLibLinphone(final Context context) {
try {
copyAssetsFromPackage(context);
mLc = LinphoneCoreFactory.instance().createLinphoneCore(
this, linphoneConfigFile, linphoneInitialConfigFile, null);
mLc.enableIpv6(mPref.getBoolean(getString(R.string.pref_ipv6_key), false));
mLc.setPlaybackGain(3);
mLc.setRing(null);
try {
//初始化参数从conf中
initFromConf(context);
} catch (LinphoneException e) {
Log.w(TAG, "no config ready yet");
}
TimerTask lTask = new TimerTask() {
@Override
public void run() {
mLc.iterate();
}
};
//
Main loop function. It is crucial that your application call it periodically. #iterate() performs various backgrounds tasks:
- receiving of SIP messages
- handles timers and timeout
- performs registration to proxies
authentication retries The application MUST call this function from periodically, in its main loop.
Be careful that this function must be call from the same thread as other liblinphone methods. In not the case make sure all liblinphone calls are serialized with a mutex.
//每隔100ms去调用iterate来获得下层的消息。
mTimer.scheduleAtFixedRate(lTask, 0, 100);
IntentFilter lFilter = new IntentFilter(Intent.ACTION_SCREEN_ON);
lFilter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mKeepAliveReceiver, lFilter);
}
catch (Exception e) {
Log.e(TAG,"Cannot start linphone",e);
}
}
2 打电话
LinphoneManager.getInstance().newOutgoingCall(mAddress);
……
public void newOutgoingCall(AddressType address) {
String to = address.getText().toString();
if (mLc.isIncall()) {
serviceListener.tryingNewOutgoingCallButAlreadyInCall();
return;
}
LinphoneAddress lAddress;
try {
lAddress = mLc.interpretUrl(to);
} catch (LinphoneCoreException e) {
serviceListener.tryingNewOutgoingCallButWrongDestinationAddress();
return;
}
lAddress.setDisplayName(address.getDisplayedName());
try {
boolean prefVideoEnable = isVideoEnabled();
boolean prefInitiateWithVideo = mPref.getBoolean(mR.getString(R.string.pref_video_initiate_call_with_video_key), false);
resetCameraFromPreferences();
CallManager.getInstance().inviteAddress(lAddress, prefVideoEnable && prefInitiateWithVideo);
} catch (LinphoneCoreException e) {
serviceListener.tryingNewOutgoingCallButCannotGetCallParameters();
return;
}
}
……
void inviteAddress(LinphoneAddress lAddress, boolean videoEnabled) throws LinphoneCoreException {
LinphoneCore lc = LinphoneManager.getLc();
LinphoneCallParams params = lc.createDefaultCallParameters();
bm().updateWithProfileSettings(lc, params);
if (videoEnabled && params.getVideoEnabled()) {
videoManager().setMuted(false);
params.setVideoEnabled(true);
} else {
params.setVideoEnabled(false);
}
lc.inviteAddressWithParams(lAddress, params);
}
3 接电话
[email protected](由lib回调) --->serviceListener.onCallStateChanged(call, state, message)
public void onCallStateChanged(final LinphoneCall call, final State state, final String message) {
if (state == LinphoneCall.State.IncomingReceived) {
//wakeup linphone
startActivity(new Intent()
.setClass(this, LinphoneActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} else if (state == LinphoneCall.State.StreamsRunning) {
if (LinphoneActivity.isInstanciated()
&& getResources().getBoolean(R.bool.use_video_activity)
&& call.getCurrentParamsCopy().getVideoEnabled()) {
LinphoneActivity.instance().startVideoActivity();
}
}
mHandler.post(new Runnable() {
public void run() {
if (guiListener() != null)
guiListener().onCallStateChanged(call, state, message);
}
});
}
接受视频部分:整个视频通话界面是一个activity,里面有两个surfaceview,本地的由本地camera capture得来。对方视频由LinphoneCoreImpl.java中
public void setVideoWindow(Object w) {
if (mVideoWindow!=null)
mVideoWindow.setListener(null);
mVideoWindow=new AndroidVideoWindowImpl((SurfaceView) w);
mVideoWindow.setListener(new AndroidVideoWindowImpl.VideoWindowListener(){
public void onSurfaceDestroyed(AndroidVideoWindowImpl vw) {
setVideoWindowId(nativePtr,null);
}
public void onSurfaceReady(AndroidVideoWindowImpl vw) {
setVideoWindowId(nativePtr,vw);
}
});
}
- =========
启动linphoneactivity或者启动VideoActivity。同时UI变化
guiListener().onCallStateChanged(call, state, message);
===========
[email protected] (UI变化,进入对应的模式)
public void onCallStateChanged(LinphoneCall call, State state, String message) {
LinphoneCore lc = LinphoneManager.getLc();
if (state == LinphoneCall.State.OutgoingInit) {
enterIncallMode(lc);
} else if (state == LinphoneCall.State.IncomingReceived) {
callPending(call);
} else if (state == LinphoneCall.State.Connected) {
if (call.getDirection() == CallDirection.Incoming) {
enterIncallMode(lc);
}
} else if (state == LinphoneCall.State.Error) {
if (mWakeLock.isHeld()) mWakeLock.release();
showToast(R.string.call_error, message);
exitCallMode();
} else if (state == LinphoneCall.State.CallEnd) {
exitCallMode();
}
}
4 上下层交互
JNI. && 上层有iterate不断的循环调用下层的方法,从而使上层能获得下层的消息。
5 参数变化
a 启动linphone的时候,会初始化一次(从1的分析中可以看出)
b 在preference的onpause中可以看到一次,在preference要退到后台或者要关闭的时候,执行一次参数重新配置。可见并不是点了一项,就应用,而是所有的参数一起应用。
LinphoneManager.getInstance().initFromConf(getApplicationContext());
c firstloginActivity中一次。
6 call的状态变化
通过接口interface LinphoneCoreListener来实现,该接口被lib调用
7 linphone 中各个文件的作用
org.linphone
AboutActivity.java 关于,不用说
BandwidthManager.java 无继承,设置上行和下行带宽,并且对应设置video的size
BootReceview.java 开启启动接收的recevier,目的是启动linphoneservice
CallManager.java 无继承,Handle call updating, reinvites
org.linphone.coreimpl
Hacks.java 工具类,判断手机类型,及摄像头类型
org.linphone.core.video 此包主要处理camera配置,record配置,recordmanager
AndroidcameraConf.java 基类,提供camera conf的基础部件,提供内部类AndroidCameras(camera的最基本实现)
AndroidcameraConf5.java 针对2.3以下的手机,并且对有双摄像头的2.3以下的手机提供支持。
AndroidcameraConf9.java 正对普通的android2.3以上的手机
AndroidCameraRecord.java 抽象类
AndroidCameraRecordImpl.java 实现类,sdk5以上。onPreviewFrame中使用native function
AndroidCameraRecord8.java 继承自AndroidCameraRecordImpl,sdk8以上
AndroidCameraRecord9.java 继承自AndroidCameraRecordImpl,sdk9以上
AndroidCameraRecordManager.java 对外提供接口,对内使用以上类。
org.linphone.core
CallDirection.java 一通电话的方向,incoming后置outgoing
VideoSize.java 控制video的size,给BandwidthManager提供服务
=============
12.14 IP3088 音频 参数分析
[sound]
playback_dev_id=ANDROID SND: Android Sound card
ringer_dev_id=ANDROID SND: Android Sound card
capture_dev_id=ANDROID SND: Android Sound card
remote_ring=/data/data/org.linphone/files/ringback.wav
local_ring=/data/data/org.linphone/files/oldphone_mono.wav
ec_tail_len=120
ec_framesize=128
el_type=mic
el_thres=0.05
el_force=100000
el_sustain=600
el_transmit_thres=1.7
ng_floorgain=0.01
- sound.el_speed 通过配置文件读取,配置文件中没有,取默认值为-1
sound.noisegate 通过配置文件读取,配置文件中没有,取默认值为0
sound.mic_gain 通过配置文件读取,配置文件中没有,取默认值为1
sound.ng_thres 通过配置文件读取,配置文件中没有,取默认值为0.05
sound.el_type 配置文件为mic,通过code判断,其值mic,1
sound.play_gain_level 没有找到
sound.argc 通过配置文件读取,配置文件中没有,取默认值为0
sound.ea 通过配置文件读取,配置文件中没有,取默认值为0
sound.ec echo cancel 是否打开,由jni向上提供接口
sound.latency 0
delay的值通过create thread 计算得知,得知后,会把delay的值写回到config文件中