iOS 端如何实现不依赖群组的实时音视频通话?

1、单聊会话进行音视频通话时,如何将其他用户加入音视频通话。 2、群聊会话进行音视频通话时,如何将群组外的其他用户加入到音视频通话中
发布时间: 2018-01-19 14:37

回答:

如何实现不依赖群组的实时音视频通话(RTC)?

#要实现不依赖群组的多人音视频通话,需要客户端和服务器端分别实现对应的逻辑

##服务器端

  1. 生成 mediaId。 mediaId要求一个全局唯一的整数,且 0 < mediaId < 0x7fffffff

  2. 调用 IM Server API 通过 private 消息发送 RTC 相关信令。调用 IM Server API 发送消息时,需要特别注意以下参数:

    fromUserId=当前用户(自己)的id
    toUserId=参会者1&toUserId=参会者2 // 除自己外的,所有参会者拼在一起
    isPersisted=0
    isCounted=0
    isIncludeSender=0


##客户端

具体步骤如下:

1. 如果开发者想要不依赖会话类型,在调用 startMultiCallViewController: targetId:mediaType:userIdList: 时使用如下方式调用:

 /// idList为用户id列表
 if (idList.count > 0) {
     [[RCCall sharedRCCall] startMultiCallViewController:0 
                                                targetId:nil 
                                               mediaType:RCCallMediaAudio
                                              userIdList:idList];
 }

2. RCCallClient. 设置外部信令服务器代理,请务必使用全局代理,确保在拨打和接听的时候代理对象都存活,这样才能确保正常通话。

/*!
 设置外部信令服务器代理

 @param signalServerDelegate 外部信令服务器代理

 @discussion
 默认情况下 app 使用融云消息作为信令即可,不需要设置此代理,此时有个限制就是所有的通话必须在某一个会话中进行,比如群组。如果您需要摆脱会话的限制,请使用 server
 api 服务实现本代理,然后 startCall 时 conversationType 传 0,targetId 传 nil,calllib 会调用您设置的代理来发送消息。
 请务必使用一个全局代理,确保在拨打和接听 VoIP 的时候代理对象都存活,这样才能确保正常通话。
 */
- (void)setSignalServerDelegate:(id<RCCallSignalServerDelegate>)signalServerDelegate;

3. 实现代理方法 (伪代码)

/*!
 获取通话参与者的唯一媒体ID,必须保证每次电话会议的每个人的媒体ID都是全局唯一的。

 @param successBlock  为当前用户分配的媒体 Id 成功的回调
 @param errorBlcok    获取媒体 Id 失败的回调
 */
- (void)getUniqueMediaId:(void (^)(NSString *mediaId))successBlock error:(void (^)(int errorCode))errorBlcok {
    NSString *url = @"xxx/xxxx/media_id";
    [XXHTTPUtility requestWithHTTPMethod:HTTPRequestMethodGet
                                URLString:url
                               parameters:nil
                                 response:^(RCEHTTPResult *result) {
                                     if (result.success) {
                                         NSString *mediaId = [NSString stringWithFormat:@"%@", result.content];
                                         successBlock(mediaId);
                                     } else {
                                         RCELogE(@"getConferenceStatus http error, result is %@", result);
                                         errorBlcok((int)result.errorCode);
                                     }
                                 }];

}

/*!
 发送 VoIP 信令消息。

 @param messageContent  消息内容
 @param toUserIdList    接收者的 Id
 @param pushContent     pushContent
 @param pushData        pushData
 @param successBlock    成功的回调
 @param errorBlcok      失败的回调
 */
- (void)sendVoipSignalMessage:(RCMessageContent *)messageContent
                 toUserIdList:(NSArray<NSString *> *)toUserIdList
                  pushContent:(NSString *)pushContent
                     pushData:(NSString *)pushData
                      success:(void (^)(void))successBlock
                        error:(void (^)(int errorCode))errorBlcok {

    NSString *url = @"xxxxxxxxx/messages";
    NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];

    [dic setObject:@(ConversationType_PRIVATE) forKey:@"conversation_type"];
    [dic setObject:toUserIdList forKey:@"target_ids"];
    [dic setObject:[[messageContent class] getObjectName] forKey:@"object_name"];
    [dic setObject:[[NSString alloc] initWithData:[messageContent encode] encoding:NSUTF8StringEncoding]
            forKey:@"content"];
    if (pushContent) {
        [dic setObject:pushContent forKey:@"push_content"];
    }

    if (pushData) {
        [dic setObject:pushData forKey:@"push_data"];
    }

    [dic setObject:@(([[messageContent class] persistentFlag] & MessagePersistent_ISPERSISTED) > 0 ? 1 : 0)
            forKey:@"persisted"];
    [dic setObject:@(1) forKey:@"include_sender"];

    [XXHTTPUtility requestWithHTTPMethod:HTTPRequestMethodPost
                                URLString:url
                               parameters:dic
                                 response:^(RCEHTTPResult *result) {
                                     if (result.success) {
                                         if (successBlock) {
                                             successBlock();
                                         }
                                     } else {
                                         if (errorBlcok)
                                             errorBlcok((int)result.errorCode);
                                     }
                                 }];
}

/*! Voip通话结束返回的统计信息

 @param summary  通话统计 
 */
- (void)onVoipCallSummary:(RCCallSummaryMessage *)summary {
  
  //根据业务逻辑从 summary 中获取信息,用来生成通话记录。
  
}

4. 由于CallKit中默认实现的选择用户功能是基于会话类型的,所以开发者需要实现选择全局用户的视图控制器 XXSelectUserViewController(该控制器应提供选择用户和已选用户不能被重复选择的功能,还需返回被选中用户的 userid 列表)

5. 在视频通话页面用户点击邀请按钮时,会调用 RCCallAudioMultiCallViewController 中inviteUserButtonClicked方法,在该方法中开发者可以使用自己实现的 XXSelectUserViewController 建议pesent该控制器

6. 用户选择完成关闭XXSelectUserViewController页面后,获取到选择的用户列表 调用 inviteRemoteUsers:mediaType:接口完成发起邀请的操作

伪代码如下:

- (void)inviteUserButtonClicked {
    [self didTapInviteUserButton];
    UIViewController *svc = [[XXSelectUserViewController alloc]
                             initWithSuccess:^(NSArray *addUserIdList) {
                                 [weakSelf.callSession inviteRemoteUsers:addUserIdList
                                                               mediaType:weakSelf.mediaType];
                             }];
    [weakSelf presentViewController:svc animated:YES completion:nil];
}
我对此仍有疑问!继续追问