iOS - 融云RTC功能梳理

2023-12-22 18:38
文章标签 功能 ios rtc 梳理 融云

本文主要是介绍iOS - 融云RTC功能梳理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一. 功能介绍

功能需求

  1. 一个文字聊天室(如图下半部分)
  2. 一个语音聊天室(语音房)(上半部分)
  3. 五个麦位
  4. 可上麦下麦

二. 业务梳理

融云流程图

  1. 业务层面的语音聊天室  在用户创建聊天室时请求业务(自己)服务器,业务服务器创建聊天室,并返回给创者者当前聊天室的 id,同时其他用户可通过获取聊天室列表接口获取到此聊天室的 id。用户调根据此聊天室 id 加入 IM 聊天室音频 RTC房间。当前用户属于哪个聊天室,当前聊天室内有哪些用户是基于此聊天室。
    备注: 在创建一个房间时, 后台依次会调用融云server API 创建一个语音通话, 文字聊天都包含的一个房间(ROOM).

  2. IM 即时通讯层面的语音聊天室  加入 IM 聊天室后,用户可以发送文本消息聊天。另外维护聊天室各种状态的信令消息也通过 IM 服务来收发。(理论上 文字聊天(IM), 语音房(RTC) 使用一个目标Id即可)

3, RTC 音频层面的语音聊天室   加入 RTC 聊天室后,用户可以获得到当前语音聊天室内所有发布音频流的用户,并选择订阅音频流来收听目标用户的声音,也可以自己发布音频流让其他人听到自己的声音。

三. 前端(App端) 梳理

屏幕快照 2019-10-26 下午5.00.05.png

  • A 可见 B、C、D 的音频、视频

  • B 可按需选择只听 A、D 的音频,只看 A、C 的视频

  • C 可按需选择只听 A、B、D 的音频,但不看其他人视频

  • D 可按需选择只看 A、B、C 的视频,但不听其他人的音频

四. iOS 端伪代码, 以下全是伪代码

  1. RTC(语音视频室基于IM)所以第一步 需要连接IM
[[KKIMMgr shareInstance] checkAndConnectRCSuccess:^{/// 连接成功, 再去创建一个上述功能的房间} error:^(RCConnectErrorCode status) {[CC_Notice show:@"聊天室未连接"];}];
  1. 创建房间成功后呢, 后台会返回一个目标Id, 或者叫做房间Id (roomId), 通过这个Id去连接 RTC Room
///  KKRtcService 是对融云RTC "<RongRTCLib/RongRTCLib.h>"进行了一层封装
[[KKRtcService shareInstance] joinRoom:channelId success:^(RongRTCRoom * _Nonnull room) {dispatch_async(dispatch_get_main_queue(), ^{/// 证明连接成功RTC///  跳转进入房间});});} error:^(RongRTCCode code) {dispatch_main_async_safe(^{[CC_Notice show:@"进入房间失败!"];});}];          
  1. 做一个上麦, 下麦的管理

没有伪代码

  1. 对于再麦位上的用户, 扩散动画处理, 也是一段伪代码.
- (void)didReportStatForm:(RongRTCStatisticalForm *)form {//1.发送for(RongRTCStreamStat *streamStat in form.sendStats){//只处理音频//处理发送的音频 动画if ([streamStat.mediaType isEqualToString:RongRTCMediaTypeAudio]) {[self updateMicPositionSpeaking:streamStat.trackId audioLevel:streamStat.audioLevel];}}//2.接收for(RongRTCStreamStat *streamStat in form.recvStats){//只处理音频//处理接收的音频 动画if ([streamStat.mediaType isEqualToString:RongRTCMediaTypeAudio]) {[self updateMicPositionSpeaking:streamStat.trackId audioLevel:streamStat.audioLevel];}}
}- (void)updateMicPositionSpeaking:(NSString *)userId audioLevel:(NSInteger)audioLevel {dispatch_async(dispatch_get_main_queue(), ^{/// 这里是处理了 CollectionViewCell里面的动画逻辑/// 关键在与userId的判断和audioLevel音量的双重条件, 来控制动画for (KKPlayerCardCollectionCell *cell in self.playerListView.visibleCells) {if (cell.dataModel.userId.length > 0 && [userId containsString:cell.dataModel.userId]) {CCLOG(@"cell.dataModel.userId === %@", cell.dataModel.userId);if (self.isMicOpen == YES) {cell.isSpeaking = audioLevel > 0;}else {if ([userId containsString:[KKUserInfoMgr shareInstance].userId]) {cell.isSpeaking = NO;}}}}});
}
  1. 解释

5.1 简单理解上麦需要发布音频流
5.2 下麦取消发布音频流
5.3 想听到谁的声音/视频 你就去订阅谁
5.4 不想听谁的声音关闭扬声器就好了

五. RTCServiceMgr 以下代码通用

@interface RTCServiceMgr : NSObject#pragma mark - life circle
+ (instancetype)shareInstance;
- (void)remove;#pragma mark - rtc
/** 当前加入的 rtc房间 (当加入房间成功之后,才会是有效值 */
@property (nonatomic, strong, readonly) RongRTCRoom *rtcRoom;/** 关闭所有声音 */
@property (nonatomic, assign) BOOL muteAllVoice;/** 设置 直播间代理 */
- (void)setRTCRoomDelegate:(id<RongRTCRoomDelegate>)delegate;/** 设置 直播间引擎代理 */
- (void)setRTCEngineDelegate:(id<RongRTCActivityMonitorDelegate>)delegate;#pragma mark 加入/退出
/** 加入直播间 */
- (void)joinRoom:(NSString *)roomId success:(void (^)( RongRTCRoom *room))success error:(void (^)(RongRTCCode code))error;/** 退出直播间 */
- (void)leaveRoom:(NSString*)roomId success:(void (^)(void))success error:(void (^)(RongRTCCode code))error;#pragma mark  音频流
/** 发布 音视频流 */
- (void)pulishCurrentUserAudioStream;/** 取消发布 音视频流 */
- (void)unpublishCurrentUserAudioStream;/** 订阅 远端音频stream */
- (void)subscribeRemoteUserAudioStream:(NSString *)userId;/** 取消订阅 远端音频stream */
- (void)unsubscribeRemoteUserAudioStream:(NSString *)userId;#pragma mark 麦克风/扬声器
/** 设置麦克风 是否可用 */
- (void)setMicrophoneDisable:(BOOL)disable;/** 设置扬声器 是否可用 */
- (void)useSpeaker:(BOOL)useSpeaker;@end
@interface RTCServiceMgr () 
@property (nonatomic, strong) RongRTCRoom *rtcRoom;
@property (nonatomic, strong) RongRTCVideoCaptureParam *captureParam;
@property (nonatomic, strong) RongRTCAVCapturer *capturer;
@end@implementation RTCServiceMgrstatic RTCServiceMgr *rongRtcMgr = nil;
static dispatch_once_t onceToken;#pragma mark - getter/setter
#pragma mark getter
- (RongRTCAVCapturer *)capturer {if(!_capturer) {_capturer = [RongRTCAVCapturer sharedInstance];}return _capturer;
}
- (RongRTCVideoCaptureParam *)captureParam {if(!_captureParam) {_captureParam = [[RongRTCVideoCaptureParam alloc] init];_captureParam.turnOnCamera = NO;}return _captureParam;}#pragma mark setter
- (void)setMuteAllVoice:(BOOL)mute {_muteAllVoice = mute;for(RongRTCRemoteUser *remoteUser in self.rtcRoom.remoteUsers) {for(RongRTCAVInputStream *stream in remoteUser.remoteAVStreams) {if(stream.streamType == RTCMediaTypeAudio) {stream.disable = mute;}}}
}#pragma mark - life circle
+ (instancetype)shareInstance {dispatch_once(&onceToken, ^{rongRtcMgr = [[KKRtcService alloc] init];});return rongRtcMgr;
}- (instancetype)init {self = [super init];if (self) {}return self;
}/** 清空 */
- (void)remove{//1.删除userDefault中的用户信息//2.释放selfrongRtcMgr = nil;onceToken = 0;
}#pragma mark - rtc
- (void)setRTCRoomDelegate:(id<RongRTCRoomDelegate>)delegate {if(!self.rtcRoom) {//[CC_Notice show:@"尚未加入招募厅,无法设置代理"];return;}self.rtcRoom.delegate = delegate;
}-(void)setRTCEngineDelegate:(id<RongRTCActivityMonitorDelegate>)delegate {[RongRTCEngine sharedEngine].monitorDelegate = delegate;
}#pragma mark 加入/退出 rtc 房间
- (void)joinRoom:(NSString *)roomId success:(void (^)( RongRTCRoom  * _Nullable room))success error:(void (^)(RongRTCCode code))error {[[RongRTCEngine sharedEngine] joinRoom:roomId completion:^(RongRTCRoom * _Nullable room, RongRTCCode code) {dispatch_async(dispatch_get_main_queue(), ^{if(RongRTCCodeSuccess == code) {self.rtcRoom = room;if(success) {success(room);}}else if(RongRTCCodeJoinRepeatedRoom == code || RongRTCCodeJoinToSameRoom == code) {//当 RTC 出现此类错误时,RTC 不会再下发 room 对象,只能用上次的 roomif(success) {success(self.rtcRoom);}}else {if(error) {error(code);}}});}];
}- (void)leaveRoom:(NSString*)roomId success:(void (^)(void))success error:(void (^)(RongRTCCode code))error {[[RongRTCEngine sharedEngine] leaveRoom:roomId completion:^(BOOL isSuccess, RongRTCCode code) {dispatch_async(dispatch_get_main_queue(), ^{BBLOG(@"离开 RTCRoom ,code = %ld",(long)code);//成功 或 不再房间中 或 没有匹配的RTC roomif(isSuccess || RongRTCCodeSuccess == code ||RongRTCCodeNotInRoom == code ||RongRTCCodeNoMatchedRoom == code) {self.rtcRoom = nil;if(success) {success();}}else {if(error) {error(code);}}});}];
}#pragma mark 音频流
/** 发布 音视频流 */
- (void)pulishCurrentUserAudioStream {if(!self.rtcRoom) {//房间没生成return;}//1.采集self.captureParam.turnOnCamera = NO;[self.capturer setCaptureParam:self.captureParam];[self.capturer startCapture];//2.发布[self.rtcRoom publishDefaultAVStream:^(BOOL isSuccess, RongRTCCode desc) {BBLOG(@"当前用户发布音频流 %@",@(desc));}];
}/** 取消发布 音视频流 */
- (void)unpublishCurrentUserAudioStream {if(!self.rtcRoom) {//房间没生成return;}//1.关闭采集[self.capturer stopCapture];//2.取消发布[self.rtcRoom unpublishDefaultAVStream:^(BOOL isSuccess, RongRTCCode desc) {BBLOG(@"当前用户取消发送音视频流 %@",@(desc));}];}/** 订阅 远端音频stream */
- (void)subscribeRemoteUserAudioStream:(NSString *)userId {if(!self.rtcRoom) {//房间没生成return;}if(userId.length < 1){return;}RongRTCRemoteUser *remoteUser = [self getRTCRemoteUser:userId];if(remoteUser.remoteAVStreams.count <= 0) {[CC_Notice show:@"远端资源不存在,不能订阅音频"];//BBLOG(@"subscribe --- user:%@ streams:%@",remoteUser.userId,remoteUser.remoteAVStreams);return;}[self.rtcRoom subscribeAVStream:remoteUser.remoteAVStreams tinyStreams:nil completion:^(BOOL isSuccess, RongRTCCode desc) {BOOL mute = [KKRtcService shareInstance].muteAllVoice;for(RongRTCAVInputStream *stream in remoteUser.remoteAVStreams) {if(stream.streamType == RTCMediaTypeAudio) {stream.disable = mute;}}BBLOG(@"subscribe --- 订阅流 %@ success:%@ code:%@",remoteUser.userId,@(isSuccess),@(desc));}];
}/** 取消订阅 远端音频stream */
- (void)unsubscribeRemoteUserAudioStream:(NSString *)userId {if(!self.rtcRoom) {//房间没生成return;}RongRTCRemoteUser *remoteUser = [self getRTCRemoteUser:userId];if(remoteUser.remoteAVStreams.count <= 0) {//[CC_Notice show:@"远端资源不存在,不能取消订阅音频"];BBLOG(@"unsubscribe --- user:%@ streams:%@",remoteUser.userId,remoteUser.remoteAVStreams);return;}[self.rtcRoom unsubscribeAVStream:remoteUser.remoteAVStreams completion:^(BOOL isSuccess, RongRTCCode desc) {BBLOG(@"取消订阅流 %@ success:%@ code:%@",remoteUser.userId,@(isSuccess),@(desc));}];
}#pragma mark - tool
#pragma mark capturer
- (void)setMicrophoneDisable:(BOOL)disable {[self.capturer setMicrophoneDisable:disable];
}- (void)useSpeaker:(BOOL)useSpeaker {[self.capturer useSpeaker:useSpeaker];
}#pragma mark RemoteUser
/** 获取 rtcRemoteUser */
- (RongRTCRemoteUser *)getRTCRemoteUser:(NSString *)userId{for (RongRTCRemoteUser *user in self.rtcRoom.remoteUsers) {if ([userId isEqualToString:user.userId]) {return user;}}return nil;
}

六 总结

  1. 创建房间(具备RTC功能的房间) 重要!
  2. 加入房间
  3. 麦位管理
  4. 动画处理
  5. 麦克风和扬声器的管理


文/夏天然后

这篇关于iOS - 融云RTC功能梳理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/525036

相关文章

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将

使用Python实现批量访问URL并解析XML响应功能

《使用Python实现批量访问URL并解析XML响应功能》在现代Web开发和数据抓取中,批量访问URL并解析响应内容是一个常见的需求,本文将详细介绍如何使用Python实现批量访问URL并解析XML响... 目录引言1. 背景与需求2. 工具方法实现2.1 单URL访问与解析代码实现代码说明2.2 示例调用

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

如何评价Ubuntu 24.04 LTS? Ubuntu 24.04 LTS新功能亮点和重要变化

《如何评价Ubuntu24.04LTS?Ubuntu24.04LTS新功能亮点和重要变化》Ubuntu24.04LTS即将发布,带来一系列提升用户体验的显著功能,本文深入探讨了该版本的亮... Ubuntu 24.04 LTS,代号 Noble NumBAT,正式发布下载!如果你在使用 Ubuntu 23.

TP-LINK/水星和hasivo交换机怎么选? 三款网管交换机系统功能对比

《TP-LINK/水星和hasivo交换机怎么选?三款网管交换机系统功能对比》今天选了三款都是”8+1″的2.5G网管交换机,分别是TP-LINK水星和hasivo交换机,该怎么选呢?这些交换机功... TP-LINK、水星和hasivo这三台交换机都是”8+1″的2.5G网管交换机,我手里的China编程has

Django中使用SMTP实现邮件发送功能

《Django中使用SMTP实现邮件发送功能》在Django中使用SMTP发送邮件是一个常见的需求,通常用于发送用户注册确认邮件、密码重置邮件等,下面我们来看看如何在Django中配置S... 目录1. 配置 Django 项目以使用 SMTP2. 创建 Django 应用3. 添加应用到项目设置4. 创建

使用 Python 和 LabelMe 实现图片验证码的自动标注功能

《使用Python和LabelMe实现图片验证码的自动标注功能》文章介绍了如何使用Python和LabelMe自动标注图片验证码,主要步骤包括图像预处理、OCR识别和生成标注文件,通过结合Pa... 目录使用 python 和 LabelMe 实现图片验证码的自动标注环境准备必备工具安装依赖实现自动标注核心