|
@@ -0,0 +1,2792 @@
|
|
|
+//
|
|
|
+// TXLiveRoomViewController.m
|
|
|
+// StudentDaya
|
|
|
+//
|
|
|
+// Created by 王智 on 2023/2/21.
|
|
|
+// Copyright © 2023 DayaMusic. All rights reserved.
|
|
|
+//
|
|
|
+
|
|
|
+#import "TXLiveRoomViewController.h"
|
|
|
+#import "TXLiteAVSDK_Professional/TXLiteAVSDK.h"
|
|
|
+#import "KSLiveEmptyView.h"
|
|
|
+#import "TXLiveVideoView.h"
|
|
|
+#import "LiveRoomHeadView.h"
|
|
|
+#import "LiveRoomBottomView.h"
|
|
|
+#import <Lottie/Lottie.h>
|
|
|
+#import "KSChatInputBarControl.h"
|
|
|
+#import "TXLiveChatListCell.h"
|
|
|
+#import "AppDelegate.h"
|
|
|
+#import "UIDevice+TFDevice.h"
|
|
|
+#import "LiveRoomLikeLayer.h"
|
|
|
+#import "LiveAlertView.h"
|
|
|
+#import "KSShopCardView.h"
|
|
|
+#import "KSLiveWebViewController.h"
|
|
|
+#import "KSLiveBadNetView.h"
|
|
|
+
|
|
|
+#import "KSLiveEndView.h"
|
|
|
+#import "LiveAnimationView.h"
|
|
|
+#import "LiveApplyView.h"
|
|
|
+#import "LiveApplyingView.h"
|
|
|
+#import <ImSDK_Plus/ImSDK_Plus.h>
|
|
|
+#import "TXIMLinsenter.h"
|
|
|
+
|
|
|
+#import "TXLiveMessageModel.h"
|
|
|
+#import "TXLiveURLUtils.h"
|
|
|
+#import "TXLiveMessageCenter.h"
|
|
|
+#import "KSDragWindowManager.h"
|
|
|
+#import "TXLiveLoadingView.h"
|
|
|
+#import "TXUISeatMember.h"
|
|
|
+#import "TXSeatContainerView.h"
|
|
|
+#import "LiveRoomConfirmAlert.h"
|
|
|
+#import "LiveModuleService.h"
|
|
|
+
|
|
|
+
|
|
|
+#define AUTOHIDE_TIME (10.0f)
|
|
|
+
|
|
|
+typedef NS_ENUM(NSInteger, MICSTATUS) {
|
|
|
+ MICSTATUS_NOMAL, // 正常状态
|
|
|
+ MICSTATUS_WAITING, // 申请中
|
|
|
+ MICSTATUS_CONNECTING, // 连麦中
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+@interface TXLiveRoomViewController ()<UIGestureRecognizerDelegate,KSChatInputBarControlDelegate,UITableViewDataSource,UITableViewDelegate,V2TXLivePlayerObserver,TRTCCloudDelegate>
|
|
|
+
|
|
|
+// 是否是观众
|
|
|
+@property (nonatomic, assign) BOOL isAudience;
|
|
|
+// rtc 连麦时使用
|
|
|
+@property (nonatomic, strong) TRTCCloud *trtcCloud;
|
|
|
+
|
|
|
+@property (nonatomic, strong) V2TXLivePlayer *livePlayer;
|
|
|
+
|
|
|
+@property (nonatomic, strong) LiveRoomHeadView *headView;
|
|
|
+
|
|
|
+@property (nonatomic, copy) LiveRoomBottomView *bottomView;
|
|
|
+
|
|
|
+// 视频主窗口容器
|
|
|
+@property (nonatomic, strong) UIView *videoContainerView;
|
|
|
+
|
|
|
+// 主屏videoView
|
|
|
+@property (nonatomic, strong) UIView *videoView;
|
|
|
+
|
|
|
+// 画面渲染的画面
|
|
|
+@property (nonatomic, strong) TXLiveVideoView *videoRenderView;
|
|
|
+
|
|
|
+// 切换按钮
|
|
|
+@property (nonatomic, strong) UIButton *switchButton;
|
|
|
+
|
|
|
+@property (nonatomic, strong) LOTAnimationView *animationView;
|
|
|
+/// 连麦的视图
|
|
|
+@property (nonatomic, strong) TXSeatContainerView *seatContainer;
|
|
|
+// 聊天UI
|
|
|
+/*!
|
|
|
+ 消息列表CollectionView和输入框都在这个view里
|
|
|
+ */
|
|
|
+@property(nonatomic, strong) UIView *messageContentView;
|
|
|
+
|
|
|
+/*!
|
|
|
+ 会话页面的TableView
|
|
|
+ */
|
|
|
+@property (nonatomic, strong) UITableView *conversationMessageTableView;
|
|
|
+
|
|
|
+/*!
|
|
|
+ 聊天内容的消息Cell数据模型的数据源
|
|
|
+
|
|
|
+ @discussion 数据源中存放的元素为消息Cell的数据模型,即RCDLiveMessageModel对象。
|
|
|
+ */
|
|
|
+@property(nonatomic, strong) NSMutableArray<TXLiveMessageModel *> *conversationDataRepository;
|
|
|
+
|
|
|
+/**
|
|
|
+ 输入工具栏
|
|
|
+ */
|
|
|
+@property(nonatomic,strong) KSChatInputBarControl *inputBar;
|
|
|
+/**
|
|
|
+ * 是否需要滚动到底部
|
|
|
+ */
|
|
|
+@property(nonatomic, assign) BOOL isNeedScrollToButtom;
|
|
|
+
|
|
|
+/**
|
|
|
+ 上次点赞按钮点击时间
|
|
|
+ */
|
|
|
+@property(nonatomic, assign) NSTimeInterval lastClickPraiseTime;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL landScape;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isHiddenHeadView;
|
|
|
+
|
|
|
+// 连麦状态
|
|
|
+@property (nonatomic, assign) MICSTATUS micStatus;
|
|
|
+
|
|
|
+@property (nonatomic, strong) LiveApplyView *applyView; // 未申请
|
|
|
+
|
|
|
+@property (nonatomic, strong) LiveApplyingView *applyingView; // 申请中
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isCreaterVideoEnable;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isCloseRoom; // 是否直播结束
|
|
|
+/// 提示窗
|
|
|
+@property (nonatomic, strong) LiveAlertView *alertView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSShopCardView *cardView;
|
|
|
+
|
|
|
+@property (nonatomic, assign) NSTimeInterval lastSendMsgTime;
|
|
|
+
|
|
|
+@property (nonatomic, strong) LiveAnimationView *enterAnimationView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSLiveEndView *endView; // 直播结束显示的画面
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSLiveEmptyView *emptyView;
|
|
|
+
|
|
|
+@property (nonatomic, strong) KSLiveBadNetView *netBadView;
|
|
|
+/// 是否在切换身份
|
|
|
+@property (nonatomic, assign) BOOL isTransferRole;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL hasSendWelcomeMessage;
|
|
|
+
|
|
|
+// 远端连麦用户
|
|
|
+@property (nonatomic, strong) NSMutableArray *remoteMemberArray;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL isPauseLive;
|
|
|
+
|
|
|
+@property (nonatomic, strong) TXLiveLoadingView *loadingView;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL hasShowSuspendView;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL muteRoomMic;
|
|
|
+
|
|
|
+@property (nonatomic, assign) BOOL micEnable;
|
|
|
+
|
|
|
+@property (nonatomic, strong) LiveRoomConfirmAlert *closeAlert;
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+// 用于记录点赞消息连续点击的次数
|
|
|
+static int clickPraiseBtnTimes = 0;
|
|
|
+
|
|
|
+@implementation TXLiveRoomViewController
|
|
|
+
|
|
|
+- (instancetype)init {
|
|
|
+ self = [super init];
|
|
|
+ if (self) {
|
|
|
+ [self defaultConfig];
|
|
|
+ [self default3AConfig];
|
|
|
+ }
|
|
|
+ return self;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)default3AConfig {
|
|
|
+ self.ANS = 0;
|
|
|
+ self.AEC = 100;
|
|
|
+ self.AGC = 0;
|
|
|
+}
|
|
|
+- (void)joinChatRoom {
|
|
|
+ if (TXIM_LINSENTER.loginIMSuccess == NO) {
|
|
|
+ [TXIM_LINSENTER TXIMLoginWithUserId:UserDefault(IM_USERID) sig:self.UserSig callback:^(BOOL isSuccess, NSString * _Nullable msg) {
|
|
|
+ [self liveConversationJoin];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self liveConversationJoin];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)liveConversationJoin {
|
|
|
+ [[V2TIMManager sharedInstance] joinGroup:self.roomId msg:nil succ:^{
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ [self addConstMessage];
|
|
|
+ [self sendWelcomeMessage];
|
|
|
+ [self getGroupAttrInfo];
|
|
|
+ [self getGroupCount];
|
|
|
+ });
|
|
|
+
|
|
|
+ } fail:^(int code, NSString *desc) {
|
|
|
+ NSLog(@"");
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)getGroupAttrInfo {
|
|
|
+ [[V2TIMManager sharedInstance] getGroupAttributes:self.roomId keys:nil succ:^(NSMutableDictionary<NSString *,NSString *> *groupAttributeList) {
|
|
|
+ NSLog(@"");
|
|
|
+ [self evaluateGroupInfo:groupAttributeList];
|
|
|
+ } fail:^(int code, NSString *desc) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)getGroupCount {
|
|
|
+ [[V2TIMManager sharedInstance] getGroupCounters:self.roomId keys:nil succ:^(NSDictionary<NSString *,NSNumber *> *groupCounters) {
|
|
|
+ NSLog(@"");
|
|
|
+ [self evaluateGroupCount:groupCounters];
|
|
|
+ } fail:^(int code, NSString *desc) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- (void)addConstMessage {
|
|
|
+ TXConstMessage *statement = [[TXConstMessage alloc] init];
|
|
|
+ statement.userInfo = [self getCurrentUser];
|
|
|
+ statement.text = @"欢迎进入直播课堂,请遵守相关法规,禁止传播低俗、暴力等不良信息。为孩子创造健康绿色的学习环境。";
|
|
|
+ [self appendAndDisplayMessage:statement];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendWelcomeMessage {
|
|
|
+ TXLiveMessageWelcome *welcomeMessage = [[TXLiveMessageWelcome alloc] init];
|
|
|
+ welcomeMessage.userInfo = [self getCurrentUser];
|
|
|
+ MJWeakSelf;
|
|
|
+ [self sendCustomMessage:welcomeMessage priority:V2TIM_PRIORITY_DEFAULT displayMessage:NO callback:^(BOOL success) {
|
|
|
+ weakSelf.hasSendWelcomeMessage = YES;
|
|
|
+ [weakSelf showEnterMessageAndJoinRoom];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showEnterMessageAndJoinRoom {
|
|
|
+ NSString *contentString = [NSString stringWithFormat:@"%@ 进入直播间",UserDefault(NicknameKey)];
|
|
|
+ [self showAnimationView:YES showMessag:contentString];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)joinLiveRoom {
|
|
|
+ [self changeEmptyStatusView];
|
|
|
+ [self resetNetTips];
|
|
|
+ [self startPlay];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)viewWillAppear:(BOOL)animated {
|
|
|
+ [super viewWillAppear:animated];
|
|
|
+ [UIApplication sharedApplication].idleTimerDisabled = YES;
|
|
|
+ [IQKeyboardManager sharedManager].enableAutoToolbar = NO;
|
|
|
+ [IQKeyboardManager sharedManager].enable = NO;
|
|
|
+ [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
|
|
|
+ // 重新渲染view
|
|
|
+ KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
|
|
|
+ if (manager.hasShowWindow) {
|
|
|
+ [manager resignDragWindow];
|
|
|
+ }
|
|
|
+ if (self.hasShowSuspendView) {
|
|
|
+ [self.videoView addSubview:self.videoRenderView];
|
|
|
+ [self.videoRenderView mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.bottom.top.mas_equalTo(self.videoView);
|
|
|
+ }];
|
|
|
+ _hasShowSuspendView = NO;
|
|
|
+ }
|
|
|
+ if (self.bottomView.isMute == NO) {
|
|
|
+ [self muteVoiceAction];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)viewWillDisappear:(BOOL)animated {
|
|
|
+ [super viewWillDisappear:animated];
|
|
|
+ [UIApplication sharedApplication].idleTimerDisabled = NO;
|
|
|
+ if (self.landScape) {
|
|
|
+ [self changeOrientation:NO];
|
|
|
+ }
|
|
|
+ [IQKeyboardManager sharedManager].enableAutoToolbar = YES;
|
|
|
+ [IQKeyboardManager sharedManager].enable = YES;
|
|
|
+ if (@available(iOS 13.0, *)) {
|
|
|
+ [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDarkContent;
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)defaultConfig {
|
|
|
+ self.enableChat = YES;
|
|
|
+ self.enableSeat = YES;
|
|
|
+ self.enableLike = YES;
|
|
|
+ self.needSwitchButton = NO;
|
|
|
+ self.micStatus = MICSTATUS_NOMAL;
|
|
|
+ self.isCreateInRoom = NO; // 默认主讲人不在房间
|
|
|
+ self.blacklistFlag = NO;
|
|
|
+ self.isCloseRoom = NO;
|
|
|
+ self.lastClickPraiseTime = 0;
|
|
|
+ self.isAudience = YES;
|
|
|
+ self.isCreaterVideoEnable = YES;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)viewDidLoad {
|
|
|
+ [super viewDidLoad];
|
|
|
+ // Do any additional setup after loading the view.
|
|
|
+ self.ks_prefersNavigationBarHidden = YES;
|
|
|
+ [self setupUI];
|
|
|
+ [self registerNotification];
|
|
|
+ [self joinChatRoom];
|
|
|
+ [self joinLiveRoom];
|
|
|
+ [self notiferJoinSuccessToServiceCallback:^{
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)registerNotification {
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveMessageNotification:) name:OnReceiveTXLiveMessageNotification object:nil];
|
|
|
+
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(liveroomLogOut) name:@"liveroomLogout" object:nil];
|
|
|
+
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(liveroomQuit) name:@"liveroomQuit" object:nil];
|
|
|
+
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(IMConnetedCallback) name:@"TXIMConnected" object:nil];
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(muteAllAudio) name:@"muteLiveAudio" object:nil];
|
|
|
+
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveGroupMessage:) name:OnReceiveTXLiveGroupNotification object:nil];
|
|
|
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveGroupCountMessage:) name:OnReceiveTXLiveGroupCountNotification object:nil];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)IMConnetedCallback {
|
|
|
+ dispatch_main_async_safe(^{
|
|
|
+ // IM 连接成功回调
|
|
|
+ if (self.hasSendWelcomeMessage) { // 如果已经发送了进入消息
|
|
|
+
|
|
|
+ // 查询是否直播间开启
|
|
|
+ [LiveModuleService imLiveBroadcastRoomQueryRoomRequest:KS_GET roomId:self.roomId success:^(NSDictionary * _Nonnull dic) {
|
|
|
+ if ([dic ks_integerValueForKey:@"code"] == 200 && [dic ks_boolValueForKey:@"status"]) {
|
|
|
+ // 回调信息
|
|
|
+ MJWeakSelf;
|
|
|
+ [self notiferJoinSuccessToServiceCallback:^{
|
|
|
+ [weakSelf sendWelcomeMessage];
|
|
|
+ [weakSelf checkPlayerStatus];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self MBPShow:MESSAGEKEY];
|
|
|
+ [self quitRoom];
|
|
|
+ }
|
|
|
+ } faliure:^(NSError * _Nonnull error) {
|
|
|
+
|
|
|
+ }];
|
|
|
+
|
|
|
+ // 查询群属性
|
|
|
+ [self getGroupAttrInfo];
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (void)checkPlayerStatus {
|
|
|
+ if (self.isCloseRoom) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (self.isAudience) {
|
|
|
+ if (self.livePlayer.isPlaying == NO) {
|
|
|
+ [self resetNetTips];
|
|
|
+ [self startPlay];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self connectHostWithStatus:NO];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)liveroomLogOut {
|
|
|
+ [self MBPShow:@"该账号在其他设备上登录"];
|
|
|
+ [self quitRoom];
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"backLoginView" object:nil];
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (void)liveroomQuit {
|
|
|
+ [self quitRoom];
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"backLoginView" object:nil];
|
|
|
+ });
|
|
|
+}
|
|
|
+- (void)muteAllAudio {
|
|
|
+ [self muteVoiceAction];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ----- // 观众上下麦
|
|
|
+- (void)connectHostWithStatus:(BOOL)isConnect {
|
|
|
+ // 如果直播结束 不转换身份
|
|
|
+ if (self.isCloseRoom == YES) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 转换中,不再处理转换身份
|
|
|
+ if (self.isTransferRole) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isConnect && self.isAudience == NO) {
|
|
|
+ self.micStatus = MICSTATUS_CONNECTING;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if (isConnect == NO && self.isAudience) {
|
|
|
+ self.micStatus = MICSTATUS_NOMAL;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ self.isTransferRole = YES;
|
|
|
+ self.micEnable = YES;
|
|
|
+ // 清理当前视图
|
|
|
+ [self removeSeatContainer];
|
|
|
+ self.remoteMemberArray = [NSMutableArray array];
|
|
|
+ // 移除状态视图
|
|
|
+
|
|
|
+ if (isConnect) {
|
|
|
+ [self stopPlay];
|
|
|
+ [self hideTipsView];
|
|
|
+ if (self.isCreaterVideoEnable) {
|
|
|
+ [self hideEmptyView];
|
|
|
+ }
|
|
|
+ self.isAudience = NO;
|
|
|
+ [self enterLiveRTCRoom];
|
|
|
+ self.isTransferRole = NO;
|
|
|
+ self.micStatus = MICSTATUS_CONNECTING;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.isAudience = YES;
|
|
|
+ if (self.isPauseLive == NO) {
|
|
|
+ [self startPlay];
|
|
|
+ }
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud stopAllRemoteView];
|
|
|
+ [self.trtcCloud stopLocalAudio];
|
|
|
+ [self.trtcCloud exitRoom];
|
|
|
+ _trtcCloud = nil;
|
|
|
+ [TRTCCloud destroySharedIntance];
|
|
|
+ }
|
|
|
+ self.micStatus = MICSTATUS_NOMAL;
|
|
|
+ self.isTransferRole = NO;
|
|
|
+ }
|
|
|
+ [self muteVoiceAction];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setupUI {
|
|
|
+ self.view.backgroundColor = HexRGB(0x25292e);
|
|
|
+ [self.view addSubview:self.emptyView];
|
|
|
+
|
|
|
+ [self.view addSubview:self.endView];
|
|
|
+
|
|
|
+ [self.view addSubview:self.videoContainerView];
|
|
|
+ [self.videoContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(self.view);
|
|
|
+ }];
|
|
|
+ [self.videoContainerView addSubview:self.videoView];
|
|
|
+
|
|
|
+ // 异常的view
|
|
|
+ [self.videoContainerView addSubview:self.netBadView];
|
|
|
+
|
|
|
+ // 按钮和视频的布局
|
|
|
+ if (self.needSwitchButton) {
|
|
|
+ [self.videoView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.mas_equalTo(self.videoContainerView);
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(160);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_top).offset(160);
|
|
|
+ }
|
|
|
+ CGFloat rate = 9 / 16.0f;
|
|
|
+ make.height.mas_equalTo(self.videoContainerView.mas_width).multipliedBy(rate);
|
|
|
+ make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
|
|
|
+ make.width.height.lessThanOrEqualTo(self.videoContainerView);
|
|
|
+ }];
|
|
|
+ // 显示切换按钮
|
|
|
+ [self.videoContainerView addSubview:self.switchButton];
|
|
|
+ [self.switchButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-10);
|
|
|
+ make.width.height.mas_equalTo(33);
|
|
|
+ make.bottom.mas_equalTo(self.videoView.mas_bottom).offset(-10);
|
|
|
+ }];
|
|
|
+ self.switchButton.hidden = NO;
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.videoView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.top.bottom.right.mas_equalTo(self.videoContainerView);
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ // 添加视频预览图
|
|
|
+ [self.videoView addSubview:self.videoRenderView];
|
|
|
+ [self.videoRenderView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(self.videoView);
|
|
|
+ }];
|
|
|
+
|
|
|
+ [self.emptyView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(self.videoView);
|
|
|
+ }];
|
|
|
+ self.emptyView.hidden = YES;
|
|
|
+
|
|
|
+ // 直播结束的画面
|
|
|
+ [self.endView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(self.videoView);
|
|
|
+ }];
|
|
|
+ self.endView.hidden = YES;
|
|
|
+
|
|
|
+ [self.netBadView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(self.videoView);
|
|
|
+ }];
|
|
|
+ self.netBadView.hidden = YES;
|
|
|
+
|
|
|
+ [self.videoContainerView addSubview:self.headView];
|
|
|
+ self.headView.boardcastName.text = [NSString returnNoNullStringWithString:self.createrName];
|
|
|
+ if (![NSString isEmptyString:self.createrAvatal]) {
|
|
|
+ [self.headView.boardcastAvatal sd_setImageWithURL:[NSURL URLWithString:[self.createrAvatal getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:@"teacher_logo"]];
|
|
|
+ }
|
|
|
+ [self countLikeMessageCount];
|
|
|
+ [self countMemberCount];
|
|
|
+ [self.headView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.mas_equalTo(self.videoContainerView);
|
|
|
+ make.height.mas_equalTo(80);
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(0);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_top).offset(0);
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ // 聊天区域
|
|
|
+ [self.videoContainerView addSubview:self.messageContentView];
|
|
|
+
|
|
|
+ if (self.hideCartButton == NO) {
|
|
|
+ [self.videoContainerView addSubview:self.animationView];
|
|
|
+ [self.animationView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.bottom.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideBottom).offset(-9);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.bottom.mas_equalTo(self.videoContainerView.mas_bottom).offset(-9);
|
|
|
+ }
|
|
|
+ make.centerX.mas_equalTo(self.videoContainerView.mas_right).offset(-80);
|
|
|
+ make.width.mas_equalTo(48);
|
|
|
+ make.height.mas_equalTo(56);
|
|
|
+ }];
|
|
|
+ [self.animationView play];
|
|
|
+ }
|
|
|
+ [self.videoContainerView addSubview:self.bottomView];
|
|
|
+ self.bottomView.hideCartVButton = self.hideCartButton;
|
|
|
+ [self.bottomView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.mas_equalTo(self.videoContainerView);
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.bottom.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideBottom).offset(-5);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.bottom.mas_equalTo(self.videoContainerView.mas_bottom).offset(-5);
|
|
|
+ }
|
|
|
+ make.height.mas_equalTo(44);
|
|
|
+ }];
|
|
|
+
|
|
|
+ // 聊天页面
|
|
|
+ if (self.needSwitchButton) {
|
|
|
+ [self.messageContentView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.mas_equalTo(self.videoContainerView.mas_left);
|
|
|
+ make.right.mas_equalTo(self.videoContainerView.mas_right);
|
|
|
+ make.bottom.mas_equalTo(self.bottomView.mas_top).offset(-5);
|
|
|
+ make.top.mas_equalTo(self.videoView.mas_bottom).offset(10).priorityLow();
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.messageContentView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.mas_equalTo(self.videoContainerView.mas_left);
|
|
|
+ make.right.mas_equalTo(self.videoContainerView.mas_right);
|
|
|
+ make.bottom.mas_equalTo(self.bottomView.mas_top).offset(-10);
|
|
|
+ make.height.mas_equalTo(180);
|
|
|
+ }];
|
|
|
+ }
|
|
|
+
|
|
|
+ [self.videoContainerView addSubview:self.inputBar];
|
|
|
+ [_inputBar setBackgroundColor: [UIColor whiteColor]];
|
|
|
+
|
|
|
+ [_inputBar setFrame:CGRectMake(0, KPortraitHeight, KPortraitWidth , 50)];
|
|
|
+ [_inputBar setHidden:YES];
|
|
|
+
|
|
|
+ [self.messageContentView addSubview:self.conversationMessageTableView];
|
|
|
+ [self.conversationMessageTableView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.top.mas_equalTo(self.messageContentView);
|
|
|
+ make.width.mas_equalTo(KPortraitWidth);
|
|
|
+ make.bottom.mas_equalTo(self.messageContentView.mas_bottom);
|
|
|
+ }];
|
|
|
+
|
|
|
+ UITapGestureRecognizer *resetBottomTapGesture =[[UITapGestureRecognizer alloc]
|
|
|
+ initWithTarget:self
|
|
|
+ action:@selector(resetBottomGesture:)];
|
|
|
+ resetBottomTapGesture.delegate = self;
|
|
|
+ [self.videoContainerView addGestureRecognizer:resetBottomTapGesture];
|
|
|
+
|
|
|
+ UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headViewAnimation:)];
|
|
|
+ tapGesture.delegate = self;
|
|
|
+ [self.videoView addGestureRecognizer:tapGesture];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)headViewAnimation:(UIGestureRecognizer *)gestureRecognizer {
|
|
|
+ if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
|
|
|
+ [self setDefaultBottomViewStatus];
|
|
|
+ }
|
|
|
+ if (self.landScape) {
|
|
|
+ self.isHiddenHeadView = !self.isHiddenHeadView;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark -- 加入直播间和退出直播间回到服务
|
|
|
+- (void)notiferJoinSuccessToServiceCallback:(void(^)(void))callback {
|
|
|
+ [LiveModuleService LiveroomJoinRequest:KS_GET roomId:self.roomId success:^(NSDictionary * _Nonnull dic) {
|
|
|
+ callback();
|
|
|
+ } faliure:^(NSError * _Nonnull error) {
|
|
|
+ callback();
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)quitNotiferService {
|
|
|
+ [LiveModuleService LiveroomQuit:KS_POST success:^(NSDictionary * _Nonnull dic) {
|
|
|
+ if ([dic ks_integerValueForKey:@"errCode"] == 0) {
|
|
|
+ NSLog(@"success");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ NSLog(@"----- error %@", [dic ks_stringValueForKey:@"errMsg"]);
|
|
|
+ }
|
|
|
+ } faliure:^(NSError * _Nonnull error) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ------ 播放控制
|
|
|
+- (void)startPlay {
|
|
|
+ if ([self.livePlayer isPlaying] == NO) {
|
|
|
+ [self resetNetTips];
|
|
|
+ if (self.isCreaterVideoEnable) {
|
|
|
+ [self hideEmptyView];
|
|
|
+ }
|
|
|
+ [self showLoading];
|
|
|
+ self.videoRenderView.hasMainStream = YES;
|
|
|
+ self.videoRenderView.hasSubStream = NO;
|
|
|
+ [self.livePlayer setRenderView:self.videoRenderView.mainView];
|
|
|
+ if (self.needSwitchButton) {
|
|
|
+ [self.livePlayer setRenderFillMode:V2TXLiveFillModeFit];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.livePlayer setRenderFillMode:V2TXLiveFillModeFill];
|
|
|
+ }
|
|
|
+ // 获取url的方法
|
|
|
+ NSString *streamId = [NSString stringWithFormat:@"%@_%@", self.roomId, self.createrId];
|
|
|
+ NSString *url = [TXLiveURLUtils generateRtmpPlayUrl:streamId playDomain:self.playDomain liveUrlKey:self.liveUrlKey];
|
|
|
+ [self.livePlayer startLivePlay:url];
|
|
|
+ [self muteVoiceAction];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)stopPlay {
|
|
|
+ if (_livePlayer) {
|
|
|
+ if ([self.livePlayer isPlaying]) {
|
|
|
+ [self hideLoading];
|
|
|
+ [self.livePlayer stopPlay];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 不播放声音
|
|
|
+- (void)pauserAudio {
|
|
|
+ [self.livePlayer pauseAudio];
|
|
|
+}
|
|
|
+// 恢复声音
|
|
|
+- (void)resumeAudio {
|
|
|
+ [self.livePlayer resumeAudio];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ---- V2TXLivePlayerObserver
|
|
|
+- (void)onError:(id<V2TXLivePlayer>)player code:(V2TXLiveCode)code message:(NSString *)msg extraInfo:(NSDictionary *)extraInfo {
|
|
|
+
|
|
|
+ if (code == V2TXLIVE_ERROR_DISCONNECTED) {
|
|
|
+ // 连接断开
|
|
|
+ if (self.isPauseLive == NO && self.isCreateInRoom) {
|
|
|
+ [self hideLoading];
|
|
|
+ [self showBadNetTips];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (code == V2TXLIVE_WARNING_VIDEO_BLOCK) {
|
|
|
+
|
|
|
+ }
|
|
|
+ NSLog(@"---- V2TXLivePlayer %zd message:%@", code, msg);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onConnected:(id<V2TXLivePlayer>)player extraInfo:(NSDictionary *)extraInfo {
|
|
|
+ NSLog(@" ------ connected %@", extraInfo);
|
|
|
+ self.isPauseLive = NO;
|
|
|
+ self.isCreateInRoom = YES;
|
|
|
+ if (self.isCreaterVideoEnable) {
|
|
|
+ [self hideEmptyView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onVideoPlaying:(id<V2TXLivePlayer>)player firstPlay:(BOOL)firstPlay extraInfo:(NSDictionary *)extraInfo {
|
|
|
+ NSLog(@" ------ onVideoPlaying %@", extraInfo);
|
|
|
+ [self hideLoading];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)onVideoResolutionChanged:(id<V2TXLivePlayer>)player width:(NSInteger)width height:(NSInteger)height {
|
|
|
+ NSLog(@" ------ onVideoResolutionChanged %zd %zd", width, height);
|
|
|
+}
|
|
|
+
|
|
|
+// 直播状态
|
|
|
+- (void)onStatisticsUpdate:(id<V2TXLivePlayer>)player statistics:(V2TXLivePlayerStatistics *)statistics {
|
|
|
+ NSLog(@"----- V2TXLivePlayerStatistics desc %@", statistics.description);
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onReceiveSeiMessage:(id<V2TXLivePlayer>)player payloadType:(int)payloadType data:(NSData *)data {
|
|
|
+ NSLog(@"----- onReceiveSeiMessage desc %@", [data mj_JSONObject]);
|
|
|
+ if (self.isAudience == YES && self.isPauseLive == NO) {
|
|
|
+ NSDictionary *parm = [data mj_JSONObject];
|
|
|
+ if ([[parm ks_stringValueForKey:@"api"] isEqualToString:@"sync"]) { // 同步连麦人数
|
|
|
+ NSArray *memberArray = [parm ks_arrayValueForKey:@"seatUser"];
|
|
|
+ NSMutableArray *remoteSeatArray = [NSMutableArray arrayWithArray:memberArray];
|
|
|
+ // 移除自己
|
|
|
+ NSMutableArray *seatArray = [NSMutableArray array];
|
|
|
+ for (NSDictionary *userParm in remoteSeatArray) {
|
|
|
+ if (![[userParm ks_stringValueForKey:@"userId"] isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ TXUISeatMember *member = [[TXUISeatMember alloc] init];
|
|
|
+ member.userId = [userParm ks_stringValueForKey:@"userId"];
|
|
|
+ member.muteMic = [userParm ks_boolValueForKey:@"micStatus"];
|
|
|
+ [seatArray addObject:member];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ [self refreshSeatWithMember:seatArray];
|
|
|
+// if (self.remoteMemberArray.count != seatArray.count) { // 如果连麦人数不同
|
|
|
+// [self refreshSeatWithMember:seatArray];
|
|
|
+// }
|
|
|
+// else {
|
|
|
+// BOOL isSameUser = YES;
|
|
|
+// NSMutableArray *seatMemberArr = [self.remoteMemberArray mutableCopy];
|
|
|
+//
|
|
|
+// for (TXUISeatMember *member in seatMemberArr) {
|
|
|
+//
|
|
|
+// if (![remoteSeatArray containsObject:member]) {
|
|
|
+// isSameUser = NO;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// if (isSameUser == NO) {
|
|
|
+// [self refreshSeatWithMember:seatArray];
|
|
|
+// }
|
|
|
+// }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self refreshSeatWithMember:@[]];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)refreshSeatWithMember:(NSArray *)memberArray {
|
|
|
+ self.remoteMemberArray = [NSMutableArray arrayWithArray:memberArray];
|
|
|
+ [self renderSeatView];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark --- trtcCloud delegate
|
|
|
+// 远端主播加入房间
|
|
|
+- (void)onRemoteUserEnterRoom:(NSString *)userId {
|
|
|
+ if (![self judgeContainerRemoteUser:userId] && ![userId isEqualToString:self.createrId]) { // 如果不包含当前用户 添加 和刷新连麦
|
|
|
+ TXUISeatMember *member = [[TXUISeatMember alloc] init];
|
|
|
+ member.userId = userId;
|
|
|
+ member.muteMic = YES;
|
|
|
+ [self.remoteMemberArray addObject:member];
|
|
|
+ }
|
|
|
+
|
|
|
+ [self renderSeatView];
|
|
|
+}
|
|
|
+
|
|
|
+// 远端主播离开
|
|
|
+- (void)onRemoteUserLeaveRoom:(NSString *)userId reason:(NSInteger)reason {
|
|
|
+ if ([self judgeContainerRemoteUser:userId]) { // 如果包含当前用户 移除当前用和刷新连麦
|
|
|
+ [self removeMemberUserId:userId];
|
|
|
+ }
|
|
|
+ [self renderSeatView];
|
|
|
+}
|
|
|
+
|
|
|
+- (BOOL)judgeContainerRemoteUser:(NSString *)userId {
|
|
|
+ BOOL containUser = NO;
|
|
|
+ NSMutableArray *remoteMemArray = [self.remoteMemberArray mutableCopy];
|
|
|
+ for (TXUISeatMember *member in remoteMemArray) {
|
|
|
+ if ([member.userId isEqualToString:userId]) {
|
|
|
+ containUser = YES;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return containUser;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)removeMemberUserId:(NSString *)userId {
|
|
|
+ NSMutableArray *remoteMemArray = [self.remoteMemberArray mutableCopy];
|
|
|
+ for (TXUISeatMember *member in remoteMemArray) {
|
|
|
+ if ([member.userId isEqualToString:userId]) {
|
|
|
+ [self.remoteMemberArray removeObject:member];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onWarning:(TXLiteAVWarning)warningCode warningMsg:(NSString *)warningMsg extInfo:(NSDictionary *)extInfo {
|
|
|
+ NSLog(@" warningMsg %@ extInfo:%@", warningMsg, extInfo.description);
|
|
|
+}
|
|
|
+
|
|
|
+// 远端视频
|
|
|
+- (void)onUserVideoAvailable:(NSString *)userId available:(BOOL)available {
|
|
|
+ NSLog(@"creater remote videoStream status: %d", available);
|
|
|
+ if (available) {
|
|
|
+ self.videoRenderView.hasMainStream = YES;
|
|
|
+ if (self.videoRenderView.hasSubStream) {
|
|
|
+ [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.smallView];
|
|
|
+ [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub view:self.videoRenderView.mainView];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.trtcCloud stopRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub];
|
|
|
+ [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.mainView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.videoRenderView.hasMainStream = NO;
|
|
|
+ [self.trtcCloud stopRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)onUserSubStreamAvailable:(NSString *)userId available:(BOOL)available {
|
|
|
+ NSLog(@"creater remote subStream status: %d", available);
|
|
|
+ if (available) {
|
|
|
+ self.videoRenderView.hasSubStream = YES;
|
|
|
+ [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub view:self.videoRenderView.mainView];
|
|
|
+ if (self.videoRenderView.hasMainStream) {
|
|
|
+ [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.smallView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.videoRenderView.hasSubStream = NO;
|
|
|
+ [self.trtcCloud stopRemoteView:self.createrId streamType:TRTCVideoStreamTypeSub];
|
|
|
+ if (self.videoRenderView.hasMainStream) {
|
|
|
+ [self.trtcCloud startRemoteView:self.createrId streamType:TRTCVideoStreamTypeBig view:self.videoRenderView.mainView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onFirstVideoFrame:(NSString *)userId streamType:(TRTCVideoStreamType)streamType width:(int)width height:(int)height {
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onRecvSEIMsg:(NSString *)userId message:(NSData *)message {
|
|
|
+// NSDictionary *parm = [message mj_JSONObject];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)onUserAudioAvailable:(NSString *)userId available:(BOOL)available {
|
|
|
+ for (TXUISeatMember *member in self.remoteMemberArray) {
|
|
|
+ if ([member.userId isEqualToString:userId]) {
|
|
|
+ member.muteMic = !available;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ [self renderSeatView];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark - gesture and button action
|
|
|
+- (void)resetBottomGesture:(UIGestureRecognizer *)gestureRecognizer {
|
|
|
+ if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
|
|
|
+ [self setDefaultBottomViewStatus];
|
|
|
+ }
|
|
|
+}
|
|
|
+- (void)setDefaultBottomViewStatus {
|
|
|
+ [self.inputBar setInputBarStatus:KSBottomBarStatusDefault];
|
|
|
+ [self.inputBar setHidden:YES];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark --- setter
|
|
|
+- (void)setIsHiddenHeadView:(BOOL)isHiddenHeadView {
|
|
|
+ if (_isHiddenHeadView == isHiddenHeadView) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ _isHiddenHeadView = isHiddenHeadView;
|
|
|
+ //告知需要更改约束
|
|
|
+ [self.view setNeedsUpdateConstraints];
|
|
|
+ [UIView animateWithDuration:0.5 animations:^{
|
|
|
+ if (isHiddenHeadView) {
|
|
|
+
|
|
|
+ [self.headView mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(-80);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_top).offset(-80);
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.headView mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(10);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_top).offset(10);
|
|
|
+ }
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ [self.headView.superview layoutIfNeeded];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ----- landscape action
|
|
|
+- (void)transferToLandscape {
|
|
|
+ [self setDefaultBottomViewStatus];
|
|
|
+ [self changeOrientation:!self.landScape];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)changeOrientation:(BOOL)isLandScape {
|
|
|
+ self.landScape = isLandScape;
|
|
|
+ if (isLandScape) {
|
|
|
+ // 切换到横屏
|
|
|
+ AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
|
|
|
+ delegate.allowAutoRotate = YES;
|
|
|
+ [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight inController:self];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
|
|
|
+ delegate.allowAutoRotate = NO;
|
|
|
+ [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait inController:self];
|
|
|
+ }
|
|
|
+ self.isHiddenHeadView = NO;
|
|
|
+ [self updateConstraint];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)updateConstraint {
|
|
|
+ if (self.landScape) {
|
|
|
+ self.bottomView.hideChatView = YES;
|
|
|
+ self.messageContentView.hidden = YES;
|
|
|
+ self.headView.hideQuitButton = YES;
|
|
|
+ [self.videoView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ if (IS_IPAD) {
|
|
|
+ make.left.right.mas_equalTo(self.videoContainerView);
|
|
|
+ make.centerX.mas_equalTo(self.videoContainerView.mas_centerX);
|
|
|
+ CGFloat rate = 9 / 16.0f;
|
|
|
+ make.height.mas_equalTo(self.videoContainerView.mas_width).multipliedBy(rate);
|
|
|
+ make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
|
|
|
+ make.width.height.lessThanOrEqualTo(self.videoContainerView);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ make.top.bottom.mas_equalTo(self.videoContainerView);
|
|
|
+ make.centerX.mas_equalTo(self.videoContainerView.mas_centerX);
|
|
|
+ // 宽高设置
|
|
|
+ CGFloat rate = 16 / 9.0f;
|
|
|
+ make.width.mas_equalTo(self.videoContainerView.mas_height).multipliedBy(rate);
|
|
|
+ make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
|
|
|
+ make.width.height.lessThanOrEqualTo(self.videoContainerView);
|
|
|
+ }
|
|
|
+
|
|
|
+ }];
|
|
|
+ [self.switchButton mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-10);
|
|
|
+ make.width.height.mas_equalTo(33);
|
|
|
+ make.centerY.mas_equalTo(self.videoView.mas_centerY);
|
|
|
+ }];
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.bottomView.hideChatView = NO;
|
|
|
+ self.messageContentView.hidden = NO;
|
|
|
+ self.headView.hideQuitButton = NO;
|
|
|
+ [self.videoView mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.mas_equalTo(self.videoContainerView);
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(160);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_top).offset(160);
|
|
|
+ }
|
|
|
+ CGFloat rate = 9 / 16.0f;
|
|
|
+ make.height.mas_equalTo(self.videoContainerView.mas_width).multipliedBy(rate);
|
|
|
+ make.width.height.mas_equalTo(self.videoContainerView).priorityLow();
|
|
|
+ make.width.height.lessThanOrEqualTo(self.videoContainerView);
|
|
|
+ }];
|
|
|
+ [self.switchButton mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-10);
|
|
|
+ make.width.height.mas_equalTo(33);
|
|
|
+ make.bottom.mas_equalTo(self.videoView.mas_bottom).offset(-10);
|
|
|
+ }];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新连麦UI
|
|
|
+ if ([self.videoContainerView.subviews containsObject:self.seatContainer]) {
|
|
|
+ [self updateSeatContainerConstraint];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showEmptyViewDisplay:(BOOL)isPublish {
|
|
|
+ NSString *imageName = @"";
|
|
|
+ NSString *descMessage = @"";
|
|
|
+ if (isPublish) {
|
|
|
+ imageName = @"liveVideo_close";
|
|
|
+ descMessage = @"主讲人已关闭画面!";
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ imageName = @"liveroom_empty";
|
|
|
+ descMessage = @"休息一下,马上回来!";
|
|
|
+ }
|
|
|
+ [self.emptyView configViewWithImageName:imageName desc:descMessage];
|
|
|
+ [self hideTipsView];
|
|
|
+ self.videoView.hidden = YES;
|
|
|
+ self.emptyView.hidden = NO;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideEmptyView {
|
|
|
+ self.emptyView.hidden = YES;
|
|
|
+ self.videoView.hidden = NO;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)changeEmptyStatusView {
|
|
|
+ if (self.isCloseRoom == NO) { // 只有在非结束直播状态下
|
|
|
+ if (self.isPauseLive) { // 暂停直播
|
|
|
+ [self showEmptyViewDisplay:NO];
|
|
|
+ }
|
|
|
+ else { // 如果未暂停直播
|
|
|
+ if (self.isCreaterVideoEnable) { // 如果摄像头开启
|
|
|
+ [self hideTipsView];
|
|
|
+ [self hideEmptyView];
|
|
|
+ }
|
|
|
+ else { // 主播关闭了摄像头
|
|
|
+ [self showEmptyViewDisplay:YES];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)renderSeatView {
|
|
|
+
|
|
|
+ NSMutableArray *seatArray = [NSMutableArray array];
|
|
|
+ NSArray *remoteUserArray = [self.remoteMemberArray mutableCopy];
|
|
|
+ for (TXUISeatMember *member in remoteUserArray) {
|
|
|
+ [seatArray addObject:member];
|
|
|
+ }
|
|
|
+
|
|
|
+ BOOL isContainSelf = NO;
|
|
|
+ for (TXUISeatMember *member in seatArray) {
|
|
|
+ if ([member.userId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ isContainSelf = YES;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (self.micStatus == MICSTATUS_CONNECTING && !isContainSelf) {
|
|
|
+ TXUISeatMember *currentMember = [[TXUISeatMember alloc] init];
|
|
|
+ currentMember.userId = UserDefault(IM_USERID);
|
|
|
+ currentMember.muteMic = !self.micEnable;
|
|
|
+ [seatArray addObject:currentMember];
|
|
|
+ }
|
|
|
+ NSLog(@"-- seat member--- %zd -", seatArray.count);
|
|
|
+ if (seatArray.count) {
|
|
|
+ if (![self.videoContainerView.subviews containsObject:self.seatContainer]) {
|
|
|
+ [self.videoContainerView addSubview:self.seatContainer];
|
|
|
+ }
|
|
|
+ [self updateSeatContainerConstraint];
|
|
|
+
|
|
|
+// self.seatContainer.seatMemberArray = [NSMutableArray arrayWithArray:seatArray];
|
|
|
+// [self.seatContainer refreshSeatUI];
|
|
|
+ [self.seatContainer refreshSeatUIWithMember:[NSMutableArray arrayWithArray:seatArray]];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self removeSeatContainer];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 移除连麦视图
|
|
|
+- (void)removeSeatContainer {
|
|
|
+ if ([self.videoContainerView.subviews containsObject:self.seatContainer]) {
|
|
|
+ [self.seatContainer removeFromSuperview];
|
|
|
+ }
|
|
|
+}
|
|
|
+- (void)updateSeatContainerConstraint {
|
|
|
+ if (self.landScape) {
|
|
|
+ [self.seatContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.left.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideLeft);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.left.mas_equalTo(self.videoContainerView.mas_left);
|
|
|
+ }
|
|
|
+ make.right.mas_equalTo(self.videoContainerView.mas_right).offset(-70);
|
|
|
+ if (@available(iOS 11.0, *)) {
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_safeAreaLayoutGuideTop).offset(85);
|
|
|
+ } else {
|
|
|
+ // Fallback on earlier versions
|
|
|
+ make.top.mas_equalTo(self.videoContainerView.mas_top).offset(85);
|
|
|
+ }
|
|
|
+ make.height.mas_equalTo(70);
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self.seatContainer mas_remakeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.mas_equalTo(self.videoContainerView);
|
|
|
+ make.top.mas_equalTo(self.headView.mas_bottom).offset(0);
|
|
|
+ make.height.mas_equalTo(70);
|
|
|
+ }];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ----- 加入TRC房间
|
|
|
+- (void)enterLiveRTCRoom {
|
|
|
+ self.videoRenderView.hasMainStream = NO;
|
|
|
+ self.videoRenderView.hasSubStream = NO;
|
|
|
+ TRTCParams *parms = [[TRTCParams alloc] init];
|
|
|
+ parms.sdkAppId = CONFIG_TXSDKAPPID;
|
|
|
+ parms.userSig = self.UserSig;
|
|
|
+ parms.role = TRTCRoleAnchor;
|
|
|
+ parms.strRoomId = self.roomId;
|
|
|
+ parms.userId = UserDefault(IM_USERID);
|
|
|
+ self.trtcCloud.delegate = self;
|
|
|
+ [self config3AParams];
|
|
|
+ [self configVideoAndAudioSetting];
|
|
|
+ [self.trtcCloud enterRoom:parms appScene:TRTCAppSceneLIVE];
|
|
|
+ TRTCRenderParams *rendParams = [[TRTCRenderParams alloc] init];
|
|
|
+
|
|
|
+ if (self.needSwitchButton) {
|
|
|
+ rendParams.fillMode = TRTCVideoFillMode_Fit;
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ rendParams.fillMode = TRTCVideoFillMode_Fill;
|
|
|
+ }
|
|
|
+ [self.trtcCloud setRemoteRenderParams:self.createrId streamType:TRTCVideoStreamTypeBig params:rendParams];
|
|
|
+ [self.trtcCloud setRemoteRenderParams:self.createrId streamType:TRTCVideoStreamTypeSub params:rendParams];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- (void)configVideoAndAudioSetting {
|
|
|
+ [self.trtcCloud startLocalAudio:TRTCAudioQualityMusic];
|
|
|
+ TRTCVideoEncParam *videoEncParam = [[TRTCVideoEncParam alloc] init];
|
|
|
+ videoEncParam.videoFps = 24;
|
|
|
+ videoEncParam.resMode = TRTCVideoResolutionModePortrait;
|
|
|
+ videoEncParam.videoResolution = TRTCVideoResolution_960_540;
|
|
|
+ [self.trtcCloud setVideoEncoderParam:videoEncParam];
|
|
|
+
|
|
|
+ if (self.muteRoomMic) {
|
|
|
+ [self micButtonAction:self.muteRoomMic];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 配置3A参数
|
|
|
+- (void)config3AParams {
|
|
|
+ [self configANSParm:self.ANS];
|
|
|
+ [self configAECParm:self.AEC];
|
|
|
+ [self configAGCParm:self.AGC];
|
|
|
+}
|
|
|
+
|
|
|
+// 背景音抑制
|
|
|
+- (void)configANSParm:(NSInteger)number {
|
|
|
+ NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
|
|
+ [dic setValue:@"enableAudioANS" forKey:@"api"];
|
|
|
+ NSMutableDictionary *subDic = [NSMutableDictionary dictionary];
|
|
|
+ if (number > 0) {
|
|
|
+ [subDic setValue:@(1) forKey:@"enable"];
|
|
|
+ [subDic setValue:@(number) forKey:@"level"];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [subDic setValue:@(0) forKey:@"enable"];
|
|
|
+ [subDic setValue:@(0) forKey:@"level"];
|
|
|
+ }
|
|
|
+ [dic setValue:subDic forKey:@"params"];
|
|
|
+ NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:kNilOptions error:nil];
|
|
|
+ NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
+ [self.trtcCloud callExperimentalAPI:jsonString];
|
|
|
+}
|
|
|
+
|
|
|
+// 回声消除
|
|
|
+- (void)configAECParm:(NSInteger)number {
|
|
|
+ NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
|
|
+ [dic setValue:@"enableAudioAEC" forKey:@"api"];
|
|
|
+ NSMutableDictionary *subDic = [NSMutableDictionary dictionary];
|
|
|
+ if (number > 0) {
|
|
|
+ [subDic setValue:@(1) forKey:@"enable"];
|
|
|
+ [subDic setValue:@(number) forKey:@"level"];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [subDic setValue:@(0) forKey:@"enable"];
|
|
|
+ [subDic setValue:@(0) forKey:@"level"];
|
|
|
+ }
|
|
|
+ [dic setValue:subDic forKey:@"params"];
|
|
|
+ NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:kNilOptions error:nil];
|
|
|
+ NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
+ [self.trtcCloud callExperimentalAPI:jsonString];
|
|
|
+}
|
|
|
+
|
|
|
+// 自动增益
|
|
|
+- (void)configAGCParm:(NSInteger)number {
|
|
|
+ NSMutableDictionary *dic = [NSMutableDictionary dictionary];
|
|
|
+ [dic setValue:@"enableAudioAGC" forKey:@"api"];
|
|
|
+ NSMutableDictionary *subDic = [NSMutableDictionary dictionary];
|
|
|
+ if (number > 0) {
|
|
|
+ [subDic setValue:@(1) forKey:@"enable"];
|
|
|
+ [subDic setValue:@(number) forKey:@"level"];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [subDic setValue:@(0) forKey:@"enable"];
|
|
|
+ [subDic setValue:@(0) forKey:@"level"];
|
|
|
+ }
|
|
|
+ [dic setValue:subDic forKey:@"params"];
|
|
|
+ NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:kNilOptions error:nil];
|
|
|
+ NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
|
+ [self.trtcCloud callExperimentalAPI:jsonString];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark -------- live message handle
|
|
|
+
|
|
|
+- (NSString *)getMsgClassName:(NSString *)messageObjectName {
|
|
|
+ TXLiveMessageModel *configModel = [[TXLiveMessageModel alloc] init];
|
|
|
+ NSString *className = [configModel getMessageClassName:messageObjectName];
|
|
|
+ return className;
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 接收到消息的回调
|
|
|
+ */
|
|
|
+- (void)didReceiveMessageNotification:(NSNotification *)notification {
|
|
|
+ NSDictionary *dic = notification.object; /// __block TXLiveMessageModel *liveMessage
|
|
|
+ NSDictionary *content = [dic ks_dictionaryValueForKey:@"message"];
|
|
|
+ NSString *objectName = [content ks_stringValueForKey:@"objectName"];
|
|
|
+ NSString *className = [self getMsgClassName:objectName];
|
|
|
+
|
|
|
+ __block TXLiveMessageModel *message = [[NSClassFromString(className) alloc] init];
|
|
|
+ message.messageId = [dic ks_stringValueForKey:@"msgID"];
|
|
|
+ message.groupId = [dic ks_stringValueForKey:@"groupID"];
|
|
|
+ [message evaluateSource:content];
|
|
|
+
|
|
|
+ if ([message.groupId isEqualToString:self.roomId]) {
|
|
|
+ __weak typeof(&*self) __blockSelf = self;
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ if ([message isMemberOfClass:[TXLiveMessageLike class]]) {
|
|
|
+ TXLiveMessageLike *likeMessage = (TXLiveMessageLike *)message;
|
|
|
+ NSLog(@"receive like count ------- %d",likeMessage.counts);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageLikeCount class]]) {
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageWelcome class]]) {
|
|
|
+ TXLiveMessageWelcome *welcomeMessage = (TXLiveMessageWelcome *)message;
|
|
|
+ // 过滤自己发送的欢迎消息
|
|
|
+ if ([welcomeMessage.userInfo.sendUserId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ([welcomeMessage.userInfo.sendUserId isEqual:__blockSelf.createrId]) { // 如果是老师 重置连麦申请状态
|
|
|
+ [__blockSelf resetMicWaitToNomal];
|
|
|
+ __blockSelf.isCreateInRoom = YES;
|
|
|
+
|
|
|
+ }
|
|
|
+ NSString *userName = [welcomeMessage.userInfo.sendUserName stringByAppendingString:@""];
|
|
|
+ NSString *contentMsg = [NSString stringWithFormat:@"%@ 进入直播间",userName];
|
|
|
+ [__blockSelf showAnimationView:YES showMessag:contentMsg];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 用户退出消息
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageLeave class]]) {
|
|
|
+// TXLiveMessageLeave *userLeaveMsg = (TXLiveMessageLeave *)message;
|
|
|
+// if ([userLeaveMsg.userInfo.sendUserId isEqualToString:__blockSelf.createrId]) {
|
|
|
+// __blockSelf.isCreateInRoom = NO;
|
|
|
+// // 下麦
|
|
|
+// if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+// [__blockSelf downSeatAction];
|
|
|
+// }
|
|
|
+// }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 用户退出回调
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageUserQuit class]]) {
|
|
|
+// TXLiveMessageUserQuit *userLeaveMsg = (TXLiveMessageUserQuit *)message;
|
|
|
+// if ([userLeaveMsg.targetId isEqualToString:__blockSelf.createrId]) {
|
|
|
+// __blockSelf.isCreateInRoom = NO;
|
|
|
+// // 下麦
|
|
|
+// if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+// [__blockSelf downSeatAction];
|
|
|
+// }
|
|
|
+// }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageMemberUp class]]) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageStatSync class]]) { // 数据同步 不取点赞数
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageKickOut class]]) {
|
|
|
+ TXLiveMessageKickOut *kickMessage = (TXLiveMessageKickOut *)message;
|
|
|
+ NSString *currentUserId = UserDefault(IM_USERID);
|
|
|
+ if ([kickMessage.targetId isEqualToString:currentUserId]) {
|
|
|
+ __blockSelf.micStatus = MICSTATUS_NOMAL;
|
|
|
+ [__blockSelf kickOutLiveRoom];
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 禁言控制
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageChatBan class]]) {
|
|
|
+ TXLiveMessageChatBan *banMessage = (TXLiveMessageChatBan *)message;
|
|
|
+ if (banMessage.chatBan) { // 禁止发言
|
|
|
+ __blockSelf.enableChat = NO;
|
|
|
+ }
|
|
|
+ else { // 开启发言
|
|
|
+ __blockSelf.enableChat = YES;
|
|
|
+ }
|
|
|
+ [__blockSelf insertMessage:banMessage];
|
|
|
+ }
|
|
|
+ // 连麦控制消息
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageSeatControl class]]) {
|
|
|
+ TXLiveMessageSeatControl *seatControlMessage = (TXLiveMessageSeatControl *)message;
|
|
|
+ if (seatControlMessage.seatBan) { // 禁止连麦
|
|
|
+ __blockSelf.enableSeat = NO;
|
|
|
+ // 未连麦情况下才重置
|
|
|
+ if (__blockSelf.micStatus != MICSTATUS_CONNECTING) {
|
|
|
+ // 如果在申请 发送取消申请消息
|
|
|
+ if (__blockSelf.micStatus == MICSTATUS_WAITING) {
|
|
|
+ [__blockSelf sendSeatMessageApply:NO];
|
|
|
+ }
|
|
|
+ __blockSelf.micStatus = MICSTATUS_NOMAL;
|
|
|
+ }
|
|
|
+ [__blockSelf hideApplyView];
|
|
|
+ [__blockSelf hideApplyingView];
|
|
|
+ }
|
|
|
+ else { // 开启连麦
|
|
|
+ __blockSelf.enableSeat = YES;
|
|
|
+ }
|
|
|
+ [__blockSelf insertMessage:seatControlMessage];
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageSeatApply class]]) {
|
|
|
+
|
|
|
+ // 只处理主讲人发起的连麦邀请或取消操作
|
|
|
+ TXLiveMessageSeatApply *seatApplyMessage = (TXLiveMessageSeatApply *)message;
|
|
|
+ NSLog(@"----- RCChatroomSeatApply type:%ld audienceName:%@ ",seatApplyMessage.type,seatApplyMessage.audienceName);
|
|
|
+ // 只处理和自己相关的消息
|
|
|
+ if (![seatApplyMessage.audienceId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ // 刷新麦序
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (seatApplyMessage.type == SEATHANDLE_INVITE) { // 主讲人发起申请
|
|
|
+ NSString *titles = [NSString stringWithFormat:@"%@邀请您连麦",[NSString returnNoNullStringWithString:seatApplyMessage.teacherName]];
|
|
|
+ [__blockSelf showInviteAlertMessage:titles];
|
|
|
+ }
|
|
|
+ else if (seatApplyMessage.type == SEATHANDLE_DISINVITE) { // 主讲人撤回申请
|
|
|
+ [__blockSelf MBPShow:@"主讲人撤回了连麦申请"];
|
|
|
+ [__blockSelf closeAlertView];
|
|
|
+ if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+ [__blockSelf downSeatAction];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (seatApplyMessage.type == SEATHANDLE_KICKSEAT) { // 主讲人断开连麦
|
|
|
+ [__blockSelf kickSeatWithMessage:@"主讲人断开连麦"];
|
|
|
+ }
|
|
|
+ [__blockSelf hideApplyingView];
|
|
|
+ [__blockSelf insertMessage:seatApplyMessage];
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageSeatResponse class]]) { // 只处理主讲人的回应
|
|
|
+ TXLiveMessageSeatResponse *seatResponseMessage = (TXLiveMessageSeatResponse *)message;
|
|
|
+ NSLog(@"----- RCChatroomSeatResponse type:%ld audienceName:%@ ",seatResponseMessage.type,seatResponseMessage.audienceName);
|
|
|
+ if (![seatResponseMessage.audienceId isEqualToString:UserDefault(IM_USERID)]) { // 只处理和自己相关的消息
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (seatResponseMessage.type == SEATRESPONSE_TEACHERAPPROVE && __blockSelf.micStatus == MICSTATUS_WAITING) { //
|
|
|
+ [__blockSelf hideApplyingView];
|
|
|
+ [__blockSelf connectHostWithStatus:YES];
|
|
|
+ [__blockSelf insertMessage:seatResponseMessage];
|
|
|
+ }
|
|
|
+ else if (seatResponseMessage.type == SEATRESPONSE_TEACHERREJECT) {
|
|
|
+ [__blockSelf MBPShow:@"主讲人拒绝了您的连麦申请"];
|
|
|
+ // 处理连麦状态
|
|
|
+ [__blockSelf connectHostWithStatus:NO];
|
|
|
+ [__blockSelf insertMessage:seatResponseMessage];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageDownSeat class]]) { // 成员主动下麦消息
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageClose class]]) { // 直播间已关闭
|
|
|
+ [__blockSelf MBPShow:@"直播已结束"];
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ // 显示直播间关闭状态
|
|
|
+ [__blockSelf closeAlertView];
|
|
|
+ [__blockSelf displayCloseView];
|
|
|
+ [__blockSelf hideApplyingView];
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveTextMessage class]]) {
|
|
|
+ TXLiveTextMessage *textMessage = (TXLiveTextMessage *)message;
|
|
|
+ [__blockSelf insertMessage:textMessage];
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageCardMessage class]]) {
|
|
|
+ TXLiveMessageCardMessage *cardMessage = (TXLiveMessageCardMessage *)message;
|
|
|
+ [__blockSelf sendCardMessage:cardMessage.goodsContent];
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessagePauseLive class]]) { // 主讲人暂停直播
|
|
|
+// __blockSelf.isPauseLive = YES;
|
|
|
+// if (__blockSelf.micStatus == MICSTATUS_WAITING) {
|
|
|
+// [__blockSelf sendSeatMessageApply:NO];
|
|
|
+// }
|
|
|
+// [__blockSelf resetNetTips];
|
|
|
+// [__blockSelf resetConnectionStatus];
|
|
|
+// [__blockSelf closeAlertView];
|
|
|
+// [__blockSelf hideApplyingView];
|
|
|
+// [__blockSelf changeEmptyStatusView];
|
|
|
+// [__blockSelf closePlayStatus];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageOpenLive class]]) { // 开始直播
|
|
|
+// __blockSelf.isPauseLive = NO;
|
|
|
+// __blockSelf.isCreateInRoom = YES;
|
|
|
+// [__blockSelf resetNetTips];
|
|
|
+// [__blockSelf hideEmptyView];
|
|
|
+// [__blockSelf startPlay];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageShopRush class]]) { //
|
|
|
+
|
|
|
+ TXLiveMessageShopRush *rushMsg = (TXLiveMessageShopRush *)message;
|
|
|
+
|
|
|
+ NSString *contentMsg = [NSString stringWithFormat:@"%@ 正在抢购",[NSString returnNoNullStringWithString:rushMsg.userInfo.sendUserName]];
|
|
|
+ [__blockSelf showAnimationView:NO showMessag:contentMsg];
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageBlockUser class]]) { // 黑名单
|
|
|
+ TXLiveMessageBlockUser *blockMessage = (TXLiveMessageBlockUser *)message;
|
|
|
+ if ([blockMessage.userId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ __blockSelf.blacklistFlag = YES;
|
|
|
+ [__blockSelf resetConnectionStatus];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageUnBlockUser class]]) {
|
|
|
+ TXLiveMessageUnBlockUser *unBlockMessage = (TXLiveMessageUnBlockUser *)message;
|
|
|
+ if ([unBlockMessage.userId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ __blockSelf.blacklistFlag = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageDownSeatAll class]]) { // 所有麦上用户下麦
|
|
|
+ TXLiveMessageDownSeatAll *downSeatMessage = (TXLiveMessageDownSeatAll *)message;
|
|
|
+ if (__blockSelf.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+ [__blockSelf kickSeatWithMessage:@"主讲人断开连麦"];
|
|
|
+ [__blockSelf insertMessage:downSeatMessage];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveMessageRejectAllSeat class]]) { // 拒绝所有连麦申请
|
|
|
+ TXLiveMessageRejectAllSeat *rejectAllMessage = (TXLiveMessageRejectAllSeat *)message;
|
|
|
+ if (__blockSelf.micStatus == MICSTATUS_WAITING) {
|
|
|
+ [__blockSelf MBPShow:@"主讲人拒绝了您的连麦申请"];
|
|
|
+ // 处理连麦状态
|
|
|
+ __blockSelf.micStatus = MICSTATUS_NOMAL;
|
|
|
+ [__blockSelf insertMessage:rejectAllMessage];
|
|
|
+ [__blockSelf hideApplyingView];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXControlMemberMic class]]) { // 控制学生麦克风
|
|
|
+ TXControlMemberMic *micCtrlMsg = (TXControlMemberMic *)message;
|
|
|
+ // 如果是控制当前学生的mic
|
|
|
+ if ([micCtrlMsg.userId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ [__blockSelf changeCurrentMicStatus:micCtrlMsg.muteMic];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([message isMemberOfClass:[TXLiveForceKickMsg class]]) {
|
|
|
+ TXLiveForceKickMsg *changeMsg = (TXLiveForceKickMsg *)message;
|
|
|
+
|
|
|
+ if (changeMsg.targetIds.count == 0 || changeMsg.targetIds == nil) { // 如果是全员
|
|
|
+ [__blockSelf courseChangeTips:changeMsg];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ for (NSString *userId in changeMsg.targetIds) { // 如果部分学员中包含自己
|
|
|
+ if ([userId isEqualToString:UserDefault(IM_USERID)]) {
|
|
|
+ [__blockSelf courseChangeTips:changeMsg];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)courseChangeTips:(TXLiveForceKickMsg *)msg {
|
|
|
+ // 显示直播间关闭状态
|
|
|
+ [self closeAlertView];
|
|
|
+ [self displayCloseView];
|
|
|
+ [self hideApplyingView];
|
|
|
+ // 退出房间
|
|
|
+ NSString *tipsMsg = [NSString isEmptyString:msg.reason] ? @"当前课程已调整,请退出直播间" : msg.reason;
|
|
|
+ MJWeakSelf;
|
|
|
+ self.closeAlert = [LiveRoomConfirmAlert liveroomAlertWithTitle:tipsMsg sureTitle:@"确定" inView:self.view confirm:^{
|
|
|
+ [weakSelf exitRoom];
|
|
|
+ [weakSelf.navigationController dismissViewControllerAnimated:YES completion:nil];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)changeCurrentMicStatus:(BOOL)isMute {
|
|
|
+ if (isMute == !self.micEnable) { // 如果麦克风状态和当前状态一致,剔除
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (_trtcCloud && self.isAudience == NO) {
|
|
|
+ self.micEnable = !self.micEnable;
|
|
|
+ [self.trtcCloud muteLocalAudio:!self.micEnable];
|
|
|
+ NSString *desc = @"";
|
|
|
+ if (self.micEnable) {
|
|
|
+ desc = @"主讲人已将您的麦克风开启";
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ desc = @"主讲人已将您的麦克风关闭";
|
|
|
+ }
|
|
|
+ [self MBPShow:desc];
|
|
|
+ [self syncMicStatus:!self.micEnable];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)didReceiveGroupMessage:(NSNotification *)notification {
|
|
|
+
|
|
|
+ NSDictionary *dic = notification.object; /// __block TXLiveMessageModel *liveMessage
|
|
|
+
|
|
|
+ NSString *groupId = [dic ks_stringValueForKey:@"groupID"];
|
|
|
+ NSDictionary *content = [dic ks_dictionaryValueForKey:@"message"];
|
|
|
+ if ([groupId isEqualToString:self.roomId]) {
|
|
|
+ [self evaluateGroupInfo:content];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)evaluateGroupInfo:(NSDictionary *)groupInfo {
|
|
|
+ // 主播在房间
|
|
|
+ if ([[groupInfo ks_stringValueForKey:@"LIVE_STATUS"] isEqualToString:@"ON"]) { // 直播中
|
|
|
+ self.isPauseLive = NO;
|
|
|
+ if (self.isAudience) {
|
|
|
+ [self startPlay];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if ([[groupInfo ks_stringValueForKey:@"LIVE_STATUS"] isEqualToString:@"OFF"]) { // 暂停直播
|
|
|
+ self.isPauseLive = YES;
|
|
|
+ [self closePlayStatus];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 摄像头状态
|
|
|
+ if ([[groupInfo ks_stringValueForKey:@"ANCHOR_CAMERA"] isEqualToString:@"OFF"]) {
|
|
|
+ self.isCreaterVideoEnable = NO;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.isCreaterVideoEnable = YES;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 禁言状态
|
|
|
+ if ([[groupInfo ks_stringValueForKey:@"GLOBAL_BAN"] isEqualToString:@"ON"]) {
|
|
|
+ self.enableChat = NO;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.enableChat = YES;
|
|
|
+ }
|
|
|
+ // 在线人数
|
|
|
+ if ([[groupInfo allKeys] containsObject:@"MEMBER_ONLINE"]) {
|
|
|
+ self.totalCount = [groupInfo ks_integerValueForKey:@"MEMBER_ONLINE"];
|
|
|
+ [self countMemberCount];
|
|
|
+ }
|
|
|
+
|
|
|
+ [self changeEmptyStatusView];
|
|
|
+
|
|
|
+
|
|
|
+ // 主播全员闭麦状态
|
|
|
+ if ([[groupInfo allKeys] containsObject:@"ANCHOR_MIC"]) {
|
|
|
+ if ([[groupInfo ks_stringValueForKey:@"ANCHOR_MIC"] isEqualToString:@"ON"]) { // 禁麦
|
|
|
+ if (self.muteRoomMic == NO) {
|
|
|
+ self.muteRoomMic = YES;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (self.muteRoomMic == YES) {
|
|
|
+ self.muteRoomMic = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (self.muteRoomMic == YES) {
|
|
|
+ self.muteRoomMic = NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setMuteRoomMic:(BOOL)muteRoomMic {
|
|
|
+ _muteRoomMic = muteRoomMic;
|
|
|
+ [self changeCurrentMicStatus:muteRoomMic];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 群属性变更信息
|
|
|
+- (void)didReceiveGroupCountMessage:(NSNotification *)notification {
|
|
|
+ NSDictionary *dic = notification.object; /// __block TXLiveMessageModel *liveMessage
|
|
|
+
|
|
|
+ NSString *groupId = [dic ks_stringValueForKey:@"groupID"];
|
|
|
+ NSDictionary *content = [dic ks_dictionaryValueForKey:@"message"];
|
|
|
+ if ([groupId isEqualToString:self.roomId]) {
|
|
|
+ [self evaluateGroupCount:content];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)evaluateGroupCount:(NSDictionary *)groupCount {
|
|
|
+ // 点赞数据
|
|
|
+ if ([[groupCount allKeys] containsObject:@"LIKES"]) {
|
|
|
+ self.likeCount = [groupCount ks_integerValueForKey:@"LIKES"];
|
|
|
+ [self countLikeMessageCount];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)closePlayStatus {
|
|
|
+ if (self.micStatus == MICSTATUS_WAITING) {
|
|
|
+ [self sendSeatMessageApply:NO];
|
|
|
+ }
|
|
|
+ [self resetNetTips];
|
|
|
+ [self resetConnectionStatus];
|
|
|
+ [self closeAlertView];
|
|
|
+ [self hideApplyingView];
|
|
|
+ if (self.isAudience) {
|
|
|
+ [self stopPlay];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud stopAllRemoteView];
|
|
|
+ [self.trtcCloud stopLocalAudio];
|
|
|
+ [self.trtcCloud exitRoom];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)kickOutLiveRoom {
|
|
|
+ [self MBPShow:@"您已被踢出直播间"];
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ [self quitRoom];
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ------ message operation
|
|
|
+- (void)resetConnectionStatus {
|
|
|
+ if (self.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ [self sendDownSeatMsg];
|
|
|
+ [self connectHostWithStatus:NO];
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.micStatus = MICSTATUS_NOMAL;
|
|
|
+ [self removeSeatContainer];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)resetMicWaitToNomal {
|
|
|
+ if (self.micStatus == MICSTATUS_WAITING) {
|
|
|
+ self.micStatus = MICSTATUS_NOMAL;
|
|
|
+ }
|
|
|
+}
|
|
|
+- (void)tipsTeacherLeaveRoom {
|
|
|
+ [self MBPShow:@"主讲人已离开房间"];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideApplyView {
|
|
|
+ if (self.applyView.isShow) {
|
|
|
+ [self.applyView hideView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideApplyingView {
|
|
|
+ if (self.applyingView.isShow) {
|
|
|
+ [self.applyingView hideView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)closeAlertView {
|
|
|
+ if (self.alertView && self.alertView.isShow) {
|
|
|
+ if (self.alertView.isQuitAlert == NO) {
|
|
|
+ [self.alertView removeFromSuperview];
|
|
|
+ self.alertView = nil;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)displayCloseView {
|
|
|
+ self.isCloseRoom = YES;
|
|
|
+ [self hideTipsView];
|
|
|
+ // 移除连麦视图
|
|
|
+ [self removeSeatContainer];
|
|
|
+ if (self.micStatus != MICSTATUS_NOMAL) {
|
|
|
+ self.micStatus = MICSTATUS_NOMAL;
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud stopLocalAudio];
|
|
|
+ [self.trtcCloud exitRoom];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ [self stopPlay];
|
|
|
+ self.videoView.hidden = YES;
|
|
|
+ self.endView.hidden = NO;
|
|
|
+ [self setDefaultBottomViewStatus];
|
|
|
+
|
|
|
+ [[NSNotificationCenter defaultCenter] postNotificationName:@"LiveroomClose" object:nil];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendCardMessage:(NSArray *)content {
|
|
|
+// if (_webContainer) {
|
|
|
+// NSMutableDictionary *sendParm = [NSMutableDictionary dictionary];
|
|
|
+// [sendParm setValue:@"cardChange" forKey:@"api"];
|
|
|
+// [sendParm setValue:content forKey:@"content"];
|
|
|
+// [self.webContainer postMessage:sendParm];
|
|
|
+// }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showInviteAlertMessage:(NSString *)tipsMessage {
|
|
|
+ if (self.alertView && self.alertView.isShow) {
|
|
|
+ [self.alertView removeFromSuperview];
|
|
|
+ self.alertView = nil;
|
|
|
+ }
|
|
|
+ MJWeakSelf;
|
|
|
+ self.alertView = [LiveAlertView shareInstance];
|
|
|
+ [self.alertView alertWithTitle:@"连麦邀请" desc:tipsMessage leftButtonTitle:@"取消" rightTitle:@"确定" inView:self.view cancel:^{
|
|
|
+ weakSelf.micStatus = MICSTATUS_NOMAL;
|
|
|
+ [weakSelf sendSeatResponseMessage:NO];
|
|
|
+ } confirm:^{
|
|
|
+ [weakSelf sendSeatResponseMessage:YES];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendSeatResponseMessage:(BOOL)isApprove {
|
|
|
+ SEATRESPONSE type = isApprove ? SEATRESPONSE_AUDIENCEAPPROVE : SEATRESPONSE_AUDIENCEREJECT;
|
|
|
+ TXLiveMessageSeatResponse *responseMessage = [[TXLiveMessageSeatResponse alloc] init];
|
|
|
+ responseMessage.userInfo = [self getCurrentUser];
|
|
|
+ responseMessage.type = type;
|
|
|
+ responseMessage.audienceId = UserDefault(IM_USERID);
|
|
|
+ responseMessage.audienceName = UserDefault(NicknameKey);
|
|
|
+ MJWeakSelf;
|
|
|
+ [self sendCustomMessage:responseMessage priority:V2TIM_PRIORITY_HIGH displayMessage:YES callback:^(BOOL success) {
|
|
|
+ if (success) {
|
|
|
+ if (isApprove) {
|
|
|
+ [weakSelf connectHostWithStatus:YES];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)kickSeatWithMessage:(NSString *)message {
|
|
|
+ [self MBPShow:message];
|
|
|
+ [self sendDownSeatMsg];
|
|
|
+ [self connectHostWithStatus:NO];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)insertMessage:(TXLiveMessageModel *)liveMessage {
|
|
|
+ [self appendAndDisplayMessage:liveMessage];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark table data source
|
|
|
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
|
|
+ return self.conversationDataRepository.count;
|
|
|
+}
|
|
|
+
|
|
|
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
|
+ TXLiveMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
|
|
|
+ TXLiveChatListCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TXLiveChatListCell"];
|
|
|
+ if (!cell) {
|
|
|
+ cell = [[TXLiveChatListCell appearance] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"TXLiveChatListCell"];
|
|
|
+ }
|
|
|
+ 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]] ){
|
|
|
+ [cell setDataModel:model createrId:self.createrId];
|
|
|
+ }
|
|
|
+
|
|
|
+ return cell;
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ----- lazying
|
|
|
+- (TRTCCloud *)trtcCloud {
|
|
|
+ if (!_trtcCloud) {
|
|
|
+ _trtcCloud = [TRTCCloud sharedInstance];
|
|
|
+ _trtcCloud.delegate = self;
|
|
|
+ }
|
|
|
+ return _trtcCloud;
|
|
|
+}
|
|
|
+- (V2TXLivePlayer *)livePlayer {
|
|
|
+ if (!_livePlayer) {
|
|
|
+ _livePlayer = [[V2TXLivePlayer alloc] init];
|
|
|
+ [_livePlayer enableReceiveSeiMessage:YES payloadType:243];
|
|
|
+ [_livePlayer setObserver:self];
|
|
|
+
|
|
|
+ }
|
|
|
+ return _livePlayer;
|
|
|
+}
|
|
|
+
|
|
|
+- (UIView *)videoContainerView {
|
|
|
+ if (!_videoContainerView) {
|
|
|
+ _videoContainerView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
|
+ _videoContainerView.backgroundColor = [UIColor clearColor];
|
|
|
+ }
|
|
|
+ return _videoContainerView;
|
|
|
+}
|
|
|
+
|
|
|
+- (UIView *)videoView {
|
|
|
+ if (!_videoView) {
|
|
|
+ _videoView = [[UIView alloc] initWithFrame:CGRectZero];
|
|
|
+ _videoView.backgroundColor = HexRGB(0x1a1a1a);
|
|
|
+ }
|
|
|
+ return _videoView;
|
|
|
+}
|
|
|
+
|
|
|
+- (TXLiveVideoView *)videoRenderView {
|
|
|
+ if (!_videoRenderView) {
|
|
|
+ _videoRenderView = [[TXLiveVideoView alloc] initWithFrame:CGRectZero];
|
|
|
+ }
|
|
|
+ return _videoRenderView;
|
|
|
+}
|
|
|
+
|
|
|
+- (LiveRoomHeadView *)headView {
|
|
|
+ if (!_headView) {
|
|
|
+ _headView = [LiveRoomHeadView shareInstance];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_headView quitAction:^{
|
|
|
+ [weakSelf showQuitAlert];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _headView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showQuitAlert {
|
|
|
+ if (self.isCloseRoom) { // 如果直播结束 直接点击退出房间
|
|
|
+ [self quitRoom];
|
|
|
+ }
|
|
|
+ else if (self.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+ [self quitAction];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ MJWeakSelf;
|
|
|
+ self.alertView = [LiveAlertView shareInstance];
|
|
|
+ self.alertView.isQuitAlert = YES;
|
|
|
+ [self.alertView alertWithTitle:@"确定要退出直播间吗?" desc:@"退出后如再次观看可重新进入~" leftButtonTitle:@"取消" rightTitle:@"确认" inView:self.view cancel:^{
|
|
|
+
|
|
|
+ } confirm:^{
|
|
|
+ [weakSelf quitAction];
|
|
|
+
|
|
|
+ }];
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)quitRoom {
|
|
|
+ KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
|
|
|
+ if (manager.hasShowWindow) {
|
|
|
+ [manager resignDragWindow];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (self.landScape) {
|
|
|
+ [self changeOrientation:!self.landScape];
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+
|
|
|
+ // 退出房间
|
|
|
+ [self exitRoom];
|
|
|
+ [self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 退出房间
|
|
|
+ [self exitRoom];
|
|
|
+ [self.navigationController dismissViewControllerAnimated:YES completion:nil];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 顶部退出房间
|
|
|
+- (void)quitAction {
|
|
|
+ if (self.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+ MJWeakSelf;
|
|
|
+ self.alertView = [LiveAlertView shareInstance];
|
|
|
+ [self.alertView alertWithTitle:@"提示" desc:@"连麦中,是否退出房间?" leftButtonTitle:@"取消" rightTitle:@"确定" inView:self.view cancel:^{
|
|
|
+
|
|
|
+ } confirm:^{
|
|
|
+ [weakSelf quitRoom];
|
|
|
+
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self quitRoom];
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)exitRoom {
|
|
|
+ if (self.isAudience) {
|
|
|
+ [self stopPlay];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud stopLocalAudio];
|
|
|
+ [self.trtcCloud exitRoom];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ TXLiveMessageLeave *leaveMsg = [[TXLiveMessageLeave alloc] init];
|
|
|
+ leaveMsg.userInfo = [self getCurrentUser];
|
|
|
+ MJWeakSelf;
|
|
|
+ [self sendCustomMessage:leaveMsg priority:V2TIM_PRIORITY_NORMAL displayMessage:NO callback:^(BOOL success) {
|
|
|
+ // quit 接口
|
|
|
+ [weakSelf quitNotiferService];
|
|
|
+ [weakSelf quitChatRoom];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)quitChatRoom {
|
|
|
+ [[V2TIMManager sharedInstance] quitGroup:self.roomId succ:^{
|
|
|
+
|
|
|
+ } fail:^(int code, NSString *desc) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (LiveRoomBottomView *)bottomView {
|
|
|
+ if (!_bottomView) {
|
|
|
+ _bottomView = [LiveRoomBottomView shareInstance];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_bottomView bottomClickAction:^(LIVEROOMACTION action) {
|
|
|
+ [weakSelf bottomViewAction:action];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _bottomView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)bottomViewAction:(LIVEROOMACTION)action {
|
|
|
+ if (self.isCloseRoom) {
|
|
|
+ [self MBPShow:@"直播已结束"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (action == LIVEROOMACTION_CHAT) {
|
|
|
+ if (self.blacklistFlag == YES) {
|
|
|
+ [self MBPShow:@"您已被管理员禁言"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (self.enableChat) { // 判断是否全员禁言
|
|
|
+ [_inputBar setHidden:NO];
|
|
|
+ [_inputBar setInputBarStatus:KSBottomBarStatusKeyboard];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self MBPShow:@"管理员关闭聊天"];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (action == LIVEROOMACTION_SEAT) { // 连麦按钮
|
|
|
+ // 判断是否是连麦状态
|
|
|
+ if (self.micStatus == MICSTATUS_CONNECTING) { // 连麦中
|
|
|
+ // 是否取消连麦
|
|
|
+ MJWeakSelf;
|
|
|
+ self.alertView = [LiveAlertView shareInstance];
|
|
|
+ [self.alertView alertWithTitle:@"连麦中,确认取消连麦吗?" desc:@"断开后如想再次连麦可重新发起" leftButtonTitle:@"取消" rightTitle:@"确定关闭" inView:self.view cancel:^{
|
|
|
+
|
|
|
+ } confirm:^{
|
|
|
+ [weakSelf downSeatAction];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ else if (self.micStatus == MICSTATUS_WAITING) { // 连麦申请中
|
|
|
+ // 弹窗取消申请
|
|
|
+ [self displayApplyingView];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 禁止聊天可以连麦(测试确认)
|
|
|
+ if (self.blacklistFlag == YES) {
|
|
|
+ [self MBPShow:@"您已被管理员禁言"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (self.isPauseLive == YES) {
|
|
|
+ [self MBPShow:@"老师暂停中,暂时无法接受申请"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (self.enableSeat) {
|
|
|
+ [self displaySeatApplyView];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self MBPShow:@"管理员关闭连麦申请"];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (action == LIVEROOMACTION_LIKE) { // 点赞
|
|
|
+
|
|
|
+ [self praiseBtnPressed];
|
|
|
+ }
|
|
|
+ else if (action == LIVEROOMACTION_SHOP) { // shop
|
|
|
+ [self showCartAlert];
|
|
|
+ }
|
|
|
+ else if (action == LIVEROOMACTION_MUTE) {
|
|
|
+ [self muteVoiceAction];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showCartAlert {
|
|
|
+ [self.cardView showViewInView:self.view];
|
|
|
+}
|
|
|
+- (void)downSeatAction {
|
|
|
+ [self sendDownSeatMsg];
|
|
|
+ [self connectHostWithStatus:NO];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)sendDownSeatMsg {
|
|
|
+ TXLiveMessageDownSeat *downSeatMsg = [[TXLiveMessageDownSeat alloc] init];
|
|
|
+ TXLiveUser *mineInfo = [self getCurrentUser];
|
|
|
+ downSeatMsg.userInfo = mineInfo;
|
|
|
+ downSeatMsg.audienceId = mineInfo.sendUserId;
|
|
|
+ downSeatMsg.audienceName = mineInfo.sendUserName;
|
|
|
+
|
|
|
+ [self sendCustomMessage:downSeatMsg priority:V2TIM_PRIORITY_HIGH displayMessage:NO callback:^(BOOL success) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (KSShopCardView *)cardView {
|
|
|
+ if (!_cardView) {
|
|
|
+ _cardView = [KSShopCardView shareInstance];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_cardView clickAction:^(CART_TYPE type) {
|
|
|
+ [weakSelf displayTeacherInfo:type];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _cardView;
|
|
|
+}
|
|
|
+// tabs= practice | 陪练课 live | 直播课 video | 视频课 music | 乐谱 跳转到对应tab页
|
|
|
+
|
|
|
+- (void)displayTeacherInfo:(CART_TYPE)type {
|
|
|
+ [self sendRushMessage];
|
|
|
+ if (self.isPauseLive == NO) {
|
|
|
+ _hasShowSuspendView = YES;
|
|
|
+ KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
|
|
|
+ BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
|
|
|
+ [manager initDragWindowWithPhoneLive:isPhoneLive];
|
|
|
+ manager.rootVC = (CustomNavViewController *)self.navigationController;
|
|
|
+
|
|
|
+ [self.videoRenderView removeFromSuperview];
|
|
|
+ [manager.dragWindow.containerView addSubview:self.videoRenderView];
|
|
|
+ [self.videoRenderView mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(manager.dragWindow.containerView);
|
|
|
+ }];
|
|
|
+ [manager.dragWindow bringSubviewToFront:manager.dragWindow.cancelButton];
|
|
|
+ }
|
|
|
+
|
|
|
+ NSString *tabs = @"";
|
|
|
+ switch (type) {
|
|
|
+ case CART_TYPE_NONE:
|
|
|
+ {
|
|
|
+ tabs = @"";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CART_TYPE_ACCOMPANY:
|
|
|
+ {
|
|
|
+ tabs = @"practice";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CART_TYPE_LIVE:
|
|
|
+ {
|
|
|
+ tabs = @"live";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CART_TYPE_VIDEO:
|
|
|
+ {
|
|
|
+ tabs = @"video";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case CART_TYPE_MUSIC:
|
|
|
+ {
|
|
|
+ tabs = @"music";
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ NSString *url = @"";
|
|
|
+ if ([NSString isEmptyString:tabs]) {
|
|
|
+ url = [NSString stringWithFormat:@"%@%@%@", WEBHOST,@"/#/teacherHome?teacherId=",self.createrId];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ url = [NSString stringWithFormat:@"%@%@%@&tabs=%@", WEBHOST,@"/#/teacherHome?teacherId=",self.createrId,tabs];
|
|
|
+ }
|
|
|
+ KSLiveWebViewController *ctrl = [[KSLiveWebViewController alloc] init];
|
|
|
+ ctrl.url = url;
|
|
|
+ [self.navigationController pushViewController:ctrl animated:YES];
|
|
|
+}
|
|
|
+- (void)sendRushMessage {
|
|
|
+ TXLiveMessageShopRush *rushMessage = [[TXLiveMessageShopRush alloc] init];
|
|
|
+ rushMessage.userInfo = [self getCurrentUser];
|
|
|
+ [self sendCustomMessage:rushMessage priority:V2TIM_PRIORITY_DEFAULT displayMessage:NO callback:^(BOOL success) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)toDetailView:(NSString *)linkUrl {
|
|
|
+ [self sendRushMessage];
|
|
|
+
|
|
|
+ if (self.isPauseLive == NO) {
|
|
|
+ _hasShowSuspendView = YES;
|
|
|
+ KSDragWindowManager *manager = [KSDragWindowManager sharedManager];
|
|
|
+ BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
|
|
|
+ [manager initDragWindowWithPhoneLive:isPhoneLive];
|
|
|
+ manager.rootVC = (CustomNavViewController *)self.navigationController;
|
|
|
+
|
|
|
+ [self.videoRenderView removeFromSuperview];
|
|
|
+ [manager.dragWindow.containerView addSubview:self.videoRenderView];
|
|
|
+ [self.videoRenderView mas_updateConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.top.bottom.mas_equalTo(manager.dragWindow.containerView);
|
|
|
+ }];
|
|
|
+ [manager.dragWindow bringSubviewToFront:manager.dragWindow.cancelButton];
|
|
|
+ }
|
|
|
+ KSLiveWebViewController *ctrl = [[KSLiveWebViewController alloc] init];
|
|
|
+ ctrl.url = linkUrl;
|
|
|
+ [self.navigationController pushViewController:ctrl animated:YES];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)muteVoiceAction {
|
|
|
+ if (self.bottomView.isMute) {
|
|
|
+ if ([self.livePlayer isPlaying]) {
|
|
|
+ [self.livePlayer setPlayoutVolume:0];
|
|
|
+ }
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud muteAllRemoteAudio:YES];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if ([self.livePlayer isPlaying]) {
|
|
|
+ [self.livePlayer setPlayoutVolume:100];
|
|
|
+ }
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud muteAllRemoteAudio:NO];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (KSLiveEmptyView *)emptyView {
|
|
|
+ if (!_emptyView) {
|
|
|
+ _emptyView = [[KSLiveEmptyView alloc] init];
|
|
|
+ BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
|
|
|
+ [_emptyView dispPlayViewIsPhoneLive:isPhoneLive];
|
|
|
+ }
|
|
|
+ return _emptyView;
|
|
|
+}
|
|
|
+
|
|
|
+- (KSLiveEndView *)endView {
|
|
|
+ if (!_endView) {
|
|
|
+ _endView = [[KSLiveEndView alloc] init];
|
|
|
+ _endView.backgroundColor = HexRGB(0x25292e);
|
|
|
+ BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
|
|
|
+ [_endView dispPlayViewIsPhoneLive:isPhoneLive];
|
|
|
+ }
|
|
|
+ return _endView;
|
|
|
+}
|
|
|
+
|
|
|
+- (KSLiveBadNetView *)netBadView {
|
|
|
+ if (!_netBadView) {
|
|
|
+ _netBadView = [[KSLiveBadNetView alloc] init];
|
|
|
+ BOOL isPhoneLive = self.needSwitchButton ? NO : YES;
|
|
|
+ [_netBadView dispPlayViewIsPhoneLive:isPhoneLive];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_netBadView configViewWithImageName:@"liveVideo_close" desc:@"视频加载失败" callback:^{
|
|
|
+ [weakSelf resetAllConnection];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _netBadView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)resetNetTips {
|
|
|
+ [self hideTipsView];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showBadNetTips {
|
|
|
+ dispatch_main_async_safe(^{
|
|
|
+ if (self.isCloseRoom == NO) {
|
|
|
+ if (self.netBadView.hidden == YES) {
|
|
|
+ self.netBadView.hidden = NO;
|
|
|
+ self.videoView.hidden = YES;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideTipsView {
|
|
|
+ dispatch_main_async_safe(^{
|
|
|
+ if (self.netBadView.hidden == NO) {
|
|
|
+ self.netBadView.hidden = YES;
|
|
|
+ self.videoView.hidden = NO;
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+}
|
|
|
+// 黑名单
|
|
|
+- (void)setBlacklistFlag:(BOOL)blacklistFlag {
|
|
|
+ _blacklistFlag = blacklistFlag;
|
|
|
+ [self refreshBottomChatStatus];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)refreshBottomChatStatus {
|
|
|
+ if (self.bottomView) {
|
|
|
+ NSString *tipsTitle = self.blacklistFlag ? @"您已被管理员禁言" : @"快来互动吧!";
|
|
|
+ self.bottomView.tipsLabel.text = tipsTitle;
|
|
|
+ self.bottomView.tipsLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)resetAllConnection {
|
|
|
+ [self resetNetTips];
|
|
|
+ // 清理视图
|
|
|
+ [self hideEmptyView];
|
|
|
+ [self removeSeatContainer];
|
|
|
+
|
|
|
+ // 立刻房间后重新加入房间
|
|
|
+ [self startPlay];
|
|
|
+}
|
|
|
+
|
|
|
+- (LOTAnimationView *)animationView {
|
|
|
+ if (!_animationView) {
|
|
|
+ _animationView = [LOTAnimationView animationWithFilePath:[[NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"LiveCard" ofType:@"bundle"]] pathForResource:@"cardAnimation" ofType:@"json"]];
|
|
|
+ _animationView.loopAnimation = YES;
|
|
|
+ }
|
|
|
+ return _animationView;
|
|
|
+}
|
|
|
+
|
|
|
+- (UIButton *)switchButton {
|
|
|
+ if (!_switchButton) {
|
|
|
+ _switchButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
|
|
+ [_switchButton setImage:[UIImage imageNamed:@"live_switch"] forState:UIControlStateNormal];
|
|
|
+ [_switchButton addTarget:self action:@selector(transferToLandscape) forControlEvents:UIControlEventTouchUpInside];
|
|
|
+ }
|
|
|
+ return _switchButton;
|
|
|
+}
|
|
|
+
|
|
|
+- (UIView *)messageContentView {
|
|
|
+ if (!_messageContentView) {
|
|
|
+ _messageContentView = [[UIView alloc] init];
|
|
|
+ [_messageContentView setBackgroundColor: [UIColor clearColor]];
|
|
|
+ }
|
|
|
+ return _messageContentView;
|
|
|
+}
|
|
|
+
|
|
|
+- (UITableView *)conversationMessageTableView {
|
|
|
+ if (!_conversationMessageTableView) {
|
|
|
+ _conversationMessageTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
|
|
+ _conversationMessageTableView.backgroundColor = [UIColor clearColor];
|
|
|
+ _conversationMessageTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
|
|
+ _conversationMessageTableView.dataSource = self;
|
|
|
+ _conversationMessageTableView.delegate = self;
|
|
|
+ _conversationMessageTableView.rowHeight = UITableViewAutomaticDimension;
|
|
|
+ _conversationMessageTableView.estimatedRowHeight = 40.0f;
|
|
|
+ _conversationMessageTableView.showsVerticalScrollIndicator = NO;
|
|
|
+ [_conversationMessageTableView registerClass:[TXLiveChatListCell class] forCellReuseIdentifier:@"TXLiveChatListCell"];
|
|
|
+ }
|
|
|
+ return _conversationMessageTableView;
|
|
|
+}
|
|
|
+
|
|
|
+- (NSMutableArray<TXLiveMessageModel *> *)conversationDataRepository {
|
|
|
+ if (!_conversationDataRepository) {
|
|
|
+ _conversationDataRepository = [NSMutableArray array];
|
|
|
+ }
|
|
|
+ return _conversationDataRepository;
|
|
|
+}
|
|
|
+
|
|
|
+- (TXSeatContainerView *)seatContainer {
|
|
|
+ if (!_seatContainer) {
|
|
|
+ _seatContainer = [[TXSeatContainerView alloc] init];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_seatContainer seatActionCallback:^(NSString * _Nonnull userId, BOOL muteMic) {
|
|
|
+ [weakSelf muteUserAction:userId muteStatus:muteMic];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _seatContainer;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)muteUserAction:(NSString *)userId muteStatus:(BOOL)muteMic {
|
|
|
+ [self micButtonAction:muteMic];
|
|
|
+ TXMicStatusSync *ctrlMsg = [[TXMicStatusSync alloc] init];
|
|
|
+ ctrlMsg.userInfo = [self getCurrentUser];
|
|
|
+ ctrlMsg.userId = userId;
|
|
|
+ ctrlMsg.muteMic = muteMic;
|
|
|
+ [self sendCustomMessage:ctrlMsg priority:V2TIM_PRIORITY_HIGH displayMessage:NO callback:^(BOOL success) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (KSChatInputBarControl *)inputBar {
|
|
|
+ if (!_inputBar) {
|
|
|
+ _inputBar = [[KSChatInputBarControl alloc] initWithStatus:KSBottomBarStatusDefault];
|
|
|
+ [_inputBar setDelegate:self];
|
|
|
+ }
|
|
|
+ return _inputBar;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)setMicStatus:(MICSTATUS)micStatus {
|
|
|
+ _micStatus = micStatus;
|
|
|
+ if (self.bottomView) {
|
|
|
+ switch (micStatus) {
|
|
|
+ case MICSTATUS_NOMAL:
|
|
|
+ {
|
|
|
+ [self.bottomView.seatButton setImage:[UIImage imageNamed:@"seat_image"] forState:UIControlStateNormal];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MICSTATUS_WAITING:
|
|
|
+ {
|
|
|
+ [self.bottomView.seatButton setImage:[UIImage imageNamed:@"seat_connecting"] forState:UIControlStateNormal];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MICSTATUS_CONNECTING:
|
|
|
+ {
|
|
|
+ [self.bottomView.seatButton setImage:[UIImage imageNamed:@"seat_connected"] forState:UIControlStateNormal];
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+#pragma mark ----- 人数和点赞数统计
|
|
|
+- (void)countLikeMessageCount {
|
|
|
+ NSInteger count = self.likeCount;
|
|
|
+ NSString *desc = @"";
|
|
|
+ if (count > 10000) {
|
|
|
+ count = count / 10000;
|
|
|
+ desc = [NSString stringWithFormat:@"本场点赞%zd万+",count];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ desc = [NSString stringWithFormat:@"本场点赞%zd",count];
|
|
|
+ }
|
|
|
+ self.headView.likeCount.text = desc;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)countMemberCount {
|
|
|
+ NSInteger count = self.totalCount;
|
|
|
+ NSString *desc = @"";
|
|
|
+ if (count > 10000) {
|
|
|
+ count = count / 10000;
|
|
|
+ desc = [NSString stringWithFormat:@"%zd万+人",count];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ desc = [NSString stringWithFormat:@"%zd人",count];
|
|
|
+ }
|
|
|
+ self.headView.roomMemberCount.text = desc;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 判断消息是否在collectionView的底部
|
|
|
+ *
|
|
|
+ * @return 是否在底部
|
|
|
+ */
|
|
|
+- (BOOL)isAtTheBottomOfTableView {
|
|
|
+ if (self.conversationMessageTableView.contentSize.height <= self.conversationMessageTableView.frame.size.height) {
|
|
|
+ return YES;
|
|
|
+ }
|
|
|
+ if(self.conversationMessageTableView.contentOffset.y +200 >= (self.conversationMessageTableView.contentSize.height - self.conversationMessageTableView.frame.size.height)) {
|
|
|
+ return YES;
|
|
|
+ }else{
|
|
|
+ return NO;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 消息滚动到底部
|
|
|
+ *
|
|
|
+ * @param animated 是否开启动画效果
|
|
|
+ */
|
|
|
+- (void)scrollToBottomAnimated:(BOOL)animated {
|
|
|
+ if ([self.conversationMessageTableView numberOfSections] == 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSUInteger finalRow = MAX(0, [self.conversationMessageTableView numberOfRowsInSection:0] - 1);
|
|
|
+ if (0 == finalRow) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSIndexPath *finalIndexPath =
|
|
|
+ [NSIndexPath indexPathForItem:finalRow inSection:0];
|
|
|
+ [self.conversationMessageTableView scrollToRowAtIndexPath:finalIndexPath atScrollPosition:UITableViewScrollPositionTop animated:animated];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark ---- dealloc
|
|
|
+- (void)dealloc {
|
|
|
+ NSLog(@"---- live room dealloc");
|
|
|
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
|
+ if (_livePlayer) {
|
|
|
+ [self.livePlayer stopPlay];
|
|
|
+ _livePlayer = nil;
|
|
|
+ }
|
|
|
+ if (_trtcCloud) {
|
|
|
+ [self.trtcCloud stopAllRemoteView];
|
|
|
+ [self.trtcCloud stopLocalAudio];
|
|
|
+ [self.trtcCloud exitRoom];
|
|
|
+ _trtcCloud = nil;
|
|
|
+ [TRTCCloud destroySharedIntance];
|
|
|
+ }
|
|
|
+ [TXIM_LINSENTER logoutTXIM];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#pragma mark - KSChatInputBarControlDelegate
|
|
|
+// 根据inputBar 回调来修改页面布局
|
|
|
+- (void)onInputBarControlContentSizeChanged:(CGRect)frame withAnimationDuration:(CGFloat)duration andAnimationCurve:(UIViewAnimationCurve)curve {
|
|
|
+ CGRect originFrame = _inputBar.frame;
|
|
|
+ __weak __typeof(&*self)weakSelf = self;
|
|
|
+ // 目前只使用y值即可 -- 只修改messageContentView高度,让展示消息view和输入框随之移动
|
|
|
+ [UIView animateWithDuration:duration animations:^{
|
|
|
+ [UIView setAnimationCurve:curve];
|
|
|
+ [weakSelf.inputBar setFrame:CGRectMake(0, frame.origin.y - originFrame.size.height, originFrame.size.width, originFrame.size.height)];
|
|
|
+ [UIView commitAnimations];
|
|
|
+ }];
|
|
|
+}
|
|
|
+// 发送消息
|
|
|
+- (void)onTouchSendButton:(NSString *)text {
|
|
|
+ if (self.blacklistFlag == YES) {
|
|
|
+ [self setDefaultBottomViewStatus];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSDate *date = [NSDate date];
|
|
|
+ NSTimeInterval currentInterval = [date timeIntervalSince1970];
|
|
|
+ if (currentInterval - self.lastSendMsgTime < 3) {
|
|
|
+ [self MBPShow:@"您说话太快啦"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (self.lastSendMsgTime == 0) {
|
|
|
+ self.lastSendMsgTime = currentInterval;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (text.length > 40) {
|
|
|
+ [self MBPShow:@"聊天消息需在40个字以内哦"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (self.enableChat) {
|
|
|
+ self.lastSendMsgTime = currentInterval;
|
|
|
+ [self touristSendMessage:text];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ [self setDefaultBottomViewStatus];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)touristSendMessage:(NSString *)text {
|
|
|
+ TXLiveTextMessage *message = [[TXLiveTextMessage alloc] init];
|
|
|
+ message.userInfo = [self getCurrentUser];
|
|
|
+ message.text = text;
|
|
|
+ MJWeakSelf;
|
|
|
+ [self sendTextMessage:message priority:V2TIM_PRIORITY_NORMAL displayMessage:YES callback:^(BOOL success) {
|
|
|
+ [weakSelf setDefaultBottomViewStatus];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (TXLiveUser *)getCurrentUser {
|
|
|
+ TXLiveUser *user = [[TXLiveUser alloc] init];
|
|
|
+ user.sendUserId = UserDefault(IM_USERID);
|
|
|
+ user.sendUserName = UserDefault(NicknameKey);
|
|
|
+ user.avatarUrl = UserDefault(AvatarUrlKey);
|
|
|
+ return user;
|
|
|
+}
|
|
|
+/**
|
|
|
+ 点赞
|
|
|
+ */
|
|
|
+- (void)praiseBtnPressed {
|
|
|
+ if (self.enableLike == NO) {
|
|
|
+ [self MBPShow:@"管理员已关闭点赞功能"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSTimeInterval currentTime = [[NSDate date] timeIntervalSince1970];
|
|
|
+ __weak __typeof(&*self)weakSelf = self;
|
|
|
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.21 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
|
+ if ([[NSDate date] timeIntervalSince1970] - self.lastClickPraiseTime >= 0.2) {
|
|
|
+
|
|
|
+ [weakSelf modifyLikeCount:clickPraiseBtnTimes];
|
|
|
+ clickPraiseBtnTimes = 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ clickPraiseBtnTimes++;
|
|
|
+ self.lastClickPraiseTime = currentTime;
|
|
|
+ [self presentLikeMessageAnimation];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)modifyLikeCount:(NSInteger)count {
|
|
|
+ [[V2TIMManager sharedInstance] increaseGroupCounter:self.roomId key:@"LIKES" value:count succ:^(NSDictionary<NSString *,NSNumber *> *groupCounters) {
|
|
|
+
|
|
|
+ } fail:^(int code, NSString *desc) {
|
|
|
+
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ 赞动画
|
|
|
+ */
|
|
|
+- (void)presentLikeMessageAnimation {
|
|
|
+ LiveRoomLikeLayer *layer = [[LiveRoomLikeLayer alloc] initWithName:@"Like"];
|
|
|
+ [self.videoContainerView.layer addSublayer:layer];
|
|
|
+ CGRect tempFrame = [self.videoContainerView convertRect:self.bottomView.likeButton.frame fromView:self.bottomView];
|
|
|
+ CGFloat width = tempFrame.size.width;
|
|
|
+ CGFloat height = tempFrame.size.height;
|
|
|
+
|
|
|
+ CGFloat dx = CGRectGetMidX(tempFrame) - width *0.5;
|
|
|
+ CGFloat dy = CGRectGetMidY(tempFrame) - 8 - height;
|
|
|
+ layer.frame = CGRectMake(dx, dy, width, height);
|
|
|
+ [layer startAnimation];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)displaySeatApplyView {
|
|
|
+ [self.applyView showInView:self.view];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)displayApplyingView {
|
|
|
+ [self.applyingView showInView:self.view];
|
|
|
+}
|
|
|
+
|
|
|
+- (LiveApplyView *)applyView {
|
|
|
+ if (!_applyView) {
|
|
|
+ _applyView = [LiveApplyView shareInstance];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_applyView applySeatCallback:^{
|
|
|
+ [weakSelf applySeatAction];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _applyView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)applySeatAction {
|
|
|
+ [self sendSeatMessageApply:YES];
|
|
|
+}
|
|
|
+
|
|
|
+- (void)sendSeatMessageApply:(BOOL)isApply {
|
|
|
+ if (self.micStatus == MICSTATUS_CONNECTING) {
|
|
|
+ [self MBPShow:@"您已上麦"];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ SEATHANDLE type = isApply ? SEATHANDLE_APPLY : SEATHANDLE_CANCELAPPLY;
|
|
|
+ TXLiveMessageSeatApply *applyMessage = [[TXLiveMessageSeatApply alloc] init];
|
|
|
+ applyMessage.type = type;
|
|
|
+ applyMessage.audienceId = UserDefault(IM_USERID);
|
|
|
+ applyMessage.audienceName = UserDefault(NicknameKey);
|
|
|
+ applyMessage.audienceAvatar = UserDefault(AvatarUrlKey);
|
|
|
+ applyMessage.teacherId = self.createrId;
|
|
|
+ applyMessage.teacherName = self.createrName;
|
|
|
+ applyMessage.userInfo = [self getCurrentUser];
|
|
|
+ MJWeakSelf;
|
|
|
+ [self sendCustomMessage:applyMessage priority:V2TIM_PRIORITY_HIGH displayMessage:YES callback:^(BOOL success) {
|
|
|
+ if (success) {
|
|
|
+ weakSelf.micStatus = isApply ? MICSTATUS_WAITING : MICSTATUS_NOMAL;
|
|
|
+ }
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+- (LiveApplyingView *)applyingView {
|
|
|
+ if (!_applyingView) {
|
|
|
+ _applyingView = [LiveApplyingView shareInstance];
|
|
|
+ NSString *avatar = UserDefaultObjectForKey(AvatarUrlKey);
|
|
|
+ [_applyingView.myAvatal sd_setImageWithURL:[NSURL URLWithString:[avatar getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:USERDEFAULT_LOGO]];
|
|
|
+ [_applyingView.teacherAvatal sd_setImageWithURL:[NSURL URLWithString:[self.createrAvatal getUrlEndcodeString]] placeholderImage:[UIImage imageNamed:USERDEFAULT_LOGO]];
|
|
|
+ MJWeakSelf;
|
|
|
+ [_applyingView cancleApplyCallback:^{
|
|
|
+ [weakSelf cancleApplyAction];
|
|
|
+ }];
|
|
|
+ }
|
|
|
+ return _applyingView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)cancleApplyAction {
|
|
|
+ MJWeakSelf;
|
|
|
+ self.alertView = [LiveAlertView shareInstance];
|
|
|
+ [self.alertView alertWithTitle:@"连麦申请中,确认取消连麦申请吗?" desc:@"断开后如想再次连麦可重新发起" leftButtonTitle:@"取消" rightTitle:@"确定关闭" inView:self.view cancel:^{
|
|
|
+
|
|
|
+ } confirm:^{
|
|
|
+ [weakSelf.applyingView hideView];
|
|
|
+ [weakSelf sendSeatMessageApply:NO];
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ---- sendMessage/showMessage
|
|
|
+// 文本消息
|
|
|
+- (void)sendTextMessage:(TXLiveMessageModel *)messageContent priority:(V2TIMMessagePriority)priority displayMessage:(BOOL)displayMessage callback:(void(^)(BOOL success))callback {
|
|
|
+ NSData *msgData = [messageContent encodeMessage];
|
|
|
+ if (msgData == nil) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ __weak typeof(&*self) __weakself = self;
|
|
|
+ [TXLiveMessageCenter sendLiveChatMessage:msgData groupId:self.roomId priority:priority success:^{
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+
|
|
|
+ if (displayMessage) {
|
|
|
+ [__weakself appendAndDisplayMessage:messageContent];
|
|
|
+ }
|
|
|
+ [__weakself.inputBar clearInputView];
|
|
|
+ callback(YES);
|
|
|
+ });
|
|
|
+ } failer:^(int code, NSString * _Nonnull msg) {
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ [__weakself.inputBar clearInputView];
|
|
|
+ NSLog(@"发送失败,errorcode is: %d message: %@",code, msg);
|
|
|
+ callback(NO);
|
|
|
+ });
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+// 其他消息
|
|
|
+- (void)sendCustomMessage:(TXLiveMessageModel *)messageContent priority:(V2TIMMessagePriority)priority displayMessage:(BOOL)displayMessage callback:(void(^)(BOOL success))callback {
|
|
|
+ NSData *msgData = [messageContent encodeMessage];
|
|
|
+ if (msgData == nil) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ __weak typeof(&*self) __weakself = self;
|
|
|
+ [TXLiveMessageCenter sendLiveChatMessage:msgData groupId:self.roomId priority:priority success:^{
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+
|
|
|
+ if (displayMessage) {
|
|
|
+ [__weakself appendAndDisplayMessage:messageContent];
|
|
|
+ }
|
|
|
+ callback(YES);
|
|
|
+ });
|
|
|
+ } failer:^(int code, NSString * _Nonnull msg) {
|
|
|
+ dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
+ NSLog(@"发送失败,errorcode is: %d message: %@",code, msg);
|
|
|
+ callback(NO);
|
|
|
+ });
|
|
|
+ }];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 将消息加入本地数组
|
|
|
+ */
|
|
|
+- (void)appendAndDisplayMessage:(TXLiveMessageModel *)liveMessage {
|
|
|
+ if (!liveMessage) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if ([self appendMessageModel:liveMessage]) {
|
|
|
+ NSIndexPath *indexPath =
|
|
|
+ [NSIndexPath indexPathForItem:self.conversationDataRepository.count - 1
|
|
|
+ inSection:0];
|
|
|
+ if ([self.conversationMessageTableView numberOfRowsInSection:0] !=
|
|
|
+ self.conversationDataRepository.count - 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // view刷新
|
|
|
+ [self.conversationMessageTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
|
|
|
+ if ([self isAtTheBottomOfTableView] || self.isNeedScrollToButtom) {
|
|
|
+ [self scrollToBottomAnimated:NO];
|
|
|
+ self.isNeedScrollToButtom=NO;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 如果当前会话没有这个消息id,把消息加入本地数组
|
|
|
+ */
|
|
|
+- (BOOL)appendMessageModel:(TXLiveMessageModel *)model {
|
|
|
+
|
|
|
+ if (!model) {
|
|
|
+ return NO;
|
|
|
+ }
|
|
|
+ //这里可以根据消息类型来决定是否显示,如果不希望显示直接return NO
|
|
|
+
|
|
|
+ //数量不可能无限制的大,这里限制收到消息过多时,就对显示消息数量进行限制。
|
|
|
+ //用户可以手动下拉更多消息,查看更多历史消息。
|
|
|
+ if (self.conversationDataRepository.count>500) {
|
|
|
+ [self.conversationDataRepository removeObjectAtIndex:0];
|
|
|
+ [self.conversationMessageTableView reloadData];
|
|
|
+ }
|
|
|
+
|
|
|
+ [self.conversationDataRepository addObject:model];
|
|
|
+ return YES;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)showAnimationView:(BOOL)isJoinRoom showMessag:(NSString *)message {
|
|
|
+ if (self.landScape) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (self.enterAnimationView && self.enterAnimationView.isShow) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ ANIMATIONTYPE type = isJoinRoom ? ANIMATIONTYPE_JOIN : ANIMATIONTYPE_RUSH;
|
|
|
+ self.enterAnimationView = [[LiveAnimationView alloc] initWithTitle:message animationType:type];
|
|
|
+ [self.view addSubview:self.enterAnimationView];
|
|
|
+ [self.enterAnimationView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|
|
+ make.left.right.mas_equalTo(self.view);
|
|
|
+ make.width.mas_equalTo(KPortraitWidth);
|
|
|
+ make.height.mas_equalTo(24.0f);
|
|
|
+ make.bottom.mas_equalTo(self.messageContentView.mas_top).offset(-6);
|
|
|
+ }];
|
|
|
+ [self.view bringSubviewToFront:self.enterAnimationView];
|
|
|
+
|
|
|
+ MJWeakSelf;
|
|
|
+ [self.enterAnimationView startAnimationEndCallback:^{
|
|
|
+
|
|
|
+ weakSelf.enterAnimationView.isShow = NO;
|
|
|
+ weakSelf.enterAnimationView = nil;
|
|
|
+ }];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (NSMutableArray *)remoteMemberArray {
|
|
|
+ if (!_remoteMemberArray) {
|
|
|
+ _remoteMemberArray = [NSMutableArray array];
|
|
|
+ }
|
|
|
+ return _remoteMemberArray;
|
|
|
+}
|
|
|
+
|
|
|
+- (TXLiveLoadingView *)loadingView {
|
|
|
+ if (!_loadingView) {
|
|
|
+ _loadingView = [TXLiveLoadingView shareInstance];
|
|
|
+ }
|
|
|
+ return _loadingView;
|
|
|
+}
|
|
|
+
|
|
|
+- (void)showLoading {
|
|
|
+ if (![self.videoView.subviews containsObject:self.loadingView]) {
|
|
|
+ [self.loadingView showLoadingView:self.videoView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)hideLoading {
|
|
|
+ if ([self.videoView.subviews containsObject:self.loadingView]) {
|
|
|
+ [self.loadingView hideLoadingView];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+- (void)micButtonAction:(BOOL)isMute {
|
|
|
+ if (isMute == !self.micEnable) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (_trtcCloud && self.isAudience == NO) {
|
|
|
+ self.micEnable = !isMute;
|
|
|
+ [self.trtcCloud muteLocalAudio:!self.micEnable];
|
|
|
+ [self syncMicStatus:!self.micEnable];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+- (void)syncMicStatus:(BOOL)isMute {
|
|
|
+ TXMicStatusSync *syncMic = [[TXMicStatusSync alloc] init];
|
|
|
+ syncMic.userInfo = [self getCurrentUser];
|
|
|
+ syncMic.userId = UserDefault(IM_USERID);
|
|
|
+ syncMic.muteMic = isMute;
|
|
|
+ [self sendCustomMessage:syncMic priority:V2TIM_PRIORITY_HIGH displayMessage:NO callback:^(BOOL success) {
|
|
|
+
|
|
|
+ }];
|
|
|
+ [self renderSeatView];
|
|
|
+}
|
|
|
+
|
|
|
+#pragma mark ---- 3A config
|
|
|
+- (void)setSubjectId:(NSString *)subjectId {
|
|
|
+ _subjectId = subjectId;
|
|
|
+ // 设置 3A参数
|
|
|
+ self.ANS = 0;
|
|
|
+ self.AEC = 100;
|
|
|
+ // 长号14和萨克斯5 AGC 一样
|
|
|
+ if ([subjectId isEqualToString:@"5"] || [subjectId isEqualToString:@"14"]) {
|
|
|
+ self.AGC = 45;
|
|
|
+ }
|
|
|
+ else if ([subjectId isEqualToString:@"16"]) { // 次中音号
|
|
|
+ self.AGC = 80;
|
|
|
+ }
|
|
|
+ else if ([subjectId isEqualToString:@"12"]) {
|
|
|
+ self.AGC = 50;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ self.AGC = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+/*
|
|
|
+ #pragma mark - Navigation
|
|
|
+
|
|
|
+ // In a storyboard-based application, you will often want to do a little preparation before navigation
|
|
|
+ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
|
+ // Get the new view controller using [segue destinationViewController].
|
|
|
+ // Pass the selected object to the new view controller.
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+@end
|