Fix buggy SDP parsing
This commit is contained in:
+30
-5
@@ -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/
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user