Search code examples
gowebrtc

How should exchange ICE Candidates via OnICECandidate ?


github.com/pion/webrtc/v3

code ‘<-gatherComplete’ taking too much time to respond to webrtc connection,but I do not know exchange ICE Candidates via OnICECandidate.

-Block until ICE Gathering is complete, disabling trickle ICE -we do this because we only can exchange one signaling message

  • should exchange ICE Candidates via OnICECandidate '<-gatherComplete'
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"},
            },
        },
    }

    // Wait for the offer to be pasted
    offer := webrtc.SessionDescription{}
    signal.Decode(signal.MustReadStdin(), &offer)

    // Create a new RTCPeerConnection
    vp8Params, err := vpx.NewVP8Params()
    if err != nil {
        panic(err)
    }
    vp8Params.BitRate = 500_000 // 500kbps

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

    // Configure WebRTC Connection
    mediaEngine := webrtc.MediaEngine{}
    mediaEngine.RegisterDefaultCodecs()
    codecSelector.Populate(&mediaEngine)
    api := webrtc.NewAPI(webrtc.WithMediaEngine(&mediaEngine))
    peerConnection, _ := api.NewPeerConnection(webRTCConfig)

    // Set the handler for ICE connection state
    // This will notify you when the peer has connected/disconnected
    peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
        fmt.Printf("Connection State has changed %s \n", connectionState.String())
    })

    // Create track video
    videoTrack, _ := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")

    // Add track to PeerConnection
    _, err = peerConnection.AddTrack(videoTrack)
    if err != nil {
        fmt.Println("\nERROR ADD TRACK: ", err)
    }

    // Set the remote SessionDescription
    err = peerConnection.SetRemoteDescription(offer)
    if err != nil {
        panic(err)
    }

    // Create an answer
    answer, err := peerConnection.CreateAnswer(nil)
    if err != nil {
        panic(err)
    }

    // Create channel that is blocked until ICE Gathering is complete
    gatherComplete := webrtc.GatheringCompletePromise(peerConnection)

    // Sets the LocalDescription, and starts our UDP listeners
    err = peerConnection.SetLocalDescription(answer)
    if err != nil {
        panic(err)
    }


    <-gatherComplete

    // Output the answer in base64 so we can paste it in browser
    fmt.Println(signal.Encode(*peerConnection.LocalDescription()))

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


    ticker := time.NewTicker(time.Second * 3)
    defer ticker.Stop()

    for {
        if ok := webcam.Read(&img); !ok {
            fmt.Printf("Device closed: %v\n", 0)
            return
        }

        if img.Empty() {
            continue
        }

        // Encode the frame in VP8
        frame, _ := gocv.IMEncode(gocv.JPEGFileExt, img)
        err = videoTrack.WriteSample(media.Sample{Data: frame.GetBytes(), Duration: time.Second})
        if err != nil {
            fmt.Println("\nERROR: ", err)
        }

    }
}

Solution

  • Trickle ICE is almost always better. You have some cases (like an SFU with WHIP) where non-trickle makes more sense.

    To see an example of trickle ICE with Pion check out custom-logger that creates two PeerConnections and does Trickle ICE between them.

    In your case ICE Gathering is probably taking a long time because of the STUN server. If the STUN Binding response never arrives you will have to wait for a timeout.