You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift集成Twilio Video Chat:对方停流时替换冻结图为提示

我来帮你解决这个Twilio iOS视频聊天的自定义提示问题!你的核心需求是:当对方没参与视频(比如进后台、没开摄像头)时,接收方看到自定义提示而非默认的冻结帧。你之前尝试在后台发停止推流指令没生效,大概率是轨道停止的逻辑没做对,或者接收端没监听到位。

下面分本地端和接收端两步来实现:

一、本地端:确保后台时正确停止/禁用视频轨道

iOS后台下系统会限制视频捕获,所以单纯调用stop()可能不够稳妥,推荐两种方案:

方案1:禁用轨道(可快速恢复)

如果只是临时后台,之后要快速回到通话,用isEnabled属性切换更方便:

// 进入后台时
func applicationDidEnterBackground(_ application: UIApplication) {
    guard let localVideoTrack = localParticipant?.videoTracks.first?.track else { return }
    localVideoTrack.isEnabled = false
}

// 回到前台时
func applicationWillEnterForeground(_ application: UIApplication) {
    guard let localVideoTrack = localParticipant?.videoTracks.first?.track else { return }
    localVideoTrack.isEnabled = true
}

方案2:取消发布轨道(彻底停止)

如果需要完全断开视频流,取消发布轨道更彻底,远端会直接收到“取消发布”事件:

func applicationDidEnterBackground(_ application: UIApplication) {
    guard let localVideoTrack = localParticipant?.videoTracks.first?.track else { return }
    localParticipant?.unpublishVideoTrack(localVideoTrack)
}

func applicationWillEnterForeground(_ application: UIApplication) {
    // 重新创建并发布轨道
    do {
        let cameraCapturer = try TVICameraCapturer(source: .frontCamera)
        let newLocalVideoTrack = TVIVideoTrack(capturer: cameraCapturer)
        localParticipant?.publishVideoTrack(newLocalVideoTrack)
    } catch {
        print("重新发布视频轨道失败:\(error.localizedDescription)")
    }
}

二、接收端:监听轨道状态,切换自定义提示

接收端需要监听远端轨道的发布/取消发布、启用状态变化两个事件,在轨道不可用时隐藏Twilio视频视图,显示自定义提示标签。

1. 初始化监听逻辑

首先在房间代理中监听远端参与者的连接和轨道发布:

// 存储远端视频视图,用参与者SID作为键
private var remoteVideoViews: [String: TVIVideoView] = [:]
// 你的自定义提示标签
@IBOutlet weak var customPromptLabel: UILabel!

func room(_ room: TVIRoom, participantDidConnect participant: TVIRemoteParticipant) {
    participant.delegate = self
    // 处理已存在的视频轨道
    participant.videoTracks.forEach { setupRemoteVideoTrack($0) }
}

2. 实现远端参与者代理

监听轨道的发布和取消发布:

extension YourViewController: TVIRemoteParticipantDelegate {
    // 远端发布视频轨道时
    func remoteParticipant(_ participant: TVIRemoteParticipant, publishedVideoTrack publication: TVIRemoteVideoTrackPublication) {
        setupRemoteVideoTrack(publication)
    }
    
    // 远端取消发布视频轨道时
    func remoteParticipant(_ participant: TVIRemoteParticipant, unpublishedVideoTrack publication: TVIRemoteVideoTrackPublication) {
        hideRemoteVideoShowPrompt(for: participant)
    }
    
    // 初始化远端视频视图
    private func setupRemoteVideoTrack(_ publication: TVIRemoteVideoTrackPublication) {
        guard let remoteTrack = publication.remoteTrack else { return }
        remoteTrack.delegate = self
        
        let remoteVideoView = TVIVideoView()
        remoteTrack.addRenderer(remoteVideoView)
        
        // 布局视频视图(这里用AutoLayout举例,你可以根据自己的UI调整)
        remoteVideoView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(remoteVideoView)
        NSLayoutConstraint.activate([
            remoteVideoView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            remoteVideoView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            remoteVideoView.widthAnchor.constraint(equalToConstant: 300),
            remoteVideoView.heightAnchor.constraint(equalToConstant: 400)
        ])
        
        remoteVideoViews[participant.sid] = remoteVideoView
        
        // 初始状态如果轨道未启用,直接显示提示
        if !remoteTrack.isEnabled {
            hideRemoteVideoShowPrompt(for: participant)
        }
    }
    
    // 隐藏视频视图,显示自定义提示
    private func hideRemoteVideoShowPrompt(for participant: TVIRemoteParticipant) {
        remoteVideoViews[participant.sid]?.isHidden = true
        customPromptLabel.isHidden = false
        customPromptLabel.text = "对方暂时未开启视频"
    }
}

3. 监听轨道启用状态变化

还要监听轨道的isEnabled变化,比如远端手动禁用视频时:

extension YourViewController: TVIVideoTrackDelegate {
    func videoTrack(_ track: TVIVideoTrack, didChange enabled: Bool) {
        guard let remoteParticipant = track.participant as? TVIRemoteParticipant else { return }
        if enabled {
            // 轨道启用,显示视频,隐藏提示
            remoteVideoViews[remoteParticipant.sid]?.isHidden = false
            customPromptLabel.isHidden = true
        } else {
            hideRemoteVideoShowPrompt(for: remoteParticipant)
        }
    }
}

关键注意点

  • 确保你正确持有localParticipant和remoteParticipant的引用,避免被提前释放导致事件监听失效。
  • 自定义提示标签的布局要和视频视图重叠,这样切换时用户感知更自然。
  • 如果是对方未加入房间的场景,可以在房间初始化时检查参与者数量,没有远端参与者时直接显示提示。

这样调整后,当本地进入后台(或远端禁用/停止视频),接收端就能正确显示自定义提示,而不是冻结帧啦!

内容的提问来源于stack exchange,提问作者Martin Kjellberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:13:19