TXLiveRoomViewController.m 101 KB

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