当前位置:   article > 正文

Android 使用Linphone SDK开发SIP客户端

linphone sdk

平台

RK3288 + Linphone 5.1.0 + Android Studio

概述

       简单来说, 有了解过互联网电话服务或IM(即时消息)功能的.一般都会接触到VOIP和SIP, 实现即时通讯, 发文本消息也好话音通话也好, 甚至于视频通话.
关于SIP(Session Initiation Protocol,会话初始协议)
       VoIP是一个广义术语,可用于描述任何互联网电话服务,从低成本的住宅服务到企业统一通信工具的复杂实现。VoIP是一个可以用来描述任何基于Internet的电话服务的术语,而SIP是一种用于大多数类型VoIP部署的通信协议。

       在早期开发androidSIP 客户端的时候, 常常可以看到sipdroid的身影, 在前面的文章中已经有提及过并使用测试过, 只是这个项目目前来看, 只能用于做做DEMO, 简单的测试一些功能, 如注册, 登陆, 发文本消息之类的, 项目的推进/更新也不积极,
|-- 尝试过另外两个项目:
   |–csipsimple: 不好用/不会用
   |–abto_sip: 可测试用, 某些平台崩溃,官方付费

最终采用了Linphone
       自 2001 年作为第一个在 Linux 上使用 SIP 的开源应用程序推出以来,Linphone 已经变得非常流行,尤其是在开源社区中。 我们的工程团队一直致力于 Linphone 项目,以支持最流行平台的最新版本,并提供高级语音/视频和即时消息功能。

linphone-android客户端源码
linphone-SDK

使用Linphone-sdk打造一个SIP客户端
Gradle 版本

linphone-SDK下载对应SDK并添加依赖
当前使用的版本是 linphone-sdk-android-5.1.0-beta.aar

build.gradle

  1. dependencies {
  2.     implementation files('libs/linphone-sdk-android-5.1.0-beta.aar')
  3. }

SipPhone.java : 初始化

  1. import org.linphone.core.Account;
  2. import org.linphone.core.AuthInfo;
  3. import org.linphone.core.Call;
  4. import org.linphone.core.CallParams;
  5. import org.linphone.core.Config;
  6. import org.linphone.core.Core;
  7. import org.linphone.core.CoreListener;
  8. import org.linphone.core.CoreListenerStub;
  9. import org.linphone.core.Factory;
  10. public class SipPhone extends IPhone {
  11.     Factory factory;
  12.     Core core;
  13.     AuthInfo user;
  14.     AccountParams accountParams;
  15.     Call currentCall;
  16.     //初始化Factory, 在APP启动时调用.
  17.     public static void loadSipLibs(){
  18.         Factory.instance();
  19.     }
  20.     
  21.     void initSip(Activity activity){
  22.         Logger.i(TAG, "initSip");
  23.         factory = Factory.instance();
  24.         core = factory.createCore(null, null, activity);
  25.         core.addListener(coreListener);
  26.         //配置视频通话
  27.         core.enableVideoCapture(true);
  28.         core.enableVideoDisplay(true);
  29.         core.getVideoActivationPolicy().setAutomaticallyAccept(true);
  30.         //音频部分, 这里增加了一个遍历, 用于设置指定的音频格式.
  31.         //h264, no VP8 fixed outgoing call no video.
  32.         PayloadType[] payloads = core.getVideoPayloadTypes();
  33.         for(int i = 0; i < payloads.length; i ++){
  34.             //Payload:null, VP8/90000/0, A VP8 video encoder using libvpx library., VP8
  35.             //Payload:profile-level-id=42801F, H264/90000/0, A H264 encoder based on MediaCodec API., H264
  36.             PayloadType pt = payloads[i];
  37.             //判断是否指定的音频格式.
  38.             boolean goodPayload = PREFER_PAYLOAD.equals(pt.getMimeType());
  39.             pt.enable(goodPayload);
  40.         }
  41.         //https://github.com/BelledonneCommunications/linphone-android/issues/1153
  42.         //https://blog.csdn.net/AdrianAndroid/article/details/70048040
  43.         //do not working
  44.         //H264Helper.setH264Mode(H264Helper.MODE_AUTO, core);
  45.         //回声消除, 与音频增益.
  46.         //Logger.d(TAG, "initSip Cancellation=" + core.echoCancellationEnabled());
  47.         Logger.d(TAG, "initSip getMicGainDb=" + core.getMicGainDb());
  48.         Logger.d(TAG, "initSip PlaybackGainDb=" + core.getPlaybackGainDb());
  49.         //core.enableEchoCancellation(true);
  50.         Logger.d(TAG, "initSip finish Cancellation=" + core.echoCancellationEnabled());
  51.     }
  52. }

SipPhone.java : 登陆

  1.     void login(){
  2.         i("login");
  3.         String username = PreferenceUtils.getStringFromDefault(App.getApp(), App.PREF_VOIP_USER, "");
  4.         String password = PreferenceUtils.getStringFromDefault(App.getApp(), App.PREF_VOIP_PWD, "");
  5.         String domain = PreferenceUtils.getStringFromDefault(App.getApp(), App.PREF_VOIP_IP, "");
  6.         String port = PreferenceUtils.getStringFromDefault(App.getApp(), App.PREF_VOIP_PORT, App.DEF_SIP_PORT);
  7.         if(!StringTools.isNotEmpty(username, password, domain, port)){
  8.             e("login failed: username(" + username + "), password(" + password + "), domain(" + domain + "), port(" + port + ")");
  9.             return;
  10.         }
  11.         //sip:100@192.168.7.119:6060
  12.         if(!domain.contains(":")){
  13.             domain += ":" + port;
  14.         }
  15.         user = factory.createAuthInfo(username, null, password, null, null, domain, null);
  16.         accountParams = core.createAccountParams();
  17.         // A SIP account is identified by an identity address that we can construct from the username and domain
  18.         String sipAddress = "sip:" + username + "@" + domain;
  19.         Address identity = factory.createAddress(sipAddress);
  20.         i("login for address " + sipAddress);
  21.         accountParams.setIdentityAddress(identity);
  22.         // We also need to configure where the proxy server is located
  23.         Address address = factory.createAddress("sip:" + domain);
  24.         // We use the Address object to easily set the transport protocol
  25.         address.setTransport(TransportType.Udp);
  26.         accountParams.setServerAddress(address);
  27.         // And we ensure the account will start the registration process
  28.         accountParams.setRegisterEnabled(true);
  29.         // Asks the CaptureTextureView to resize to match the captured video's size ratio
  30.         //core.getConfig().setBool("video", "auto_resize_preview_to_keep_ratio", true);
  31.         // Now that our AccountParams is configured, we can create the Account object
  32.         Account account = core.createAccount(accountParams);
  33.         //account.setCustomHeader("Header1", "Header2");
  34.         // Now let's add our objects to the Core
  35.         core.addAuthInfo(user);
  36.         core.addAccount(account);
  37.         // Also set the newly added account as default
  38.         core.setDefaultAccount(account);
  39.         core.setUserAgent("User", "Agent");
  40.         // Finally we need the Core to be started for the registration to happen (it could have been started before)
  41.         core.start();
  42.     }
  43.     
  44.     void logout(){
  45.         i("logout");
  46.         Account account = core.getDefaultAccount();
  47.         if(account != null) {
  48.             accountParams = account.getParams().clone();
  49.             accountParams.setRegisterEnabled(false);
  50.             account.setParams(accountParams);
  51.         }
  52.     }

SipPhone.java : 通话部分

  1. //拨打电话.
  2.     @Override
  3.     public void call(String number, boolean video) {
  4.         i("call " + number + " video(" + video + ")");
  5.         String domain = PreferenceUtils.getStringFromDefault(App.getApp(), App.PREF_VOIP_IP, "");
  6.         String port = PreferenceUtils.getStringFromDefault(App.getApp(), App.PREF_VOIP_PORT, App.DEF_SIP_PORT);
  7.         // As for everything we need to get the SIP URI of the remote and convert it to an Address
  8.         String remoteSipUri = "sip:" + toNumber + "@" + domain + ":" + port;
  9.         Address remoteAddress = factory.createAddress(remoteSipUri);
  10.         if(remoteAddress == null)return;
  11.         // If address parsing fails, we can't continue with outgoing call process
  12.         // We also need a CallParams object
  13.         // Create call params expects a Call object for incoming calls, but for outgoing we must use null safely
  14.         CallParams params = core.createCallParams(null);
  15.         // We can now configure it
  16.         // Here we ask for no encryption but we could ask for ZRTP/SRTP/DTLS
  17.         params.setMediaEncryption(MediaEncryption.None);
  18.         params.enableVideo(video);
  19.         //show preview before caling.
  20.         //core.enableVideoPreview(video);
  21.         // Finally we start the call
  22.         core.inviteAddressWithParams(remoteAddress, params);
  23.         //回声消除
  24.         // Call process can be followed in onCallStateChanged callback from core listener
  25.     }
  26. //挂断
  27.     @Override
  28.     public void hangup() {
  29.         i("hangup");
  30.         if (core.getCallsNb() == 0) return;
  31.         // If the call state isn't paused, we can get it using core.currentCall
  32.         Call call = core.getCurrentCall() != null ? core.getCurrentCall() : core.getCalls()[0];
  33.         if(call != null) {
  34.             // Terminating a call is quite simple
  35.             call.terminate();
  36.         }
  37.     }
  38. //接听/应答
  39.     @Override
  40.     public void answer() {
  41.         i("answer");
  42.         if(currentCall != null){
  43.             if(remoteHasVideo()) {
  44.                 enableCamera();
  45.                 currentCall.getParams().enableVideo(true);
  46.             }
  47.             currentCall.accept();
  48.         }
  49.     }

SipPhone.java : 监听和回调

  1. //在initSip中使用.
  2.     CoreListener coreListener = new CoreListenerStub(){
  3.         @Override
  4.         public void onCallStateChanged(Core core, Call call, Call.State state, String message) {
  5.             d("onCallStateChanged " + state);
  6.             currentCall = call;
  7.             if(state == Call.State.OutgoingProgress){
  8.                 //呼出
  9.             }else if(state == Call.State.IncomingReceived){
  10.                 //来电
  11.             }else if(state == Call.State.StreamsRunning){
  12.                 //通话中, 有音视频流.
  13.             }else if(state == Call.State.UpdatedByRemote){
  14.                 //通话变化, 有可能变成语音, 也有可能是带视频...
  15.             }else if(state == Call.State.Released){
  16.                 //挂电或结束通话
  17.             }else if(state == Call.State.Error){
  18.                 //出错.
  19.             }
  20.         }
  21.         @Override
  22.         public void onRegistrationStateChanged(Core core, ProxyConfig proxyConfig, RegistrationState state, String message) {
  23.             //message:
  24.             // case "io error": server offline.
  25.             //
  26.             i("onRegistrationStateChanged " + state + " with msg:" + message);
  27.             //((Button)findViewById(R.id.btLogin)).setText(state == RegistrationState.Ok ? "Logout":"Login");
  28.             if(state == RegistrationState.Ok) {
  29.                 //登陆成功
  30.             }else{
  31.                 //登出
  32.             }
  33.         }
  34.     };


关于视频部分:
如何设置视频显示的控件, 在通话呼起后可以调用这个函数.

  1.     public void setVideoView(View v1, View v2){
  2.         core.setNativePreviewWindowId(v1);
  3.         core.setNativeVideoWindowId(v2);
  4.     }

所有的功能接口, 请以参考源码及官方为主
强烈建议下载linphone-android客户端源码并编译运行, 学习如何更好地使用SDK开发自己需要的功能


配置文件

      在优化视频通话的过程中, 接触到关于初始化配置的问题. 很多资料显示, 可能通过配置方件的方式, 配置优化音频参数来优化通话效果:
Echo suppression does not work
Android音视频通话——Linphone开发笔记总结
2022-09-24-voice_communication_audio_codec.md

大致的方法是:

1.增加配置文件

 assets/linphone_factory或 assets/linphonerc_factory
 res/raw/linphone_factory 或 res/raw/linphonerc_factory

2.编写对应配置
  1. [sip]
  2. guess_hostname=1
  3. register_only_when_network_is_up=1
  4. auto_net_state_mon=1
  5. auto_answer_replacing_calls=1
  6. ping_with_options=0
  7. use_cpim=1
  8. zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_KYB512
  9. chat_messages_aggregation_delay=1000
  10. chat_messages_aggregation=1
  11. [sound]
  12. #remove this property for any application that is not Linphone public version itself
  13. ec_calibrator_cool_tones=1
  14. # 打开回声消除
  15. echocancellation=1
  16. # MIC 增益
  17. mic_gain_db=0.0
  18. # 回放增益
  19. playback_gain_db=0.0
  20. [video]
  21. displaytype=MSAndroidTextureDisplay
  22. auto_resize_preview_to_keep_ratio=1
  23. max_mosaic_size=vga
3.打包到程序中运行.

以上这些方法, 仅适用于linphone-android客户端源码, 针对基于SDK开发的话, 则需要在对应的地方加入载入配置文件的代码:

  1. //参考
  2. //-linphone-android/app/src/main/java/org/linphone/core/CorePreferences.kt
  3. //-linphone-android/app/src/main/java/org/linphone/LinphoneApplication.kt
  4. //在创建Core之前载入配置文件.
  5. Config config = factory.createConfigWithFactory(App.LINPHONE_CONFIG_DEF, App.LINPHONE_CONFIG_FAC);
  6. core = factory.createCoreWithConfig(config, App.getApp().getActivity());

参考

Ubuntu搭建简单SIP服务器并使用sipdroid测试
一文详解SIP 协议- xiaxueliang - 博客园
sipdroid

————————————————
原文链接:https://blog.csdn.net/ansondroider/article/details/127743946

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/128730
推荐阅读
相关标签
  

闽ICP备14008679号