Files
webrtc/examples/broadcast/main.go
Ben Weitzman 73f8db9316 Populate MediaEngine from Offer in broadcast
Use offerer PayloadType when generating answer. Before we would use the
hardcoded values, this is invalid behavior and would cause browsers to
just ignore our video when publishing.
2020-03-28 14:50:03 -07:00

153 lines
3.8 KiB
Go

package main
import (
"fmt"
"io"
"time"
"github.com/pion/rtcp"
"github.com/pion/webrtc/v2"
"github.com/pion/webrtc/v2/examples/internal/signal"
)
const (
rtcpPLIInterval = time.Second * 3
)
func main() {
sdpChan := signal.HTTPSDPServer()
// Everything below is the Pion WebRTC API, thanks for using it ❤️.
offer := webrtc.SessionDescription{}
signal.Decode(<-sdpChan, &offer)
fmt.Println("")
// Since we are answering use PayloadTypes declared by offerer
mediaEngine := webrtc.MediaEngine{}
err := mediaEngine.PopulateFromSDP(offer)
if err != nil {
panic(err)
}
// Create the API object with the MediaEngine
api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine))
peerConnectionConfig := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
}
// Create a new RTCPeerConnection
peerConnection, err := api.NewPeerConnection(peerConnectionConfig)
if err != nil {
panic(err)
}
// Allow us to receive 1 video track
if _, err = peerConnection.AddTransceiver(webrtc.RTPCodecTypeVideo); err != nil {
panic(err)
}
localTrackChan := make(chan *webrtc.Track)
// Set a handler for when a new remote track starts, this just distributes all our packets
// to connected peers
peerConnection.OnTrack(func(remoteTrack *webrtc.Track, receiver *webrtc.RTPReceiver) {
// Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval
// This can be less wasteful by processing incoming RTCP events, then we would emit a NACK/PLI when a viewer requests it
go func() {
ticker := time.NewTicker(rtcpPLIInterval)
for range ticker.C {
if rtcpSendErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: remoteTrack.SSRC()}}); rtcpSendErr != nil {
fmt.Println(rtcpSendErr)
}
}
}()
// Create a local track, all our SFU clients will be fed via this track
localTrack, newTrackErr := peerConnection.NewTrack(remoteTrack.PayloadType(), remoteTrack.SSRC(), "video", "pion")
if newTrackErr != nil {
panic(newTrackErr)
}
localTrackChan <- localTrack
rtpBuf := make([]byte, 1400)
for {
i, readErr := remoteTrack.Read(rtpBuf)
if readErr != nil {
panic(readErr)
}
// ErrClosedPipe means we don't have any subscribers, this is ok if no peers have connected yet
if _, err = localTrack.Write(rtpBuf[:i]); err != nil && err != io.ErrClosedPipe {
panic(err)
}
}
})
// Set the remote SessionDescription
err = peerConnection.SetRemoteDescription(offer)
if err != nil {
panic(err)
}
// Create answer
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
panic(err)
}
// Sets the LocalDescription, and starts our UDP listeners
err = peerConnection.SetLocalDescription(answer)
if err != nil {
panic(err)
}
// Get the LocalDescription and take it to base64 so we can paste in browser
fmt.Println(signal.Encode(answer))
localTrack := <-localTrackChan
for {
fmt.Println("")
fmt.Println("Curl an base64 SDP to start sendonly peer connection")
recvOnlyOffer := webrtc.SessionDescription{}
signal.Decode(<-sdpChan, &recvOnlyOffer)
// Create a new PeerConnection
peerConnection, err := api.NewPeerConnection(peerConnectionConfig)
if err != nil {
panic(err)
}
_, err = peerConnection.AddTrack(localTrack)
if err != nil {
panic(err)
}
// Set the remote SessionDescription
err = peerConnection.SetRemoteDescription(recvOnlyOffer)
if err != nil {
panic(err)
}
// Create answer
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
panic(err)
}
// Sets the LocalDescription, and starts our UDP listeners
err = peerConnection.SetLocalDescription(answer)
if err != nil {
panic(err)
}
// Get the LocalDescription and take it to base64 so we can paste in browser
fmt.Println(signal.Encode(answer))
}
}