rewrite Unit definition (#5079)
Some checks are pending
code_lint / go (push) Waiting to run
code_lint / go_mod (push) Waiting to run
code_lint / docs (push) Waiting to run
code_lint / api_docs (push) Waiting to run
code_test / test_64 (push) Waiting to run
code_test / test_32 (push) Waiting to run
code_test / test_e2e (push) Waiting to run

Stream units now share the same struct, with a specialized payload.
This commit is contained in:
Alessandro Ros 2025-10-11 12:18:51 +02:00 committed by GitHub
parent e2294836f5
commit f5f03562d3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
86 changed files with 1100 additions and 1602 deletions

View file

@ -3,11 +3,9 @@ package codecprocessor
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpac3"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -47,10 +45,8 @@ func (t *ac3) createEncoder() error {
return t.encoder.Init()
}
func (t *ac3) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.AC3)
pkts, err := t.encoder.Encode(u.Frames)
func (t *ac3) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadAC3))
if err != nil {
return err
}
@ -64,25 +60,17 @@ func (t *ac3) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *ac3) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.AC3{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -92,7 +80,7 @@ func (t *ac3) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -100,14 +88,13 @@ func (t *ac3) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpac3.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpac3.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frames = frames
u.Payload = unit.PayloadAC3(frames)
}
// route packet as is
return u, nil
return nil
}

View file

@ -3,12 +3,10 @@ package codecprocessor //nolint:dupl
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpav1"
mcav1 "github.com/bluenviron/mediacommon/v2/pkg/codecs/av1"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -49,7 +47,7 @@ func (t *av1) createEncoder() error {
return t.encoder.Init()
}
func (t *av1) remuxTemporalUnit(tu [][]byte) [][]byte {
func (t *av1) remuxTemporalUnit(tu unit.PayloadAV1) unit.PayloadAV1 {
n := 0
for _, obu := range tu {
@ -82,12 +80,10 @@ func (t *av1) remuxTemporalUnit(tu [][]byte) [][]byte {
return filteredTU
}
func (t *av1) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.AV1)
func (t *av1) ProcessUnit(u *unit.Unit) error { //nolint:dupl
u.Payload = t.remuxTemporalUnit(u.Payload.(unit.PayloadAV1))
u.TU = t.remuxTemporalUnit(u.TU)
pkts, err := t.encoder.Encode(u.TU)
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadAV1))
if err != nil {
return err
}
@ -101,25 +97,17 @@ func (t *av1) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *av1) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.AV1{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -129,7 +117,7 @@ func (t *av1) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -137,14 +125,13 @@ func (t *av1) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpav1.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpav1.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.TU = t.remuxTemporalUnit(tu)
u.Payload = t.remuxTemporalUnit(tu)
}
// route packet as is
return u, nil
return nil
}

View file

@ -15,11 +15,9 @@ func TestAV1RemoveTUD(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
u := &unit.AV1{
Base: unit.Base{
u := &unit.Unit{
PTS: 30000,
},
TU: [][]byte{
Payload: unit.PayloadAV1{
{byte(mcav1.OBUTypeTemporalDelimiter) << 3},
{5},
},
@ -28,7 +26,7 @@ func TestAV1RemoveTUD(t *testing.T) {
err = p.ProcessUnit(u)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadAV1{
{5},
}, u.TU)
}, u.Payload)
}

View file

@ -2,11 +2,9 @@ package codecprocessor //nolint:dupl
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtplpcm"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -49,10 +47,8 @@ func (t *g711) createEncoder() error {
return t.encoder.Init()
}
func (t *g711) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.G711)
pkts, err := t.encoder.Encode(u.Samples)
func (t *g711) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadG711))
if err != nil {
return err
}
@ -66,25 +62,17 @@ func (t *g711) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *g711) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.G711{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -94,18 +82,17 @@ func (t *g711) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
samples, err := t.decoder.Decode(pkt)
if err != nil {
return nil, err
return err
}
u.Samples = samples
u.Payload = unit.PayloadG711(samples)
}
// route packet as is
return u, nil
return nil
}

View file

@ -21,8 +21,8 @@ func TestG711ProcessUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
unit := &unit.G711{
Samples: []byte{1, 2, 3, 4},
unit := &unit.Unit{
Payload: unit.PayloadG711{1, 2, 3, 4},
}
err = p.ProcessUnit(unit)
@ -50,8 +50,8 @@ func TestG711ProcessUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
unit := &unit.G711{
Samples: []byte{1, 2, 3, 4},
unit := &unit.Unit{
Payload: unit.PayloadG711{1, 2, 3, 4},
}
err = p.ProcessUnit(unit)

View file

@ -2,10 +2,8 @@ package codecprocessor
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -26,32 +24,24 @@ func (t *generic) initialize() error {
return nil
}
func (t *generic) ProcessUnit(_ unit.Unit) error {
func (t *generic) ProcessUnit(_ *unit.Unit) error {
return fmt.Errorf("using a generic unit without RTP is not supported")
}
func (t *generic) ProcessRTPPacket(
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
_ bool,
) (unit.Unit, error) {
u := &unit.Generic{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
return u, nil
return nil
}

View file

@ -2,9 +2,9 @@ package codecprocessor
import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/mediamtx/internal/unit"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@ -34,7 +34,8 @@ func TestGenericProcessRTPPacket(t *testing.T) {
PaddingSize: 20,
}
_, err = p.ProcessRTPPacket(pkt, time.Time{}, 0, false)
u := &unit.Unit{RTPPackets: []*rtp.Packet{pkt}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
// check that padding has been removed

View file

@ -3,12 +3,10 @@ package codecprocessor
import (
"bytes"
"errors"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtph264"
mch264 "github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -138,7 +136,7 @@ func (t *h264) updateTrackParametersFromRTPPacket(payload []byte) {
}
}
func (t *h264) updateTrackParametersFromAU(au [][]byte) {
func (t *h264) updateTrackParametersFromAU(au unit.PayloadH264) {
sps := t.Format.SPS
pps := t.Format.PPS
update := false
@ -166,7 +164,7 @@ func (t *h264) updateTrackParametersFromAU(au [][]byte) {
}
}
func (t *h264) remuxAccessUnit(au [][]byte) [][]byte {
func (t *h264) remuxAccessUnit(au unit.PayloadH264) unit.PayloadH264 {
isKeyFrame := false
n := 0
@ -224,14 +222,12 @@ func (t *h264) remuxAccessUnit(au [][]byte) [][]byte {
return filteredAU
}
func (t *h264) ProcessUnit(uu unit.Unit) error {
u := uu.(*unit.H264)
func (t *h264) ProcessUnit(u *unit.Unit) error {
t.updateTrackParametersFromAU(u.Payload.(unit.PayloadH264))
u.Payload = t.remuxAccessUnit(u.Payload.(unit.PayloadH264))
t.updateTrackParametersFromAU(u.AU)
u.AU = t.remuxAccessUnit(u.AU)
if u.AU != nil {
pkts, err := t.encoder.Encode(u.AU)
if !u.NilPayload() {
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadH264))
if err != nil {
return err
}
@ -246,18 +242,10 @@ func (t *h264) ProcessUnit(uu unit.Unit) error {
}
func (t *h264) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.H264{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
t.updateTrackParametersFromRTPPacket(pkt.Payload)
@ -274,7 +262,7 @@ func (t *h264) ProcessRTPPacket( //nolint:dupl
v2 := pkt.SequenceNumber
err := t.createEncoder(&v1, &v2)
if err != nil {
return nil, err
return err
}
}
}
@ -285,7 +273,7 @@ func (t *h264) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -298,24 +286,24 @@ func (t *h264) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtph264.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtph264.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.AU = t.remuxAccessUnit(au)
u.Payload = t.remuxAccessUnit(au)
}
// route packet as is
if t.encoder == nil {
return u, nil
return nil
}
// encode into RTP
if len(u.AU) != 0 {
pkts, err := t.encoder.Encode(u.AU)
if !u.NilPayload() {
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadH264))
if err != nil {
return nil, err
return err
}
u.RTPPackets = pkts
@ -324,5 +312,5 @@ func (t *h264) ProcessRTPPacket( //nolint:dupl
}
}
return u, nil
return nil
}

View file

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"testing"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
mch264 "github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
@ -34,11 +33,9 @@ func TestH264RemoveAUD(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
u := &unit.H264{
Base: unit.Base{
u := &unit.Unit{
PTS: 30000,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{9, 24}, // AUD
{5, 1}, // IDR
},
@ -47,9 +44,9 @@ func TestH264RemoveAUD(t *testing.T) {
err = p.ProcessUnit(u)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH264{
{5, 1}, // IDR
}, u.AU)
}, u.Payload)
}
func TestH264AddParams(t *testing.T) {
@ -58,11 +55,9 @@ func TestH264AddParams(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
u1 := &unit.H264{
Base: unit.Base{
u1 := &unit.Unit{
PTS: 30000,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{7, 4, 5, 6}, // SPS
{8, 1}, // PPS
{5, 1}, // IDR
@ -72,17 +67,15 @@ func TestH264AddParams(t *testing.T) {
err = p.ProcessUnit(u1)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH264{
{7, 4, 5, 6}, // SPS
{8, 1}, // PPS
{5, 1}, // IDR
}, u1.AU)
}, u1.Payload)
u2 := &unit.H264{
Base: unit.Base{
u2 := &unit.Unit{
PTS: 30000 * 2,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2}, // IDR
},
}
@ -95,11 +88,11 @@ func TestH264AddParams(t *testing.T) {
require.Equal(t, []byte{8, 1}, forma.PPS)
// test that params have been added to the frame
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH264{
{7, 4, 5, 6}, // SPS
{8, 1}, // PPS
{5, 2}, // IDR
}, u2.AU)
}, u2.Payload)
// test that timestamp has increased
require.Equal(t, u1.RTPPackets[0].Timestamp+30000, u2.RTPPackets[0].Timestamp)
@ -114,8 +107,8 @@ func TestH264ProcessEmptyUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
unit := &unit.H264{
AU: [][]byte{
unit := &unit.Unit{
Payload: unit.PayloadH264{
{0x07, 0x01, 0x02, 0x03}, // SPS
{0x08, 0x01, 0x02}, // PPS
},
@ -145,24 +138,27 @@ func TestH264RTPExtractParams(t *testing.T) {
pkts, err := enc.Encode([][]byte{{byte(mch264.NALUTypeIDR)}})
require.NoError(t, err)
data, err := p.ProcessRTPPacket(pkts[0], time.Time{}, 0, true)
u := &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, true)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH264{
{byte(mch264.NALUTypeIDR)},
}, data.(*unit.H264).AU)
}, u.Payload)
if ca == "standard" {
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}) // SPS
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
pkts, err = enc.Encode([][]byte{{8, 1}}) // PPS
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
} else {
pkts, err = enc.Encode([][]byte{
@ -171,7 +167,8 @@ func TestH264RTPExtractParams(t *testing.T) {
})
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
}
@ -181,14 +178,15 @@ func TestH264RTPExtractParams(t *testing.T) {
pkts, err = enc.Encode([][]byte{{byte(mch264.NALUTypeIDR)}})
require.NoError(t, err)
data, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, true)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, true)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH264{
{0x07, 4, 5, 6},
{0x08, 1},
{byte(mch264.NALUTypeIDR)},
}, data.(*unit.H264).AU)
}, u.Payload)
})
}
}
@ -250,11 +248,11 @@ func TestH264RTPOversized(t *testing.T) {
Payload: []byte{0x1c, 0b01000000, 0x01, 0x02, 0x03, 0x04},
},
} {
var data unit.Unit
data, err = p.ProcessRTPPacket(pkt, time.Time{}, 0, false)
u := &unit.Unit{RTPPackets: []*rtp.Packet{pkt}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
out = append(out, data.GetRTPPackets()...)
out = append(out, u.RTPPackets...)
}
require.Equal(t, []*rtp.Packet{

View file

@ -3,12 +3,10 @@ package codecprocessor
import (
"bytes"
"errors"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtph265"
mch265 "github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -162,7 +160,7 @@ func (t *h265) updateTrackParametersFromRTPPacket(payload []byte) {
}
}
func (t *h265) updateTrackParametersFromAU(au [][]byte) {
func (t *h265) updateTrackParametersFromAU(au unit.PayloadH265) {
vps := t.Format.VPS
sps := t.Format.SPS
pps := t.Format.PPS
@ -197,7 +195,7 @@ func (t *h265) updateTrackParametersFromAU(au [][]byte) {
}
}
func (t *h265) remuxAccessUnit(au [][]byte) [][]byte {
func (t *h265) remuxAccessUnit(au unit.PayloadH265) unit.PayloadH265 {
isKeyFrame := false
n := 0
@ -256,14 +254,12 @@ func (t *h265) remuxAccessUnit(au [][]byte) [][]byte {
return filteredAU
}
func (t *h265) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.H265)
func (t *h265) ProcessUnit(u *unit.Unit) error { //nolint:dupl
t.updateTrackParametersFromAU(u.Payload.(unit.PayloadH265))
u.Payload = t.remuxAccessUnit(u.Payload.(unit.PayloadH265))
t.updateTrackParametersFromAU(u.AU)
u.AU = t.remuxAccessUnit(u.AU)
if u.AU != nil {
pkts, err := t.encoder.Encode(u.AU)
if !u.NilPayload() {
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadH265))
if err != nil {
return err
}
@ -278,18 +274,10 @@ func (t *h265) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *h265) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.H265{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
t.updateTrackParametersFromRTPPacket(pkt.Payload)
@ -306,7 +294,7 @@ func (t *h265) ProcessRTPPacket( //nolint:dupl
v2 := pkt.SequenceNumber
err := t.createEncoder(&v1, &v2)
if err != nil {
return nil, err
return err
}
}
}
@ -317,7 +305,7 @@ func (t *h265) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -330,24 +318,24 @@ func (t *h265) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtph265.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtph265.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.AU = t.remuxAccessUnit(au)
u.Payload = t.remuxAccessUnit(au)
}
// route packet as is
if t.encoder == nil {
return u, nil
return nil
}
// encode into RTP
if len(u.AU) != 0 {
pkts, err := t.encoder.Encode(u.AU)
if !u.NilPayload() {
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadH265))
if err != nil {
return nil, err
return err
}
u.RTPPackets = pkts
@ -356,5 +344,5 @@ func (t *h265) ProcessRTPPacket( //nolint:dupl
}
}
return u, nil
return nil
}

View file

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"testing"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
mch265 "github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
@ -21,11 +20,9 @@ func TestH265RemoveAUD(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
u := &unit.H265{
Base: unit.Base{
u := &unit.Unit{
PTS: 30000,
},
AU: [][]byte{
Payload: unit.PayloadH265{
{byte(mch265.NALUType_AUD_NUT) << 1, 0},
{byte(mch265.NALUType_CRA_NUT) << 1, 0},
},
@ -34,9 +31,9 @@ func TestH265RemoveAUD(t *testing.T) {
err = p.ProcessUnit(u)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH265{
{byte(mch265.NALUType_CRA_NUT) << 1, 0},
}, u.AU)
}, u.Payload)
}
func TestH265AddParams(t *testing.T) {
@ -45,11 +42,9 @@ func TestH265AddParams(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
u1 := &unit.H265{
Base: unit.Base{
u1 := &unit.Unit{
PTS: 30000,
},
AU: [][]byte{
Payload: unit.PayloadH265{
{byte(mch265.NALUType_VPS_NUT) << 1, 1, 2, 3},
{byte(mch265.NALUType_SPS_NUT) << 1, 4, 5, 6},
{byte(mch265.NALUType_PPS_NUT) << 1, 7, 8, 9},
@ -60,18 +55,16 @@ func TestH265AddParams(t *testing.T) {
err = p.ProcessUnit(u1)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH265{
{byte(mch265.NALUType_VPS_NUT) << 1, 1, 2, 3},
{byte(mch265.NALUType_SPS_NUT) << 1, 4, 5, 6},
{byte(mch265.NALUType_PPS_NUT) << 1, 7, 8, 9},
{byte(mch265.NALUType_CRA_NUT) << 1, 0},
}, u1.AU)
}, u1.Payload)
u2 := &unit.H265{
Base: unit.Base{
u2 := &unit.Unit{
PTS: 30000 * 2,
},
AU: [][]byte{
Payload: unit.PayloadH265{
{byte(mch265.NALUType_CRA_NUT) << 1, 1},
},
}
@ -85,12 +78,12 @@ func TestH265AddParams(t *testing.T) {
require.Equal(t, []byte{byte(mch265.NALUType_PPS_NUT) << 1, 7, 8, 9}, forma.PPS)
// test that params have been added to the frame
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH265{
{byte(mch265.NALUType_VPS_NUT) << 1, 1, 2, 3},
{byte(mch265.NALUType_SPS_NUT) << 1, 4, 5, 6},
{byte(mch265.NALUType_PPS_NUT) << 1, 7, 8, 9},
{byte(mch265.NALUType_CRA_NUT) << 1, 1},
}, u2.AU)
}, u2.Payload)
// test that timestamp has increased
require.Equal(t, u1.RTPPackets[0].Timestamp+30000, u2.RTPPackets[0].Timestamp)
@ -104,8 +97,8 @@ func TestH265ProcessEmptyUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
unit := &unit.H265{
AU: [][]byte{
unit := &unit.Unit{
Payload: unit.PayloadH265{
{byte(mch265.NALUType_VPS_NUT) << 1, 10, 11, 12}, // VPS
{byte(mch265.NALUType_SPS_NUT) << 1, 13, 14, 15}, // SPS
{byte(mch265.NALUType_PPS_NUT) << 1, 16, 17, 18}, // PPS
@ -135,30 +128,34 @@ func TestH265RTPExtractParams(t *testing.T) {
pkts, err := enc.Encode([][]byte{{byte(mch265.NALUType_CRA_NUT) << 1, 0}})
require.NoError(t, err)
data, err := p.ProcessRTPPacket(pkts[0], time.Time{}, 0, true)
u := &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, true)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH265{
{byte(mch265.NALUType_CRA_NUT) << 1, 0},
}, data.(*unit.H265).AU)
}, u.Payload)
if ca == "standard" {
pkts, err = enc.Encode([][]byte{{byte(mch265.NALUType_VPS_NUT) << 1, 1, 2, 3}})
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
pkts, err = enc.Encode([][]byte{{byte(mch265.NALUType_SPS_NUT) << 1, 4, 5, 6}})
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
pkts, err = enc.Encode([][]byte{{byte(mch265.NALUType_PPS_NUT) << 1, 7, 8, 9}})
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
} else {
pkts, err = enc.Encode([][]byte{
@ -168,7 +165,8 @@ func TestH265RTPExtractParams(t *testing.T) {
})
require.NoError(t, err)
_, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, false)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
}
@ -179,15 +177,16 @@ func TestH265RTPExtractParams(t *testing.T) {
pkts, err = enc.Encode([][]byte{{byte(mch265.NALUType_CRA_NUT) << 1, 0}})
require.NoError(t, err)
data, err = p.ProcessRTPPacket(pkts[0], time.Time{}, 0, true)
u = &unit.Unit{RTPPackets: []*rtp.Packet{pkts[0]}}
err = p.ProcessRTPPacket(u, true)
require.NoError(t, err)
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH265{
{byte(mch265.NALUType_VPS_NUT) << 1, 1, 2, 3},
{byte(mch265.NALUType_SPS_NUT) << 1, 4, 5, 6},
{byte(mch265.NALUType_PPS_NUT) << 1, 7, 8, 9},
{byte(mch265.NALUType_CRA_NUT) << 1, 0},
}, data.(*unit.H265).AU)
}, u.Payload)
})
}
}
@ -237,11 +236,11 @@ func TestH265RTPOversized(t *testing.T) {
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 2000/4),
},
} {
var data unit.Unit
data, err = p.ProcessRTPPacket(pkt, time.Time{}, 0, false)
u := &unit.Unit{RTPPackets: []*rtp.Packet{pkt}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
out = append(out, data.GetRTPPackets()...)
out = append(out, u.RTPPackets...)
}
require.Equal(t, []*rtp.Packet{

View file

@ -2,11 +2,9 @@ package codecprocessor
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpklv"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -47,11 +45,7 @@ func (t *klv) createEncoder() error {
return t.encoder.Init()
}
func (t *klv) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.KLV)
if u.Unit != nil {
// ensure the format processor's encoder is initialized
func (t *klv) ProcessUnit(u *unit.Unit) error { //nolint:dupl
if t.encoder == nil {
err := t.createEncoder()
if err != nil {
@ -59,7 +53,7 @@ func (t *klv) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
}
pkts, err := t.encoder.Encode(u.Unit)
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadKLV))
if err != nil {
return err
}
@ -68,31 +62,22 @@ func (t *klv) ProcessUnit(uu unit.Unit) error { //nolint:dupl
for _, pkt := range u.RTPPackets {
pkt.Timestamp += t.randomStart + uint32(u.PTS)
}
}
return nil
}
func (t *klv) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.KLV{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -102,18 +87,17 @@ func (t *klv) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
unit, err := t.decoder.Decode(pkt)
un, err := t.decoder.Decode(pkt)
if err != nil {
return nil, err
return err
}
u.Unit = unit
u.Payload = unit.PayloadKLV(un)
}
// route packet as is
return u, nil
return nil
}

View file

@ -2,7 +2,6 @@ package codecprocessor
import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/mediamtx/internal/unit"
@ -30,15 +29,11 @@ func TestKlvProcessUnit(t *testing.T) {
require.NoError(t, err)
// create test Unit
theTime := time.Now()
when := int64(5000000000) // 5 seconds in nanoseconds
u := unit.KLV{
Base: unit.Base{
u := unit.Unit{
RTPPackets: nil,
NTP: theTime,
PTS: when,
},
Unit: []byte{1, 2, 3, 4},
Payload: unit.PayloadKLV{1, 2, 3, 4},
}
uu := &u
@ -67,7 +62,8 @@ func TestKlvProcessRTPPacket(t *testing.T) {
Payload: []byte{1, 2, 3, 4},
PaddingSize: 20,
}
_, err = p.ProcessRTPPacket(pkt, time.Time{}, 0, false)
u := &unit.Unit{RTPPackets: []*rtp.Packet{pkt}}
err = p.ProcessRTPPacket(u, false)
require.NoError(t, err)
require.Equal(t, &rtp.Packet{

View file

@ -2,11 +2,9 @@ package codecprocessor //nolint:dupl
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtplpcm"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -49,10 +47,8 @@ func (t *lpcm) createEncoder() error {
return t.encoder.Init()
}
func (t *lpcm) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.LPCM)
pkts, err := t.encoder.Encode(u.Samples)
func (t *lpcm) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadLPCM))
if err != nil {
return err
}
@ -66,25 +62,17 @@ func (t *lpcm) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *lpcm) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.LPCM{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -94,18 +82,17 @@ func (t *lpcm) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
samples, err := t.decoder.Decode(pkt)
if err != nil {
return nil, err
return err
}
u.Samples = samples
u.Payload = unit.PayloadLPCM(samples)
}
// route packet as is
return u, nil
return nil
}

View file

@ -20,8 +20,8 @@ func TestLPCMProcessUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
unit := &unit.LPCM{
Samples: []byte{1, 2, 3, 4},
unit := &unit.Unit{
Payload: unit.PayloadLPCM{1, 2, 3, 4},
}
err = p.ProcessUnit(unit)

View file

@ -3,11 +3,9 @@ package codecprocessor //nolint:dupl
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmjpeg"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -47,11 +45,9 @@ func (t *mjpeg) createEncoder() error {
return t.encoder.Init()
}
func (t *mjpeg) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.MJPEG)
func (t *mjpeg) ProcessUnit(u *unit.Unit) error { //nolint:dupl
// encode into RTP
pkts, err := t.encoder.Encode(u.Frame)
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadMJPEG))
if err != nil {
return err
}
@ -65,25 +61,17 @@ func (t *mjpeg) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *mjpeg) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.MJPEG{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -93,7 +81,7 @@ func (t *mjpeg) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -101,14 +89,13 @@ func (t *mjpeg) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpmjpeg.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpmjpeg.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frame = frame
u.Payload = unit.PayloadMJPEG(frame)
}
// route packet as is
return u, nil
return nil
}

View file

@ -3,11 +3,9 @@ package codecprocessor //nolint:dupl
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg1audio"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -47,10 +45,8 @@ func (t *mpeg1Audio) createEncoder() error {
return t.encoder.Init()
}
func (t *mpeg1Audio) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.MPEG1Audio)
pkts, err := t.encoder.Encode(u.Frames)
func (t *mpeg1Audio) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadMPEG1Audio))
if err != nil {
return err
}
@ -64,25 +60,17 @@ func (t *mpeg1Audio) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *mpeg1Audio) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.MPEG1Audio{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -92,7 +80,7 @@ func (t *mpeg1Audio) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -100,14 +88,13 @@ func (t *mpeg1Audio) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpmpeg1audio.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpmpeg1audio.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frames = frames
u.Payload = unit.PayloadMPEG1Audio(frames)
}
// route packet as is
return u, nil
return nil
}

View file

@ -3,11 +3,9 @@ package codecprocessor //nolint:dupl
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg1video"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -56,11 +54,9 @@ func (t *mpeg1Video) createEncoder() error {
return t.encoder.Init()
}
func (t *mpeg1Video) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.MPEG1Video)
func (t *mpeg1Video) ProcessUnit(u *unit.Unit) error { //nolint:dupl
// encode into RTP
pkts, err := t.encoder.Encode(u.Frame)
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadMPEG1Video))
if err != nil {
return err
}
@ -74,25 +70,17 @@ func (t *mpeg1Video) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *mpeg1Video) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.MPEG1Video{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -102,7 +90,7 @@ func (t *mpeg1Video) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -110,14 +98,13 @@ func (t *mpeg1Video) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpmpeg1video.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpmpeg1video.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frame = frame
u.Payload = unit.PayloadMPEG1Video(frame)
}
// route packet as is
return u, nil
return nil
}

View file

@ -3,11 +3,9 @@ package codecprocessor
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg4audio"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -51,10 +49,8 @@ func (t *mpeg4Audio) createEncoder() error {
return t.encoder.Init()
}
func (t *mpeg4Audio) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.MPEG4Audio)
pkts, err := t.encoder.Encode(u.AUs)
func (t *mpeg4Audio) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadMPEG4Audio))
if err != nil {
return err
}
@ -68,25 +64,17 @@ func (t *mpeg4Audio) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *mpeg4Audio) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.MPEG4Audio{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -96,21 +84,20 @@ func (t *mpeg4Audio) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
aus, err := t.decoder.Decode(pkt)
if err != nil {
if errors.Is(err, rtpmpeg4audio.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.AUs = aus
u.Payload = unit.PayloadMPEG4Audio(aus)
}
// route packet as is
return u, nil
return nil
}

View file

@ -3,12 +3,10 @@ package codecprocessor
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpfragmented"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpmpeg4audio"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -49,10 +47,8 @@ func (t *mpeg4AudioLATM) createEncoder() error {
return t.encoder.Init()
}
func (t *mpeg4AudioLATM) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.MPEG4AudioLATM)
pkts, err := t.encoder.Encode(u.Element)
func (t *mpeg4AudioLATM) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadMPEG4AudioLATM))
if err != nil {
return err
}
@ -66,25 +62,17 @@ func (t *mpeg4AudioLATM) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *mpeg4AudioLATM) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.MPEG4AudioLATM{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -94,21 +82,20 @@ func (t *mpeg4AudioLATM) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
el, err := t.decoder.Decode(pkt)
if err != nil {
if errors.Is(err, rtpmpeg4audio.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Element = el
u.Payload = unit.PayloadMPEG4AudioLATM(el)
}
// route packet as is
return u, nil
return nil
}

View file

@ -4,12 +4,10 @@ import (
"bytes"
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpfragmented"
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4video"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -62,7 +60,7 @@ func (t *mpeg4Video) createEncoder() error {
return t.encoder.Init()
}
func (t *mpeg4Video) updateTrackParameters(frame []byte) {
func (t *mpeg4Video) updateTrackParameters(frame unit.PayloadMPEG4Video) {
if bytes.HasPrefix(frame, []byte{0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode)}) {
end := bytes.Index(frame[4:], []byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
if end < 0 {
@ -76,7 +74,7 @@ func (t *mpeg4Video) updateTrackParameters(frame []byte) {
}
}
func (t *mpeg4Video) remuxFrame(frame []byte) []byte {
func (t *mpeg4Video) remuxFrame(frame unit.PayloadMPEG4Video) unit.PayloadMPEG4Video {
// remove config
if bytes.HasPrefix(frame, []byte{0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode)}) {
end := bytes.Index(frame[4:], []byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
@ -93,17 +91,19 @@ func (t *mpeg4Video) remuxFrame(frame []byte) []byte {
frame = f
}
if len(frame) == 0 {
return nil
}
return frame
}
func (t *mpeg4Video) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.MPEG4Video)
func (t *mpeg4Video) ProcessUnit(u *unit.Unit) error { //nolint:dupl
t.updateTrackParameters(u.Payload.(unit.PayloadMPEG4Video))
u.Payload = t.remuxFrame(u.Payload.(unit.PayloadMPEG4Video))
t.updateTrackParameters(u.Frame)
u.Frame = t.remuxFrame(u.Frame)
if len(u.Frame) != 0 {
pkts, err := t.encoder.Encode(u.Frame)
if !u.NilPayload() {
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadMPEG4Video))
if err != nil {
return err
}
@ -118,18 +118,10 @@ func (t *mpeg4Video) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *mpeg4Video) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.MPEG4Video{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
t.updateTrackParameters(pkt.Payload)
@ -138,7 +130,7 @@ func (t *mpeg4Video) ProcessRTPPacket( //nolint:dupl
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -148,21 +140,20 @@ func (t *mpeg4Video) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
frame, err := t.decoder.Decode(pkt)
if err != nil {
if errors.Is(err, rtpfragmented.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frame = t.remuxFrame(frame)
u.Payload = t.remuxFrame(frame)
}
// route packet as is
return u, nil
return nil
}

View file

@ -17,11 +17,9 @@ func TestMPEG4VideoProcessUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
u1 := &unit.MPEG4Video{
Base: unit.Base{
u1 := &unit.Unit{
PTS: 30000,
},
Frame: []byte{
Payload: unit.PayloadMPEG4Video{
0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode),
0, 0, 1, 0xFF,
0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode),
@ -32,18 +30,16 @@ func TestMPEG4VideoProcessUnit(t *testing.T) {
err = p.ProcessUnit(u1)
require.NoError(t, err)
require.Equal(t, []byte{
require.Equal(t, unit.PayloadMPEG4Video{
0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode),
0, 0, 1, 0xFF,
0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode),
0, 0, 1, 0xF0,
}, u1.Frame)
}, u1.Payload)
u2 := &unit.MPEG4Video{
Base: unit.Base{
u2 := &unit.Unit{
PTS: 30000 * 2,
},
Frame: []byte{
Payload: unit.PayloadMPEG4Video{
0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode),
0, 0, 1, 0xF1,
},
@ -59,12 +55,12 @@ func TestMPEG4VideoProcessUnit(t *testing.T) {
}, forma.Config)
// test that params have been added to the frame
require.Equal(t, []byte{
require.Equal(t, unit.PayloadMPEG4Video{
0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode),
0, 0, 1, 0xFF,
0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode),
0, 0, 1, 0xF1,
}, u2.Frame)
}, u2.Payload)
// test that timestamp has increased
require.Equal(t, u1.RTPPackets[0].Timestamp+30000, u2.RTPPackets[0].Timestamp)

View file

@ -2,7 +2,6 @@ package codecprocessor
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpsimpleaudio"
@ -48,13 +47,11 @@ func (t *opus) createEncoder() error {
return t.encoder.Init()
}
func (t *opus) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.Opus)
func (t *opus) ProcessUnit(u *unit.Unit) error { //nolint:dupl
var rtpPackets []*rtp.Packet //nolint:prealloc
pts := u.PTS
for _, packet := range u.Packets {
for _, packet := range u.Payload.(unit.PayloadOpus) {
pkt, err := t.encoder.Encode(packet)
if err != nil {
return err
@ -72,25 +69,17 @@ func (t *opus) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *opus) ProcessRTPPacket(
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.Opus{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -100,18 +89,17 @@ func (t *opus) ProcessRTPPacket(
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
packet, err := t.decoder.Decode(pkt)
if err != nil {
return nil, err
return err
}
u.Packets = [][]byte{packet}
u.Payload = unit.PayloadOpus{packet}
}
// route packet as is
return u, nil
return nil
}

View file

@ -18,8 +18,8 @@ func TestOpusProcessUnit(t *testing.T) {
p, err := New(1450, forma, true, nil)
require.NoError(t, err)
unit := &unit.Opus{
Packets: [][]byte{
unit := &unit.Unit{
Payload: unit.PayloadOpus{
{
0xfc, 0x1e, 0x61, 0x96, 0xfc, 0xf7, 0x9b, 0x23,
0x5b, 0xc9, 0x56, 0xad, 0x05, 0x12, 0x2f, 0x6c,

View file

@ -3,10 +3,8 @@ package codecprocessor
import (
"crypto/rand"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -24,15 +22,13 @@ func randUint32() (uint32, error) {
// Processor is the codec-specific part of the processing that happens inside stream.Stream.
type Processor interface {
// process a Unit.
ProcessUnit(unit.Unit) error
ProcessUnit(*unit.Unit) error
// process a RTP packet and convert it into a unit.
// process a RTP packet.
ProcessRTPPacket(
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error)
) error
initialize() error
}

View file

@ -3,11 +3,9 @@ package codecprocessor //nolint:dupl
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpvp8"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -48,10 +46,8 @@ func (t *vp8) createEncoder() error {
return t.encoder.Init()
}
func (t *vp8) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.VP8)
pkts, err := t.encoder.Encode(u.Frame)
func (t *vp8) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadVP8))
if err != nil {
return err
}
@ -65,25 +61,17 @@ func (t *vp8) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *vp8) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.VP8{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -93,7 +81,7 @@ func (t *vp8) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -101,14 +89,13 @@ func (t *vp8) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpvp8.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpvp8.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frame = frame
u.Payload = unit.PayloadVP8(frame)
}
// route packet as is
return u, nil
return nil
}

View file

@ -3,11 +3,9 @@ package codecprocessor //nolint:dupl
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/gortsplib/v5/pkg/format/rtpvp9"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
@ -48,10 +46,8 @@ func (t *vp9) createEncoder() error {
return t.encoder.Init()
}
func (t *vp9) ProcessUnit(uu unit.Unit) error { //nolint:dupl
u := uu.(*unit.VP9)
pkts, err := t.encoder.Encode(u.Frame)
func (t *vp9) ProcessUnit(u *unit.Unit) error { //nolint:dupl
pkts, err := t.encoder.Encode(u.Payload.(unit.PayloadVP9))
if err != nil {
return err
}
@ -65,25 +61,17 @@ func (t *vp9) ProcessUnit(uu unit.Unit) error { //nolint:dupl
}
func (t *vp9) ProcessRTPPacket( //nolint:dupl
pkt *rtp.Packet,
ntp time.Time,
pts int64,
u *unit.Unit,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.VP9{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
) error {
pkt := u.RTPPackets[0]
// remove padding
pkt.Padding = false
pkt.PaddingSize = 0
if len(pkt.Payload) > t.RTPMaxPayloadSize {
return nil, fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
return fmt.Errorf("RTP payload size (%d) is greater than maximum allowed (%d)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
@ -93,7 +81,7 @@ func (t *vp9) ProcessRTPPacket( //nolint:dupl
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
return err
}
}
@ -101,14 +89,13 @@ func (t *vp9) ProcessRTPPacket( //nolint:dupl
if err != nil {
if errors.Is(err, rtpvp9.ErrNonStartingPacketAndNoPrevious) ||
errors.Is(err, rtpvp9.ErrMorePacketsNeeded) {
return u, nil
return nil
}
return nil, err
return err
}
u.Frame = frame
u.Payload = unit.PayloadVP9(frame)
}
// route packet as is
return u, nil
return nil
}

View file

@ -48,18 +48,16 @@ func setupVideoTrack(
videoMedia,
videoFormatAV1,
track,
func(u unit.Unit) error {
tunit := u.(*unit.AV1)
if tunit.TU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
err := muxer.WriteAV1(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
tunit.TU)
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.Payload.(unit.PayloadAV1))
if err != nil {
return fmt.Errorf("muxer error: %w", err)
}
@ -83,18 +81,16 @@ func setupVideoTrack(
videoMedia,
videoFormatVP9,
track,
func(u unit.Unit) error {
tunit := u.(*unit.VP9)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
err := muxer.WriteVP9(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
tunit.Frame)
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.Payload.(unit.PayloadVP9))
if err != nil {
return fmt.Errorf("muxer error: %w", err)
}
@ -123,18 +119,16 @@ func setupVideoTrack(
videoMedia,
videoFormatH265,
track,
func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
err := muxer.WriteH265(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
tunit.AU)
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.Payload.(unit.PayloadH265))
if err != nil {
return fmt.Errorf("muxer error: %w", err)
}
@ -162,18 +156,16 @@ func setupVideoTrack(
videoMedia,
videoFormatH264,
track,
func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
err := muxer.WriteH264(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
tunit.AU)
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.Payload.(unit.PayloadH264))
if err != nil {
return fmt.Errorf("muxer error: %w", err)
}
@ -215,14 +207,12 @@ func setupAudioTracks(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.Opus)
func(u *unit.Unit) error {
err := muxer.WriteOpus(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
tunit.Packets)
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.Payload.(unit.PayloadOpus))
if err != nil {
return fmt.Errorf("muxer error: %w", err)
}
@ -242,18 +232,16 @@ func setupAudioTracks(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
err := muxer.WriteMPEG4Audio(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
tunit.AUs)
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.Payload.(unit.PayloadMPEG4Audio))
if err != nil {
return fmt.Errorf("muxer error: %w", err)
}
@ -274,24 +262,22 @@ func setupAudioTracks(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.Element == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var ame mpeg4audio.AudioMuxElement
ame.StreamMuxConfig = forma.StreamMuxConfig
err := ame.Unmarshal(tunit.Element)
err := ame.Unmarshal(u.Payload.(unit.PayloadMPEG4AudioLATM))
if err != nil {
return err
}
return muxer.WriteMPEG4Audio(
track,
tunit.NTP,
tunit.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
u.NTP,
u.PTS, // no conversion is needed since we set gohlslib.Track.ClockRate = format.ClockRate
[][]byte{ame.Payloads[0][0][0]})
})
}

View file

@ -91,12 +91,10 @@ func ToStream(
newClockRate := medi.Formats[0].ClockRate()
c.OnDataAV1(track, func(pts int64, tu [][]byte) {
(*stream).WriteUnit(medi, medi.Formats[0], &unit.AV1{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: handleNTP(track),
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(clockRate)),
},
TU: tu,
Payload: unit.PayloadAV1(tu),
})
})
@ -110,12 +108,10 @@ func ToStream(
newClockRate := medi.Formats[0].ClockRate()
c.OnDataVP9(track, func(pts int64, frame []byte) {
(*stream).WriteUnit(medi, medi.Formats[0], &unit.VP9{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: handleNTP(track),
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(clockRate)),
},
Frame: frame,
Payload: unit.PayloadVP9(frame),
})
})
@ -132,12 +128,10 @@ func ToStream(
newClockRate := medi.Formats[0].ClockRate()
c.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) {
(*stream).WriteUnit(medi, medi.Formats[0], &unit.H265{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: handleNTP(track),
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(clockRate)),
},
AU: au,
Payload: unit.PayloadH265(au),
})
})
@ -154,12 +148,10 @@ func ToStream(
newClockRate := medi.Formats[0].ClockRate()
c.OnDataH26x(track, func(pts int64, _ int64, au [][]byte) {
(*stream).WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: handleNTP(track),
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(clockRate)),
},
AU: au,
Payload: unit.PayloadH264(au),
})
})
@ -174,12 +166,10 @@ func ToStream(
newClockRate := medi.Formats[0].ClockRate()
c.OnDataOpus(track, func(pts int64, packets [][]byte) {
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Opus{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: handleNTP(track),
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(clockRate)),
},
Packets: packets,
Payload: unit.PayloadOpus(packets),
})
})
@ -197,12 +187,10 @@ func ToStream(
newClockRate := medi.Formats[0].ClockRate()
c.OnDataMPEG4Audio(track, func(pts int64, aus [][]byte) {
(*stream).WriteUnit(medi, medi.Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: handleNTP(track),
PTS: multiplyAndDivide(pts, int64(newClockRate), int64(clockRate)),
},
AUs: aus,
Payload: unit.PayloadMPEG4Audio(aus),
})
})

View file

@ -115,8 +115,8 @@ func TestToStream(t *testing.T) {
r.OnData(
medias[0],
medias[0].Formats[0],
func(u unit.Unit) error {
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.GetNTP())
func(u *unit.Unit) error {
require.Equal(t, time.Date(2018, 0o5, 20, 8, 17, 15, 0, time.UTC), u.NTP)
close(done)
return nil
})

View file

@ -61,22 +61,20 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if dtsExtractor == nil {
if !h265.IsRandomAccess(tunit.AU) {
if !h265.IsRandomAccess(u.Payload.(unit.PayloadH265)) {
return nil
}
dtsExtractor = &h265.DTSExtractor{}
dtsExtractor.Initialize()
}
dts, err := dtsExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := dtsExtractor.Extract(u.Payload.(unit.PayloadH265), u.PTS)
if err != nil {
return err
}
@ -84,9 +82,9 @@ func FromStream(
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err = (*w).WriteH265(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
dts,
tunit.AU)
u.Payload.(unit.PayloadH265))
if err != nil {
return err
}
@ -102,14 +100,12 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
idrPresent := h264.IsRandomAccess(tunit.AU)
idrPresent := h264.IsRandomAccess(u.Payload.(unit.PayloadH264))
if dtsExtractor == nil {
if !idrPresent {
@ -119,7 +115,7 @@ func FromStream(
dtsExtractor.Initialize()
}
dts, err := dtsExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := dtsExtractor.Extract(u.Payload.(unit.PayloadH264), u.PTS)
if err != nil {
return err
}
@ -127,9 +123,9 @@ func FromStream(
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err = (*w).WriteH264(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
dts,
tunit.AU)
u.Payload.(unit.PayloadH264))
if err != nil {
return err
}
@ -146,25 +142,23 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Video)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("MPEG-4 Video streams with B-frames are not supported (yet)")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteMPEG4Video(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
tunit.Frame)
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.Payload.(unit.PayloadMPEG4Video))
if err != nil {
return err
}
@ -181,25 +175,23 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Video)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("MPEG-1 Video streams with B-frames are not supported (yet)")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteMPEG1Video(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
tunit.Frame)
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.Payload.(unit.PayloadMPEG1Video))
if err != nil {
return err
}
@ -215,18 +207,16 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.Opus)
if tunit.Packets == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteOpus(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
tunit.Packets)
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
u.Payload.(unit.PayloadOpus))
if err != nil {
return err
}
@ -244,15 +234,13 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.KLV)
if tunit.Unit == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteKLV(track, multiplyAndDivide(tunit.PTS, 90000, 90000), tunit.Unit)
err := (*w).WriteKLV(track, multiplyAndDivide(u.PTS, 90000, 90000), u.Payload.(unit.PayloadKLV))
if err != nil {
return err
}
@ -268,18 +256,16 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteMPEG4Audio(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
tunit.AUs)
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
u.Payload.(unit.PayloadMPEG4Audio))
if err != nil {
return err
}
@ -294,17 +280,15 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.Element == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var elIn mpeg4audio.AudioMuxElement
elIn.MuxConfigPresent = false
elIn.StreamMuxConfig = forma.StreamMuxConfig
err := elIn.Unmarshal(tunit.Element)
err := elIn.Unmarshal(u.Payload.(unit.PayloadMPEG4AudioLATM))
if err != nil {
return err
}
@ -322,7 +306,7 @@ func FromStream(
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err = (*w).WriteMPEG4AudioLATM(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
[][]byte{buf})
if err != nil {
return err
@ -334,18 +318,16 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.Element == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteMPEG4AudioLATM(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
[][]byte{tunit.Element})
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
[][]byte{u.Payload.(unit.PayloadMPEG4AudioLATM)})
if err != nil {
return err
}
@ -360,18 +342,16 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Audio)
if tunit.Frames == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteMPEG1Audio(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
tunit.Frames)
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.Payload.(unit.PayloadMPEG1Audio))
if err != nil {
return err
}
@ -385,15 +365,13 @@ func FromStream(
media,
forma,
track,
func(u unit.Unit) error {
tunit := u.(*unit.AC3)
if tunit.Frames == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
for i, frame := range tunit.Frames {
framePTS := tunit.PTS + int64(i)*ac3.SamplesPerFrame
for i, frame := range u.Payload.(unit.PayloadAC3) {
framePTS := u.PTS + int64(i)*ac3.SamplesPerFrame
sconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteAC3(

View file

@ -48,12 +48,10 @@ func ToStream(
r.OnDataH265(track, func(pts int64, _ int64, au [][]byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.H265{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
},
AU: au,
Payload: unit.PayloadH265(au),
})
return nil
})
@ -70,12 +68,10 @@ func ToStream(
r.OnDataH264(track, func(pts int64, _ int64, au [][]byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.H264{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
},
AU: au,
Payload: unit.PayloadH264(au),
})
return nil
})
@ -91,12 +87,10 @@ func ToStream(
r.OnDataMPEGxVideo(track, func(pts int64, frame []byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.MPEG4Video{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
},
Frame: frame,
Payload: unit.PayloadMPEG4Video(frame),
})
return nil
})
@ -110,12 +104,10 @@ func ToStream(
r.OnDataMPEGxVideo(track, func(pts int64, frame []byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.MPEG1Video{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
},
Frame: frame,
Payload: unit.PayloadMPEG1Video(frame),
})
return nil
})
@ -132,12 +124,10 @@ func ToStream(
r.OnDataOpus(track, func(pts int64, packets [][]byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Opus{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: multiplyAndDivide(pts, int64(medi.Formats[0].ClockRate()), 90000),
},
Packets: packets,
Payload: unit.PayloadOpus(packets),
})
return nil
})
@ -152,12 +142,10 @@ func ToStream(
r.OnDataKLV(track, func(pts int64, uni []byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.KLV{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts,
},
Unit: uni,
Payload: unit.PayloadKLV(uni),
})
return nil
})
@ -177,12 +165,10 @@ func ToStream(
r.OnDataMPEG4Audio(track, func(pts int64, aus [][]byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: multiplyAndDivide(pts, int64(medi.Formats[0].ClockRate()), 90000),
},
AUs: aus,
Payload: unit.PayloadMPEG4Audio(aus),
})
return nil
})
@ -231,12 +217,10 @@ func ToStream(
return err
}
(*stream).WriteUnit(medi, medi.Formats[0], &unit.MPEG4AudioLATM{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts,
},
Element: buf,
Payload: unit.PayloadMPEG4AudioLATM(buf),
})
pts += mpeg4audio.SamplesPerAccessUnit
@ -254,12 +238,10 @@ func ToStream(
r.OnDataMPEG1Audio(track, func(pts int64, frames [][]byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.MPEG1Audio{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: pts, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
},
Frames: frames,
Payload: unit.PayloadMPEG1Audio(frames),
})
return nil
})
@ -277,12 +259,10 @@ func ToStream(
r.OnDataAC3(track, func(pts int64, frame []byte) error {
pts = td.Decode(pts)
(*stream).WriteUnit(medi, medi.Formats[0], &unit.AC3{
Base: unit.Base{
(*stream).WriteUnit(medi, medi.Formats[0], &unit.Unit{
NTP: time.Now(),
PTS: multiplyAndDivide(pts, int64(medi.Formats[0].ClockRate()), 90000),
},
Frames: [][]byte{frame},
Payload: unit.PayloadAC3{frame},
})
return nil
})

View file

@ -55,18 +55,16 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.AV1)
if tunit.TU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteAV1(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
tunit.TU)
timestampToDuration(u.PTS, forma.ClockRate()),
u.Payload.(unit.PayloadAV1))
})
tracks = append(tracks, forma)
@ -77,18 +75,16 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.VP9)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteVP9(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
tunit.Frame)
timestampToDuration(u.PTS, forma.ClockRate()),
u.Payload.(unit.PayloadVP9))
})
tracks = append(tracks, forma)
@ -101,22 +97,20 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if videoDTSExtractor == nil {
if !h265.IsRandomAccess(tunit.AU) {
if !h265.IsRandomAccess(u.Payload.(unit.PayloadH265)) {
return nil
}
videoDTSExtractor = &h265.DTSExtractor{}
videoDTSExtractor.Initialize()
}
dts, err := videoDTSExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := videoDTSExtractor.Extract(u.Payload.(unit.PayloadH265), u.PTS)
if err != nil {
return err
}
@ -124,9 +118,9 @@ func FromStream(
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteH265(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
timestampToDuration(u.PTS, forma.ClockRate()),
timestampToDuration(dts, forma.ClockRate()),
tunit.AU)
u.Payload.(unit.PayloadH265))
})
tracks = append(tracks, forma)
@ -138,17 +132,15 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
idrPresent := false
nonIDRPresent := false
for _, nalu := range tunit.AU {
for _, nalu := range u.Payload.(unit.PayloadH264) {
typ := h264.NALUType(nalu[0] & 0x1F)
switch typ {
case h264.NALUTypeIDR:
@ -171,7 +163,7 @@ func FromStream(
return nil
}
dts, err := videoDTSExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := videoDTSExtractor.Extract(u.Payload.(unit.PayloadH264), u.PTS)
if err != nil {
return err
}
@ -179,9 +171,9 @@ func FromStream(
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteH264(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
timestampToDuration(u.PTS, forma.ClockRate()),
timestampToDuration(dts, forma.ClockRate()),
tunit.AU)
u.Payload.(unit.PayloadH264))
})
tracks = append(tracks, forma)
@ -191,16 +183,14 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.Opus)
if tunit.Packets == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
pts := tunit.PTS
pts := u.PTS
for _, pkt := range tunit.Packets {
for _, pkt := range u.Payload.(unit.PayloadOpus) {
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteOpus(
forma,
@ -224,15 +214,13 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
for i, au := range tunit.AUs {
pts := tunit.PTS + int64(i)*mpeg4audio.SamplesPerAccessUnit
for i, au := range u.Payload.(unit.PayloadMPEG4Audio) {
pts := u.PTS + int64(i)*mpeg4audio.SamplesPerAccessUnit
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteMPEG4Audio(
@ -255,16 +243,14 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.Element == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var ame mpeg4audio.AudioMuxElement
ame.StreamMuxConfig = forma.StreamMuxConfig
err := ame.Unmarshal(tunit.Element)
err := ame.Unmarshal(u.Payload.(unit.PayloadMPEG4AudioLATM))
if err != nil {
return err
}
@ -272,7 +258,7 @@ func FromStream(
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteMPEG4Audio(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
timestampToDuration(u.PTS, forma.ClockRate()),
ame.Payloads[0][0][0],
)
})
@ -286,12 +272,14 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Audio)
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
pts := tunit.PTS
pts := u.PTS
for _, frame := range tunit.Frames {
for _, frame := range u.Payload.(unit.PayloadMPEG1Audio) {
var h mpeg1audio.FrameHeader
err := h.Unmarshal(frame)
if err != nil {
@ -321,11 +309,13 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.AC3)
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
for i, frame := range tunit.Frames {
pts := tunit.PTS + int64(i)*ac3.SamplesPerFrame
for i, frame := range u.Payload.(unit.PayloadAC3) {
pts := u.PTS + int64(i)*ac3.SamplesPerFrame
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
err := (*w).WriteAC3(
@ -348,18 +338,16 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.G711)
if tunit.Samples == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteG711(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
tunit.Samples,
timestampToDuration(u.PTS, forma.ClockRate()),
u.Payload.(unit.PayloadG711),
)
})
@ -375,18 +363,16 @@ func FromStream(
r.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.LPCM)
if tunit.Samples == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
return (*w).WriteLPCM(
forma,
timestampToDuration(tunit.PTS, forma.ClockRate()),
tunit.Samples,
timestampToDuration(u.PTS, forma.ClockRate()),
u.Payload.(unit.PayloadLPCM),
)
})

View file

@ -419,31 +419,25 @@ func TestFromStream(t *testing.T) {
switch ca {
case "h264 + aac":
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 0,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2}, // IDR
},
})
strm.WriteUnit(medias[1], medias[1].Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(medias[1], medias[1].Formats[0], &unit.Unit{
PTS: 90000 * 5,
},
AUs: [][]byte{
Payload: unit.PayloadMPEG4Audio{
{3, 4},
},
})
case "av1":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.AV1{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 2 * int64(i),
},
TU: [][]byte{{
Payload: unit.PayloadAV1{{
0x0a, 0x0e, 0x00, 0x00, 0x00, 0x4a, 0xab, 0xbf,
0xc3, 0x77, 0x6b, 0xe4, 0x40, 0x40, 0x40, 0x41,
}},
@ -452,21 +446,17 @@ func TestFromStream(t *testing.T) {
case "vp9":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.VP9{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 2 * int64(i),
},
Frame: []byte{1, 2},
Payload: unit.PayloadVP9{1, 2},
})
}
case "h265":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.H265{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 2 * int64(i),
},
AU: [][]byte{{
Payload: unit.PayloadH265{{
0x2a, 0x01, 0xad, 0xe0, 0xf5, 0x34, 0x11, 0x0b,
0x41, 0xe8,
}},
@ -475,11 +465,9 @@ func TestFromStream(t *testing.T) {
case "h264":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 2 * int64(i),
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2}, // IDR
},
})
@ -487,11 +475,9 @@ func TestFromStream(t *testing.T) {
case "opus":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Opus{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 5 * int64(i),
},
Packets: [][]byte{
Payload: unit.PayloadOpus{
{3, 4},
},
})
@ -499,11 +485,9 @@ func TestFromStream(t *testing.T) {
case "aac":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 5 * int64(i),
},
AUs: [][]byte{
Payload: unit.PayloadMPEG4Audio{
{3, 4},
},
})
@ -511,11 +495,9 @@ func TestFromStream(t *testing.T) {
case "mp3":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.MPEG1Audio{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 5 * int64(i),
},
Frames: [][]byte{
Payload: unit.PayloadMPEG1Audio{
{
0xff, 0xfa, 0x52, 0x04, 0x00,
},
@ -525,11 +507,9 @@ func TestFromStream(t *testing.T) {
case "ac-3":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.AC3{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 5 * int64(i),
},
Frames: [][]byte{
Payload: unit.PayloadAC3{
{
0x0b, 0x77, 0x47, 0x11, 0x0c, 0x40, 0x2f, 0x84,
0x2b, 0xc1, 0x07, 0x7a, 0xb0, 0xfa, 0xbb, 0xea,
@ -586,11 +566,9 @@ func TestFromStream(t *testing.T) {
case "pcma", "pcmu":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.G711{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 5 * int64(i),
},
Samples: []byte{
Payload: unit.PayloadG711{
3, 4,
},
})
@ -598,19 +576,17 @@ func TestFromStream(t *testing.T) {
case "lpcm":
for i := range 2 {
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.LPCM{
Base: unit.Base{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
PTS: 90000 * 5 * int64(i),
},
Samples: []byte{
Payload: unit.PayloadLPCM{
3, 4, 5, 6,
},
})
}
case "h265 + h264 + vp9 + av1 + opus + aac":
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.H265{
AU: [][]byte{
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
Payload: unit.PayloadH265{
{
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03,
@ -634,36 +610,34 @@ func TestFromStream(t *testing.T) {
},
})
strm.WriteUnit(medias[1], medias[1].Formats[0], &unit.H264{
AU: [][]byte{
strm.WriteUnit(medias[1], medias[1].Formats[0], &unit.Unit{
Payload: unit.PayloadH264{
codecprocessor.H264DefaultSPS,
codecprocessor.H264DefaultPPS,
{5, 2}, // IDR
},
})
strm.WriteUnit(medias[2], medias[2].Formats[0], &unit.VP9{
Frame: []byte{1, 2},
strm.WriteUnit(medias[2], medias[2].Formats[0], &unit.Unit{
Payload: unit.PayloadVP9{1, 2},
})
strm.WriteUnit(medias[3], medias[3].Formats[0], &unit.AV1{
TU: [][]byte{{
strm.WriteUnit(medias[3], medias[3].Formats[0], &unit.Unit{
Payload: unit.PayloadAV1{{
0x0a, 0x0e, 0x00, 0x00, 0x00, 0x4a, 0xab, 0xbf,
0xc3, 0x77, 0x6b, 0xe4, 0x40, 0x40, 0x40, 0x41,
}},
})
strm.WriteUnit(medias[4], medias[4].Formats[0], &unit.Opus{
Packets: [][]byte{
strm.WriteUnit(medias[4], medias[4].Formats[0], &unit.Unit{
Payload: unit.PayloadOpus{
{3, 4},
},
})
strm.WriteUnit(medias[5], medias[5].Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(medias[5], medias[5].Formats[0], &unit.Unit{
PTS: 90000 * 5,
},
AUs: [][]byte{
Payload: unit.PayloadMPEG4Audio{
{3, 4},
},
})

View file

@ -46,12 +46,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataAV1(ttrack, func(pts time.Duration, tu [][]byte) {
(*stream).WriteUnit(medi, ctrack, &unit.AV1{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
TU: tu,
Payload: unit.PayloadAV1(tu),
})
})
@ -63,12 +61,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataVP9(ttrack, func(pts time.Duration, frame []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.VP9{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
Frame: frame,
Payload: unit.PayloadVP9(frame),
})
})
@ -80,12 +76,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataH265(ttrack, func(pts time.Duration, _ time.Duration, au [][]byte) {
(*stream).WriteUnit(medi, ctrack, &unit.H265{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
AU: au,
Payload: unit.PayloadH265(au),
})
})
@ -97,12 +91,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataH264(ttrack, func(pts time.Duration, _ time.Duration, au [][]byte) {
(*stream).WriteUnit(medi, ctrack, &unit.H264{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
AU: au,
Payload: unit.PayloadH264(au),
})
})
@ -114,12 +106,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataOpus(ttrack, func(pts time.Duration, packet []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.Opus{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
Packets: [][]byte{packet},
Payload: unit.PayloadOpus{packet},
})
})
@ -131,12 +121,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataMPEG4Audio(ttrack, func(pts time.Duration, au []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.MPEG4Audio{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
AUs: [][]byte{au},
Payload: unit.PayloadMPEG4Audio{au},
})
})
@ -148,12 +136,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataMPEG1Audio(ttrack, func(pts time.Duration, frame []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.MPEG1Audio{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
Frames: [][]byte{frame},
Payload: unit.PayloadMPEG1Audio{frame},
})
})
@ -165,12 +151,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataAC3(ttrack, func(pts time.Duration, frame []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.AC3{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
Frames: [][]byte{frame},
Payload: unit.PayloadAC3{frame},
})
})
@ -182,12 +166,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataG711(ttrack, func(pts time.Duration, samples []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.G711{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
Samples: samples,
Payload: unit.PayloadG711(samples),
})
})
@ -199,12 +181,10 @@ func ToStream(r *gortmplib.Reader, stream **stream.Stream) ([]*description.Media
medias = append(medias, medi)
r.OnDataLPCM(ttrack, func(pts time.Duration, samples []byte) {
(*stream).WriteUnit(medi, ctrack, &unit.LPCM{
Base: unit.Base{
(*stream).WriteUnit(medi, ctrack, &unit.Unit{
NTP: time.Now(),
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
},
Samples: samples,
Payload: unit.PayloadLPCM(samples),
})
})

View file

@ -83,21 +83,19 @@ func setupVideoTrack(
r.OnData(
media,
av1Format,
func(u unit.Unit) error {
tunit := u.(*unit.AV1)
if tunit.TU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
packets, err2 := encoder.Encode(tunit.TU)
packets, err2 := encoder.Encode(u.Payload.(unit.PayloadAV1))
if err2 != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += tunit.RTPPackets[0].Timestamp
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += u.RTPPackets[0].Timestamp
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -133,21 +131,19 @@ func setupVideoTrack(
r.OnData(
media,
vp9Format,
func(u unit.Unit) error {
tunit := u.(*unit.VP9)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
packets, err2 := encoder.Encode(tunit.Frame)
packets, err2 := encoder.Encode(u.Payload.(unit.PayloadVP9))
if err2 != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += tunit.RTPPackets[0].Timestamp
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += u.RTPPackets[0].Timestamp
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -181,21 +177,19 @@ func setupVideoTrack(
r.OnData(
media,
vp8Format,
func(u unit.Unit) error {
tunit := u.(*unit.VP8)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
packets, err2 := encoder.Encode(tunit.Frame)
packets, err2 := encoder.Encode(u.Payload.(unit.PayloadVP8))
if err2 != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += tunit.RTPPackets[0].Timestamp
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += u.RTPPackets[0].Timestamp
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -233,28 +227,26 @@ func setupVideoTrack(
r.OnData(
media,
h265Format,
func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("WebRTC doesn't support H265 streams with B-frames")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
packets, err2 := encoder.Encode(tunit.AU)
packets, err2 := encoder.Encode(u.Payload.(unit.PayloadH265))
if err2 != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += tunit.RTPPackets[0].Timestamp
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += u.RTPPackets[0].Timestamp
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -292,28 +284,26 @@ func setupVideoTrack(
r.OnData(
media,
h264Format,
func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("WebRTC doesn't support H264 streams with B-frames")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
packets, err2 := encoder.Encode(tunit.AU)
packets, err2 := encoder.Encode(u.Payload.(unit.PayloadH264))
if err2 != nil {
return nil //nolint:nilerr
}
for _, pkt := range packets {
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += tunit.RTPPackets[0].Timestamp
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp), 90000))
pkt.Timestamp += u.RTPPackets[0].Timestamp
track.WriteRTPWithNTP(pkt, ntp.Add(-1*time.Minute)) //nolint:errcheck
}
@ -377,8 +367,8 @@ func setupAudioTrack(
r.OnData(
media,
opusFormat,
func(u unit.Unit) error {
for _, orig := range u.GetRTPPackets() {
func(u *unit.Unit) error {
for _, orig := range u.RTPPackets {
pkt := &rtp.Packet{
Header: orig.Header,
Payload: orig.Payload,
@ -389,7 +379,7 @@ func setupAudioTrack(
pkt.Timestamp = curTimestamp
curTimestamp += uint32(opus.PacketDuration2(pkt.Payload))
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp-u.GetRTPPackets()[0].Timestamp), 48000))
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp-u.RTPPackets[0].Timestamp), 48000))
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -414,9 +404,9 @@ func setupAudioTrack(
r.OnData(
media,
g722Format,
func(u unit.Unit) error {
for _, pkt := range u.GetRTPPackets() {
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp-u.GetRTPPackets()[0].Timestamp), 8000))
func(u *unit.Unit) error {
for _, pkt := range u.RTPPackets {
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp-u.RTPPackets[0].Timestamp), 8000))
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -493,8 +483,8 @@ func setupAudioTrack(
r.OnData(
media,
g711Format,
func(u unit.Unit) error {
for _, orig := range u.GetRTPPackets() {
func(u *unit.Unit) error {
for _, orig := range u.RTPPackets {
pkt := &rtp.Packet{
Header: orig.Header,
Payload: orig.Payload,
@ -505,7 +495,7 @@ func setupAudioTrack(
pkt.Timestamp = curTimestamp
curTimestamp += uint32(len(pkt.Payload)) / uint32(g711Format.ChannelCount)
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp-u.GetRTPPackets()[0].Timestamp), 8000))
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp-u.RTPPackets[0].Timestamp), 8000))
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -531,21 +521,19 @@ func setupAudioTrack(
r.OnData(
media,
g711Format,
func(u unit.Unit) error {
tunit := u.(*unit.G711)
if tunit.Samples == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var lpcm []byte
if g711Format.MULaw {
var mu g711.Mulaw
mu.Unmarshal(tunit.Samples)
mu.Unmarshal(u.Payload.(unit.PayloadG711))
lpcm = mu
} else {
var al g711.Alaw
al.Unmarshal(tunit.Samples)
al.Unmarshal(u.Payload.(unit.PayloadG711))
lpcm = al
}
@ -560,7 +548,7 @@ func setupAudioTrack(
pkt.Timestamp = curTimestamp
curTimestamp += uint32(len(pkt.Payload)) / 2 / uint32(g711Format.ChannelCount)
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp-u.GetRTPPackets()[0].Timestamp),
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp-u.RTPPackets[0].Timestamp),
g711Format.ClockRate()))
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}
@ -619,14 +607,12 @@ func setupAudioTrack(
r.OnData(
media,
lpcmFormat,
func(u unit.Unit) error {
tunit := u.(*unit.LPCM)
if tunit.Samples == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
packets, err2 := encoder.Encode(tunit.Samples)
packets, err2 := encoder.Encode(u.Payload.(unit.PayloadLPCM))
if err2 != nil {
return nil //nolint:nilerr
}
@ -637,7 +623,7 @@ func setupAudioTrack(
pkt.Timestamp = curTimestamp
curTimestamp += uint32(len(pkt.Payload)) / 2 / uint32(lpcmFormat.ChannelCount)
ntp := u.GetNTP().Add(timestampToDuration(int64(pkt.Timestamp-u.GetRTPPackets()[0].Timestamp),
ntp := u.NTP.Add(timestampToDuration(int64(pkt.Timestamp-u.RTPPackets[0].Timestamp),
lpcmFormat.ClockRate()))
track.WriteRTPWithNTP(pkt, ntp) //nolint:errcheck
}

View file

@ -143,17 +143,15 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.AV1)
if tunit.TU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := false
paramsChanged := false
for _, obu := range tunit.TU {
for _, obu := range u.Payload.(unit.PayloadAV1) {
typ := av1.OBUType((obu[0] >> 3) & 0b1111)
if typ == av1.OBUTypeSequenceHeader {
@ -177,15 +175,15 @@ func (f *formatFMP4) initialize() bool {
}
var sampl fmp4.Sample
err := sampl.FillAV1(tunit.TU)
err := sampl.FillAV1(u.Payload.(unit.PayloadAV1))
if err != nil {
return err
}
return track.write(&sample{
Sample: &sampl,
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
@ -205,15 +203,13 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.VP9)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var h vp9.Header
err := h.Unmarshal(tunit.Frame)
err := h.Unmarshal(u.Payload.(unit.PayloadVP9))
if err != nil {
return err
}
@ -264,10 +260,10 @@ func (f *formatFMP4) initialize() bool {
return track.write(&sample{
Sample: &fmp4.Sample{
IsNonSyncSample: !randomAccess,
Payload: tunit.Frame,
Payload: u.Payload.(unit.PayloadVP9),
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
@ -294,17 +290,15 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := false
paramsChanged := false
for _, nalu := range tunit.AU {
for _, nalu := range u.Payload.(unit.PayloadH265) {
typ := h265.NALUType((nalu[0] >> 1) & 0b111111)
switch typ {
@ -343,15 +337,13 @@ func (f *formatFMP4) initialize() bool {
dtsExtractor.Initialize()
}
dts, err := dtsExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := dtsExtractor.Extract(u.Payload.(unit.PayloadH265), u.PTS)
if err != nil {
return err
}
var sampl fmp4.Sample
err = sampl.FillH265(
int32(tunit.PTS-dts),
tunit.AU)
err = sampl.FillH265(int32(u.PTS-dts), u.Payload.(unit.PayloadH265))
if err != nil {
return err
}
@ -359,7 +351,7 @@ func (f *formatFMP4) initialize() bool {
return track.write(&sample{
Sample: &sampl,
dts: dts,
ntp: tunit.NTP,
ntp: u.NTP,
})
})
@ -381,17 +373,15 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := false
paramsChanged := false
for _, nalu := range tunit.AU {
for _, nalu := range u.Payload.(unit.PayloadH264) {
typ := h264.NALUType(nalu[0] & 0x1F)
switch typ {
case h264.NALUTypeSPS:
@ -423,15 +413,13 @@ func (f *formatFMP4) initialize() bool {
dtsExtractor.Initialize()
}
dts, err := dtsExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := dtsExtractor.Extract(u.Payload.(unit.PayloadH264), u.PTS)
if err != nil {
return err
}
var sampl fmp4.Sample
err = sampl.FillH264(
int32(tunit.PTS-dts),
tunit.AU)
err = sampl.FillH264(int32(u.PTS-dts), u.Payload.(unit.PayloadH264))
if err != nil {
return err
}
@ -439,7 +427,7 @@ func (f *formatFMP4) initialize() bool {
return track.write(&sample{
Sample: &sampl,
dts: dts,
ntp: tunit.NTP,
ntp: u.NTP,
})
})
@ -461,19 +449,20 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Video)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := bytes.Contains(tunit.Frame, []byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
randomAccess := bytes.Contains(u.Payload.(unit.PayloadMPEG4Video),
[]byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
if bytes.HasPrefix(tunit.Frame, []byte{0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode)}) {
end := bytes.Index(tunit.Frame[4:], []byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
if bytes.HasPrefix(u.Payload.(unit.PayloadMPEG4Video),
[]byte{0, 0, 1, byte(mpeg4video.VisualObjectSequenceStartCode)}) {
end := bytes.Index(u.Payload.(unit.PayloadMPEG4Video)[4:],
[]byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
if end >= 0 {
config2 := tunit.Frame[:end+4]
config2 := u.Payload.(unit.PayloadMPEG4Video)[:end+4]
if !bytes.Equal(codec.Config, config2) {
codec.Config = config2
@ -487,18 +476,18 @@ func (f *formatFMP4) initialize() bool {
return nil
}
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("MPEG-4 Video streams with B-frames are not supported (yet)")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
return track.write(&sample{
Sample: &fmp4.Sample{
Payload: tunit.Frame,
Payload: u.Payload.(unit.PayloadMPEG4Video),
IsNonSyncSample: !randomAccess,
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
@ -514,19 +503,17 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Video)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := bytes.Contains(tunit.Frame, []byte{0, 0, 1, 0xB8})
randomAccess := bytes.Contains(u.Payload.(unit.PayloadMPEG1Video), []byte{0, 0, 1, 0xB8})
if bytes.HasPrefix(tunit.Frame, []byte{0, 0, 1, 0xB3}) {
end := bytes.Index(tunit.Frame[4:], []byte{0, 0, 1, 0xB8})
if bytes.HasPrefix(u.Payload.(unit.PayloadMPEG1Video), []byte{0, 0, 1, 0xB3}) {
end := bytes.Index(u.Payload.(unit.PayloadMPEG1Video)[4:], []byte{0, 0, 1, 0xB8})
if end >= 0 {
config := tunit.Frame[:end+4]
config := u.Payload.(unit.PayloadMPEG1Video)[:end+4]
if !bytes.Equal(codec.Config, config) {
codec.Config = config
@ -540,18 +527,18 @@ func (f *formatFMP4) initialize() bool {
return nil
}
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("MPEG-1 Video streams with B-frames are not supported (yet)")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
return track.write(&sample{
Sample: &fmp4.Sample{
Payload: tunit.Frame,
Payload: u.Payload.(unit.PayloadMPEG1Video),
IsNonSyncSample: !randomAccess,
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
@ -567,16 +554,14 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MJPEG)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !parsed {
parsed = true
width, height, err := jpegExtractSize(tunit.Frame)
width, height, err := jpegExtractSize(u.Payload.(unit.PayloadMJPEG))
if err != nil {
return err
}
@ -587,10 +572,10 @@ func (f *formatFMP4) initialize() bool {
return track.write(&sample{
Sample: &fmp4.Sample{
Payload: tunit.Frame,
Payload: u.Payload.(unit.PayloadMJPEG),
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
@ -603,22 +588,20 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.Opus)
if tunit.Packets == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
pts := tunit.PTS
pts := u.PTS
for _, packet := range tunit.Packets {
for _, packet := range u.Payload.(unit.PayloadOpus) {
err := track.write(&sample{
Sample: &fmp4.Sample{
Payload: packet,
},
dts: pts,
ntp: tunit.NTP.Add(timestampToDuration(pts-tunit.PTS, clockRate)),
ntp: u.NTP.Add(timestampToDuration(pts-u.PTS, clockRate)),
})
if err != nil {
return err
@ -639,22 +622,20 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
for i, au := range tunit.AUs {
pts := tunit.PTS + int64(i)*mpeg4audio.SamplesPerAccessUnit
for i, au := range u.Payload.(unit.PayloadMPEG4Audio) {
pts := u.PTS + int64(i)*mpeg4audio.SamplesPerAccessUnit
err := track.write(&sample{
Sample: &fmp4.Sample{
Payload: au,
},
dts: pts,
ntp: tunit.NTP.Add(timestampToDuration(pts-tunit.PTS, clockRate)),
ntp: u.NTP.Add(timestampToDuration(pts-u.PTS, clockRate)),
})
if err != nil {
return err
@ -674,16 +655,14 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.Element == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var ame mpeg4audio.AudioMuxElement
ame.StreamMuxConfig = forma.StreamMuxConfig
err := ame.Unmarshal(tunit.Element)
err := ame.Unmarshal(u.Payload.(unit.PayloadMPEG4AudioLATM))
if err != nil {
return err
}
@ -692,8 +671,8 @@ func (f *formatFMP4) initialize() bool {
Sample: &fmp4.Sample{
Payload: ame.Payloads[0][0][0],
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
}
@ -710,16 +689,14 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Audio)
if tunit.Frames == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var dt time.Duration
for _, frame := range tunit.Frames {
for _, frame := range u.Payload.(unit.PayloadMPEG1Audio) {
var h mpeg1audio.FrameHeader
err := h.Unmarshal(frame)
if err != nil {
@ -737,8 +714,8 @@ func (f *formatFMP4) initialize() bool {
Sample: &fmp4.Sample{
Payload: frame,
},
dts: tunit.PTS + tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS + u.PTS,
ntp: u.NTP,
})
if err != nil {
return err
@ -769,14 +746,12 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.AC3)
if tunit.Frames == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
for i, frame := range tunit.Frames {
for i, frame := range u.Payload.(unit.PayloadAC3) {
var syncInfo ac3.SyncInfo
err := syncInfo.Unmarshal(frame)
if err != nil {
@ -802,14 +777,14 @@ func (f *formatFMP4) initialize() bool {
f.updateCodecParams()
}
pts := tunit.PTS + int64(i)*ac3.SamplesPerFrame
pts := u.PTS + int64(i)*ac3.SamplesPerFrame
err = track.write(&sample{
Sample: &fmp4.Sample{
Payload: frame,
},
dts: pts,
ntp: tunit.NTP.Add(timestampToDuration(pts-tunit.PTS, clockRate)),
ntp: u.NTP.Add(timestampToDuration(pts-u.PTS, clockRate)),
})
if err != nil {
return err
@ -834,21 +809,19 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.G711)
if tunit.Samples == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var lpcm []byte
if forma.MULaw {
var mu g711.Mulaw
mu.Unmarshal(tunit.Samples)
mu.Unmarshal(u.Payload.(unit.PayloadG711))
lpcm = mu
} else {
var al g711.Alaw
al.Unmarshal(tunit.Samples)
al.Unmarshal(u.Payload.(unit.PayloadG711))
lpcm = al
}
@ -856,8 +829,8 @@ func (f *formatFMP4) initialize() bool {
Sample: &fmp4.Sample{
Payload: lpcm,
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
@ -873,19 +846,17 @@ func (f *formatFMP4) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.LPCM)
if tunit.Samples == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
return track.write(&sample{
Sample: &fmp4.Sample{
Payload: tunit.Samples,
Payload: u.Payload.(unit.PayloadLPCM),
},
dts: tunit.PTS,
ntp: tunit.NTP,
dts: u.PTS,
ntp: u.NTP,
})
})
}

View file

@ -87,14 +87,12 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.H265)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := h265.IsRandomAccess(tunit.AU)
randomAccess := h265.IsRandomAccess(u.Payload.(unit.PayloadH265))
if dtsExtractor == nil {
if !randomAccess {
@ -104,22 +102,22 @@ func (f *formatMPEGTS) initialize() bool {
dtsExtractor.Initialize()
}
dts, err := dtsExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := dtsExtractor.Extract(u.Payload.(unit.PayloadH265), u.PTS)
if err != nil {
return err
}
return f.write(
timestampToDuration(dts, clockRate),
tunit.NTP,
u.NTP,
true,
randomAccess,
func() error {
return f.mw.WriteH265(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
dts,
tunit.AU)
u.Payload.(unit.PayloadH265))
},
)
})
@ -132,14 +130,12 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.H264)
if tunit.AU == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
randomAccess := h264.IsRandomAccess(tunit.AU)
randomAccess := h264.IsRandomAccess(u.Payload.(unit.PayloadH264))
if dtsExtractor == nil {
if !randomAccess {
@ -149,22 +145,22 @@ func (f *formatMPEGTS) initialize() bool {
dtsExtractor.Initialize()
}
dts, err := dtsExtractor.Extract(tunit.AU, tunit.PTS)
dts, err := dtsExtractor.Extract(u.Payload.(unit.PayloadH264), u.PTS)
if err != nil {
return err
}
return f.write(
timestampToDuration(dts, clockRate),
tunit.NTP,
u.NTP,
true,
randomAccess,
func() error {
return f.mw.WriteH264(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
dts,
tunit.AU)
u.Payload.(unit.PayloadH264))
},
)
})
@ -178,32 +174,31 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Video)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("MPEG-4 Video streams with B-frames are not supported (yet)")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
randomAccess := bytes.Contains(tunit.Frame, []byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
randomAccess := bytes.Contains(u.Payload.(unit.PayloadMPEG4Video),
[]byte{0, 0, 1, byte(mpeg4video.GroupOfVOPStartCode)})
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
true,
randomAccess,
func() error {
return f.mw.WriteMPEG4Video(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
tunit.Frame)
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.Payload.(unit.PayloadMPEG4Video))
},
)
})
@ -217,32 +212,30 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Video)
if tunit.Frame == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
if !firstReceived {
firstReceived = true
} else if tunit.PTS < lastPTS {
} else if u.PTS < lastPTS {
return fmt.Errorf("MPEG-1 Video streams with B-frames are not supported (yet)")
}
lastPTS = tunit.PTS
lastPTS = u.PTS
randomAccess := bytes.Contains(tunit.Frame, []byte{0, 0, 1, 0xB8})
randomAccess := bytes.Contains(u.Payload.(unit.PayloadMPEG1Video), []byte{0, 0, 1, 0xB8})
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
true,
randomAccess,
func() error {
return f.mw.WriteMPEG1Video(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
tunit.Frame)
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.Payload.(unit.PayloadMPEG1Video))
},
)
})
@ -255,23 +248,21 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.Opus)
if tunit.Packets == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
false,
true,
func() error {
return f.mw.WriteOpus(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
tunit.Packets)
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
u.Payload.(unit.PayloadOpus))
},
)
})
@ -284,20 +275,18 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.KLV)
if tunit.Unit == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
return f.write(
timestampToDuration(tunit.PTS, 90000),
tunit.NTP,
timestampToDuration(u.PTS, 90000),
u.NTP,
false,
true,
func() error {
return f.mw.WriteKLV(track, multiplyAndDivide(tunit.PTS, 90000, 90000), tunit.Unit)
return f.mw.WriteKLV(track, multiplyAndDivide(u.PTS, 90000, 90000), u.Payload.(unit.PayloadKLV))
},
)
})
@ -310,23 +299,21 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4Audio)
if tunit.AUs == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
false,
true,
func() error {
return f.mw.WriteMPEG4Audio(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
tunit.AUs)
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
u.Payload.(unit.PayloadMPEG4Audio))
},
)
})
@ -340,29 +327,27 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG4AudioLATM)
if tunit.Element == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
var ame mpeg4audio.AudioMuxElement
ame.StreamMuxConfig = forma.StreamMuxConfig
err := ame.Unmarshal(tunit.Element)
err := ame.Unmarshal(u.Payload.(unit.PayloadMPEG4AudioLATM))
if err != nil {
return err
}
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
false,
true,
func() error {
return f.mw.WriteMPEG4Audio(
track,
multiplyAndDivide(tunit.PTS, 90000, int64(clockRate)),
multiplyAndDivide(u.PTS, 90000, int64(clockRate)),
[][]byte{ame.Payloads[0][0][0]})
},
)
@ -375,23 +360,21 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.MPEG1Audio)
if tunit.Frames == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
false,
true,
func() error {
return f.mw.WriteMPEG1Audio(
track,
tunit.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
tunit.Frames)
u.PTS, // no conversion is needed since clock rate is 90khz in both MPEG-TS and RTSP
u.Payload.(unit.PayloadMPEG1Audio))
},
)
})
@ -402,21 +385,19 @@ func (f *formatMPEGTS) initialize() bool {
f.ri.reader.OnData(
media,
forma,
func(u unit.Unit) error {
tunit := u.(*unit.AC3)
if tunit.Frames == nil {
func(u *unit.Unit) error {
if u.NilPayload() {
return nil
}
return f.write(
timestampToDuration(tunit.PTS, clockRate),
tunit.NTP,
timestampToDuration(u.PTS, clockRate),
u.NTP,
false,
true,
func() error {
for i, frame := range tunit.Frames {
framePTS := tunit.PTS + int64(i)*ac3.SamplesPerFrame
for i, frame := range u.Payload.(unit.PayloadAC3) {
framePTS := u.PTS + int64(i)*ac3.SamplesPerFrame
err := f.mw.WriteAC3(
track,

View file

@ -75,23 +75,19 @@ func TestRecorder(t *testing.T) {
pts := startDTS + int64(i)*100*90000/1000
ntp := startNTP.Add(time.Duration(i*60) * time.Second)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: pts,
NTP: ntp,
},
AU: [][]byte{
Payload: unit.PayloadH264{
test.FormatH264.SPS,
test.FormatH264.PPS,
{5}, // IDR
},
})
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.H265{
Base: unit.Base{
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.Unit{
PTS: pts,
},
AU: [][]byte{
Payload: unit.PayloadH265{
test.FormatH265.VPS,
test.FormatH265.SPS,
test.FormatH265.PPS,
@ -99,25 +95,19 @@ func TestRecorder(t *testing.T) {
},
})
strm.WriteUnit(desc.Medias[2], desc.Medias[2].Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(desc.Medias[2], desc.Medias[2].Formats[0], &unit.Unit{
PTS: pts * int64(desc.Medias[2].Formats[0].ClockRate()) / 90000,
},
AUs: [][]byte{{1, 2, 3, 4}},
Payload: unit.PayloadMPEG4Audio{{1, 2, 3, 4}},
})
strm.WriteUnit(desc.Medias[3], desc.Medias[3].Formats[0], &unit.G711{
Base: unit.Base{
strm.WriteUnit(desc.Medias[3], desc.Medias[3].Formats[0], &unit.Unit{
PTS: pts * int64(desc.Medias[3].Formats[0].ClockRate()) / 90000,
},
Samples: []byte{1, 2, 3, 4},
Payload: unit.PayloadG711{1, 2, 3, 4},
})
strm.WriteUnit(desc.Medias[4], desc.Medias[4].Formats[0], &unit.LPCM{
Base: unit.Base{
strm.WriteUnit(desc.Medias[4], desc.Medias[4].Formats[0], &unit.Unit{
PTS: pts * int64(desc.Medias[4].Formats[0].ClockRate()) / 90000,
},
Samples: []byte{1, 2, 3, 4},
Payload: unit.PayloadLPCM{1, 2, 3, 4},
})
}
}
@ -208,11 +198,9 @@ func TestRecorder(t *testing.T) {
time.Date(2008, 5, 20, 22, 16, 25, 0, time.UTC))
// simulate a write error
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: 0,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5}, // IDR
},
})
@ -369,23 +357,19 @@ func TestRecorderFMP4NegativeDTS(t *testing.T) {
w.Initialize()
for i := 0; i < 3; i++ {
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: -50*90000/1000 + (int64(i) * 200 * 90000 / 1000),
NTP: time.Date(2008, 5, 20, 22, 15, 25, 0, time.UTC),
},
AU: [][]byte{
Payload: unit.PayloadH264{
test.FormatH264.SPS,
test.FormatH264.PPS,
{5}, // IDR
},
})
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.Unit{
PTS: -100*44100/1000 + (int64(i) * 200 * 44100 / 1000),
},
AUs: [][]byte{{1, 2, 3, 4}},
Payload: unit.PayloadMPEG4Audio{{1, 2, 3, 4}},
})
}
@ -595,12 +579,10 @@ func TestRecorderFMP4SegmentSwitch(t *testing.T) {
pts := 50 * time.Second
ntp := time.Date(2008, 5, 20, 22, 15, 25, 0, time.UTC)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: int64(pts) * 90000 / int64(time.Second),
NTP: ntp,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5}, // IDR
},
})
@ -608,23 +590,19 @@ func TestRecorderFMP4SegmentSwitch(t *testing.T) {
pts += 700 * time.Millisecond
ntp = ntp.Add(700 * time.Millisecond)
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.MPEG4Audio{ // segment switch should happen here
Base: unit.Base{
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.Unit{ // segment switch should happen here
PTS: int64(pts) * 44100 / int64(time.Second),
NTP: ntp,
},
AUs: [][]byte{{1, 2}},
Payload: unit.PayloadMPEG4Audio{{1, 2}},
})
pts += 400 * time.Millisecond
ntp = ntp.Add(400 * time.Millisecond)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: int64(pts) * 90000 / int64(time.Second),
NTP: ntp,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5}, // IDR
},
})
@ -632,23 +610,19 @@ func TestRecorderFMP4SegmentSwitch(t *testing.T) {
pts += 100 * time.Millisecond
ntp = ntp.Add(100 * time.Millisecond)
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(desc.Medias[1], desc.Medias[1].Formats[0], &unit.Unit{
PTS: int64(pts) * 44100 / int64(time.Second),
NTP: ntp,
},
AUs: [][]byte{{3, 4}},
Payload: unit.PayloadMPEG4Audio{{3, 4}},
})
pts += 400 * time.Millisecond
ntp = ntp.Add(400 * time.Millisecond)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: int64(pts) * 90000 / int64(time.Second),
NTP: ntp,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5}, // IDR
},
})

View file

@ -286,21 +286,17 @@ func TestServerRead(t *testing.T) {
time.Sleep(100 * time.Millisecond)
for i := 0; i < 4; i++ {
strm.WriteUnit(test.MediaH264, test.FormatH264, &unit.H264{
Base: unit.Base{
strm.WriteUnit(test.MediaH264, test.FormatH264, &unit.Unit{
NTP: time.Time{},
PTS: int64(i) * 90000,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 1}, // IDR
},
})
strm.WriteUnit(test.MediaMPEG4Audio, test.FormatMPEG4Audio, &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(test.MediaMPEG4Audio, test.FormatMPEG4Audio, &unit.Unit{
NTP: time.Time{},
PTS: int64(i) * 44100,
},
AUs: [][]byte{{1, 2}},
Payload: unit.PayloadMPEG4Audio{{1, 2}},
})
}
@ -331,21 +327,17 @@ func TestServerRead(t *testing.T) {
time.Sleep(500 * time.Millisecond)
for i := range 4 {
strm.WriteUnit(test.MediaH264, test.FormatH264, &unit.H264{
Base: unit.Base{
strm.WriteUnit(test.MediaH264, test.FormatH264, &unit.Unit{
NTP: time.Time{},
PTS: int64(i) * 90000,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 1}, // IDR
},
})
strm.WriteUnit(test.MediaMPEG4Audio, test.FormatMPEG4Audio, &unit.MPEG4Audio{
Base: unit.Base{
strm.WriteUnit(test.MediaMPEG4Audio, test.FormatMPEG4Audio, &unit.Unit{
NTP: time.Time{},
PTS: int64(i) * 44100,
},
AUs: [][]byte{{1, 2}},
Payload: unit.PayloadMPEG4Audio{{1, 2}},
})
}

View file

@ -150,12 +150,12 @@ func TestServerPublish(t *testing.T) {
r.OnData(
strm.Desc.Medias[0],
strm.Desc.Medias[0].Formats[0],
func(u unit.Unit) error {
require.Equal(t, [][]byte{
func(u *unit.Unit) error {
require.Equal(t, unit.PayloadH264{
test.FormatH264.SPS,
test.FormatH264.PPS,
{5, 2, 3, 4},
}, u.(*unit.H264).AU)
}, u.Payload)
close(recv)
return nil
})
@ -260,31 +260,25 @@ func TestServerRead(t *testing.T) {
go func() {
time.Sleep(500 * time.Millisecond)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
NTP: time.Time{},
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2, 3, 4}, // IDR
},
})
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
NTP: time.Time{},
PTS: 2 * 90000,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2, 3, 4}, // IDR
},
})
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
NTP: time.Time{},
PTS: 3 * 90000,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2, 3, 4}, // IDR
},
})

View file

@ -140,12 +140,12 @@ func TestServerPublish(t *testing.T) {
r.OnData(
strm.Desc.Medias[0],
strm.Desc.Medias[0].Formats[0],
func(u unit.Unit) error {
require.Equal(t, [][]byte{
func(u *unit.Unit) error {
require.Equal(t, unit.PayloadH264{
test.FormatH264.SPS,
test.FormatH264.PPS,
{5, 2, 3, 4},
}, u.(*unit.H264).AU)
}, u.Payload)
close(recv)
return nil
})
@ -304,11 +304,9 @@ func TestServerRead(t *testing.T) {
_, err = reader.Play(nil)
require.NoError(t, err)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
NTP: time.Time{},
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2, 3, 4}, // IDR
},
})

View file

@ -132,12 +132,12 @@ func TestServerPublish(t *testing.T) {
r.OnData(
strm.Desc.Medias[0],
strm.Desc.Medias[0].Formats[0],
func(u unit.Unit) error {
require.Equal(t, [][]byte{
func(u *unit.Unit) error {
require.Equal(t, unit.PayloadH264{
test.FormatH264.SPS,
test.FormatH264.PPS,
{0x05, 1}, // IDR
}, u.(*unit.H264).AU)
}, u.Payload)
close(recv)
return nil
})
@ -215,11 +215,9 @@ func TestServerRead(t *testing.T) {
time.Sleep(500 * time.Millisecond)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
NTP: time.Time{},
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 1}, // IDR
},
})
@ -247,11 +245,9 @@ func TestServerRead(t *testing.T) {
return nil
})
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
NTP: time.Time{},
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2},
},
})

View file

@ -298,16 +298,16 @@ func TestServerPublish(t *testing.T) {
r.OnData(
strm.Desc.Medias[0],
strm.Desc.Medias[0].Formats[0],
func(u unit.Unit) error {
func(u *unit.Unit) error {
select {
case <-recv:
return nil
default:
}
require.Equal(t, [][]byte{
require.Equal(t, unit.PayloadH264{
{1},
}, u.(*unit.H264).AU)
}, u.Payload)
close(recv)
return nil
@ -336,7 +336,7 @@ func TestServerRead(t *testing.T) {
for _, ca := range []struct {
name string
medias []*description.Media
unit unit.Unit
unit *unit.Unit
outRTPPayload []byte
}{
{
@ -347,8 +347,8 @@ func TestServerRead(t *testing.T) {
PayloadTyp: 96,
}},
}},
&unit.AV1{
TU: [][]byte{{1, 2}},
&unit.Unit{
Payload: unit.PayloadAV1{{1, 2}},
},
[]byte{0x10, 0x01, 0x02},
},
@ -360,8 +360,8 @@ func TestServerRead(t *testing.T) {
PayloadTyp: 96,
}},
}},
&unit.VP9{
Frame: []byte{0x82, 0x49, 0x83, 0x42, 0x0, 0x77, 0xf0, 0x32, 0x34},
&unit.Unit{
Payload: unit.PayloadVP9{0x82, 0x49, 0x83, 0x42, 0x0, 0x77, 0xf0, 0x32, 0x34},
},
[]byte{
0x8f, 0xa0, 0xfd, 0x18, 0x07, 0x80, 0x03, 0x24,
@ -377,16 +377,16 @@ func TestServerRead(t *testing.T) {
PayloadTyp: 96,
}},
}},
&unit.VP8{
Frame: []byte{1, 2},
&unit.Unit{
Payload: unit.PayloadVP8{1, 2},
},
[]byte{0x10, 1, 2},
},
{
"h264",
[]*description.Media{test.MediaH264},
&unit.H264{
AU: [][]byte{
&unit.Unit{
Payload: unit.PayloadH264{
{5, 1},
},
},
@ -407,8 +407,8 @@ func TestServerRead(t *testing.T) {
ChannelCount: 2,
}},
}},
&unit.Opus{
Packets: [][]byte{{1, 2}},
&unit.Unit{
Payload: unit.PayloadOpus{{1, 2}},
},
[]byte{1, 2},
},
@ -418,8 +418,7 @@ func TestServerRead(t *testing.T) {
Type: description.MediaTypeAudio,
Formats: []format.Format{&format.G722{}},
}},
&unit.Generic{
Base: unit.Base{
&unit.Unit{
RTPPackets: []*rtp.Packet{{
Header: rtp.Header{
Version: 2,
@ -432,7 +431,6 @@ func TestServerRead(t *testing.T) {
Payload: []byte{1, 2},
}},
},
},
[]byte{1, 2},
},
{
@ -445,8 +443,8 @@ func TestServerRead(t *testing.T) {
ChannelCount: 1,
}},
}},
&unit.G711{
Samples: []byte{1, 2, 3},
&unit.Unit{
Payload: unit.PayloadG711{1, 2, 3},
},
[]byte{1, 2, 3},
},
@ -460,8 +458,8 @@ func TestServerRead(t *testing.T) {
ChannelCount: 2,
}},
}},
&unit.G711{
Samples: []byte{1, 2, 3, 4},
&unit.Unit{
Payload: unit.PayloadG711{1, 2, 3, 4},
},
[]byte{0x86, 0x84, 0x8a, 0x84, 0x8e, 0x84, 0x92, 0x84},
},
@ -476,8 +474,8 @@ func TestServerRead(t *testing.T) {
ChannelCount: 2,
}},
}},
&unit.LPCM{
Samples: []byte{1, 2, 3, 4},
&unit.Unit{
Payload: unit.PayloadLPCM{1, 2, 3, 4},
},
[]byte{1, 2, 3, 4},
},
@ -489,7 +487,7 @@ func TestServerRead(t *testing.T) {
WriteQueueSize: 512,
RTPMaxPayloadSize: 1450,
Desc: desc,
GenerateRTPPackets: reflect.TypeOf(ca.unit) != reflect.TypeOf(&unit.Generic{}),
GenerateRTPPackets: ca.unit.Payload != nil,
Parent: test.NilLogger,
}
err := strm.Initialize()
@ -555,11 +553,11 @@ func TestServerRead(t *testing.T) {
r := reflect.New(reflect.TypeOf(ca.unit).Elem())
r.Elem().Set(reflect.ValueOf(ca.unit).Elem())
if g, ok := r.Interface().(*unit.Generic); ok {
clone := *g.RTPPackets[0]
if ca.unit.Payload == nil {
clone := *ca.unit.RTPPackets[0]
strm.WriteRTPPacket(desc.Medias[0], desc.Medias[0].Formats[0], &clone, time.Time{}, 0)
} else {
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], r.Interface().(unit.Unit))
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], r.Interface().(*unit.Unit))
}
}()

View file

@ -287,8 +287,8 @@ func (s *Source) runSecondary(params defs.StaticSourceRunParams) error {
rdr.OnData(
origStream.Desc.Medias[1],
origStream.Desc.Medias[1].Formats[0],
func(u unit.Unit) error {
pkt := u.GetRTPPackets()[0]
func(u *unit.Unit) error {
pkt := u.RTPPackets[0]
newPkt := &rtp.Packet{
Header: pkt.Header,
@ -296,7 +296,7 @@ func (s *Source) runSecondary(params defs.StaticSourceRunParams) error {
}
newPkt.PayloadType = 26
res.Stream.WriteRTPPacket(media, media.Formats[0], newPkt, u.GetNTP(), u.GetPTS())
res.Stream.WriteRTPPacket(media, media.Formats[0], newPkt, u.NTP, u.PTS)
return nil
})

View file

@ -12,7 +12,7 @@ import (
)
// OnDataFunc is the callback passed to OnData().
type OnDataFunc func(unit.Unit) error
type OnDataFunc func(*unit.Unit) error
// Reader is a stream reader.
type Reader struct {

View file

@ -187,7 +187,7 @@ func (s *Stream) RemoveReader(r *Reader) {
}
// WriteUnit writes a Unit.
func (s *Stream) WriteUnit(medi *description.Media, forma format.Format, u unit.Unit) {
func (s *Stream) WriteUnit(medi *description.Media, forma format.Format, u *unit.Unit) {
sm := s.medias[medi]
sf := sm.formats[forma]

View file

@ -14,9 +14,9 @@ import (
"github.com/bluenviron/mediamtx/internal/unit"
)
func unitSize(u unit.Unit) uint64 {
func unitSize(u *unit.Unit) uint64 {
n := uint64(0)
for _, pkt := range u.GetRTPPackets() {
for _, pkt := range u.RTPPackets {
n += uint64(pkt.MarshalSize())
}
return n
@ -45,7 +45,7 @@ func (sf *streamFormat) initialize() error {
return nil
}
func (sf *streamFormat) writeUnit(s *Stream, medi *description.Media, u unit.Unit) {
func (sf *streamFormat) writeUnit(s *Stream, medi *description.Media, u *unit.Unit) {
err := sf.proc.ProcessUnit(u)
if err != nil {
sf.processingErrors.Increase()
@ -64,7 +64,13 @@ func (sf *streamFormat) writeRTPPacket(
) {
hasNonRTSPReaders := len(sf.onDatas) > 0
u, err := sf.proc.ProcessRTPPacket(pkt, ntp, pts, hasNonRTSPReaders)
u := &unit.Unit{
PTS: pts,
NTP: ntp,
RTPPackets: []*rtp.Packet{pkt},
}
err := sf.proc.ProcessRTPPacket(u, hasNonRTSPReaders)
if err != nil {
sf.processingErrors.Increase()
return
@ -73,20 +79,20 @@ func (sf *streamFormat) writeRTPPacket(
sf.writeUnitInner(s, medi, u)
}
func (sf *streamFormat) writeUnitInner(s *Stream, medi *description.Media, u unit.Unit) {
func (sf *streamFormat) writeUnitInner(s *Stream, medi *description.Media, u *unit.Unit) {
size := unitSize(u)
atomic.AddUint64(s.bytesReceived, size)
if s.rtspStream != nil {
for _, pkt := range u.GetRTPPackets() {
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, u.GetNTP()) //nolint:errcheck
for _, pkt := range u.RTPPackets {
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, u.NTP) //nolint:errcheck
}
}
if s.rtspsStream != nil {
for _, pkt := range u.GetRTPPackets() {
s.rtspsStream.WritePacketRTPWithNTP(medi, pkt, u.GetNTP()) //nolint:errcheck
for _, pkt := range u.RTPPackets {
s.rtspsStream.WritePacketRTPWithNTP(medi, pkt, u.NTP) //nolint:errcheck
}
}

View file

@ -35,7 +35,7 @@ func TestStream(t *testing.T) {
recv := make(chan struct{})
r.OnData(desc.Medias[0], desc.Medias[0].Formats[0], func(_ unit.Unit) error {
r.OnData(desc.Medias[0], desc.Medias[0].Formats[0], func(_ *unit.Unit) error {
close(recv)
return nil
})
@ -43,11 +43,9 @@ func TestStream(t *testing.T) {
strm.AddReader(r)
defer strm.RemoveReader(r)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: 30000 * 2,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2}, // IDR
},
})
@ -86,7 +84,7 @@ func TestStreamSkipBytesSent(t *testing.T) {
recv := make(chan struct{})
r.OnData(desc.Medias[0], desc.Medias[0].Formats[0], func(_ unit.Unit) error {
r.OnData(desc.Medias[0], desc.Medias[0].Formats[0], func(_ *unit.Unit) error {
close(recv)
return nil
})
@ -94,11 +92,9 @@ func TestStreamSkipBytesSent(t *testing.T) {
strm.AddReader(r)
defer strm.RemoveReader(r)
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.H264{
Base: unit.Base{
strm.WriteUnit(desc.Medias[0], desc.Medias[0].Formats[0], &unit.Unit{
PTS: 30000 * 2,
},
AU: [][]byte{
Payload: unit.PayloadH264{
{5, 2}, // IDR
},
})

View file

@ -11,7 +11,7 @@ import (
type StaticSourceParent struct {
stream *stream.Stream
reader *stream.Reader
Unit chan unit.Unit
Unit chan *unit.Unit
}
// Log implements logger.Writer.
@ -19,7 +19,7 @@ func (*StaticSourceParent) Log(logger.Level, string, ...interface{}) {}
// Initialize initializes StaticSourceParent.
func (p *StaticSourceParent) Initialize() {
p.Unit = make(chan unit.Unit)
p.Unit = make(chan *unit.Unit)
}
// Close closes StaticSourceParent.
@ -46,7 +46,7 @@ func (p *StaticSourceParent) SetReady(req defs.PathSourceStaticSetReadyReq) defs
p.reader.OnData(
req.Desc.Medias[0],
req.Desc.Medias[0].Formats[0],
func(u unit.Unit) error {
func(u *unit.Unit) error {
p.Unit <- u
close(p.Unit)
return nil

View file

@ -1,7 +0,0 @@
package unit
// AC3 is a AC-3 data unit.
type AC3 struct {
Base
Frames [][]byte
}

View file

@ -1,7 +0,0 @@
package unit
// AV1 is an AV1 data unit.
type AV1 struct {
Base
TU [][]byte
}

View file

@ -1,29 +0,0 @@
package unit
import (
"time"
"github.com/pion/rtp"
)
// Base contains fields shared across all units.
type Base struct {
RTPPackets []*rtp.Packet
NTP time.Time
PTS int64
}
// GetRTPPackets implements Unit.
func (u *Base) GetRTPPackets() []*rtp.Packet {
return u.RTPPackets
}
// GetNTP implements Unit.
func (u *Base) GetNTP() time.Time {
return u.NTP
}
// GetPTS implements Unit.
func (u *Base) GetPTS() int64 {
return u.PTS
}

View file

@ -1,7 +0,0 @@
package unit
// G711 is a G711 data unit.
type G711 struct {
Base
Samples []byte
}

View file

@ -1,6 +0,0 @@
package unit
// Generic is a generic data unit.
type Generic struct {
Base
}

View file

@ -1,7 +0,0 @@
package unit
// H264 is a H264 data unit.
type H264 struct {
Base
AU [][]byte
}

View file

@ -1,7 +0,0 @@
package unit
// H265 is a H265 data unit.
type H265 struct {
Base
AU [][]byte
}

View file

@ -1,7 +0,0 @@
package unit
// KLV is a KLV data unit.
type KLV struct {
Base
Unit []byte
}

View file

@ -1,7 +0,0 @@
package unit
// LPCM is a LPCM data unit.
type LPCM struct {
Base
Samples []byte
}

View file

@ -1,7 +0,0 @@
package unit
// MJPEG is a M-JPEG data unit.
type MJPEG struct {
Base
Frame []byte
}

View file

@ -1,7 +0,0 @@
package unit
// MPEG1Audio is a MPEG-1/2 Audio data unit.
type MPEG1Audio struct {
Base
Frames [][]byte
}

View file

@ -1,7 +0,0 @@
package unit
// MPEG1Video is a MPEG-1/2 Video data unit.
type MPEG1Video struct {
Base
Frame []byte
}

View file

@ -1,7 +0,0 @@
package unit
// MPEG4Audio is a MPEG-4 Audio data unit.
type MPEG4Audio struct {
Base
AUs [][]byte
}

View file

@ -1,7 +0,0 @@
package unit
// MPEG4AudioLATM is a MPEG-4 Audio LATM data unit.
type MPEG4AudioLATM struct {
Base
Element []byte
}

View file

@ -1,7 +0,0 @@
package unit
// MPEG4Video is a MPEG-4 Video data unit.
type MPEG4Video struct {
Base
Frame []byte
}

View file

@ -1,7 +0,0 @@
package unit
// Opus is a Opus data unit.
type Opus struct {
Base
Packets [][]byte
}

6
internal/unit/payload.go Normal file
View file

@ -0,0 +1,6 @@
package unit
// Payload is a codec-dependent payload.
type Payload interface {
isPayload()
}

View file

@ -0,0 +1,6 @@
package unit
// PayloadAC3 is the payload of an AC3 track.
type PayloadAC3 [][]byte
func (PayloadAC3) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadAV1 is the payload of an AV1 track.
type PayloadAV1 [][]byte
func (PayloadAV1) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadG711 is the payload of a G711 track.
type PayloadG711 []byte
func (PayloadG711) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadH264 is the payload of a H264 track.
type PayloadH264 [][]byte
func (PayloadH264) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadH265 is the payload of a H265 track.
type PayloadH265 [][]byte
func (PayloadH265) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadKLV is the payload of a KLV track.
type PayloadKLV []byte
func (PayloadKLV) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadLPCM is the payload of a LPCM track.
type PayloadLPCM []byte
func (PayloadLPCM) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadMJPEG is the payload of a MJPEG track.
type PayloadMJPEG []byte
func (PayloadMJPEG) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadMPEG1Audio is the payload of a MPEG-1 Audio track.
type PayloadMPEG1Audio [][]byte
func (PayloadMPEG1Audio) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadMPEG1Video is the payload of a MPEG-1 Video track.
type PayloadMPEG1Video []byte
func (PayloadMPEG1Video) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadMPEG4Audio is the payload of a MPEG-4 Audio track.
type PayloadMPEG4Audio [][]byte
func (PayloadMPEG4Audio) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadMPEG4AudioLATM is the payload of a MPEG-4 Audio LATM track.
type PayloadMPEG4AudioLATM []byte
func (PayloadMPEG4AudioLATM) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadMPEG4Video is the payload of a MPEG-4 Video track.
type PayloadMPEG4Video []byte
func (PayloadMPEG4Video) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadOpus is the payload of a Opus track.
type PayloadOpus [][]byte
func (PayloadOpus) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadVP8 is the payload of a VP8 track.
type PayloadVP8 []byte
func (PayloadVP8) isPayload() {}

View file

@ -0,0 +1,6 @@
package unit
// PayloadVP9 is the payload of a VP9 track.
type PayloadVP9 []byte
func (PayloadVP9) isPayload() {}

View file

@ -1,20 +1,29 @@
// Package unit contains the Unit definition.
// Package unit contains the unit definition.
package unit
import (
"reflect"
"time"
"github.com/pion/rtp"
)
// Unit is the elementary data unit routed across the server.
type Unit interface {
// returns RTP packets contained into the unit.
GetRTPPackets() []*rtp.Packet
// Unit is an atomic unit of a stream.
type Unit struct {
// relative time
PTS int64
// returns the NTP timestamp of the unit.
GetNTP() time.Time
// absolute time
NTP time.Time
// returns the PTS of the unit.
GetPTS() int64
// RTP packets
RTPPackets []*rtp.Packet
// codec-dependent payload
Payload Payload
}
// NilPayload checks whether the payload is nil.
func (u Unit) NilPayload() bool {
return u.Payload == nil || reflect.ValueOf(u.Payload).IsNil()
}

View file

@ -1,7 +0,0 @@
package unit
// VP8 is a VP8 data unit.
type VP8 struct {
Base
Frame []byte
}

View file

@ -1,7 +0,0 @@
package unit
// VP9 is a VP9 data unit.
type VP9 struct {
Base
Frame []byte
}