TXLiveRoomViewController.m 100 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840
  1. //
  2. // TXLiveRoomViewController.m
  3. // StudentDaya
  4. //
  5. // Created by 王智 on 2023/2/21.
  6. // Copyright © 2023 DayaMusic. All rights reserved.
  7. //
  8. #import "TXLiveRoomViewController.h"
  9. #import "TXLiteAVSDK_Professional/TXLiteAVSDK.h"
  10. #import "KSLiveEmptyView.h"
  11. #import "TXLiveVideoView.h"
  12. #import "LiveRoomHeadView.h"
  13. #import "LiveRoomBottomView.h"
  14. #import <Lottie/Lottie.h>
  15. #import "KSChatInputBarControl.h"
  16. #import "TXLiveChatListCell.h"
  17. #import "AppDelegate.h"
  18. #import <UIDevice+TFDevice.h>
  19. #import "LiveRoomLikeLayer.h"
  20. #import "LiveAlertView.h"
  21. #import "KSShopCardView.h"
  22. #import "KSLiveWebViewController.h"
  23. #import "KSLiveBadNetView.h"
  24. #import "KSLiveEndView.h"
  25. #import "LiveAnimationView.h"
  26. #import "LiveApplyView.h"
  27. #import "LiveApplyingView.h"
  28. #import <ImSDK_Plus/ImSDK_Plus.h>
  29. #import "TXIMLinsenter.h"
  30. #import "TXLiveMessageModel.h"
  31. #import "TXLiveURLUtils.h"
  32. #import "TXLiveMessageCenter.h"
  33. #import "KSDragWindowManager.h"
  34. #import "TXLiveLoadingView.h"
  35. #import "TXUISeatMember.h"
  36. #import "TXSeatContainerView.h"
  37. #import "LiveRoomConfirmAlert.h"
  38. #import "LiveModuleService.h"
  39. #define AUTOHIDE_TIME (10.0f)
  40. typedef NS_ENUM(NSInteger, MICSTATUS) {
  41. MICSTATUS_NOMAL, // 正常状态
  42. MICSTATUS_WAITING, // 申请中
  43. MICSTATUS_CONNECTING, // 连麦中
  44. };
  45. @interface TXLiveRoomViewController ()<UIGestureRecognizerDelegate,KSChatInputBarControlDelegate,UITableViewDataSource,UITableViewDelegate,V2TXLivePlayerObserver,TRTCCloudDelegate>
  46. // 是否是观众
  47. @property (nonatomic, assign) BOOL isAudience;
  48. // rtc 连麦时使用
  49. @property (nonatomic, strong) TRTCCloud *trtcCloud;
  50. @property (nonatomic, strong) V2TXLivePlayer *livePlayer;
  51. @property (nonatomic, strong) LiveRoomHeadView *headView;
  52. @property (nonatomic, strong) LiveRoomBottomView *bottomView;
  53. // 视频主窗口容器
  54. @property (nonatomic, strong) UIView *videoContainerView;
  55. // 主屏videoView
  56. @property (nonatomic, strong) UIView *videoView;
  57. // 画面渲染的画面
  58. @property (nonatomic, strong) TXLiveVideoView *videoRenderView;
  59. // 切换按钮
  60. @property (nonatomic, strong) UIButton *switchButton;
  61. @property (nonatomic, strong) LOTAnimationView *animationView;
  62. /// 连麦的视图
  63. @property (nonatomic, strong) TXSeatContainerView *seatContainer;
  64. // 聊天UI
  65. /*!
  66. 消息列表CollectionView和输入框都在这个view里
  67. */
  68. @property(nonatomic, strong) UIView *messageContentView;
  69. /*!
  70. 会话页面的TableView
  71. */
  72. @property (nonatomic, strong) UITableView *conversationMessageTableView;
  73. /*!
  74. 聊天内容的消息Cell数据模型的数据源
  75. @discussion 数据源中存放的元素为消息Cell的数据模型,即RCDLiveMessageModel对象。
  76. */
  77. @property(nonatomic, strong) NSMutableArray<TXLiveMessageModel *> *conversationDataRepository;
  78. /**
  79. 输入工具栏
  80. */
  81. @property(nonatomic,strong) KSChatInputBarControl *inputBar;
  82. /**
  83. * 是否需要滚动到底部
  84. */
  85. @property(nonatomic, assign) BOOL isNeedScrollToButtom;
  86. /**
  87. 上次点赞按钮点击时间
  88. */
  89. @property(nonatomic, assign) NSTimeInterval lastClickPraiseTime;
  90. @property (nonatomic, assign) BOOL landScape;
  91. @property (nonatomic, assign) BOOL isHiddenHeadView;
  92. // 连麦状态
  93. @property (nonatomic, assign) MICSTATUS micStatus;
  94. @property (nonatomic, strong) LiveApplyView *applyView; // 未申请
  95. @property (nonatomic, strong) LiveApplyingView *applyingView; // 申请中
  96. @property (nonatomic, assign) BOOL isCreaterVideoEnable;
  97. @property (nonatomic, assign) BOOL isCloseRoom; // 是否直播结束
  98. /// 提示窗
  99. @property (nonatomic, strong) LiveAlertView *alertView;
  100. @property (nonatomic, strong) KSShopCardView *cardView;
  101. @property (nonatomic, assign) NSTimeInterval lastSendMsgTime;
  102. @property (nonatomic, strong) LiveAnimationView *enterAnimationView;
  103. @property (nonatomic, strong) KSLiveEndView *endView; // 直播结束显示的画面
  104. @property (nonatomic, strong) KSLiveEmptyView *emptyView;
  105. @property (nonatomic, strong) KSLiveBadNetView *netBadView;
  106. /// 是否在切换身份
  107. @property (nonatomic, assign) BOOL isTransferRole;
  108. @property (nonatomic, assign) BOOL hasSendWelcomeMessage;
  109. // 远端连麦用户
  110. @property (nonatomic, strong) NSMutableArray *remoteMemberArray;
  111. @property (nonatomic, assign) BOOL isPauseLive;
  112. @property (nonatomic, strong) TXLiveLoadingView *loadingView;
  113. @property (nonatomic, assign) BOOL hasShowSuspendView;
  114. @property (nonatomic, assign) BOOL muteRoomMic;
  115. @property (nonatomic, assign) BOOL micEnable;
  116. @property (nonatomic, strong) LiveRoomConfirmAlert *closeAlert;
  117. @property (nonatomic, assign) BOOL isConnectedFailed;
  118. #pragma mark --- 时间统计
  119. // 后端返回进入教室时间戳 ms
  120. @property (nonatomic, assign) NSTimeInterval enterTime;
  121. // 进入教室时间date
  122. @property (nonatomic, strong) NSDate *enterDate;
  123. @end
  124. // 用于记录点赞消息连续点击的次数
  125. static int clickPraiseBtnTimes = 0;
  126. @implementation TXLiveRoomViewController
  127. - (instancetype)init {
  128. self = [super init];
  129. if (self) {
  130. [self defaultConfig];
  131. [self default3AConfig];
  132. }
  133. return self;
  134. }
  135. - (void)default3AConfig {
  136. self.ANS = 0;
  137. self.AEC = 100;
  138. self.AGC = 0;
  139. }
  140. - (void)joinChatRoom {
  141. if (TXIM_LINSENTER.loginIMSuccess == NO) {
  142. [TXIM_LINSENTER TXIMLoginWithUserId:UserDefault(IM_USERID) sig:self.UserSig callback:^(BOOL isSuccess, NSString * _Nullable msg) {
  143. [self liveConversationJoin];
  144. }];
  145. }
  146. else {
  147. [self liveConversationJoin];
  148. }
  149. }
  150. - (void)liveConversationJoin {
  151. [[V2TIMManager sharedInstance] joinGroup:self.roomId msg:nil succ:^{
  152. dispatch_async(dispatch_get_main_queue(), ^{
  153. [self addConstMessage];
  154. [self sendWelcomeMessage];
  155. [self getGroupAttrInfo];
  156. [self getGroupCount];
  157. });
  158. } fail:^(int code, NSString *desc) {
  159. NSLog(@"");
  160. }];
  161. }
  162. - (void)getGroupAttrInfo {
  163. [[V2TIMManager sharedInstance] getGroupAttributes:self.roomId keys:nil succ:^(NSMutableDictionary<NSString *,NSString *> *groupAttributeList) {
  164. NSLog(@"");
  165. [self evaluateGroupInfo:groupAttributeList];
  166. } fail:^(int code, NSString *desc) {
  167. }];
  168. }
  169. - (void)getGroupCount {
  170. [[V2TIMManager sharedInstance] getGroupCounters:self.roomId keys:nil succ:^(NSDictionary<NSString *,NSNumber *> *groupCounters) {
  171. NSLog(@"");
  172. [self evaluateGroupCount:groupCounters];
  173. } fail:^(int code, NSString *desc) {
  174. }];
  175. }
  176. - (void)addConstMessage {
  177. TXConstMessage *statement = [[TXConstMessage alloc] init];
  178. statement.userInfo = [self getCurrentUser];
  179. statement.text = @"欢迎进入直播课堂,请遵守相关法规,禁止传播低俗、暴力等不良信息。为孩子创造健康绿色的学习环境。";
  180. [self appendAndDisplayMessage:statement];
  181. }
  182. - (void)sendWelcomeMessage {
  183. TXLiveMessageWelcome *welcomeMessage = [[TXLiveMessageWelcome alloc] init];
  184. welcomeMessage.userInfo = [self getCurrentUser];
  185. MJWeakSelf;
  186. [self sendCustomMessage:welcomeMessage priority:V2TIM_PRIORITY_DEFAULT displayMessage:NO callback:^(BOOL success) {
  187. weakSelf.hasSendWelcomeMessage = YES;
  188. [weakSelf showEnterMessageAndJoinRoom];
  189. }];
  190. }
  191. - (void)showEnterMessageAndJoinRoom {
  192. NSString *contentString = [NSString stringWithFormat:@"%@ 进入直播间",UserDefault(NicknameKey)];
  193. [self showAnimationView:YES showMessag:contentString];
  194. }
  195. - (void)joinLiveRoom {
  196. [self changeEmptyStatusView];
  197. [self resetNetTips];
  198. [self startPlay];
  199. }
  200. - (void)viewWillAppear:(BOOL)animated {
  201. [super viewWillAppear:animated];
  202. [UIApplication sharedApplication].idleTimerDisabled = YES;
  203. [IQKeyboardManager sharedManager].enableAutoToolbar = NO;
  204. [IQKeyboardManager sharedManager].enable = NO;
  205. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
  206. // 重新渲染view
  207. KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
  208. if (manager.hasShowWindow) {
  209. [manager resignDragWindow];
  210. }
  211. if (self.hasShowSuspendView) {
  212. [self.videoView addSubview:self.videoRenderView];
  213. [self.videoRenderView mas_updateConstraints:^(MASConstraintMaker *make) {
  214. make.left.right.bottom.top.mas_equalTo(self.videoView);
  215. }];
  216. _hasShowSuspendView = NO;
  217. }
  218. if (self.bottomView.isMute == NO) {
  219. [self muteVoiceAction];
  220. }
  221. }
  222. - (void)viewWillDisappear:(BOOL)animated {
  223. [super viewWillDisappear:animated];
  224. [UIApplication sharedApplication].idleTimerDisabled = NO;
  225. if (self.landScape) {
  226. [self changeOrientation:NO];
  227. }
  228. [IQKeyboardManager sharedManager].enableAutoToolbar = YES;
  229. [IQKeyboardManager sharedManager].enable = YES;
  230. if (@available(iOS 13.0, *)) {
  231. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDarkContent;
  232. } else {
  233. // Fallback on earlier versions
  234. [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
  235. }
  236. }
  237. - (void)defaultConfig {
  238. self.enableChat = YES;
  239. self.enableSeat = YES;
  240. self.enableLike = YES;
  241. self.needSwitchButton = NO;
  242. self.micStatus = MICSTATUS_NOMAL;
  243. self.isCreateInRoom = NO; // 默认主讲人不在房间
  244. self.blacklistFlag = NO;
  245. self.isCloseRoom = NO;
  246. self.lastClickPraiseTime = 0;
  247. self.isAudience = YES;
  248. self.isCreaterVideoEnable = YES;
  249. }
  250. - (void)viewDidLoad {
  251. [super viewDidLoad];
  252. // Do any additional setup after loading the view.
  253. self.ks_prefersNavigationBarHidden = YES;
  254. [self setupUI];
  255. [self registerNotification];
  256. [self joinChatRoom];
  257. [self joinLiveRoom];
  258. [self notiferJoinSuccessToServiceCallback:^{
  259. }];
  260. }
  261. - (void)registerNotification {
  262. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMessageNotification:) name:OnReceiveTXLiveMessageNotification object:nil];
  263. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(liveroomLogOut) name:@"liveroomLogout" object:nil];
  264. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(IMConnetedCallback) name:@"TXIMConnected" object:nil];
  265. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(muteAllAudio) name:@"muteLiveAudio" object:nil];
  266. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveGroupMessage:) name:OnReceiveTXLiveGroupNotification object:nil];
  267. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveGroupCountMessage:) name:OnReceiveTXLiveGroupCountNotification object:nil];
  268. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkTips) name:@"networkError" object:nil];
  269. }
  270. - (void)networkTips {
  271. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"直播已断开,请检查您的网络"];
  272. self.isConnectedFailed = YES;
  273. }
  274. - (void)IMConnetedCallback {
  275. self.isConnectedFailed = NO;
  276. dispatch_main_async_safe(^{
  277. // IM 连接成功回调
  278. if (self.hasSendWelcomeMessage) { // 如果已经发送了进入消息
  279. // 查询是否直播间开启
  280. [LiveModuleService imLiveBroadcastRoomQueryRoomRequest:KS_GET roomId:self.roomId success:^(NSDictionary * _Nonnull dic) {
  281. if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
  282. // 回调信息
  283. MJWeakSelf;
  284. [self notiferJoinSuccessToServiceCallback:^{
  285. [weakSelf sendWelcomeMessage];
  286. [weakSelf checkPlayerStatus];
  287. }];
  288. }
  289. else {
  290. [LOADING_MANAGER MBShowAUTOHidingInWindow:MESSAGEKEY];
  291. [self quitRoom];
  292. }
  293. } faliure:^(NSError * _Nonnull error) {
  294. }];
  295. // 查询群属性
  296. [self getGroupAttrInfo];
  297. }
  298. });
  299. }
  300. - (void)checkPlayerStatus {
  301. if (self.isCloseRoom) {
  302. return;
  303. }
  304. if (self.isAudience) {
  305. if (self.livePlayer.isPlaying == NO) {
  306. [self resetNetTips];
  307. [self startPlay];
  308. }
  309. }
  310. else {
  311. [self connectHostWithStatus:NO];
  312. }
  313. }
  314. - (void)liveroomLogOut {
  315. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"该账号在其他设备上登录"];
  316. [self quitRoom];
  317. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  318. [[NSNotificationCenter defaultCenter] postNotificationName:@"backLoginView" object:nil];
  319. });
  320. }
  321. - (void)muteAllAudio {
  322. [self muteVoiceAction];
  323. }
  324. #pragma mark ----- // 观众上下麦
  325. - (void)connectHostWithStatus:(BOOL)isConnect {
  326. // 如果直播结束 不转换身份
  327. if (self.isCloseRoom == YES) {
  328. return;
  329. }
  330. // 转换中,不再处理转换身份
  331. if (self.isTransferRole) {
  332. return;
  333. }
  334. if (isConnect && self.isAudience == NO) {
  335. self.micStatus = MICSTATUS_CONNECTING;
  336. return;
  337. }
  338. else if (isConnect == NO && self.isAudience) {
  339. self.micStatus = MICSTATUS_NOMAL;
  340. return;
  341. }
  342. self.isTransferRole = YES;
  343. self.micEnable = YES;
  344. // 清理当前视图
  345. [self removeSeatContainer];
  346. self.remoteMemberArray = [NSMutableArray array];
  347. // 移除状态视图
  348. if (isConnect) {
  349. [self stopPlay];
  350. [self hideTipsView];
  351. if (self.isCreaterVideoEnable) {
  352. [self hideEmptyView];
  353. }
  354. self.isAudience = NO;
  355. [self enterLiveRTCRoom];
  356. self.isTransferRole = NO;
  357. self.micStatus = MICSTATUS_CONNECTING;
  358. }
  359. else {
  360. self.isAudience = YES;
  361. if (self.isPauseLive == NO) {
  362. [self startPlay];
  363. }
  364. if (_trtcCloud) {
  365. [self.trtcCloud stopAllRemoteView];
  366. [self.trtcCloud stopLocalAudio];
  367. [self.trtcCloud exitRoom];
  368. _trtcCloud = nil;
  369. [TRTCCloud destroySharedInstance];
  370. }
  371. self.micStatus = MICSTATUS_NOMAL;
  372. self.isTransferRole = NO;
  373. }
  374. [self muteVoiceAction];
  375. }
  376. - (void)setupUI {
  377. self.view.backgroundColor = HexRGB(0x25292e);
  378. [self.view addSubview:self.emptyView];
  379. [self.view addSubview:self.endView];
  380. [self.view addSubview:self.videoContainerView];
  381. [self.videoContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
  382. make.left.right.top.bottom.mas_equalTo(self.view);
  383. }];
  384. [self.videoContainerView addSubview:self.videoView];
  385. // 异常的view
  386. [self.videoContainerView addSubview:self.netBadView];
  387. // 按钮和视频的布局
  388. if (self.needSwitchButton) {
  389. [self.videoView mas_makeConstraints:^(MASConstraintMaker *make) {
  390. make.left.right.mas_equalTo(self.videoContainerView);
  391. if (@available(iOS 11.0, *)) {
  392. make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(160);
  393. } else {
  394. // Fallback on earlier versions
  395. make.top.mas_equalTo(self.videoContainerView.mas_top).offset(160);
  396. }
  397. CGFloat rate = 9 / 16.0f;
  398. make.height.mas_equalTo(self.videoContainerView.mas_width).multipliedBy(rate);
  399. make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
  400. make.width.height.lessThanOrEqualTo(self.videoContainerView);
  401. }];
  402. // 显示切换按钮
  403. [self.videoContainerView addSubview:self.switchButton];
  404. [self.switchButton mas_makeConstraints:^(MASConstraintMaker *make) {
  405. make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-10);
  406. make.width.height.mas_equalTo(33);
  407. make.bottom.mas_equalTo(self.videoView.mas_bottom).offset(-10);
  408. }];
  409. self.switchButton.hidden = NO;
  410. }
  411. else {
  412. [self.videoView mas_makeConstraints:^(MASConstraintMaker *make) {
  413. make.left.top.bottom.right.mas_equalTo(self.videoContainerView);
  414. }];
  415. }
  416. // 添加视频预览图
  417. [self.videoView addSubview:self.videoRenderView];
  418. [self.videoRenderView mas_makeConstraints:^(MASConstraintMaker *make) {
  419. make.left.right.top.bottom.mas_equalTo(self.videoView);
  420. }];
  421. [self.emptyView mas_makeConstraints:^(MASConstraintMaker *make) {
  422. make.left.right.top.bottom.mas_equalTo(self.videoView);
  423. }];
  424. self.emptyView.hidden = YES;
  425. // 直播结束的画面
  426. [self.endView mas_makeConstraints:^(MASConstraintMaker *make) {
  427. make.left.right.top.bottom.mas_equalTo(self.videoView);
  428. }];
  429. self.endView.hidden = YES;
  430. [self.netBadView mas_makeConstraints:^(MASConstraintMaker *make) {
  431. make.left.right.top.bottom.mas_equalTo(self.videoView);
  432. }];
  433. self.netBadView.hidden = YES;
  434. [self.videoContainerView addSubview:self.headView];
  435. self.headView.boardcastName.text = [NSString returnNoNullStringWithString:self.createrName];
  436. if (![NSString isEmptyString:self.createrAvatal]) {
  437. [self.headView.boardcastAvatal sd_setImageWithURL:[NSURL URLWithString:[self.createrAvatal getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:@"teacher_logo"]];
  438. }
  439. [self countLikeMessageCount];
  440. [self countMemberCount];
  441. [self.headView mas_makeConstraints:^(MASConstraintMaker *make) {
  442. make.left.right.mas_equalTo(self.videoContainerView);
  443. make.height.mas_equalTo(80);
  444. if (@available(iOS 11.0, *)) {
  445. make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(0);
  446. } else {
  447. // Fallback on earlier versions
  448. make.top.mas_equalTo(self.videoContainerView.mas_top).offset(0);
  449. }
  450. }];
  451. // 聊天区域
  452. [self.videoContainerView addSubview:self.messageContentView];
  453. if (self.hideCartButton == NO) {
  454. [self.videoContainerView addSubview:self.animationView];
  455. [self.animationView mas_makeConstraints:^(MASConstraintMaker *make) {
  456. if (@available(iOS 11.0, *)) {
  457. make.bottom.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideBottom).offset(-9);
  458. } else {
  459. // Fallback on earlier versions
  460. make.bottom.mas_equalTo(self.videoContainerView.mas_bottom).offset(-9);
  461. }
  462. make.centerX.mas_equalTo(self.videoContainerView.mas_right).offset(-80);
  463. make.width.mas_equalTo(48);
  464. make.height.mas_equalTo(56);
  465. }];
  466. [self.animationView play];
  467. }
  468. [self.videoContainerView addSubview:self.bottomView];
  469. self.bottomView.hideCartVButton = self.hideCartButton;
  470. [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
  471. make.left.right.mas_equalTo(self.videoContainerView);
  472. if (@available(iOS 11.0, *)) {
  473. make.bottom.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideBottom).offset(-5);
  474. } else {
  475. // Fallback on earlier versions
  476. make.bottom.mas_equalTo(self.videoContainerView.mas_bottom).offset(-5);
  477. }
  478. make.height.mas_equalTo(44);
  479. }];
  480. // 聊天页面
  481. if (self.needSwitchButton) {
  482. [self.messageContentView mas_makeConstraints:^(MASConstraintMaker *make) {
  483. make.left.mas_equalTo(self.videoContainerView.mas_left);
  484. make.right.mas_equalTo(self.videoContainerView.mas_right);
  485. make.bottom.mas_equalTo(self.bottomView.mas_top).offset(-5);
  486. make.top.mas_equalTo(self.videoView.mas_bottom).offset(10).priorityLow();
  487. }];
  488. }
  489. else {
  490. [self.messageContentView mas_makeConstraints:^(MASConstraintMaker *make) {
  491. make.left.mas_equalTo(self.videoContainerView.mas_left);
  492. make.right.mas_equalTo(self.videoContainerView.mas_right);
  493. make.bottom.mas_equalTo(self.bottomView.mas_top).offset(-10);
  494. make.height.mas_equalTo(180);
  495. }];
  496. }
  497. [self.videoContainerView addSubview:self.inputBar];
  498. [_inputBar setBackgroundColor: [UIColor whiteColor]];
  499. [_inputBar setFrame:CGRectMake(0, KPortraitHeight, KPortraitWidth , 50)];
  500. [_inputBar setHidden:YES];
  501. [self.messageContentView addSubview:self.conversationMessageTableView];
  502. [self.conversationMessageTableView mas_makeConstraints:^(MASConstraintMaker *make) {
  503. make.left.top.mas_equalTo(self.messageContentView);
  504. make.width.mas_equalTo(KPortraitWidth);
  505. make.bottom.mas_equalTo(self.messageContentView.mas_bottom);
  506. }];
  507. UITapGestureRecognizer *resetBottomTapGesture =[[UITapGestureRecognizer alloc]
  508. initWithTarget:self
  509. action:@selector(resetBottomGesture:)];
  510. resetBottomTapGesture.delegate = self;
  511. [self.videoContainerView addGestureRecognizer:resetBottomTapGesture];
  512. UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headViewAnimation:)];
  513. tapGesture.delegate = self;
  514. [self.videoView addGestureRecognizer:tapGesture];
  515. }
  516. - (void)headViewAnimation:(UIGestureRecognizer *)gestureRecognizer {
  517. if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
  518. [self setDefaultBottomViewStatus];
  519. }
  520. if (self.landScape) {
  521. self.isHiddenHeadView = !self.isHiddenHeadView;
  522. }
  523. }
  524. #pragma mark -- 加入直播间和退出直播间回到服务
  525. - (void)notiferJoinSuccessToServiceCallback:(void(^)(void))callback {
  526. [LiveModuleService LiveroomJoinRequest:KS_GET roomId:self.roomId success:^(NSDictionary * _Nonnull dic) {
  527. NSTimeInterval enterTime = [dic ks_integerValueForKey:@"data"];
  528. self.enterTime = enterTime;
  529. NSDate *currentDate = [NSDate date];
  530. self.enterDate = currentDate;
  531. callback();
  532. } faliure:^(NSError * _Nonnull error) {
  533. callback();
  534. }];
  535. }
  536. - (void)quitNotiferService {
  537. NSDate *currentDate = [NSDate date];
  538. NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:self.enterDate];
  539. NSInteger time = (NSInteger)(timeInterval *1000);
  540. if (self.enterTime > 0) {
  541. time = self.enterTime + time;
  542. }
  543. else {
  544. time = (NSInteger)([currentDate timeIntervalSince1970] *1000);
  545. }
  546. [LiveModuleService LiveroomQuit:KS_POST time:time success:^(NSDictionary * _Nonnull dic) {
  547. if ([dic ks_integerValueForKey:@"errCode"] == 0) {
  548. NSLog(@"success");
  549. }
  550. else {
  551. NSLog(@"----- error %@", [dic ks_stringValueForKey:@"errMsg"]);
  552. }
  553. } faliure:^(NSError * _Nonnull error) {
  554. }];
  555. }
  556. #pragma mark ------ 播放控制
  557. - (void)startPlay {
  558. if ([self.livePlayer isPlaying] == NO) {
  559. [self resetNetTips];
  560. if (self.isCreaterVideoEnable) {
  561. [self hideEmptyView];
  562. }
  563. [self showLoading];
  564. self.videoRenderView.hasMainStream = YES;
  565. self.videoRenderView.hasSubStream = NO;
  566. [self.livePlayer setRenderView:self.videoRenderView.mainView];
  567. if (self.needSwitchButton) {
  568. [self.livePlayer setRenderFillMode:V2TXLiveFillModeFit];
  569. }
  570. else {
  571. [self.livePlayer setRenderFillMode:V2TXLiveFillModeFill];
  572. }
  573. // 获取url的方法
  574. NSString *streamId = [NSString stringWithFormat:@"%@_%@", self.roomId, self.createrId];
  575. NSString *url = [TXLiveURLUtils generateRtmpPlayUrl:streamId playDomain:self.playDomain liveUrlKey:self.liveUrlKey];
  576. [self.livePlayer startLivePlay:url];
  577. [self muteVoiceAction];
  578. }
  579. }
  580. - (void)stopPlay {
  581. if (_livePlayer) {
  582. if ([self.livePlayer isPlaying]) {
  583. [self hideLoading];
  584. [self.livePlayer stopPlay];
  585. }
  586. }
  587. }
  588. // 不播放声音
  589. - (void)pauserAudio {
  590. [self.livePlayer pauseAudio];
  591. }
  592. // 恢复声音
  593. - (void)resumeAudio {
  594. [self.livePlayer resumeAudio];
  595. }
  596. #pragma mark ---- V2TXLivePlayerObserver
  597. - (void)onError:(id<V2TXLivePlayer>)player code:(V2TXLiveCode)code message:(NSString *)msg extraInfo:(NSDictionary *)extraInfo {
  598. if (code == V2TXLIVE_ERROR_DISCONNECTED) {
  599. // 连接断开
  600. if (self.isPauseLive == NO && self.isCreateInRoom) {
  601. [self hideLoading];
  602. [self showBadNetTips];
  603. }
  604. }
  605. else if (code == V2TXLIVE_WARNING_VIDEO_BLOCK) {
  606. }
  607. NSLog(@"---- V2TXLivePlayer %zd message:%@", code, msg);
  608. }
  609. - (void)onConnected:(id<V2TXLivePlayer>)player extraInfo:(NSDictionary *)extraInfo {
  610. NSLog(@" ------ connected %@", extraInfo);
  611. self.isPauseLive = NO;
  612. self.isCreateInRoom = YES;
  613. if (self.isCreaterVideoEnable) {
  614. [self hideEmptyView];
  615. }
  616. }
  617. - (void)onVideoPlaying:(id<V2TXLivePlayer>)player firstPlay:(BOOL)firstPlay extraInfo:(NSDictionary *)extraInfo {
  618. NSLog(@" ------ onVideoPlaying %@", extraInfo);
  619. [self hideLoading];
  620. }
  621. - (void)onVideoResolutionChanged:(id<V2TXLivePlayer>)player width:(NSInteger)width height:(NSInteger)height {
  622. NSLog(@" ------ onVideoResolutionChanged %zd %zd", width, height);
  623. }
  624. // 直播状态
  625. - (void)onStatisticsUpdate:(id<V2TXLivePlayer>)player statistics:(V2TXLivePlayerStatistics *)statistics {
  626. NSLog(@"----- V2TXLivePlayerStatistics desc %@", statistics.description);
  627. }
  628. - (void)onReceiveSeiMessage:(id<V2TXLivePlayer>)player payloadType:(int)payloadType data:(NSData *)data {
  629. NSLog(@"----- onReceiveSeiMessage desc %@", [data mj_JSONObject]);
  630. if (self.isAudience == YES && self.isPauseLive == NO) {
  631. NSDictionary *parm = [data mj_JSONObject];
  632. if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"sync"]) { // 同步连麦人数
  633. NSArray *memberArray = [parm ks_arrayValueForKey:@"seatUser"];
  634. NSMutableArray *remoteSeatArray = [NSMutableArray arrayWithArray:memberArray];
  635. // 移除自己
  636. NSMutableArray *seatArray = [NSMutableArray array];
  637. for (NSDictionary *userParm in remoteSeatArray) {
  638. if (![[userParm ks_stringValueForKey:@"userId"] isEqualToString:UserDefault(IM_USERID)]) {
  639. TXUISeatMember *member = [[TXUISeatMember alloc] init];
  640. member.userId = [userParm ks_stringValueForKey:@"userId"];
  641. member.muteMic = [userParm ks_boolValueForKey:@"micStatus"];
  642. [seatArray addObject:member];
  643. }
  644. }
  645. [self refreshSeatWithMember:seatArray];
  646. // if (self.remoteMemberArray.count != seatArray.count) { // 如果连麦人数不同
  647. // [self refreshSeatWithMember:seatArray];
  648. // }
  649. // else {
  650. // BOOL isSameUser = YES;
  651. // NSMutableArray *seatMemberArr = [self.remoteMemberArray mutableCopy];
  652. //
  653. // for (TXUISeatMember *member in seatMemberArr) {
  654. //
  655. // if (![remoteSeatArray containsObject:member]) {
  656. // isSameUser = NO;
  657. // }
  658. // }
  659. // if (isSameUser == NO) {
  660. // [self refreshSeatWithMember:seatArray];
  661. // }
  662. // }
  663. }
  664. }
  665. else {
  666. [self refreshSeatWithMember:@[]];
  667. }
  668. }
  669. - (void)refreshSeatWithMember:(NSArray *)memberArray {
  670. self.remoteMemberArray = [NSMutableArray arrayWithArray:memberArray];
  671. [self renderSeatView];
  672. }
  673. #pragma mark --- trtcCloud delegate
  674. // 远端主播加入房间
  675. - (void)onRemoteUserEnterRoom:(NSString *)userId {
  676. if (![self judgeContainerRemoteUser:userId] && ![userId isEqualToString:self.createrId]) { // 如果不包含当前用户 添加 和刷新连麦
  677. TXUISeatMember *member = [[TXUISeatMember alloc] init];
  678. member.userId = userId;
  679. member.muteMic = YES;
  680. [self.remoteMemberArray addObject:member];
  681. }
  682. [self renderSeatView];
  683. }
  684. // 远端主播离开
  685. - (void)onRemoteUserLeaveRoom:(NSString *)userId reason:(NSInteger)reason {
  686. if ([self judgeContainerRemoteUser:userId]) { // 如果包含当前用户 移除当前用和刷新连麦
  687. [self removeMemberUserId:userId];
  688. }
  689. [self renderSeatView];
  690. }
  691. - (BOOL)judgeContainerRemoteUser:(NSString *)userId {
  692. BOOL containUser = NO;
  693. NSMutableArray *remoteMemArray = [self.remoteMemberArray mutableCopy];
  694. for (TXUISeatMember *member in remoteMemArray) {
  695. if ([member.userId isEqualToString:userId]) {
  696. containUser = YES;
  697. }
  698. }
  699. return containUser;
  700. }
  701. - (void)removeMemberUserId:(NSString *)userId {
  702. NSMutableArray *remoteMemArray = [self.remoteMemberArray mutableCopy];
  703. for (TXUISeatMember *member in remoteMemArray) {
  704. if ([member.userId isEqualToString:userId]) {
  705. [self.remoteMemberArray removeObject:member];
  706. return;
  707. }
  708. }
  709. }
  710. - (void)onWarning:(TXLiteAVWarning)warningCode warningMsg:(NSString *)warningMsg extInfo:(NSDictionary *)extInfo {
  711. NSLog(@" warningMsg %@ extInfo:%@", warningMsg, extInfo.description);
  712. }
  713. // 远端视频
  714. - (void)onUserVideoAvailable:(NSString *)userId available:(BOOL)available {
  715. NSLog(@"creater remote videoStream status: %d", available);
  716. if (available) {
  717. self.videoRenderView.hasMainStream = YES;
  718. if (self.videoRenderView.hasSubStream) {
  719. [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.smallView];
  720. [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub view:self.videoRenderView.mainView];
  721. }
  722. else {
  723. [self.trtcCloud stopRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub];
  724. [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.mainView];
  725. }
  726. }
  727. else {
  728. self.videoRenderView.hasMainStream = NO;
  729. [self.trtcCloud stopRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig];
  730. }
  731. }
  732. - (void)onUserSubStreamAvailable:(NSString *)userId available:(BOOL)available {
  733. NSLog(@"creater remote subStream status: %d", available);
  734. if (available) {
  735. self.videoRenderView.hasSubStream = YES;
  736. [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub view:self.videoRenderView.mainView];
  737. if (self.videoRenderView.hasMainStream) {
  738. [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.smallView];
  739. }
  740. }
  741. else {
  742. self.videoRenderView.hasSubStream = NO;
  743. [self.trtcCloud stopRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub];
  744. if (self.videoRenderView.hasMainStream) {
  745. [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.mainView];
  746. }
  747. }
  748. }
  749. - (void)onFirstVideoFrame:(NSString *)userId streamType:(TRTCVideoStreamType)streamType width:(int)width height:(int)height {
  750. }
  751. - (void)onRecvSEIMsg:(NSString *)userId message:(NSData *)message {
  752. // NSDictionary *parm = [message mj_JSONObject];
  753. }
  754. - (void)onUserAudioAvailable:(NSString *)userId available:(BOOL)available {
  755. for (TXUISeatMember *member in self.remoteMemberArray) {
  756. if ([member.userId isEqualToString:userId]) {
  757. member.muteMic = !available;
  758. }
  759. }
  760. [self renderSeatView];
  761. }
  762. #pragma mark - gesture and button action
  763. - (void)resetBottomGesture:(UIGestureRecognizer *)gestureRecognizer {
  764. if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
  765. [self setDefaultBottomViewStatus];
  766. }
  767. }
  768. - (void)setDefaultBottomViewStatus {
  769. [self.inputBar setInputBarStatus:KSBottomBarStatusDefault];
  770. [self.inputBar setHidden:YES];
  771. }
  772. #pragma mark --- setter
  773. - (void)setIsHiddenHeadView:(BOOL)isHiddenHeadView {
  774. if (_isHiddenHeadView == isHiddenHeadView) {
  775. return;
  776. }
  777. _isHiddenHeadView = isHiddenHeadView;
  778. //告知需要更改约束
  779. [self.view setNeedsUpdateConstraints];
  780. [UIView animateWithDuration:0.5 animations:^{
  781. if (isHiddenHeadView) {
  782. [self.headView mas_updateConstraints:^(MASConstraintMaker *make) {
  783. if (@available(iOS 11.0, *)) {
  784. make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(-80);
  785. } else {
  786. // Fallback on earlier versions
  787. make.top.mas_equalTo(self.videoContainerView.mas_top).offset(-80);
  788. }
  789. }];
  790. }
  791. else {
  792. [self.headView mas_updateConstraints:^(MASConstraintMaker *make) {
  793. if (@available(iOS 11.0, *)) {
  794. make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(10);
  795. } else {
  796. // Fallback on earlier versions
  797. make.top.mas_equalTo(self.videoContainerView.mas_top).offset(10);
  798. }
  799. }];
  800. }
  801. [self.headView.superview layoutIfNeeded];
  802. }];
  803. }
  804. #pragma mark ----- landscape action
  805. - (void)transferToLandscape {
  806. [self setDefaultBottomViewStatus];
  807. [self changeOrientation:!self.landScape];
  808. }
  809. - (void)changeOrientation:(BOOL)isLandScape {
  810. self.landScape = isLandScape;
  811. if (isLandScape) {
  812. if (IS_IPAD) {
  813. self.zh_statusBarHidden = YES;
  814. }
  815. // 切换到横屏
  816. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  817. delegate.allowAutoRotate = YES;
  818. [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight inController:self];
  819. }
  820. else {
  821. if (IS_IPAD) {
  822. self.zh_statusBarHidden = NO;
  823. }
  824. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  825. delegate.allowAutoRotate = NO;
  826. [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait inController:self];
  827. }
  828. self.isHiddenHeadView = NO;
  829. [self updateConstraint];
  830. }
  831. - (void)updateConstraint {
  832. if (self.landScape) {
  833. self.bottomView.hideChatView = YES;
  834. self.messageContentView.hidden = YES;
  835. self.headView.hideQuitButton = YES;
  836. [self.videoView mas_remakeConstraints:^(MASConstraintMaker *make) {
  837. if (IS_IPAD) {
  838. make.left.right.mas_equalTo(self.videoContainerView);
  839. make.centerX.mas_equalTo(self.videoContainerView.mas_centerX);
  840. CGFloat rate = 9 / 16.0f;
  841. make.height.mas_equalTo(self.videoContainerView.mas_width).multipliedBy(rate);
  842. make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
  843. make.width.height.lessThanOrEqualTo(self.videoContainerView);
  844. }
  845. else {
  846. make.top.bottom.mas_equalTo(self.videoContainerView);
  847. make.centerX.mas_equalTo(self.videoContainerView.mas_centerX);
  848. // 宽高设置
  849. CGFloat rate = 16 / 9.0f;
  850. make.width.mas_equalTo(self.videoContainerView.mas_height).multipliedBy(rate);
  851. make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
  852. make.width.height.lessThanOrEqualTo(self.videoContainerView);
  853. }
  854. }];
  855. [self.switchButton mas_remakeConstraints:^(MASConstraintMaker *make) {
  856. make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-10);
  857. make.width.height.mas_equalTo(33);
  858. make.centerY.mas_equalTo(self.videoView.mas_centerY);
  859. }];
  860. }
  861. else {
  862. self.bottomView.hideChatView = NO;
  863. self.messageContentView.hidden = NO;
  864. self.headView.hideQuitButton = NO;
  865. [self.videoView mas_remakeConstraints:^(MASConstraintMaker *make) {
  866. make.left.right.mas_equalTo(self.videoContainerView);
  867. if (@available(iOS 11.0, *)) {
  868. make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(160);
  869. } else {
  870. // Fallback on earlier versions
  871. make.top.mas_equalTo(self.videoContainerView.mas_top).offset(160);
  872. }
  873. CGFloat rate = 9 / 16.0f;
  874. make.height.mas_equalTo(self.videoContainerView.mas_width).multipliedBy(rate);
  875. make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
  876. make.width.height.lessThanOrEqualTo(self.videoContainerView);
  877. }];
  878. [self.switchButton mas_remakeConstraints:^(MASConstraintMaker *make) {
  879. make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-10);
  880. make.width.height.mas_equalTo(33);
  881. make.bottom.mas_equalTo(self.videoView.mas_bottom).offset(-10);
  882. }];
  883. }
  884. // 更新连麦UI
  885. if ([self.videoContainerView.subviews containsObject:self.seatContainer]) {
  886. [self updateSeatContainerConstraint];
  887. }
  888. }
  889. - (void)showEmptyViewDisplay:(BOOL)isPublish {
  890. NSString *imageName = @"";
  891. NSString *descMessage = @"";
  892. if (isPublish) {
  893. imageName = @"liveVideo_close";
  894. descMessage = @"主讲人已关闭画面!";
  895. }
  896. else {
  897. imageName = @"liveroom_empty";
  898. descMessage = @"休息一下,马上回来!";
  899. }
  900. [self.emptyView configViewWithImageName:imageName desc:descMessage];
  901. [self hideTipsView];
  902. self.videoView.hidden = YES;
  903. self.emptyView.hidden = NO;
  904. }
  905. - (void)hideEmptyView {
  906. self.emptyView.hidden = YES;
  907. self.videoView.hidden = NO;
  908. }
  909. - (void)changeEmptyStatusView {
  910. if (self.isCloseRoom == NO) { // 只有在非结束直播状态下
  911. if (self.isPauseLive) { // 暂停直播
  912. [self showEmptyViewDisplay:NO];
  913. }
  914. else { // 如果未暂停直播
  915. if (self.isCreaterVideoEnable) { // 如果摄像头开启
  916. [self hideTipsView];
  917. [self hideEmptyView];
  918. }
  919. else { // 主播关闭了摄像头
  920. [self showEmptyViewDisplay:YES];
  921. }
  922. }
  923. }
  924. }
  925. - (void)renderSeatView {
  926. NSMutableArray *seatArray = [NSMutableArray array];
  927. NSArray *remoteUserArray = [self.remoteMemberArray mutableCopy];
  928. for (TXUISeatMember *member in remoteUserArray) {
  929. [seatArray addObject:member];
  930. }
  931. BOOL isContainSelf = NO;
  932. for (TXUISeatMember *member in seatArray) {
  933. if ([member.userId isEqualToString:UserDefault(IM_USERID)]) {
  934. isContainSelf = YES;
  935. break;
  936. }
  937. }
  938. if (self.micStatus == MICSTATUS_CONNECTING && !isContainSelf) {
  939. TXUISeatMember *currentMember = [[TXUISeatMember alloc] init];
  940. currentMember.userId = UserDefault(IM_USERID);
  941. currentMember.muteMic = !self.micEnable;
  942. [seatArray addObject:currentMember];
  943. }
  944. NSLog(@"-- seat member--- %zd -", seatArray.count);
  945. if (seatArray.count) {
  946. if (![self.videoContainerView.subviews containsObject:self.seatContainer]) {
  947. [self.videoContainerView addSubview:self.seatContainer];
  948. }
  949. [self updateSeatContainerConstraint];
  950. // self.seatContainer.seatMemberArray = [NSMutableArray arrayWithArray:seatArray];
  951. // [self.seatContainer refreshSeatUI];
  952. [self.seatContainer refreshSeatUIWithMember:[NSMutableArray arrayWithArray:seatArray]];
  953. }
  954. else {
  955. [self removeSeatContainer];
  956. }
  957. }
  958. // 移除连麦视图
  959. - (void)removeSeatContainer {
  960. if ([self.videoContainerView.subviews containsObject:self.seatContainer]) {
  961. [self.seatContainer removeFromSuperview];
  962. }
  963. }
  964. - (void)updateSeatContainerConstraint {
  965. if (self.landScape) {
  966. [self.seatContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
  967. if (@available(iOS 11.0, *)) {
  968. make.left.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideLeft);
  969. } else {
  970. // Fallback on earlier versions
  971. make.left.mas_equalTo(self.videoContainerView.mas_left);
  972. }
  973. make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-70);
  974. if (@available(iOS 11.0, *)) {
  975. make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(85);
  976. } else {
  977. // Fallback on earlier versions
  978. make.top.mas_equalTo(self.videoContainerView.mas_top).offset(85);
  979. }
  980. make.height.mas_equalTo(70);
  981. }];
  982. }
  983. else {
  984. [self.seatContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
  985. make.left.right.mas_equalTo(self.videoContainerView);
  986. make.top.mas_equalTo(self.headView.mas_bottom).offset(0);
  987. make.height.mas_equalTo(70);
  988. }];
  989. }
  990. }
  991. #pragma mark ----- 加入TRC房间
  992. - (void)enterLiveRTCRoom {
  993. self.videoRenderView.hasMainStream = NO;
  994. self.videoRenderView.hasSubStream = NO;
  995. TRTCParams *parms = [[TRTCParams alloc] init];
  996. parms.sdkAppId = CONFIG_TXSDKAPPID;
  997. parms.userSig = self.UserSig;
  998. parms.role = TRTCRoleAnchor;
  999. parms.strRoomId = self.roomId;
  1000. parms.userId = UserDefault(IM_USERID);
  1001. [self.trtcCloud addDelegate:self];
  1002. [self config3AParams];
  1003. [self configVideoAndAudioSetting];
  1004. [self.trtcCloud enterRoom:parms appScene:TRTCAppSceneLIVE];
  1005. TRTCRenderParams *rendParams = [[TRTCRenderParams alloc] init];
  1006. if (self.needSwitchButton) {
  1007. rendParams.fillMode = TRTCVideoFillMode_Fit;
  1008. }
  1009. else {
  1010. rendParams.fillMode = TRTCVideoFillMode_Fill;
  1011. }
  1012. [self.trtcCloud setRemoteRenderParams:self.createrId streamType:TRTCVideoStreamTypeBig params:rendParams];
  1013. [self.trtcCloud setRemoteRenderParams:self.createrId streamType:TRTCVideoStreamTypeSub params:rendParams];
  1014. }
  1015. - (void)configVideoAndAudioSetting {
  1016. [self.trtcCloud startLocalAudio:TRTCAudioQualityMusic];
  1017. TRTCVideoEncParam *videoEncParam = [[TRTCVideoEncParam alloc] init];
  1018. videoEncParam.videoFps = 24;
  1019. videoEncParam.resMode = TRTCVideoResolutionModePortrait;
  1020. videoEncParam.videoResolution = TRTCVideoResolution_960_540;
  1021. [self.trtcCloud setVideoEncoderParam:videoEncParam];
  1022. if (self.muteRoomMic) {
  1023. [self micButtonAction:self.muteRoomMic];
  1024. }
  1025. }
  1026. // 配置3A参数
  1027. - (void)config3AParams {
  1028. [self configANSParm:self.ANS];
  1029. [self configAECParm:self.AEC];
  1030. [self configAGCParm:self.AGC];
  1031. }
  1032. // 背景音抑制
  1033. - (void)configANSParm:(NSInteger)number {
  1034. NSMutableDictionary *dic = [NSMutableDictionary dictionary];
  1035. [dic setValue:@"enableAudioANS" forKey:@"api"];
  1036. NSMutableDictionary *subDic = [NSMutableDictionary dictionary];
  1037. if (number > 0) {
  1038. [subDic setValue:@(1) forKey:@"enable"];
  1039. [subDic setValue:@(number) forKey:@"level"];
  1040. }
  1041. else {
  1042. [subDic setValue:@(0) forKey:@"enable"];
  1043. [subDic setValue:@(0) forKey:@"level"];
  1044. }
  1045. [dic setValue:subDic forKey:@"params"];
  1046. NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:kNilOptions error:nil];
  1047. NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  1048. [self.trtcCloud callExperimentalAPI:jsonString];
  1049. }
  1050. // 回声消除
  1051. - (void)configAECParm:(NSInteger)number {
  1052. NSMutableDictionary *dic = [NSMutableDictionary dictionary];
  1053. [dic setValue:@"enableAudioAEC" forKey:@"api"];
  1054. NSMutableDictionary *subDic = [NSMutableDictionary dictionary];
  1055. if (number > 0) {
  1056. [subDic setValue:@(1) forKey:@"enable"];
  1057. [subDic setValue:@(number) forKey:@"level"];
  1058. }
  1059. else {
  1060. [subDic setValue:@(0) forKey:@"enable"];
  1061. [subDic setValue:@(0) forKey:@"level"];
  1062. }
  1063. [dic setValue:subDic forKey:@"params"];
  1064. NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:kNilOptions error:nil];
  1065. NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  1066. [self.trtcCloud callExperimentalAPI:jsonString];
  1067. }
  1068. // 自动增益
  1069. - (void)configAGCParm:(NSInteger)number {
  1070. NSMutableDictionary *dic = [NSMutableDictionary dictionary];
  1071. [dic setValue:@"enableAudioAGC" forKey:@"api"];
  1072. NSMutableDictionary *subDic = [NSMutableDictionary dictionary];
  1073. if (number > 0) {
  1074. [subDic setValue:@(1) forKey:@"enable"];
  1075. [subDic setValue:@(number) forKey:@"level"];
  1076. }
  1077. else {
  1078. [subDic setValue:@(0) forKey:@"enable"];
  1079. [subDic setValue:@(0) forKey:@"level"];
  1080. }
  1081. [dic setValue:subDic forKey:@"params"];
  1082. NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:kNilOptions error:nil];
  1083. NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  1084. [self.trtcCloud callExperimentalAPI:jsonString];
  1085. }
  1086. #pragma mark -------- live message handle
  1087. - (NSString *)getMsgClassName:(NSString *)messageObjectName {
  1088. TXLiveMessageModel *configModel = [[TXLiveMessageModel alloc] init];
  1089. NSString *className = [configModel getMessageClassName:messageObjectName];
  1090. return className;
  1091. }
  1092. /**
  1093. * 接收到消息的回调
  1094. */
  1095. - (void)didReceiveMessageNotification:(NSNotification *)notification {
  1096. NSDictionary *dic = notification.object; /// __block TXLiveMessageModel *liveMessage
  1097. NSDictionary *content = [dic ks_dictionaryValueForKey:@"message"];
  1098. NSString *objectName = [content ks_stringValueForKey:@"objectName"];
  1099. NSString *className = [self getMsgClassName:objectName];
  1100. __block TXLiveMessageModel *message = [[NSClassFromString(className) alloc] init];
  1101. message.messageId = [dic ks_stringValueForKey:@"msgID"];
  1102. message.groupId = [dic ks_stringValueForKey:@"groupID"];
  1103. [message evaluateSource:content];
  1104. if ([message.groupId isEqualToString:self.roomId]) {
  1105. __weak typeof(&*self) __blockSelf = self;
  1106. dispatch_async(dispatch_get_main_queue(), ^{
  1107. if ([message isMemberOfClass:[TXLiveMessageLike class]]) {
  1108. TXLiveMessageLike *likeMessage = (TXLiveMessageLike *)message;
  1109. NSLog(@"receive like count ------- %d",likeMessage.counts);
  1110. return;
  1111. }
  1112. else if ([message isMemberOfClass:[TXLiveMessageLikeCount class]]) {
  1113. return;
  1114. }
  1115. else if ([message isMemberOfClass:[TXLiveMessageWelcome class]]) {
  1116. TXLiveMessageWelcome *welcomeMessage = (TXLiveMessageWelcome *)message;
  1117. // 过滤自己发送的欢迎消息
  1118. if ([welcomeMessage.userInfo.sendUserId isEqualToString:UserDefault(IM_USERID)]) {
  1119. return;
  1120. }
  1121. if ([welcomeMessage.userInfo.sendUserId isEqual:__blockSelf.createrId]) { // 如果是老师 重置连麦申请状态
  1122. [__blockSelf resetMicWaitToNomal];
  1123. __blockSelf.isCreateInRoom = YES;
  1124. }
  1125. NSString *userName = [welcomeMessage.userInfo.sendUserName stringByAppendingString:@""];
  1126. NSString *contentMsg = [NSString stringWithFormat:@"%@ 进入直播间",userName];
  1127. [__blockSelf showAnimationView:YES showMessag:contentMsg];
  1128. return;
  1129. }
  1130. // 用户退出消息
  1131. else if ([message isMemberOfClass:[TXLiveMessageLeave class]]) {
  1132. // TXLiveMessageLeave *userLeaveMsg = (TXLiveMessageLeave *)message;
  1133. // if ([userLeaveMsg.userInfo.sendUserId isEqualToString:__blockSelf.createrId]) {
  1134. // __blockSelf.isCreateInRoom = NO;
  1135. // // 下麦
  1136. // if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
  1137. // [__blockSelf downSeatAction];
  1138. // }
  1139. // }
  1140. return;
  1141. }
  1142. // 用户退出回调
  1143. else if ([message isMemberOfClass:[TXLiveMessageUserQuit class]]) {
  1144. // TXLiveMessageUserQuit *userLeaveMsg = (TXLiveMessageUserQuit *)message;
  1145. // if ([userLeaveMsg.targetId isEqualToString:__blockSelf.createrId]) {
  1146. // __blockSelf.isCreateInRoom = NO;
  1147. // // 下麦
  1148. // if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
  1149. // [__blockSelf downSeatAction];
  1150. // }
  1151. // }
  1152. return;
  1153. }
  1154. else if ([message isMemberOfClass:[TXLiveMessageMemberUp class]]) {
  1155. return;
  1156. }
  1157. else if ([message isMemberOfClass:[TXLiveMessageStatSync class]]) { // 数据同步 不取点赞数
  1158. return;
  1159. }
  1160. else if ([message isMemberOfClass:[TXLiveMessageKickOut class]]) {
  1161. TXLiveMessageKickOut *kickMessage = (TXLiveMessageKickOut *)message;
  1162. NSString *currentUserId = UserDefault(IM_USERID);
  1163. if ([kickMessage.targetId isEqualToString:currentUserId]) {
  1164. __blockSelf.micStatus = MICSTATUS_NOMAL;
  1165. [__blockSelf kickOutLiveRoom];
  1166. }
  1167. return;
  1168. }
  1169. // 禁言控制
  1170. else if ([message isMemberOfClass:[TXLiveMessageChatBan class]]) {
  1171. TXLiveMessageChatBan *banMessage = (TXLiveMessageChatBan *)message;
  1172. if (banMessage.chatBan) { // 禁止发言
  1173. __blockSelf.enableChat = NO;
  1174. }
  1175. else { // 开启发言
  1176. __blockSelf.enableChat = YES;
  1177. }
  1178. [__blockSelf insertMessage:banMessage];
  1179. }
  1180. // 连麦控制消息
  1181. else if ([message isMemberOfClass:[TXLiveMessageSeatControl class]]) {
  1182. TXLiveMessageSeatControl *seatControlMessage = (TXLiveMessageSeatControl *)message;
  1183. if (seatControlMessage.seatBan) { // 禁止连麦
  1184. __blockSelf.enableSeat = NO;
  1185. // 未连麦情况下才重置
  1186. if (__blockSelf.micStatus != MICSTATUS_CONNECTING) {
  1187. // 如果在申请 发送取消申请消息
  1188. if (__blockSelf.micStatus == MICSTATUS_WAITING) {
  1189. [__blockSelf sendSeatMessageApply:NO];
  1190. }
  1191. __blockSelf.micStatus = MICSTATUS_NOMAL;
  1192. }
  1193. [__blockSelf hideApplyView];
  1194. [__blockSelf hideApplyingView];
  1195. }
  1196. else { // 开启连麦
  1197. __blockSelf.enableSeat = YES;
  1198. }
  1199. [__blockSelf insertMessage:seatControlMessage];
  1200. }
  1201. else if ([message isMemberOfClass:[TXLiveMessageSeatApply class]]) {
  1202. // 只处理主讲人发起的连麦邀请或取消操作
  1203. TXLiveMessageSeatApply *seatApplyMessage = (TXLiveMessageSeatApply *)message;
  1204. NSLog(@"----- RCChatroomSeatApply type:%ld audienceName:%@ ",seatApplyMessage.type,seatApplyMessage.audienceName);
  1205. // 只处理和自己相关的消息
  1206. if (![seatApplyMessage.audienceId isEqualToString:UserDefault(IM_USERID)]) {
  1207. // 刷新麦序
  1208. return;
  1209. }
  1210. if (seatApplyMessage.type == SEATHANDLE_INVITE) { // 主讲人发起申请
  1211. NSString *titles = [NSString stringWithFormat:@"%@邀请您连麦",[NSString returnNoNullStringWithString:seatApplyMessage.teacherName]];
  1212. [__blockSelf showInviteAlertMessage:titles];
  1213. }
  1214. else if (seatApplyMessage.type == SEATHANDLE_DISINVITE) { // 主讲人撤回申请
  1215. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"主讲人撤回了连麦申请"];
  1216. [__blockSelf closeAlertView];
  1217. if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
  1218. [__blockSelf downSeatAction];
  1219. }
  1220. }
  1221. else if (seatApplyMessage.type == SEATHANDLE_KICKSEAT) { // 主讲人断开连麦
  1222. [__blockSelf kickSeatWithMessage:@"主讲人断开连麦"];
  1223. }
  1224. [__blockSelf hideApplyingView];
  1225. [__blockSelf insertMessage:seatApplyMessage];
  1226. }
  1227. else if ([message isMemberOfClass:[TXLiveMessageSeatResponse class]]) { // 只处理主讲人的回应
  1228. TXLiveMessageSeatResponse *seatResponseMessage = (TXLiveMessageSeatResponse *)message;
  1229. NSLog(@"----- RCChatroomSeatResponse type:%ld audienceName:%@ ",seatResponseMessage.type,seatResponseMessage.audienceName);
  1230. if (![seatResponseMessage.audienceId isEqualToString:UserDefault(IM_USERID)]) { // 只处理和自己相关的消息
  1231. return;
  1232. }
  1233. if (seatResponseMessage.type == SEATRESPONSE_TEACHERAPPROVE && __blockSelf.micStatus == MICSTATUS_WAITING) { //
  1234. [__blockSelf hideApplyingView];
  1235. [__blockSelf connectHostWithStatus:YES];
  1236. [__blockSelf insertMessage:seatResponseMessage];
  1237. }
  1238. else if (seatResponseMessage.type == SEATRESPONSE_TEACHERREJECT) {
  1239. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"主讲人拒绝了您的连麦申请"];
  1240. // 处理连麦状态
  1241. [__blockSelf connectHostWithStatus:NO];
  1242. [__blockSelf insertMessage:seatResponseMessage];
  1243. }
  1244. }
  1245. else if ([message isMemberOfClass:[TXLiveMessageDownSeat class]]) { // 成员主动下麦消息
  1246. return;
  1247. }
  1248. else if ([message isMemberOfClass:[TXLiveMessageClose class]]) { // 直播间已关闭
  1249. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"直播已结束"];
  1250. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1251. // 显示直播间关闭状态
  1252. [__blockSelf closeAlertView];
  1253. [__blockSelf displayCloseView];
  1254. [__blockSelf hideApplyingView];
  1255. });
  1256. return;
  1257. }
  1258. else if ([message isMemberOfClass:[TXLiveTextMessage class]]) {
  1259. TXLiveTextMessage *textMessage = (TXLiveTextMessage *)message;
  1260. [__blockSelf insertMessage:textMessage];
  1261. }
  1262. else if ([message isMemberOfClass:[TXLiveMessageCardMessage class]]) {
  1263. TXLiveMessageCardMessage *cardMessage = (TXLiveMessageCardMessage *)message;
  1264. [__blockSelf sendCardMessage:cardMessage.goodsContent];
  1265. }
  1266. else if ([message isMemberOfClass:[TXLiveMessagePauseLive class]]) { // 主讲人暂停直播
  1267. // __blockSelf.isPauseLive = YES;
  1268. // if (__blockSelf.micStatus == MICSTATUS_WAITING) {
  1269. // [__blockSelf sendSeatMessageApply:NO];
  1270. // }
  1271. // [__blockSelf resetNetTips];
  1272. // [__blockSelf resetConnectionStatus];
  1273. // [__blockSelf closeAlertView];
  1274. // [__blockSelf hideApplyingView];
  1275. // [__blockSelf changeEmptyStatusView];
  1276. // [__blockSelf closePlayStatus];
  1277. return;
  1278. }
  1279. else if ([message isMemberOfClass:[TXLiveMessageOpenLive class]]) { // 开始直播
  1280. // __blockSelf.isPauseLive = NO;
  1281. // __blockSelf.isCreateInRoom = YES;
  1282. // [__blockSelf resetNetTips];
  1283. // [__blockSelf hideEmptyView];
  1284. // [__blockSelf startPlay];
  1285. return;
  1286. }
  1287. else if ([message isMemberOfClass:[TXLiveMessageShopRush class]]) { //
  1288. TXLiveMessageShopRush *rushMsg = (TXLiveMessageShopRush *)message;
  1289. NSString *contentMsg = [NSString stringWithFormat:@"%@ 正在抢购",[NSString returnNoNullStringWithString:rushMsg.userInfo.sendUserName]];
  1290. [__blockSelf showAnimationView:NO showMessag:contentMsg];
  1291. }
  1292. else if ([message isMemberOfClass:[TXLiveMessageBlockUser class]]) { // 黑名单
  1293. TXLiveMessageBlockUser *blockMessage = (TXLiveMessageBlockUser *)message;
  1294. if ([blockMessage.userId isEqualToString:UserDefault(IM_USERID)]) {
  1295. __blockSelf.blacklistFlag = YES;
  1296. [__blockSelf resetConnectionStatus];
  1297. }
  1298. }
  1299. else if ([message isMemberOfClass:[TXLiveMessageUnBlockUser class]]) {
  1300. TXLiveMessageUnBlockUser *unBlockMessage = (TXLiveMessageUnBlockUser *)message;
  1301. if ([unBlockMessage.userId isEqualToString:UserDefault(IM_USERID)]) {
  1302. __blockSelf.blacklistFlag = NO;
  1303. }
  1304. }
  1305. else if ([message isMemberOfClass:[TXLiveMessageDownSeatAll class]]) { // 所有麦上用户下麦
  1306. TXLiveMessageDownSeatAll *downSeatMessage = (TXLiveMessageDownSeatAll *)message;
  1307. if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
  1308. [__blockSelf kickSeatWithMessage:@"主讲人断开连麦"];
  1309. [__blockSelf insertMessage:downSeatMessage];
  1310. }
  1311. }
  1312. else if ([message isMemberOfClass:[TXLiveMessageRejectAllSeat class]]) { // 拒绝所有连麦申请
  1313. TXLiveMessageRejectAllSeat *rejectAllMessage = (TXLiveMessageRejectAllSeat *)message;
  1314. if (__blockSelf.micStatus == MICSTATUS_WAITING) {
  1315. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"主讲人拒绝了您的连麦申请"];
  1316. // 处理连麦状态
  1317. __blockSelf.micStatus = MICSTATUS_NOMAL;
  1318. [__blockSelf insertMessage:rejectAllMessage];
  1319. [__blockSelf hideApplyingView];
  1320. }
  1321. }
  1322. else if ([message isMemberOfClass:[TXControlMemberMic class]]) { // 控制学生麦克风
  1323. TXControlMemberMic *micCtrlMsg = (TXControlMemberMic *)message;
  1324. // 如果是控制当前学生的mic
  1325. if ([micCtrlMsg.userId isEqualToString:UserDefault(IM_USERID)]) {
  1326. [__blockSelf changeCurrentMicStatus:micCtrlMsg.muteMic];
  1327. }
  1328. }
  1329. else if ([message isMemberOfClass:[TXLiveForceKickMsg class]]) {
  1330. TXLiveForceKickMsg *changeMsg = (TXLiveForceKickMsg *)message;
  1331. if (changeMsg.targetIds.count == 0 || changeMsg.targetIds == nil) { // 如果是全员
  1332. [__blockSelf courseChangeTips:changeMsg];
  1333. }
  1334. else {
  1335. for (NSString *userId in changeMsg.targetIds) { // 如果部分学员中包含自己
  1336. if ([userId isEqualToString:UserDefault(IM_USERID)]) {
  1337. [__blockSelf courseChangeTips:changeMsg];
  1338. break;
  1339. }
  1340. }
  1341. }
  1342. }
  1343. });
  1344. }
  1345. }
  1346. - (void)courseChangeTips:(TXLiveForceKickMsg *)msg {
  1347. // 显示直播间关闭状态
  1348. [self closeAlertView];
  1349. [self displayCloseView];
  1350. [self hideApplyingView];
  1351. // 退出房间
  1352. NSString *tipsMsg = [NSString isEmptyString:msg.reason] ? @"当前课程已调整,请退出直播间" : msg.reason;
  1353. MJWeakSelf;
  1354. self.closeAlert = [LiveRoomConfirmAlert liveroomAlertWithTitle:tipsMsg sureTitle:@"确定" inView:self.view confirm:^{
  1355. [weakSelf exitRoom];
  1356. }];
  1357. }
  1358. - (void)changeCurrentMicStatus:(BOOL)isMute {
  1359. if (isMute == !self.micEnable) { // 如果麦克风状态和当前状态一致,剔除
  1360. return;
  1361. }
  1362. if (_trtcCloud && self.isAudience == NO) {
  1363. self.micEnable = !self.micEnable;
  1364. [self.trtcCloud muteLocalAudio:!self.micEnable];
  1365. NSString *desc = @"";
  1366. if (self.micEnable) {
  1367. desc = @"主讲人已将您的麦克风开启";
  1368. }
  1369. else {
  1370. desc = @"主讲人已将您的麦克风关闭";
  1371. }
  1372. [LOADING_MANAGER MBShowAUTOHidingInWindow:desc];
  1373. [self syncMicStatus:!self.micEnable];
  1374. }
  1375. }
  1376. - (void)didReceiveGroupMessage:(NSNotification *)notification {
  1377. NSDictionary *dic = notification.object; /// __block TXLiveMessageModel *liveMessage
  1378. NSString *groupId = [dic ks_stringValueForKey:@"groupID"];
  1379. NSDictionary *content = [dic ks_dictionaryValueForKey:@"message"];
  1380. if ([groupId isEqualToString:self.roomId]) {
  1381. [self evaluateGroupInfo:content];
  1382. }
  1383. }
  1384. - (void)evaluateGroupInfo:(NSDictionary *)groupInfo {
  1385. // 主播在房间
  1386. if ([[groupInfo ks_stringValueForKey:@"LIVE_STATUS"] isEqualToString:@"ON"]) { // 直播中
  1387. self.isPauseLive = NO;
  1388. if (self.isAudience) {
  1389. [self startPlay];
  1390. }
  1391. }
  1392. else if ([[groupInfo ks_stringValueForKey:@"LIVE_STATUS"] isEqualToString:@"OFF"]) { // 暂停直播
  1393. self.isPauseLive = YES;
  1394. [self closePlayStatus];
  1395. }
  1396. // 摄像头状态
  1397. if ([[groupInfo ks_stringValueForKey:@"ANCHOR_CAMERA"] isEqualToString:@"OFF"]) {
  1398. self.isCreaterVideoEnable = NO;
  1399. }
  1400. else {
  1401. self.isCreaterVideoEnable = YES;
  1402. }
  1403. // 禁言状态
  1404. if ([[groupInfo ks_stringValueForKey:@"GLOBAL_BAN"] isEqualToString:@"ON"]) {
  1405. self.enableChat = NO;
  1406. }
  1407. else {
  1408. self.enableChat = YES;
  1409. }
  1410. // 在线人数
  1411. if ([[groupInfo allKeys] containsObject:@"MEMBER_ONLINE"]) {
  1412. self.totalCount = [groupInfo ks_integerValueForKey:@"MEMBER_ONLINE"];
  1413. [self countMemberCount];
  1414. }
  1415. [self changeEmptyStatusView];
  1416. // 主播全员闭麦状态
  1417. if ([[groupInfo allKeys] containsObject:@"ANCHOR_MIC"]) {
  1418. if ([[groupInfo ks_stringValueForKey:@"ANCHOR_MIC"] isEqualToString:@"ON"]) { // 禁麦
  1419. if (self.muteRoomMic == NO) {
  1420. self.muteRoomMic = YES;
  1421. }
  1422. }
  1423. else {
  1424. if (self.muteRoomMic == YES) {
  1425. self.muteRoomMic = NO;
  1426. }
  1427. }
  1428. }
  1429. else {
  1430. if (self.muteRoomMic == YES) {
  1431. self.muteRoomMic = NO;
  1432. }
  1433. }
  1434. // 禁止连麦
  1435. if ([[groupInfo allKeys] containsObject:@"BAN_LINK_MIC"]) {
  1436. if ([[groupInfo ks_stringValueForKey:@"BAN_LINK_MIC"] isEqualToString:@"ON"]) { // 禁止
  1437. self.enableSeat = NO;
  1438. }
  1439. else {
  1440. self.enableSeat = YES;
  1441. }
  1442. }
  1443. else {
  1444. self.enableSeat = YES;
  1445. }
  1446. }
  1447. - (void)setMuteRoomMic:(BOOL)muteRoomMic {
  1448. _muteRoomMic = muteRoomMic;
  1449. [self changeCurrentMicStatus:muteRoomMic];
  1450. }
  1451. // 群属性变更信息
  1452. - (void)didReceiveGroupCountMessage:(NSNotification *)notification {
  1453. NSDictionary *dic = notification.object; /// __block TXLiveMessageModel *liveMessage
  1454. NSString *groupId = [dic ks_stringValueForKey:@"groupID"];
  1455. NSDictionary *content = [dic ks_dictionaryValueForKey:@"message"];
  1456. if ([groupId isEqualToString:self.roomId]) {
  1457. [self evaluateGroupCount:content];
  1458. }
  1459. }
  1460. - (void)evaluateGroupCount:(NSDictionary *)groupCount {
  1461. // 点赞数据
  1462. if ([[groupCount allKeys] containsObject:@"LIKES"]) {
  1463. self.likeCount = [groupCount ks_integerValueForKey:@"LIKES"];
  1464. [self countLikeMessageCount];
  1465. }
  1466. }
  1467. - (void)closePlayStatus {
  1468. if (self.micStatus == MICSTATUS_WAITING) {
  1469. [self sendSeatMessageApply:NO];
  1470. }
  1471. [self resetNetTips];
  1472. [self resetConnectionStatus];
  1473. [self closeAlertView];
  1474. [self hideApplyingView];
  1475. if (self.isAudience) {
  1476. [self stopPlay];
  1477. }
  1478. else {
  1479. if (_trtcCloud) {
  1480. [self.trtcCloud stopAllRemoteView];
  1481. [self.trtcCloud stopLocalAudio];
  1482. [self.trtcCloud exitRoom];
  1483. }
  1484. }
  1485. }
  1486. - (void)kickOutLiveRoom {
  1487. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"您已被踢出直播间"];
  1488. @weakObj(self);
  1489. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1490. @strongObj(self);
  1491. [self quitRoom];
  1492. });
  1493. }
  1494. #pragma mark ------ message operation
  1495. - (void)resetConnectionStatus {
  1496. if (self.micStatus == MICSTATUS_CONNECTING) {
  1497. @weakObj(self);
  1498. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1499. @strongObj(self);
  1500. [self sendDownSeatMsg];
  1501. [self connectHostWithStatus:NO];
  1502. });
  1503. }
  1504. else {
  1505. self.micStatus = MICSTATUS_NOMAL;
  1506. [self removeSeatContainer];
  1507. }
  1508. }
  1509. - (void)resetMicWaitToNomal {
  1510. if (self.micStatus == MICSTATUS_WAITING) {
  1511. self.micStatus = MICSTATUS_NOMAL;
  1512. }
  1513. }
  1514. - (void)tipsTeacherLeaveRoom {
  1515. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"主讲人已离开房间"];
  1516. }
  1517. - (void)hideApplyView {
  1518. if (self.applyView.isShow) {
  1519. [self.applyView hideView];
  1520. }
  1521. }
  1522. - (void)hideApplyingView {
  1523. if (self.applyingView.isShow) {
  1524. [self.applyingView hideView];
  1525. }
  1526. }
  1527. - (void)closeAlertView {
  1528. if (self.alertView && self.alertView.isShow) {
  1529. if (self.alertView.isQuitAlert == NO) {
  1530. [self.alertView removeFromSuperview];
  1531. self.alertView = nil;
  1532. }
  1533. }
  1534. }
  1535. - (void)displayCloseView {
  1536. self.isCloseRoom = YES;
  1537. [self hideTipsView];
  1538. // 移除连麦视图
  1539. [self removeSeatContainer];
  1540. if (self.micStatus != MICSTATUS_NOMAL) {
  1541. self.micStatus = MICSTATUS_NOMAL;
  1542. if (_trtcCloud) {
  1543. [self.trtcCloud stopLocalAudio];
  1544. [self.trtcCloud exitRoom];
  1545. }
  1546. }
  1547. [self stopPlay];
  1548. self.videoView.hidden = YES;
  1549. self.endView.hidden = NO;
  1550. [self setDefaultBottomViewStatus];
  1551. [[NSNotificationCenter defaultCenter] postNotificationName:@"LiveroomClose" object:nil];
  1552. }
  1553. - (void)sendCardMessage:(NSArray *)content {
  1554. // if (_webContainer) {
  1555. // NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
  1556. // [sendParm setValue:@"cardChange" forKey:@"api"];
  1557. // [sendParm setValue:content forKey:@"content"];
  1558. // [self.webContainer postMessage:sendParm];
  1559. // }
  1560. }
  1561. - (void)showInviteAlertMessage:(NSString *)tipsMessage {
  1562. if (self.alertView && self.alertView.isShow) {
  1563. [self.alertView removeFromSuperview];
  1564. self.alertView = nil;
  1565. }
  1566. MJWeakSelf;
  1567. self.alertView = [LiveAlertView shareInstance];
  1568. [self.alertView alertWithTitle:@"连麦邀请" desc:tipsMessage leftButtonTitle:@"取消" rightTitle:@"确定" inView:self.view cancel:^{
  1569. weakSelf.micStatus = MICSTATUS_NOMAL;
  1570. [weakSelf sendSeatResponseMessage:NO];
  1571. } confirm:^{
  1572. [weakSelf sendSeatResponseMessage:YES];
  1573. }];
  1574. }
  1575. - (void)sendSeatResponseMessage:(BOOL)isApprove {
  1576. SEATRESPONSE type = isApprove ? SEATRESPONSE_AUDIENCEAPPROVE : SEATRESPONSE_AUDIENCEREJECT;
  1577. TXLiveMessageSeatResponse *responseMessage = [[TXLiveMessageSeatResponse alloc] init];
  1578. responseMessage.userInfo = [self getCurrentUser];
  1579. responseMessage.type = type;
  1580. responseMessage.audienceId = UserDefault(IM_USERID);
  1581. responseMessage.audienceName = UserDefault(NicknameKey);
  1582. MJWeakSelf;
  1583. [self sendCustomMessage:responseMessage priority:V2TIM_PRIORITY_HIGH displayMessage:YES callback:^(BOOL success) {
  1584. if (success) {
  1585. if (isApprove) {
  1586. [weakSelf connectHostWithStatus:YES];
  1587. }
  1588. }
  1589. }];
  1590. }
  1591. - (void)kickSeatWithMessage:(NSString *)message {
  1592. [LOADING_MANAGER MBShowAUTOHidingInWindow:message];
  1593. [self sendDownSeatMsg];
  1594. [self connectHostWithStatus:NO];
  1595. }
  1596. - (void)insertMessage:(TXLiveMessageModel *)liveMessage {
  1597. [self appendAndDisplayMessage:liveMessage];
  1598. }
  1599. #pragma mark table data source
  1600. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  1601. return self.conversationDataRepository.count;
  1602. }
  1603. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  1604. TXLiveMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
  1605. TXLiveChatListCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TXLiveChatListCell"];
  1606. if (!cell) {
  1607. cell = [[TXLiveChatListCell appearance] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TXLiveChatListCell"];
  1608. }
  1609. if ([model isMemberOfClass:[TXConstMessage class]] || [model isMemberOfClass:[TXLiveTextMessage class]] || [model isMemberOfClass:[TXLiveMessageWelcome class]] || [model isMemberOfClass:[TXLiveMessageLike class]] || [model isMemberOfClass:[TXLiveMessageSeatControl class]] ||[model isMemberOfClass:[TXLiveMessageChatBan class]] || [model isMemberOfClass:[TXLiveMessageSeatApply class]] || [model isMemberOfClass:[TXLiveMessageSeatResponse class]] || [model isMemberOfClass:[TXLiveMessageShopRush class]] || [model isMemberOfClass:[TXLiveMessageDownSeatAll class]] || [model isMemberOfClass:[TXLiveMessageRejectAllSeat class]] ){
  1610. [cell setDataModel:model createrId:self.createrId];
  1611. }
  1612. return cell;
  1613. }
  1614. #pragma mark ----- lazying
  1615. - (TRTCCloud *)trtcCloud {
  1616. if (!_trtcCloud) {
  1617. _trtcCloud = [TRTCCloud sharedInstance];
  1618. [_trtcCloud addDelegate:self];
  1619. }
  1620. return _trtcCloud;
  1621. }
  1622. - (V2TXLivePlayer *)livePlayer {
  1623. if (!_livePlayer) {
  1624. _livePlayer = [[V2TXLivePlayer alloc] init];
  1625. [_livePlayer enableReceiveSeiMessage:YES payloadType:243];
  1626. [_livePlayer setObserver:self];
  1627. }
  1628. return _livePlayer;
  1629. }
  1630. - (UIView *)videoContainerView {
  1631. if (!_videoContainerView) {
  1632. _videoContainerView = [[UIView alloc] initWithFrame:CGRectZero];
  1633. _videoContainerView.backgroundColor = [UIColor clearColor];
  1634. }
  1635. return _videoContainerView;
  1636. }
  1637. - (UIView *)videoView {
  1638. if (!_videoView) {
  1639. _videoView = [[UIView alloc] initWithFrame:CGRectZero];
  1640. _videoView.backgroundColor = HexRGB(0x1a1a1a);
  1641. }
  1642. return _videoView;
  1643. }
  1644. - (TXLiveVideoView *)videoRenderView {
  1645. if (!_videoRenderView) {
  1646. _videoRenderView = [[TXLiveVideoView alloc] initWithFrame:CGRectZero];
  1647. }
  1648. return _videoRenderView;
  1649. }
  1650. - (LiveRoomHeadView *)headView {
  1651. if (!_headView) {
  1652. _headView = [LiveRoomHeadView shareInstance];
  1653. MJWeakSelf;
  1654. [_headView quitAction:^{
  1655. [weakSelf showQuitAlert];
  1656. }];
  1657. }
  1658. return _headView;
  1659. }
  1660. - (void)showQuitAlert {
  1661. if (self.isCloseRoom) { // 如果直播结束 直接点击退出房间
  1662. [self quitRoom];
  1663. }
  1664. else if (self.micStatus == MICSTATUS_CONNECTING) {
  1665. [self quitAction];
  1666. }
  1667. else {
  1668. MJWeakSelf;
  1669. self.alertView = [LiveAlertView shareInstance];
  1670. self.alertView.isQuitAlert = YES;
  1671. [self.alertView alertWithTitle:@"确定要退出直播间吗?" desc:@"退出后如再次观看可重新进入~" leftButtonTitle:@"取消" rightTitle:@"确认" inView:self.view cancel:^{
  1672. } confirm:^{
  1673. [weakSelf quitAction];
  1674. }];
  1675. }
  1676. }
  1677. - (void)quitRoom {
  1678. KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
  1679. if (manager.hasShowWindow) {
  1680. [manager resignDragWindow];
  1681. }
  1682. if (self.landScape) {
  1683. [self changeOrientation:!self.landScape];
  1684. @weakObj(self);
  1685. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  1686. @strongObj(self);
  1687. // 退出房间
  1688. [self exitRoom];
  1689. });
  1690. }
  1691. else {
  1692. // 退出房间
  1693. [self exitRoom];
  1694. }
  1695. }
  1696. // 顶部退出房间
  1697. - (void)quitAction {
  1698. if (self.micStatus == MICSTATUS_CONNECTING) {
  1699. MJWeakSelf;
  1700. self.alertView = [LiveAlertView shareInstance];
  1701. [self.alertView alertWithTitle:@"提示" desc:@"连麦中,是否退出房间?" leftButtonTitle:@"取消" rightTitle:@"确定" inView:self.view cancel:^{
  1702. } confirm:^{
  1703. [weakSelf quitRoom];
  1704. }];
  1705. }
  1706. else {
  1707. [self quitRoom];
  1708. }
  1709. }
  1710. - (void)exitRoom {
  1711. if (self.isAudience) {
  1712. [self stopPlay];
  1713. }
  1714. else {
  1715. if (_trtcCloud) {
  1716. [self.trtcCloud stopLocalAudio];
  1717. [self.trtcCloud exitRoom];
  1718. self.trtcCloud = nil;
  1719. }
  1720. }
  1721. TXLiveMessageLeave *leaveMsg = [[TXLiveMessageLeave alloc] init];
  1722. leaveMsg.userInfo = [self getCurrentUser];
  1723. MJWeakSelf;
  1724. [self sendCustomMessage:leaveMsg priority:V2TIM_PRIORITY_NORMAL displayMessage:NO callback:^(BOOL success) {
  1725. // quit 接口
  1726. [weakSelf quitNotiferService];
  1727. [weakSelf quitChatRoom];
  1728. [weakSelf.navigationController dismissViewControllerAnimated:YES completion:nil];
  1729. }];
  1730. }
  1731. - (void)quitChatRoom {
  1732. [[V2TIMManager sharedInstance] quitGroup:self.roomId succ:^{
  1733. } fail:^(int code, NSString *desc) {
  1734. }];
  1735. }
  1736. - (LiveRoomBottomView *)bottomView {
  1737. if (!_bottomView) {
  1738. _bottomView = [LiveRoomBottomView shareInstance];
  1739. MJWeakSelf;
  1740. [_bottomView bottomClickAction:^(LIVEROOMACTION action) {
  1741. [weakSelf bottomViewAction:action];
  1742. }];
  1743. }
  1744. return _bottomView;
  1745. }
  1746. - (void)bottomViewAction:(LIVEROOMACTION)action {
  1747. if (self.isCloseRoom) {
  1748. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"直播已结束"];
  1749. return;
  1750. }
  1751. if (action == LIVEROOMACTION_CHAT) {
  1752. if (self.blacklistFlag == YES) {
  1753. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"您已被管理员禁言"];
  1754. return;
  1755. }
  1756. if (self.enableChat) { // 判断是否全员禁言
  1757. [_inputBar setHidden:NO];
  1758. [_inputBar setInputBarStatus:KSBottomBarStatusKeyboard];
  1759. }
  1760. else {
  1761. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"管理员关闭聊天"];
  1762. }
  1763. }
  1764. else if (action == LIVEROOMACTION_SEAT) { // 连麦按钮
  1765. // 判断是否是连麦状态
  1766. if (self.micStatus == MICSTATUS_CONNECTING) { // 连麦中
  1767. // 是否取消连麦
  1768. MJWeakSelf;
  1769. self.alertView = [LiveAlertView shareInstance];
  1770. [self.alertView alertWithTitle:@"连麦中,确认取消连麦吗?" desc:@"断开后如想再次连麦可重新发起" leftButtonTitle:@"取消" rightTitle:@"确定关闭" inView:self.view cancel:^{
  1771. } confirm:^{
  1772. [weakSelf downSeatAction];
  1773. }];
  1774. }
  1775. else if (self.micStatus == MICSTATUS_WAITING) { // 连麦申请中
  1776. // 弹窗取消申请
  1777. [self displayApplyingView];
  1778. }
  1779. else {
  1780. // 禁止聊天可以连麦(测试确认)
  1781. if (self.blacklistFlag == YES) {
  1782. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"您已被管理员禁言"];
  1783. return;
  1784. }
  1785. if (self.isPauseLive == YES) {
  1786. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"老师暂停中,暂时无法接受申请"];
  1787. return;
  1788. }
  1789. if (self.enableSeat) {
  1790. [self displaySeatApplyView];
  1791. }
  1792. else {
  1793. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"管理员关闭连麦申请"];
  1794. }
  1795. }
  1796. }
  1797. else if (action == LIVEROOMACTION_LIKE) { // 点赞
  1798. [self praiseBtnPressed];
  1799. }
  1800. else if (action == LIVEROOMACTION_SHOP) { // shop
  1801. [self showCartAlert];
  1802. }
  1803. else if (action == LIVEROOMACTION_MUTE) {
  1804. [self muteVoiceAction];
  1805. }
  1806. }
  1807. - (void)showCartAlert {
  1808. [self.cardView showViewInView:self.view];
  1809. }
  1810. - (void)downSeatAction {
  1811. [self sendDownSeatMsg];
  1812. [self connectHostWithStatus:NO];
  1813. }
  1814. - (void)sendDownSeatMsg {
  1815. TXLiveMessageDownSeat *downSeatMsg = [[TXLiveMessageDownSeat alloc] init];
  1816. TXLiveUser *mineInfo = [self getCurrentUser];
  1817. downSeatMsg.userInfo = mineInfo;
  1818. downSeatMsg.audienceId = mineInfo.sendUserId;
  1819. downSeatMsg.audienceName = mineInfo.sendUserName;
  1820. [self sendCustomMessage:downSeatMsg priority:V2TIM_PRIORITY_HIGH displayMessage:NO callback:^(BOOL success) {
  1821. }];
  1822. }
  1823. - (KSShopCardView *)cardView {
  1824. if (!_cardView) {
  1825. _cardView = [KSShopCardView shareInstance];
  1826. MJWeakSelf;
  1827. [_cardView clickAction:^(CART_TYPE type) {
  1828. [weakSelf displayTeacherInfo:type];
  1829. }];
  1830. }
  1831. return _cardView;
  1832. }
  1833. // tabs= vip | VIP定制课 practice | 趣纠课 live | 直播课 video | 视频课 music | 乐谱 跳转到对应tab页
  1834. - (void)displayTeacherInfo:(CART_TYPE)type {
  1835. [self sendRushMessage];
  1836. if (self.isPauseLive == NO) {
  1837. _hasShowSuspendView = YES;
  1838. KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
  1839. BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
  1840. [manager initDragWindowWithPhoneLive:isPhoneLive];
  1841. manager.rootVC = (CustomNavViewController *)self.navigationController;
  1842. [self.videoRenderView removeFromSuperview];
  1843. [manager.dragWindow.containerView addSubview:self.videoRenderView];
  1844. [self.videoRenderView mas_updateConstraints:^(MASConstraintMaker *make) {
  1845. make.left.right.top.bottom.mas_equalTo(manager.dragWindow.containerView);
  1846. }];
  1847. [manager.dragWindow bringSubviewToFront:manager.dragWindow.cancelButton];
  1848. }
  1849. NSString *tabs = @"";
  1850. switch (type) {
  1851. case CART_TYPE_NONE:
  1852. {
  1853. tabs = @"";
  1854. }
  1855. break;
  1856. case CART_TYPE_VIP:
  1857. {
  1858. tabs = @"vip";
  1859. }
  1860. break;
  1861. case CART_TYPE_ACCOMPANY:
  1862. {
  1863. tabs = @"practice";
  1864. }
  1865. break;
  1866. case CART_TYPE_LIVE:
  1867. {
  1868. tabs = @"live";
  1869. }
  1870. break;
  1871. case CART_TYPE_VIDEO:
  1872. {
  1873. tabs = @"video";
  1874. }
  1875. break;
  1876. case CART_TYPE_MUSIC:
  1877. {
  1878. tabs = @"music";
  1879. }
  1880. break;
  1881. default:
  1882. break;
  1883. }
  1884. NSString *url = @"";
  1885. if ([NSString isEmptyString:tabs]) {
  1886. url = [NSString stringWithFormat:@"%@%@%@", WEBHOST,@"/#/teacherHome?teacherId=",self.createrUserId];
  1887. }
  1888. else {
  1889. url = [NSString stringWithFormat:@"%@%@%@&tabs=%@", WEBHOST,@"/#/teacherHome?teacherId=",self.createrUserId,tabs];
  1890. }
  1891. KSLiveWebViewController *ctrl = [[KSLiveWebViewController alloc] init];
  1892. ctrl.url = url;
  1893. [self.navigationController pushViewController:ctrl animated:YES];
  1894. }
  1895. - (void)sendRushMessage {
  1896. TXLiveMessageShopRush *rushMessage = [[TXLiveMessageShopRush alloc] init];
  1897. rushMessage.userInfo = [self getCurrentUser];
  1898. [self sendCustomMessage:rushMessage priority:V2TIM_PRIORITY_DEFAULT displayMessage:NO callback:^(BOOL success) {
  1899. }];
  1900. }
  1901. - (void)toDetailView:(NSString *)linkUrl {
  1902. [self sendRushMessage];
  1903. if (self.isPauseLive == NO) {
  1904. _hasShowSuspendView = YES;
  1905. KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
  1906. BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
  1907. [manager initDragWindowWithPhoneLive:isPhoneLive];
  1908. manager.rootVC = (CustomNavViewController *)self.navigationController;
  1909. [self.videoRenderView removeFromSuperview];
  1910. [manager.dragWindow.containerView addSubview:self.videoRenderView];
  1911. [self.videoRenderView mas_updateConstraints:^(MASConstraintMaker *make) {
  1912. make.left.right.top.bottom.mas_equalTo(manager.dragWindow.containerView);
  1913. }];
  1914. [manager.dragWindow bringSubviewToFront:manager.dragWindow.cancelButton];
  1915. }
  1916. KSLiveWebViewController *ctrl = [[KSLiveWebViewController alloc] init];
  1917. ctrl.url = linkUrl;
  1918. [self.navigationController pushViewController:ctrl animated:YES];
  1919. }
  1920. - (void)muteVoiceAction {
  1921. if (self.bottomView.isMute) {
  1922. if ([self.livePlayer isPlaying]) {
  1923. [self.livePlayer setPlayoutVolume:0];
  1924. }
  1925. if (_trtcCloud) {
  1926. [self.trtcCloud muteAllRemoteAudio:YES];
  1927. }
  1928. }
  1929. else {
  1930. if ([self.livePlayer isPlaying]) {
  1931. [self.livePlayer setPlayoutVolume:100];
  1932. }
  1933. if (_trtcCloud) {
  1934. [self.trtcCloud muteAllRemoteAudio:NO];
  1935. }
  1936. }
  1937. }
  1938. - (KSLiveEmptyView *)emptyView {
  1939. if (!_emptyView) {
  1940. _emptyView = [[KSLiveEmptyView alloc] init];
  1941. BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
  1942. [_emptyView dispPlayViewIsPhoneLive:isPhoneLive];
  1943. }
  1944. return _emptyView;
  1945. }
  1946. - (KSLiveEndView *)endView {
  1947. if (!_endView) {
  1948. _endView = [[KSLiveEndView alloc] init];
  1949. _endView.backgroundColor = HexRGB(0x25292e);
  1950. BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
  1951. [_endView dispPlayViewIsPhoneLive:isPhoneLive];
  1952. }
  1953. return _endView;
  1954. }
  1955. - (KSLiveBadNetView *)netBadView {
  1956. if (!_netBadView) {
  1957. _netBadView = [[KSLiveBadNetView alloc] init];
  1958. BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
  1959. [_netBadView dispPlayViewIsPhoneLive:isPhoneLive];
  1960. MJWeakSelf;
  1961. [_netBadView configViewWithImageName:@"liveVideo_close" desc:@"视频加载失败" callback:^{
  1962. [weakSelf resetAllConnection];
  1963. }];
  1964. }
  1965. return _netBadView;
  1966. }
  1967. - (void)resetNetTips {
  1968. [self hideTipsView];
  1969. }
  1970. - (void)showBadNetTips {
  1971. dispatch_main_async_safe(^{
  1972. if (self.isCloseRoom == NO) {
  1973. if (self.netBadView.hidden == YES) {
  1974. self.netBadView.hidden = NO;
  1975. self.videoView.hidden = YES;
  1976. }
  1977. }
  1978. });
  1979. }
  1980. - (void)hideTipsView {
  1981. dispatch_main_async_safe(^{
  1982. if (self.netBadView.hidden == NO) {
  1983. self.netBadView.hidden = YES;
  1984. self.videoView.hidden = NO;
  1985. }
  1986. })
  1987. }
  1988. // 黑名单
  1989. - (void)setBlacklistFlag:(BOOL)blacklistFlag {
  1990. _blacklistFlag = blacklistFlag;
  1991. [self refreshBottomChatStatus];
  1992. }
  1993. - (void)refreshBottomChatStatus {
  1994. NSString *tipsTitle = self.blacklistFlag ? @"您已被管理员禁言" : @"快来互动吧!";
  1995. self.bottomView.tipsLabel.text = tipsTitle;
  1996. self.bottomView.tipsLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
  1997. }
  1998. - (void)resetAllConnection {
  1999. [self resetNetTips];
  2000. // 清理视图
  2001. [self hideEmptyView];
  2002. [self removeSeatContainer];
  2003. // 立刻房间后重新加入房间
  2004. [self startPlay];
  2005. }
  2006. - (LOTAnimationView *)animationView {
  2007. if (!_animationView) {
  2008. _animationView = [LOTAnimationView animationWithFilePath:[[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"LiveCard" ofType:@"bundle"]] pathForResource:@"cardAnimation" ofType:@"json"]];
  2009. _animationView.loopAnimation = YES;
  2010. }
  2011. return _animationView;
  2012. }
  2013. - (UIButton *)switchButton {
  2014. if (!_switchButton) {
  2015. _switchButton = [UIButton buttonWithType:UIButtonTypeCustom];
  2016. [_switchButton setImage:[UIImage imageNamed:@"live_switch"] forState:UIControlStateNormal];
  2017. [_switchButton addTarget:self action:@selector(transferToLandscape) forControlEvents:UIControlEventTouchUpInside];
  2018. }
  2019. return _switchButton;
  2020. }
  2021. - (UIView *)messageContentView {
  2022. if (!_messageContentView) {
  2023. _messageContentView = [[UIView alloc] init];
  2024. [_messageContentView setBackgroundColor: [UIColor clearColor]];
  2025. }
  2026. return _messageContentView;
  2027. }
  2028. - (UITableView *)conversationMessageTableView {
  2029. if (!_conversationMessageTableView) {
  2030. _conversationMessageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
  2031. _conversationMessageTableView.backgroundColor = [UIColor clearColor];
  2032. _conversationMessageTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
  2033. _conversationMessageTableView.dataSource = self;
  2034. _conversationMessageTableView.delegate = self;
  2035. _conversationMessageTableView.rowHeight = UITableViewAutomaticDimension;
  2036. _conversationMessageTableView.estimatedRowHeight = 40.0f;
  2037. _conversationMessageTableView.showsVerticalScrollIndicator = NO;
  2038. [_conversationMessageTableView registerClass:[TXLiveChatListCell class] forCellReuseIdentifier:@"TXLiveChatListCell"];
  2039. }
  2040. return _conversationMessageTableView;
  2041. }
  2042. - (NSMutableArray<TXLiveMessageModel *> *)conversationDataRepository {
  2043. if (!_conversationDataRepository) {
  2044. _conversationDataRepository = [NSMutableArray array];
  2045. }
  2046. return _conversationDataRepository;
  2047. }
  2048. - (TXSeatContainerView *)seatContainer {
  2049. if (!_seatContainer) {
  2050. _seatContainer = [[TXSeatContainerView alloc] init];
  2051. MJWeakSelf;
  2052. [_seatContainer seatActionCallback:^(NSString * _Nonnull userId, BOOL muteMic) {
  2053. [weakSelf muteUserAction:userId muteStatus:muteMic];
  2054. }];
  2055. }
  2056. return _seatContainer;
  2057. }
  2058. - (void)muteUserAction:(NSString *)userId muteStatus:(BOOL)muteMic {
  2059. [self micButtonAction:muteMic];
  2060. TXMicStatusSync *ctrlMsg = [[TXMicStatusSync alloc] init];
  2061. ctrlMsg.userInfo = [self getCurrentUser];
  2062. ctrlMsg.userId = userId;
  2063. ctrlMsg.muteMic = muteMic;
  2064. [self sendCustomMessage:ctrlMsg priority:V2TIM_PRIORITY_HIGH displayMessage:NO callback:^(BOOL success) {
  2065. }];
  2066. }
  2067. - (KSChatInputBarControl *)inputBar {
  2068. if (!_inputBar) {
  2069. _inputBar = [[KSChatInputBarControl alloc] initWithStatus:KSBottomBarStatusDefault];
  2070. [_inputBar setDelegate:self];
  2071. }
  2072. return _inputBar;
  2073. }
  2074. - (void)setMicStatus:(MICSTATUS)micStatus {
  2075. _micStatus = micStatus;
  2076. if (self.bottomView) {
  2077. switch (micStatus) {
  2078. case MICSTATUS_NOMAL:
  2079. {
  2080. [self.bottomView.seatButton setImage:[UIImage imageNamed:@"seat_image"] forState:UIControlStateNormal];
  2081. }
  2082. break;
  2083. case MICSTATUS_WAITING:
  2084. {
  2085. [self.bottomView.seatButton setImage:[UIImage imageNamed:@"seat_connecting"] forState:UIControlStateNormal];
  2086. }
  2087. break;
  2088. case MICSTATUS_CONNECTING:
  2089. {
  2090. [self.bottomView.seatButton setImage:[UIImage imageNamed:@"seat_connected"] forState:UIControlStateNormal];
  2091. }
  2092. break;
  2093. default:
  2094. break;
  2095. }
  2096. }
  2097. }
  2098. #pragma mark ----- 人数和点赞数统计
  2099. - (void)countLikeMessageCount {
  2100. NSInteger count = self.likeCount;
  2101. NSString *desc = @"";
  2102. if (count > 10000) {
  2103. count = count / 10000;
  2104. desc = [NSString stringWithFormat:@"本场点赞%zd万+",count];
  2105. }
  2106. else {
  2107. desc = [NSString stringWithFormat:@"本场点赞%zd",count];
  2108. }
  2109. self.headView.likeCount.text = desc;
  2110. }
  2111. - (void)countMemberCount {
  2112. NSInteger count = self.totalCount;
  2113. NSString *desc = @"";
  2114. if (count > 10000) {
  2115. count = count / 10000;
  2116. desc = [NSString stringWithFormat:@"%zd万+人",count];
  2117. }
  2118. else {
  2119. desc = [NSString stringWithFormat:@"%zd人",count];
  2120. }
  2121. self.headView.roomMemberCount.text = desc;
  2122. }
  2123. /**
  2124. * 判断消息是否在collectionView的底部
  2125. *
  2126. * @return 是否在底部
  2127. */
  2128. - (BOOL)isAtTheBottomOfTableView {
  2129. if (self.conversationMessageTableView.contentSize.height <= self.conversationMessageTableView.frame.size.height) {
  2130. return YES;
  2131. }
  2132. if(self.conversationMessageTableView.contentOffset.y +200 >= (self.conversationMessageTableView.contentSize.height - self.conversationMessageTableView.frame.size.height)) {
  2133. return YES;
  2134. }else{
  2135. return NO;
  2136. }
  2137. }
  2138. /**
  2139. * 消息滚动到底部
  2140. *
  2141. * @param animated 是否开启动画效果
  2142. */
  2143. - (void)scrollToBottomAnimated:(BOOL)animated {
  2144. if ([self.conversationMessageTableView numberOfSections] == 0) {
  2145. return;
  2146. }
  2147. NSUInteger finalRow = MAX(0, [self.conversationMessageTableView numberOfRowsInSection:0] - 1);
  2148. if (0 == finalRow) {
  2149. return;
  2150. }
  2151. NSIndexPath *finalIndexPath =
  2152. [NSIndexPath indexPathForItem:finalRow inSection:0];
  2153. [self.conversationMessageTableView scrollToRowAtIndexPath:finalIndexPath atScrollPosition:UITableViewScrollPositionTop animated:animated];
  2154. }
  2155. #pragma mark ---- dealloc
  2156. - (void)dealloc {
  2157. NSLog(@"---- live room dealloc");
  2158. [[NSNotificationCenter defaultCenter] removeObserver:self];
  2159. if (_livePlayer) {
  2160. [self.livePlayer stopPlay];
  2161. _livePlayer = nil;
  2162. }
  2163. if (_trtcCloud) {
  2164. [self.trtcCloud stopAllRemoteView];
  2165. [self.trtcCloud stopLocalAudio];
  2166. [self.trtcCloud exitRoom];
  2167. [self.trtcCloud removeDelegate:self];
  2168. _trtcCloud = nil;
  2169. [TRTCCloud destroySharedInstance];
  2170. }
  2171. }
  2172. #pragma mark - KSChatInputBarControlDelegate
  2173. // 根据inputBar 回调来修改页面布局
  2174. - (void)onInputBarControlContentSizeChanged:(CGRect)frame withAnimationDuration:(CGFloat)duration andAnimationCurve:(UIViewAnimationCurve)curve {
  2175. CGRect originFrame = _inputBar.frame;
  2176. __weak __typeof(&*self)weakSelf = self;
  2177. // 目前只使用y值即可 -- 只修改messageContentView高度,让展示消息view和输入框随之移动
  2178. [UIView animateWithDuration:duration animations:^{
  2179. [UIView setAnimationCurve:curve];
  2180. [weakSelf.inputBar setFrame:CGRectMake(0, frame.origin.y - originFrame.size.height, originFrame.size.width, originFrame.size.height)];
  2181. [UIView commitAnimations];
  2182. }];
  2183. }
  2184. // 发送消息
  2185. - (void)onTouchSendButton:(NSString *)text {
  2186. if (self.blacklistFlag == YES) {
  2187. [self setDefaultBottomViewStatus];
  2188. return;
  2189. }
  2190. NSDate *date = [NSDate date];
  2191. NSTimeInterval currentInterval = [date timeIntervalSince1970];
  2192. if (currentInterval - self.lastSendMsgTime < 3) {
  2193. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"您说话太快啦"];
  2194. return;
  2195. }
  2196. if (self.lastSendMsgTime == 0) {
  2197. self.lastSendMsgTime = currentInterval;
  2198. }
  2199. if (text.length > 40) {
  2200. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"聊天消息需在40个字以内哦"];
  2201. return;
  2202. }
  2203. if (self.enableChat) {
  2204. self.lastSendMsgTime = currentInterval;
  2205. [self touristSendMessage:text];
  2206. }
  2207. else {
  2208. [self setDefaultBottomViewStatus];
  2209. }
  2210. }
  2211. - (void)touristSendMessage:(NSString *)text {
  2212. TXLiveTextMessage *message = [[TXLiveTextMessage alloc] init];
  2213. message.userInfo = [self getCurrentUser];
  2214. message.text = text;
  2215. MJWeakSelf;
  2216. [self sendTextMessage:message priority:V2TIM_PRIORITY_NORMAL displayMessage:YES callback:^(BOOL success) {
  2217. [weakSelf setDefaultBottomViewStatus];
  2218. }];
  2219. }
  2220. - (TXLiveUser *)getCurrentUser {
  2221. TXLiveUser *user = [[TXLiveUser alloc] init];
  2222. user.sendUserId = UserDefault(IM_USERID);
  2223. user.sendUserName = UserDefault(NicknameKey);
  2224. user.avatarUrl = UserDefault(AvatarUrlKey);
  2225. return user;
  2226. }
  2227. /**
  2228. 点赞
  2229. */
  2230. - (void)praiseBtnPressed {
  2231. if (self.enableLike == NO) {
  2232. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"管理员已关闭点赞功能"];
  2233. return;
  2234. }
  2235. NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
  2236. @weakObj(self);
  2237. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.21 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  2238. @strongObj(self);
  2239. if ([[NSDate date] timeIntervalSince1970] - self.lastClickPraiseTime >= 0.2) {
  2240. [self modifyLikeCount:clickPraiseBtnTimes];
  2241. clickPraiseBtnTimes = 0;
  2242. }
  2243. });
  2244. clickPraiseBtnTimes++;
  2245. self.lastClickPraiseTime = currentTime;
  2246. [self presentLikeMessageAnimation];
  2247. }
  2248. - (void)modifyLikeCount:(NSInteger)count {
  2249. [[V2TIMManager sharedInstance] increaseGroupCounter:self.roomId key:@"LIKES" value:count succ:^(NSDictionary<NSString *,NSNumber *> *groupCounters) {
  2250. } fail:^(int code, NSString *desc) {
  2251. }];
  2252. }
  2253. /**
  2254. 赞动画
  2255. */
  2256. - (void)presentLikeMessageAnimation {
  2257. LiveRoomLikeLayer *layer = [[LiveRoomLikeLayer alloc] initWithName:@"Like"];
  2258. [self.videoContainerView.layer addSublayer:layer];
  2259. CGRect tempFrame = [self.videoContainerView convertRect:self.bottomView.likeButton.frame fromView:self.bottomView];
  2260. CGFloat width = tempFrame.size.width;
  2261. CGFloat height = tempFrame.size.height;
  2262. CGFloat dx = CGRectGetMidX(tempFrame) - width *0.5;
  2263. CGFloat dy = CGRectGetMidY(tempFrame) - 8 - height;
  2264. layer.frame = CGRectMake(dx, dy, width, height);
  2265. [layer startAnimation];
  2266. }
  2267. - (void)displaySeatApplyView {
  2268. [self.applyView showInView:self.view];
  2269. }
  2270. - (void)displayApplyingView {
  2271. [self.applyingView showInView:self.view];
  2272. }
  2273. - (LiveApplyView *)applyView {
  2274. if (!_applyView) {
  2275. _applyView = [LiveApplyView shareInstance];
  2276. MJWeakSelf;
  2277. [_applyView applySeatCallback:^{
  2278. [weakSelf applySeatAction];
  2279. }];
  2280. }
  2281. return _applyView;
  2282. }
  2283. - (void)applySeatAction {
  2284. [self sendSeatMessageApply:YES];
  2285. }
  2286. - (void)sendSeatMessageApply:(BOOL)isApply {
  2287. if (self.micStatus == MICSTATUS_CONNECTING) {
  2288. [LOADING_MANAGER MBShowAUTOHidingInWindow:@"您已上麦"];
  2289. return;
  2290. }
  2291. SEATHANDLE type = isApply ? SEATHANDLE_APPLY : SEATHANDLE_CANCELAPPLY;
  2292. TXLiveMessageSeatApply *applyMessage = [[TXLiveMessageSeatApply alloc] init];
  2293. applyMessage.type = type;
  2294. applyMessage.audienceId = UserDefault(IM_USERID);
  2295. applyMessage.audienceName = UserDefault(NicknameKey);
  2296. applyMessage.audienceAvatar = UserDefault(AvatarUrlKey);
  2297. applyMessage.teacherId = self.createrId;
  2298. applyMessage.teacherName = self.createrName;
  2299. applyMessage.userInfo = [self getCurrentUser];
  2300. MJWeakSelf;
  2301. [self sendCustomMessage:applyMessage priority:V2TIM_PRIORITY_HIGH displayMessage:YES callback:^(BOOL success) {
  2302. if (success) {
  2303. weakSelf.micStatus = isApply ? MICSTATUS_WAITING : MICSTATUS_NOMAL;
  2304. }
  2305. }];
  2306. }
  2307. - (LiveApplyingView *)applyingView {
  2308. if (!_applyingView) {
  2309. _applyingView = [LiveApplyingView shareInstance];
  2310. NSString *avatar = UserDefaultObjectForKey(AvatarUrlKey);
  2311. [_applyingView.myAvatal sd_setImageWithURL:[NSURL URLWithString:[avatar getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:STUDENT_AVATAR]];
  2312. [_applyingView.teacherAvatal sd_setImageWithURL:[NSURL URLWithString:[self.createrAvatal getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:TEACHER_AVATAR]];
  2313. MJWeakSelf;
  2314. [_applyingView cancleApplyCallback:^{
  2315. [weakSelf cancleApplyAction];
  2316. }];
  2317. }
  2318. return _applyingView;
  2319. }
  2320. - (void)cancleApplyAction {
  2321. MJWeakSelf;
  2322. self.alertView = [LiveAlertView shareInstance];
  2323. [self.alertView alertWithTitle:@"连麦申请中,确认取消连麦申请吗?" desc:@"断开后如想再次连麦可重新发起" leftButtonTitle:@"取消" rightTitle:@"确定关闭" inView:self.view cancel:^{
  2324. } confirm:^{
  2325. [weakSelf.applyingView hideView];
  2326. [weakSelf sendSeatMessageApply:NO];
  2327. }];
  2328. }
  2329. #pragma mark ---- sendMessage/showMessage
  2330. // 文本消息
  2331. - (void)sendTextMessage:(TXLiveMessageModel *)messageContent priority:(V2TIMMessagePriority)priority displayMessage:(BOOL)displayMessage callback:(void(^)(BOOL success))callback {
  2332. NSData *msgData = [messageContent encodeMessage];
  2333. if (msgData == nil) {
  2334. return;
  2335. }
  2336. __weak typeof(&*self) __weakself = self;
  2337. [TXLiveMessageCenter sendLiveChatMessage:msgData groupId:self.roomId priority:priority success:^{
  2338. dispatch_async(dispatch_get_main_queue(), ^{
  2339. if (displayMessage) {
  2340. [__weakself appendAndDisplayMessage:messageContent];
  2341. }
  2342. [__weakself.inputBar clearInputView];
  2343. callback(YES);
  2344. });
  2345. } failer:^(int code, NSString * _Nonnull msg) {
  2346. dispatch_async(dispatch_get_main_queue(), ^{
  2347. [__weakself.inputBar clearInputView];
  2348. NSLog(@"发送失败,errorcode is: %d message: %@",code, msg);
  2349. callback(NO);
  2350. });
  2351. }];
  2352. }
  2353. // 其他消息
  2354. - (void)sendCustomMessage:(TXLiveMessageModel *)messageContent priority:(V2TIMMessagePriority)priority displayMessage:(BOOL)displayMessage callback:(void(^)(BOOL success))callback {
  2355. NSData *msgData = [messageContent encodeMessage];
  2356. if (msgData == nil) {
  2357. return;
  2358. }
  2359. __weak typeof(&*self) __weakself = self;
  2360. [TXLiveMessageCenter sendLiveChatMessage:msgData groupId:self.roomId priority:priority success:^{
  2361. dispatch_async(dispatch_get_main_queue(), ^{
  2362. if (displayMessage) {
  2363. [__weakself appendAndDisplayMessage:messageContent];
  2364. }
  2365. callback(YES);
  2366. });
  2367. } failer:^(int code, NSString * _Nonnull msg) {
  2368. dispatch_async(dispatch_get_main_queue(), ^{
  2369. NSLog(@"发送失败,errorcode is: %d message: %@",code, msg);
  2370. callback(NO);
  2371. });
  2372. }];
  2373. }
  2374. /**
  2375. * 将消息加入本地数组
  2376. */
  2377. - (void)appendAndDisplayMessage:(TXLiveMessageModel *)liveMessage {
  2378. if (!liveMessage) {
  2379. return;
  2380. }
  2381. if ([self appendMessageModel:liveMessage]) {
  2382. NSIndexPath *indexPath =
  2383. [NSIndexPath indexPathForItem:self.conversationDataRepository.count - 1
  2384. inSection:0];
  2385. if ([self.conversationMessageTableView numberOfRowsInSection:0] !=
  2386. self.conversationDataRepository.count - 1) {
  2387. return;
  2388. }
  2389. // view刷新
  2390. [self.conversationMessageTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
  2391. if ([self isAtTheBottomOfTableView] || self.isNeedScrollToButtom) {
  2392. [self scrollToBottomAnimated:NO];
  2393. self.isNeedScrollToButtom=NO;
  2394. }
  2395. }
  2396. return;
  2397. }
  2398. /**
  2399. * 如果当前会话没有这个消息id,把消息加入本地数组
  2400. */
  2401. - (BOOL)appendMessageModel:(TXLiveMessageModel *)model {
  2402. if (!model) {
  2403. return NO;
  2404. }
  2405. //这里可以根据消息类型来决定是否显示,如果不希望显示直接return NO
  2406. //数量不可能无限制的大,这里限制收到消息过多时,就对显示消息数量进行限制。
  2407. //用户可以手动下拉更多消息,查看更多历史消息。
  2408. if (self.conversationDataRepository.count>500) {
  2409. [self.conversationDataRepository removeObjectAtIndex:0];
  2410. [self.conversationMessageTableView reloadData];
  2411. }
  2412. [self.conversationDataRepository addObject:model];
  2413. return YES;
  2414. }
  2415. - (void)showAnimationView:(BOOL)isJoinRoom showMessag:(NSString *)message {
  2416. if (self.landScape) {
  2417. return;
  2418. }
  2419. if (self.enterAnimationView && self.enterAnimationView.isShow) {
  2420. return;
  2421. }
  2422. else {
  2423. ANIMATIONTYPE type = isJoinRoom ? ANIMATIONTYPE_JOIN : ANIMATIONTYPE_RUSH;
  2424. self.enterAnimationView = [[LiveAnimationView alloc] initWithTitle:message animationType:type];
  2425. [self.view addSubview:self.enterAnimationView];
  2426. [self.enterAnimationView mas_makeConstraints:^(MASConstraintMaker *make) {
  2427. make.left.right.mas_equalTo(self.view);
  2428. make.width.mas_equalTo(KPortraitWidth);
  2429. make.height.mas_equalTo(24.0f);
  2430. make.bottom.mas_equalTo(self.messageContentView.mas_top).offset(-6);
  2431. }];
  2432. [self.view bringSubviewToFront:self.enterAnimationView];
  2433. MJWeakSelf;
  2434. [self.enterAnimationView startAnimationEndCallback:^{
  2435. weakSelf.enterAnimationView.isShow = NO;
  2436. weakSelf.enterAnimationView = nil;
  2437. }];
  2438. }
  2439. }
  2440. - (NSMutableArray *)remoteMemberArray {
  2441. if (!_remoteMemberArray) {
  2442. _remoteMemberArray = [NSMutableArray array];
  2443. }
  2444. return _remoteMemberArray;
  2445. }
  2446. - (TXLiveLoadingView *)loadingView {
  2447. if (!_loadingView) {
  2448. _loadingView = [TXLiveLoadingView shareInstance];
  2449. }
  2450. return _loadingView;
  2451. }
  2452. - (void)showLoading {
  2453. if (![self.videoView.subviews containsObject:self.loadingView]) {
  2454. [self.loadingView showLoadingView:self.videoView];
  2455. }
  2456. }
  2457. - (void)hideLoading {
  2458. if ([self.videoView.subviews containsObject:self.loadingView]) {
  2459. [self.loadingView hideLoadingView];
  2460. }
  2461. }
  2462. - (void)micButtonAction:(BOOL)isMute {
  2463. if (isMute == !self.micEnable) {
  2464. return;
  2465. }
  2466. if (_trtcCloud && self.isAudience == NO) {
  2467. self.micEnable = !isMute;
  2468. [self.trtcCloud muteLocalAudio:!self.micEnable];
  2469. [self syncMicStatus:!self.micEnable];
  2470. }
  2471. }
  2472. - (void)syncMicStatus:(BOOL)isMute {
  2473. TXMicStatusSync *syncMic = [[TXMicStatusSync alloc] init];
  2474. syncMic.userInfo = [self getCurrentUser];
  2475. syncMic.userId = UserDefault(IM_USERID);
  2476. syncMic.muteMic = isMute;
  2477. [self sendCustomMessage:syncMic priority:V2TIM_PRIORITY_HIGH displayMessage:NO callback:^(BOOL success) {
  2478. }];
  2479. [self renderSeatView];
  2480. }
  2481. #pragma mark ---- 3A config
  2482. - (void)setSubjectId:(NSString *)subjectId {
  2483. _subjectId = subjectId;
  2484. // 设置 3A参数
  2485. self.ANS = 0;
  2486. self.AEC = 100;
  2487. // 长号14和萨克斯5 AGC 一样
  2488. if ([subjectId isEqualToString:@"5"] || [subjectId isEqualToString:@"14"]) {
  2489. self.AGC = 45;
  2490. }
  2491. else if ([subjectId isEqualToString:@"16"]) { // 次中音号
  2492. self.AGC = 80;
  2493. }
  2494. else if ([subjectId isEqualToString:@"12"]) {
  2495. self.AGC = 50;
  2496. }
  2497. else {
  2498. self.AGC = 0;
  2499. }
  2500. }
  2501. /*
  2502. #pragma mark - Navigation
  2503. // In a storyboard-based application, you will often want to do a little preparation before navigation
  2504. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  2505. // Get the new view controller using [segue destinationViewController].
  2506. // Pass the selected object to the new view controller.
  2507. }
  2508. */
  2509. @end