怎樣將Linphone移植到自己的項(xiàng)目

我自己集成的Demo & SDK源碼路徑

最新版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.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坪仇,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子垃你,更是在濱河造成了極大的恐慌椅文,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惜颇,死亡現(xiàn)場離奇詭異雾袱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)官还,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毒坛,“玉大人望伦,你說我怎么就攤上這事〖逡螅” “怎么了屯伞?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長豪直。 經(jīng)常有香客問我劣摇,道長,這世上最難降的妖魔是什么弓乙? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任末融,我火速辦了婚禮钧惧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勾习。我一直安慰自己浓瞪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布巧婶。 她就那樣靜靜地躺著乾颁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艺栈。 梳的紋絲不亂的頭發(fā)上英岭,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機(jī)與錄音湿右,去河邊找鬼诅妹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛诅需,可吹牛的內(nèi)容都是我干的漾唉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼堰塌,長吁一口氣:“原來是場噩夢啊……” “哼赵刑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起场刑,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤般此,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后牵现,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铐懊,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年瞎疼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了科乎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贼急,死狀恐怖茅茂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情太抓,我是刑警寧澤空闲,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站走敌,受9級(jí)特大地震影響碴倾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一跌榔、第九天 我趴在偏房一處隱蔽的房頂上張望异雁。 院中可真熱鬧,春花似錦矫户、人聲如沸片迅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柑蛇。三九已至,卻和暖如春驱闷,著一層夾襖步出監(jiān)牢的瞬間耻台,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工空另, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盆耽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓扼菠,卻偏偏與公主長得像摄杂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子循榆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容