1,Linphone简介
1.1 简介
LinPhone是一个遵循GPL协定的开源网络电话或者IP语音电话(VOIP)零碎,其次要如下。应用linphone,开发者能够在互联网上随便的通信,包含语音、视频、即时文本音讯。linphone应用SIP协定,是一个规范的开源网络电话零碎,能将linphone与任何基于SIP的VoIP运营商连接起来,包含咱们本人开发的收费的基于SIP的Audio/Video服务器。
LinPhone是一款自由软件(或者开源软件),你能够随便的下载和在LinPhone的根底上二次开发。LinPhone是可用于Linux, Windows, MacOSX 桌面电脑以及Android, iPhone, Blackberry挪动设施。
学习LinPhone的源码,开源从以下几个局部着手:
Java层框架实现的SIP三层协定架构: 传输层,事务层,语法编解码层;
linphone动静库C源码实现的SIP性能: 注册,申请,申请超时,邀请会话,挂断电话,邀请视频,收发短信...
linphone动静库C源码实现的音视频编解码性能;
Android平台上的音视频捕捉,播放性能;
1.2 根本应用
如果是Android零碎用户,能够从谷歌利用商店装置或者从这个链接下载Linphone 。装置实现后,点击左上角的菜单按钮,抉择进入助手界面。在助手界面,能够设定SIP账户或者Linphone账号,如下图:
对于咱们来说,就是设置SIP账户,须要填入几个参数:
- 用户名:就是SIP账户号码或名称。
- 明码:该SIP账户对应的明码。
- 域名:填写SIP服务器(IPPBX)的IP地址或域名。
- 显示名:该SIP账户的显示名,是可选的。
- 传输:该SIP服务器反对传输协定,个别是UDP,也能够依据须要抉择TCP或者TLS。
注册胜利之后呢,软电话APP会有提示信息,左上角显示连贯状态,如下图。
而后,输出对方的SIP账户,就能够通话了,如下图。
1.3 相干文档
上面是Linphone开发可能会用到的一些材料:
- Linphone官网 :http://www.linphone.org/technical-corner/liblinphone
- 官网文档:https://wiki.linphone.org/xwiki/wiki/public/view/Lib/Getting%20started/Android/
- 官网Android Demo:https://gitlab.linphone.org/BC/public/linphone-android
- 各个版本的aar库:https://linphone.org/releases/maven_repository/org/linphone/linphone-sdk-android/
2,疾速上手
2.1 编译App
首先,应用 Android Studio关上我的项目,而后构建/装置应用程序即可,可能编译过程中会比较慢。当然,也能够应用命令形式进行编译:
./gradlew assembleDebug//或者./gradlew installDebug
2.2 编译SDK
在Android利用程序开发中,引入第三方库的形式有源码依赖和sdk依赖。当然,咱们也能够把sdk的代码下载下来,而后执行本地编译。
git clone https://gitlab.linphone.org/BC/public/linphone-sdk.git --recursive
而后装置官网文档的阐明编译sdk。
2.3 集成Linphone
首先,须要引入linphone依赖,能够间接下载aar包执行本地以来,也能够应用gradle形式引入。此处,咱们应用他人曾经编译好的sdk:
dependencies { //linphone debugImplementation "org.linphone:linphone-sdk-android-debug:5.0.0" releaseImplementation "org.linphone:linphone-sdk-android:5.0.0"}
CoreManager
为了不便调用,咱们须要对Linphone进行简略的封装。首先,依照官网文档的介绍,创立一个CoreManager类,此类是sdk外面的治理类,用来管制复电铃声和启动CoreService,无非凡需要不需调用。须要留神的是,启动复电铃声须要导入media包,否则不会有复电铃声,如下:
implementation 'androidx.media:media:1.2.0'
而后,咱们新建一个LinphoneManager类用来治理Linphone sdk,比方将Linphone注册到服务器、拨打语音电话等。
class LinphoneManager private constructor(private val context: Context) { ... //省略其余代码 /** * 注册到服务器 * * @param username 账号名 * @param password 明码 * @param domain IP地址:端口号 */ fun createProxyConfig( username: String, password: String, domain: String, type: TransportType? = TransportType.Udp ) { core.clearProxyConfig() val accountCreator = core.createAccountCreator(corePreferences.xmlRpcServerUrl) accountCreator.language = Locale.getDefault().language accountCreator.reset() accountCreator.username = username accountCreator.password = password accountCreator.domain = domain accountCreator.displayName = username accountCreator.transport = type accountCreator.createProxyConfig() } /** * 勾销注册 */ fun removeInvalidProxyConfig() { core.clearProxyConfig() } /** * 拨打电话 * @param to String * @param isVideoCall Boolean */ fun startCall(to: String, isVideoCall: Boolean) { try { val addressToCall = core.interpretUrl(to) addressToCall?.displayName = to val params = core.createCallParams(null) //启用通话录音// params?.recordFile = LinphoneUtils.getRecordingFilePathForAddress(context, addressToCall!!) //启动低宽带模式 if (LinphoneUtils.checkIfNetworkHasLowBandwidth(context)) { Log.w(TAG, "[Context] Enabling low bandwidth mode!") params?.enableLowBandwidth(true) } if (isVideoCall) { params?.enableVideo(true) core.enableVideoCapture(true) core.enableVideoDisplay(true) } else { params?.enableVideo(false) } if (params != null) { core.inviteAddressWithParams(addressToCall!!, params) } else { core.inviteAddress(addressToCall!!) } } catch (e: Exception) { e.printStackTrace() } } ... //省略其余代码}
CoreService
接下来就是CoreService类,该类的作用是一个保活服务,在复电时会调用触动办法和启动告诉,所以必须在AndroidManifest.xml里注册。
<service android:name="org.linphone.core.tools.service.CoreService" android:foregroundServiceType="phoneCall|camera|microphone" android:label="@string/app_name" android:stopWithTask="false" />
官网Demo那样继承CoreService而后本人实现 。
class CoreService : CoreService() { override fun onCreate() { super.onCreate() Log.i("[Service] Created") } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.i("[Service] Ensuring Core exists") if (corePreferences.keepServiceAlive) { Log.i("[Service] Starting as foreground to keep app alive in background") if (!ensureCoreExists(applicationContext, pushReceived = false, service = this, useAutoStartDescription = false)) { coreContext.notificationsManager.startForeground(this, false) } } else if (intent?.extras?.get("StartForeground") == true) { Log.i("[Service] Starting as foreground due to device boot or app update") if (!ensureCoreExists(applicationContext, pushReceived = false, service = this, useAutoStartDescription = true)) { coreContext.notificationsManager.startForeground(this, true) } coreContext.checkIfForegroundServiceNotificationCanBeRemovedAfterDelay(5000) } return super.onStartCommand(intent, flags, startId) } override fun createServiceNotificationChannel() { // Done elsewhere } override fun showForegroundServiceNotification() { Log.i("[Service] Starting service as foreground") coreContext.notificationsManager.startCallForeground(this) } override fun hideForegroundServiceNotification() { Log.i("[Service] Stopping service as foreground") coreContext.notificationsManager.stopCallForeground() } override fun onTaskRemoved(rootIntent: Intent?) { if (!corePreferences.keepServiceAlive) { if (coreContext.core.isInBackground) { Log.i("[Service] Task removed, stopping Core") coreContext.stop() } else { Log.w("[Service] Task removed but Core in not in background, skipping") } } else { Log.i("[Service] Task removed but we were asked to keep the service alive, so doing nothing") } super.onTaskRemoved(rootIntent) } override fun onDestroy() { if (LinphoneApplication.contextExists()) { Log.i("[Service] Stopping") coreContext.notificationsManager.serviceDestroyed() } super.onDestroy() }}
3,其余优化
对于局部设施可能存在啸叫、乐音的问题,能够批改assets/linphone_factory 文件下的语音参数,默认曾经配置了一些,如果不能满足你的要求,能够增加上面的一些参数。
回声打消
- echocancellation=1:回声打消这个必须=1,否则会听到本人谈话的声音
- ec_tail_len= 100:尾长示意回声时长,越长须要cpu解决能力越强
- ec_delay=0:延时,示意回声从话筒到扬声器工夫,默认不写
- ec_framesize=128:采样数,必定是刚好一个采样周期最好,默认不写
回声克制
- echolimiter=0:等于0时不散会有空洞的声音,倡议不开
- el_type=mic:这个选full 和 mic 示意克制哪个设施
- eq_location=hp:这个示意均衡器用在哪个设施
- speaker_agc_enabled=0:这个示意是否启用扬声器增益
- el_thres=0.001:零碎响应的阈值 意思在哪个阈值以上零碎有响应解决
- el_force=600 :管制收音范畴 值越大收音越广,意思是否收到很远的背景音
- el_sustain=50:管制发声到缄默工夫,用于管制声音是否拉长,意思说完一个字是否被拉长丢包时心愿拉长防止断断续续
降噪
- noisegate=1 :这个示意开启降乐音,不散会有背景音
- ng_thres=0.03:这个示意声音这个阈值以上都能够通过,用于判断哪些是乐音
- ng_floorgain=0.03:这个示意低于阈值的声音进行增益,用于弥补声音太小被吃掉
网络抖动延时丢包
- audio_jitt_comp=160:这个参数用于抖动解决,值越大解决抖动越好,但声音延时较大 理论值是80依据理论调整160
- nortp_timeout=20:这个参数用于丢包解决,值越小丢包越快声音不会断很长时间,同时要跟el_sustain配合声音才好听
源码参考:
https://github.com/MattLjp/LinphoneCall