diff --git a/client.go b/client.go index 4333c715..03df4f7d 100644 --- a/client.go +++ b/client.go @@ -1229,7 +1229,7 @@ func (c *Client) doDescribe(u *url.URL) (Tracks, *url.URL, *base.Response, error } var tracks Tracks - sd, err := tracks.Unmarshal(res.Body, true) + sd, err := tracks.Unmarshal(res.Body) if err != nil { return nil, nil, nil, err } diff --git a/pkg/rtcpreceiver/rtcpreceiver.go b/pkg/rtcpreceiver/rtcpreceiver.go index 6372ca96..9d0b9893 100644 --- a/pkg/rtcpreceiver/rtcpreceiver.go +++ b/pkg/rtcpreceiver/rtcpreceiver.go @@ -103,6 +103,10 @@ func (rr *RTCPReceiver) report(ts time.Time) rtcp.Packet { return nil } + if rr.clockRate == 0 { + return nil + } + report := &rtcp.ReceiverReport{ SSRC: rr.receiverSSRC, Reports: []rtcp.ReceptionReport{ diff --git a/pkg/rtcpsender/rtcpsender.go b/pkg/rtcpsender/rtcpsender.go index a25558b6..f98e3bb2 100644 --- a/pkg/rtcpsender/rtcpsender.go +++ b/pkg/rtcpsender/rtcpsender.go @@ -80,6 +80,10 @@ func (rs *RTCPSender) report(ts time.Time) rtcp.Packet { return nil } + if rs.clockRate == 0 { + return nil + } + return &rtcp.SenderReport{ SSRC: *rs.senderSSRC, NTPTime: func() uint64 { diff --git a/serversession.go b/serversession.go index 63a10d4b..d8af91c8 100644 --- a/serversession.go +++ b/serversession.go @@ -532,7 +532,7 @@ func (ss *ServerSession) handleRequest(sc *ServerConn, req *base.Request) (*base } var tracks Tracks - _, err = tracks.Unmarshal(req.Body, false) + _, err = tracks.Unmarshal(req.Body) if err != nil { return &base.Response{ StatusCode: base.StatusBadRequest, diff --git a/serverstream.go b/serverstream.go index 808cd95a..a5e64d8d 100644 --- a/serverstream.go +++ b/serverstream.go @@ -100,16 +100,20 @@ func (st *ServerStream) rtpInfo(trackID int, now time.Time) (uint16, uint32, boo return 0, 0, false } + clockRate := st.tracks[trackID].ClockRate() + if clockRate == 0 { + return 0, 0, false + } + // sequence number of the first packet of the stream seq := track.lastSequenceNumber + 1 // RTP timestamp corresponding to the time value in // the Range response header. // remove a small quantity in order to avoid DTS > PTS - cr := st.tracks[trackID].ClockRate() ts := uint32(uint64(track.lastTimeRTP) + - uint64(now.Sub(track.lastTimeNTP).Seconds()*float64(cr)) - - uint64(cr)/10) + uint64(now.Sub(track.lastTimeNTP).Seconds()*float64(clockRate)) - + uint64(clockRate)/10) return seq, ts, true } diff --git a/track_generic.go b/track_generic.go index 4bb4c840..d77d41d5 100644 --- a/track_generic.go +++ b/track_generic.go @@ -110,12 +110,7 @@ func newTrackGenericFromMediaDescription( // Init initializes a TrackGeneric func (t *TrackGeneric) Init() error { - var err error - t.clockRate, err = findClockRate(t) - if err != nil { - return fmt.Errorf("unable to get clock rate: %s", err) - } - + t.clockRate, _ = findClockRate(t) return nil } diff --git a/track_test.go b/track_test.go index 3ac59a2d..e6e5dade 100644 --- a/track_test.go +++ b/track_test.go @@ -599,68 +599,6 @@ func TestTrackNewFromMediaDescriptionErrors(t *testing.T) { }, "no media formats found", }, - { - "no rtpmap", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"90"}, - }, - }, - "unable to get clock rate: attribute 'rtpmap' not found", - }, - { - "invalid clockrate 1", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "97 mpeg4-generic/48000/2", - }, - }, - }, - "unable to get clock rate: attribute 'rtpmap' not found", - }, - { - "invalid clockrate 2", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 mpeg4-generic", - }, - }, - }, - "unable to get clock rate: invalid rtpmap (mpeg4-generic)", - }, - { - "invalid clockrate 3", - &psdp.MediaDescription{ - MediaName: psdp.MediaName{ - Media: "video", - Protos: []string{"RTP", "AVP"}, - Formats: []string{"96"}, - }, - Attributes: []psdp.Attribute{ - { - Key: "rtpmap", - Value: "96 mpeg4-generic/aa", - }, - }, - }, - "unable to get clock rate: strconv.ParseInt: parsing \"aa\": invalid syntax", - }, { "aac missing fmtp", &psdp.MediaDescription{ @@ -966,7 +904,7 @@ func TestTrackURL(t *testing.T) { } { t.Run(ca.name, func(t *testing.T) { var tracks Tracks - _, err := tracks.Unmarshal(ca.sdp, false) + _, err := tracks.Unmarshal(ca.sdp) require.NoError(t, err) ur, err := tracks[0].url(ca.baseURL) require.NoError(t, err) diff --git a/tracks.go b/tracks.go index 9a343477..ef62c187 100644 --- a/tracks.go +++ b/tracks.go @@ -3,7 +3,6 @@ package gortsplib import ( "fmt" "strconv" - "strings" psdp "github.com/pion/sdp/v3" @@ -14,7 +13,7 @@ import ( type Tracks []Track // Unmarshal decodes tracks from the SDP format. It returns the decoded SDP. -func (ts *Tracks) Unmarshal(byts []byte, skipGenericTracksWithoutClockRate bool) (*sdp.SessionDescription, error) { +func (ts *Tracks) Unmarshal(byts []byte) (*sdp.SessionDescription, error) { var sd sdp.SessionDescription err := sd.Unmarshal(byts) if err != nil { @@ -26,10 +25,6 @@ func (ts *Tracks) Unmarshal(byts []byte, skipGenericTracksWithoutClockRate bool) for i, md := range sd.MediaDescriptions { t, err := newTrackFromMediaDescription(md) if err != nil { - if skipGenericTracksWithoutClockRate && - strings.HasPrefix(err.Error(), "unable to get clock rate") { - continue - } return nil, fmt.Errorf("unable to parse track %d: %s", i+1, err) } diff --git a/tracks_test.go b/tracks_test.go index 88134f9d..e87a2d45 100644 --- a/tracks_test.go +++ b/tracks_test.go @@ -6,6 +6,60 @@ import ( "github.com/stretchr/testify/require" ) +func TestTracksRead(t *testing.T) { + sdp := []byte("v=0\r\n" + + "o=- 0 0 IN IP4 10.0.0.131\r\n" + + "s=Media Presentation\r\n" + + "i=samsung\r\n" + + "c=IN IP4 0.0.0.0\r\n" + + "b=AS:2632\r\n" + + "t=0 0\r\n" + + "a=control:rtsp://10.0.100.50/profile5/media.smp\r\n" + + "a=range:npt=now-\r\n" + + "m=video 42504 RTP/AVP 97\r\n" + + "b=AS:2560\r\n" + + "a=rtpmap:97 H264/90000\r\n" + + "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=v\r\n" + + "a=cliprect:0,0,1080,1920\r\n" + + "a=framesize:97 1920-1080\r\n" + + "a=framerate:30.0\r\n" + + "a=fmtp:97 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=Z2QAKKy0A8ARPyo=,aO4Bniw=\r\n" + + "m=audio 42506 RTP/AVP 0\r\n" + + "b=AS:64\r\n" + + "a=rtpmap:0 PCMU/8000\r\n" + + "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=a\r\n" + + "a=recvonly\r\n" + + "m=application 42508 RTP/AVP 107\r\n" + + "b=AS:8\r\n") + + var tracks Tracks + _, err := tracks.Unmarshal(sdp) + require.NoError(t, err) + require.Equal(t, Tracks{ + &TrackH264{ + trackBase: trackBase{ + control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", + }, + PayloadType: 97, + SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, + PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, + PacketizationMode: 1, + }, + &TrackG711{ + MULaw: true, + trackBase: trackBase{ + control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", + }, + }, + &TrackGeneric{ + Media: "application", + Payloads: []TrackGenericPayload{{ + Type: 107, + }}, + }, + }, tracks) +} + func TestTracksReadErrors(t *testing.T) { for _, ca := range []struct { name string @@ -36,56 +90,8 @@ func TestTracksReadErrors(t *testing.T) { } { t.Run(ca.name, func(t *testing.T) { var tracks Tracks - _, err := tracks.Unmarshal(ca.sdp, false) + _, err := tracks.Unmarshal(ca.sdp) require.EqualError(t, err, ca.err) }) } } - -func TestTracksReadSkipGenericTracksWithoutClockRate(t *testing.T) { - sdp := []byte("v=0\r\n" + - "o=- 0 0 IN IP4 10.0.0.131\r\n" + - "s=Media Presentation\r\n" + - "i=samsung\r\n" + - "c=IN IP4 0.0.0.0\r\n" + - "b=AS:2632\r\n" + - "t=0 0\r\n" + - "a=control:rtsp://10.0.100.50/profile5/media.smp\r\n" + - "a=range:npt=now-\r\n" + - "m=video 42504 RTP/AVP 97\r\n" + - "b=AS:2560\r\n" + - "a=rtpmap:97 H264/90000\r\n" + - "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=v\r\n" + - "a=cliprect:0,0,1080,1920\r\n" + - "a=framesize:97 1920-1080\r\n" + - "a=framerate:30.0\r\n" + - "a=fmtp:97 packetization-mode=1;profile-level-id=640028;sprop-parameter-sets=Z2QAKKy0A8ARPyo=,aO4Bniw=\r\n" + - "m=audio 42506 RTP/AVP 0\r\n" + - "b=AS:64\r\n" + - "a=rtpmap:0 PCMU/8000\r\n" + - "a=control:rtsp://10.0.100.50/profile5/media.smp/trackID=a\r\n" + - "a=recvonly\r\n" + - "m=application 42508 RTP/AVP 107\r\n" + - "b=AS:8\r\n") - - var tracks Tracks - _, err := tracks.Unmarshal(sdp, true) - require.NoError(t, err) - require.Equal(t, Tracks{ - &TrackH264{ - trackBase: trackBase{ - control: "rtsp://10.0.100.50/profile5/media.smp/trackID=v", - }, - PayloadType: 97, - SPS: []byte{0x67, 0x64, 0x00, 0x28, 0xac, 0xb4, 0x03, 0xc0, 0x11, 0x3f, 0x2a}, - PPS: []byte{0x68, 0xee, 0x01, 0x9e, 0x2c}, - PacketizationMode: 1, - }, - &TrackG711{ - MULaw: true, - trackBase: trackBase{ - control: "rtsp://10.0.100.50/profile5/media.smp/trackID=a", - }, - }, - }, tracks) -}