// // SLSVideoTileView.m // SellyCloudSDK_Example // // Created by Caleb on 13/11/25. // Copyright © 2025 Caleb. All rights reserved. // #import "SLSVideoTileView.h" @interface SLSVideoTileView () @property (nonatomic, strong)NSString *userId; @property (nonatomic, strong) UIView *contentViewInternal; @property (nonatomic, strong) UILabel *nameLabel; @property (nonatomic, strong) UIImageView *muteIcon; @property (nonatomic, strong) UIView *videoMaskView; @property (nonatomic, strong) UILabel *videoOffLabel; @property (nonatomic, strong) UILabel *bitrate; @property (nonatomic, strong) UILabel *fps; @property (nonatomic, strong) UILabel *codec; @property (nonatomic, strong) UILabel *rtt; @end @implementation SLSVideoTileView - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { self.layer.cornerRadius = 10.f; self.clipsToBounds = YES; self.backgroundColor = [UIColor blackColor]; _contentViewInternal = [[UIView alloc] initWithFrame:self.bounds]; _contentViewInternal.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _contentViewInternal.backgroundColor = [UIColor blackColor]; [self addSubview:_contentViewInternal]; _videoMaskView = [[UIView alloc] initWithFrame:self.bounds]; _videoMaskView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; _videoMaskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6]; _videoMaskView.hidden = YES; [self addSubview:_videoMaskView]; _videoOffLabel = [[UILabel alloc] initWithFrame:CGRectZero]; _videoOffLabel.textAlignment = NSTextAlignmentCenter; _videoOffLabel.textColor = [UIColor lightGrayColor]; _videoOffLabel.font = [UIFont systemFontOfSize:14]; _videoOffLabel.text = @"视频已关闭"; [_videoMaskView addSubview:_videoOffLabel]; _nameLabel = [[UILabel alloc] initWithFrame:CGRectZero]; _nameLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium]; _nameLabel.textColor = [UIColor whiteColor]; _nameLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; _nameLabel.layer.cornerRadius = 8; _nameLabel.clipsToBounds = YES; [self addSubview:_nameLabel]; _muteIcon = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"mic.slash.fill"]]; _muteIcon.tintColor = [UIColor systemRedColor]; [self addSubview:_muteIcon]; [self addSubview:self.bitrate]; [self addSubview:self.fps]; [self addSubview:self.codec]; [self addSubview:self.rtt]; [self.bitrate mas_makeConstraints:^(MASConstraintMaker *make) { make.left.top.offset(10); }]; [self.fps mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.bitrate.mas_bottom).offset(10); make.left.offset(10); }]; [self.codec mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.fps.mas_bottom).offset(10); make.left.offset(10); }]; [self.rtt mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.codec.mas_bottom).offset(10); make.left.offset(10); }]; } return self; } - (UIView *)contentView { return self.contentViewInternal; } - (void)layoutSubviews { [super layoutSubviews]; self.contentViewInternal.frame = self.bounds; self.videoMaskView.frame = self.bounds; self.videoOffLabel.frame = CGRectInset(self.videoMaskView.bounds, 8, 8); CGFloat padding = 6.f; CGSize maxSize = CGSizeMake(self.bounds.size.width - 2*padding, 20); CGSize nameSize = [self.nameLabel sizeThatFits:maxSize]; self.nameLabel.frame = CGRectMake(padding, self.bounds.size.height - nameSize.height - padding, nameSize.width + 12, nameSize.height); self.muteIcon.frame = CGRectMake(CGRectGetMaxX(self.nameLabel.frame) + 4, CGRectGetMinY(self.nameLabel.frame) - 2, 18, 18); } - (void)updateWithParticipant:(SLSParticipant *)p { self.userId = p.uid; // 昵称 self.nameLabel.text = p.displayName.length ? p.displayName : p.uid; // 音频静音图标 self.muteIcon.hidden = !p.audioMuted; // 视频开关:只用遮罩层控制,不移除 contentView 里的子视图 BOOL videoOn = !p.videoMuted; self.videoMaskView.hidden = videoOn; // 说话者高亮 if (p.level > 0.25 && !p.audioMuted) { self.layer.borderWidth = 2.f; self.layer.borderColor = [UIColor systemGreenColor].CGColor; } else { self.layer.borderWidth = 0.f; self.layer.borderColor = nil; } [self setNeedsLayout]; } - (void)setStats:(SellyRTCP2pStats *)stats { _stats = stats; self.rtt.text = [NSString stringWithFormat:@" rtt:%ldms ",(NSInteger)stats.transportRttMs]; self.codec.text = [NSString stringWithFormat:@" %@/%@ ",stats.videoCodec,stats.audioCodec]; if ([self.userId isEqualToString:SellyRTCEngine.sharedEngine.userId]) { self.bitrate.text = [NSString stringWithFormat:@" 码率:%ld kbps ",(NSInteger)(stats.txKbps)]; self.fps.text = [NSString stringWithFormat:@" 视频:%ld fps %ldx%ld ",(NSInteger)(stats.sentFps),(NSInteger)(stats.sentWidth),(NSInteger)(stats.sentHeight)]; } else { self.bitrate.text = [NSString stringWithFormat:@" 码率:%ld kbps ",(NSInteger)(stats.rxKbps)]; self.fps.text = [NSString stringWithFormat:@" 视频:%ld fps %ldx%ld ",(NSInteger)(stats.recvFps),(NSInteger)(stats.recvWidth),(NSInteger)(stats.recvHeight)]; } } - (UILabel *)bitrate { if (!_bitrate) { _bitrate = [self createStatsLabel]; } return _bitrate; } - (UILabel *)fps { if (!_fps) { _fps = [self createStatsLabel]; } return _fps; } - (UILabel *)codec { if (!_codec) { _codec = [self createStatsLabel]; } return _codec; } - (UILabel *)rtt { if (!_rtt) { _rtt = [self createStatsLabel]; } return _rtt; } - (UILabel *)createStatsLabel { UILabel *label = [[UILabel alloc] init]; label.font = [UIFont systemFontOfSize:12]; label.textColor = [UIColor whiteColor]; label.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.2]; label.textAlignment = NSTextAlignmentLeft; label.layer.cornerRadius = 4; label.layer.masksToBounds = YES; return label; } @end