Fix buggy SDP parsing

This commit is contained in:
Alexey Khit
2023-02-06 11:46:00 +03:00
parent 73cdb39335
commit 497594f53f
4 changed files with 62 additions and 16 deletions
+30 -5
View File
@@ -4,8 +4,10 @@ import (
"bytes"
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/pion/rtcp"
"github.com/pion/sdp/v3"
"net/url"
"regexp"
"strconv"
"strings"
)
@@ -21,8 +23,8 @@ s=-
t=0 0`
func UnmarshalSDP(rawSDP []byte) ([]*streamer.Media, error) {
medias, err := streamer.UnmarshalSDP(rawSDP)
if err != nil {
sd := &sdp.SessionDescription{}
if err := sd.Unmarshal(rawSDP); err != nil {
// fix multiple `s=` https://github.com/AlexxIT/WebRTC/issues/417
re, _ := regexp.Compile("\ns=[^\n]+")
rawSDP = re.ReplaceAll(rawSDP, nil)
@@ -30,16 +32,28 @@ func UnmarshalSDP(rawSDP []byte) ([]*streamer.Media, error) {
// fix SDP header for some cameras
if i := bytes.Index(rawSDP, []byte("\nm=")); i > 0 {
rawSDP = append([]byte(sdpHeader), rawSDP[i:]...)
medias, err = streamer.UnmarshalSDP(rawSDP)
sd = &sdp.SessionDescription{}
err = sd.Unmarshal(rawSDP)
}
if err != nil {
return nil, err
}
}
// fix bug in ONVIF spec
// https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec-v241.pdf
medias := streamer.UnmarshalMedias(sd.MediaDescriptions)
for _, media := range medias {
// Check buggy SDP with fmtp for H264 on another track
// https://github.com/AlexxIT/WebRTC/issues/419
for _, codec := range media.Codecs {
if codec.Name == streamer.CodecH264 && codec.FmtpLine == "" {
codec.FmtpLine = findFmtpLine(codec.PayloadType, sd.MediaDescriptions)
}
}
// fix bug in ONVIF spec
// https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec-v241.pdf
switch media.Direction {
case streamer.DirectionRecvonly, "":
media.Direction = streamer.DirectionSendonly
@@ -51,6 +65,17 @@ func UnmarshalSDP(rawSDP []byte) ([]*streamer.Media, error) {
return medias, nil
}
func findFmtpLine(payloadType uint8, descriptions []*sdp.MediaDescription) string {
s := strconv.Itoa(int(payloadType))
for _, md := range descriptions {
codec := streamer.UnmarshalCodec(md, s)
if codec.FmtpLine != "" {
return codec.FmtpLine
}
}
return ""
}
// urlParse fix bugs:
// 1. Content-Base: rtsp://::ffff:192.168.1.123/onvif/profile.1/
// 2. Content-Base: rtsp://rtsp://turret2-cam.lan:554/stream1/
+23
View File
@@ -20,6 +20,7 @@ func TestURLParse(t *testing.T) {
}
func TestMultipleSinSDP(t *testing.T) {
// https://github.com/AlexxIT/WebRTC/issues/417
s := `v=0
o=- 91674849066 1 IN IP4 192.168.1.123
s=RtspServer
@@ -50,3 +51,25 @@ a=control:track1
assert.Nil(t, err)
assert.NotNil(t, medias)
}
func TestFindFmtp(t *testing.T) {
// https://github.com/AlexxIT/WebRTC/issues/419
s := `v=0
o=- 1675628282 1675628283 IN IP4 192.168.1.123
s=streamed by the RTSP server
t=0 0
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=control:track0
m=audio 0 RTP/AVP 8
a=rtpmap:0 pcma/8000/1
a=control:track1
a=framerate:25
a=range:npt=now-
a=fmtp:96 packetization-mode=1;profile-level-id=64001F;sprop-parameter-sets=Z0IAH5WoFAFuQA==,aM48gA==
`
medias, err := UnmarshalSDP([]byte(s))
assert.Nil(t, err)
assert.NotNil(t, medias)
assert.NotEqual(t, "", medias[0].Codecs[0].FmtpLine)
}
+3 -9
View File
@@ -166,14 +166,8 @@ func (c *Codec) Match(codec *Codec) bool {
(c.Channels == codec.Channels || codec.Channels == 0)
}
func UnmarshalSDP(rawSDP []byte) ([]*Media, error) {
sd := &sdp.SessionDescription{}
if err := sd.Unmarshal(rawSDP); err != nil {
return nil, err
}
var medias []*Media
for _, md := range sd.MediaDescriptions {
func UnmarshalMedias(descriptions []*sdp.MediaDescription) (medias []*Media) {
for _, md := range descriptions {
media := UnmarshalMedia(md)
if media.Direction == DirectionSendRecv {
@@ -187,7 +181,7 @@ func UnmarshalSDP(rawSDP []byte) ([]*Media, error) {
medias = append(medias, media)
}
return medias, nil
return
}
func MarshalSDP(name string, medias []*Media) ([]byte, error) {
+6 -2
View File
@@ -2,6 +2,7 @@ package webrtc
import (
"github.com/AlexxIT/go2rtc/pkg/streamer"
"github.com/pion/sdp/v3"
"github.com/pion/webrtc/v3"
)
@@ -90,12 +91,15 @@ func (c *Conn) SetOffer(offer string) (err error) {
if err = c.Conn.SetRemoteDescription(sdOffer); err != nil {
return
}
rawSDP := []byte(c.Conn.RemoteDescription().SDP)
medias, err := streamer.UnmarshalSDP(rawSDP)
if err != nil {
sd := &sdp.SessionDescription{}
if err = sd.Unmarshal(rawSDP); err != nil {
return
}
medias := streamer.UnmarshalMedias(sd.MediaDescriptions)
// sort medias, so video will always be before audio
// and ignore application media from Hass default lovelace card
for _, media := range medias {