LiveRoomViewController.m 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048
  1. //
  2. // LiveRoomViewController.m
  3. // KulexiuForTeacher
  4. //
  5. // Created by 王智 on 2022/6/21.
  6. //
  7. #import "LiveRoomViewController.h"
  8. #import "LivePreviewBodyView.h"
  9. #import <RongRTCLib/RongRTCLib.h>
  10. #import <RongIMKit/RongIMKit.h>
  11. #import <RongFaceBeautifier/RongFaceBeautifier.h>
  12. #import "KSBeautySettingView.h"
  13. #import "KSEnterLiveroomManager.h"
  14. #import "LiveroomTimeManager.h"
  15. #import "UserInfoManager.h"
  16. #import <RongIMKit/RongIMKit.h>
  17. #import "KSChatInputBarControl.h"
  18. #import "LiveRoomHeadView.h"
  19. #import "LiveRoomBottomView.h"
  20. #import "SeatContentView.h"
  21. #import "SeatTipsView.h"
  22. #import "KSLiveStreamVideo.h"
  23. #import "KSRCMessageModel.h"
  24. #import "KSChatroomMessageCenter.h"
  25. #import "KSChatroomTextCell.h"
  26. #import "LiveRoomAlertView.h"
  27. #import "LiveMoreDisplayView.h"
  28. #import "LiveAnimationView.h"
  29. #import "KSShareChooseViewController.h"
  30. #import "KSChatLiveMessage.h"
  31. #import "LiveSeatActionView.h"
  32. typedef NS_ENUM(NSInteger, LIVEPAGE) {
  33. LIVEPAGE_PREVIEW,
  34. LIVEPAGE_LIVE,
  35. };
  36. @interface LiveRoomViewController ()<RCRTCEngineEventDelegate,RCRTCRoomEventDelegate,RCRTCStatusReportDelegate,UIGestureRecognizerDelegate,KSChatInputBarControlDelegate,UITableViewDataSource,UITableViewDelegate,LiveroomTimeManagerDelegate>
  37. @property (nonatomic, strong) LiveroomTimeManager *timeManager;
  38. @property (nonatomic, assign) LIVEPAGE pageType;
  39. @property (nonatomic, assign) BOOL isPauseLive; // 是否暂停直播
  40. @property (nonatomic, assign) BOOL isOtherLogin; // 是否被顶掉
  41. @property (nonatomic, strong) LivePreviewBodyView *previewPageView;
  42. @property (nonatomic, strong) RCRTCVideoView *preVideoView;
  43. @property (nonatomic, strong) KSBeautySettingView *settingView;
  44. @property (nonatomic, assign) BOOL isBeautyOn;
  45. @property (nonatomic, assign) int filterLevel;
  46. #pragma mark ------- 直播房间信息
  47. /*!
  48. 身份状态 主讲人/观众
  49. */
  50. @property (nonatomic, assign) RCRTCLiveRoleType liveRoleType;
  51. /// 主讲人id
  52. @property (nonatomic, strong) NSString *createrId;
  53. /// 主讲人名称
  54. @property (nonatomic, strong) NSString *createrName;
  55. /// 主讲人头像
  56. @property (nonatomic, strong) NSString *createrAvatal;
  57. // 是否禁止连麦
  58. @property (nonatomic, assign) BOOL enableSeat;
  59. // 是否禁止聊天
  60. @property (nonatomic, assign) BOOL enableChat;
  61. // 是否允许点赞
  62. @property (nonatomic, assign) BOOL enableLike;
  63. @property (nonatomic, assign) NSInteger likeCount;
  64. /// 房间人数
  65. @property (nonatomic, assign) NSInteger totalCount;
  66. #pragma mark ---- Live page
  67. @property (nonatomic, strong) UIView *livePageView;
  68. @property (nonatomic, strong) RCRTCRoom *room;
  69. @property (nonatomic, assign) BOOL isImConnected;
  70. @property (nonatomic, assign) BOOL hasSendWelcomeMessage;
  71. @property (nonatomic, strong) KSLiveStreamVideo *localVideo;
  72. @property (nonatomic, strong) UIView *liveVideoView;
  73. @property (nonatomic, strong) LiveRoomHeadView *headView;
  74. @property (nonatomic, copy) LiveRoomBottomView *bottomView;
  75. /// 连麦的视图
  76. @property (nonatomic, strong) SeatContentView *seatContainer;
  77. // 聊天UI
  78. /*!
  79. 消息列表CollectionView和输入框都在这个view里
  80. */
  81. @property(nonatomic, strong) UIView *messageContentView;
  82. /*!
  83. 会话页面的TableView
  84. */
  85. @property (nonatomic, strong) UITableView *conversationMessageTableView;
  86. /*!
  87. 聊天内容的消息Cell数据模型的数据源
  88. @discussion 数据源中存放的元素为消息Cell的数据模型,即RCDLiveMessageModel对象。
  89. */
  90. @property(nonatomic, strong) NSMutableArray<KSRCMessageModel *> *conversationDataRepository;
  91. /**
  92. 输入工具栏
  93. */
  94. @property(nonatomic,strong) KSChatInputBarControl *inputBar;
  95. /**
  96. * 是否需要滚动到底部
  97. */
  98. @property(nonatomic, assign) BOOL isNeedScrollToButtom;
  99. @property (nonatomic, strong) UIView *seatCtrlView;
  100. @property (nonatomic, strong) LiveSeatActionView *seatActionView;
  101. @property (nonatomic, strong) NSMutableArray *seatApplyArray; // 申请连麦数组
  102. // 远端连麦用户
  103. @property (nonatomic, strong) NSMutableArray *remoteMemberArray;
  104. /// 连麦申请统计数据
  105. @property (nonatomic, strong) SeatTipsView *seatApplyTips;
  106. /// 提示窗
  107. @property (nonatomic, strong) LiveRoomAlertView *alertView;
  108. // 定时器
  109. @property (nonatomic, strong) dispatch_source_t timer;
  110. @property (nonatomic, strong) LiveMoreDisplayView *moreView;
  111. @property (nonatomic) LiveAnimationView *animationView;
  112. /// 直播课结束时间
  113. @property (nonatomic, strong) NSString *liveEndTime;
  114. /// 直播间关闭延迟时间
  115. @property (nonatomic, assign) NSInteger expiredCloseMinute;
  116. @property (nonatomic, copy) LiveRoomBackAction callback;
  117. @property (nonatomic, strong) UITapGestureRecognizer *gesture;
  118. @property (nonatomic, assign) BOOL needPublishStream;
  119. @end
  120. @implementation LiveRoomViewController
  121. - (void)backRefreshCallback:(LiveRoomBackAction)callback {
  122. if (callback) {
  123. self.callback = callback;
  124. }
  125. }
  126. - (void)viewDidLoad {
  127. [super viewDidLoad];
  128. // Do any additional setup after loading the view.
  129. self.ks_prefersNavigationBarHidden = YES;
  130. self.isOtherLogin = NO;
  131. [self registerOtherLoginNotice];
  132. [self configUI];
  133. [self configEngine];
  134. self.pageType = LIVEPAGE_PREVIEW;
  135. }
  136. - (void)registerOtherLoginNotice {
  137. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(liveroomLogOut) name:@"liveroomLogout" object:nil];
  138. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(liveroomQuit) name:@"liveroomQuit" object:nil];
  139. }
  140. - (void)viewWillAppear:(BOOL)animated {
  141. [super viewWillAppear:animated];
  142. [UIApplication sharedApplication].idleTimerDisabled = YES;
  143. [IQKeyboardManager sharedManager].enableAutoToolbar = NO;
  144. [IQKeyboardManager sharedManager].enable = NO;
  145. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
  146. }
  147. - (void)viewWillDisappear:(BOOL)animated {
  148. [super viewWillDisappear:animated];
  149. [UIApplication sharedApplication].idleTimerDisabled = NO;
  150. [IQKeyboardManager sharedManager].enableAutoToolbar = YES;
  151. [IQKeyboardManager sharedManager].enable = YES;
  152. if (@available(iOS 13.0, *)) {
  153. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDarkContent;
  154. } else {
  155. // Fallback on earlier versions
  156. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
  157. }
  158. }
  159. - (void)configUI {
  160. self.previewPageView = [LivePreviewBodyView shareInstance];
  161. [self.view addSubview:self.previewPageView];
  162. [self.previewPageView mas_makeConstraints:^(MASConstraintMaker *make) {
  163. make.left.right.bottom.top.mas_equalTo(self.view);
  164. }];
  165. MJWeakSelf;
  166. [self.previewPageView previewOperationCallback:^(PREVIEWLIVEACTION action) {
  167. [weakSelf previewOperationAction:action];
  168. }];
  169. [[RCRTCBeautyEngine sharedInstance] reset];
  170. }
  171. - (void)configEngine {
  172. if (!self.preVideoView) {
  173. self.preVideoView = [[RCRTCVideoView alloc] init];
  174. self.preVideoView.fillMode = RCRTCVideoFillModeAspectFill;
  175. self.preVideoView.frameAnimated = NO;
  176. [self.previewPageView.videoView addSubview:self.preVideoView];
  177. [self.preVideoView mas_makeConstraints:^(MASConstraintMaker *make) {
  178. make.left.top.bottom.right.mas_equalTo(self.previewPageView.videoView);
  179. }];
  180. }
  181. [[RCRTCEngine sharedInstance].defaultVideoStream setVideoView:self.preVideoView];
  182. RCRTCVideoStreamConfig *videoConfig = [RCRTCEngine sharedInstance].defaultVideoStream.videoConfig;
  183. videoConfig.videoSizePreset = RCRTCVideoSizePreset1920x1080;
  184. [[RCRTCEngine sharedInstance].defaultVideoStream setVideoConfig:videoConfig];
  185. [[RCRTCEngine sharedInstance].defaultVideoStream startCapture];
  186. self.filterLevel = 50;
  187. }
  188. - (void)previewOperationAction:(PREVIEWLIVEACTION)action {
  189. switch (action) {
  190. case PREVIEWLIVEACTION_BACK: // 返回
  191. {
  192. if (self.isPauseLive) { // 退出RTC房间
  193. [self quitRoomBackPreView:NO];
  194. }
  195. else {
  196. self.room = nil;
  197. self.preVideoView = nil;
  198. [[RCRTCEngine sharedInstance].defaultVideoStream stopCapture];
  199. if (self.callback) {
  200. self.callback();
  201. }
  202. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  203. }
  204. }
  205. break;
  206. case PREVIEWLIVEACTION_SWITCH: // 切换
  207. {
  208. [[RCRTCEngine sharedInstance].defaultVideoStream switchCamera];
  209. }
  210. break;
  211. case PREVIEWLIVEACTION_BEAUTY: // 美颜
  212. {
  213. [self displayBeautyView];
  214. }
  215. break;
  216. case PREVIEWLIVEACTION_SHARE: // 分享
  217. {
  218. [self shareLiveRoomMessage];
  219. }
  220. break;
  221. case PREVIEWLIVEACTION_OPEN: // 进入直播间
  222. {
  223. [self showLiveView];
  224. }
  225. break;
  226. default:
  227. break;
  228. }
  229. }
  230. - (void)shareLiveRoomMessage {
  231. // 选择群组分享
  232. KSChatLiveMessage *liveShareMsg = [[KSChatLiveMessage alloc] init];
  233. liveShareMsg.roomUID = self.roomId;
  234. NSString *teacherName = UserDefaultObjectForKey(NicknameKey);
  235. if ([NSString isEmptyString:teacherName]) {
  236. teacherName = [NSString stringWithFormat:@"游客%@",UserDefaultObjectForKey(UIDKey)];
  237. }
  238. liveShareMsg.teacherName = teacherName;
  239. liveShareMsg.teacherAvatar = UserDefaultObjectForKey(AvatarUrlKey);
  240. liveShareMsg.liveDescMessage = self.liveContent;
  241. KSShareChooseViewController *chooseCtrl = [[KSShareChooseViewController alloc] init];
  242. chooseCtrl.msgContent = liveShareMsg;
  243. [self.navigationController pushViewController:chooseCtrl animated:YES];
  244. }
  245. - (void)displayBeautyView {
  246. [self displayBottomView:YES];
  247. if (_settingView == nil) {
  248. RCRTCBeautyOption *option = [[RCRTCBeautyEngine sharedInstance] getCurrentBeautyOption];
  249. RCRTCBeautyFilter filter = [[RCRTCBeautyEngine sharedInstance] getCurrentBeautyFilter];
  250. FILTER_TYPE type = [self getFilterType:filter];
  251. MJWeakSelf;
  252. [self.settingView evaluateMessageWithWhite:option.whitenessLevel smoothLevel:option.smoothLevel ruddyLevel:option.ruddyLevel brightLevel:option.brightLevel filter:type filterLevel:self.filterLevel callback:^(BOOL isOpenBeauty, NSInteger white, NSInteger smoothLevel, NSInteger ruddyLevel, NSInteger brightLevel, FILTER_TYPE type, int filterLevel) {
  253. [weakSelf configSettingBeauty:YES white:white smoothLevel:smoothLevel ruddyLevel:ruddyLevel brightLevel:brightLevel filter:[self getRongBeautyWithType:type] filterLevel:filterLevel];
  254. }];
  255. }
  256. [self.view addSubview:self.settingView];
  257. [self.settingView mas_makeConstraints:^(MASConstraintMaker *make) {
  258. make.left.right.top.bottom.mas_equalTo(self.view);
  259. }];
  260. }
  261. - (FILTER_TYPE)getFilterType:(RCRTCBeautyFilter)filter {
  262. switch (filter) {
  263. case RCRTCBeautyFilterNone:
  264. return FILTER_TYPE_NONE;
  265. case RCRTCBeautyFilterFresh:
  266. return FILTER_TYPE_QINGXIN;
  267. case RCRTCBeautyFilterEsthetic:
  268. return FILTER_TYPE_WEIMEI;
  269. case RCRTCBeautyFilterRomantic:
  270. return FILTER_TYPE_LANGMAN;
  271. default:
  272. return FILTER_TYPE_NONE;
  273. break;
  274. }
  275. }
  276. - (RCRTCBeautyFilter)getRongBeautyWithType:(FILTER_TYPE)type {
  277. switch (type) {
  278. case FILTER_TYPE_NONE:
  279. return RCRTCBeautyFilterNone;
  280. case FILTER_TYPE_QINGXIN:
  281. return RCRTCBeautyFilterFresh;
  282. case FILTER_TYPE_WEIMEI:
  283. return RCRTCBeautyFilterEsthetic;
  284. case FILTER_TYPE_LANGMAN:
  285. return RCRTCBeautyFilterRomantic;
  286. default:
  287. return RCRTCBeautyFilterNone;
  288. break;
  289. }
  290. }
  291. - (void)configSettingBeauty:(BOOL)isOn white:(NSInteger)whitenessLevel smoothLevel:(NSInteger)smoothLevel ruddyLevel:(NSInteger)ruddyLevel brightLevel:(NSInteger)brightLevel filter:(RCRTCBeautyFilter)filter filterLevel:(int)filterLevel {
  292. self.isBeautyOn = isOn;
  293. if (isOn) {
  294. // 获取当前美颜参数
  295. RCRTCBeautyOption *option = [[RCRTCBeautyEngine sharedInstance] getCurrentBeautyOption];
  296. // 修改参数
  297. option.whitenessLevel = whitenessLevel;
  298. option.smoothLevel = smoothLevel;
  299. option.ruddyLevel = ruddyLevel;
  300. option.brightLevel = brightLevel;
  301. // 设置美颜
  302. [[RCRTCBeautyEngine sharedInstance] setBeautyOption:isOn option:option];
  303. // 设置滤镜
  304. [[RCRTCBeautyEngine sharedInstance] setBeautyFilter:filter];
  305. // 设置滤镜强度
  306. [[RCRTCBeautyEngine sharedInstance] setFilterIntensity:filterLevel];
  307. }
  308. else {
  309. [[RCRTCBeautyEngine sharedInstance] reset];
  310. }
  311. }
  312. - (void)showLiveView {
  313. if (self.isPauseLive) { // 直接切换页面
  314. self.pageType = LIVEPAGE_LIVE;
  315. [self.view addSubview:self.livePageView];
  316. [self.livePageView mas_makeConstraints:^(MASConstraintMaker *make) {
  317. make.left.right.top.bottom.mas_equalTo(self.view);
  318. }];
  319. // 开启推流
  320. [self startPublishStream];
  321. }
  322. else {
  323. [self showhud];
  324. MJWeakSelf;
  325. [KSEnterLiveroomManager queryLiveroomConfig:self.roomId callback:^(NSDictionary * _Nullable parm) {
  326. [weakSelf removehub];
  327. if (parm != nil) {
  328. [weakSelf setupLiveroomConfig:parm];
  329. [weakSelf createLivePageViewDisplay];
  330. }
  331. }];
  332. }
  333. }
  334. - (void)startPublishStream {
  335. self.pageType = LIVEPAGE_LIVE;
  336. self.isPauseLive = NO;
  337. [self publishLocalStream];
  338. }
  339. - (void)setupLiveroomConfig:(NSDictionary *)source {
  340. NSLog(@"room config----- %@",source);
  341. self.liveRoleType = RCRTCLiveRoleTypeBroadcaster;
  342. self.createrId = [source stringValueForKey:@"speakerId"];
  343. self.createrName = [source stringValueForKey:@"speakerName"];
  344. self.createrAvatal = [source stringValueForKey:@"speakerPic"];
  345. self.likeCount = [source integerValueForKey:@"likeNum"]; // 点赞数
  346. NSInteger lookCount = [source integerValueForKey:@"lookNum"];
  347. self.totalCount = lookCount; // 观看人数
  348. NSString *roomConfig = [source stringValueForKey:@"roomConfig"];
  349. NSData *jsonData = [roomConfig dataUsingEncoding:NSUTF8StringEncoding];
  350. NSError *err;
  351. NSDictionary *configDic = [NSJSONSerialization JSONObjectWithData:jsonData
  352. options:NSJSONReadingMutableContainers
  353. error:&err];
  354. if (configDic) {
  355. self.enableChat = ![configDic boolValueForKey:@"whether_chat"];
  356. self.enableSeat = ![configDic boolValueForKey:@"whether_mic"];
  357. self.enableLike = ![configDic boolValueForKey:@"whether_like"];
  358. }
  359. BOOL isTemp = [[source stringValueForKey:@"roomType"] isEqualToString:@"TEMP"];
  360. self.isTempRoom = isTemp;
  361. if (isTemp == NO) {
  362. self.liveEndTime = [source stringValueForKey:@"liveEndTime"];
  363. self.expiredCloseMinute = [source integerValueForKey:@"expiredCloseMinute"];
  364. }
  365. else {
  366. self.liveEndTime = [source stringValueForKey:@"liveEndTime"];
  367. self.expiredCloseMinute = 0;
  368. }
  369. }
  370. - (void)createLivePageViewDisplay {
  371. self.pageType = LIVEPAGE_LIVE;
  372. self.isImConnected = [USER_MANAGER checkIMConnected];
  373. [self registerNotification];
  374. [self setupUI];
  375. [self configIMConnect];
  376. [self startTimer];
  377. }
  378. #pragma mark --- settingView
  379. - (KSBeautySettingView *)settingView {
  380. if (!_settingView) {
  381. _settingView = [KSBeautySettingView shareInstance];
  382. MJWeakSelf;
  383. [_settingView hiddenViewCallback:^{
  384. [weakSelf displayBottomView:NO];
  385. }];
  386. }
  387. return _settingView;
  388. }
  389. - (void)displayBottomView:(BOOL)isHidden {
  390. if (isHidden) {
  391. self.previewPageView.functionView.hidden = YES;
  392. }
  393. else {
  394. self.previewPageView.functionView.hidden = NO;
  395. }
  396. }
  397. #pragma mark ------- Live page
  398. - (void)startTimer {
  399. if (self.timer) {
  400. dispatch_resume(self.timer);
  401. }
  402. }
  403. - (void)registerNotification {
  404. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(IMConnetedCallback) name:@"RongIMConnected" object:nil];
  405. [KSChatroomMessageCenter registerMessageTypes];
  406. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMessageNotification:) name:OnReceiveChatroomMessageNotification object:nil];
  407. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appEnterBackground) name:@"appEnterBackground" object:nil];
  408. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeActive) name:@"appBecomeActive" object:nil];
  409. [self configTimerManager];
  410. }
  411. - (void)configTimerManager {
  412. self.timeManager.endClassTime = self.liveEndTime;
  413. self.timeManager.autoCloseNetworkRoomTime = [self getCloseTime];
  414. self.timeManager.isTempRoom = self.isTempRoom;
  415. }
  416. #pragma mark ------ enter back ground
  417. - (void)appEnterBackground {
  418. self.isEnterBackground = YES;
  419. if (self.isPauseLive == NO) { // 暂停推流
  420. [self pauseLiveActionBack:NO];
  421. }
  422. }
  423. - (void)appBecomeActive {
  424. if (self.isEnterBackground && self.pageType == LIVEPAGE_LIVE && self.isOtherLogin == NO) {
  425. if ([USER_MANAGER checkIMConnected]) {
  426. // 开启推流
  427. [self startPublishStream];
  428. }
  429. else {
  430. self.needPublishStream = YES;
  431. }
  432. }
  433. self.isEnterBackground = NO;
  434. }
  435. - (void)setupUI {
  436. CGSize size = self.view.bounds.size;
  437. [self.view addSubview:self.livePageView];
  438. [self.livePageView mas_makeConstraints:^(MASConstraintMaker *make) {
  439. make.left.right.top.bottom.mas_equalTo(self.view);
  440. }];
  441. [self.livePageView addSubview:self.liveVideoView];
  442. [self.liveVideoView mas_makeConstraints:^(MASConstraintMaker *make) {
  443. make.left.top.bottom.right.mas_equalTo(self.livePageView);
  444. }];
  445. [self.livePageView addSubview:self.headView];
  446. [self.headView mas_makeConstraints:^(MASConstraintMaker *make) {
  447. make.left.right.mas_equalTo(self.livePageView);
  448. make.height.mas_equalTo(90);
  449. if (@available(iOS 11.0, *)) {
  450. make.top.mas_equalTo(self.livePageView.mas_safeAreaLayoutGuideTop).offset(10);
  451. } else {
  452. // Fallback on earlier versions
  453. make.top.mas_equalTo(self.livePageView.mas_top).offset(10);
  454. }
  455. }];
  456. [self.livePageView addSubview:self.messageContentView];
  457. [self.livePageView addSubview:self.bottomView];
  458. self.headView.boardcastName.text = [NSString returnNoNullStringWithString:self.createrName];
  459. if (![NSString isEmptyString:self.createrAvatal]) {
  460. [self.headView.boardcastAvatal sd_setImageWithURL:[NSURL URLWithString:[self.createrAvatal getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:@"teacher_logo"]];
  461. }
  462. [self countLikeMessageCount];
  463. [self.headView mas_makeConstraints:^(MASConstraintMaker *make) {
  464. make.left.right.mas_equalTo(self.livePageView);
  465. make.height.mas_equalTo(90);
  466. if (@available(iOS 11.0, *)) {
  467. make.top.mas_equalTo(self.livePageView.mas_safeAreaLayoutGuideTop).offset(10);
  468. } else {
  469. // Fallback on earlier versions
  470. make.top.mas_equalTo(self.livePageView.mas_top).offset(10);
  471. }
  472. }];
  473. [self.livePageView addSubview:self.messageContentView];
  474. [self.livePageView addSubview:self.bottomView];
  475. [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
  476. make.left.right.mas_equalTo(self.livePageView);
  477. if (@available(iOS 11.0, *)) {
  478. make.bottom.mas_equalTo(self.livePageView.mas_safeAreaLayoutGuideBottom).offset(-5);
  479. } else {
  480. // Fallback on earlier versions
  481. make.bottom.mas_equalTo(self.livePageView.mas_bottom).offset(-5);
  482. }
  483. make.height.mas_equalTo(44);
  484. }];
  485. [self.messageContentView mas_makeConstraints:^(MASConstraintMaker *make) {
  486. make.left.mas_equalTo(self.livePageView.mas_left);
  487. make.right.mas_equalTo(self.livePageView.mas_right);
  488. make.bottom.mas_equalTo(self.bottomView.mas_top).offset(-5);
  489. make.height.mas_equalTo(180);
  490. }];
  491. [self.livePageView addSubview:self.inputBar];
  492. [_inputBar setBackgroundColor: [UIColor whiteColor]];
  493. [_inputBar setFrame:CGRectMake(0, kScreenHeight, size.width , 50)];
  494. [_inputBar setHidden:YES];
  495. [self.messageContentView addSubview:self.conversationMessageTableView];
  496. [self.conversationMessageTableView mas_makeConstraints:^(MASConstraintMaker *make) {
  497. make.left.top.mas_equalTo(self.messageContentView);
  498. make.width.mas_equalTo(KPortraitWidth);
  499. make.bottom.mas_equalTo(self.messageContentView.mas_bottom);
  500. }];
  501. [self.livePageView addSubview:self.seatApplyTips];
  502. [self.seatApplyTips hideView];
  503. [self.seatApplyTips mas_makeConstraints:^(MASConstraintMaker *make) {
  504. make.bottom.mas_equalTo(self.bottomView.mas_top).offset(2);
  505. make.height.mas_equalTo(20);
  506. make.left.mas_equalTo(self.bottomView.mas_right).offset(-72);
  507. }];
  508. UITapGestureRecognizer *resetBottomTapGesture =[[UITapGestureRecognizer alloc]
  509. initWithTarget:self
  510. action:@selector(resetBottomGesture:)];
  511. resetBottomTapGesture.delegate = self;
  512. [self.livePageView addGestureRecognizer:resetBottomTapGesture];
  513. }
  514. - (void)joinChatRoom {
  515. [[RCIMClient sharedRCIMClient] joinChatRoom:self.roomId messageCount:-1 success:^{
  516. dispatch_async(dispatch_get_main_queue(), ^{
  517. [self addConstMessage];
  518. });
  519. KSLiveChatroomWelcome *joinChatroomMessage = [[KSLiveChatroomWelcome alloc] init];
  520. [joinChatroomMessage setMsgId:[RCIM sharedRCIM].currentUserInfo.userId];
  521. MJWeakSelf;
  522. [self sendMessage:joinChatroomMessage displayMessage:NO callback:^(BOOL success) {
  523. weakSelf.hasSendWelcomeMessage = YES;
  524. }];
  525. // 加入成功发送消息
  526. } error:^(RCErrorCode status) {
  527. if (status == RC_CHATROOM_NOT_EXIST || status == KICKED_FROM_CHATROOM || status == RC_PARAMETER_INVALID_CHATROOM) {
  528. dispatch_async(dispatch_get_main_queue(), ^{
  529. [self MBPShow:@"加入聊天室失败"];
  530. });
  531. }
  532. }];
  533. }
  534. - (void)configIMConnect {
  535. if (self.isImConnected == YES) {
  536. [self connectionService];
  537. }
  538. else {
  539. MJWeakSelf;
  540. [USER_MANAGER connectionIMCallback:^(BOOL isSuccess) {
  541. dispatch_main_async_safe(^{
  542. [weakSelf connectionService];
  543. });
  544. }];
  545. }
  546. }
  547. - (void)IMConnetedCallback {
  548. dispatch_main_async_safe(^{
  549. // IM 连接成功回调
  550. if (self.hasSendWelcomeMessage) { // 如果已经发送了进入消息
  551. // 查询是否直播间开启
  552. [KSNetworkingManager speakerCheckRoomInfoRequest:KS_GET roomUid:self.roomId success:^(NSDictionary * _Nonnull dic) {
  553. if ([dic integerValueForKey:@"code"] == 200 && [dic boolValueForKey:@"status"]) {
  554. // 回调信息
  555. MJWeakSelf;
  556. [self notiferJoinSuccessToServiceCallback:^{
  557. [weakSelf MBPShow:@"IM连接成功"];
  558. [weakSelf sendWelcomeMessage];
  559. }];
  560. }
  561. else {
  562. [self MBPShow:MESSAGEKEY];
  563. [self quitRoomBackPreView:NO];
  564. }
  565. } faliure:^(NSError * _Nonnull error) {
  566. }];
  567. }
  568. if (self.needPublishStream) {
  569. [self startPublishStream];
  570. }
  571. });
  572. }
  573. - (void)sendWelcomeMessage {
  574. MJWeakSelf;
  575. [[RCIMClient sharedRCIMClient] joinExistChatRoom:self.roomId messageCount:-1 success:^{
  576. KSLiveChatroomWelcome *joinChatroomMessage = [[KSLiveChatroomWelcome alloc] init];
  577. [joinChatroomMessage setMsgId:[RCIM sharedRCIM].currentUserInfo.userId];
  578. [self sendMessage:joinChatroomMessage displayMessage:NO callback:^(BOOL success) {
  579. [weakSelf joinRTCRoom];
  580. }];
  581. } error:^(RCErrorCode status) {
  582. NSLog(@"error code %zd" ,status);
  583. }];
  584. }
  585. - (void)joinRTCRoom {
  586. [self setRoleType];
  587. }
  588. - (void)liveroomLogOut {
  589. if (self.isOtherLogin) {
  590. return;
  591. }
  592. self.isOtherLogin = YES;
  593. [self MBPShow:@"该账号在其他设备上登录"];
  594. [self quitRoomBackPreView:NO];
  595. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  596. [[NSNotificationCenter defaultCenter] postNotificationName:@"liveroomBackRefresh" object:nil];
  597. });
  598. }
  599. - (void)liveroomQuit {
  600. if (self.isOtherLogin) {
  601. return;
  602. }
  603. self.isOtherLogin = YES;
  604. [self quitRoomBackPreView:NO];
  605. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  606. [[NSNotificationCenter defaultCenter] postNotificationName:@"liveroomBackRefresh" object:nil];
  607. });
  608. }
  609. - (void)connectionService {
  610. [self setRoleType];
  611. [self joinChatRoom];
  612. }
  613. - (void)countLikeMessageCount {
  614. NSInteger count = self.likeCount;
  615. NSString *desc = @"";
  616. if (count > 10000) {
  617. count = count / 10000;
  618. desc = [NSString stringWithFormat:@"本场点赞%zd万+",count];
  619. }
  620. else {
  621. desc = [NSString stringWithFormat:@"本场点赞%zd",count];
  622. }
  623. self.headView.likeCount.text = desc;
  624. }
  625. - (void)countMemberCount {
  626. NSInteger count = self.totalCount;
  627. NSString *desc = @"";
  628. if (count > 10000) {
  629. count = count / 10000;
  630. desc = [NSString stringWithFormat:@"%zd万+人",count];
  631. }
  632. else {
  633. desc = [NSString stringWithFormat:@"%zd人",count];
  634. }
  635. self.headView.roomMemberCount.text = desc;
  636. }
  637. // 刷新seatView
  638. - (void)renderSeatView {
  639. NSMutableArray *seatArray = [NSMutableArray array];
  640. NSArray *remoteUserArray = [self.room.remoteUsers mutableCopy];
  641. for (RCRTCRemoteUser *user in remoteUserArray) {
  642. if (![user.userId containsString:@"rc_mc_"]) {
  643. [seatArray addObject:user.userId];
  644. }
  645. }
  646. if (seatArray.count) {
  647. if (![self.livePageView.subviews containsObject:self.seatContainer]) {
  648. [self.livePageView addSubview:self.seatContainer];
  649. }
  650. [self.seatContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
  651. make.left.right.mas_equalTo(self.livePageView);
  652. make.top.mas_equalTo(self.headView.mas_bottom).offset(10);
  653. make.height.mas_equalTo(80);
  654. }];
  655. self.seatContainer.seatMemberArray = [NSMutableArray arrayWithArray:seatArray];
  656. [self.seatContainer refreshSeatUI];
  657. }
  658. else {
  659. [self removeSeatContainer];
  660. }
  661. }
  662. - (void)removeSeatContainer {
  663. if ([self.livePageView.subviews containsObject:self.seatContainer]) {
  664. [self.seatContainer removeFromSuperview];
  665. }
  666. }
  667. - (void)setRoleType {
  668. // 1.设置切换听筒为扬声器
  669. [[RCRTCEngine sharedInstance] setDefaultAudioRouteToSpeaker:YES];
  670. [RCRTCEngine sharedInstance].statusReportDelegate = self;
  671. // 2.添加本地采集预览界面
  672. [self setupLocalVideoView];
  673. // 3.加入RTC房间
  674. [self joinRTCLiveRoom];
  675. }
  676. // 添加本地采集预览页面
  677. - (void)setupLocalVideoView {
  678. [self.liveVideoView addSubview:self.localVideo.canvesView];
  679. [self.localVideo.canvesView mas_makeConstraints:^(MASConstraintMaker *make) {
  680. make.left.right.top.bottom.mas_equalTo(self.liveVideoView);
  681. }];
  682. }
  683. #pragma mark ----- RCRTCStatusReportDelegate
  684. - (void)didReportStatusForm:(RCRTCStatusForm *)form {
  685. BOOL isConnectedFailed = NO;
  686. for (RCRTCStreamStat* stat in form.sendStats) {
  687. if (stat.packetLoss == 1.0) {
  688. isConnectedFailed = YES;
  689. break;
  690. }
  691. }
  692. [self showNetStatus:form.rtt networkLost:isConnectedFailed];
  693. // NSLog(@"%@",form.description);
  694. // 网络状态差提示
  695. }
  696. - (void)showNetStatus:(NSInteger)rttValue networkLost:(BOOL)isFailed {
  697. dispatch_main_async_safe((^{
  698. if (isFailed) {
  699. [self.headView.rttImage setImage:[UIImage imageNamed:@"live_networking_bad"]];
  700. self.headView.msLabel.text = @"网络已断开";
  701. }
  702. else {
  703. self.headView.msLabel.text = [NSString stringWithFormat:@"%zdms",rttValue];
  704. if (rttValue < 100) {
  705. [self.headView.rttImage setImage:[UIImage imageNamed:@"live_networking_good"]];
  706. }
  707. else if (rttValue < 200) {
  708. [self.headView.rttImage setImage:[UIImage imageNamed:@"live_networking_nomal"]];
  709. }
  710. else {
  711. [self.headView.rttImage setImage:[UIImage imageNamed:@"live_networking_bad"]];
  712. }
  713. }
  714. }));
  715. }
  716. #pragma mark ---- RCRTCRoomEventDelegate
  717. // 远端发布流
  718. - (void)didPublishStreams:(NSArray<RCRTCInputStream *> *)streams {
  719. [self subscribeRemoteResource:streams];
  720. NSLog(@"didPublishStreams --------");
  721. }
  722. // 订阅远端流
  723. - (void)subscribeRemoteResource:(NSArray<RCRTCInputStream *> *)streams {
  724. [self subscribeRemoteResource:streams isTiny:NO];
  725. }
  726. - (void)subscribeRemoteResource:(NSArray<RCRTCInputStream *> *)streams isTiny:(BOOL)isTiny {
  727. // 订阅房间中远端用户音视频流资源
  728. NSArray *tinyStream = isTiny ? streams : @[];
  729. NSArray *ordinaryStream = isTiny ? @[] : streams;
  730. MJWeakSelf;
  731. [self.room.localUser subscribeStream:ordinaryStream
  732. tinyStreams:tinyStream
  733. completion:^(BOOL isSuccess, RCRTCCode desc) {
  734. if (desc != RCRTCCodeSuccess) {
  735. NSString *errorStr = [NSString stringWithFormat:@"订阅远端流失败:%ld", (long) desc];
  736. [weakSelf MBPShow:errorStr];
  737. return;
  738. }
  739. }];
  740. [self renderSeatView];
  741. }
  742. - (void)didUnpublishStreams:(NSArray<RCRTCInputStream *> *)streams {
  743. NSLog(@"didUnPublishStreams --------");
  744. [self renderSeatView];
  745. }
  746. - (void)didOfflineUser:(RCRTCRemoteUser *)user {
  747. [self renderSeatView];
  748. }
  749. - (void)didJoinUser:(RCRTCRemoteUser *)user {
  750. NSLog(@"--didJoinUser-");
  751. // [self renderSeatView];
  752. }
  753. // 如果有远端用户离开房间
  754. - (void)didLeaveUser:(RCRTCRemoteUser *)user {
  755. NSLog(@"--didLeaveUser-");
  756. // 如果有人离开房间
  757. if ([self judgeContainMember:user.userId]) {
  758. [self removeMember:user.userId];
  759. [self refreshSeatApplyView];
  760. }
  761. [self renderSeatView];
  762. }
  763. // 远端用户切换身份
  764. - (void)didSwitchRoleWithUser:(RCRTCRemoteUser *)user roleType:(RCRTCLiveRoleType)roleType {
  765. if (roleType == RCRTCLiveRoleTypeBroadcaster) {
  766. if ([self judgeContainUser:user.userId] == NO) {
  767. [self.remoteMemberArray addObject:user];
  768. // 刷新连麦成员状态数据
  769. if ([self judgeContainMember:user.userId]) {
  770. // 更新状态
  771. [self updateMemberStatusConnecting:user.userId];
  772. [self refreshSeatApplyView];
  773. }
  774. }
  775. }
  776. else {
  777. [self removeMemberUserId:user.userId];
  778. // 刷新连麦成员数据
  779. if ([self judgeContainMember:user.userId]) {
  780. [self removeMember:user.userId];
  781. [self refreshSeatApplyView];
  782. }
  783. }
  784. [self renderSeatView];
  785. NSLog(@"didSwitchRoleWithUser --------");
  786. }
  787. - (BOOL)judgeContainUser:(NSString *)userId {
  788. BOOL containUser = NO;
  789. for (RCRTCRemoteUser *remoteUser in self.remoteMemberArray) {
  790. if ([remoteUser.userId isEqualToString:userId]) {
  791. containUser = YES;
  792. }
  793. }
  794. return containUser;
  795. }
  796. - (BOOL)getUserConnectStatus:(NSString *)userId {
  797. BOOL isConnected = NO;
  798. for (LiveSeatMember *member in self.seatApplyArray) {
  799. if ([member.userId isEqualToString:userId]) {
  800. return member.isConnected;
  801. }
  802. }
  803. return isConnected;
  804. }
  805. - (void)removeMemberUserId:(NSString *)userId {
  806. for (RCRTCRemoteUser *remoteUser in self.remoteMemberArray) {
  807. if ([remoteUser.userId isEqualToString:userId]) {
  808. [self.remoteMemberArray removeObject:remoteUser];
  809. return;
  810. }
  811. }
  812. }
  813. #pragma mark - KSChatInputBarControlDelegate
  814. // 根据inputBar 回调来修改页面布局
  815. - (void)onInputBarControlContentSizeChanged:(CGRect)frame withAnimationDuration:(CGFloat)duration andAnimationCurve:(UIViewAnimationCurve)curve {
  816. CGRect originFrame = _inputBar.frame;
  817. __weak __typeof(&*self)weakSelf = self;
  818. // 目前只使用y值即可 -- 只修改messageContentView高度,让展示消息view和输入框随之移动
  819. [UIView animateWithDuration:duration animations:^{
  820. [UIView setAnimationCurve:curve];
  821. [weakSelf.inputBar setFrame:CGRectMake(0, frame.origin.y - originFrame.size.height, originFrame.size.width, originFrame.size.height)];
  822. [UIView commitAnimations];
  823. }];
  824. }
  825. // 发送消息
  826. - (void)onTouchSendButton:(NSString *)text {
  827. [self touristSendMessage:text];
  828. }
  829. - (void)touristSendMessage:(NSString *)text {
  830. RCTextMessage *rcTextMessage = [RCTextMessage messageWithContent:text];
  831. MJWeakSelf;
  832. [self sendMessage:rcTextMessage displayMessage:YES callback:^(BOOL success) {
  833. [weakSelf setDefaultBottomViewStatus];
  834. }];
  835. }
  836. #pragma mark ---- RTC
  837. - (void)joinRTCLiveRoom {
  838. // 1.配置房间
  839. RCRTCRoomConfig *config = [[RCRTCRoomConfig alloc] init];
  840. config.roomType = RCRTCRoomTypeLive;
  841. config.liveType = RCRTCLiveTypeAudioVideo;
  842. config.roleType = RCRTCLiveRoleTypeBroadcaster;
  843. __weak typeof(self) weakSelf = self;
  844. [[RCRTCEngine sharedInstance] joinRoom:self.roomId config:config completion:^(RCRTCRoom * _Nullable room, RCRTCCode code) {
  845. dispatch_main_async_safe(^{
  846. __strong typeof(weakSelf) strongSelf = weakSelf;
  847. if (code == RCRTCCodeSignalServerNotConnect || code == RCRTCCodeParameterError || code == RCRTCCodeNotInRTCRoom || code == RCRTCCodeRTCTokenIsNull || code == RCRTCCodeHttpTimeoutError || code == RCRTCCodeHttpError || code == RCRTCCodeVoIPNotAvailable) {
  848. NSLog(@"%@",[NSString stringWithFormat:@"加入失败 code %ld",code]);
  849. [strongSelf MBPShow:@"加入直播间失败"];
  850. // [strongSelf.navigationController dismissViewControllerAnimated:YES completion:nil];
  851. }
  852. else {
  853. [strongSelf notiferJoinSuccessToServiceCallback:^{
  854. }];
  855. if (room) {
  856. strongSelf.room = room;
  857. strongSelf.room.delegate = self;
  858. }
  859. [strongSelf countMemberCount];
  860. // 发布本地视频流
  861. if (strongSelf.isPauseLive == NO && strongSelf.isEnterBackground == NO) {
  862. [strongSelf publishLocalStream];
  863. }
  864. }
  865. });
  866. }];
  867. }
  868. - (void)publishLocalStream {
  869. // 1.设置渲染视图渲染视图
  870. [[RCRTCEngine sharedInstance].defaultVideoStream setVideoView:self.localVideo.canvesView];
  871. // 2.设置视频流参数
  872. RCRTCVideoStreamConfig *videoConfig = [RCRTCEngine sharedInstance].defaultVideoStream.videoConfig;
  873. videoConfig.videoSizePreset = RCRTCVideoSizePreset1280x720;
  874. videoConfig.videoFps = RCRTCVideoFPS30;
  875. [[RCRTCEngine sharedInstance].defaultVideoStream setVideoConfig:videoConfig];
  876. // 3.开始视频采集
  877. [[RCRTCEngine sharedInstance].defaultVideoStream startCapture];
  878. [RCRTCEngine sharedInstance].defaultVideoStream.enableTinyStream = NO;
  879. [[RCRTCEngine sharedInstance].defaultAudioStream setAudioQuality:RCRTCAudioQualityMusicHigh Scenario:RCRTCAudioScenarioMusicChatRoom];
  880. // 发布本地视频流到房间
  881. MJWeakSelf;
  882. [self.room.localUser publishDefaultLiveStreams:^(BOOL isSuccess, RCRTCCode code, RCRTCLiveInfo * _Nullable liveInfo) {
  883. if (code == RCRTCCodeSuccess) {
  884. weakSelf.needPublishStream = NO;
  885. }
  886. else {
  887. [weakSelf MBPShow:@"视频流发布失败"];
  888. }
  889. }];
  890. }
  891. - (void)exitRoomCallback:(void(^)(BOOL success))callback {
  892. [[RCRTCEngine sharedInstance].defaultVideoStream stopCapture];
  893. _localVideo = nil;
  894. _preVideoView = nil;
  895. _room = nil;
  896. MJWeakSelf;
  897. [[RCRTCEngine sharedInstance] leaveRoom:^(BOOL isSuccess, RCRTCCode code) {
  898. dispatch_main_async_safe(^{
  899. [weakSelf sendLeaveMessageCallback:^(BOOL success) {
  900. callback(success);
  901. }];
  902. });
  903. }];
  904. }
  905. // 销毁房间
  906. - (void)distoryRoomCallback:(void(^)(BOOL success))callback {
  907. [[RCRTCEngine sharedInstance].defaultVideoStream stopCapture];
  908. _localVideo = nil;
  909. _preVideoView = nil;
  910. _room = nil;
  911. __weak typeof(self) weakSelf = self;
  912. [[RCRTCEngine sharedInstance] leaveRoom:^(BOOL isSuccess, RCRTCCode code) {
  913. dispatch_main_async_safe(^{
  914. __strong typeof(weakSelf) strongSelf = weakSelf;
  915. [strongSelf sendLeaveMessageCallback:^(BOOL success) {
  916. callback(success);
  917. }];
  918. });
  919. }];
  920. }
  921. #pragma mark -- 加入直播间和退出直播间回到服务
  922. - (void)notiferJoinSuccessToServiceCallback:(void(^)(void))callback {
  923. [KSNetworkingManager liveRoomJoinRoomRequest:KS_GET roomUid:self.roomId success:^(NSDictionary * _Nonnull dic) {
  924. callback();
  925. } faliure:^(NSError * _Nonnull error) {
  926. callback();
  927. }];
  928. }
  929. #pragma mark -- 退出直播间回掉服务
  930. - (void)quitNotiferService {
  931. [KSNetworkingManager LiveroomQuit:KS_POST success:^(NSDictionary * _Nonnull dic) {
  932. if ([dic integerValueForKey:@"errCode"] == 0) {
  933. NSLog(@"success");
  934. }
  935. else {
  936. NSLog(@"----- error %@", [dic stringValueForKey:@"errMsg"]);
  937. }
  938. } faliure:^(NSError * _Nonnull error) {
  939. }];
  940. }
  941. - (void)closeRoomNotiferService {
  942. [KSNetworkingManager destroyLiveRoomRequest:KS_GET roomUid:self.roomId success:^(NSDictionary * _Nonnull dic) {
  943. if ([dic integerValueForKey:@"errCode"] == 0) {
  944. NSLog(@"success");
  945. }
  946. else {
  947. NSLog(@"----- error %@", [dic stringValueForKey:@"errMsg"]);
  948. }
  949. } faliure:^(NSError * _Nonnull error) {
  950. }];
  951. }
  952. - (void)sendLeaveMessageCallback:(void(^)(BOOL success))callback {
  953. RCUserInfo *currentUserInfo = [RCIM sharedRCIM].currentUserInfo;
  954. KSLiveChatroomLeave *leaveMessage = [[KSLiveChatroomLeave alloc] init];
  955. leaveMessage.userId = currentUserInfo.userId;
  956. leaveMessage.userName = currentUserInfo.name;
  957. [self sendMessage:leaveMessage displayMessage:NO callback:^(BOOL success) {
  958. callback(success);
  959. }];
  960. }
  961. - (void)quitChatRoom {
  962. [[RCIMClient sharedRCIMClient] quitChatRoom:self.roomId success:^{
  963. NSLog(@"sucess log out--");
  964. } error:^(RCErrorCode status) {
  965. NSLog(@"RCErrorCode------%ld ", (long)status);
  966. }];
  967. }
  968. - (void)addConstMessage { // 固定欢迎词语
  969. RCTextMessage *statement = [RCTextMessage messageWithContent:@"欢迎进入直播课堂,请遵守相关法规,禁止传播低俗、暴力等不良信息。为孩子创造健康绿色的学习环境。"];
  970. statement.extra = @"statement";
  971. RCMessage *constMessage = [[RCMessage alloc] initWithType:ConversationType_CHATROOM targetId:self.roomId direction:MessageDirection_SEND content:statement];
  972. constMessage.content.senderUserInfo = [RCIM sharedRCIM].currentUserInfo;
  973. [self appendAndDisplayMessage:constMessage];
  974. }
  975. #pragma mark - views init
  976. /**
  977. * 接收到消息的回调
  978. */
  979. - (void)didReceiveMessageNotification:(NSNotification *)notification {
  980. NSDictionary *dic = notification.object;
  981. __block RCMessage *rcMessage = dic[@"message"];
  982. KSRCMessageModel *model = [[KSRCMessageModel alloc] initWithMessage:rcMessage];
  983. model.userInfo = rcMessage.content.senderUserInfo;
  984. if (model.conversationType == ConversationType_CHATROOM && [model.targetId isEqualToString:self.roomId]) {
  985. __weak typeof(&*self) __blockSelf = self;
  986. dispatch_main_async_safe((^{
  987. // 对赞消息拦截 ,展示动画,不插入数据源 ,对于seat消息 和踢出消息,弹出alert
  988. if (rcMessage) {
  989. // 👍🏻消息
  990. if ([rcMessage.content isMemberOfClass:[KSLiveChatroomLike class]]) {
  991. KSLiveChatroomLike *likeMessage = (KSLiveChatroomLike *)rcMessage.content;
  992. // 统计点赞数量
  993. __blockSelf.likeCount += likeMessage.counts;
  994. [__blockSelf countLikeMessageCount];
  995. }
  996. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomWelcome class]]) {
  997. // 过滤自己发送的欢迎消息
  998. if ([rcMessage.senderUserId isEqualToString:UserDefault(UIDKey)]) {
  999. return;
  1000. }
  1001. // 弹幕
  1002. RCUserInfo *userInfo = rcMessage.content.senderUserInfo;
  1003. NSString *userName = [userInfo.name stringByAppendingString:@""];
  1004. NSString *contentMsg = [NSString stringWithFormat:@"%@ 进入直播间",userName];
  1005. [__blockSelf showAnimationView:YES showMessag:contentMsg];
  1006. if ([__blockSelf judgeContainMember:rcMessage.senderUserId]) {
  1007. [__blockSelf removeMember:rcMessage.senderUserId];
  1008. [__blockSelf refreshSeatApplyView];
  1009. [__blockSelf renderSeatView];
  1010. }
  1011. }
  1012. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomMemberUp class]]) {
  1013. KSLiveChatroomMemberUp *member = (KSLiveChatroomMemberUp *)rcMessage.content;
  1014. __blockSelf.totalCount = member.count;
  1015. [__blockSelf countMemberCount];
  1016. }
  1017. // 用户退出消息
  1018. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomLeave class]]) {
  1019. KSLiveChatroomLeave *memberLeaveMsg = (KSLiveChatroomLeave *)rcMessage.content;
  1020. if ([__blockSelf judgeContainMember:memberLeaveMsg.userId]) {
  1021. [__blockSelf removeMember:memberLeaveMsg.userId];
  1022. [__blockSelf refreshSeatApplyView];
  1023. }
  1024. return;
  1025. }
  1026. // 连麦申请消息
  1027. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomSeatApply class]]) {
  1028. KSLiveChatroomSeatApply *seatApplyMessage = (KSLiveChatroomSeatApply *)rcMessage.content;
  1029. if (seatApplyMessage.type == SEATHANDLE_APPLY) { // 观众申请 加入申请中
  1030. LiveSeatMember *member = [[LiveSeatMember alloc] init];
  1031. member.avatar = seatApplyMessage.audienceAvatar;
  1032. member.name = seatApplyMessage.audienceName;
  1033. member.userId = seatApplyMessage.audienceId;
  1034. member.isConnected = NO;
  1035. if (![__blockSelf judgeContainMember:member.userId]) {
  1036. [__blockSelf.seatApplyArray addObject:member];
  1037. [__blockSelf refreshSeatApplyView];
  1038. }
  1039. }
  1040. else if (seatApplyMessage.type == SEATHANDLE_CANCELAPPLY) { // 观众取消申请 从申请中剔除
  1041. if ([__blockSelf judgeContainMember:seatApplyMessage.audienceId]) {
  1042. // 如果在申请中的状态
  1043. BOOL connectedStatus = [__blockSelf getUserConnectStatus:seatApplyMessage.audienceId];
  1044. if (connectedStatus == NO) {
  1045. NSLog(@"hahahhahah");
  1046. [__blockSelf removeMember:seatApplyMessage.audienceId];
  1047. [__blockSelf refreshSeatApplyView];
  1048. }
  1049. }
  1050. }
  1051. return;
  1052. }
  1053. // 连麦回复消息(暂不处理)
  1054. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomSeatResponse class]]) {
  1055. }
  1056. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomDownSeat class]]) { // 观众下麦,从列表中移除
  1057. KSLiveChatroomDownSeat *downSeatMessage = (KSLiveChatroomDownSeat *)rcMessage.content;
  1058. if ([__blockSelf judgeContainMember:downSeatMessage.audienceId]) {
  1059. [__blockSelf removeMember:downSeatMessage.audienceId];
  1060. [__blockSelf refreshSeatApplyView];
  1061. }
  1062. return;
  1063. }
  1064. else if ([rcMessage.content isMemberOfClass:[KSRCShopRushMessage class]]) {
  1065. KSRCShopRushMessage *rushMsg = (KSRCShopRushMessage *)rcMessage.content;
  1066. NSString *contentMsg = [NSString stringWithFormat:@"%@ 正在抢购",[NSString returnNoNullStringWithString:rushMsg.userName]];
  1067. [__blockSelf showAnimationView:NO showMessag:contentMsg];
  1068. }
  1069. else if ([rcMessage.content isMemberOfClass:[RCTextMessage class]]) {
  1070. [__blockSelf insertMessage:rcMessage userInfo:notification.userInfo];
  1071. }
  1072. else if ([rcMessage.content isMemberOfClass:[KSLiveChatroomClose class]]) { // 直播间已关闭
  1073. [__blockSelf MBPShow:@"直播已结束"];
  1074. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1075. // 退出直播间
  1076. [__blockSelf quitRoomBackPreView:NO];
  1077. });
  1078. return;
  1079. }
  1080. }
  1081. }));
  1082. }
  1083. }
  1084. - (void)insertMessage:(RCMessage *)rcMessage userInfo:(NSDictionary *)userInfo {
  1085. // left
  1086. NSDictionary *leftDic = userInfo;
  1087. if (leftDic && [leftDic[@"left"] isEqual:@(0)]) {
  1088. self.isNeedScrollToButtom = YES;
  1089. }
  1090. [self appendAndDisplayMessage:rcMessage];
  1091. }
  1092. - (BOOL)judgeContainMember:(NSString *)userId {
  1093. for (LiveSeatMember *obj in self.seatApplyArray) {
  1094. if ([obj.userId isEqualToString:userId]) {
  1095. return YES;
  1096. }
  1097. }
  1098. return NO;
  1099. }
  1100. - (NSInteger)queryConnectingCount {
  1101. NSInteger count = 0;
  1102. for (LiveSeatMember *member in self.seatApplyArray) {
  1103. if (member.isConnected) {
  1104. count++;
  1105. }
  1106. }
  1107. return count;
  1108. }
  1109. - (void)removeMember:(NSString *)memberId {
  1110. for (LiveSeatMember *member in self.seatApplyArray) {
  1111. if ([member.userId isEqualToString:memberId]) {
  1112. [self.seatApplyArray removeObject:member];
  1113. return;
  1114. }
  1115. }
  1116. }
  1117. - (void)updateMemberStatusWaiting:(NSString *)userId {
  1118. for (LiveSeatMember *obj in self.seatApplyArray) {
  1119. if ([obj.userId isEqualToString:userId]) {
  1120. obj.isConnected = NO;
  1121. return;
  1122. }
  1123. }
  1124. }
  1125. - (void)updateMemberStatusConnecting:(NSString *)userId {
  1126. for (LiveSeatMember *obj in self.seatApplyArray) {
  1127. if ([obj.userId isEqualToString:userId]) {
  1128. obj.isConnected = YES;
  1129. return;
  1130. }
  1131. }
  1132. }
  1133. - (void)refreshSeatApplyView {
  1134. if (self.seatApplyArray.count) {
  1135. [self showSeatTips:self.seatApplyArray.count];
  1136. }
  1137. else {
  1138. [self hideSeatTips];
  1139. }
  1140. [self.seatActionView refreshSeatApplyTable:self.seatApplyArray];
  1141. }
  1142. - (void)showSeatTips:(NSInteger)count {
  1143. NSString *countDesc = count >= 100 ? @"99+" : [NSString stringWithFormat:@"%zd",count];
  1144. [self.seatApplyTips configCountMessage:countDesc];
  1145. [self.seatApplyTips showTipsView];
  1146. }
  1147. - (void)hideSeatTips {
  1148. [self.seatApplyTips hideView];
  1149. }
  1150. // 发送人数同步消息
  1151. - (void)sendMemberCountMessage {
  1152. KSLiveChatroomMemberCount *syncMessage = [[KSLiveChatroomMemberCount alloc] init];
  1153. syncMessage.count = self.totalCount;
  1154. [self sendMessage:syncMessage displayMessage:NO callback:^(BOOL success) {
  1155. }];
  1156. }
  1157. #pragma mark ----- time manager delegate
  1158. - (void)formatTime:(NSString *)timeString {
  1159. self.headView.timeLabel.text = timeString;
  1160. }
  1161. - (void)timeLabelAddAnimation:(CABasicAnimation *)animation {
  1162. self.headView.timeLabel.textColor = HexRGB(0x2DC7AA);
  1163. // [self.headView.timeLabel.layer addAnimation:animation forKey:nil];
  1164. }
  1165. //- (void)quitClassroomNotifer {
  1166. // [self MBPShow:@"直播已结束!"];
  1167. // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1168. // [self quitRoomBackPreView:NO];
  1169. // });
  1170. //}
  1171. #pragma mark - gesture and button action
  1172. - (void)resetBottomGesture:(UIGestureRecognizer *)gestureRecognizer {
  1173. if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
  1174. [self setDefaultBottomViewStatus];
  1175. }
  1176. }
  1177. - (void)setDefaultBottomViewStatus {
  1178. [self.inputBar setInputBarStatus:KSBottomBarStatusDefault];
  1179. [self.inputBar setHidden:YES];
  1180. }
  1181. #pragma mark ---- sendMessage/showMessage
  1182. - (void)sendMessage:(RCMessageContent *)messageContent displayMessage:(BOOL)displayMessage callback:(void(^)(BOOL success))callback {
  1183. if (_roomId == nil) {
  1184. return;
  1185. }
  1186. messageContent.senderUserInfo = [RCIM sharedRCIM].currentUserInfo;
  1187. if (messageContent == nil) {
  1188. return;
  1189. }
  1190. __weak typeof(&*self) __weakself = self;
  1191. [KSChatroomMessageCenter sendChatMessage:self.roomId content:messageContent success:^(long messageId) {
  1192. dispatch_async(dispatch_get_main_queue(), ^{
  1193. RCMessage *message = [[RCMessage alloc] initWithType:ConversationType_CHATROOM targetId:__weakself.roomId direction:MessageDirection_SEND content:messageContent];
  1194. message.content.senderUserInfo = [RCIM sharedRCIM].currentUserInfo;
  1195. message.senderUserId = UserDefault(UIDKey);
  1196. if (displayMessage) {
  1197. [__weakself appendAndDisplayMessage:message];
  1198. }
  1199. [__weakself.inputBar clearInputView];
  1200. callback(YES);
  1201. });
  1202. } error:^(RCErrorCode errorCode, long messageId) {
  1203. if (errorCode == RC_CHATROOM_NOT_EXIST) {
  1204. dispatch_async(dispatch_get_main_queue(), ^{
  1205. [self MBPShow:@"聊天已被解散,请退出后重进。"];
  1206. });
  1207. }
  1208. dispatch_async(dispatch_get_main_queue(), ^{
  1209. [__weakself.inputBar clearInputView];
  1210. NSLog(@"发送失败,errorcode is: %ld",(long)errorCode);
  1211. callback(NO);
  1212. });
  1213. }];
  1214. }
  1215. /**
  1216. * 将消息加入本地数组
  1217. */
  1218. - (void)appendAndDisplayMessage:(RCMessage *)rcMessage {
  1219. if (!rcMessage) {
  1220. return;
  1221. }
  1222. KSRCMessageModel *model = [[KSRCMessageModel alloc] initWithMessage:rcMessage];
  1223. model.userInfo = rcMessage.content.senderUserInfo;
  1224. if (!model.userInfo) {
  1225. model.userInfo = [RCIMClient sharedRCIMClient].currentUserInfo;
  1226. }
  1227. if ([self appendMessageModel:model]) {
  1228. NSIndexPath *indexPath =
  1229. [NSIndexPath indexPathForItem:self.conversationDataRepository.count - 1
  1230. inSection:0];
  1231. if ([self.conversationMessageTableView numberOfRowsInSection:0] !=
  1232. self.conversationDataRepository.count - 1) {
  1233. return;
  1234. }
  1235. // view刷新
  1236. [self.conversationMessageTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
  1237. if ([self isAtTheBottomOfTableView] || self.isNeedScrollToButtom) {
  1238. [self scrollToBottomAnimated:YES];
  1239. self.isNeedScrollToButtom=NO;
  1240. }
  1241. }
  1242. return;
  1243. }
  1244. /**
  1245. * 如果当前会话没有这个消息id,把消息加入本地数组
  1246. */
  1247. - (BOOL)appendMessageModel:(KSRCMessageModel *)model {
  1248. if (!model.content) {
  1249. return NO;
  1250. }
  1251. //这里可以根据消息类型来决定是否显示,如果不希望显示直接return NO
  1252. //数量不可能无限制的大,这里限制收到消息过多时,就对显示消息数量进行限制。
  1253. //用户可以手动下拉更多消息,查看更多历史消息。
  1254. if (self.conversationDataRepository.count>100) {
  1255. // NSRange range = NSMakeRange(0, 1);
  1256. KSRCMessageModel *message = self.conversationDataRepository[0];
  1257. [[RCIMClient sharedRCIMClient]deleteMessages:@[@(message.messageId)]];
  1258. [self.conversationDataRepository removeObjectAtIndex:0];
  1259. [self.conversationMessageTableView reloadData];
  1260. }
  1261. [self.conversationDataRepository addObject:model];
  1262. return YES;
  1263. }
  1264. /**
  1265. * 判断消息是否在collectionView的底部
  1266. *
  1267. * @return 是否在底部
  1268. */
  1269. - (BOOL)isAtTheBottomOfTableView {
  1270. if (self.conversationMessageTableView.contentSize.height <= self.conversationMessageTableView.frame.size.height) {
  1271. return YES;
  1272. }
  1273. if(self.conversationMessageTableView.contentOffset.y +200 >= (self.conversationMessageTableView.contentSize.height - self.conversationMessageTableView.frame.size.height)) {
  1274. return YES;
  1275. }else{
  1276. return NO;
  1277. }
  1278. }
  1279. /**
  1280. * 消息滚动到底部
  1281. *
  1282. * @param animated 是否开启动画效果
  1283. */
  1284. - (void)scrollToBottomAnimated:(BOOL)animated {
  1285. if ([self.conversationMessageTableView numberOfSections] == 0) {
  1286. return;
  1287. }
  1288. NSUInteger finalRow = MAX(0, [self.conversationMessageTableView numberOfRowsInSection:0] - 1);
  1289. if (0 == finalRow) {
  1290. return;
  1291. }
  1292. NSIndexPath *finalIndexPath =
  1293. [NSIndexPath indexPathForItem:finalRow inSection:0];
  1294. [self.conversationMessageTableView scrollToRowAtIndexPath:finalIndexPath atScrollPosition:UITableViewScrollPositionTop animated:animated];
  1295. }
  1296. /*
  1297. #pragma mark - Navigation
  1298. // In a storyboard-based application, you will often want to do a little preparation before navigation
  1299. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  1300. // Get the new view controller using [segue destinationViewController].
  1301. // Pass the selected object to the new view controller.
  1302. }
  1303. */
  1304. #pragma mark ----- table data source
  1305. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  1306. return self.conversationDataRepository.count;
  1307. }
  1308. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  1309. KSRCMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
  1310. RCMessageContent *messageContent = model.content;
  1311. KSChatroomTextCell *cell = [tableView dequeueReusableCellWithIdentifier:@"KSChatroomTextCell"];
  1312. if(!cell){
  1313. cell = [[KSChatroomTextCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"KSChatroomTextCell"];
  1314. }
  1315. if ([messageContent isMemberOfClass:[RCTextMessage class]] || [messageContent isMemberOfClass:[KSLiveChatroomWelcome class]] || [messageContent isMemberOfClass:[KSLiveChatroomLike class]] || [messageContent isMemberOfClass:[KSLiveChatroomSeatApply class]] || [messageContent isMemberOfClass:[KSLiveChatroomSeatResponse class]]){
  1316. [cell setDataModel:model createrId:self.createrId];
  1317. }
  1318. return cell;
  1319. }
  1320. #pragma mark --- lazying
  1321. - (UIView *)livePageView {
  1322. if (!_livePageView) {
  1323. _livePageView = [[UIView alloc] init];
  1324. _livePageView.backgroundColor = HexRGB(0x25292e);
  1325. }
  1326. return _livePageView;
  1327. }
  1328. - (UIView *)liveVideoView {
  1329. if (!_liveVideoView) {
  1330. _liveVideoView = [[UIView alloc] init];
  1331. _liveVideoView.backgroundColor = [UIColor clearColor];
  1332. }
  1333. return _liveVideoView;
  1334. }
  1335. - (LiveRoomHeadView *)headView {
  1336. if (!_headView) {
  1337. _headView = [LiveRoomHeadView shareInstance];
  1338. }
  1339. return _headView;
  1340. }
  1341. - (LiveRoomBottomView *)bottomView {
  1342. if (!_bottomView) {
  1343. _bottomView = [LiveRoomBottomView shareInstance];
  1344. MJWeakSelf;
  1345. [_bottomView bottomClickAction:^(LIVEROOMACTION action) {
  1346. [weakSelf bottomViewAction:action];
  1347. }];
  1348. }
  1349. return _bottomView;
  1350. }
  1351. - (void)bottomViewAction:(LIVEROOMACTION)action {
  1352. switch (action) {
  1353. case LIVEROOMACTION_CHAT: // 聊天
  1354. {
  1355. [_inputBar setHidden:NO];
  1356. [_inputBar setInputBarStatus:KSBottomBarStatusKeyboard];
  1357. }
  1358. break;
  1359. case LIVEROOMACTION_SEAT: // 呼出连麦页面
  1360. {
  1361. [self displaySeatView];
  1362. }
  1363. break;
  1364. case LIVEROOMACTION_QUIT: // 退出
  1365. {
  1366. [self showMoreView];
  1367. }
  1368. default:
  1369. break;
  1370. }
  1371. }
  1372. - (void)showMoreView {
  1373. [self.view addSubview:self.moreView];
  1374. }
  1375. - (SeatTipsView *)seatApplyTips {
  1376. if (!_seatApplyTips) {
  1377. _seatApplyTips = [[SeatTipsView alloc] init];
  1378. }
  1379. return _seatApplyTips;
  1380. }
  1381. - (void)displaySeatView {
  1382. [self.view addSubview:self.seatCtrlView];
  1383. [self refreshSeatApplyView];
  1384. }
  1385. - (void)closeRoomAction {
  1386. MJWeakSelf;
  1387. self.alertView = [LiveRoomAlertView liveroomAlertWithTitle:@"结束直播后,不可再次开启" leftButtonTitle:@"取消" rightTitle:@"结束直播" inView:self.view cancel:^{
  1388. } confirm:^{
  1389. [weakSelf distoryRoomAction];
  1390. }];
  1391. }
  1392. - (void)pauseAction {
  1393. MJWeakSelf;
  1394. self.alertView = [LiveRoomAlertView liveroomAlertWithTitle:@"暂停后观众将无法看到视频画面" leftButtonTitle:@"取消" rightTitle:@"暂停直播" inView:self.view cancel:^{
  1395. } confirm:^{
  1396. [weakSelf quitRoomBackPreView:YES];
  1397. }];
  1398. }
  1399. - (void)distoryRoomAction {
  1400. MJWeakSelf;
  1401. [self distoryRoomCallback:^(BOOL success) {
  1402. [weakSelf closeRoomNotiferService];
  1403. [weakSelf quitChatRoom];
  1404. weakSelf.room = nil;
  1405. if (self.callback) {
  1406. self.callback();
  1407. }
  1408. [weakSelf.navigationController dismissViewControllerAnimated:YES completion:nil];
  1409. }];
  1410. }
  1411. - (void)pauseLiveActionBack:(BOOL)backPreView {
  1412. self.needPublishStream = NO;
  1413. KSRCPauseLiveMessage *pauseMsg = [[KSRCPauseLiveMessage alloc] init];
  1414. MJWeakSelf;
  1415. [self sendMessage:pauseMsg displayMessage:NO callback:^(BOOL success) {
  1416. if (backPreView) {
  1417. [weakSelf.moreView hideView];
  1418. weakSelf.isPauseLive = YES;
  1419. weakSelf.pageType = LIVEPAGE_PREVIEW;
  1420. [weakSelf.livePageView removeFromSuperview];
  1421. [weakSelf configEngine];
  1422. }
  1423. }];
  1424. [self.room.localUser unpublishDefaultLiveStreams:^(BOOL isSuccess, RCRTCCode code) {
  1425. if (isSuccess) {
  1426. }
  1427. }];
  1428. }
  1429. - (void)quitRoomBackPreView:(BOOL)backPreView {
  1430. if (backPreView) { // 暂停
  1431. [self pauseLiveActionBack:YES];
  1432. }
  1433. else {
  1434. if (self.pageType == LIVEPAGE_PREVIEW && self.isPauseLive == NO) {
  1435. self.room = nil;
  1436. self.preVideoView = nil;
  1437. [[RCRTCEngine sharedInstance].defaultVideoStream stopCapture];
  1438. if (self.callback) {
  1439. self.callback();
  1440. }
  1441. [self.navigationController dismissViewControllerAnimated:YES completion:nil];
  1442. }
  1443. else {
  1444. MJWeakSelf;
  1445. [self exitRoomCallback:^(BOOL success) {
  1446. // quit 接口
  1447. [weakSelf quitNotiferService];
  1448. [weakSelf quitChatRoom];
  1449. [weakSelf.moreView hideView];
  1450. if (self.callback) {
  1451. self.callback();
  1452. }
  1453. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1454. [weakSelf.navigationController dismissViewControllerAnimated:YES completion:nil];
  1455. });
  1456. }];
  1457. }
  1458. }
  1459. }
  1460. - (KSLiveStreamVideo *)localVideo {
  1461. if (!_localVideo) {
  1462. _localVideo = [KSLiveStreamVideo LocalStreamVideo];
  1463. }
  1464. return _localVideo;
  1465. }
  1466. - (NSMutableArray *)remoteMemberArray {
  1467. if (!_remoteMemberArray) {
  1468. _remoteMemberArray = [NSMutableArray array];
  1469. }
  1470. return _remoteMemberArray;
  1471. }
  1472. - (UIView *)messageContentView {
  1473. if (!_messageContentView) {
  1474. _messageContentView = [[UIView alloc] init];
  1475. [_messageContentView setBackgroundColor: [UIColor clearColor]];
  1476. }
  1477. return _messageContentView;
  1478. }
  1479. - (NSMutableArray<KSRCMessageModel *> *)conversationDataRepository {
  1480. if (!_conversationDataRepository) {
  1481. _conversationDataRepository = [NSMutableArray array];
  1482. }
  1483. return _conversationDataRepository;
  1484. }
  1485. - (KSChatInputBarControl *)inputBar {
  1486. if (!_inputBar) {
  1487. _inputBar = [[KSChatInputBarControl alloc] initWithStatus:KSBottomBarStatusDefault];
  1488. [_inputBar setDelegate:self];
  1489. }
  1490. return _inputBar;
  1491. }
  1492. - (UITableView *)conversationMessageTableView {
  1493. if (!_conversationMessageTableView) {
  1494. _conversationMessageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
  1495. _conversationMessageTableView.backgroundColor = [UIColor clearColor];
  1496. _conversationMessageTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  1497. _conversationMessageTableView.dataSource = self;
  1498. _conversationMessageTableView.delegate = self;
  1499. _conversationMessageTableView.rowHeight = UITableViewAutomaticDimension;
  1500. _conversationMessageTableView.estimatedRowHeight = 40.0f;
  1501. _conversationMessageTableView.showsVerticalScrollIndicator = NO;
  1502. [_conversationMessageTableView registerClass:[KSChatroomTextCell class] forCellReuseIdentifier:@"KSChatroomTextCell"];
  1503. }
  1504. return _conversationMessageTableView;
  1505. }
  1506. - (SeatContentView *)seatContainer {
  1507. if (!_seatContainer) {
  1508. _seatContainer = [[SeatContentView alloc] init];
  1509. }
  1510. return _seatContainer;
  1511. }
  1512. - (UIView *)seatCtrlView {
  1513. if (!_seatCtrlView) {
  1514. _seatCtrlView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, KPortraitWidth, KPortraitHeight)];
  1515. _seatCtrlView.backgroundColor = [UIColor clearColor];
  1516. self.gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideSeatCtrlView)];
  1517. self.gesture.delegate = self;
  1518. [_seatCtrlView addGestureRecognizer:self.gesture];
  1519. [_seatCtrlView addSubview:self.seatActionView];
  1520. UIView *headView = [[UIView alloc] initWithFrame:CGRectMake(0, KPortraitHeight - 300 - iPhoneXSafeBottomMargin - 16, KPortraitWidth, 16)];
  1521. headView.backgroundColor = [UIColor whiteColor];
  1522. [_seatCtrlView addSubview:headView];
  1523. if (@available(iOS 11.0, *)) {
  1524. headView.layer.cornerRadius = 8;
  1525. headView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner; // 左上圆角
  1526. }
  1527. else {
  1528. UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:headView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(8, 8)];
  1529. CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
  1530. maskLayer.frame = headView.bounds;
  1531. maskLayer.path = path.CGPath;
  1532. headView.layer.mask = maskLayer;
  1533. }
  1534. }
  1535. return _seatCtrlView;
  1536. }
  1537. - (void)hideSeatCtrlView {
  1538. if (_seatCtrlView) {
  1539. [_seatCtrlView removeFromSuperview];
  1540. }
  1541. }
  1542. - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
  1543. if (gestureRecognizer == self.gesture) {
  1544. if ([touch.view isDescendantOfView:self.seatActionView]) {
  1545. return NO;
  1546. }
  1547. }
  1548. return YES;
  1549. }
  1550. - (LiveSeatActionView *)seatActionView {
  1551. if (!_seatActionView) {
  1552. _seatActionView = [[LiveSeatActionView alloc] initWithFrame:CGRectMake(0, kScreenHeight - 300 - iPhoneXSafeBottomMargin, kScreenWidth, 300 + iPhoneXSafeBottomMargin)];
  1553. MJWeakSelf;
  1554. [_seatActionView opreationCallback:^(LIVESECONTROL action, LiveSeatMember * _Nonnull member) {
  1555. [weakSelf opreationSeatApplyAction:action member:member];
  1556. }];
  1557. }
  1558. return _seatActionView;
  1559. }
  1560. - (void)opreationSeatApplyAction:(LIVESECONTROL)control member:(LiveSeatMember *)member {
  1561. switch (control) {
  1562. case LIVESECONTROL_ALLOW: // 允许连麦
  1563. {
  1564. RCChatroomSeatsControl *message = [[RCChatroomSeatsControl alloc] init];
  1565. message.userId = self.createrId;
  1566. message.userName = self.createrName;
  1567. message.seatBan = NO;
  1568. [self sendMessage:message displayMessage:NO callback:^(BOOL success) {
  1569. }];
  1570. }
  1571. break;
  1572. case LIVESECONTROL_FORBIDDEN: // 禁止连麦
  1573. {
  1574. RCChatroomSeatsControl *message = [[RCChatroomSeatsControl alloc] init];
  1575. message.userId = self.createrId;
  1576. message.userName = self.createrName;
  1577. message.seatBan = YES;
  1578. MJWeakSelf;
  1579. [self sendMessage:message displayMessage:NO callback:^(BOOL success) {
  1580. [weakSelf removeSeatMember:YES];
  1581. }];
  1582. }
  1583. break;
  1584. case LIVESECONTROL_APPROVE: // 上麦
  1585. {
  1586. if (member) {
  1587. // 判断连麦人数超过4人 无法点击
  1588. NSInteger count = [self queryConnectingCount];
  1589. if (count >= 4) {
  1590. [self MBPShow:@"最多可连麦4人"];
  1591. return;
  1592. }
  1593. KSLiveChatroomSeatResponse *responseMessage = [[KSLiveChatroomSeatResponse alloc] init];
  1594. responseMessage.type = SEATRESPONSE_TEACHERAPPROVE;
  1595. responseMessage.teacherId = self.createrId;
  1596. responseMessage.teacherName = self.createrName;
  1597. responseMessage.audienceId = member.userId;
  1598. responseMessage.audienceName = member.name;
  1599. MJWeakSelf;
  1600. [self sendMessage:responseMessage displayMessage:NO callback:^(BOOL success) {
  1601. [weakSelf refreshSeatArrayRemoveMember:NO member:member];
  1602. }];
  1603. }
  1604. }
  1605. break;
  1606. case LIVESECONTROL_DOWNSEAT: // 下麦
  1607. {
  1608. if (member) {
  1609. KSLiveChatroomSeatApply *kickSeatMessage = [[KSLiveChatroomSeatApply alloc] init];
  1610. kickSeatMessage.type = SEATHANDLE_KICKSEAT;
  1611. kickSeatMessage.teacherId = self.createrId;
  1612. kickSeatMessage.teacherName = self.createrName;
  1613. kickSeatMessage.audienceId = member.userId;
  1614. kickSeatMessage.audienceName = member.name;
  1615. MJWeakSelf;
  1616. [self sendMessage:kickSeatMessage displayMessage:NO callback:^(BOOL success) {
  1617. [weakSelf refreshSeatArrayRemoveMember:YES member:member];
  1618. }];
  1619. }
  1620. }
  1621. break;
  1622. case LIVESECONTROL_DOWNSEATALL:
  1623. {
  1624. KSDownSeatAllMessage *message = [[KSDownSeatAllMessage alloc] init];
  1625. MJWeakSelf;
  1626. [self sendMessage:message displayMessage:NO callback:^(BOOL success) {
  1627. [weakSelf removeSeatMember:NO];
  1628. }];
  1629. }
  1630. break;
  1631. case LIVESECONTROL_REFUSEALL:
  1632. {
  1633. KSRejectAllSeatMessage *message = [[KSRejectAllSeatMessage alloc] init];
  1634. MJWeakSelf;
  1635. [self sendMessage:message displayMessage:NO callback:^(BOOL success) {
  1636. [weakSelf removeSeatMember:YES];
  1637. }];
  1638. }
  1639. break;
  1640. default:
  1641. break;
  1642. }
  1643. }
  1644. - (void)removeSeatMember:(BOOL)isApply {
  1645. NSMutableArray *seatArray = [self.seatApplyArray mutableCopy];
  1646. if (isApply) {
  1647. NSMutableArray *array = [NSMutableArray array];
  1648. for (LiveSeatMember *member in seatArray) {
  1649. if (member.isConnected == YES) {
  1650. [array addObject:member];
  1651. }
  1652. }
  1653. self.seatApplyArray = [NSMutableArray arrayWithArray:array];
  1654. }
  1655. else {
  1656. NSMutableArray *array = [NSMutableArray array];
  1657. for (LiveSeatMember *member in seatArray) {
  1658. if (member.isConnected == NO) {
  1659. [array addObject:member];
  1660. }
  1661. }
  1662. self.seatApplyArray = [NSMutableArray arrayWithArray:array];
  1663. }
  1664. [self refreshSeatApplyView];
  1665. }
  1666. - (void)refreshSeatArrayRemoveMember:(BOOL)isRemove member:(LiveSeatMember *)member {
  1667. if (isRemove) {
  1668. [self.seatApplyArray removeObject:member];
  1669. }
  1670. else {
  1671. member.isConnected = YES;
  1672. if (![self judgeContainMember:member.userId]) {
  1673. [self.seatApplyArray addObject:member];
  1674. NSLog(@"hah--------");
  1675. }
  1676. }
  1677. [self refreshSeatApplyView];
  1678. }
  1679. - (NSMutableArray *)seatApplyArray {
  1680. if (!_seatApplyArray) {
  1681. _seatApplyArray = [NSMutableArray array];
  1682. }
  1683. return _seatApplyArray;
  1684. }
  1685. - (LiveMoreDisplayView *)moreView {
  1686. if (!_moreView) {
  1687. _moreView = [LiveMoreDisplayView shareInstance];
  1688. _moreView.frame = CGRectMake(0, 0, KPortraitWidth, KPortraitHeight);
  1689. MJWeakSelf;
  1690. [_moreView operationQuitAction:^(BOOL isCloseRoom) {
  1691. [weakSelf leaveRoom:isCloseRoom];
  1692. }];
  1693. }
  1694. return _moreView;
  1695. }
  1696. - (void)leaveRoom:(BOOL)closeRoom {
  1697. if (closeRoom) {
  1698. [self closeRoomAction];
  1699. }
  1700. else {
  1701. [self pauseAction];
  1702. }
  1703. }
  1704. - (LiveroomTimeManager *)timeManager {
  1705. if (!_timeManager) {
  1706. _timeManager = [[LiveroomTimeManager alloc] initWithDelegate:self];
  1707. }
  1708. return _timeManager;
  1709. }
  1710. - (NSInteger)getCloseTime {
  1711. return self.expiredCloseMinute;
  1712. }
  1713. - (void)dealloc {
  1714. if (_timeManager) {
  1715. [_timeManager stopDurationTimer];
  1716. }
  1717. [[NSNotificationCenter defaultCenter] removeObserver:self];
  1718. if (_timer) {
  1719. dispatch_source_cancel(_timer);
  1720. _timer = nil;
  1721. }
  1722. }
  1723. - (dispatch_source_t)timer {
  1724. if (!_timer) {
  1725. _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
  1726. uint64_t interval = (uint64_t)(10 * NSEC_PER_SEC);
  1727. dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, interval, 0);
  1728. MJWeakSelf;
  1729. dispatch_source_set_event_handler(_timer, ^{
  1730. dispatch_async(dispatch_get_main_queue(), ^{
  1731. [weakSelf syncLikeCount];
  1732. });
  1733. });
  1734. }
  1735. return _timer;
  1736. }
  1737. - (void)showAnimationView:(BOOL)isJoinRoom showMessag:(NSString *)message {
  1738. if (self.animationView && self.animationView.isShow) {
  1739. return;
  1740. }
  1741. else {
  1742. if (self.pageType == LIVEPAGE_PREVIEW) {
  1743. return;
  1744. }
  1745. ANIMATIONTYPE type = isJoinRoom ? ANIMATIONTYPE_JOIN : ANIMATIONTYPE_RUSH;
  1746. self.animationView = [[LiveAnimationView alloc] initWithTitle:message animationType:type];
  1747. [self.view addSubview:self.animationView];
  1748. [self.animationView mas_makeConstraints:^(MASConstraintMaker *make) {
  1749. make.left.right.mas_equalTo(self.view);
  1750. make.width.mas_equalTo(KPortraitWidth);
  1751. make.height.mas_equalTo(24.0f);
  1752. make.bottom.mas_equalTo(self.messageContentView.mas_top).offset(-6);
  1753. }];
  1754. [self.view bringSubviewToFront:self.animationView];
  1755. NSLog(@"------- start animation ");
  1756. MJWeakSelf;
  1757. [self.animationView startAnimationEndCallback:^{
  1758. NSLog(@"----- hide ");
  1759. weakSelf.animationView.isShow = YES;
  1760. weakSelf.animationView = nil;
  1761. }];
  1762. }
  1763. }
  1764. #pragma mark ---- 同步点赞数据
  1765. - (void)syncLikeCount {
  1766. if (self.isOtherLogin) {
  1767. return;
  1768. }
  1769. [KSNetworkingManager syncLikeRequest:KS_GET likeNum:self.likeCount roomUid:self.roomId success:^(NSDictionary * _Nonnull dic) {
  1770. if ([dic integerValueForKey:@"code"] == 200 && [dic boolValueForKey:@"status"]) {
  1771. [self notiferLikeStatusToAudience];
  1772. }
  1773. } faliure:^(NSError * _Nonnull error) {
  1774. }];
  1775. }
  1776. - (void)notiferLikeStatusToAudience {
  1777. RCChatroomLikeCount *likeCountMsg = [[RCChatroomLikeCount alloc] init];
  1778. likeCountMsg.count = self.likeCount;
  1779. [self sendMessage:likeCountMsg displayMessage:NO callback:^(BOOL success) {
  1780. }];
  1781. }
  1782. /*
  1783. #pragma mark - Navigation
  1784. // In a storyboard-based application, you will often want to do a little preparation before navigation
  1785. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  1786. // Get the new view controller using [segue destinationViewController].
  1787. // Pass the selected object to the new view controller.
  1788. }
  1789. */
  1790. @end