iOS 平臺下Linphone的移植與使用

背景

我想使用linphone的朋友都有下載官方的demo揪罕。但是里面真的是耦合度太高了蠢莺。讀了好多遍發(fā)現(xiàn)他控制器跳轉(zhuǎn)的代碼都是很舊的了雄可,所以想把他功能給擇出來是有一定的難度的购公。 我從網(wǎng)上找到ESSipManager這個文件 芯侥。大家可以在github上搜索下泊交。不過這個人封裝的工程中引用的linphonesdk比較舊了。好像官網(wǎng)上是把那個sdk刪除了柱查。所以下載下來會報錯活合。我就是在他的基礎(chǔ)上改了點東西。 感覺基本上可以實現(xiàn)常規(guī)的功能物赶,所以分享給大家白指。

SDK的安裝

目前SDK安裝已經(jīng)支持cocoaPods 所以在你原來的podfile代碼里加上" pod 'linphone-sdk' , '4.3' 如果請求過程很慢的話 再把源指定下source "https://gitlab.linphone.org/BC/public/podspec.git"

使用

image.png
image.png
image.png
//
//  ESSipManager.m
//  EasySip
//  edit by ivna.evecom. 1361817133@qq.com
#import "ESSipManager.h"
#import "LinphoneManager.h"
#import "FastAddressBook.h"
#define LC ([LinphoneManager getLc])

NSString *const ES_ON_REMOTE_OPEN_CEMERA = @"ES_ON_REMOTE_OPEN_CEMERA";
NSString *const ES_ON_CALL_COMMING = @"ES_ON_CALL_COMMING";
NSString *const ES_ON_CALL_END = @"ES_ON_CALL_END";
NSString *const ES_ON_CALL_STREAM_UPDATE = @"ES_ON_CALL_STREAM_UPDATE";
NSString *const ES_ON_Call_StateOutgoingInit = @"ES_ON_CallStateOutgoingInit";
NSString *const ES_ON_REGISTER_STATE = @"ES_ON_REGISTER_STATE";

@implementation ESSipManager

static ESSipManager* _instance = nil;

+(instancetype) instance
{
    static dispatch_once_t onceToken ;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
    }) ;
    
    return _instance ;
}

+(id)allocWithZone:(struct _NSZone *)zone
{
    return [ESSipManager instance];
}

-(id)copyWithZone:(struct _NSZone *)zone
{
    return [ESSipManager instance];
}

- (instancetype)init {
    self = [super init];
    if (self) {
        [[LinphoneManager instance] startLinphoneCore];
        LinphoneLoggingService * logService = linphone_logging_service_get();
        
        linphone_logging_service_set_log_level(logService,LinphoneLogLevelFatal);
    
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onCallUpdate:) name:kLinphoneCallUpdate object:nil];
        [NSNotificationCenter.defaultCenter addObserver:self
                                                 selector:@selector(registrationUpdate:)
                                                     name:kLinphoneRegistrationUpdate
                                                   object:nil];
    }
    return self;
}

- (void)registrationUpdate:(NSNotification *)notif {
    LinphoneRegistrationState state = [[notif.userInfo objectForKey:@"state"] intValue];
    if (state == LinphoneRegistrationFailed &&
        [UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        
        [[NSNotificationCenter defaultCenter]postNotificationName:ES_ON_REGISTER_STATE object:self userInfo:@{@"state":@"failed",@"message":[notif.userInfo objectForKey:@"message"]}];
    } else if (state == LinphoneRegistrationOk) {
        DLog(@"注冊成功!");
        [[NSNotificationCenter defaultCenter]postNotificationName:ES_ON_REGISTER_STATE object:self userInfo:@{@"state":@"success",@"message":[notif.userInfo objectForKey:@"message"]}];
    }
}
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void) login: (NSString*) username password: (NSString*) password displayName: (NSString*) displayName domain: (NSString*) domain port: (NSString *) port withTransport: (NSString*) transport {
    LinphoneProxyConfig *config = linphone_core_create_proxy_config(LC);
    LinphoneAddress *addr = linphone_address_new(NULL);
    LinphoneAddress *tmpAddr = linphone_address_new([NSString stringWithFormat:@"sip:%@",domain].UTF8String);
    linphone_address_set_username(addr, username.UTF8String);
    linphone_address_set_port(addr, linphone_address_get_port(tmpAddr));
    linphone_address_set_domain(addr, linphone_address_get_domain(tmpAddr));
    if (displayName && ![displayName isEqualToString:@""]) {
        linphone_address_set_display_name(addr, displayName.UTF8String);
    }
    linphone_proxy_config_set_identity_address(config, addr);
    if (transport) {
        linphone_proxy_config_set_route(
                                        config,
                                        [NSString stringWithFormat:@"%s;transport=%s", domain.UTF8String, transport.lowercaseString.UTF8String]
                                        .UTF8String);
        linphone_proxy_config_set_server_addr(
                                              config,
                                              [NSString stringWithFormat:@"%s;transport=%s", domain.UTF8String, transport.lowercaseString.UTF8String]
                                              .UTF8String);
    }
    
    linphone_proxy_config_enable_publish(config, FALSE);
    linphone_proxy_config_enable_register(config, TRUE);
    
    LinphoneAuthInfo *info =
    linphone_auth_info_new(linphone_address_get_username(addr), // username
                           NULL,                                // user id
                           password.UTF8String,                        // passwd
                           NULL,                                // ha1
                           linphone_address_get_domain(addr),   // realm - assumed to be domain
                           linphone_address_get_domain(addr)    // domain
                           );
    linphone_core_add_auth_info(LC, info);
    linphone_address_unref(addr);
    linphone_address_unref(tmpAddr);
    
    LCSipTransports linp = {-1,-1,-1,-1};
    //linphone_core_set_sip_transports
    if (linphone_core_set_sip_transports(LC, &linp)) { //設(shè)置隨機端口
        
    }
    
    if (config) {
        [[LinphoneManager instance] configurePushTokenForProxyConfig:config];
        if (linphone_core_add_proxy_config(LC, config) != -1) {
            linphone_core_set_default_proxy_config(LC, config);
            // reload address book to prepend proxy config domain to contacts' phone number
            // todo: STOP doing that!
//            [LinphoneManager instance]
//            [[LinphoneManager.instance fastAddressBook] fetchContactsInBackGroundThread];
            //            [PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription];
        } else {
            //            [self displayAssistantConfigurationError];
        }
    } else {
        //        [self displayAssistantConfigurationError];
    }
    
    NSLog(@"登陸信息配置成功!\nusername:%@,\npassword:%@,\ndisplayName:%@\ndomain:%@,\nport:%@\ntransport:%@", username, password, displayName, domain, port, transport);
}

- (void) logout {
    [[LinphoneManager instance] lpConfigSetBool:FALSE forKey:@"pushnotification_preference"];
    
    LinphoneCore *lc = [LinphoneManager getLc];
    LCSipTransports transportValue = {5060,5060,-1,-1};
    
    if (linphone_core_set_sip_transports(lc, &transportValue)) {
        NSLog(@"cannot set transport");
    }
    
    [[LinphoneManager instance] lpConfigSetString:@"" forKey:@"sharing_server_preference"];
    [[LinphoneManager instance] lpConfigSetBool:FALSE forKey:@"ice_preference"];
    [[LinphoneManager instance] lpConfigSetString:@"" forKey:@"stun_preference"];
    linphone_core_set_stun_server(lc, NULL);
    linphone_core_set_firewall_policy(lc, LinphonePolicyNoFirewall);
//    linphone_core_set_nat_policy()
    
//    linphone_core_set_nat_policy(LC,&LinphonePolicyNoFirewall);
}

- (void) call: (NSString*) username displayName: (NSString*) displayName {
    
  
    LinphoneCall *call = [[LinphoneManager instance] callByUsername:username];
    if (call == nil) {
        NSLog(@"撥打失敗");
    } else {
        NSLog(@"正在撥叫...\naddress:%@,\ndisplayName:%@", username, displayName);
    }
}

- (void) acceptCall: (LinphoneCall*) call {
    [[LinphoneManager instance] acceptCall:call evenWithVideo:true];
    NSLog(@"接聽電話");
}

- (void) hangUpCall {
    LinphoneCore* lc = [LinphoneManager getLc];
    LinphoneCall* currentcall = linphone_core_get_current_call(lc);
    if (linphone_core_is_in_conference(lc) || // In conference
        (linphone_core_get_conference_size(lc) > 0) // Only one conf
        ) {
        linphone_core_terminate_conference(lc);
    } else if(currentcall != NULL) { // In a call
//        linphone_core_terminate_call(lc, currentcall);
        //linphone_call_terminate()
        linphone_call_terminate(currentcall);
//        linphone_core_terminate_all_calls(lc)
    } else {
        const MSList* calls = linphone_core_get_calls(lc);
        //bctbx_list_size()
        if (bctbx_list_size(calls) == 1) { // Only one call
//            linphone_core_terminate_call(lc,(LinphoneCall*)(calls->data));
            linphone_core_terminate_all_calls(lc);
        }
    }
    NSLog(@"掛斷");
}

- (void) configVideo: (UIView*) videoView cameraView: (UIView*) cameraView {
    linphone_core_set_native_video_window_id([LinphoneManager getLc], (__bridge void *)(videoView));
    linphone_core_set_native_preview_window_id([LinphoneManager getLc], (__bridge void *)(cameraView));
}

- (void) requestOpenCamera {
    
    if (!linphone_core_video_display_enabled(LC)){
        NSLog(@"1111");
        return;
    }
  
    
    LinphoneCall *call = linphone_core_get_current_call(LC);
    if (call) {
        NSLog(@"2222");
        LinphoneCallAppData *callAppData = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
        callAppData->videoRequested = TRUE; /* will be used later to notify user if video was not activated because of the linphone core*/
        LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call);
        linphone_call_params_enable_video(call_params, TRUE);
        linphone_core_update_call(LC, call, call_params);
        linphone_call_params_destroy(call_params);
    } else {
        NSLog(@"Cannot toggle video button, because no current call");
    }
}

- (void) closeCamera {
    if (!linphone_core_video_display_enabled(LC))
    return;
    [LinphoneManager.instance setSpeakerEnabled:FALSE];
    
    LinphoneCall *call = linphone_core_get_current_call(LC);
    if (call) {
        LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call);
        linphone_call_params_enable_video(call_params, FALSE);
        linphone_core_update_call(LC, call, call_params);
        linphone_call_params_destroy(call_params);
    } else {
        NSLog(@"Cannot toggle video button, because no current call");
    }
}

-(BOOL)isVideoEnabled{
    bool video_enabled = false;
    LinphoneCall *currentCall = linphone_core_get_current_call(LC);
    if (linphone_core_video_supported(LC)) {
        if (linphone_core_video_display_enabled(LC) && currentCall && !linphone_core_sound_resources_locked(LC) &&
            linphone_call_get_state(currentCall) == LinphoneCallStreamsRunning) {
            video_enabled = TRUE;
        }
    }
 
    if (video_enabled) {
        video_enabled = linphone_call_params_video_enabled(linphone_call_get_current_params(currentCall));
    }
    
    

    return video_enabled;
}


- (void) onCallUpdate: (NSNotification*) notification {
    NSDictionary* userInfo = [notification userInfo];
    NSValue* c = [userInfo valueForKey:@"call"];
    //    int state = (int)[userInfo valueForKey:@"state"];
    LinphoneCallState state = [[userInfo objectForKey:@"state"] intValue];
    NSString* message = [userInfo valueForKey:@"message"];
    NSLog(@"========== state: %d, message: %@", state, message);
    LinphoneCall* call = c.pointerValue;
    
    NSDictionary *dict = @{@"call" : [NSValue valueWithPointer:call],
                           @"state" : [NSNumber numberWithInt:state],
                           @"message" : message};
    
    switch (state) {
            case LinphoneCallIncomingReceived:
            [NSNotificationCenter.defaultCenter postNotificationName:ES_ON_CALL_COMMING object: self userInfo:dict];
            case LinphoneCallOutgoingInit:
            
            [NSNotificationCenter.defaultCenter postNotificationName:ES_ON_Call_StateOutgoingInit object:self userInfo:dict];
            case LinphoneCallConnected:
           
            case LinphoneCallStreamsRunning: {
                // check video
                if (![self isVideoEnabled]) {
                    const LinphoneCallParams *param = linphone_call_get_current_params(call);
                    const LinphoneCallAppData *callAppData =
                    (__bridge const LinphoneCallAppData *)(linphone_call_get_user_data(call));
                    if (state == LinphoneCallStreamsRunning && callAppData->videoRequested &&
                        linphone_call_params_low_bandwidth_enabled(param)) {
                        // too bad video was not enabled because low bandwidth
                        
                        NSLog(@"帶寬太低,無法開啟視頻通話");
                        
                        callAppData->videoRequested = FALSE; /*reset field*/
                    }
                }
                [NSNotificationCenter.defaultCenter postNotificationName:ES_ON_CALL_STREAM_UPDATE object:self userInfo:dict];
                break;
            }
            case LinphoneCallUpdatedByRemote: {
                const LinphoneCallParams *current = linphone_call_get_current_params(call);
                const LinphoneCallParams *remote = linphone_call_get_remote_params(call);
                
                DLog(@"%d",(linphone_core_video_display_enabled([LinphoneManager getLc])));
                     DLog(@"%d",!linphone_call_params_video_enabled(current));
                DLog(@"%d",linphone_call_params_video_enabled(remote));
                DLog(@"%d",!linphone_core_get_video_policy([LinphoneManager getLc])->automatically_accept);
                /* remote wants to add video */
                if ((linphone_core_video_display_enabled([LinphoneManager getLc]) && !linphone_call_params_video_enabled(current) &&
                     linphone_call_params_video_enabled(remote)) &&
                    (!linphone_core_get_video_policy([LinphoneManager getLc])->automatically_accept ||
                     (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) &&
                      floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max))) {
                         linphone_core_defer_call_update([LinphoneManager getLc], call);
                         
                         
                         [NSNotificationCenter.defaultCenter postNotificationName:ES_ON_REMOTE_OPEN_CEMERA object: self userInfo:dict];
                         
                        
                         
                     } else if (linphone_call_params_video_enabled(current) && !linphone_call_params_video_enabled(remote)) {
                         
                     }
                break;
            }
            case LinphoneCallUpdating:
            break;
            case LinphoneCallPausing:
            case LinphoneCallPaused:
            break;
            case LinphoneCallPausedByRemote:
            break;
            case LinphoneCallEnd://LinphoneCallEnd
            [NSNotificationCenter.defaultCenter postNotificationName:ES_ON_CALL_END object: self userInfo:NULL];
            case LinphoneCallError:
        default:
            break;
    }
}

-(void) allowToOpenCameraByRemote: (LinphoneCall*)call {
    LinphoneCallParams *params = linphone_core_create_call_params([LinphoneManager getLc], call);
    linphone_call_params_enable_video(params, TRUE);
    linphone_call_accept_update(call, params);
    linphone_call_params_destroy(params);
}

-(void) refuseToOpenCameraByRemote: (LinphoneCall*)call {
    LinphoneCallParams *params = linphone_core_create_call_params(LC, call);
    linphone_call_params_enable_video(params, FALSE);
    linphone_call_accept_update(call, params);
    linphone_call_params_destroy(params);
}

-(void)switchCamera{
    
    const char *currentCamId = (char *)linphone_core_get_video_device(LC);
    const char **cameras = linphone_core_get_video_devices(LC);
    const char *newCamId = NULL;
    int i;

    for (i = 0; cameras[i] != NULL; ++i) {
        if (strcmp(cameras[i], "StaticImage: Static picture") == 0)
            continue;
        if (strcmp(cameras[i], currentCamId) != 0) {
            newCamId = cameras[i];
            break;
        }
    }
    if (newCamId) {
//        LOGI(@"Switching from [%s] to [%s]", currentCamId, newCamId);
        linphone_core_set_video_device(LC, newCamId);
        LinphoneCall *call = linphone_core_get_current_call(LC);
        if (call != NULL) {
            linphone_call_update(call, NULL);
        }
    }
}

-(BOOL)turnOnVideo{
    if (!linphone_core_video_display_enabled(LC)){
        DLog(@"執(zhí)行到這里...");
            return false;
    }
    
    LinphoneCall *call = linphone_core_get_current_call(LC);
    if (call) {
        LinphoneCallAppData *callAppData = (__bridge LinphoneCallAppData *)linphone_call_get_user_data(call);
        callAppData->videoRequested =
            TRUE; /* will be used later to notify user if video was not activated because of the linphone core*/
        LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call);
        linphone_call_params_enable_video(call_params, TRUE);
        linphone_call_update(call, call_params);
        linphone_call_params_unref(call_params);
        DLog(@"執(zhí)行到這里...");
        return true;
    } else {
        DLog(@"執(zhí)行到這里...");
//        LOGW(@"Cannot toggle video button, because no current call");
        return false;
    }
}
-(BOOL)turnOffVideo{
    if (!linphone_core_video_display_enabled(LC))
        return false;
    [LinphoneManager.instance setSpeakerEnabled:FALSE];
//    [self setEnabled:FALSE];
//    [waitView startAnimating];

    LinphoneCall *call = linphone_core_get_current_call(LC);
    if (call) {
        LinphoneCallParams *call_params = linphone_core_create_call_params(LC,call);
        linphone_call_params_enable_video(call_params, FALSE);
        linphone_core_update_call(LC, call, call_params);
        linphone_call_params_destroy(call_params);
        return true;
    } else {
//        LOGW(@"Cannot toggle video button, because no current call");
        return false;
    }
}

-(NSString*) getCallName: (LinphoneCall*)call {
    if (call == NULL)
    return NULL;
    LinphoneAddress *addr = linphone_call_get_remote_address(call);
    
    return [FastAddressBook displayNameForAddress:addr];
}

@end

如上圖所示 每個方法都有介紹了酵紫。

注冊 domin 填入你們自己的地址


    func loginWithInfo(_ info:JSON){
        let userInfo = info["userinfo"].stringValue
        
        let dic =   SimpleOperation.getDictionaryFromJSONString(jsonString:userInfo)
        
        consoleLine(message: dic)
        
        let videoPhone = dic["videophone"] as? String ?? ""
        
        consoleLine(message: videoPhone)
        let name = dic["name"] as? String ?? ""
        
        ESSipManager.instance()?.login(videoPhone, password: "123456", displayName: name, domain: "", port: "5060", withTransport: "TCP")
        
    }
    func logOut(){
        ESSipManager.instance()?.logout()
    }

退出

 func logOut(){
        ESSipManager.instance()?.logout()
    }

撥打電話

ESSipManager.instance()?.call(phone, displayName: name)

接聽電話 注冊一個通知 然后接聽到之后 進行跳轉(zhuǎn)

image.png

最后.電腦端 下載一個Linphone 手機端運行下告嘲。倆端互撥 。慢慢調(diào)咯奖地。祝各位好運橄唬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市参歹,隨后出現(xiàn)的幾起案子仰楚,更是在濱河造成了極大的恐慌,老刑警劉巖犬庇,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件僧界,死亡現(xiàn)場離奇詭異,居然都是意外死亡臭挽,警方通過查閱死者的電腦和手機捂襟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欢峰,“玉大人葬荷,你說我怎么就攤上這事涨共。” “怎么了宠漩?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵举反,是天一觀的道長。 經(jīng)常有香客問我扒吁,道長照筑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任瘦陈,我火速辦了婚禮凝危,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘晨逝。我一直安慰自己蛾默,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布捉貌。 她就那樣靜靜地躺著支鸡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趁窃。 梳的紋絲不亂的頭發(fā)上牧挣,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音醒陆,去河邊找鬼瀑构。 笑死,一個胖子當(dāng)著我的面吹牛刨摩,可吹牛的內(nèi)容都是我干的寺晌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼澡刹,長吁一口氣:“原來是場噩夢啊……” “哼呻征!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起罢浇,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤陆赋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嚷闭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體攒岛,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年凌受,在試婚紗的時候發(fā)現(xiàn)自己被綠了阵子。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片思杯。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡胜蛉,死狀恐怖挠进,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情誊册,我是刑警寧澤领突,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站案怯,受9級特大地震影響君旦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嘲碱,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一金砍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧麦锯,春花似錦恕稠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至料祠,卻和暖如春骆捧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背髓绽。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工敛苇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人顺呕。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓接谨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親塘匣。 傳聞我的和親對象是個殘疾皇子脓豪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348