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

如何通过OnICECandidate交换ICE Candidates?解决<-gatherComplete耗时问题

解决pion/webrtc/v3中<-gatherComplete导致连接响应过慢的问题

当前代码依赖<-gatherComplete阻塞等待所有ICE候选收集完成,相当于禁用Trickle ICE,只能通过单条信令交换全部候选,直接导致WebRTC连接建立延迟。需要改用OnICECandidate回调实时交换ICE候选,替代阻塞等待逻辑。

修改思路

  • 删除gatherComplete相关的阻塞代码
  • 注册OnICECandidate回调,收集到ICE候选后立即发送给对端
  • 启用Trickle ICE后,无需等待所有候选收集完成即可发送Answer,大幅缩短连接准备时间

修改后的完整代码

package main

import (
    "fmt"
    "time"

    signal "github.com/Sup3r-Us3r/test-app/internal"
    "github.com/pion/mediadevices"
    "github.com/pion/mediadevices/pkg/codec/vpx"
    "github.com/pion/webrtc/v3"
    "github.com/pion/webrtc/v3/pkg/media"
    "gocv.io/x/gocv"
)

func main() {
    webcam, _ := gocv.OpenVideoCapture(0)
    defer webcam.Close()

    webRTCConfig := webrtc.Configuration{
        ICEServers: []webrtc.ICEServer{
            {
                URLs: []string{"stun:stun.l.google.com:19302"},
            },
        },
    }

    // 等待输入远端Offer
    offer := webrtc.SessionDescription{}
    signal.Decode(signal.MustReadStdin(), &offer)

    // 初始化VP8编码参数
    vp8Params, err := vpx.NewVP8Params()
    if err != nil {
        panic(err)
    }
    vp8Params.BitRate = 500_000 // 500kbps

    codecSelector := mediadevices.NewCodecSelector(
        mediadevices.WithVideoEncoders(&vp8Params),
    )

    // 配置媒体引擎与PeerConnection
    mediaEngine := webrtc.MediaEngine{}
    mediaEngine.RegisterDefaultCodecs()
    codecSelector.Populate(&mediaEngine)
    api := webrtc.NewAPI(webrtc.WithMediaEngine(&mediaEngine))
    peerConnection, _ := api.NewPeerConnection(webRTCConfig)

    // ICE连接状态变更监听
    peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
        fmt.Printf("连接状态已变更: %s \n", connectionState.String())
    })

    // 实时处理ICE候选
    peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) {
        if candidate == nil {
            fmt.Println("ICE候选收集完成")
            return
        }
        // 将候选编码后发送给对端,需根据实际信令机制调整发送逻辑
        fmt.Println("新ICE候选:", signal.Encode(candidate.ToJSON()))
    })

    // 创建本地视频Track
    videoTrack, _ := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")

    // 将Track添加到PeerConnection
    _, err = peerConnection.AddTrack(videoTrack)
    if err != nil {
        fmt.Println("\n添加Track错误: ", err)
    }

    // 设置远端SessionDescription
    err = peerConnection.SetRemoteDescription(offer)
    if err != nil {
        panic(err)
    }

    // 创建Answer
    answer, err := peerConnection.CreateAnswer(nil)
    if err != nil {
        panic(err)
    }

    // 设置本地SessionDescription,启动ICE收集
    err = peerConnection.SetLocalDescription(answer)
    if err != nil {
        panic(err)
    }

    // 立即输出Answer,无需等待ICE收集完成
    fmt.Println("Answer:", signal.Encode(*peerConnection.LocalDescription()))

    img := gocv.NewMat()
    defer img.Close()

    for {
        if ok := webcam.Read(&img); !ok {
            fmt.Printf("设备已关闭: %v\n", 0)
            return
        }

        if img.Empty() {
            continue
        }

        // 编码帧为JPEG并写入Track
        frame, _ := gocv.IMEncode(gocv.JPEGFileExt, img)
        err = videoTrack.WriteSample(media.Sample{Data: frame.GetBytes(), Duration: time.Second})
        if err != nil {
            fmt.Println("\n错误: ", err)
        }
    }
}

关键修改说明

  1. 移除了webrtc.GatheringCompletePromise和<-gatherComplete的阻塞逻辑,无需等待所有ICE候选收集完成
  2. 添加OnICECandidate回调,实时推送新收集到的ICE候选;当回调参数为nil时,表示ICE候选收集完成
  3. 设置本地描述后立即输出Answer,让对端提前启动连接流程,显著降低连接建立的等待时长

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 02:01:02