-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Closed as not planned
Closed as not planned
Copy link
Milestone
Description
Your environment.
- Version: github.com/pion/webrtc/v3 v3.1.11
- Browser: NOT RELATED
- Other Information - ion-sfu c8cea05fa60612ae819594afc9655573d8958a5d
What did you do?
- follow here broadcasting-ion-sfu
package main import ( "bytes" "encoding/json" "flag" "fmt" "io" "log" "net/url" "github.com/google/uuid" "github.com/gorilla/websocket" "github.com/pion/mediadevices" "github.com/pion/mediadevices/pkg/codec/vpx" "github.com/pion/mediadevices/pkg/frame" "github.com/pion/mediadevices/pkg/prop" "github.com/pion/webrtc/v3" "github.com/sourcegraph/jsonrpc2" // Note: If you don't have a camera or microphone or your adapters are not supported, // you can always swap your adapters with our dummy adapters below. // _ "github.com/pion/mediadevices/pkg/driver/videotest" // _ "github.com/pion/mediadevices/pkg/driver/audiotest" _ "github.com/pion/mediadevices/pkg/driver/camera" // This is required to register camera adapter _ "github.com/pion/mediadevices/pkg/driver/microphone" // This is required to register microphone adapter ) type Candidate struct { Target int `json:"target"` Candidate *webrtc.ICECandidate `json:candidate` } type ResponseCandidate struct { Target int `json:"target"` Candidate *webrtc.ICECandidateInit `json:candidate` } // SendOffer object to send to the sfu over Websockets type SendOffer struct { SID string `json:sid` Offer *webrtc.SessionDescription `json:offer` } // SendAnswer object to send to the sfu over Websockets type SendAnswer struct { SID string `json:sid` Answer *webrtc.SessionDescription `json:answer` } // TrickleResponse received from the sfu server type TrickleResponse struct { Params ResponseCandidate `json:params` Method string `json:method` } // Response received from the sfu over Websockets type Response struct { Params *webrtc.SessionDescription `json:params` Result *webrtc.SessionDescription `json:result` Method string `json:method` Id uint64 `json:id` } var peerConnection *webrtc.PeerConnection var connectionID uint64 var remoteDescription *webrtc.SessionDescription var addr string func main() { flag.StringVar(&addr, "a", "${HOST}:7000", "address to use") flag.Parse() u := url.URL{Scheme: "ws", Host: addr, Path: "/ws"} log.Printf("connecting to %s", u.String()) c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) if err != nil { log.Fatal("dial:", err) } defer c.Close() config := webrtc.Configuration{ ICEServers: []webrtc.ICEServer{ { URLs: []string{"stun:stun.l.google.com:19302"}, }, /*{ URLs: []string{"turn:TURN_IP:3478"}, Username: "username", Credential: "password", },*/ }, SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, } // Create a new RTCPeerConnection mediaEngine := webrtc.MediaEngine{} vpxParams, err := vpx.NewVP8Params() if err != nil { panic(err) } vpxParams.BitRate = 500_000 // 500kbps codecSelector := mediadevices.NewCodecSelector( mediadevices.WithVideoEncoders(&vpxParams), ) codecSelector.Populate(&mediaEngine) api := webrtc.NewAPI(webrtc.WithMediaEngine(&mediaEngine)) peerConnection, err = api.NewPeerConnection(config) if err != nil { panic(err) } // Read incoming Websocket messages done := make(chan struct{}) go readMessage(c, done) fmt.Println(mediadevices.EnumerateDevices()) s, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{ Video: func(c *mediadevices.MediaTrackConstraints) { c.FrameFormat = prop.FrameFormat(frame.FormatYUY2) c.Width = prop.Int(640) c.Height = prop.Int(480) }, Codec: codecSelector, }) if err != nil { panic(err) } for _, track := range s.GetTracks() { track.OnEnded(func(err error) { fmt.Printf("Track (ID: %s) ended with error: %v\n", track.ID(), err) }) _, err = peerConnection.AddTransceiverFromTrack(track, webrtc.RtpTransceiverInit{ Direction: webrtc.RTPTransceiverDirectionSendonly, }, ) if err != nil { panic(err) } } // Creating WebRTC offer offer, err := peerConnection.CreateOffer(nil) // Set the remote SessionDescription err = peerConnection.SetLocalDescription(offer) if err != nil { panic(err) } // Handling OnICECandidate event peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) { if candidate != nil { candidateJSON, err := json.Marshal(&Candidate{ Candidate: candidate, Target: 0, }) params := (*json.RawMessage)(&candidateJSON) if err != nil { log.Fatal(err) } message := &jsonrpc2.Request{ Method: "trickle", Params: params, } reqBodyBytes := new(bytes.Buffer) json.NewEncoder(reqBodyBytes).Encode(message) messageBytes := reqBodyBytes.Bytes() c.WriteMessage(websocket.TextMessage, messageBytes) } }) peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { fmt.Printf("Connection State has changed to %s \n", connectionState.String()) }) offerJSON, err := json.Marshal(&SendOffer{ Offer: peerConnection.LocalDescription(), SID: "test room", }) params := (*json.RawMessage)(&offerJSON) connectionUUID := uuid.New() connectionID = uint64(connectionUUID.ID()) offerMessage := &jsonrpc2.Request{ Method: "join", Params: params, ID: jsonrpc2.ID{ IsString: false, Str: "", Num: connectionID, }, } reqBodyBytes := new(bytes.Buffer) json.NewEncoder(reqBodyBytes).Encode(offerMessage) messageBytes := reqBodyBytes.Bytes() c.WriteMessage(websocket.TextMessage, messageBytes) <-done } func readMessage(connection *websocket.Conn, done chan struct{}) { defer close(done) for { _, message, err := connection.ReadMessage() if err != nil || err == io.EOF { log.Fatal("Error reading: ", err) break } fmt.Printf("recv: %s", message) var response Response json.Unmarshal(message, &response) if response.Id == connectionID { result := *response.Result remoteDescription = response.Result if err := peerConnection.SetRemoteDescription(result); err != nil { log.Fatal(err) } } else if response.Id != 0 && response.Method == "offer" { peerConnection.SetRemoteDescription(*response.Params) answer, err := peerConnection.CreateAnswer(nil) if err != nil { log.Fatal(err) } peerConnection.SetLocalDescription(answer) connectionUUID := uuid.New() connectionID = uint64(connectionUUID.ID()) offerJSON, err := json.Marshal(&SendAnswer{ Answer: peerConnection.LocalDescription(), SID: "test room", }) params := (*json.RawMessage)(&offerJSON) answerMessage := &jsonrpc2.Request{ Method: "answer", Params: params, ID: jsonrpc2.ID{ IsString: false, Str: "", Num: connectionID, }, } reqBodyBytes := new(bytes.Buffer) json.NewEncoder(reqBodyBytes).Encode(answerMessage) messageBytes := reqBodyBytes.Bytes() connection.WriteMessage(websocket.TextMessage, messageBytes) } else if response.Method == "trickle" { var trickleResponse TrickleResponse if err := json.Unmarshal(message, &trickleResponse); err != nil { log.Fatal(err) } err := peerConnection.AddICECandidate(*trickleResponse.Params.Candidate) if err != nil { log.Fatal(err) } } } }
go mod init WebRTCCamera
go mod tidy
module WebRTCCamera go 1.17 require ( github.com/google/uuid v1.3.0 github.com/gorilla/websocket v1.4.2 github.com/pion/logging v0.2.2 github.com/pion/mediadevices v0.3.1 github.com/pion/webrtc/v3 v3.1.11 github.com/sourcegraph/jsonrpc2 v0.1.0 ) require ( github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539 // indirect github.com/gen2brain/malgo v0.10.35 // indirect github.com/pion/datachannel v1.5.2 // indirect github.com/pion/dtls/v2 v2.0.10 // indirect github.com/pion/ice/v2 v2.1.14 // indirect github.com/pion/interceptor v0.1.2 // indirect github.com/pion/mdns v0.0.5 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.9 // indirect github.com/pion/rtp v1.7.4 // indirect github.com/pion/sctp v1.8.0 // indirect github.com/pion/sdp/v3 v3.0.4 // indirect github.com/pion/srtp/v2 v2.0.5 // indirect github.com/pion/stun v0.3.5 // indirect github.com/pion/transport v0.12.3 // indirect github.com/pion/turn/v2 v2.0.5 // indirect github.com/pion/udp v0.1.1 // indirect golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect )
- build and run
- got disconnected around 60 seconds later
What did you expect?
- do not disconnect, keep connected
What happened?
ICE
connection got disconnected- It is not sending
STUN
message any more, with help of wireshark - Supposed to send
STUN
message here https://github.com/pion/ice/blob/715f2083100814b24cf1ec213d71178d64d65777/agent.go#L694 - BUT,
selectedPair.Local.LastSent()
is not long ago enough, should be longer than 2 seconds - Why
LastSent
is keep being not long ago, because it is keep updating at here https://github.com/pion/ice/blob/715f2083100814b24cf1ec213d71178d64d65777/candidate_base.go#L306 - here siblings https://github.com/pion/ice/blob/715f2083100814b24cf1ec213d71178d64d65777/candidatepair.go#L89 and https://github.com/pion/ice/blob/715f2083100814b24cf1ec213d71178d64d65777/candidatepair.go#L94 they both write message to remote
- the problem is
CandidatePair.Write
is doing too much,candidateBase.lastSent
is keep updating totime.Now()
then https://github.com/pion/ice/blob/715f2083100814b24cf1ec213d71178d64d65777/agent.go#L701 has no chance to sendSTUN
heartbeat message to keep alive, then got disconnected after timeout - Is there anything I did wrong or how to fix it?
Metadata
Metadata
Assignees
Labels
No labels