音视频

一、VoIP插件介绍及导入

1.1、VoIP插件介绍及结构

  1.1.1、插件介绍

       VoIP插件是为用户开发音视频点对点通话的应用提供的完善的开发框架,VoIP插件 将音视频点对点通话的基本功能和界面集成到静态库中,方便用户快速高效的完成集成。用户可以根据自身需求集成我们的VoIP插件开发自己的应用,也可以基于我们的Demo开发。

  1.1.2、插件结构

  1.1.3、VoIP插件主要头文件

    AppModel.h:该类中有登陆、登出方法。

    Dialing.h:该类中是实现点对点语音、视频通话的功能方法。

    插件KitBaseHeader.h:该类是代理类,其中的代理回调方法有:获取用户信   息,通话结束回调等带了方法的声明。

  1.1.4、需导入第三方库

    第三方库采用cocopods管理,集成CocoaPods:

    需要集成如下框架,或者下载下来直接拖入您的工程中

    pod 'SDWebImage': 图片处理库

    pod 'MBProgressHUD': 用于提示加载刷新

1.2、集成VoIP插件

  1.2.1、导入VoIP插件

    第1步:先解压对应的VoIP插件压缩包,解压之后目录如图所示例:

  

    第2步:然后导入VoIP插件。将解压后的文件夹拖入您的工程中,并勾选上Destination,如图所示:提示说明(将lib文件夹导入项目中)

    

  1.2.2、设置工程属性

    向Build Phases -> Link Binary With Libraries 中添加系统依赖库,操作步骤如下所示:

    

    

    按照上图,点击加号后,在搜索框中输入需要的依赖库名称,点击Add添加依赖库成功(Xcode7.0以上尾缀是.tbd),IM插件需要iOS8.0或更高版本。

    插件所依赖的系统库如下:

  •     libz.tbd
  •     libicucore.tbd
  •     libresolv.9.tbd
  •     libstdc++.6.0.9.tbd
  •     libc++.tbd
  •     libsqlite3.tbd
  •     CoreGraphics.framework
  •     CoreTelephony.framework
  •     AVFoundation.framework
  •     MessageUI.framework
  •     VideoToolbox.framework
  •     AudioToolbox.framework

    

    添加完依赖库之后,第一步即完成,可以进行下一步了。

    编译设置:

    因为插件采用的是C、C++代码编写且对语言版本有一定要求,所以需要进行编译设置:

    1.如图使用系统默认或选择stdlibc++模式编译:

    

   2.在第一次调用的地方,如demo中,更改AppDelegate.m文件的后缀为.mm,如果不设置这两项,编译时出现std::编译错  误。

   3.IM插件暂不支持bitcode,Xcode7之后创建的工程需要关闭bitcode设置。

   

4.需要在other link flags 加上-ObjC字段,否则会编译错误

  1.2.3、适配iOS 10注意事项         

       1.IOS 10在使用摄像头、麦克风、定位、相册等功能的时候,会检查相应权限。用户需要手动在项目的info.plist文件中添加相应权限,如下图所示:    

    

    未手动添加权限时,会报如下错误:

    

2、对于iOS 10而言,需将Capabilities -> Push Notifications 开启,如图所示:

        注:如不开启改选项,会出现无法获取deviceToken, 老项目或会出现deviceToken无效的情况。

  1.2.4、编译工程

    以上步骤进行完后,编译工程。如果没有报错,恭喜你,集成 IM插件 成功,可以进行下一步了。

    提示:修改demo中配置App_AppKey和 App_Token,在CCPSDKBundle.bundle/ ServerAddr_SandBox.xml和          ServerAddr.xml中配置自己的服务器地址

     提示:以上步骤进行完后,编译工程。

    

二、初始化及登录VoIP插件

2.1、登录

2.1.1、强制登录

 服务器配置:

 在导入插件后,进入ServerAddr.xml和ServerAddr_SandBox.xml文件中配置ip地址和端口号。

  ip地址和端口号请联系我们的商务人员,由我们的商务人员提供。

    

 LoginMode_InputPassword:默认的首次登录方式,该登录模式可以把其他设备踢出,一般在输入密码登录时使用,可以作为强制登录方式踢出已在其他设备登录的账户。

2.1.2、自动登录

 LoginMode_AutoInputLogin:一般从后台返回应用时使用,如果账户已经在其它设备强制登录,自动登录将会返回错误码并作出提示登录失败。

       自动登录在以下几种情况下会被取消:

       • 用户调用了 SDK 的登出接口;

       • 用户在别的设备上更改了密码,导致此设备上自动登录失败返回175004错误码;

       • 用户的账号被从服务器端删除;

       • 用户从另一个设备强制登录,把当前设备上登录的用户踢出。

2.1.3、代码示例   

1. 第一步:导入头文件

    #import "AppModel.h"

   #import "ECError.h"  //登陆状态改变代理回调中使用的错误类

2.第二步:在登陆按钮的点击事件里实现登陆功能。

   Demo中代码示例:    (Demo中ViewController.m文件中65-110行代码)

  •  NSMutableDictionary* loginDict = [[NSMutableDictionary alloc] initWithCapacity:10];
  •  [loginDict setObject:userID forKey:Table_User_account];
  •  [loginDict setObject:[NSString stringWithFormat:@"模拟器%@",userID] forKey:Table_User_member_name];
  •  [loginDict setObject:_myPassField.text?_myPassField.text:@"" forKey:Table_User_mobile];
  •  
  •  [loginDict setObject:@"" forKey:App_AppKey];//App_AppKey对应用户在官网申请的appKey
  •  [loginDict setObject:@"" forKey:App_Token];//App_Token对应用户在官网申请的appToken
  •  
  •  [loginDict setObject:[NSNumber numberWithInt:1] forKey:@"mode"];
  •  [[AppModel sharedInstance] loginSDK:loginDict :^(NSError *error) {        sender.userInteractionEnabled = YES;
  •  if (error.code == 200) {
  •  UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"登录成功" preferredStyle:UIAlertControllerStyleAlert];
  •  UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
  •  [alertController addAction:cancelAction];
  •  [self presentViewController:alertController animated:YES completion:nil];
  •  _stateLabel.text = @"登录成功";
  •  }else{
  •  UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"登录失败" preferredStyle:UIAlertControllerStyleAlert];
  •  UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
  •  [alertController addAction:cancelAction];
  •  [self presentViewController:alertController animated:YES completion:nil];
  •  _stateLabel.text = @"登录失败";
  •  }
  • }];

 2.2、退出登录

在退出按钮的点击事件里实现退出功能。

Demo中代码示例:    (Demo中ViewController.m文件中114-128行代码)

  •  [[AppModel sharedInstance]logout:^(NSError *error) {
  •  sender.userInteractionEnabled = YES;
  •  if (error.code == 200) {
  •  UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:@"退出成功" preferredStyle:UIAlertControllerStyleAlert];
  •  UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
  •  [alertController addAction:cancelAction];
  •  [self presentViewController:alertController animated:YES completion:nil];
  •  _stateLabel.text = @"退出成功";
  •  }else{
  •  _stateLabel.text = @"退出失败";
  •  }
  •     }];

 2.3、登陆状态改变代理回调

        该代理回调方法的主要应用场景为当用户想要知道当前的登陆状态时使用,此代理方法可以获取到当前的登陆状态。

        简单使用:创建一个label用于显示当前的登陆状态,当登陆状态改变时,label的显示也会相应改变。

实现如下:              

             1、设置AppModel代理  

               [AppModel sharedInstance].delegate = self;

             2、实现代理回调方法

               Demo中代码示例:(Demo中ViewController.m文件中158-184行代码)   

  •  -(void)onConnectState:(ECConnectState)state  failed:(ECError*)error{
  •  switch (state) {
  •  case State_ConnectSuccess:
  •  {
  •  _stateLabel.text = @"sdk连接成功";
  •  }
  •  break;
  •  case State_Connecting:
  •  {
  •  _stateLabel.text = @"sdk连接中";
  •  }
  •  break;
  •  case State_ConnectFailed:
  •  {
  •  _stateLabel.text = @"sdk连接失败";
  •  if ([error errorCode]==ECErrorType_KickedOff) {
  •  //被踢下线
  •  _stateLabel.text = @"在其他设备登录";
  •  }
  •  
  •  }
  •  break;
  •  default:
  •  break;
  •  }}

三、音视频点对点功能模块

 该模块是用来实现单个用户与另一个用户之间的音视频通话,

   1.导入头文件

        #import " Dialing.h"

   2.设置收到通话通知后接听页面和通话页面的容器

        [AppModel sharedInstance].owner = self.view.window;

3.1、在Dialing.h中定义的主调方法

 3.1.1、发起点对点通话   

/**

     @brief 发起点对点通话

     @discussion 根据传入的参数发起点对点通话

     @param dict 配置信息 NSDictionary类型 keys:@“callType” @"caller" @"nickname" @" callDirect"

     @param callType 0 音频, 1 视频, 2 网络直拨, 3 网络回拨

     @param caller 音频和视频类型需传入用户账号, 网络直播和网络回拨需传入用户手机号

     @param nickname 用户姓名

     @param callDirect 呼叫方向 0:呼出 1:接入 该字段为NSNumber类型

     @return

     */

 -(void)startCallViewWithDict:(NSDictionary*)dict;              

  Demo中代码示例:    (Demo中ViewController.m文件中48-64行代码)

        

  •  - (IBAction)CallAction:(UIButton *)sender {
  •  if (KCNSSTRING_ISEMPTY(_toSendField.text)) {
  •  return;
  •  }
  •  [self.view endEditing:YES];
  •  NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:@"0",@"callType",_toSendField.text,@"caller",_toSendField.text,@"nickname",[NSNumber numberWithInt:0],@"callDirect",nil];
  •  [[Dialing sharedInstance] startCallViewWithDict:dict];
  • }

3.2、代理方法

在功能实现中有很多的代理方法需要开发人员根据自身应用的需要去实现相关的方法。在这里做统一的讲解和代码示例。

VoIP插件主要有音视频点对点通话,代理方法需要设置代理,如下:

设置代理:

  •                    [Dialing sharedInstance].componentDelegate = self;

3.2.1、必须实现的回调方法

3.2.1.1、实现获取当前登录人信息                  

该代理方法是用来获取登录人的个人信息。

个人信息获取代理方法返回为NSDictionary类型,account对应账号id,member_name对应姓名,mobile对应手机号,avatar对应头像url

代理方法介绍:

 /**

  @brief 获取个人信息

  @discussion 无需参数

   @return

    */

     -(NSDictionary*)onGetUserInfo;

 Demo中代码示例:    (Demo中ViewController.m文件中138-145行代码)

  •  -(NSDictionary*)onGetUserInfo{
  •  NSMutableDictionary *dic = [NSMutableDictionary dictionary];
  •  [dic setObject:userID?userID:@"" forKey:Table_User_account];
  •  [dic setObject:[NSString stringWithFormat:@"模拟器%@",userID] forKey:Table_User_member_name];
  •  [dic setObject:_myPassField.text?_myPassField.text:@"" forKey:Table_User_mobile];
  •  return dic;
  • }

3.2.1.2、实现获取联系人信息

该代理方法是用来获取用户信息,实现了该代理方法就可以获取到根据手机号或者account 查询到的用户的信息。

用户信息查询代理方法返回为NSDictionary类型,account对应账号id,member_name对应姓名,mobile对应手机号,avatar对应头像url

   代理方法介绍:

   /**

  @brief 用户信息查询

  @discussion 根据传入的参数获取用户信息,姓名头像等

  @param Id 用户账号或者手机号

  @param type 0根据account获取,1根据手机号获取

  @param userData 附加参数信息,用户透传字段,发起时传的什么,毁掉时候透传回去,一般可用作权限设置等

  @return

    */

   -(NSDictionary*)getDicWithId:(NSString*)Id withType:(int) type  UserData:(NSString *)userData;   

    Demo中代码示例:    (Demo中ViewController.m文件中132-136行代码)

  • -(NSDictionary*)getDicWithId:(NSString*)Id withType:(int) type  UserData:(NSString *)userData
  • {
  •  return nil;
  • }

   3.2.2、可选实现的回调方法

 3.2.2.1、点对点开始前设置头像

 /**

 暴露imageView接口

 @param urlStr:头像地址(此处是方法的实现)

 (备注:该方法只需要写实现即可,回调在库里,发起方和接听方都不需主动调用)

 */

 需要在点击发起点对点通话之前实现以下方法(实现方法由用户来写): 

 -(UIImageView *)setPortraitImageViewWithURL:(NSString *)urlStr;

 示例代码: 

  •  -(UIImageView *)setPortraitImageViewWithURL:(NSString *)urlStr{
  •  
  •  CGFloat kWidth = [UIScreen mainScreen].bounds.size.width;
  • CGFloat fitWitdth = kWidth / 320;
  •  
  •  UIImageView *headImageView =[[UIImageView alloc]initWithFrame:CGRectMake((kWidth-100*fitWitdth)/2,105*fitWitdth, 100*fitWitdth, 100*fitWitdth)];
  •  headImageView.layer.cornerRadius=headImageView.frame.size.width/2;
  •  headImageView.layer.masksToBounds=YES;
  •  headImageView.layer.borderColor=[UIColor clearColor].CGColor;
  •  [headImageView sd_setImageWithURL:[NSURL URLWithString:urlStr] placeholderImage:[UIImage imageNamed:@"monitorPlatform"]];
  •  headImageView.contentMode = UIViewContentModeScaleAspectFill;
  • return headImageView;
  • }

  3.2.2.1、通话结束后信息回调

 

      该代理方法,是用来获取通话结束,当用户希望在通话结束后做相应的操作就可以实现这个代理方法,处理自己应用关于通话结束后的业务逻辑。

    代理参数中information说明:

        CallInitiatorID:发起者ID

        CallReceiverID:接受者ID,若为会议模式,该字段为空

        Call_status:呼叫状态,0:呼出   1:呼出未接听   2:呼入接听   3:呼入拒接

        startTime:通话接通的时间(时间戳形式,NSNumber类型)

        endTime:通话结束时间(时间戳形式NSNumber类型)

实现回调方法:

     /**

 @brief 通话结束回调

 @param error 通话失败的错误码传回

 @param type 通话类型 0语音单聊 1视频单聊 2网络直播 3网络回拨 20语音会议 21视频会议

 @param information 通话信息 

 @param userData 用户透传信息字段 点对点时为空

 */

- (void)finishedCallWithError:(NSError *)error WithType:(VoipCallType)type WithCallInformation:(NSDictionary *)information  UserData:(NSString *)userData;

 Demo中代码示例:    (Demo中ViewController.m文件中184-246行代码)

  •  - (void)finishedCallWithError:(NSError *)error WithType:(VoipCallType)type WithCallInformation:(NSDictionary *)information  UserData:(NSString *)userData{ 
  •  NSString *CallInitiatorID = information[@"CallInitiatorID"];
  •  NSString *CallReceiverID = information[@"CallReceiverID"];
  •  NSString *Call_status = information[@"Call_status"];//0 呼出 1 呼出未接听 2 呼入接听 3 呼入拒接
  •  NSNumber *startTime = information[@"startTime"];
  •  NSNumber *endTime = information[@"endTime"];
  •   
  •  NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  •  [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  •  NSString *Call_status_String;
  •  switch ([Call_status intValue]) {
  •  case 0:
  •  Call_status_String = @"呼出";
  •  break;
  •  case 1:
  •  Call_status_String = @"呼出未接听";
  •  break;
  •  case 2:
  •  Call_status_String = @"呼入";
  •  break;
  •  case 3:
  •  Call_status_String = @"呼入未接听";
  •  break;
  •  default:
  •  break;
  •  } 
  •  switch (type) {
  •  case VoipCallType_Voice:
  •  if (error) {
  •  _callEndLabel.text = [NSString stringWithFormat:@"%@",error.description];
  •  }else{
  •  _callEndLabel.text = [NSString stringWithFormat:@"语音点对点发起方:%@,接收方:%@,状态:%@,开始时间:%@,结束时间:%@.",CallInitiatorID,CallReceiverID,Call_status_String,[formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:[startTime doubleValue]]],[formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:[endTime doubleValue]]]];
  •  }
  •  break;
  •  case VoipCallType_Video:
  •  if (error) {
  •  _callEndLabel.text = [NSString stringWithFormat:@"%@",error.description];
  •  }else{
  •  _callEndLabel.text = [NSString stringWithFormat:@"视频点对点发起方:%@,接收方:%@,状态:%@,开始时间:%@,结束时间:%@.",CallInitiatorID,CallReceiverID,Call_status_String,[formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:[startTime doubleValue]]],[formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:[endTime doubleValue]]]];
  •  }
  •  break;
  •  default:
  •  break;
  •  }}

四、其他设置

4.1、角标设置接口

  • /**
  •  @brief 设置角标数
  •  @param badgeNumber 角标数字
  •  */
  • -(void)setAppleBadgeNumber:(NSInteger)badgeNumber;