From aa339fcd1b009d824a8c269eabc2849b2e0f2e73 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Mon, 10 Jun 2024 15:39:49 +0200 Subject: [PATCH] webrtc: fix error "Failed to setup RTCP mux" on some readers (#3381) --- internal/protocols/webrtc/peer_connection.go | 32 +++++++++ .../protocols/webrtc/peer_connection_test.go | 66 +++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/internal/protocols/webrtc/peer_connection.go b/internal/protocols/webrtc/peer_connection.go index 850ca3456cb..5b8c24b17f2 100644 --- a/internal/protocols/webrtc/peer_connection.go +++ b/internal/protocols/webrtc/peer_connection.go @@ -128,6 +128,9 @@ func (co *PeerConnection) Start() error { mediaEngine := &webrtc.MediaEngine{} if co.Publish { + videoSetupped := false + audioSetupped := false + for _, tr := range co.OutgoingTracks { params, err := tr.codecParameters() if err != nil { @@ -137,8 +140,10 @@ func (co *PeerConnection) Start() error { var codecType webrtc.RTPCodecType if tr.isVideo() { codecType = webrtc.RTPCodecTypeVideo + videoSetupped = true } else { codecType = webrtc.RTPCodecTypeAudio + audioSetupped = true } err = mediaEngine.RegisterCodec(params, codecType) @@ -146,6 +151,33 @@ func (co *PeerConnection) Start() error { return err } } + + // always register at least a video and a audio codec + // otherwise handshake will fail or audio will be muted on some clients (like Firefox) + if !videoSetupped { + err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + ClockRate: 90000, + }, + PayloadType: 96, + }, webrtc.RTPCodecTypeVideo) + if err != nil { + return err + } + } + if !audioSetupped { + err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{ + RTPCodecCapability: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypePCMU, + ClockRate: 8000, + }, + PayloadType: 0, + }, webrtc.RTPCodecTypeAudio) + if err != nil { + return err + } + } } else { for _, codec := range incomingVideoCodecs { err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo) diff --git a/internal/protocols/webrtc/peer_connection_test.go b/internal/protocols/webrtc/peer_connection_test.go index 82f1f203d35..1927786fafe 100644 --- a/internal/protocols/webrtc/peer_connection_test.go +++ b/internal/protocols/webrtc/peer_connection_test.go @@ -9,6 +9,7 @@ import ( "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/test" "github.com/pion/rtp" + "github.com/pion/sdp/v3" "github.com/pion/webrtc/v3" "github.com/stretchr/testify/require" ) @@ -409,3 +410,68 @@ func TestPeerConnectionPublishRead(t *testing.T) { }) } } + +// test that an audio codec is present regardless of the fact that an audio track is not. +func TestPeerConnectionFallbackCodecs(t *testing.T) { + pc1 := &PeerConnection{ + HandshakeTimeout: conf.StringDuration(10 * time.Second), + TrackGatherTimeout: conf.StringDuration(2 * time.Second), + LocalRandomUDP: true, + IPsFromInterfaces: true, + Publish: false, + Log: test.NilLogger, + } + err := pc1.Start() + require.NoError(t, err) + defer pc1.Close() + + pc2 := &PeerConnection{ + HandshakeTimeout: conf.StringDuration(10 * time.Second), + TrackGatherTimeout: conf.StringDuration(2 * time.Second), + LocalRandomUDP: true, + IPsFromInterfaces: true, + Publish: true, + OutgoingTracks: []*OutgoingTrack{{ + Format: &format.AV1{ + PayloadTyp: 96, + }, + }}, + Log: test.NilLogger, + } + err = pc2.Start() + require.NoError(t, err) + defer pc2.Close() + + offer, err := pc1.CreatePartialOffer() + require.NoError(t, err) + + answer, err := pc2.CreateFullAnswer(context.Background(), offer) + require.NoError(t, err) + + var s sdp.SessionDescription + err = s.Unmarshal([]byte(answer.SDP)) + require.NoError(t, err) + + require.Equal(t, []*sdp.MediaDescription{ + { + MediaName: sdp.MediaName{ + Media: "video", + Port: sdp.RangedPort{Value: 9}, + Protos: []string{"UDP", "TLS", "RTP", "SAVPF"}, + Formats: []string{"97"}, + }, + ConnectionInformation: s.MediaDescriptions[0].ConnectionInformation, + Attributes: s.MediaDescriptions[0].Attributes, + }, + { + MediaName: sdp.MediaName{ + Media: "audio", + Port: sdp.RangedPort{Value: 9}, + Protos: []string{"UDP", "TLS", "RTP", "SAVPF"}, + Formats: []string{"0"}, + }, + ConnectionInformation: s.MediaDescriptions[1].ConnectionInformation, + Attributes: s.MediaDescriptions[1].Attributes, + }, + }, s.MediaDescriptions) +}