Skip to content

Commit

Permalink
support recording G711 tracks with fMP4 (#2853)
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 authored Dec 28, 2023
1 parent ef2b6c7 commit 698963a
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ And can be recorded with:

|format|video codecs|audio codecs|
|------|------------|------------|
|[fMP4](#record-streams-to-disk)|AV1, VP9, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, LPCM|
|[fMP4](#record-streams-to-disk)|AV1, VP9, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3, G711, LPCM|
|[MPEG-TS](#record-streams-to-disk)|H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), AC-3|

**Features**
Expand Down
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/aler9/writerseeker v1.1.0
github.com/bluenviron/gohlslib v1.1.0
github.com/bluenviron/gortsplib/v4 v4.6.2
github.com/bluenviron/mediacommon v1.6.0
github.com/bluenviron/mediacommon v1.6.1-0.20231228213201-7bb211dba7e1
github.com/datarhei/gosrt v0.5.5
github.com/fsnotify/fsnotify v1.7.0
github.com/gin-gonic/gin v1.9.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ github.com/bluenviron/gohlslib v1.1.0 h1:NL8CYg1BqHpy9tUugd/SbXH5p5vEi7Im/zaw0dp
github.com/bluenviron/gohlslib v1.1.0/go.mod h1:kG/Sjebsxnf5asMGaGcQ0aSvtFGNChJPgctds2wDHOI=
github.com/bluenviron/gortsplib/v4 v4.6.2 h1:CGIsxpnUFvSlIxnSFS0oFSSfwsHMmBCmYcrGAtIcwXc=
github.com/bluenviron/gortsplib/v4 v4.6.2/go.mod h1:dN1YjyPNMfy/NwC17Ga6MiIMiUoQfg5GL7LGsVHa0Jo=
github.com/bluenviron/mediacommon v1.6.0 h1:xJgwbOKKwxyyrEONnSb5J5Sq7NLjNhVQoR/5gs2IDdQ=
github.com/bluenviron/mediacommon v1.6.0/go.mod h1:Ij/kE1LEucSjryNBVTyPL/gBI0d6/Css3f5PyrM957w=
github.com/bluenviron/mediacommon v1.6.1-0.20231228213201-7bb211dba7e1 h1:f8XDAHvgPbT+n5qf73REdUo9kLmGpP4HNgphKI/8fGE=
github.com/bluenviron/mediacommon v1.6.1-0.20231228213201-7bb211dba7e1/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.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
Expand Down
107 changes: 107 additions & 0 deletions internal/formatprocessor/g711.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package formatprocessor //nolint:dupl

import (
"fmt"
"time"

"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpsimpleaudio"
"github.com/pion/rtp"

"github.com/bluenviron/mediamtx/internal/unit"
)

type formatProcessorG711 struct {
udpMaxPayloadSize int
format *format.G711
encoder *rtpsimpleaudio.Encoder
decoder *rtpsimpleaudio.Decoder
}

func newG711(
udpMaxPayloadSize int,
forma *format.G711,
generateRTPPackets bool,
) (*formatProcessorG711, error) {
t := &formatProcessorG711{
udpMaxPayloadSize: udpMaxPayloadSize,
format: forma,
}

if generateRTPPackets {
err := t.createEncoder()
if err != nil {
return nil, err
}
}

return t, nil
}

func (t *formatProcessorG711) createEncoder() error {
t.encoder = &rtpsimpleaudio.Encoder{
PayloadMaxSize: t.udpMaxPayloadSize - 12,
}
return t.encoder.Init()
}

func (t *formatProcessorG711) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.G711)

pkt, err := t.encoder.Encode(u.Samples)
if err != nil {
return err
}

ts := uint32(multiplyAndDivide(u.PTS, time.Duration(t.format.ClockRate()), time.Second))
pkt.Timestamp += ts

u.RTPPackets = []*rtp.Packet{pkt}

return nil
}

func (t *formatProcessorG711) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts time.Duration,
hasNonRTSPReaders bool,
) (Unit, error) {
u := &unit.G711{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}

// remove padding
pkt.Header.Padding = false
pkt.PaddingSize = 0

if pkt.MarshalSize() > t.udpMaxPayloadSize {
return nil, fmt.Errorf("payload size (%d) is greater than maximum allowed (%d)",
pkt.MarshalSize(), t.udpMaxPayloadSize)
}

// decode from RTP
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.format.CreateDecoder()
if err != nil {
return nil, err
}
}

samples, err := t.decoder.Decode(pkt)
if err != nil {
return nil, err
}

u.Samples = samples
}

// route packet as is
return u, nil
}
3 changes: 3 additions & 0 deletions internal/formatprocessor/processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ func New(
case *format.AC3:
return newAC3(udpMaxPayloadSize, forma, generateRTPPackets)

case *format.G711:
return newG711(udpMaxPayloadSize, forma, generateRTPPackets)

case *format.LPCM:
return newLPCM(udpMaxPayloadSize, forma, generateRTPPackets)

Expand Down
26 changes: 26 additions & 0 deletions internal/record/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ func TestAgent(t *testing.T) {
IndexDeltaLength: 3,
}},
},
{
Type: description.MediaTypeAudio,
Formats: []rtspformat.Format{&rtspformat.G711{
MULaw: false,
}},
},
{
Type: description.MediaTypeAudio,
Formats: []rtspformat.Format{&rtspformat.G711{
MULaw: true,
}},
},
}}

writeToStream := func(stream *stream.Stream) {
Expand Down Expand Up @@ -107,6 +119,20 @@ func TestAgent(t *testing.T) {
},
AUs: [][]byte{{1, 2, 3, 4}},
})

stream.WriteUnit(desc.Medias[3], desc.Medias[3].Formats[0], &unit.G711{
Base: unit.Base{
PTS: (50 + time.Duration(i)) * time.Second,
},
Samples: []byte{1, 2, 3, 4},
})

stream.WriteUnit(desc.Medias[4], desc.Medias[4].Formats[0], &unit.G711{
Base: unit.Base{
PTS: (50 + time.Duration(i)) * time.Second,
},
Samples: []byte{1, 2, 3, 4},
})
}
}

Expand Down
30 changes: 29 additions & 1 deletion internal/record/format_fmp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
rtspformat "github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/ac3"
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
"github.com/bluenviron/mediacommon/pkg/codecs/g711"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/bluenviron/mediacommon/pkg/codecs/jpeg"
Expand Down Expand Up @@ -771,7 +772,34 @@ func (f *formatFMP4) initialize() {
// TODO

case *rtspformat.G711:
// TODO
codec := &fmp4.CodecLPCM{
LittleEndian: false,
BitDepth: 16,
SampleRate: 8000,
ChannelCount: 1,
}
track := addTrack(codec)

f.a.agent.Stream.AddReader(f.a.writer, media, forma, func(u unit.Unit) error {
tunit := u.(*unit.G711)
if tunit.Samples == nil {
return nil
}

var out []byte
if forma.MULaw {
out = g711.DecodeMulaw(tunit.Samples)
} else {
out = g711.DecodeAlaw(tunit.Samples)
}

return track.record(&sample{
PartSample: &fmp4.PartSample{
Payload: out,
},
dts: tunit.PTS,
})
})

case *rtspformat.LPCM:
codec := &fmp4.CodecLPCM{
Expand Down
7 changes: 7 additions & 0 deletions internal/unit/g711.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package unit

// G711 is a G711 data unit.
type G711 struct {
Base
Samples []byte
}

0 comments on commit 698963a

Please sign in to comment.