首次写博客,写这博客一是为了后期需移植LinphoneSDK时做个参考,大脑很多东西是会忘记的;二是因为在国内网站上找了好久都没找到如何将LinphoneSDK移植到自己项目里面的资料,只希望这篇博客对后面有人需要使用Linphone实现网络通话功能的同鞋有点帮助,做个借鉴。
对于编译Linphone,各种博客已经一大堆,这里推荐一篇:http://www.successmonkey.co.nz/blog/building-linphone-for-ios,有人翻译了这篇:http://blog.csdn.net/showhilllee/article/details/42966305,可惜LZ克隆的Linphone源码一直不全,最后一步的设置SDK版本无法进行,LZ直接使用了Linphone官网中的SDK,对上面博客中的命令,有些命令无法进行时,尝试着命令最前面加个sudo;官网中SDK的下载地址为:http://www.linphone.org/releases/ios/liblinphone-iphone-sdk-latest.zip。
另外,LZ从CSDN网站中下载了一个别人编译好的demo(以下称为第三方demo),不过这demo编译的有点时间了的感觉,关于里面显示为Linphone Core 3.7.0,LZ记得最新的已经到3.8.5了,不过LZ使用Xcode6.3能够跑通,不会报错,需使用的账号需到Linphone官网申请,下载地址为:http://download.csdn.net/detail/showhilllee/8688073,需要2个积分,没积分同鞋可以找我要份,哈哈;有同鞋编译成功最新的也可以发一份给我,先谢过。
好了,接下来进行编写demo,LZ使用的是Xcode7.1,手机使用iPhone5S(iOS8.3系统)和iPhone6(iOS9.1系统)测试均能拨打接听电话。
一、新建工程,导入LiphoneSDK
这时进行编译会报错,这边列出LZ出现的问题及解决办法:
1、提示<libxml/tree.h> file not found
导入libxml2.tbd框架(Xcode7之前为libxml2.dylib),若还会出现此问题请参考博客:http://www.tuicool.com/articles/Nraau2
2、LinphoneSDK中出现例如"ortp/ortp.h" file not found
是因为路劲没有配对,LZ这边设置的路劲如下图:
二、新建PrefixHeader.pch文件,加入#import <linphone/linphonecore.h>
三、Build Settings -> Other Linker Flags 中增加 -ObjC
四、添加库和框架,列表如下:
- libz.tbd
- libiconv.tbd
- libsqlite3.tbd
- libc++.tbd
- libstdc++.6.tbd
- libresolv.tbd
- AudioToolbox.framework
- UIKit.framework
- QuartzCore.framework
- OpenGLES.framework
- MessageUI.framework
- MediaPlayer.framework
- CoreGraphics.framework
- MobileCoreServices.framework
- AddressBookUI.framework
- AddressBook.framework
- SystemConfiguration.framework
- CFNetwork.framework
- AssetsLibrary.framework
- AVFoundation.framework
- CoreAudio.framework
- CoreMedia.framework
- CoreTelephony.framework
- CoreVideo.framework
- Foundation.framework
- CoreLocation.framework
LinphoneManager.[h,m]
FastAddressBook.[h,m]
Utils.[h.m]
UILinphone.[h.m]
ColorSpaceUtilities.[h.m]
六、注册部分,Linphone需先和后台连接成功,才能进行网络通话,LZ这后台的客服系统是购买的第三方系统,只有IP和端口,使用第三方demo,用在官网中申请到的账号(两个账号,两台手机各登录一个账号),此时可以连接成功,并能进行通话;但当User name、Password、Domain修改为我们自己注册好的账户进行登录时则无法登录成功,此处LZ纠结了好久,经过打断点各种修改比对测试,最终成功连接,修改部分如下代码:
- (BOOL)addProxyConfig:(NSString*)username password:(NSString*)password domain:(NSString*)domain withTransport:(NSString*)transport {
LinphoneCore* 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 = linphone_proxy_config_get_identity(proxyCfg);
// if( !identity || !*identity ) identity = "sip:user@example.com";
// 修改部分 1
const char *identity = [@"sip:User name@IP:端口" cStringUsingEncoding:NSUTF8StringEncoding];
LinphoneAddress* linphoneAddress = linphone_address_new(identity);
linphone_address_set_username(linphoneAddress, normalizedUserName);
if( domain && [domain length] != 0) {
if( transport != nil ){
// 修改部分 2
server_address = [NSString stringWithFormat:@"%@:%@;transport=%@", server_address, @"端口", [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]);
}
char* extractedAddres = linphone_address_as_string_uri_only(linphoneAddress);
LinphoneAddress* parsedAddress = linphone_address_new(extractedAddres);
ms_free(extractedAddres); // 释放
if( parsedAddress == NULL || !linphone_address_is_sip(parsedAddress) ){
if( parsedAddress ) linphone_address_destroy(parsedAddress);
UIAlertView* errorView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Check error(s)",nil)
message:NSLocalizedString(@"Please enter a valid username", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"Continue",nil)
otherButtonTitles:nil,nil];
[errorView show];
return FALSE;
}
char *c_parsedAddress = linphone_address_as_string_uri_only(parsedAddress);
linphone_proxy_config_set_identity(proxyCfg, c_parsedAddress);
linphone_address_destroy(parsedAddress);
ms_free(c_parsedAddress);
LinphoneAuthInfo* info = linphone_auth_info_new([username UTF8String]
, NULL, [password UTF8String]
, NULL
, NULL
,linphone_proxy_config_get_domain(proxyCfg));
[self setDefaultSettings:proxyCfg];
[self clearProxyConfig];
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);
return TRUE;
}
注:
1、因涉及安全问题,为提供IP和端口详细信息;
2、修改部分为上面代码中的1、2部分,需说明一点的是LZ传入的Domain只有IP,无端口,transport使用的为UDP(使用TCP、TLS都无法成功连接,LZ也不知为什么,若有大侠知道还望告知),LZ传入的参数在代码中写死,如下:
NSString *username =@"User name";
NSString *password = @"Password";
NSString *domain = @"IP";
NSString *transport = @"UDP";
3、此方法在第三方demo中的WizardViewController.m文件中可找到;
七、如何知道有没有登录成功?可监听名为 kLinphoneRegistrationUpdate 的通知,通过 linphone_proxy_config_get_state方法可获取到当前的状态。
八、通过监听名为 kLinphoneCallUpdate 的通知,可知道当前电话的状态,如下代码,可通过 astate 的状态进行各种事件处理:
- (void)callUpdateEvent:(NSNotification*)notif {
LinphoneCall *acall = [[notif.userInfoobjectForKey: @"call"]pointerValue];
LinphoneCallState astate = [[notif.userInfoobjectForKey: @"state"]intValue];
[selfcallUpdate:acall state:astate];
}
九、其他
1、当来电时,不接通电话,会在二十几时自动挂断,在实际使用场合中此时间会较短,可通过如下方法进行设置(安卓中的方法为setIncomingTimeout):
LinphoneCore *lc=[LinphoneManagergetLc];
linphone_core_set_inc_timeout(lc,60);// 来电超时自动挂断时间设置
2、来电时无铃声,到后来才发现竟然是没有音频文件,着实让LZ OUT了一把,音频文件在第三方demo中有,为mag.wav文件,LZ将.wav和.caf文件都加入了工程中,高兴了一把,有声音了;
3、回声抑制,有方法进行设置,在查看linphonecore.h文件时有看到过,LZ暂未去研究;
4、因LZ暂时只需先实现通话功能,对于视频通话这块还未进行研究,有研究过的同鞋可分享你们的经验,若后期有进行视频通话这块的研究,到时再跟新此博客;
5、因LZ手中没有iOS7的设备,故在iOS7中未进行测试,iOS8、9均可实现通话功能。
备注:1、感谢上面所使用到的博客的博主,若有地址无法打开,则为国外网站,需翻墙,LZ以购买VPN,上面地址均能打开;
2、若有在研究Linphone的同鞋,碰到问题可咨询LZ,LZ邮箱为rongfeng.fang@icloud.com,QQ群:345752242,若你发现博客中有问题存在,也烦请告知LZ,以便及时修改;