Skip to content

Commit

Permalink
Fix codec matching in case of multiple sample rates or channel count
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Jun 10, 2024
1 parent 3bbc7c1 commit eaec240
Show file tree
Hide file tree
Showing 26 changed files with 263 additions and 126 deletions.
2 changes: 1 addition & 1 deletion e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func createTrack(offer webrtc.SessionDescription) (*webrtc.PeerConnection, *webr
return nil, nil, nil, errPc
}

track, errTrack := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "pion")
track, errTrack := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2}, "audio", "pion")
if errTrack != nil {
return nil, nil, nil, errTrack
}
Expand Down
2 changes: 1 addition & 1 deletion examples/bandwidth-estimation-from-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func main() {
estimator := <-estimatorChan

// Create a video track
videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video", "pion")
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/insertable-streams/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func main() {
}()

// Create a video track
videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
videoTrack, err := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video", "pion")
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/play-from-disk-renegotiation/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func createPeerConnection(w http.ResponseWriter, r *http.Request) {
// Add a single video track
func addVideo(w http.ResponseWriter, r *http.Request) {
videoTrack, err := webrtc.NewTrackLocalStaticSample(
webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8},
webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000},
fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()),
fmt.Sprintf("video-%d", randutil.NewMathRandomGenerator().Uint32()),
)
Expand Down
4 changes: 2 additions & 2 deletions examples/play-from-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func main() {
}

// Create a video track
videoTrack, videoTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: trackCodec}, "video", "pion")
videoTrack, videoTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: trackCodec, ClockRate: 90000}, "video", "pion")
if videoTrackErr != nil {
panic(videoTrackErr)
}
Expand Down Expand Up @@ -149,7 +149,7 @@ func main() {

if haveAudioFile {
// Create a audio track
audioTrack, audioTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus}, "audio", "pion")
audioTrack, audioTrackErr := webrtc.NewTrackLocalStaticSample(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2}, "audio", "pion")
if audioTrackErr != nil {
panic(audioTrackErr)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/reflect/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func main() {
}()

// Create Track that we send video back to browser on
outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video", "pion")
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/rtp-forwarder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func main() {
panic(err)
}
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "", RTCPFeedback: nil},
}, webrtc.RTPCodecTypeAudio); err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/rtp-to-webrtc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func main() {
}()

// Create a video track
videoTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
videoTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video", "pion")
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/save-to-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func main() {
panic(err)
}
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 48000, Channels: 2, SDPFmtpLine: "", RTCPFeedback: nil},
PayloadType: 111,
}, webrtc.RTPCodecTypeAudio); err != nil {
panic(err)
Expand Down
6 changes: 3 additions & 3 deletions examples/simulcast/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,19 @@ func main() {
outputTracks := map[string]*webrtc.TrackLocalStaticRTP{}

// Create Track that we send video back to browser on
outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video_q", "pion_q")
outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video_q", "pion_q")
if err != nil {
panic(err)
}
outputTracks["q"] = outputTrack

outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video_h", "pion_h")
outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video_h", "pion_h")
if err != nil {
panic(err)
}
outputTracks["h"] = outputTrack

outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video_f", "pion_f")
outputTrack, err = webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video_f", "pion_f")
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/swap-tracks/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func main() { // nolint:gocognit
}()

// Create Track that we send video back to browser on
outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8}, "video", "pion")
outputTrack, err := webrtc.NewTrackLocalStaticRTP(webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000}, "video", "pion")
if err != nil {
panic(err)
}
Expand Down
4 changes: 2 additions & 2 deletions interceptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func TestPeerConnection_Interceptor(t *testing.T) {
offerer := createPC()
answerer := createPC()

track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000}, "video", "pion")
assert.NoError(t, err)

_, err = offerer.AddTrack(track)
Expand Down Expand Up @@ -163,7 +163,7 @@ func Test_Interceptor_BindUnbind(t *testing.T) {
sender, receiver, err := NewAPI(WithMediaEngine(m), WithInterceptorRegistry(ir)).newPair(Configuration{})
assert.NoError(t, err)

track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8}, "video", "pion")
track, err := NewTrackLocalStaticSample(RTPCodecCapability{MimeType: MimeTypeVP8, ClockRate: 90000}, "video", "pion")
assert.NoError(t, err)

_, err = sender.AddTrack(track)
Expand Down
24 changes: 23 additions & 1 deletion internal/fmtp/fmtp.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ func parseParameters(line string) map[string]string {
return parameters
}

func channelsEqual(a, b uint16) bool {
if a == 0 {
a = 1
}
if b == 0 {
b = 1
}
return a == b
}

// FMTP interface for implementing custom
// FMTP parsers based on MimeType
type FMTP interface {
Expand All @@ -39,7 +49,7 @@ type FMTP interface {
}

// Parse parses an fmtp string based on the MimeType
func Parse(mimeType, line string) FMTP {
func Parse(mimeType string, clockRate uint32, channels uint16, line string) FMTP {
var f FMTP

parameters := parseParameters(line)
Expand All @@ -63,6 +73,8 @@ func Parse(mimeType, line string) FMTP {
default:
f = &genericFMTP{
mimeType: mimeType,
clockRate: clockRate,
channels: channels,
parameters: parameters,
}
}
Expand All @@ -72,6 +84,8 @@ func Parse(mimeType, line string) FMTP {

type genericFMTP struct {
mimeType string
clockRate uint32
channels uint16
parameters map[string]string
}

Expand All @@ -91,6 +105,14 @@ func (g *genericFMTP) Match(b FMTP) bool {
return false
}

if g.clockRate != c.clockRate {
return false
}

if !channelsEqual(g.channels, c.channels) {
return false
}

for k, v := range g.parameters {
if vb, ok := c.parameters[k]; ok && !strings.EqualFold(vb, v) {
return false
Expand Down
97 changes: 90 additions & 7 deletions internal/fmtp/fmtp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,23 @@ func TestParseParameters(t *testing.T) {

func TestParse(t *testing.T) {
for _, ca := range []struct {
name string
mimeType string
line string
expected FMTP
name string
mimeType string
clockRate uint32
channels uint16
line string
expected FMTP
}{
{
"generic",
"generic",
90000,
2,
"key-name=value",
&genericFMTP{
mimeType: "generic",
mimeType: "generic",
clockRate: 90000,
channels: 2,
parameters: map[string]string{
"key-name": "value",
},
Expand All @@ -75,9 +81,13 @@ func TestParse(t *testing.T) {
{
"generic case normalization",
"generic",
90000,
2,
"Key=value",
&genericFMTP{
mimeType: "generic",
mimeType: "generic",
clockRate: 90000,
channels: 2,
parameters: map[string]string{
"key": "value",
},
Expand All @@ -86,6 +96,8 @@ func TestParse(t *testing.T) {
{
"h264",
"video/h264",
90000,
0,
"key-name=value",
&h264FMTP{
parameters: map[string]string{
Expand All @@ -96,6 +108,8 @@ func TestParse(t *testing.T) {
{
"vp9",
"video/vp9",
90000,
0,
"key-name=value",
&vp9FMTP{
parameters: map[string]string{
Expand All @@ -106,6 +120,8 @@ func TestParse(t *testing.T) {
{
"av1",
"video/av1",
90000,
0,
"key-name=value",
&av1FMTP{
parameters: map[string]string{
Expand All @@ -115,7 +131,7 @@ func TestParse(t *testing.T) {
},
} {
t.Run(ca.name, func(t *testing.T) {
f := Parse(ca.mimeType, ca.line)
f := Parse(ca.mimeType, ca.clockRate, ca.channels, ca.line)
if !reflect.DeepEqual(ca.expected, f) {
t.Errorf("expected '%v', got '%v'", ca.expected, f)
}
Expand Down Expand Up @@ -177,6 +193,27 @@ func TestMatch(t *testing.T) {
},
true,
},
{
"generic inferred channels",
&genericFMTP{
mimeType: "generic",
channels: 1,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
true,
},
{
"generic inconsistent different kind",
&genericFMTP{
Expand Down Expand Up @@ -210,6 +247,52 @@ func TestMatch(t *testing.T) {
},
false,
},
{
"generic inconsistent different clock rate",
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
clockRate: 48000,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
false,
},
{
"generic inconsistent different channels",
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
channels: 2,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
&genericFMTP{
mimeType: "generic",
clockRate: 90000,
channels: 1,
parameters: map[string]string{
"key1": "value1",
"key2": "value2",
"key3": "value3",
},
},
false,
},
{
"generic inconsistent different parameters",
&genericFMTP{
Expand Down
Loading

0 comments on commit eaec240

Please sign in to comment.