support routing KLV metadata (#2693) (#4670)

Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com>
This commit is contained in:
Yaroslav Molochko 2025-07-06 21:34:41 +03:00 committed by GitHub
parent 1083eea307
commit 0df5e2c81a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 301 additions and 6 deletions

View file

@ -0,0 +1,119 @@
package formatprocessor
import (
"fmt"
"time"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/gortsplib/v4/pkg/format/rtpklv"
"github.com/pion/rtp"
"github.com/bluenviron/mediamtx/internal/logger"
"github.com/bluenviron/mediamtx/internal/unit"
)
type klv struct {
RTPMaxPayloadSize int
Format *format.KLV
GenerateRTPPackets bool
Parent logger.Writer
encoder *rtpklv.Encoder
decoder *rtpklv.Decoder
randomStart uint32
}
func (t *klv) initialize() error {
if t.GenerateRTPPackets {
err := t.createEncoder()
if err != nil {
return err
}
t.randomStart, err = randUint32()
if err != nil {
return err
}
}
return nil
}
func (t *klv) createEncoder() error {
t.encoder = &rtpklv.Encoder{
PayloadMaxSize: t.RTPMaxPayloadSize,
PayloadType: t.Format.PayloadTyp,
}
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
if t.encoder == nil {
err := t.createEncoder()
if err != nil {
return err
}
}
pkts, err := t.encoder.Encode(u.Unit)
if err != nil {
return err
}
u.RTPPackets = pkts
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,
hasNonRTSPReaders bool,
) (unit.Unit, error) {
u := &unit.KLV{
Base: unit.Base{
RTPPackets: []*rtp.Packet{pkt},
NTP: ntp,
PTS: pts,
},
}
// 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)",
len(pkt.Payload), t.RTPMaxPayloadSize)
}
// decode from RTP
if hasNonRTSPReaders || t.decoder != nil {
if t.decoder == nil {
var err error
t.decoder, err = t.Format.CreateDecoder()
if err != nil {
return nil, err
}
}
unit, err := t.decoder.Decode(pkt)
if err != nil {
return nil, err
}
u.Unit = unit
}
// route packet as is
return u, nil
}

View file

@ -0,0 +1,84 @@
package formatprocessor
import (
"testing"
"time"
"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediamtx/internal/unit"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestKlvCreateEncoder(t *testing.T) {
forma := &format.KLV{
PayloadTyp: 96,
}
p, err := New(1472, forma, false, nil)
require.NoError(t, err)
klvProc := p.(*klv)
err = klvProc.createEncoder()
require.NoError(t, err)
}
func TestKlvProcessUnit(t *testing.T) {
forma := &format.KLV{
PayloadTyp: 96,
}
p, err := New(1472, forma, true, nil)
require.NoError(t, err)
// create test Unit
theTime := time.Now()
when := int64(5000000000) // 5 seconds in nanoseconds
u := unit.KLV{
Base: unit.Base{
RTPPackets: nil,
NTP: theTime,
PTS: when,
},
Unit: []byte{1, 2, 3, 4},
}
uu := &u
// process the unit
err = p.ProcessUnit(uu)
require.NoError(t, err)
}
func TestKlvProcessRTPPacket(t *testing.T) {
forma := &format.KLV{
PayloadTyp: 96,
}
p, err := New(1472, forma, false, nil)
require.NoError(t, err)
pkt := &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 3446,
Timestamp: 175349,
SSRC: 563423,
Padding: true,
},
Payload: []byte{1, 2, 3, 4},
PaddingSize: 20,
}
_, err = p.ProcessRTPPacket(pkt, time.Time{}, 0, false)
require.NoError(t, err)
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 3446,
Timestamp: 175349,
SSRC: 563423,
},
Payload: []byte{1, 2, 3, 4},
}, pkt)
}

View file

@ -119,6 +119,14 @@ func New(
Parent: parent,
}
case *format.KLV:
proc = &klv{
RTPMaxPayloadSize: rtpMaxPayloadSize,
Format: forma,
GenerateRTPPackets: generateRTPPackets,
Parent: parent,
}
case *format.MPEG4Audio:
proc = &mpeg4Audio{
RTPMaxPayloadSize: rtpMaxPayloadSize,

View file

@ -83,6 +83,13 @@ func TestNew(t *testing.T) {
&format.LPCM{},
&lpcm{},
},
{
"klv",
&format.KLV{
PayloadTyp: 96,
},
&klv{},
},
{
"generic",
&format.Generic{},