Skip to content

Instantly share code, notes, and snippets.

@Sean-Der
Created November 5, 2020 07:25
Show Gist options
  • Save Sean-Der/69e2f5a13557de7bdadb2c7a9b0adc73 to your computer and use it in GitHub Desktop.
Save Sean-Der/69e2f5a13557de7bdadb2c7a9b0adc73 to your computer and use it in GitHub Desktop.
package main
import (
"context"
"fmt"
"io"
"os"
"time"
"github.com/pion/randutil"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/examples/internal/signal"
"github.com/pion/webrtc/v3/pkg/media"
"github.com/pion/webrtc/v3/pkg/media/h264reader"
)
const (
videoFileName = "doom.h264"
)
func main() {
// Assert that we have an audio or video file
_, err := os.Stat(videoFileName)
haveVideoFile := !os.IsNotExist(err)
// Wait for the offer to be pasted
offer := webrtc.SessionDescription{}
signal.Decode(signal.MustReadStdin(), &offer)
// We make our own mediaEngine so we can place the sender's codecs in it. This because we must use the
// dynamic media type from the sender in our answer. This is not required if we are the offerer
mediaEngine := webrtc.MediaEngine{}
if err = mediaEngine.PopulateFromSDP(offer); err != nil {
panic(err)
}
// Create a new RTCPeerConnection
api := webrtc.NewAPI(webrtc.WithMediaEngine(mediaEngine))
peerConnection, err := api.NewPeerConnection(webrtc.Configuration{
ICEServers: []webrtc.ICEServer{
{
URLs: []string{"stun:stun.l.google.com:19302"},
},
},
})
if err != nil {
panic(err)
}
iceConnectedCtx, iceConnectedCtxCancel := context.WithCancel(context.Background())
if haveVideoFile {
// Create a video track
videoTrack, addTrackErr := peerConnection.NewTrack(getPayloadType(mediaEngine, webrtc.RTPCodecTypeVideo, "H264"), randutil.NewMathRandomGenerator().Uint32(), "video", "pion")
if addTrackErr != nil {
panic(addTrackErr)
}
if _, addTrackErr = peerConnection.AddTrack(videoTrack); addTrackErr != nil {
panic(addTrackErr)
}
go func() {
file, h264Err := os.Open(videoFileName)
if h264Err != nil {
panic(h264Err)
}
h264, h264Err := h264reader.NewWith(file)
if h264Err != nil {
panic(h264Err)
}
// Wait for connection established
<-iceConnectedCtx.Done()
spsAndPpsCache := []byte{}
for {
nal, h264Err := h264.NextNAL()
if h264Err == io.EOF {
fmt.Printf("All video frames parsed and sent")
os.Exit(0)
}
if h264Err != nil {
panic(h264Err)
}
time.Sleep(time.Millisecond * 33)
nal.Data = append([]byte{0x00, 0x00, 0x00, 0x01}, nal.Data...)
if nal.UnitType == h264reader.NalUnitTypeSPS || nal.UnitType == h264reader.NalUnitTypePPS {
spsAndPpsCache = append(spsAndPpsCache, nal.Data...)
continue
} else if nal.UnitType == h264reader.NalUnitTypeCodedSliceIdr {
nal.Data = append(spsAndPpsCache, nal.Data...)
spsAndPpsCache = []byte{}
}
if h264Err = videoTrack.WriteSample(media.Sample{Data: nal.Data, Samples: 90000}); h264Err != nil {
panic(h264Err)
}
}
}()
}
// 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())
if connectionState == webrtc.ICEConnectionStateConnected {
iceConnectedCtxCancel()
}
})
// Set the remote SessionDescription
if err = peerConnection.SetRemoteDescription(offer); err != nil {
panic(err)
}
// Create 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
if err = peerConnection.SetLocalDescription(answer); err != nil {
panic(err)
}
// Block until ICE Gathering is complete, disabling trickle ICE
// we do this because we only can exchange one signaling message
// in a production application you should exchange ICE Candidates via OnICECandidate
<-gatherComplete
// Output the answer in base64 so we can paste it in browser
fmt.Println(signal.Encode(*peerConnection.LocalDescription()))
// Block forever
select {}
}
// Search for Codec PayloadType
//
// Since we are answering we need to match the remote PayloadType
func getPayloadType(m webrtc.MediaEngine, codecType webrtc.RTPCodecType, codecName string) uint8 {
for _, codec := range m.GetCodecsByKind(codecType) {
if codec.Name == codecName {
return codec.PayloadType
}
}
panic(fmt.Sprintf("Remote peer does not support %s", codecName))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment