-
Notifications
You must be signed in to change notification settings - Fork 59
Description
Hi,
I'm working on a simple RTSP to SRT/MPEG-TS gateway (X.7). Lately I'm having error messages and panics during my development and tests.
Here is the test flow:
ffmpeg (mimicing a RTSP camera) --> RTSP server ( mediamtx) --> X.7 gateway app --> ffplay (mimicing SRT consumer)
RTSP Source:
ffmpeg -re -f lavfi -i "smptehdbars=rate=15:size=1280x720" -f lavfi -i "sine=frequency=1000:sample_rate=48000" -vf drawtext="text='Test Stream #1 // :timecode=01\:00\:00\:00':rate=25:x=(w-tw)/2:y=(h-lh)/2:fontsize=55:fontcolor=black:box=1:boxcolor=red" -f rtsp -c:v h264 -crf 26 -preset fast -tune zerolatency -c:a aac -b:a 128k "rtsp://127.0.0.1:8554/live1"
mediamtx is the prebuilt binary with default configuration available on github.
X.7 is using the following packages:
"github.com/asticode/go-astits"
"github.com/haivision/srtgo"
"github.com/bluenviron/gortsplib/v3"
X.7 decode H.264 video and MPEG-4 audio from RTSP stream using gortsplib, extract access units then feed them to MPEG-TS/SRT encoder.
the encoder muxes the access units into TS stream withgo- astits with SRT wrapping using srtgo then transmit it to SRT consumer.
and finally ffplay is acting like a SRT consumer
ffplay "srt://127.0.0.1:5808?mode=listener"
This workflow was working fine for several hours without any error or panic. Then last week I faced a panic with the working code.
The panic is not instant, I run the code, have a fine stream on ffplay for a while (several minutes mainly) then start to get error messages on app log
2023/06/13 10:46:23 SRT MPEGTS muxer write SRT Video error: writePacket: can't write 213 bytes of payload: only 182 is available...
after some point the video on ffplay freeze and stay frozen forever and ffplay log popping the following following messages continously.
...
[mpegts @ 0x7fbf19f16c00] changing packet size to 192 0B f=0/0
[mpegts @ 0x7fbf19f16c00] changing packet size to 188 0B f=0/0
[mpegts @ 0x7fbf19f16c00] changing packet size to 192 0B f=0/0
[mpegts @ 0x7fbf19f16c00] changing packet size to 188 0B f=0/0
[mpegts @ 0x7fbf19f16c00] changing packet size to 204 0B f=0/0
[mpegts @ 0x7fbf19f16c00] changing packet size to 188 0B f=0/0
1906.04 A-V:520.879 fd= 123 aq= 0KB vq= 0KB sq= 0B f=0/0
...
after a while, I have panic on app:
panic: runtime error: slice bounds out of range [120:0]
goroutine 52 [running]:
bytes.(*Buffer).Write(0x3?, {0xc000280318?, 0xc00004a970?, 0x10039f53b?})
/usr/local/go/src/bytes/buffer.go:172 +0xd6
github.com/asticode/go-astikit.(*BitsWriter).write(0xc000298320, {0xc000280318, 0x1, 0x1})
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:142 +0x3d
github.com/asticode/go-astikit.(*BitsWriter).flushBsCache(...)
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:169
github.com/asticode/go-astikit.(*BitsWriter).writeFullByte(0x0?, 0x20?)
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:179 +0x7e
github.com/asticode/go-astikit.(*BitsWriter).writeByteSlice(0xc000298320, {0xc000599180, 0xa8, 0xc00004aaa0?})
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:130 +0x89
github.com/asticode/go-astikit.(*BitsWriter).Write(0xc000298320?, {0x1003e5cc0?, 0xc00004aa50?})
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/binary.go:75 +0xa5
github.com/asticode/go-astits.writePESData(0x100405d00?, 0xc000282570?, {0xc000599180?, 0x1aa?, 0x1aa?}, 0x8?, 0xb6?)
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/data_pes.go:465 +0xb3
github.com/asticode/go-astits.(*Muxer).WriteData(0xc0002846e0, 0xc00004acb8)
/Users/anyuser/go/pkg/mod/github.com/asticode/[email protected]/muxer.go:230 +0x4dd
X.7/gateway.(*SRT).Encode(0xc0000e2580, {0xc0003c9aa0, 0x4, 0x10038d28a?}, {0x0?, 0x10005687f?, 0x10004e552?}, 0x7db35b0a00)
/Users/anyuser/GolandProjects/X.7/gateway/srt.go:175 +0x10cd
X.7/gateway.(*Gateway).RunRTSP.func1(0xc000292140?)
/Users/anyuser/GolandProjects/X.7/gateway/rtsp.go:127 +0x191
github.com/bluenviron/gortsplib/v3.(*clientFormat).readRTPUDP(0xc0002983c0, 0xc000282870?)
/Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_format.go:117 +0x185
github.com/bluenviron/gortsplib/v3.(*clientMedia).readRTPUDPPlay(0xc000296480, {0xc000176000, 0xc00004af20?, 0x5c1})
/Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_media.go:291 +0xae
github.com/bluenviron/gortsplib/v3.(*clientUDPListener).runReader(0xc0002b80e0)
/Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_udp_listener.go:195 +0x1e5
created by github.com/bluenviron/gortsplib/v3.(*clientUDPListener).start
/Users/anyuser/go/pkg/mod/github.com/bluenviron/gortsplib/[email protected]/client_udp_listener.go:153 +0xaa
exit status 2
This is how I implemented the encoder:
(error handling removed for brevity)
func (s *SRT) Encode(auv [][]byte, aua []byte, pts time.Duration) error {
if auv != nil && aua == nil { //Video stream
filteredNALUs := [][]byte{
{byte(h264.NALUTypeAccessUnitDelimiter), 240},
}
nonIDRPresent := false
idrPresent := false
for _, nalu := range auv {
typ := h264.NALUType(nalu[0] & 0x1F)
switch typ {
case h264.NALUTypeSPS:
s.sps = append([]byte(nil), nalu...)
continue
case h264.NALUTypePPS:
s.pps = append([]byte(nil), nalu...)
continue
case h264.NALUTypeAccessUnitDelimiter:
continue
case h264.NALUTypeIDR:
idrPresent = true
case h264.NALUTypeNonIDR:
nonIDRPresent = true
}
filteredNALUs = append(filteredNALUs, nalu)
}
auv = filteredNALUs
if !nonIDRPresent && !idrPresent {
log.Println("IDR-nonIDR Not Present Error: ")
return fmt.Errorf("IDR-nonIDR Not Present Error")
}
if idrPresent {
auv = append([][]byte{s.sps, s.pps}, auv...)
}
var dts time.Duration
if !s.firstIDRReceived {
// skip samples silently until we find one with a IDR
if !idrPresent {
log.Println("IDR Not Present Error: ")
return fmt.Errorf("IDR Not Present Error")
}
s.firstIDRReceived = true
s.dtsExtractor = h264.NewDTSExtractor()
dts, err = s.dtsExtractor.Extract(auv, pts)
s.startDTS = dts
dts = 0
pts -= s.startDTS
} else {
dts, err = s.dtsExtractor.Extract(auv, pts)
if err != nil {
log.Println("DTS Extract Error: ", err)
}
dts -= s.startDTS
pts -= s.startDTS
}
oh := &astits.PESOptionalHeader{
MarkerBits: 2,
}
if dts == pts {
oh.PTSDTSIndicator = astits.PTSDTSIndicatorOnlyPTS
oh.PTS = &astits.ClockReference{Base: int64(pts.Seconds() * 90000)}
} else {
oh.PTSDTSIndicator = astits.PTSDTSIndicatorBothPresent
oh.DTS = &astits.ClockReference{Base: int64(dts.Seconds() * 90000)}
oh.PTS = &astits.ClockReference{Base: int64(pts.Seconds() * 90000)}
}
// encode into Annex-B
annexb, err := h264.AnnexBMarshal(auv)
// write TS packet to srt
_, err = s.muxSRT.WriteData(&astits.MuxerData{ //////// this is where I get the error message !
PID: 256,
AdaptationField: &astits.PacketAdaptationField{
RandomAccessIndicator: idrPresent,
},
PES: &astits.PESData{
Header: &astits.PESHeader{
OptionalHeader: oh,
StreamID: 224, // video
},
Data: annexb,
},
})
} else if auv == nil && aua != nil{ //Audio stream
// wrap access unit inside an ADTS packet
pkts := mpeg4audio.ADTSPackets{
{
Type: s.config.Type,
SampleRate: s.config.SampleRate,
ChannelCount: s.config.ChannelCount,
AU: aua,
},
}
enc, err := pkts.Marshal()
err = s.muxSRT.WriteData(&astits.MuxerData{ //////// this is where I get the error message !
PID: 257,
AdaptationField: &astits.PacketAdaptationField{
RandomAccessIndicator: true,
},
PES: &astits.PESData{
Header: &astits.PESHeader{
OptionalHeader: &astits.PESOptionalHeader{
MarkerBits: 2,
PTSDTSIndicator: astits.PTSDTSIndicatorOnlyPTS,
PTS: &astits.ClockReference{Base: int64(pts.Seconds() * 90000)},
},
PacketLength: uint16(len(enc) + 8),
StreamID: 192, // audio
},
Data: enc,
},
})
}
return nil
}Thanks...