iOS 平台下Linphone的移植与使用

背景

我想使用linphone的朋友都有下载官方的demo。但是里面真的是耦合度太高了。读了好多遍发现他控制器跳转的代码都是很旧的了,所以想把他功能给择出来是有一定的难度的。 我从网上找到ESSipManager这个文件 。大家可以在github上搜索下。不过这个人封装的工程中引用的linphonesdk比较旧了。好像官网上是把那个sdk删除了。所以下载下来会报错。我就是在他的基础上改了点东西。 感觉基本上可以实现常规的功能,所以分享给大家。

SDK的安装

目前SDK安装已经支持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. [email protected]
#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)) { //设置随机端口
        
    }
    
    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(@"执行到这里...");
            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(@"执行到这里...");
        return true;
    } else {
        DLog(@"执行到这里...");
//        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)

接听电话 注册一个通知 然后接听到之后 进行跳转

image.png

最后.电脑端 下载一个Linphone 手机端运行下。俩端互拨 。慢慢调咯。祝各位好运。

你可能感兴趣的:(iOS 平台下Linphone的移植与使用)