mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-20 02:00:05 -08:00
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
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:
parent
e2294836f5
commit
f5f03562d3
86 changed files with 1100 additions and 1602 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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}},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// AC3 is a AC-3 data unit.
|
||||
type AC3 struct {
|
||||
Base
|
||||
Frames [][]byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// AV1 is an AV1 data unit.
|
||||
type AV1 struct {
|
||||
Base
|
||||
TU [][]byte
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// G711 is a G711 data unit.
|
||||
type G711 struct {
|
||||
Base
|
||||
Samples []byte
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package unit
|
||||
|
||||
// Generic is a generic data unit.
|
||||
type Generic struct {
|
||||
Base
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// H264 is a H264 data unit.
|
||||
type H264 struct {
|
||||
Base
|
||||
AU [][]byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// H265 is a H265 data unit.
|
||||
type H265 struct {
|
||||
Base
|
||||
AU [][]byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// KLV is a KLV data unit.
|
||||
type KLV struct {
|
||||
Base
|
||||
Unit []byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// LPCM is a LPCM data unit.
|
||||
type LPCM struct {
|
||||
Base
|
||||
Samples []byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// MJPEG is a M-JPEG data unit.
|
||||
type MJPEG struct {
|
||||
Base
|
||||
Frame []byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// MPEG1Audio is a MPEG-1/2 Audio data unit.
|
||||
type MPEG1Audio struct {
|
||||
Base
|
||||
Frames [][]byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// MPEG1Video is a MPEG-1/2 Video data unit.
|
||||
type MPEG1Video struct {
|
||||
Base
|
||||
Frame []byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// MPEG4Audio is a MPEG-4 Audio data unit.
|
||||
type MPEG4Audio struct {
|
||||
Base
|
||||
AUs [][]byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// MPEG4AudioLATM is a MPEG-4 Audio LATM data unit.
|
||||
type MPEG4AudioLATM struct {
|
||||
Base
|
||||
Element []byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// MPEG4Video is a MPEG-4 Video data unit.
|
||||
type MPEG4Video struct {
|
||||
Base
|
||||
Frame []byte
|
||||
}
|
||||
|
|
@ -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
6
internal/unit/payload.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// Payload is a codec-dependent payload.
|
||||
type Payload interface {
|
||||
isPayload()
|
||||
}
|
||||
6
internal/unit/payload_ac3.go
Normal file
6
internal/unit/payload_ac3.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadAC3 is the payload of an AC3 track.
|
||||
type PayloadAC3 [][]byte
|
||||
|
||||
func (PayloadAC3) isPayload() {}
|
||||
6
internal/unit/payload_av1.go
Normal file
6
internal/unit/payload_av1.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadAV1 is the payload of an AV1 track.
|
||||
type PayloadAV1 [][]byte
|
||||
|
||||
func (PayloadAV1) isPayload() {}
|
||||
6
internal/unit/payload_g711.go
Normal file
6
internal/unit/payload_g711.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadG711 is the payload of a G711 track.
|
||||
type PayloadG711 []byte
|
||||
|
||||
func (PayloadG711) isPayload() {}
|
||||
6
internal/unit/payload_h264.go
Normal file
6
internal/unit/payload_h264.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadH264 is the payload of a H264 track.
|
||||
type PayloadH264 [][]byte
|
||||
|
||||
func (PayloadH264) isPayload() {}
|
||||
6
internal/unit/payload_h265.go
Normal file
6
internal/unit/payload_h265.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadH265 is the payload of a H265 track.
|
||||
type PayloadH265 [][]byte
|
||||
|
||||
func (PayloadH265) isPayload() {}
|
||||
6
internal/unit/payload_klv.go
Normal file
6
internal/unit/payload_klv.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadKLV is the payload of a KLV track.
|
||||
type PayloadKLV []byte
|
||||
|
||||
func (PayloadKLV) isPayload() {}
|
||||
6
internal/unit/payload_lpcm.go
Normal file
6
internal/unit/payload_lpcm.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadLPCM is the payload of a LPCM track.
|
||||
type PayloadLPCM []byte
|
||||
|
||||
func (PayloadLPCM) isPayload() {}
|
||||
6
internal/unit/payload_mjpeg.go
Normal file
6
internal/unit/payload_mjpeg.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadMJPEG is the payload of a MJPEG track.
|
||||
type PayloadMJPEG []byte
|
||||
|
||||
func (PayloadMJPEG) isPayload() {}
|
||||
6
internal/unit/payload_mpeg1_audio.go
Normal file
6
internal/unit/payload_mpeg1_audio.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadMPEG1Audio is the payload of a MPEG-1 Audio track.
|
||||
type PayloadMPEG1Audio [][]byte
|
||||
|
||||
func (PayloadMPEG1Audio) isPayload() {}
|
||||
6
internal/unit/payload_mpeg1_video.go
Normal file
6
internal/unit/payload_mpeg1_video.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadMPEG1Video is the payload of a MPEG-1 Video track.
|
||||
type PayloadMPEG1Video []byte
|
||||
|
||||
func (PayloadMPEG1Video) isPayload() {}
|
||||
6
internal/unit/payload_mpeg4_audio.go
Normal file
6
internal/unit/payload_mpeg4_audio.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadMPEG4Audio is the payload of a MPEG-4 Audio track.
|
||||
type PayloadMPEG4Audio [][]byte
|
||||
|
||||
func (PayloadMPEG4Audio) isPayload() {}
|
||||
6
internal/unit/payload_mpeg4_audio_latm.go
Normal file
6
internal/unit/payload_mpeg4_audio_latm.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadMPEG4AudioLATM is the payload of a MPEG-4 Audio LATM track.
|
||||
type PayloadMPEG4AudioLATM []byte
|
||||
|
||||
func (PayloadMPEG4AudioLATM) isPayload() {}
|
||||
6
internal/unit/payload_mpeg4_video.go
Normal file
6
internal/unit/payload_mpeg4_video.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadMPEG4Video is the payload of a MPEG-4 Video track.
|
||||
type PayloadMPEG4Video []byte
|
||||
|
||||
func (PayloadMPEG4Video) isPayload() {}
|
||||
6
internal/unit/payload_opus.go
Normal file
6
internal/unit/payload_opus.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadOpus is the payload of a Opus track.
|
||||
type PayloadOpus [][]byte
|
||||
|
||||
func (PayloadOpus) isPayload() {}
|
||||
6
internal/unit/payload_vp8.go
Normal file
6
internal/unit/payload_vp8.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadVP8 is the payload of a VP8 track.
|
||||
type PayloadVP8 []byte
|
||||
|
||||
func (PayloadVP8) isPayload() {}
|
||||
6
internal/unit/payload_vp9.go
Normal file
6
internal/unit/payload_vp9.go
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
package unit
|
||||
|
||||
// PayloadVP9 is the payload of a VP9 track.
|
||||
type PayloadVP9 []byte
|
||||
|
||||
func (PayloadVP9) isPayload() {}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// VP8 is a VP8 data unit.
|
||||
type VP8 struct {
|
||||
Base
|
||||
Frame []byte
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package unit
|
||||
|
||||
// VP9 is a VP9 data unit.
|
||||
type VP9 struct {
|
||||
Base
|
||||
Frame []byte
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue