Skip to content

Commit 8a73a30

Browse files
authored
Fix renegotiation issue where codecs where not registered (#205)
1 parent 1a066aa commit 8a73a30

9 files changed

Lines changed: 177 additions & 21 deletions

File tree

cmd/server/grpc/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func (s *server) Signal(stream pb.SFU_SignalServer) error {
186186
SDP: string(payload.Join.Offer.Sdp),
187187
}
188188

189-
me := webrtc.MediaEngine{}
189+
me := sfu.MediaEngine{}
190190
err = me.PopulateFromSDP(offer)
191191
if err != nil {
192192
log.Errorf("join error: %v", err)

cmd/server/json-rpc/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func (r *RPC) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Req
161161
break
162162
}
163163

164-
me := webrtc.MediaEngine{}
164+
me := sfu.MediaEngine{}
165165
err = me.PopulateFromSDP(join.Offer)
166166
if err != nil {
167167
log.Errorf("connect: error creating peer: %v", err)

examples/custom-signaling/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func (r *RPC) Handle(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Req
161161
break
162162
}
163163

164-
me := webrtc.MediaEngine{}
164+
me := sfu.MediaEngine{}
165165
err = me.PopulateFromSDP(join.Offer)
166166
if err != nil {
167167
log.Errorf("connect: error creating peer: %v", err)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/pion/mediadevices v0.0.0-20200929170321-f3e3dc9589ca
1111
github.com/pion/rtcp v1.2.4
1212
github.com/pion/rtp v1.6.1
13+
github.com/pion/sdp/v2 v2.4.0
1314
github.com/pion/sdp/v3 v3.0.1
1415
github.com/pion/webrtc/v2 v2.2.26
1516
github.com/pion/webrtc/v3 v3.0.0-beta.5

pkg/mediaengine.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package sfu
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/pion/sdp/v2"
9+
"github.com/pion/webrtc/v3"
10+
)
11+
12+
const (
13+
mediaNameAudio = "audio"
14+
mediaNameVideo = "video"
15+
)
16+
17+
var (
18+
rtcpfb = []webrtc.RTCPFeedback{
19+
{Type: webrtc.TypeRTCPFBCCM},
20+
{Type: webrtc.TypeRTCPFBNACK},
21+
{Type: "nack pli"},
22+
}
23+
)
24+
25+
// MediaEngine handles stream codecs
26+
type MediaEngine struct {
27+
webrtc.MediaEngine
28+
}
29+
30+
// PopulateFromSDP finds all codecs in sd and adds them to m, using the dynamic
31+
// payload types and parameters from sd.
32+
// PopulateFromSDP is intended for use when answering a request.
33+
// The offerer sets the PayloadTypes for the connection.
34+
// PopulateFromSDP allows an answerer to properly match the PayloadTypes from the offerer.
35+
// A MediaEngine populated by PopulateFromSDP should be used only for a single session.
36+
func (e *MediaEngine) PopulateFromSDP(sd webrtc.SessionDescription) error {
37+
sdp := sdp.SessionDescription{}
38+
if err := sdp.Unmarshal([]byte(sd.SDP)); err != nil {
39+
return err
40+
}
41+
42+
for _, md := range sdp.MediaDescriptions {
43+
if md.MediaName.Media != mediaNameAudio && md.MediaName.Media != mediaNameVideo {
44+
continue
45+
}
46+
47+
for _, format := range md.MediaName.Formats {
48+
pt, err := strconv.Atoi(format)
49+
if err != nil {
50+
return fmt.Errorf("format parse error")
51+
}
52+
53+
payloadType := uint8(pt)
54+
payloadCodec, err := sdp.GetCodecForPayloadType(payloadType)
55+
if err != nil {
56+
return fmt.Errorf("could not find codec for payload type %d", payloadType)
57+
}
58+
59+
var codec *webrtc.RTPCodec
60+
switch {
61+
case strings.EqualFold(payloadCodec.Name, webrtc.Opus):
62+
codec = webrtc.NewRTPOpusCodec(payloadType, payloadCodec.ClockRate)
63+
case strings.EqualFold(payloadCodec.Name, webrtc.VP8):
64+
codec = webrtc.NewRTPVP8CodecExt(payloadType, payloadCodec.ClockRate, rtcpfb, payloadCodec.Fmtp)
65+
case strings.EqualFold(payloadCodec.Name, webrtc.VP9):
66+
codec = webrtc.NewRTPVP9CodecExt(payloadType, payloadCodec.ClockRate, rtcpfb, payloadCodec.Fmtp)
67+
case strings.EqualFold(payloadCodec.Name, webrtc.H264):
68+
codec = webrtc.NewRTPH264CodecExt(payloadType, payloadCodec.ClockRate, rtcpfb, payloadCodec.Fmtp)
69+
default:
70+
// ignoring other codecs
71+
continue
72+
}
73+
74+
e.RegisterCodec(codec)
75+
}
76+
}
77+
78+
// Use defaults for codecs not provided in sdp
79+
if len(e.GetCodecsByName(webrtc.Opus)) == 0 {
80+
codec := webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000)
81+
e.RegisterCodec(codec)
82+
}
83+
84+
if len(e.GetCodecsByName(webrtc.VP8)) == 0 {
85+
codec := webrtc.NewRTPVP8CodecExt(webrtc.DefaultPayloadTypeVP8, 90000, rtcpfb, "")
86+
e.RegisterCodec(codec)
87+
}
88+
89+
if len(e.GetCodecsByName(webrtc.VP9)) == 0 {
90+
codec := webrtc.NewRTPVP9CodecExt(webrtc.DefaultPayloadTypeVP9, 90000, rtcpfb, "")
91+
e.RegisterCodec(codec)
92+
}
93+
94+
if len(e.GetCodecsByName(webrtc.H264)) == 0 {
95+
codec := webrtc.NewRTPH264CodecExt(webrtc.DefaultPayloadTypeH264, 90000, rtcpfb, "")
96+
e.RegisterCodec(codec)
97+
}
98+
99+
return nil
100+
}

pkg/mediaengine_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package sfu
2+
3+
import (
4+
"testing"
5+
6+
"github.com/pion/webrtc/v3"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
const sdpValue = `v=0
11+
o=- 884433216 1576829404 IN IP4 0.0.0.0
12+
s=-
13+
t=0 0
14+
a=fingerprint:sha-256 1D:6B:6D:18:95:41:F9:BC:E4:AC:25:6A:26:A3:C8:09:D2:8C:EE:1B:7D:54:53:33:F7:E3:2C:0D:FE:7A:9D:6B
15+
a=group:BUNDLE 0 1 2
16+
m=audio 9 UDP/TLS/RTP/SAVPF 0 8 111 9
17+
c=IN IP4 0.0.0.0
18+
a=mid:0
19+
a=rtpmap:0 PCMU/8000
20+
a=rtpmap:8 PCMA/8000
21+
a=rtpmap:111 opus/48000/2
22+
a=fmtp:111 minptime=10;useinbandfec=1
23+
a=rtpmap:9 G722/8000
24+
a=ssrc:1823804162 cname:pion1
25+
a=ssrc:1823804162 msid:pion1 audio
26+
a=ssrc:1823804162 mslabel:pion1
27+
a=ssrc:1823804162 label:audio
28+
a=msid:pion1 audio
29+
m=video 9 UDP/TLS/RTP/SAVPF 105 115 135
30+
c=IN IP4 0.0.0.0
31+
a=mid:1
32+
a=rtpmap:105 VP8/90000
33+
a=rtpmap:115 H264/90000
34+
a=fmtp:115 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
35+
a=rtpmap:135 VP9/90000
36+
a=ssrc:2949882636 cname:pion2
37+
a=ssrc:2949882636 msid:pion2 video
38+
a=ssrc:2949882636 mslabel:pion2
39+
a=ssrc:2949882636 label:video
40+
a=msid:pion2 video
41+
m=application 9 DTLS/SCTP 5000
42+
c=IN IP4 0.0.0.0
43+
a=mid:2
44+
a=sctpmap:5000 webrtc-datachannel 1024
45+
`
46+
47+
func TestPopulateFromSDP(t *testing.T) {
48+
m := MediaEngine{}
49+
assertCodecWithPayloadType := func(name string, payloadType uint8) {
50+
for _, c := range m.GetCodecsByName(name) {
51+
if c.PayloadType == payloadType && c.Name == name {
52+
return
53+
}
54+
}
55+
t.Fatalf("Failed to find codec(%s) with PayloadType(%d)", name, payloadType)
56+
}
57+
58+
m.RegisterDefaultCodecs()
59+
assert.NoError(t, m.PopulateFromSDP(webrtc.SessionDescription{SDP: sdpValue}))
60+
61+
assertCodecWithPayloadType(webrtc.Opus, 111)
62+
assertCodecWithPayloadType(webrtc.VP8, 105)
63+
assertCodecWithPayloadType(webrtc.H264, 115)
64+
assertCodecWithPayloadType(webrtc.VP9, 135)
65+
}

pkg/sfu.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,6 @@ type SFU struct {
4444
sessions map[string]*Session
4545
}
4646

47-
var (
48-
rtcpfb = []webrtc.RTCPFeedback{
49-
{Type: webrtc.TypeRTCPFBCCM},
50-
{Type: webrtc.TypeRTCPFBNACK},
51-
{Type: "nack pli"},
52-
}
53-
)
54-
5547
// NewSFU creates a new sfu instance
5648
func NewSFU(c Config) *SFU {
5749
// Init random seed
@@ -164,7 +156,7 @@ func (s *SFU) getSession(id string) *Session {
164156
}
165157

166158
// NewWebRTCTransport creates a new WebRTCTransport that is a member of a session
167-
func (s *SFU) NewWebRTCTransport(sid string, me webrtc.MediaEngine) (*WebRTCTransport, error) {
159+
func (s *SFU) NewWebRTCTransport(sid string, me MediaEngine) (*WebRTCTransport, error) {
168160
session := s.getSession(sid)
169161

170162
if session == nil {

pkg/webrtctransport.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type WebRTCTransport struct {
2929
ctx context.Context
3030
cancel context.CancelFunc
3131
pc *webrtc.PeerConnection
32-
me webrtc.MediaEngine
32+
me MediaEngine
3333
mu sync.RWMutex
3434
candidates []webrtc.ICECandidateInit
3535
session *Session
@@ -41,8 +41,8 @@ type WebRTCTransport struct {
4141
}
4242

4343
// NewWebRTCTransport creates a new WebRTCTransport
44-
func NewWebRTCTransport(ctx context.Context, session *Session, me webrtc.MediaEngine, cfg WebRTCTransportConfig) (*WebRTCTransport, error) {
45-
api := webrtc.NewAPI(webrtc.WithMediaEngine(me), webrtc.WithSettingEngine(cfg.setting))
44+
func NewWebRTCTransport(ctx context.Context, session *Session, me MediaEngine, cfg WebRTCTransportConfig) (*WebRTCTransport, error) {
45+
api := webrtc.NewAPI(webrtc.WithMediaEngine(me.MediaEngine), webrtc.WithSettingEngine(cfg.setting))
4646
pc, err := api.NewPeerConnection(cfg.configuration)
4747

4848
if err != nil {

pkg/webrtctransport_test.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@ import (
77
"time"
88

99
"github.com/pion/rtcp"
10-
11-
"github.com/stretchr/testify/assert"
12-
1310
"github.com/pion/webrtc/v3"
11+
"github.com/stretchr/testify/assert"
1412
)
1513

1614
func TestNewWebRTCTransport(t *testing.T) {
1715
type args struct {
1816
ctx context.Context
1917
session *Session
20-
me webrtc.MediaEngine
18+
me MediaEngine
2119
cfg WebRTCTransportConfig
2220
}
2321
tests := []struct {
@@ -30,7 +28,7 @@ func TestNewWebRTCTransport(t *testing.T) {
3028
args: args{
3129
ctx: context.Background(),
3230
session: NewSession("test"),
33-
me: webrtc.MediaEngine{},
31+
me: MediaEngine{},
3432
cfg: WebRTCTransportConfig{},
3533
},
3634
wantErr: false,
@@ -50,7 +48,7 @@ func TestNewWebRTCTransport(t *testing.T) {
5048
}
5149

5250
func TestWebRTCTransport_AddTransceiverFromKind(t *testing.T) {
53-
me := webrtc.MediaEngine{}
51+
me := MediaEngine{}
5452
me.RegisterDefaultCodecs()
5553

5654
type args struct {

0 commit comments

Comments
 (0)