Skip to content

Commit cc27cf6

Browse files
authored
mpegts, srt: support MPEG-4 Audio LATM tracks (#4403) (#4759)
1 parent d0a97e4 commit cc27cf6

File tree

19 files changed

+668
-76
lines changed

19 files changed

+668
-76
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ require (
1010
github.com/alecthomas/kong v1.12.0
1111
github.com/asticode/go-astits v1.13.0
1212
github.com/bluenviron/gohlslib/v2 v2.2.2
13-
github.com/bluenviron/gortsplib/v4 v4.15.0
13+
github.com/bluenviron/gortsplib/v4 v4.16.0
1414
github.com/bluenviron/mediacommon/v2 v2.4.0
1515
github.com/datarhei/gosrt v0.9.0
1616
github.com/fsnotify/fsnotify v1.9.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYh
3535
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
3636
github.com/bluenviron/gohlslib/v2 v2.2.2 h1:Q86VloPjwONKF8pu6jSEh9ENm4UzdMl5SzYvtjneL5k=
3737
github.com/bluenviron/gohlslib/v2 v2.2.2/go.mod h1:3Lby/VMDD/cN0B3uJPd3bEEiJZ34LqXs71FEvN/fq2k=
38-
github.com/bluenviron/gortsplib/v4 v4.15.0 h1:R5mimKNlmzpUqcAVfCqkSznGk/2hl4Kk9LPFo2KZJeU=
39-
github.com/bluenviron/gortsplib/v4 v4.15.0/go.mod h1:mqAxRuombKOUHREiKuKJ4VBjEC4U7VeMar4/G4Sbq04=
38+
github.com/bluenviron/gortsplib/v4 v4.16.0 h1:qzJxlZXCv11oxNkNTAFMaeX0uEXJE0L6lDv3CKUYT/k=
39+
github.com/bluenviron/gortsplib/v4 v4.16.0/go.mod h1:pcSNf/GToNEwdWy74moR4Tp5JWIEDJ0d9CzCSUPkiwM=
4040
github.com/bluenviron/mediacommon/v2 v2.4.0 h1:Ss1T7AMxTrICJ+a/N5urS/1lp1ZpsF+3iJq3B/RLDMw=
4141
github.com/bluenviron/mediacommon/v2 v2.4.0/go.mod h1:a6MbPmXtYda9mKibKVMZlW20GYLLrX2R7ZkUE+1pwV0=
4242
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package formatprocessor
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"time"
7+
8+
"github.com/bluenviron/gortsplib/v4/pkg/format"
9+
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpfragmented"
10+
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4audio"
11+
"github.com/pion/rtp"
12+
13+
"github.com/bluenviron/mediamtx/internal/logger"
14+
"github.com/bluenviron/mediamtx/internal/unit"
15+
)
16+
17+
type mpeg4AudioLATM struct {
18+
RTPMaxPayloadSize int
19+
Format *format.MPEG4AudioLATM
20+
GenerateRTPPackets bool
21+
Parent logger.Writer
22+
23+
encoder *rtpfragmented.Encoder
24+
decoder *rtpfragmented.Decoder
25+
randomStart uint32
26+
}
27+
28+
func (t *mpeg4AudioLATM) initialize() error {
29+
if t.GenerateRTPPackets {
30+
err := t.createEncoder()
31+
if err != nil {
32+
return err
33+
}
34+
35+
t.randomStart, err = randUint32()
36+
if err != nil {
37+
return err
38+
}
39+
}
40+
41+
return nil
42+
}
43+
44+
func (t *mpeg4AudioLATM) createEncoder() error {
45+
t.encoder = &rtpfragmented.Encoder{
46+
PayloadMaxSize: t.RTPMaxPayloadSize,
47+
PayloadType: t.Format.PayloadTyp,
48+
}
49+
return t.encoder.Init()
50+
}
51+
52+
func (t *mpeg4AudioLATM) ProcessUnit(uu unit.Unit) error { //nolint:dupl
53+
u := uu.(*unit.MPEG4AudioLATM)
54+
55+
pkts, err := t.encoder.Encode(u.Element)
56+
if err != nil {
57+
return err
58+
}
59+
u.RTPPackets = pkts
60+
61+
for _, pkt := range u.RTPPackets {
62+
pkt.Timestamp += t.randomStart + uint32(u.PTS)
63+
}
64+
65+
return nil
66+
}
67+
68+
func (t *mpeg4AudioLATM) ProcessRTPPacket( //nolint:dupl
69+
pkt *rtp.Packet,
70+
ntp time.Time,
71+
pts int64,
72+
hasNonRTSPReaders bool,
73+
) (unit.Unit, error) {
74+
u := &unit.MPEG4AudioLATM{
75+
Base: unit.Base{
76+
RTPPackets: []*rtp.Packet{pkt},
77+
NTP: ntp,
78+
PTS: pts,
79+
},
80+
}
81+
82+
// remove padding
83+
pkt.Padding = false
84+
pkt.PaddingSize = 0
85+
86+
if len(pkt.Payload) > t.RTPMaxPayloadSize {
87+
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
88+
len(pkt.Payload), t.RTPMaxPayloadSize)
89+
}
90+
91+
// decode from RTP
92+
if hasNonRTSPReaders || t.decoder != nil {
93+
if t.decoder == nil {
94+
var err error
95+
t.decoder, err = t.Format.CreateDecoder()
96+
if err != nil {
97+
return nil, err
98+
}
99+
}
100+
101+
el, err := t.decoder.Decode(pkt)
102+
if err != nil {
103+
if errors.Is(err, rtpmpeg4audio.ErrMorePacketsNeeded) {
104+
return u, nil
105+
}
106+
return nil, err
107+
}
108+
109+
u.Element = el
110+
}
111+
112+
// route packet as is
113+
return u, nil
114+
}

internal/formatprocessor/mpeg4_video.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"time"
88

99
"github.com/bluenviron/gortsplib/v4/pkg/format"
10-
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpmpeg4video"
10+
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpfragmented"
1111
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4video"
1212
"github.com/pion/rtp"
1313

@@ -33,8 +33,8 @@ type mpeg4Video struct {
3333
GenerateRTPPackets bool
3434
Parent logger.Writer
3535

36-
encoder *rtpmpeg4video.Encoder
37-
decoder *rtpmpeg4video.Decoder
36+
encoder *rtpfragmented.Encoder
37+
decoder *rtpfragmented.Decoder
3838
randomStart uint32
3939
}
4040

@@ -55,7 +55,7 @@ func (t *mpeg4Video) initialize() error {
5555
}
5656

5757
func (t *mpeg4Video) createEncoder() error {
58-
t.encoder = &rtpmpeg4video.Encoder{
58+
t.encoder = &rtpfragmented.Encoder{
5959
PayloadMaxSize: t.RTPMaxPayloadSize,
6060
PayloadType: t.Format.PayloadTyp,
6161
}
@@ -154,7 +154,7 @@ func (t *mpeg4Video) ProcessRTPPacket( //nolint:dupl
154154

155155
frame, err := t.decoder.Decode(pkt)
156156
if err != nil {
157-
if errors.Is(err, rtpmpeg4video.ErrMorePacketsNeeded) {
157+
if errors.Is(err, rtpfragmented.ErrMorePacketsNeeded) {
158158
return u, nil
159159
}
160160
return nil, err

internal/formatprocessor/processor.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ func New(
135135
Parent: parent,
136136
}
137137

138+
case *format.MPEG4AudioLATM:
139+
proc = &mpeg4AudioLATM{
140+
RTPMaxPayloadSize: rtpMaxPayloadSize,
141+
Format: forma,
142+
GenerateRTPPackets: generateRTPPackets,
143+
Parent: parent,
144+
}
145+
138146
case *format.MPEG1Audio:
139147
proc = &mpeg1Audio{
140148
RTPMaxPayloadSize: rtpMaxPayloadSize,

internal/protocols/hls/from_stream.go

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/bluenviron/gohlslib/v2/pkg/codecs"
1010
"github.com/bluenviron/gortsplib/v4/pkg/description"
1111
"github.com/bluenviron/gortsplib/v4/pkg/format"
12+
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
1213
"github.com/bluenviron/mediamtx/internal/logger"
1314
"github.com/bluenviron/mediamtx/internal/stream"
1415
"github.com/bluenviron/mediamtx/internal/unit"
@@ -233,11 +234,41 @@ func setupAudioTracks(
233234
})
234235

235236
case *format.MPEG4Audio:
236-
co := forma.GetConfig()
237-
if co != nil {
237+
track := &gohlslib.Track{
238+
Codec: &codecs.MPEG4Audio{
239+
Config: *forma.Config,
240+
},
241+
ClockRate: forma.ClockRate(),
242+
}
243+
244+
addTrack(
245+
media,
246+
forma,
247+
track,
248+
func(u unit.Unit) error {
249+
tunit := u.(*unit.MPEG4Audio)
250+
251+
if tunit.AUs == nil {
252+
return nil
253+
}
254+
255+
err := muxer.WriteMPEG4Audio(
256+
track,
257+
tunit.NTP,
258+
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
259+
tunit.AUs)
260+
if err != nil {
261+
return fmt.Errorf("muxer error: %w", err)
262+
}
263+
264+
return nil
265+
})
266+
267+
case *format.MPEG4AudioLATM:
268+
if !forma.CPresent {
238269
track := &gohlslib.Track{
239270
Codec: &codecs.MPEG4Audio{
240-
Config: *co,
271+
Config: *forma.StreamMuxConfig.Programs[0].Layers[0].AudioSpecificConfig,
241272
},
242273
ClockRate: forma.ClockRate(),
243274
}
@@ -247,22 +278,24 @@ func setupAudioTracks(
247278
forma,
248279
track,
249280
func(u unit.Unit) error {
250-
tunit := u.(*unit.MPEG4Audio)
281+
tunit := u.(*unit.MPEG4AudioLATM)
251282

252-
if tunit.AUs == nil {
283+
if tunit.Element == nil {
253284
return nil
254285
}
255286

256-
err := muxer.WriteMPEG4Audio(
257-
track,
258-
tunit.NTP,
259-
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
260-
tunit.AUs)
287+
var ame mpeg4audio.AudioMuxElement
288+
ame.StreamMuxConfig = forma.StreamMuxConfig
289+
err := ame.Unmarshal(tunit.Element)
261290
if err != nil {
262-
return fmt.Errorf("muxer error: %w", err)
291+
return err
263292
}
264293

265-
return nil
294+
return muxer.WriteMPEG4Audio(
295+
track,
296+
tunit.NTP,
297+
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
298+
[][]byte{ame.Payloads[0][0][0]})
266299
})
267300
}
268301
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package mpegts
2+
3+
import (
4+
"io"
5+
6+
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
7+
mcmpegts "github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts"
8+
"github.com/bluenviron/mediacommon/v2/pkg/rewindablereader"
9+
)
10+
11+
// EnhancedReader is a mpegts.Reader wrapper
12+
// That provides additional informations that are needed in order
13+
// to perform conversion to RTSP.
14+
type EnhancedReader struct {
15+
R io.Reader
16+
17+
*mcmpegts.Reader
18+
19+
latmConfigs map[uint16]*mpeg4audio.StreamMuxConfig
20+
}
21+
22+
// Initialize initializes EnhancedReader.
23+
func (r *EnhancedReader) Initialize() error {
24+
rr := &rewindablereader.Reader{R: r.R}
25+
mr := &mcmpegts.Reader{R: rr}
26+
err := mr.Initialize()
27+
if err != nil {
28+
return err
29+
}
30+
31+
r.latmConfigs = make(map[uint16]*mpeg4audio.StreamMuxConfig)
32+
tracksToParse := 0
33+
34+
for _, track := range mr.Tracks() {
35+
if _, ok := track.Codec.(*mcmpegts.CodecMPEG4AudioLATM); ok {
36+
cpid := track.PID
37+
done := false
38+
tracksToParse++
39+
40+
mr.OnDataMPEG4AudioLATM(track, func(_ int64, els [][]byte) error {
41+
if done {
42+
return nil
43+
}
44+
45+
var ame mpeg4audio.AudioMuxElement
46+
ame.MuxConfigPresent = true
47+
err2 := ame.Unmarshal(els[0])
48+
if err2 != nil {
49+
return nil //nolint:nilerr
50+
}
51+
52+
if ame.MuxConfigPresent {
53+
r.latmConfigs[cpid] = ame.StreamMuxConfig
54+
tracksToParse--
55+
done = true
56+
}
57+
58+
return nil
59+
})
60+
}
61+
}
62+
63+
for tracksToParse > 0 {
64+
err = mr.Read()
65+
if err != nil {
66+
return err
67+
}
68+
}
69+
70+
rr.Rewind()
71+
r.Reader = &mcmpegts.Reader{R: rr}
72+
err = r.Reader.Initialize()
73+
if err != nil {
74+
return err
75+
}
76+
77+
return err
78+
}

0 commit comments

Comments
 (0)