LiveRoomViewController.m 67 KB

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