9
9
package main
10
10
11
11
import (
12
+ "errors"
12
13
"fmt"
13
14
"io"
14
15
"net/http"
15
16
16
17
"github.com/pion/interceptor"
17
18
"github.com/pion/interceptor/pkg/intervalpli"
18
- "github.com/pion/interceptor/pkg/packetdump"
19
- "github.com/pion/interceptor/pkg/report"
20
- "github.com/pion/rtp"
21
19
"github.com/pion/webrtc/v4"
22
20
)
23
21
24
22
// nolint: gochecknoglobals
25
23
var (
26
24
videoTrack * webrtc.TrackLocalStaticRTP
25
+ audioTrack * webrtc.TrackLocalStaticRTP
27
26
28
27
peerConnectionConfiguration = webrtc.Configuration {
29
28
ICEServers : []webrtc.ICEServer {
@@ -43,6 +42,11 @@ func main() {
43
42
}, "video" , "pion" ); err != nil {
44
43
panic (err )
45
44
}
45
+ if audioTrack , err = webrtc .NewTrackLocalStaticRTP (webrtc.RTPCodecCapability {
46
+ MimeType : webrtc .MimeTypeOpus ,
47
+ }, "audio" , "pion" ); err != nil {
48
+ panic (err )
49
+ }
46
50
47
51
http .Handle ("/" , http .FileServer (http .Dir ("." )))
48
52
http .HandleFunc ("/whep" , whepHandler )
@@ -53,6 +57,17 @@ func main() {
53
57
}
54
58
55
59
func whipHandler (res http.ResponseWriter , req * http.Request ) { // nolint: cyclop
60
+ fmt .Printf ("Request to %s, method = %s\n " , req .URL , req .Method )
61
+
62
+ res .Header ().Add ("Access-Control-Allow-Origin" , "*" )
63
+ res .Header ().Add ("Access-Control-Allow-Methods" , "POST" )
64
+ res .Header ().Add ("Access-Control-Allow-Headers" , "*" )
65
+ res .Header ().Add ("Access-Control-Allow-Headers" , "Authorization" )
66
+
67
+ if req .Method == http .MethodOptions {
68
+ return
69
+ }
70
+
56
71
// Read the offer from HTTP Request
57
72
offer , err := io .ReadAll (req .Body )
58
73
if err != nil {
@@ -62,8 +77,8 @@ func whipHandler(res http.ResponseWriter, req *http.Request) { // nolint: cyclop
62
77
// Create a MediaEngine object to configure the supported codec
63
78
mediaEngine := & webrtc.MediaEngine {}
64
79
65
- // Setup the codecs you want to use.
66
- // We'll only use H264 but you can also define your own
80
+ // Set up the codecs you want to use.
81
+ // We'll only use H264 and Opus but you can also define your own
67
82
if err = mediaEngine .RegisterCodec (webrtc.RTPCodecParameters {
68
83
RTPCodecCapability : webrtc.RTPCodecCapability {
69
84
MimeType : webrtc .MimeTypeH264 , ClockRate : 90000 , Channels : 0 , SDPFmtpLine : "" , RTCPFeedback : nil ,
@@ -72,8 +87,16 @@ func whipHandler(res http.ResponseWriter, req *http.Request) { // nolint: cyclop
72
87
}, webrtc .RTPCodecTypeVideo ); err != nil {
73
88
panic (err )
74
89
}
90
+ if err = mediaEngine .RegisterCodec (webrtc.RTPCodecParameters {
91
+ RTPCodecCapability : webrtc.RTPCodecCapability {
92
+ MimeType : webrtc .MimeTypeOpus , ClockRate : 48000 , Channels : 2 , SDPFmtpLine : "" , RTCPFeedback : nil ,
93
+ },
94
+ PayloadType : 97 ,
95
+ }, webrtc .RTPCodecTypeAudio ); err != nil {
96
+ panic (err )
97
+ }
75
98
76
- // Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
99
+ // Create an InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
77
100
// This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection`
78
101
// this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry
79
102
// for each PeerConnection.
@@ -103,62 +126,127 @@ func whipHandler(res http.ResponseWriter, req *http.Request) { // nolint: cyclop
103
126
panic (err )
104
127
}
105
128
106
- // Allow us to receive 1 video trac
129
+ // Allow us to receive 1 video track and 1 audio track
107
130
if _ , err = peerConnection .AddTransceiverFromKind (webrtc .RTPCodecTypeVideo ); err != nil {
108
131
panic (err )
109
132
}
133
+ if _ , err = peerConnection .AddTransceiverFromKind (webrtc .RTPCodecTypeAudio ); err != nil {
134
+ panic (err )
135
+ }
110
136
111
137
// Set a handler for when a new remote track starts, this handler saves buffers to disk as
112
138
// an ivf file, since we could have multiple video tracks we provide a counter.
113
139
// In your application this is where you would handle/process video
114
140
peerConnection .OnTrack (func (track * webrtc.TrackRemote , receiver * webrtc.RTPReceiver ) {
115
141
go func () {
116
142
for {
117
- _ , _ , rtcpErr := receiver .ReadRTCP ()
118
- if rtcpErr != nil {
119
- panic (rtcpErr )
143
+ _ , _ , err := receiver .ReadRTCP ()
144
+ if err != nil {
145
+ if errors .Is (err , io .EOF ) {
146
+ fmt .Printf ("***** EOF reading RTCP from publish peer connection\n " )
147
+
148
+ break
149
+ } else {
150
+ panic (err )
151
+ }
120
152
}
121
153
}
122
154
}()
123
- for {
124
- pkt , _ , err := track .ReadRTP ()
125
- if err != nil {
126
- panic (err )
127
- }
128
- if err = videoTrack .WriteRTP (pkt ); err != nil {
129
- panic (err )
155
+ go func () {
156
+ for {
157
+ pkt , _ , err := track .ReadRTP ()
158
+ if err != nil {
159
+ if errors .Is (err , io .EOF ) {
160
+ fmt .Printf ("***** EOF reading RTP from publish peer connection\n " )
161
+
162
+ break
163
+ } else {
164
+ panic (err )
165
+ }
166
+ }
167
+
168
+ // Strip any WHIP extensions before forwarding to WHEP
169
+ pkt .Header .Extensions = nil
170
+ pkt .Header .Extension = false
171
+
172
+ if track .Kind () == webrtc .RTPCodecTypeVideo {
173
+ if err = videoTrack .WriteRTP (pkt ); err != nil {
174
+ panic (err )
175
+ }
176
+ } else if track .Kind () == webrtc .RTPCodecTypeAudio {
177
+ if err = audioTrack .WriteRTP (pkt ); err != nil {
178
+ panic (err )
179
+ }
180
+ }
130
181
}
131
- }
182
+ }()
132
183
})
133
184
// Send answer via HTTP Response
134
185
writeAnswer (res , peerConnection , offer , "/whip" )
135
186
}
136
187
137
- func whepHandler (res http.ResponseWriter , req * http.Request ) {
188
+ func whepHandler (res http.ResponseWriter , req * http.Request ) { //nolint:cyclop
189
+ fmt .Printf ("Request to %s, method = %s\n " , req .URL , req .Method )
190
+
191
+ res .Header ().Add ("Access-Control-Allow-Origin" , "*" )
192
+ res .Header ().Add ("Access-Control-Allow-Methods" , "POST" )
193
+ res .Header ().Add ("Access-Control-Allow-Headers" , "*" )
194
+ res .Header ().Add ("Access-Control-Allow-Headers" , "Authorization" )
195
+
196
+ if req .Method == http .MethodOptions {
197
+ return
198
+ }
199
+
138
200
// Read the offer from HTTP Request
139
201
offer , err := io .ReadAll (req .Body )
140
202
if err != nil {
141
203
panic (err )
142
204
}
143
205
144
- interceptorRegistry := & interceptor.Registry {}
145
- packetDump , err := packetdump .NewSenderInterceptor (
146
- // filter out all RTP packets, only RTCP packets will be logged
147
- packetdump .RTPFilter (func (_ * rtp.Packet ) bool {
148
- return false
149
- }),
150
- )
151
- if err != nil {
206
+ // Create a MediaEngine object to configure the supported codec
207
+ media := & webrtc.MediaEngine {}
208
+
209
+ // Set up the codecs you want to use.
210
+ if err = media .RegisterCodec (webrtc.RTPCodecParameters {
211
+ RTPCodecCapability : webrtc.RTPCodecCapability {
212
+ MimeType : webrtc .MimeTypeH264 ,
213
+ ClockRate : 90000 ,
214
+ Channels : 0 ,
215
+ SDPFmtpLine : "" ,
216
+ RTCPFeedback : nil ,
217
+ },
218
+ PayloadType : 96 ,
219
+ }, webrtc .RTPCodecTypeVideo ); err != nil {
152
220
panic (err )
153
221
}
154
- interceptorRegistry .Add (packetDump )
155
- senderInterceptor , err := report .NewSenderInterceptor ()
156
- if err != nil {
222
+ if err = media .RegisterCodec (webrtc.RTPCodecParameters {
223
+ RTPCodecCapability : webrtc.RTPCodecCapability {
224
+ MimeType : webrtc .MimeTypeOpus ,
225
+ ClockRate : 48000 ,
226
+ Channels : 2 ,
227
+ SDPFmtpLine : "" ,
228
+ RTCPFeedback : nil ,
229
+ },
230
+ PayloadType : 97 ,
231
+ }, webrtc .RTPCodecTypeAudio ); err != nil {
232
+ panic (err )
233
+ }
234
+
235
+ // Create an InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
236
+ ir := & interceptor.Registry {}
237
+
238
+ // Use the default set of Interceptors
239
+ if err = webrtc .RegisterDefaultInterceptors (media , ir ); err != nil {
240
+ panic (err )
241
+ }
242
+
243
+ // We want TWCC in case the subscriber supports it
244
+ if err = webrtc .ConfigureTWCCHeaderExtensionSender (media , ir ); err != nil {
157
245
panic (err )
158
246
}
159
- interceptorRegistry .Add (senderInterceptor )
160
247
161
- api := webrtc .NewAPI (webrtc .WithInterceptorRegistry (interceptorRegistry ))
248
+ // Create the API object with the MediaEngine
249
+ api := webrtc .NewAPI (webrtc .WithMediaEngine (media ), webrtc .WithInterceptorRegistry (ir ))
162
250
163
251
// Create a new RTCPeerConnection
164
252
peerConnection , err := api .NewPeerConnection (peerConnectionConfiguration )
@@ -167,18 +255,33 @@ func whepHandler(res http.ResponseWriter, req *http.Request) {
167
255
}
168
256
169
257
// Add Video Track that is being written to from WHIP Session
170
- rtpSender , err := peerConnection .AddTrack (videoTrack )
258
+ rtpSenderVideo , err := peerConnection .AddTrack (videoTrack )
259
+ if err != nil {
260
+ panic (err )
261
+ }
262
+ // Add Audio Track that is being written to from WHIP Session
263
+ rtpSenderAudio , err := peerConnection .AddTrack (audioTrack )
171
264
if err != nil {
172
265
panic (err )
173
266
}
174
267
175
- // Read incoming RTCP packets
268
+ // Read incoming RTCP packets for video
176
269
// Before these packets are returned they are processed by interceptors. For things
177
270
// like NACK this needs to be called.
178
271
go func () {
179
272
rtcpBuf := make ([]byte , 1500 )
180
273
for {
181
- if _ , _ , rtcpErr := rtpSender .Read (rtcpBuf ); rtcpErr != nil {
274
+ if _ , _ , rtcpErr := rtpSenderVideo .Read (rtcpBuf ); rtcpErr != nil {
275
+ return
276
+ }
277
+ }
278
+ }()
279
+
280
+ // Read incoming RTCP packets for audio
281
+ go func () {
282
+ rtcpBuf := make ([]byte , 1500 )
283
+ for {
284
+ if _ , _ , rtcpErr := rtpSenderAudio .Read (rtcpBuf ); rtcpErr != nil {
182
285
return
183
286
}
184
287
}
0 commit comments