128 lines
2.8 KiB
Go
128 lines
2.8 KiB
Go
package webrtc
|
|
|
|
import (
|
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
|
"github.com/AlexxIT/go2rtc/pkg/streamer"
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
type Conn struct {
|
|
streamer.Element
|
|
|
|
UserAgent string
|
|
|
|
pc *webrtc.PeerConnection
|
|
|
|
medias []*streamer.Media
|
|
tracks []*streamer.Track
|
|
|
|
receive int
|
|
send int
|
|
|
|
offer string
|
|
remote string
|
|
closed core.Waiter
|
|
}
|
|
|
|
func NewConn(pc *webrtc.PeerConnection) *Conn {
|
|
c := &Conn{pc: pc}
|
|
|
|
pc.OnICECandidate(func(candidate *webrtc.ICECandidate) {
|
|
// last candidate will be empty
|
|
if candidate != nil {
|
|
c.Fire(candidate)
|
|
}
|
|
})
|
|
|
|
pc.OnDataChannel(func(channel *webrtc.DataChannel) {
|
|
c.Fire(channel)
|
|
})
|
|
|
|
pc.OnICEConnectionStateChange(func(state webrtc.ICEConnectionState) {
|
|
if state != webrtc.ICEConnectionStateChecking {
|
|
return
|
|
}
|
|
pc.SCTP().Transport().ICETransport().OnSelectedCandidatePairChange(
|
|
func(pair *webrtc.ICECandidatePair) {
|
|
c.remote = pair.Remote.String()
|
|
},
|
|
)
|
|
})
|
|
|
|
pc.OnTrack(func(remote *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
|
|
track := c.getTrack(remote)
|
|
if track == nil {
|
|
println("ERROR: webrtc: can't find track")
|
|
return
|
|
}
|
|
|
|
for {
|
|
packet, _, err := remote.ReadRTP()
|
|
if err != nil {
|
|
return
|
|
}
|
|
if len(packet.Payload) == 0 {
|
|
continue
|
|
}
|
|
c.receive += len(packet.Payload)
|
|
_ = track.WriteRTP(packet)
|
|
}
|
|
})
|
|
|
|
// OK connection:
|
|
// 15:01:46 ICE connection state changed: checking
|
|
// 15:01:46 peer connection state changed: connected
|
|
// 15:01:54 peer connection state changed: disconnected
|
|
// 15:02:20 peer connection state changed: failed
|
|
//
|
|
// Fail connection:
|
|
// 14:53:08 ICE connection state changed: checking
|
|
// 14:53:39 peer connection state changed: failed
|
|
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
|
|
c.Fire(state)
|
|
|
|
switch state {
|
|
case webrtc.PeerConnectionStateDisconnected, webrtc.PeerConnectionStateFailed, webrtc.PeerConnectionStateClosed:
|
|
// disconnect event comes earlier, than failed
|
|
// but it comes only for success connections
|
|
_ = c.Close()
|
|
}
|
|
})
|
|
|
|
return c
|
|
}
|
|
|
|
func (c *Conn) Close() error {
|
|
c.closed.Done()
|
|
return c.pc.Close()
|
|
}
|
|
|
|
func (c *Conn) AddCandidate(candidate string) error {
|
|
// pion uses only candidate value from json/object candidate struct
|
|
return c.pc.AddICECandidate(webrtc.ICECandidateInit{Candidate: candidate})
|
|
}
|
|
|
|
func (c *Conn) getTrack(remote *webrtc.TrackRemote) *streamer.Track {
|
|
payloadType := uint8(remote.PayloadType())
|
|
|
|
// search existing track (two way audio)
|
|
for _, track := range c.tracks {
|
|
if track.Codec.PayloadType == payloadType {
|
|
return track
|
|
}
|
|
}
|
|
|
|
// create new track (incoming WebRTC WHIP)
|
|
for _, media := range c.medias {
|
|
for _, codec := range media.Codecs {
|
|
if codec.PayloadType == payloadType {
|
|
track := streamer.NewTrack(media, codec)
|
|
c.tracks = append(c.tracks, track)
|
|
return track
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|