Linphone 培训课件

作者: 83ef79c43e42 | 来源:发表于2017-06-12 09:13 被阅读628次

linphone是一款老牌的全平台的多人语音视频通话业务的软件(始自2001年),不仅支持视频和语音通话,还支持即时消息。

当然,重要的是:linphone是开源的,毕竟linphone的sip服务和媒体数据处理也大量使用开源框架;更重要的是,linphone官网提供免费的sip服务,这也意味着你不需要自己手动搭建sip服务即可享受voip业务,这对于有voip需求的前端来说无疑是最佳的选择。但是虽然开源,linphone的工程比较庞大,依赖项较多,编译的过程中容易出现问题。

那么下面,我将介绍自己linphone-iphone的安装历程。

获取Linphone源代码的途径

1.在CSDN上获取:(需要CSDN的2积分,只能在真机上运行)http://download.csdn.net/detail/showhilllee/8688073
2.在Github上获取:
https://github.com/BelledonneCommunications/linphone-iphone

之后,我把代码都上传到自己的Github上,亲测能跑,欢迎下载。
https://github.com/chencanfeng/CHLinphoneForCSDN
https://github.com/chencanfeng/CHLinphoneForGitHub

在GitHub下载linphone-iphone项目,并进行编译

1.linphone-iphone的下载:

这里要用git clone命令下载,主要是比直接download下载的多了一个git管理的仓库,命令行输入:

git clone https://github.com/BelledonneCommunications/linphone-iphone.git

2.linphone-iphone的编译

按照上面的步骤:

(1)为了支撑脚本顺利运行,我们需要安装 HomeBrew



①根据官网提示在终端输入安装命令:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

②装上wget

 brew install wget

完成。

(2)安装好环境之后进入linphone目录内:cd linphone-iphone/

运行下面的命令来安装linphone-iphone的依赖项:

./prepare.py

如无例外,会报如下错误

ERROR:root:Could not find prepare module: 'module' object has no attribute 'Target', probably missing submodules/cmake-builder? Try running:
git submodule sync && git submodule update --init --recursive

原因是执行python脚本缺少模块,安照他给的提示执行

git submodule sync && git submodule update --init --recursive

注:git submodule sync && git submodule update --init --recursive 这句要卡很久,大概花了一天一夜的时间,醒来就已经执行完了。

接着再执行

 ./prepare.py

(3)配置环境路径

执行命令 export PATH=/usr/local/bin:$PATH

(4)Build SDK

执行命令 ./prepare.py -c && ./prepare.py && make

这里又会报错:


提示我们要安装java JDK,那就去装一下好了

①安装Java JDK

到官网下载MAC版的java jdk然后安装 :

http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

②根据提示运行组件,执行如下命令

brew install doxygen nasm intltool yasm automake coreutils optipng autoconf pkg-config

完成这些准备工作后,可以开始重头戏编译了,执行如下的make命令

./prepare.py -c && ./prepare.py && make

然后进入等待中。。。大概20分钟左右就可以编译好。

make完之后就可以看到子目录下多出了一个liblinphone-sdk文件夹,里面是编译好的liblinphone在各个平台下的静态库。

如果缺少文件就要重新执行一次make命令。

完成,打开linphone.xcodepro,工程文件夹中自带了企业推送证书,可以实现推送,连接真机后,按⌘R就可以享受了。

主要了解通话业务相关的代码

1.注册与使用


选择CREAT ACCOUNT进行帐号的注册,注册完成即可享受免费的sip服务,进入注册页面后,建议用户名最好填写电话号码或者纯数字帐号,因为sip地址是根据用户名来定义的,而且拨号盘只能输入数字。注册后在邮箱收到邮件并点击链接验证后帐号注册完成,可以向另一台注册了帐号的设备发送voip通话了。

2.与工程相关的类全部放在Classes中,LinphoneUI中存放自定义的各种控件,Utils中存放用到的工具类和第三方视图类,XMLRPC是远程过程调用的一个ios框架,工程中几乎所有的UIViewController都继承自TPMultiLayoutViewController,是用来解决同时适用横向和竖向的问题。

3.跟通话相关的类型主要有:

  • CallIncomingView:电话打入的视图;
  • CallOutgoingView:电话打出的视图;
  • CallView:电话接通后的视图、包括语音聊天、视频聊天、会议电话的视图;
  • PhoneMainView: 电话视图的容器,单例模式;监听大部分的通话状态变化,负责管理不同通话视图间的跳转和主要业务逻辑;
  • LinphoneManager:对liblinphone中各种状态的封装以及功能的补充(音频播放、音频设备设置,音频设备占用处理,低电量,新消息处理,前后台处理),中间件。

4.代码分析

UI:

与通话相关的页面一共有3个:CallIncomingView、CallOutgoingView、CallView,分别为打入电话、呼出电话与通话期间的视图。没有特别难懂的地方,用通知来进行UI更新。
而"PhoneMainView"则比较特殊,该单例不仅起到业务层的作用,而且是上面3个页面的容器,通过它来进行通话页面之间的切换和管理。

业务,位于linphoneManager中:
  • 基本业务:
    1.拨号
    判断网络状况
    检查是否在GSM通话中
    检测提供的addr是否合法
    2g网络下是否使用low_bandwidth模式
    linphone_core_invite_address_with_params
    2.接听
    2g网络是否是同low_bandwidth模式
    linphone_call_params_enable_video 是否允许视频流
    linphone_core_accept_call_with_params 接电话

  • 状态监听:
    voip通话状态改变的监听是通过通知kLinphoneCallUpdate下发的,注册该通知后,就可以从userInfo中的keycall和state中分别拿到当前通话的实例以及通话状态码。

那么,该通知又是怎么来的呢,看了voipManager.m便知,在linphonecore.h文件中有一个void (*LinphoneCoreCallStateChangedCb)(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message)类型的blockcall_state_changed,该回调会在通话状态改变时触发,所以将方法static void linphone_iphone_call_state(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState state, const char *message)赋值给了该blcok,在该方法中会对各种state进行处理,最后将state、call、message封装到dict后发送通知。

  • 音频设备相关
    需要处理两个问题:
    1.音频设备占用,这里又分为GSM电话和普通音频应用的占用;
    2.音频设备监测,主要针对蓝牙设备的接入。
  • 前台与后台
    为了保证应用进入后台也能长时间通话,做了如下处理:
    1.触发applicationDidEnterBackground时:enterBackgroundMode
    (1)refreshRegisters
    (2)设置超时处理[[UIApplication sharedApplication] setKeepAliveTimeout: 600 handler:^{ linphone_core_iterate }
    (3)有通话则延长后台存活时间startCallPausedLongRunningTask
    (4)[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler
    (5)停止视频预览 linphone_core_stop_dtmf_stream
    (6)如果不进入后台模式,destroy voip socket if any
    2.触发applicationDidBecomeActive时:调用becomeActive
    (1)refreshRegisters
    (2)停止后台任务pausedCallBgTask和incallBgTask
    (3)linphone_core_start_dtmf_stream

其他部分就不一一列举了,说的不够清楚,建议如果感兴趣的话自己实际编译调试一遍,从而对linphone的ios端接口有更深的了解。

如何将Linphone移植到自己的项目

在网上关于Linphone移植的资料并不多,当时从GitHub下载的项目怎么也编译不成功,花了两三天的时间,最终选择CSDN上的案例进行移植,小编是看了下面的两篇文章慢慢摸索,最终移植成功了。

好了,接下来开始编写demo

1.新建工程,拷贝CSDN案例中的liblinphone-sdk到新项目,导入lib文件夹中所有的依赖库,如图所示:


2.这时编译会报很多错,导入如下图的依赖库,一个不漏喔 ~


3.Build Settings 设置 Header Search Paths 、 Library Search Paths、Other Linker Flags

4.在lnfo.plist文件,设置http请求

从iOS9开始,苹果建议所有的网络请求都要使用https,如果还想保留原有的http请求方式,开发者需要修改配置文件。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

5.在info.plist 文件,设置可以在后台运行

一般的iOS程序进入后台后会被系统挂起,就会停止执行,不能执行任何操作

从iOS4开始,苹果增加了特性,很好的支持了语音通话功能:
苹果支持应用可以在后台播放和录制声音;
苹果支持网络托管,保证应用在后台时,还能保持网络连接,能接收到来电;
应用可以设置一个超时处理,程序在后台运行时,周期性地唤醒应用,保证客户端和服务器有长连接,使网络不断开。
SDK封装了这些特性,保证了在iOS平台上,有很好的语音通话体验。
开发者需要修改配置文件,这样iOS工程才能支持这些特性。
<key>UIBackgroundModes</key>
<array>
    <string>audio</string>
    <string>voip</string>
</array>

6、编译
如果出现了形如“file not found”的错误提示,检查头文件、静态库是否关联成功

目前为止,这个demo已经配置好了Linphone的开发环境。这里使用了CSDN案例中的几个类文件,包括:
LinphoneManager.[h,m],FastAddressBook.[h,m],Utils.[h.m]
注:非 ARC 模式的代码文件需要加入 -fno-objc-arc 标签。

事不宜迟,直接上代码,~

1.在AppDelegate.m初始化Linphone

[[LinphoneManager instance] startLibLinphone];

2.使用sip账号登陆Linphone,配置账号信息

云会议项目获取sip账号的接口:
/bjtel/RegisterVoipUser.mt
参数:voipusername = [UIDevice currentDevice].imei

- (BOOL)addProxyConfig:(NSString*)username password:(NSString*)password displayName:(NSString *)displayName domain:(NSString*)domain port:(NSString *)port withTransport:(NSString*)transport {
    LinphoneCore* lc = [LinphoneManager getLc];
    
    if (lc == nil) {
        [[LinphoneManager instance] startLibLinphone];
        lc = [LinphoneManager getLc];
    }
    
    LinphoneProxyConfig* proxyCfg = linphone_core_create_proxy_config(lc);
    NSString* server_address = domain;
    
    char normalizedUserName[256];
    linphone_proxy_config_normalize_number(proxyCfg, [username cStringUsingEncoding:[NSString defaultCStringEncoding]], normalizedUserName, sizeof(normalizedUserName));

    const char *identity = [[NSString stringWithFormat:@"sip:%@@%@", username, domain] cStringUsingEncoding:NSUTF8StringEncoding];
    
    LinphoneAddress* linphoneAddress = linphone_address_new(identity);
    linphone_address_set_username(linphoneAddress, normalizedUserName);
    if (displayName && displayName.length != 0) {
        linphone_address_set_display_name(linphoneAddress, (displayName.length ? displayName.UTF8String : NULL));
    }
    if( domain && [domain length] != 0) {
        if( transport != nil ){
            server_address = [NSString stringWithFormat:@"%@:%@;transport=%@", server_address, port, [transport lowercaseString]];
        }
        // when the domain is specified (for external login), take it as the server address
        linphone_proxy_config_set_server_addr(proxyCfg, [server_address UTF8String]);
        linphone_address_set_domain(linphoneAddress, [domain UTF8String]);
        
    }
    
    // 添加了昵称后的identity
    identity = linphone_address_as_string(linphoneAddress);
    
    linphone_address_destroy(linphoneAddress);
    
    LinphoneAuthInfo* info = linphone_auth_info_new([username UTF8String]
                                                    , NULL, [password UTF8String]
                                                    , NULL
                                                    , linphone_proxy_config_get_realm(proxyCfg)
                                                    ,linphone_proxy_config_get_domain(proxyCfg));
    
    [self setDefaultSettings:proxyCfg];
    
    [self clearProxyConfig];
    
    linphone_proxy_config_set_identity(proxyCfg, identity);
    linphone_proxy_config_set_expires(proxyCfg, 2000);
    linphone_proxy_config_enable_register(proxyCfg, true);
    linphone_core_add_auth_info(lc, info);
    linphone_core_add_proxy_config(lc, proxyCfg);
    linphone_core_set_default_proxy_config(lc, proxyCfg);
    ms_free(identity);

    return TRUE;
}


- (void)clearProxyConfig {
    linphone_core_clear_proxy_config([LinphoneManager getLc]);
    linphone_core_clear_all_auth_info([LinphoneManager getLc]);
}

- (void)setDefaultSettings:(LinphoneProxyConfig*)proxyCfg {
    LinphoneManager* lm = [LinphoneManager instance];
    
    [lm configurePushTokenForProxyConfig:proxyCfg];
    
}

3.监听名为 kLinphoneRegistrationUpdate 的通知,通过 linphone_proxy_config_get_state方法可获取到当前的状态,就可以知道有没有登陆成功。

#pragma mark -发送通知
- (void)onRegister:(LinphoneCore *)lc cfg:(LinphoneProxyConfig*) cfg state:(LinphoneRegistrationState) state message:(const char*) message;
#pragma mark -监听到通知,获取到当前的状态
- (void)registrationUpdate: (NSNotification*) notif {
    LinphoneProxyConfig* config = NULL;
    linphone_core_get_default_proxy([LinphoneManager getLc], &config);

    LinphoneRegistrationState state = LinphoneRegistrationNone;
    
    if (config == NULL) {
       state = LinphoneRegistrationNone;
    } else {
       state = linphone_proxy_config_get_state(config);
    }
...
}

4.监听名为 kLinphoneCallUpdate 的通知,可知道当前电话的状态,通过 state 的状态进行各种事件处理:

#pragma mark -发送通知
- (void)onCall:(LinphoneCall*)call StateChanged:(LinphoneCallState)state withMessage:(const char *)message;

#pragma mark -这里是监听通话结束时挂断电话
- (void)callUpdateEvent:(NSNotification *)notif {
    
    LinphoneCallState state = [[notif.userInfo objectForKey: @"state"] intValue];
    
    if (state == LinphoneCallError || state == LinphoneCallEnd) {
        [self processHangup];
        [self.navigationController popViewControllerAnimated:YES];
    }
}

5.开始拨打电话

(1)拨打电话/挂断电话

sip的电话需要拼接一串前缀,
云会议项目拨打的前缀是 8319*12829565* + 国际区号 + 手机号码

#pragma mark -点击按钮拨打电话
- (void)processMakeCall {
    NSString *address = [self.callNumber copy];
    if(![address containsString:@"8319*12829565"]) {
        address = [NSString stringWithFormat:@"8319*12829565*%@",self.callNumber];
    }
    
    if( [address length] > 0){
        
        [[LinphoneManager instance] call:address recordPath:_contact.path transfer:FALSE];
    }
}
#pragma mark -点击按钮挂断电话
- (void)processHangup {
    LinphoneCore* lc = [LinphoneManager getLc];
    LinphoneCall* currentcall = linphone_core_get_current_call(lc);
    if(currentcall != NULL) { // In a call
        linphone_core_terminate_call(lc, currentcall);
    }
}
#pragma mark -监听通讯状态,当通话结束时挂断电话
- (void)callUpdateEvent:(NSNotification *)notif {
    
    LinphoneCallState state = [[notif.userInfo objectForKey: @"state"] intValue];
    
    if (state == LinphoneCallError || state == LinphoneCallEnd) {
        [self processHangup];
        [self.navigationController popViewControllerAnimated:YES];
    }
}

(2)打开/关闭扩音器 (扬声器)

#pragma mark -打开扬声器
[[LinphoneManager instance] setSpeakerEnabled:TRUE];
#pragma mark -关闭扬声器
[[LinphoneManager instance] setSpeakerEnabled:FALSE];

(3)打开/关闭麦克风(关闭时,对方听不到我方的声音)

#pragma mark -打开麦克风
linphone_core_mute_mic([LinphoneManager getLc], true);
#pragma mark -关闭麦克风
linphone_core_mute_mic([LinphoneManager getLc], false);

(4)设置录音保存的位置以及格式

linphone_call_params_set_record_file

这个函数时linPhone中对通话时的音频进行录音,并保存到本地的一个重要函数。其作用就是设置录音保存的位置以及格式等。注意在linPhoneCall还没有分配资源时就应该调用这个函数。

也就是说,在点击拨打号码创建LinphoneCallParams时,就应该调用

- (void)call:(NSString *)address recordPath:(NSString*)path transfer:(BOOL)transfer {

...

LinphoneProxyConfig* proxyCfg;
//get default proxy
linphone_core_get_default_proxy(theLinphoneCore,&proxyCfg);
LinphoneCallParams* lcallParams = linphone_core_create_default_call_parameters(theLinphoneCore);
    
//linPhone对通话时的音频进行录音,并保存到本地
[self initSetRcordWithParams:lcallParams recordPath:path];

...

}
- (void)initSetRcordWithParams:(LinphoneCallParams *)params recordPath:(NSString *)strPath{

    //NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    //NSString *strPath = [NSString stringWithFormat:@"%@/voip_recordfile_%@.wav", documentsDirectory,fullTime];

    //设置音频保存路径
    const char *path = [strPath UTF8String];
    
    //如果文件存在则删除
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if([fileManager fileExistsAtPath:strPath]) {
        [fileManager removeItemAtPath:strPath error:nil];
    }
    
    linphone_call_params_set_record_file(params,path);
}

(5)开始/结束录音

#pragma mark -打开录音
LinphoneCall* call = linphone_core_get_current_call([LinphoneManager getLc]);
linphone_call_start_recording(call);
#pragma mark -结束录音
LinphoneCall* call = linphone_core_get_current_call([LinphoneManager getLc]);
linphone_call_stop_recording(call);

录音是由第一次按开始和最后一次按结束来确定录音范围,同一个通话只生成一段录音,会自动衔接各段录音。完成录音之后,就可以在你保存录音的位置获取到录音文件,我是保存在真机NSDocumentDirectory文件下。

注意:录音为双向录音,被叫和主叫均被录音。结束录音前必须保证已经开始了,否则会crash。

到此为止,我们已经成功地将Linphone移植到项目中去,可以开心的使用了。

参考文档

Linphone的官网:http://www.linphone.org/
Linphone的说明文档:https://www.linphone.org/docs/liblinphone/modules.html
whiteking的文章:http://www.jianshu.com/p/ec5cfb72c9e7
江湖的文章:http://www.jianshu.com/p/2ba2d4aa168e
偶尔鸣叫的丹丹龙的文章:http://www.jianshu.com/p/845cd812fcd7
linphone-iphone的安装与调试: http://www.jianshu.com/p/a4a00264a8e8

相关文章

  • Linphone 培训课件

    linphone是一款老牌的全平台的多人语音视频通话业务的软件(始自2001年),不仅支持视频和语音通话,还支持即...

  • 快速移植Linphone到自己的项目

    首先我们来看Linphone的简单介绍: Linphone for smartphones, tablets an...

  • 做培训课件

    本来有前几天的培训课件,再一次看又觉得不完美。本来心里想着:偷个懒,培训时把以前的课件用上。 可是我心里...

  • 我就是我不一样的烟火

    最近在负责新员工的培训项目,公司有变更所以新员工的培训课件在不断的修改中,周末还在修改课件。完成课件后,早上在地铁...

  • #PPT365# 071 渐隐字

    在赶写培训课件,潦草打卡

  • 培训师课件设计---多维化038-100 170524

    培训师课件设计---多维化038-100 170524 成人学习的特点 我和同事一起讨论,做培训课件设计, ...

  • 12月工作总结和差异

    1、培训课件的开发(已经有3个课件) 2、培训规划(1月份培训已经规划完毕) 3、店面周围小区的研发(做了一套中冶...

  • Linphone-iPhone下载和编译sdk

    Linphone iPhone App demo 下载 下载完后会得到一个linphone-iphone的文件夹,...

  • Git内部培训课件

    Git内部培训课件 Git简介 什么是版本控制 版本控制系统(Version Control System,简称V...

  • 晨练导师培训课件

    今天早课殷建兵导师做了晨练导师培训课件的分享,让我很受益。他从以下几个点做了梳理。 (一)什么是晨练导师? (二)...

网友评论

    本文标题:Linphone 培训课件

    本文链接:https://www.haomeiwen.com/subject/zlakqxtx.html