我自己集成的Demo & SDK源碼路徑
開始前先安利下江湖哥簡書躏啰,當(dāng)初都是照著這一步步摸索的http://www.reibang.com/p/2ba2d4aa168e
更新ipv6
見文末
正文開始。
很多場景下忌栅,我們的App會(huì)使用到VOIP通話旋廷,雖然目前各大通訊公司都推出了各自的SDK赊舶,相比Linphone叹螟,它們對開發(fā)者更為友好(體現(xiàn)在有更及時(shí)的技術(shù)支持暂氯、豐富的開發(fā)文檔)罕袋,但有些時(shí)候改淑,迫于某些需求,也只有掏出Linphone SDK浴讯,硬著頭皮上了朵夏。而我是為了實(shí)現(xiàn)純SIP通話,以及客戶需求兰珍,所以進(jìn)行了對Linphone SDK的集成侍郭、開發(fā),本文的目的一為記錄以便日后翻閱掠河,二為了讓后來的開發(fā)者少跳一些坑亮元,白白耽誤時(shí)間。
首先我們要下載一個(gè)重要的Linphone Demo,需要CSDN的2積分唠摹,如果沒有可以留言問我要爆捞。只能在真機(jī)上運(yùn)行。
1. 準(zhǔn)備工作
1.1 添加依賴庫
為支持新版ipv6 SDK勾拉,需要再添加VideoToolBox.framework
1.2 相關(guān)配置
1.3 http請求設(shè)置
從iOS9開始煮甥,蘋果建議所有的網(wǎng)絡(luò)請求都要使用https盗温,如果還想保留原有的http請求方式,開發(fā)者需要修改配置文件(注:xcode7.0以下則不需要修改)成肘,在工程名的文件夾下面的Supporting Files文件夾中找到并且選擇(工程名)lnfo.pList在右邊出現(xiàn)的窗口中添加Key:NSAppTransportSecurity卖局,在下面添加項(xiàng):NSAllowsArbitraryLoads,設(shè)置Boolean值為YES双霍,這樣平臺(tái)SDK內(nèi)部工程才能支持http請求方式砚偶。
1.4 后臺(tái)運(yùn)行
一般的iOS程序進(jìn)入后臺(tái)后會(huì)被系統(tǒng)掛起,就會(huì)停止執(zhí)行洒闸,不能執(zhí)行任何操作染坯。
從iOS4開始,蘋果增加了特性丘逸,很好的支持了語音通話功能:
蘋果支持應(yīng)用可以在后臺(tái)播放和錄制聲音单鹿;
蘋果支持網(wǎng)絡(luò)托管,保證應(yīng)用在后臺(tái)時(shí)深纲,還能保持網(wǎng)絡(luò)連接仲锄,能接收到來電;
應(yīng)用可以設(shè)置一個(gè)超時(shí)處理湃鹊,程序在后臺(tái)運(yùn)行時(shí)昼窗,周期性地喚醒應(yīng)用,保證客戶端和服務(wù)器有長連接涛舍,使網(wǎng)絡(luò)不斷開。
SDK封裝了這些特性,保證了在iOS平臺(tái)上,有很好的語音通話體驗(yàn)吐葱。
開發(fā)者需要修改配置文件囚似,這樣iOS工程才能支持這些特性。
在工程名的文件夾下面的Supporting Files文件夾中找到并且選擇(工程名)lnfo.pList在右邊出現(xiàn)的窗口中添加Key: Required background modes百姓,在下面添加兩個(gè)項(xiàng):App plays audio和App provides Voice over IP services。(注:如果只是使用發(fā)起通話,并無接聽功能蛤奢,則不需要添加App provides Voice over IP services)
2. 導(dǎo)入Linphone SDK
上面工作做完后就可以開始導(dǎo)入Linphone SDK了。
2.1 首先再回到這個(gè)熟悉的頁面陶贼,點(diǎn)擊Add Other
2.2 找到下載好的sdk目錄啤贩,把所有.a庫都選擇,如下圖拜秧。
2.3 查看是否關(guān)聯(lián)
Build Settings搜索 Search Paths痹屹,在下列兩個(gè)選項(xiàng)下觀察是否已經(jīng)關(guān)聯(lián),如圖
順帶說一下這個(gè)libxml2枉氮,大多數(shù)時(shí)候是不需要的志衍,如果報(bào)一些莫名其妙的錯(cuò)誤不妨將其導(dǎo)入暖庄,且在Build Phases里添加相應(yīng)庫libxml2.tbd
2.4 編譯
如果出現(xiàn)了形如“file not found”的錯(cuò)誤提示,回到第三步楼肪,檢查頭文件培廓、靜態(tài)庫是否關(guān)聯(lián)成功
3. 功能實(shí)現(xiàn)
這一部分可以參考CSDN Linphone Demo(以下統(tǒng)稱為CDemo),也可以直接下載我在github代碼里的SDK代碼部分春叫,更為直接肩钠。
3.1 初始化
需要關(guān)注CSDN Demo里的主要功能類LinphoneManage.m
實(shí)現(xiàn)如下方法即可
/**
@author Jozo, 16-06-30 11:06:18
初始化
*/
- (void)startUCSphone {
[[LinphoneManager instance] startLibLinphone];
[UCSIPCCSDKLog saveDemoLogInfo:@"初始化成功" withDetail:nil];
}
3.2 注冊
注冊方法在CDemo的manager類里沒有直接的體現(xiàn),值得注意的是我標(biāo)注“三個(gè)大坑”的地方象缀,如果有需要用到displayName這個(gè)參數(shù)的同學(xué)一定要注意了蔬将,設(shè)置好displayName后一定要通過linphone_address_as_string(..)
方法再次獲取identity,然后再調(diào)用linphone_proxy_config_set_identity(proxyCfg, identity);
將新的identity設(shè)置到設(shè)置文件中央星,因?yàn)槲议_始沒理清這個(gè)proxyCfg的實(shí)現(xiàn)邏輯霞怀,始終沒注意到這一步,還有就是使用了另一個(gè)函數(shù)linphone_address_as_string_uri_only
的誤導(dǎo)莉给,是誤導(dǎo),后者獲取到的僅有“sip:”以及之后的那個(gè)字符串毙石,并不會(huì)將昵稱也帶進(jìn)去傳入proxyCfg,導(dǎo)致displayName一直設(shè)置不成功颓遏,掉坑三日終于在某個(gè)狂風(fēng)亂作的下午解決此bug徐矩,導(dǎo)致天氣都變得風(fēng)和日麗起來。
注:以下賬號(hào)叁幢、密碼滤灯、IP、端口需要自己注冊曼玩。
/**
@author Jozo, 16-06-30 11:06:13
登陸
@param username 用戶名
@param password 密碼
@param displayName 昵稱
@param domain 域名或IP
@param port 端口
@param transport 連接方式
*/
- (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) {
[self startUCSphone];
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);
[UCSIPCCSDKLog saveDemoLogInfo:@"登陸信息配置成功" withDetail:[NSString stringWithFormat:@"username:%@,\npassword:%@,\ndisplayName:%@\ndomain:%@,\nport:%@\ntransport:%@", username, password, displayName, domain, port, transport]];
return TRUE;
}
3.3 撥打電話
接著簡單的調(diào)用如下方法即可進(jìn)行SIP呼叫黍判。`
[[LinphoneManager instance] call:address displayName:displayName transfer:transfer];
值得注意的是豫尽,呼叫狀態(tài)、登陸狀態(tài)的回調(diào)都是通過通知來傳遞的顷帖,所以要想獲取這些回調(diào)美旧,可以監(jiān)聽這些回調(diào),具體通知名在LinphoneManager.h里贬墩,形如“kLinphoneCallUpdate”榴嗅,也可以像我一樣,在LinphoneManager.m的各個(gè)回調(diào)處震糖,將相應(yīng)的通知改為自己想要設(shè)置的代理方法录肯,并設(shè)置好代理,可以看我在Github Demo里的LinphoneManager.m處如下方法里的實(shí)現(xiàn)吊说。
- (void)onRegister:(LinphoneCore *)lc cfg:(LinphoneProxyConfig*) cfg state:(LinphoneRegistrationState) state message:(const char*) message;
- (void)onCall:(LinphoneCall*)call StateChanged:(LinphoneCallState)state withMessage:(const char *)message;
4. 為支持ipv 6而導(dǎo)入新版Linphone SDK
舊版SDK不支持ipv6论咏,更后到新版后linphonecore.h增加了一個(gè)新的宏LINPHONE_DEPRECATED
优炬,有此標(biāo)記表示該函數(shù)已棄用,而我由于只用到了SIP電話厅贪,故直接替換sdk后只報(bào)了如下錯(cuò)誤蠢护,下面一一解決
4.1 替換LINPHONE_DEPRECATED標(biāo)識(shí)的函數(shù)
棄用的函數(shù)都有對應(yīng)的新函數(shù),在注釋里可以輕易找到
/**
* @return the default proxy configuration, that is the one used to determine the current identity.
* @deprecated Use linphone_core_get_default_proxy_config() instead.
**/
LINPHONE_PUBLIC LINPHONE_DEPRECATED int linphone_core_get_default_proxy(LinphoneCore *lc, LinphoneProxyConfig **config);
/**
* @ingroup media_parameters
* Get default call parameters reflecting current linphone core configuration
* @param lc LinphoneCore object
* @return LinphoneCallParams
* @deprecated use linphone_core_create_call_params()
*/
LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneCallParams *linphone_core_create_default_call_parameters(LinphoneCore *lc);
4.2 刪除棄用庫的初始化
在上圖中提現(xiàn)為“_libmsilbc_init”, referenced from:..這是因?yàn)樾掳鎙inphone sdk里lib文件夾里的.a庫變動(dòng)养涮,有刪有減少葵硕,而libmsilbc.a則被移除了,故要在LinphoneManager createLinphoneCore]
里刪除libmsilbc_init();
4.3 第三個(gè)bug是上兩個(gè)錯(cuò)誤所導(dǎo)致
解決1贯吓、2懈凹,自然也會(huì)解決3。
(后續(xù)貌似還有些許問題悄谐,待續(xù))
基本就是這么多了介评,其實(shí)回想起來也不是特別復(fù)雜,只是初次上手時(shí)網(wǎng)路上沒有太多資料文檔爬舰,大多靠自己摸索们陆,以及在少得可憐的博客中慢慢領(lǐng)悟也是挺辛酸的,希望大家少走彎路吧情屹。
如有其它麻煩可以電郵我 ischenzht@gmail.com
That's all, thank you.