forked from pion/webrtc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
166 lines (139 loc) · 4.69 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// +build !js
package main
import (
"encoding/json"
"fmt"
"math/rand"
"net/http"
"os"
"time"
"github.com/pion/randutil"
"github.com/pion/webrtc/v3"
"github.com/pion/webrtc/v3/pkg/media"
"github.com/pion/webrtc/v3/pkg/media/ivfreader"
)
var peerConnection *webrtc.PeerConnection //nolint
// doSignaling exchanges all state of the local PeerConnection and is called
// every time a video is added or removed
func doSignaling(w http.ResponseWriter, r *http.Request) {
var offer webrtc.SessionDescription
if err := json.NewDecoder(r.Body).Decode(&offer); err != nil {
panic(err)
}
if err := peerConnection.SetRemoteDescription(offer); err != nil {
panic(err)
}
// Create channel that is blocked until ICE Gathering is complete
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
answer, err := peerConnection.CreateAnswer(nil)
if err != nil {
panic(err)
} else 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
response, err := json.Marshal(*peerConnection.LocalDescription())
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write(response); err != nil {
panic(err)
}
}
// Add a single video track
func createPeerConnection(w http.ResponseWriter, r *http.Request) {
if peerConnection.ConnectionState() != webrtc.PeerConnectionStateNew {
panic(fmt.Sprintf("createPeerConnection called in non-new state (%s)", peerConnection.ConnectionState()))
}
doSignaling(w, r)
fmt.Println("PeerConnection has been created")
}
// Add a single video track
func addVideo(w http.ResponseWriter, r *http.Request) {
videoTrack, err := webrtc.NewTrackLocalStaticSample(
webrtc.RTPCodecCapability{MimeType: "video/vp8"},
fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()),
fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()),
)
if err != nil {
panic(err)
}
rtpSender, err := peerConnection.AddTrack(videoTrack)
if err != nil {
panic(err)
}
// Read incoming RTCP packets
// Before these packets are returned they are processed by interceptors. For things
// like NACK this needs to be called.
go func() {
rtcpBuf := make([]byte, 1500)
for {
if _, _, rtcpErr := rtpSender.Read(rtcpBuf); rtcpErr != nil {
return
}
}
}()
go writeVideoToTrack(videoTrack)
doSignaling(w, r)
fmt.Println("Video track has been added")
}
// Remove a single sender
func removeVideo(w http.ResponseWriter, r *http.Request) {
if senders := peerConnection.GetSenders(); len(senders) != 0 {
if err := peerConnection.RemoveTrack(senders[0]); err != nil {
panic(err)
}
}
doSignaling(w, r)
fmt.Println("Video track has been removed")
}
func main() {
rand.Seed(time.Now().UTC().UnixNano())
var err error
if peerConnection, err = webrtc.NewPeerConnection(webrtc.Configuration{}); err != nil {
panic(err)
}
// 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("ICE Connection State has changed: %s\n", connectionState.String())
})
http.Handle("/", http.FileServer(http.Dir(".")))
http.HandleFunc("/createPeerConnection", createPeerConnection)
http.HandleFunc("/addVideo", addVideo)
http.HandleFunc("/removeVideo", removeVideo)
fmt.Println("Open http://localhost:8080 to access this demo")
panic(http.ListenAndServe(":8080", nil))
}
// Read a video file from disk and write it to a webrtc.Track
// When the video has been completely read this exits without error
func writeVideoToTrack(t *webrtc.TrackLocalStaticSample) {
// Open a IVF file and start reading using our IVFReader
file, err := os.Open("output.ivf")
if err != nil {
panic(err)
}
ivf, header, err := ivfreader.NewWith(file)
if err != nil {
panic(err)
}
// Send our video file frame at a time. Pace our sending so we send it at the same speed it should be played back as.
// This isn't required since the video is timestamped, but we will such much higher loss if we send all at once.
sleepTime := time.Millisecond * time.Duration((float32(header.TimebaseNumerator)/float32(header.TimebaseDenominator))*1000)
for {
frame, _, err := ivf.ParseNextFrame()
if err != nil {
fmt.Printf("Finish writing video track: %s ", err)
return
}
time.Sleep(sleepTime)
if err = t.WriteSample(media.Sample{Data: frame, Duration: time.Second}); err != nil {
fmt.Printf("Finish writing video track: %s ", err)
return
}
}
}