Skip to content

Commit

Permalink
record, hls: fix panic with MPEG-4 audio tracks without config (#3590)
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Aug 1, 2024
1 parent da5420a commit 05716bc
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 135 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/abema/go-mp4 v1.2.0
github.com/alecthomas/kong v0.9.0
github.com/bluenviron/gohlslib v1.4.0
github.com/bluenviron/gortsplib/v4 v4.10.2
github.com/bluenviron/gortsplib/v4 v4.10.3-0.20240801095652-e2d1e6dab418
github.com/bluenviron/mediacommon v1.12.1
github.com/datarhei/gosrt v0.7.0
github.com/fsnotify/fsnotify v1.7.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYh
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/bluenviron/gohlslib v1.4.0 h1:3a9W1x8eqlxJUKt1sJCunPGtti5ALIY2ik4GU0RVe7E=
github.com/bluenviron/gohlslib v1.4.0/go.mod h1:q5ZElzNw5GRbV1VEI45qkcPbKBco6BP58QEY5HyFsmo=
github.com/bluenviron/gortsplib/v4 v4.10.2 h1:O7HPRG8Pv4zUbyYD0HYH4Ufu1Hg9FJGTlizx6a09hL0=
github.com/bluenviron/gortsplib/v4 v4.10.2/go.mod h1:re/L/vYh2wLPElQNAYah+bRFHJs0aRkM1MLX3WJ3N6M=
github.com/bluenviron/gortsplib/v4 v4.10.3-0.20240801095652-e2d1e6dab418 h1:j+qo+yB1S1KiTOKSN4ABPbJ9rNdzoT6AoH9F/+k1QjE=
github.com/bluenviron/gortsplib/v4 v4.10.3-0.20240801095652-e2d1e6dab418/go.mod h1:FE/oUV476F/paP+D2YAV2LvXDgpZef4lN5uAKAuVAsQ=
github.com/bluenviron/mediacommon v1.12.1 h1:sgDJaKV6OXrPCSO0KPp9zi/pwNWtKHenn5/dvjtY+Tg=
github.com/bluenviron/mediacommon v1.12.1/go.mod h1:HDyW2CzjvhYJXtdxstdFPio3G0qSocPhqkhUt/qffec=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
Expand Down
7 changes: 6 additions & 1 deletion internal/protocols/mpegts/from_stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,13 @@ func FromStream(
})

case *format.MPEG4Audio:
co := forma.GetConfig()
if co == nil {
return fmt.Errorf("MPEG-4 audio tracks without explicit configuration are not supported")
}

Check warning on line 189 in internal/protocols/mpegts/from_stream.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/mpegts/from_stream.go#L186-L189

Added lines #L186 - L189 were not covered by tests

track := addTrack(&mcmpegts.CodecMPEG4Audio{
Config: *forma.GetConfig(),
Config: *co,

Check warning on line 192 in internal/protocols/mpegts/from_stream.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/mpegts/from_stream.go#L192

Added line #L192 was not covered by tests
})

stream.AddReader(writer, medi, forma, func(u unit.Unit) error {
Expand Down
63 changes: 34 additions & 29 deletions internal/record/agent_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,55 +28,60 @@ type agentInstance struct {
done chan struct{}
}

func (a *agentInstance) initialize() {
a.pathFormat = a.agent.PathFormat
// Log implements logger.Writer.
func (ai *agentInstance) Log(level logger.Level, format string, args ...interface{}) {
ai.agent.Log(level, format, args...)
}

func (ai *agentInstance) initialize() {
ai.pathFormat = ai.agent.PathFormat

a.pathFormat = PathAddExtension(
strings.ReplaceAll(a.pathFormat, "%path", a.agent.PathName),
a.agent.Format,
ai.pathFormat = PathAddExtension(
strings.ReplaceAll(ai.pathFormat, "%path", ai.agent.PathName),
ai.agent.Format,
)

a.terminate = make(chan struct{})
a.done = make(chan struct{})
ai.terminate = make(chan struct{})
ai.done = make(chan struct{})

a.writer = asyncwriter.New(a.agent.WriteQueueSize, a.agent)
ai.writer = asyncwriter.New(ai.agent.WriteQueueSize, ai.agent)

switch a.agent.Format {
switch ai.agent.Format {
case conf.RecordFormatMPEGTS:
a.format = &formatMPEGTS{
a: a,
ai.format = &formatMPEGTS{
ai: ai,
}
a.format.initialize()
ai.format.initialize()

default:
a.format = &formatFMP4{
a: a,
ai.format = &formatFMP4{
ai: ai,
}
a.format.initialize()
ai.format.initialize()
}

go a.run()
go ai.run()
}

func (a *agentInstance) close() {
close(a.terminate)
<-a.done
func (ai *agentInstance) close() {
close(ai.terminate)
<-ai.done
}

func (a *agentInstance) run() {
defer close(a.done)
func (ai *agentInstance) run() {
defer close(ai.done)

a.writer.Start()
ai.writer.Start()

select {
case err := <-a.writer.Error():
a.agent.Log(logger.Error, err.Error())
a.agent.Stream.RemoveReader(a.writer)
case err := <-ai.writer.Error():
ai.Log(logger.Error, err.Error())
ai.agent.Stream.RemoveReader(ai.writer)

case <-a.terminate:
a.agent.Stream.RemoveReader(a.writer)
a.writer.Stop()
case <-ai.terminate:
ai.agent.Stream.RemoveReader(ai.writer)
ai.writer.Stop()
}

a.format.close()
ai.format.close()
}
87 changes: 46 additions & 41 deletions internal/record/format_fmp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func jpegExtractSize(image []byte) (int, int, error) {
}

type formatFMP4 struct {
a *agentInstance
ai *agentInstance

tracks []*formatFMP4Track
hasVideo bool
Expand Down Expand Up @@ -140,7 +140,7 @@ func (f *formatFMP4) initialize() {
}
}

for _, media := range f.a.agent.Stream.Desc().Medias {
for _, media := range f.ai.agent.Stream.Desc().Medias {
for _, forma := range media.Formats {
switch forma := forma.(type) {
case *rtspformat.AV1:
Expand All @@ -153,7 +153,7 @@ func (f *formatFMP4) initialize() {

firstReceived := false

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 156 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L156

Added line #L156 was not covered by tests
tunit := u.(*unit.AV1)
if tunit.TU == nil {
return nil
Expand Down Expand Up @@ -211,7 +211,7 @@ func (f *formatFMP4) initialize() {

firstReceived := false

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 214 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L214

Added line #L214 was not covered by tests
tunit := u.(*unit.VP9)
if tunit.Frame == nil {
return nil
Expand Down Expand Up @@ -309,7 +309,7 @@ func (f *formatFMP4) initialize() {

var dtsExtractor *h265.DTSExtractor

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
return nil
Expand Down Expand Up @@ -387,7 +387,7 @@ func (f *formatFMP4) initialize() {

var dtsExtractor *h264.DTSExtractor

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
return nil
Expand Down Expand Up @@ -464,7 +464,7 @@ func (f *formatFMP4) initialize() {
firstReceived := false
var lastPTS time.Duration

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 467 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L467

Added line #L467 was not covered by tests
tunit := u.(*unit.MPEG4Video)
if tunit.Frame == nil {
return nil
Expand Down Expand Up @@ -517,7 +517,7 @@ func (f *formatFMP4) initialize() {
firstReceived := false
var lastPTS time.Duration

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 520 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L520

Added line #L520 was not covered by tests
tunit := u.(*unit.MPEG1Video)
if tunit.Frame == nil {
return nil
Expand Down Expand Up @@ -566,7 +566,7 @@ func (f *formatFMP4) initialize() {

parsed := false

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 569 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L569

Added line #L569 was not covered by tests
tunit := u.(*unit.MJPEG)
if tunit.Frame == nil {
return nil
Expand Down Expand Up @@ -598,7 +598,7 @@ func (f *formatFMP4) initialize() {
}
track := addTrack(forma, codec)

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 601 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L601

Added line #L601 was not covered by tests
tunit := u.(*unit.Opus)
if tunit.Packets == nil {
return nil
Expand All @@ -625,37 +625,42 @@ func (f *formatFMP4) initialize() {
})

case *rtspformat.MPEG4Audio:
codec := &fmp4.CodecMPEG4Audio{
Config: *forma.GetConfig(),
}
track := addTrack(forma, codec)

sampleRate := time.Duration(forma.ClockRate())

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
return nil
co := forma.GetConfig()
if co == nil {
f.ai.Log(logger.Warn, "skipping MPEG-4 audio track: tracks without explicit configuration are not supported")

Check warning on line 630 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L630

Added line #L630 was not covered by tests
} else {
codec := &fmp4.CodecMPEG4Audio{
Config: *co,
}
track := addTrack(forma, codec)

for i, au := range tunit.AUs {
dt := time.Duration(i) * mpeg4audio.SamplesPerAccessUnit *
time.Second / sampleRate
sampleRate := time.Duration(forma.ClockRate())

err := track.write(&sample{
PartSample: &fmp4.PartSample{
Payload: au,
},
dts: tunit.PTS + dt,
ntp: tunit.NTP.Add(dt),
})
if err != nil {
return err
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
return nil

Check warning on line 642 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L642

Added line #L642 was not covered by tests
}
}

return nil
})
for i, au := range tunit.AUs {
dt := time.Duration(i) * mpeg4audio.SamplesPerAccessUnit *
time.Second / sampleRate

err := track.write(&sample{
PartSample: &fmp4.PartSample{
Payload: au,
},
dts: tunit.PTS + dt,
ntp: tunit.NTP.Add(dt),
})
if err != nil {
return err
}

Check warning on line 658 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L657-L658

Added lines #L657 - L658 were not covered by tests
}

return nil
})
}

case *rtspformat.MPEG1Audio:
codec := &fmp4.CodecMPEG1Audio{
Expand All @@ -666,7 +671,7 @@ func (f *formatFMP4) initialize() {

parsed := false

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 674 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L674

Added line #L674 was not covered by tests
tunit := u.(*unit.MPEG1Audio)
if tunit.Frames == nil {
return nil
Expand Down Expand Up @@ -721,7 +726,7 @@ func (f *formatFMP4) initialize() {

parsed := false

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {

Check warning on line 729 in internal/record/format_fmp4.go

View check run for this annotation

Codecov / codecov/patch

internal/record/format_fmp4.go#L729

Added line #L729 was not covered by tests
tunit := u.(*unit.AC3)
if tunit.Frames == nil {
return nil
Expand Down Expand Up @@ -783,7 +788,7 @@ func (f *formatFMP4) initialize() {
}
track := addTrack(forma, codec)

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.G711)
if tunit.Samples == nil {
return nil
Expand Down Expand Up @@ -814,7 +819,7 @@ func (f *formatFMP4) initialize() {
}
track := addTrack(forma, codec)

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
f.ai.agent.Stream.AddReader(f.ai.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.LPCM)
if tunit.Samples == nil {
return nil
Expand All @@ -832,7 +837,7 @@ func (f *formatFMP4) initialize() {
}
}

f.a.agent.Log(logger.Info, "recording %s",
f.ai.Log(logger.Info, "recording %s",
defs.FormatsInfo(formats))
}

Expand Down
6 changes: 3 additions & 3 deletions internal/record/format_fmp4_part.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ func (p *formatFMP4Part) initialize() {

func (p *formatFMP4Part) close() error {
if p.s.fi == nil {
p.s.path = Path{Start: p.s.startNTP}.Encode(p.s.f.a.pathFormat)
p.s.f.a.agent.Log(logger.Debug, "creating segment %s", p.s.path)
p.s.path = Path{Start: p.s.startNTP}.Encode(p.s.f.ai.pathFormat)
p.s.f.ai.Log(logger.Debug, "creating segment %s", p.s.path)

err := os.MkdirAll(filepath.Dir(p.s.path), 0o755)
if err != nil {
Expand All @@ -67,7 +67,7 @@ func (p *formatFMP4Part) close() error {
return err
}

p.s.f.a.agent.OnSegmentCreate(p.s.path)
p.s.f.ai.agent.OnSegmentCreate(p.s.path)

err = writeInit(fi, p.s.f.tracks)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions internal/record/format_fmp4_segment.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ func (s *formatFMP4Segment) close() error {
}

if s.fi != nil {
s.f.a.agent.Log(logger.Debug, "closing segment %s", s.path)
s.f.ai.Log(logger.Debug, "closing segment %s", s.path)
err2 := s.fi.Close()
if err == nil {
err = err2
}

if err2 == nil {
duration := s.lastDTS - s.startDTS
s.f.a.agent.OnSegmentComplete(s.path, duration)
s.f.ai.agent.OnSegmentComplete(s.path, duration)
}
}

Expand All @@ -80,7 +80,7 @@ func (s *formatFMP4Segment) write(track *formatFMP4Track, sample *sample) error
}
s.curPart.initialize()
s.f.nextSequenceNumber++
} else if s.curPart.duration() >= s.f.a.agent.PartDuration {
} else if s.curPart.duration() >= s.f.ai.agent.PartDuration {
err := s.curPart.close()
s.curPart = nil

Expand Down
2 changes: 1 addition & 1 deletion internal/record/format_fmp4_track.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (t *formatFMP4Track) write(sample *sample) error {

if (!t.f.hasVideo || t.initTrack.Codec.IsVideo()) &&
!t.nextSample.IsNonSyncSample &&
(t.nextSample.dts-t.f.currentSegment.startDTS) >= t.f.a.agent.SegmentDuration {
(t.nextSample.dts-t.f.currentSegment.startDTS) >= t.f.ai.agent.SegmentDuration {
t.f.currentSegment.lastDTS = t.nextSample.dts
err := t.f.currentSegment.close()
if err != nil {
Expand Down
Loading

0 comments on commit 05716bc

Please sign in to comment.