Browse Source

support G711 tracks with multiple channels and custom sample rates (#2891)

pull/2907/head
Alessandro Ros 2 years ago committed by GitHub
parent
commit
20bb9b90cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      go.mod
  2. 4
      go.sum
  3. 18
      internal/formatprocessor/g711.go
  4. 10
      internal/formatprocessor/g711_test.go
  5. 36
      internal/protocols/rtmp/reader.go
  6. 12
      internal/protocols/rtmp/reader_test.go
  7. 10
      internal/protocols/webrtc/incoming_track.go
  8. 10
      internal/record/agent_test.go
  9. 4
      internal/record/format_fmp4.go
  10. 8
      internal/servers/webrtc/session.go

2
go.mod

@ -8,7 +8,7 @@ require (
github.com/alecthomas/kong v0.8.1 github.com/alecthomas/kong v0.8.1
github.com/aler9/writerseeker v1.1.0 github.com/aler9/writerseeker v1.1.0
github.com/bluenviron/gohlslib v1.2.0 github.com/bluenviron/gohlslib v1.2.0
github.com/bluenviron/gortsplib/v4 v4.6.3 github.com/bluenviron/gortsplib/v4 v4.6.4-0.20240108201647-63a81d0896f5
github.com/bluenviron/mediacommon v1.7.1 github.com/bluenviron/mediacommon v1.7.1
github.com/datarhei/gosrt v0.5.5 github.com/datarhei/gosrt v0.5.5
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0

4
go.sum

@ -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/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/bluenviron/gohlslib v1.2.0 h1:Hrx2/n/AcmKKIV+MjZLKs5kmW+O7xCdUSPJQoS39JKw= github.com/bluenviron/gohlslib v1.2.0 h1:Hrx2/n/AcmKKIV+MjZLKs5kmW+O7xCdUSPJQoS39JKw=
github.com/bluenviron/gohlslib v1.2.0/go.mod h1:kG/Sjebsxnf5asMGaGcQ0aSvtFGNChJPgctds2wDHOI= github.com/bluenviron/gohlslib v1.2.0/go.mod h1:kG/Sjebsxnf5asMGaGcQ0aSvtFGNChJPgctds2wDHOI=
github.com/bluenviron/gortsplib/v4 v4.6.3 h1:/FgUhxe/DGFkmwHNGNnw7NhcuZeSwnxCzYHDbyAtMjo= github.com/bluenviron/gortsplib/v4 v4.6.4-0.20240108201647-63a81d0896f5 h1:DU63YvVivfcIq7jcA1KCwi9YO+jEVglYNJCIrOLj8QE=
github.com/bluenviron/gortsplib/v4 v4.6.3/go.mod h1:UqdkRR5YvKHP/wHEQQySJFKJm6tIZcftdP7cNszIZ1g= github.com/bluenviron/gortsplib/v4 v4.6.4-0.20240108201647-63a81d0896f5/go.mod h1:UqdkRR5YvKHP/wHEQQySJFKJm6tIZcftdP7cNszIZ1g=
github.com/bluenviron/mediacommon v1.7.1 h1:7Lm2b8M9gUk3Ben1w+OPwadAeYseIBscwgdiL+aLtfw= github.com/bluenviron/mediacommon v1.7.1 h1:7Lm2b8M9gUk3Ben1w+OPwadAeYseIBscwgdiL+aLtfw=
github.com/bluenviron/mediacommon v1.7.1/go.mod h1:Ij/kE1LEucSjryNBVTyPL/gBI0d6/Css3f5PyrM957w= github.com/bluenviron/mediacommon v1.7.1/go.mod h1:Ij/kE1LEucSjryNBVTyPL/gBI0d6/Css3f5PyrM957w=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=

18
internal/formatprocessor/g711.go

@ -5,7 +5,7 @@ import (
"time" "time"
"github.com/bluenviron/gortsplib/v4/pkg/format" "github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio" "github.com/bluenviron/gortsplib/v4/pkg/format/rtplpcm"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/unit" "github.com/bluenviron/mediamtx/internal/unit"
@ -14,8 +14,8 @@ import (
type formatProcessorG711 struct { type formatProcessorG711 struct {
udpMaxPayloadSize int udpMaxPayloadSize int
format *format.G711 format *format.G711
encoder *rtpsimpleaudio.Encoder encoder *rtplpcm.Encoder
decoder *rtpsimpleaudio.Decoder decoder *rtplpcm.Decoder
} }
func newG711( func newG711(
@ -39,9 +39,11 @@ func newG711(
} }
func (t *formatProcessorG711) createEncoder() error { func (t *formatProcessorG711) createEncoder() error {
t.encoder = &rtpsimpleaudio.Encoder{ t.encoder = &rtplpcm.Encoder{
PayloadMaxSize: t.udpMaxPayloadSize - 12, PayloadMaxSize: t.udpMaxPayloadSize - 12,
PayloadType: t.format.PayloadType(), PayloadType: t.format.PayloadType(),
BitDepth: 8,
ChannelCount: t.format.ChannelCount,
} }
return t.encoder.Init() return t.encoder.Init()
} }
@ -49,14 +51,16 @@ func (t *formatProcessorG711) createEncoder() error {
func (t *formatProcessorG711) ProcessUnit(uu unit.Unit) error { //nolint:dupl func (t *formatProcessorG711) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.G711) u := uu.(*unit.G711)
pkt, err := t.encoder.Encode(u.Samples) pkts, err := t.encoder.Encode(u.Samples)
if err != nil { if err != nil {
return err return err
} }
u.RTPPackets = []*rtp.Packet{pkt} u.RTPPackets = pkts
ts := uint32(multiplyAndDivide(u.PTS, time.Duration(t.format.ClockRate()), time.Second)) ts := uint32(multiplyAndDivide(u.PTS, time.Duration(t.format.ClockRate()), time.Second))
u.RTPPackets[0].Timestamp += ts for _, pkt := range u.RTPPackets {
pkt.Timestamp += ts
}
return nil return nil
} }

10
internal/formatprocessor/g711_test.go

@ -12,7 +12,10 @@ import (
func TestG611Encode(t *testing.T) { func TestG611Encode(t *testing.T) {
t.Run("alaw", func(t *testing.T) { t.Run("alaw", func(t *testing.T) {
forma := &format.G711{ forma := &format.G711{
MULaw: false, PayloadTyp: 8,
MULaw: false,
SampleRate: 8000,
ChannelCount: 1,
} }
p, err := New(1472, forma, true) p, err := New(1472, forma, true)
@ -37,7 +40,10 @@ func TestG611Encode(t *testing.T) {
t.Run("mulaw", func(t *testing.T) { t.Run("mulaw", func(t *testing.T) {
forma := &format.G711{ forma := &format.G711{
MULaw: true, PayloadTyp: 0,
MULaw: true,
SampleRate: 8000,
ChannelCount: 1,
} }
p, err := New(1472, forma, true) p, err := New(1472, forma, true)

36
internal/protocols/rtmp/reader.go

@ -95,17 +95,9 @@ func hasAudio(md flvio.AMFMap, audioTrack *format.Format) (bool, error) {
return true, nil return true, nil
case message.CodecPCMA: case message.CodecPCMA:
v, ok := md.GetV("stereo")
if ok && v == true {
return false, fmt.Errorf("stereo PCMA is not supported")
}
return true, nil return true, nil
case message.CodecPCMU: case message.CodecPCMU:
v, ok := md.GetV("stereo")
if ok && v == true {
return false, fmt.Errorf("stereo PCMU is not supported")
}
return true, nil return true, nil
} }
@ -336,19 +328,31 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (format.Format, forma
} }
case msg.Codec == message.CodecPCMA: case msg.Codec == message.CodecPCMA:
if msg.Channels == message.ChannelsStereo { audioTrack = &format.G711{
return nil, nil, fmt.Errorf("stereo PCMA is not supported") PayloadTyp: 8,
MULaw: false,
SampleRate: 8000,
ChannelCount: func() int {
if msg.Channels == message.ChannelsStereo {
return 2
}
return 1
}(),
} }
audioTrack = &format.G711{MULaw: false}
case msg.Codec == message.CodecPCMU: case msg.Codec == message.CodecPCMU:
if msg.Channels == message.ChannelsStereo { audioTrack = &format.G711{
return nil, nil, fmt.Errorf("stereo PCMU is not supported") PayloadTyp: 0,
MULaw: true,
SampleRate: 8000,
ChannelCount: func() int {
if msg.Channels == message.ChannelsStereo {
return 2
}
return 1
}(),
} }
audioTrack = &format.G711{MULaw: true}
case msg.Codec == message.CodecLPCM: case msg.Codec == message.CodecLPCM:
audioTrack = &format.LPCM{ audioTrack = &format.LPCM{
PayloadTyp: 96, PayloadTyp: 96,

12
internal/protocols/rtmp/reader_test.go

@ -786,7 +786,12 @@ func TestReadTracks(t *testing.T) {
{ {
"pcma", "pcma",
nil, nil,
&format.G711{}, &format.G711{
PayloadTyp: 8,
MULaw: false,
SampleRate: 8000,
ChannelCount: 1,
},
[]message.Message{ []message.Message{
&message.DataAMF0{ &message.DataAMF0{
ChunkStreamID: 4, ChunkStreamID: 4,
@ -817,7 +822,10 @@ func TestReadTracks(t *testing.T) {
"pcmu", "pcmu",
nil, nil,
&format.G711{ &format.G711{
MULaw: true, PayloadTyp: 0,
MULaw: true,
SampleRate: 8000,
ChannelCount: 1,
}, },
[]message.Message{ []message.Message{
&message.DataAMF0{ &message.DataAMF0{

10
internal/protocols/webrtc/incoming_track.go

@ -80,12 +80,18 @@ func newIncomingTrack(
case strings.ToLower(webrtc.MimeTypePCMU): case strings.ToLower(webrtc.MimeTypePCMU):
t.format = &format.G711{ t.format = &format.G711{
MULaw: true, PayloadTyp: 0,
MULaw: true,
SampleRate: 8000,
ChannelCount: 1,
} }
case strings.ToLower(webrtc.MimeTypePCMA): case strings.ToLower(webrtc.MimeTypePCMA):
t.format = &format.G711{ t.format = &format.G711{
MULaw: false, PayloadTyp: 8,
MULaw: false,
SampleRate: 8000,
ChannelCount: 1,
} }
default: default:

10
internal/record/agent_test.go

@ -56,13 +56,19 @@ func TestAgent(t *testing.T) {
{ {
Type: description.MediaTypeAudio, Type: description.MediaTypeAudio,
Formats: []rtspformat.Format{&rtspformat.G711{ Formats: []rtspformat.Format{&rtspformat.G711{
MULaw: false, PayloadTyp: 8,
MULaw: false,
SampleRate: 8000,
ChannelCount: 1,
}}, }},
}, },
{ {
Type: description.MediaTypeAudio, Type: description.MediaTypeAudio,
Formats: []rtspformat.Format{&rtspformat.G711{ Formats: []rtspformat.Format{&rtspformat.G711{
MULaw: true, PayloadTyp: 0,
MULaw: true,
SampleRate: 8000,
ChannelCount: 1,
}}, }},
}, },
}} }}

4
internal/record/format_fmp4.go

@ -775,8 +775,8 @@ func (f *formatFMP4) initialize() {
codec := &fmp4.CodecLPCM{ codec := &fmp4.CodecLPCM{
LittleEndian: false, LittleEndian: false,
BitDepth: 16, BitDepth: 16,
SampleRate: 8000, SampleRate: forma.SampleRate,
ChannelCount: 1, ChannelCount: forma.ChannelCount,
} }
track := addTrack(codec) track := addTrack(codec)

8
internal/servers/webrtc/session.go

@ -244,6 +244,14 @@ func findAudioTrack(
if g711Format != nil { if g711Format != nil {
return g711Format, func(track *webrtc.OutgoingTrack) error { return g711Format, func(track *webrtc.OutgoingTrack) error {
if g711Format.SampleRate != 8000 {
return fmt.Errorf("unsupported G711 sample rate")
}
if g711Format.ChannelCount != 1 {
return fmt.Errorf("unsupported G711 channel count")
}
stream.AddReader(writer, media, g711Format, func(u unit.Unit) error { stream.AddReader(writer, media, g711Format, func(u unit.Unit) error {
for _, pkt := range u.GetRTPPackets() { for _, pkt := range u.GetRTPPackets() {
track.WriteRTP(pkt) //nolint:errcheck track.WriteRTP(pkt) //nolint:errcheck

Loading…
Cancel
Save