Browse Source

RTSP client: fix race condition during computation of RTP-Info that prevented clients from reading frames (#353)

pull/346/head
aler9 5 years ago
parent
commit
8b3cd43a51
  1. 6
      internal/clientrtsp/client.go
  2. 62
      internal/streamproc/streamproc.go
  3. 3
      main_sourcertsp_test.go

6
internal/clientrtsp/client.go

@ -334,7 +334,7 @@ func (c *Client) run() {
// add RTP-Info // add RTP-Info
var ri headers.RTPInfo var ri headers.RTPInfo
for trackID, ti := range res.TrackInfos { for trackID, ti := range res.TrackInfos {
if !ti.Initialized { if ti.LastTimeNTP == 0 {
continue continue
} }
@ -351,8 +351,8 @@ func (c *Client) run() {
} }
clockRate, _ := track.ClockRate() clockRate, _ := track.ClockRate()
ts := uint32(uint64(time.Since(ti.NTPTime).Seconds()*float64(clockRate)) + ts := uint32(uint64(ti.LastTimeRTP) +
uint64(ti.RTPTime)) uint64(time.Since(time.Unix(ti.LastTimeNTP, 0)).Seconds()*float64(clockRate)))
lsn := ti.LastSequenceNumber lsn := ti.LastSequenceNumber
ri = append(ri, &headers.RTPInfoEntry{ ri = append(ri, &headers.RTPInfoEntry{

62
internal/streamproc/streamproc.go

@ -2,7 +2,6 @@ package streamproc
import ( import (
"encoding/binary" "encoding/binary"
"sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -16,68 +15,63 @@ type Path interface {
// TrackInfo contains infos about a track. // TrackInfo contains infos about a track.
type TrackInfo struct { type TrackInfo struct {
Initialized bool
LastSequenceNumber uint16 LastSequenceNumber uint16
RTPTime uint32 LastTimeRTP uint32
NTPTime time.Time LastTimeNTP int64
} }
type trackInfo struct { type track struct {
initialized bool
lastSequenceNumber uint32 lastSequenceNumber uint32
timeMutex sync.Mutex lastTimeRTP uint32
rtpTime uint32 lastTimeNTP int64
ntpTime time.Time
} }
// StreamProc is a stream processor, an intermediate layer between a source and a path. // StreamProc is a stream processor, an intermediate layer between a source and a path.
type StreamProc struct { type StreamProc struct {
path Path path Path
trackInfos []trackInfo tracks []*track
} }
// New allocates a StreamProc. // New allocates a StreamProc.
func New(path Path, tracksLen int) *StreamProc { func New(path Path, tracksLen int) *StreamProc {
return &StreamProc{ sp := &StreamProc{
path: path, path: path,
trackInfos: make([]trackInfo, tracksLen),
} }
sp.tracks = make([]*track, tracksLen)
for i := range sp.tracks {
sp.tracks[i] = &track{}
}
return sp
} }
// TrackInfos returns infos about the tracks of the stream. // TrackInfos returns infos about the tracks of the stream.
func (sp *StreamProc) TrackInfos() []TrackInfo { func (sp *StreamProc) TrackInfos() []TrackInfo {
ret := make([]TrackInfo, len(sp.trackInfos)) ret := make([]TrackInfo, len(sp.tracks))
for trackID, track := range sp.tracks {
for trackID := range sp.trackInfos {
sp.trackInfos[trackID].timeMutex.Lock()
ret[trackID] = TrackInfo{ ret[trackID] = TrackInfo{
Initialized: sp.trackInfos[trackID].initialized, LastSequenceNumber: uint16(atomic.LoadUint32(&track.lastSequenceNumber)),
LastSequenceNumber: uint16(atomic.LoadUint32(&sp.trackInfos[trackID].lastSequenceNumber)), LastTimeRTP: atomic.LoadUint32(&track.lastTimeRTP),
RTPTime: sp.trackInfos[trackID].rtpTime, LastTimeNTP: atomic.LoadInt64(&track.lastTimeNTP),
NTPTime: sp.trackInfos[trackID].ntpTime,
} }
sp.trackInfos[trackID].timeMutex.Unlock()
} }
return ret return ret
} }
// OnFrame processes a frame. // OnFrame processes a frame.
func (sp *StreamProc) OnFrame(trackID int, streamType gortsplib.StreamType, payload []byte) { func (sp *StreamProc) OnFrame(trackID int, streamType gortsplib.StreamType, payload []byte) {
if streamType == gortsplib.StreamTypeRTP && len(payload) >= 8 { if streamType == gortsplib.StreamTypeRTP && len(payload) >= 8 {
track := sp.tracks[trackID]
// store last sequence number // store last sequence number
sequenceNumber := binary.BigEndian.Uint16(payload[2 : 2+2]) sequenceNumber := binary.BigEndian.Uint16(payload[2 : 2+2])
atomic.StoreUint32(&sp.trackInfos[trackID].lastSequenceNumber, uint32(sequenceNumber)) atomic.StoreUint32(&track.lastSequenceNumber, uint32(sequenceNumber))
// store time mapping // store last RTP time and correspondent NTP time
if !sp.trackInfos[trackID].initialized { timestamp := binary.BigEndian.Uint32(payload[4 : 4+4])
timestamp := binary.BigEndian.Uint32(payload[4 : 4+4]) atomic.StoreUint32(&track.lastTimeRTP, timestamp)
sp.trackInfos[trackID].timeMutex.Lock() atomic.StoreInt64(&track.lastTimeNTP, time.Now().Unix())
sp.trackInfos[trackID].initialized = true
sp.trackInfos[trackID].rtpTime = timestamp
sp.trackInfos[trackID].ntpTime = time.Now()
sp.trackInfos[trackID].timeMutex.Unlock()
}
} }
sp.path.OnSPFrame(trackID, streamType, payload) sp.path.OnSPFrame(trackID, streamType, payload)

3
main_sourcertsp_test.go

@ -321,4 +321,7 @@ func TestSourceRTSPRTPInfo(t *testing.T) {
Timestamp: (*dest.RTPInfo())[1].Timestamp, Timestamp: (*dest.RTPInfo())[1].Timestamp,
}, },
}, dest.RTPInfo()) }, dest.RTPInfo())
require.Less(t, uint32(756436454), *(*dest.RTPInfo())[0].Timestamp)
require.Less(t, uint32(156457686), *(*dest.RTPInfo())[1].Timestamp)
} }

Loading…
Cancel
Save