decode RTP time globally

This commit is contained in:
aler9
2023-08-14 13:12:38 +02:00
committed by Alessandro Ros
parent 95f58fa6b6
commit 8b047b545b
100 changed files with 703 additions and 993 deletions

View File

@@ -27,6 +27,7 @@ import (
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
"github.com/bluenviron/gortsplib/v4/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
"github.com/bluenviron/gortsplib/v4/pkg/url"
)
@@ -316,6 +317,7 @@ type Client struct {
writer asyncProcessor
reader *clientReader
connCloser *clientConnCloser
timeDecoder *rtptime.GlobalDecoder
// in
options chan optionsReq
@@ -671,6 +673,23 @@ func (c *Client) playRecordStart() {
c.connCloser.close()
c.connCloser = nil
c.timeDecoder = rtptime.NewGlobalDecoder()
if c.state == clientStatePlay {
// when reading, buffer is only used to send RTCP receiver reports,
// that are much smaller than RTP packets and are sent at a fixed interval.
// decrease RAM consumption by allocating less buffers.
c.writer.allocateBuffer(8)
} else {
c.writer.allocateBuffer(c.WriteBufferCount)
}
c.writer.start()
for _, cm := range c.medias {
cm.start()
}
if c.state == clientStatePlay {
c.keepaliveTimer = time.NewTimer(c.keepalivePeriod)
@@ -689,21 +708,6 @@ func (c *Client) playRecordStart() {
}
}
if c.state == clientStatePlay {
// when reading, buffer is only used to send RTCP receiver reports,
// that are much smaller than RTP packets and are sent at a fixed interval.
// decrease RAM consumption by allocating less buffers.
c.writer.allocateBuffer(8)
} else {
c.writer.allocateBuffer(c.WriteBufferCount)
}
c.writer.start()
for _, cm := range c.medias {
cm.start()
}
c.reader = newClientReader(c)
}
@@ -714,16 +718,17 @@ func (c *Client) playRecordStop(isClosing bool) {
c.reader = nil
}
// stop timers
c.checkTimeoutTimer = emptyTimer()
c.keepaliveTimer = emptyTimer()
c.writer.stop()
for _, cm := range c.medias {
cm.stop()
}
c.writer.stop()
c.timeDecoder = nil
if !isClosing {
c.connCloser = newClientConnCloser(c.ctx, c.nconn)
}
@@ -1700,6 +1705,12 @@ func (c *Client) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error {
return nil
}
// PacketPTS returns the PTS of an incoming RTP packet.
// It is computed by decoding the packet timestamp and sychronizing it with other tracks.
func (c *Client) PacketPTS(forma format.Format, pkt *rtp.Packet) (time.Duration, bool) {
return c.timeDecoder.Decode(forma, pkt)
}
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
// The NTP timestamp is computed from sender reports.
func (c *Client) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {

View File

@@ -24,7 +24,7 @@ func main() {
}
defer pc.Close()
log.Println("Waiting for a RTP/MPEG4-audio stream on UDP port 9000 - you can send one with GStreamer:\n" +
log.Println("Waiting for a RTP/MPEG-4 audio stream on UDP port 9000 - you can send one with GStreamer:\n" +
"gst-launch-1.0 audiotestsrc freq=300 ! audioconvert ! audioresample ! audio/x-raw,rate=48000" +
" ! avenc_aac bitrate=128000 ! rtpmp4gpay ! udpsink host=127.0.0.1 port=9000")
@@ -36,7 +36,7 @@ func main() {
}
log.Println("stream connected")
// create a media that contains a MPEG4-audio format
// create a media that contains a MPEG-4 audio format
medi := &media.Media{
Type: media.TypeAudio,
Formats: []format.Format{&format.MPEG4Audio{

View File

@@ -57,15 +57,20 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract G711 frames from RTP packets
op, _, err := rtpDec.Decode(pkt)
op, err := rtpDec.Decode(pkt)
if err != nil {
log.Printf("ERR: %v", err)
return
}
// print
log.Printf("received G711 frame of size %d\n", len(op))
log.Printf("received G711 frame with PTS %v and size %d\n", pts, len(op))
})
// start playing

View File

@@ -57,15 +57,20 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract G722 frames from RTP packets
op, _, err := rtpDec.Decode(pkt)
op, err := rtpDec.Decode(pkt)
if err != nil {
log.Printf("ERR: %v", err)
return
}
// print
log.Printf("received G722 frame of size %d\n", len(op))
log.Printf("received G722 frame with PTS %v size %d\n", pts, len(op))
})
// start playing

View File

@@ -97,11 +97,12 @@ func main() {
panic(err)
}
// called when a RTP packet arrives
saveCount := 0
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// extract access units from RTP packets
au, _, err := rtpDec.Decode(pkt)
au, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)

View File

@@ -64,8 +64,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract access unit from RTP packets
au, pts, err := rtpDec.Decode(pkt)
au, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)
@@ -74,7 +79,13 @@ func main() {
}
// encode the access unit into MPEG-TS
mpegtsMuxer.encode(au, pts)
err = mpegtsMuxer.encode(au, pts)
if err != nil {
log.Printf("ERR: %v", err)
return
}
log.Printf("saved TS packet")
})
// start playing

View File

@@ -2,7 +2,6 @@ package main
import (
"bufio"
"log"
"os"
"time"
@@ -25,7 +24,6 @@ type mpegtsMuxer struct {
track *mpegts.Track
dtsExtractor *h264.DTSExtractor
firstIDRReceived bool
startDTS time.Duration
}
// newMPEGTSMuxer allocates a mpegtsMuxer.
@@ -94,7 +92,7 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
au = filteredNALUs
if !nonIDRPresent && !idrPresent {
if len(au) <= 1 || (!nonIDRPresent && !idrPresent) {
return nil
}
@@ -119,28 +117,14 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
if err != nil {
return err
}
e.startDTS = dts
dts = 0
pts -= e.startDTS
} else {
var err error
dts, err = e.dtsExtractor.Extract(au, pts)
if err != nil {
return err
}
dts -= e.startDTS
pts -= e.startDTS
}
// encode into MPEG-TS
err := e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
if err != nil {
return err
}
log.Println("wrote TS packet")
return nil
return e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
}

View File

@@ -76,8 +76,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract access units from RTP packets
au, pts, err := rtpDec.Decode(pkt)
au, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)
@@ -97,7 +102,7 @@ func main() {
continue
}
log.Printf("decoded frame with size %v and pts %v", img.Bounds().Max, pts)
log.Printf("decoded frame with PTS %v and size %v and pts %v", pts, img.Bounds().Max)
}
})

View File

@@ -58,8 +58,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract access units from RTP packets
au, pts, err := rtpDec.Decode(pkt)
au, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtph265.ErrNonStartingPacketAndNoPrevious && err != rtph265.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)

View File

@@ -57,15 +57,20 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract LPCM samples from RTP packets
op, _, err := rtpDec.Decode(pkt)
op, err := rtpDec.Decode(pkt)
if err != nil {
log.Printf("ERR: %v", err)
return
}
// print
log.Printf("received LPCM samples of size %d\n", len(op))
log.Printf("received LPCM samples with PTS %v size %d\n", pts, len(op))
})
// start playing

View File

@@ -61,8 +61,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract JPEG images from RTP packets
enc, pts, err := rtpDec.Decode(pkt)
enc, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtpmjpeg.ErrNonStartingPacketAndNoPrevious && err != rtpmjpeg.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)
@@ -76,7 +81,7 @@ func main() {
panic(err)
}
log.Printf("decoded image with size %v and pts %v", image.Bounds().Max, pts)
log.Printf("decoded image with PTS %v and size %v", pts, image.Bounds().Max)
})
// start playing

View File

@@ -63,17 +63,26 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract access units from RTP packets
aus, pts, err := rtpDec.Decode(pkt)
aus, err := rtpDec.Decode(pkt)
if err != nil {
log.Printf("ERR: %v", err)
return
}
for _, au := range aus {
// encode the access unit into MPEG-TS
mpegtsMuxer.encode(au, pts)
// encode access units into MPEG-TS
err = mpegtsMuxer.encode(aus, pts)
if err != nil {
log.Printf("ERR: %v", err)
return
}
log.Printf("saved TS packet")
})
// start playing

View File

@@ -2,7 +2,6 @@ package main
import (
"bufio"
"log"
"os"
"time"
@@ -54,14 +53,7 @@ func (e *mpegtsMuxer) close() {
e.f.Close()
}
// encode encodes a MPEG4-audio access unit into MPEG-TS.
func (e *mpegtsMuxer) encode(au []byte, pts time.Duration) error {
// encode into MPEG-TS
err := e.w.WriteMPEG4Audio(e.track, durationGoToMPEGTS(pts), [][]byte{au})
if err != nil {
return err
}
log.Println("wrote TS packet")
return nil
// encode encodes MPEG-4 audio access units into MPEG-TS.
func (e *mpegtsMuxer) encode(aus [][]byte, pts time.Duration) error {
return e.w.WriteMPEG4Audio(e.track, durationGoToMPEGTS(pts), aus)
}

View File

@@ -57,8 +57,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract access units from RTP packets
aus, _, err := rtpDec.Decode(pkt)
aus, err := rtpDec.Decode(pkt)
if err != nil {
log.Printf("ERR: %v", err)
return
@@ -66,7 +71,7 @@ func main() {
// print AUs
for _, au := range aus {
log.Printf("received MPEG4-audio AU of size %d\n", len(au))
log.Printf("received access unit with PTS %v size %d\n", pts, len(au))
}
})

View File

@@ -57,15 +57,20 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract Opus packets from RTP packets
op, _, err := rtpDec.Decode(pkt)
op, err := rtpDec.Decode(pkt)
if err != nil {
log.Printf("ERR: %v", err)
return
}
// print
log.Printf("received Opus packet of size %d\n", len(op))
log.Printf("received Opus packet with PTS %v size %d\n", pts, len(op))
})
// start playing

View File

@@ -58,8 +58,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract VP8 frames from RTP packets
vf, _, err := rtpDec.Decode(pkt)
vf, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtpvp8.ErrNonStartingPacketAndNoPrevious && err != rtpvp8.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)
@@ -67,7 +72,7 @@ func main() {
return
}
log.Printf("received frame of size %d\n", len(vf))
log.Printf("received frame with PTS %v size %d\n", pts, len(vf))
})
// start playing

View File

@@ -58,8 +58,13 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
pts, ok := c.PacketPTS(forma, pkt)
if !ok {
return
}
// extract VP9 frames from RTP packets
vf, _, err := rtpDec.Decode(pkt)
vf, err := rtpDec.Decode(pkt)
if err != nil {
if err != rtpvp9.ErrNonStartingPacketAndNoPrevious && err != rtpvp9.ErrMorePacketsNeeded {
log.Printf("ERR: %v", err)
@@ -67,7 +72,7 @@ func main() {
return
}
log.Printf("received frame of size %d\n", len(vf))
log.Printf("received frame with PTS %v and size %d\n", pts, len(vf))
})
// start playing

View File

@@ -20,6 +20,7 @@ import (
// 3. save the content of the H264 media into a file in MPEG-TS format
type serverHandler struct {
s *gortsplib.Server
mutex sync.Mutex
publisher *gortsplib.ServerSession
media *media.Media
@@ -115,13 +116,18 @@ func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*bas
// called when receiving a RTP packet
ctx.Session.OnPacketRTP(sh.media, sh.format, func(pkt *rtp.Packet) {
nalus, pts, err := sh.rtpDec.Decode(pkt)
pts, ok := ctx.Session.PacketPTS(sh.format, pkt)
if !ok {
return
}
au, err := sh.rtpDec.Decode(pkt)
if err != nil {
return
}
// encode H264 NALUs into MPEG-TS
sh.mpegtsMuxer.encode(nalus, pts)
// encode H264 access unit into MPEG-TS
sh.mpegtsMuxer.encode(au, pts)
})
return &base.Response{
@@ -131,8 +137,9 @@ func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*bas
func main() {
// configure the server
s := &gortsplib.Server{
Handler: &serverHandler{},
h := &serverHandler{}
h.s = &gortsplib.Server{
Handler: h,
RTSPAddress: ":8554",
UDPRTPAddress: ":8000",
UDPRTCPAddress: ":8001",
@@ -143,5 +150,5 @@ func main() {
// start server and wait until a fatal error
log.Printf("server is ready")
panic(s.StartAndWait())
panic(h.s.StartAndWait())
}

View File

@@ -2,7 +2,6 @@ package main
import (
"bufio"
"log"
"os"
"time"
@@ -25,7 +24,6 @@ type mpegtsMuxer struct {
track *mpegts.Track
dtsExtractor *h264.DTSExtractor
firstIDRReceived bool
startDTS time.Duration
}
// newMPEGTSMuxer allocates a mpegtsMuxer.
@@ -58,7 +56,7 @@ func (e *mpegtsMuxer) close() {
e.f.Close()
}
// encode encodes H264 NALUs into MPEG-TS.
// encode encodes a H264 access unit into MPEG-TS.
func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
// prepend an AUD. This is required by some players
filteredNALUs := [][]byte{
@@ -94,7 +92,7 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
au = filteredNALUs
if !nonIDRPresent && !idrPresent {
if len(au) <= 1 || (!nonIDRPresent && !idrPresent) {
return nil
}
@@ -119,28 +117,14 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
if err != nil {
return err
}
e.startDTS = dts
dts = 0
pts -= e.startDTS
} else {
var err error
dts, err = e.dtsExtractor.Extract(au, pts)
if err != nil {
return err
}
dts -= e.startDTS
pts -= e.startDTS
}
// encode into MPEG-TS
err := e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
if err != nil {
return err
}
log.Println("wrote TS packet")
return nil
return e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
}

View File

@@ -22,14 +22,14 @@ func TestAV1DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}}, 0)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
}

View File

@@ -56,9 +56,7 @@ func (f *G711) PTSEqualsDTS(*rtp.Packet) bool {
// CreateDecoder creates a decoder able to decode the content of the format.
func (f *G711) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
d := &rtpsimpleaudio.Decoder{
SampleRate: 8000,
}
d := &rtpsimpleaudio.Decoder{}
err := d.Init()
if err != nil {
@@ -72,7 +70,6 @@ func (f *G711) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
func (f *G711) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
e := &rtpsimpleaudio.Encoder{
PayloadType: f.PayloadType(),
SampleRate: 8000,
}
err := e.Init()

View File

@@ -26,14 +26,14 @@ func TestG711DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkt, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkt, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkt.PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkt)
byts, err := dec.Decode(pkt)
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -46,9 +46,7 @@ func (f *G722) PTSEqualsDTS(*rtp.Packet) bool {
// CreateDecoder creates a decoder able to decode the content of the format.
func (f *G722) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
d := &rtpsimpleaudio.Decoder{
SampleRate: 8000,
}
d := &rtpsimpleaudio.Decoder{}
err := d.Init()
if err != nil {
@@ -62,7 +60,6 @@ func (f *G722) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
func (f *G722) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
e := &rtpsimpleaudio.Encoder{
PayloadType: 9,
SampleRate: 8000,
}
err := e.Init()

View File

@@ -20,14 +20,14 @@ func TestG722DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkt, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkt, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkt.PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkt)
byts, err := dec.Decode(pkt)
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -50,14 +50,14 @@ func TestH264DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}}, 0)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
}

View File

@@ -53,14 +53,14 @@ func TestH265DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}}, 0)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
}

View File

@@ -100,7 +100,6 @@ func (f *LPCM) PTSEqualsDTS(*rtp.Packet) bool {
func (f *LPCM) CreateDecoder() (*rtplpcm.Decoder, error) {
d := &rtplpcm.Decoder{
BitDepth: f.BitDepth,
SampleRate: f.SampleRate,
ChannelCount: f.ChannelCount,
}
@@ -117,7 +116,6 @@ func (f *LPCM) CreateEncoder() (*rtplpcm.Encoder, error) {
e := &rtplpcm.Encoder{
PayloadType: f.PayloadTyp,
BitDepth: f.BitDepth,
SampleRate: f.SampleRate,
ChannelCount: f.ChannelCount,
}

View File

@@ -30,14 +30,14 @@ func TestLPCMDecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -281,7 +281,7 @@ func TestMJPEGDecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode(b, 0)
pkts, err := enc.Encode(b)
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
@@ -290,7 +290,7 @@ func TestMJPEGDecEncoder(t *testing.T) {
var byts []byte
for _, pkt := range pkts {
byts, _, _ = dec.Decode(pkt)
byts, _ = dec.Decode(pkt)
}
require.Equal(t, b, byts)
}

View File

@@ -33,14 +33,14 @@ func TestMPEG1AudioDecEncoder(t *testing.T) {
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
}}, 0)
}})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, [][]byte{{
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,

View File

@@ -172,7 +172,6 @@ func (f *MPEG4AudioGeneric) PTSEqualsDTS(*rtp.Packet) bool {
// CreateDecoder creates a decoder able to decode the content of the format.
func (f *MPEG4AudioGeneric) CreateDecoder() (*rtpmpeg4audio.Decoder, error) {
d := &rtpmpeg4audio.Decoder{
SampleRate: f.Config.SampleRate,
SizeLength: f.SizeLength,
IndexLength: f.IndexLength,
IndexDeltaLength: f.IndexDeltaLength,
@@ -190,7 +189,6 @@ func (f *MPEG4AudioGeneric) CreateDecoder() (*rtpmpeg4audio.Decoder, error) {
func (f *MPEG4AudioGeneric) CreateEncoder() (*rtpmpeg4audio.Encoder, error) {
e := &rtpmpeg4audio.Encoder{
PayloadType: f.PayloadTyp,
SampleRate: f.Config.SampleRate,
SizeLength: f.SizeLength,
IndexLength: f.IndexLength,
IndexDeltaLength: f.IndexDeltaLength,

View File

@@ -42,14 +42,14 @@ func TestMPEG4AudioGenericDecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}}, 0)
pkts, err := enc.Encode([][]byte{{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
}

View File

@@ -159,9 +159,7 @@ func (f *MPEG4AudioLATM) PTSEqualsDTS(*rtp.Packet) bool {
// CreateDecoder creates a decoder able to decode the content of the format.
func (f *MPEG4AudioLATM) CreateDecoder() (*rtpmpeg4audiolatm.Decoder, error) {
d := &rtpmpeg4audiolatm.Decoder{
Config: f.Config,
}
d := &rtpmpeg4audiolatm.Decoder{}
err := d.Init()
if err != nil {
@@ -175,7 +173,6 @@ func (f *MPEG4AudioLATM) CreateDecoder() (*rtpmpeg4audiolatm.Decoder, error) {
func (f *MPEG4AudioLATM) CreateEncoder() (*rtpmpeg4audiolatm.Encoder, error) {
e := &rtpmpeg4audiolatm.Encoder{
PayloadType: f.PayloadTyp,
Config: f.Config,
}
err := e.Init()

View File

@@ -51,14 +51,14 @@ func TestMPEG4AudioLATMDecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -26,14 +26,14 @@ func TestMPEG4VideoESDecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -88,9 +88,7 @@ func (f *Opus) PTSEqualsDTS(*rtp.Packet) bool {
// CreateDecoder creates a decoder able to decode the content of the format.
func (f *Opus) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
d := &rtpsimpleaudio.Decoder{
SampleRate: 48000,
}
d := &rtpsimpleaudio.Decoder{}
err := d.Init()
if err != nil {
@@ -104,7 +102,6 @@ func (f *Opus) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
func (f *Opus) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
e := &rtpsimpleaudio.Encoder{
PayloadType: f.PayloadTyp,
SampleRate: 48000,
}
err := e.Init()

View File

@@ -23,14 +23,14 @@ func TestOpusDecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkt, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkt, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkt.PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkt)
byts, err := dec.Decode(pkt)
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -3,13 +3,10 @@ package rtpav1
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -34,7 +31,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/AV1 decoder.
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
type Decoder struct {
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentsSize int
fragments [][]byte
@@ -47,39 +43,38 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(90000)
return nil
}
func (d *Decoder) decodeOBUs(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
func (d *Decoder) decodeOBUs(pkt *rtp.Packet) ([][]byte, error) {
var av1header codecs.AV1Packet
_, err := av1header.Unmarshal(pkt.Payload)
if err != nil {
d.fragments = d.fragments[:0] // discard pending fragments
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("invalid header: %v", err)
return nil, fmt.Errorf("invalid header: %v", err)
}
for _, el := range av1header.OBUElements {
if len(el) == 0 {
return nil, 0, fmt.Errorf("invalid OBU fragment")
return nil, fmt.Errorf("invalid OBU fragment")
}
}
if av1header.Z {
if len(d.fragments) == 0 {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
return nil, 0, fmt.Errorf("received a subsequent fragment without previous fragments")
return nil, fmt.Errorf("received a subsequent fragment without previous fragments")
}
d.fragmentsSize += len(av1header.OBUElements[0])
if d.fragmentsSize > av1.MaxTemporalUnitSize {
d.fragments = d.fragments[:0]
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("OBU size (%d) is too big, maximum is %d", d.fragmentsSize, av1.MaxTemporalUnitSize)
return nil, fmt.Errorf("OBU size (%d) is too big, maximum is %d", d.fragmentsSize, av1.MaxTemporalUnitSize)
}
d.fragments = append(d.fragments, av1header.OBUElements[0])
@@ -104,7 +99,7 @@ func (d *Decoder) decodeOBUs(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
if d.fragmentsSize > av1.MaxTemporalUnitSize {
d.fragments = d.fragments[:0]
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("OBU size (%d) is too big, maximum is %d", d.fragmentsSize, av1.MaxTemporalUnitSize)
return nil, fmt.Errorf("OBU size (%d) is too big, maximum is %d", d.fragmentsSize, av1.MaxTemporalUnitSize)
}
d.fragments = append(d.fragments, av1header.OBUElements[elementCount-1])
@@ -119,17 +114,17 @@ func (d *Decoder) decodeOBUs(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
}
if len(obus) == 0 {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
return obus, d.timeDecoder.Decode(pkt.Timestamp), nil
return obus, nil
}
// Decode decodes a temporal unit from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
obus, pts, err := d.decodeOBUs(pkt)
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
obus, err := d.decodeOBUs(pkt)
if err != nil {
return nil, 0, err
return nil, err
}
l := len(obus)
@@ -137,7 +132,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBuffer = nil
d.frameBufferLen = 0
d.frameBufferSize = 0
return nil, 0, fmt.Errorf("OBU count exceeds maximum allowed (%d)",
return nil, fmt.Errorf("OBU count exceeds maximum allowed (%d)",
av1.MaxOBUsPerTemporalUnit)
}
@@ -151,7 +146,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBuffer = nil
d.frameBufferLen = 0
d.frameBufferSize = 0
return nil, 0, fmt.Errorf("temporal unit size (%d) is too big, maximum is %d",
return nil, fmt.Errorf("temporal unit size (%d) is too big, maximum is %d",
d.frameBufferSize+addSize, av1.MaxOBUsPerTemporalUnit)
}
@@ -160,7 +155,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBufferSize += addSize
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
ret := d.frameBuffer
@@ -170,5 +165,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBufferLen = 0
d.frameBufferSize = 0
return ret, pts, nil
return ret, nil
}

View File

@@ -18,7 +18,7 @@ func TestDecode(t *testing.T) {
var obus [][]byte
for _, pkt := range ca.pkts {
addOBUs, _, err := d.Decode(pkt)
addOBUs, err := d.Decode(pkt)
if err == ErrMorePacketsNeeded {
continue
}
@@ -38,7 +38,7 @@ func TestDecoderErrorLimit(t *testing.T) {
require.NoError(t, err)
for i := 0; i <= av1.MaxOBUsPerTemporalUnit; i++ {
_, _, err = d.Decode(&rtp.Packet{
_, err = d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: false,

View File

@@ -2,12 +2,9 @@ package rtpav1
import (
"crypto/rand"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -38,16 +35,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -67,30 +59,21 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
return nil
}
// Encode encodes OBUs into RTP packets.
func (e *Encoder) Encode(obus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(obus [][]byte) ([]*rtp.Packet, error) {
isKeyFrame, err := av1.ContainsKeyFrame(obus)
if err != nil {
return nil, err
}
ts := e.timeEncoder.Encode(pts)
var curPacket *rtp.Packet
var packets []*rtp.Packet
curPayloadLen := 0
@@ -101,7 +84,6 @@ func (e *Encoder) Encode(obus [][]byte, pts time.Duration) ([]*rtp.Packet, error
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: ts,
SSRC: *e.SSRC,
},
Payload: []byte{0},

View File

@@ -244,7 +244,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -265,7 +264,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -460,7 +458,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -502,7 +499,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -525,7 +521,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -720,7 +715,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -915,7 +909,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -988,12 +981,11 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.obus, 0)
pkts, err := e.Encode(ca.obus)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -1008,5 +1000,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -4,11 +4,9 @@ import (
"bytes"
"errors"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
)
@@ -37,7 +35,6 @@ type Decoder struct {
// indicates the packetization mode.
PacketizationMode int
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentsSize int
fragments [][]byte
@@ -54,15 +51,13 @@ func (d *Decoder) Init() error {
if d.PacketizationMode >= 2 {
return fmt.Errorf("PacketizationMode >= 2 is not supported")
}
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
return nil
}
func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
if len(pkt.Payload) < 1 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
typ := h264.NALUType(pkt.Payload[0] & 0x1F)
@@ -71,7 +66,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
switch typ {
case h264.NALUTypeFUA:
if len(pkt.Payload) < 2 {
return nil, 0, fmt.Errorf("invalid FU-A packet (invalid size)")
return nil, fmt.Errorf("invalid FU-A packet (invalid size)")
}
start := pkt.Payload[1] >> 7
@@ -81,7 +76,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
d.fragments = d.fragments[:0] // discard pending fragments
if end != 0 {
return nil, 0, fmt.Errorf("invalid FU-A packet (can't contain both a start and end bit)")
return nil, fmt.Errorf("invalid FU-A packet (can't contain both a start and end bit)")
}
nri := (pkt.Payload[0] >> 5) & 0x03
@@ -90,28 +85,28 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
d.fragments = append(d.fragments, []byte{(nri << 5) | typ}, pkt.Payload[2:])
d.firstPacketReceived = true
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
if len(d.fragments) == 0 {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
return nil, 0, fmt.Errorf("invalid FU-A packet (non-starting)")
return nil, fmt.Errorf("invalid FU-A packet (non-starting)")
}
d.fragmentsSize += len(pkt.Payload[2:])
if d.fragmentsSize > h264.MaxAccessUnitSize {
d.fragments = d.fragments[:0]
return nil, 0, fmt.Errorf("NALU size (%d) is too big, maximum is %d", d.fragmentsSize, h264.MaxAccessUnitSize)
return nil, fmt.Errorf("NALU size (%d) is too big, maximum is %d", d.fragmentsSize, h264.MaxAccessUnitSize)
}
d.fragments = append(d.fragments, pkt.Payload[2:])
if end != 1 {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
@@ -124,7 +119,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
for len(payload) > 0 {
if len(payload) < 2 {
return nil, 0, fmt.Errorf("invalid STAP-A packet (invalid size)")
return nil, fmt.Errorf("invalid STAP-A packet (invalid size)")
}
size := uint16(payload[0])<<8 | uint16(payload[1])
@@ -136,7 +131,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
}
if int(size) > len(payload) {
return nil, 0, fmt.Errorf("invalid STAP-A packet (invalid size)")
return nil, fmt.Errorf("invalid STAP-A packet (invalid size)")
}
nalus = append(nalus, payload[:size])
@@ -144,7 +139,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
}
if nalus == nil {
return nil, 0, fmt.Errorf("STAP-A packet doesn't contain any NALU")
return nil, fmt.Errorf("STAP-A packet doesn't contain any NALU")
}
d.firstPacketReceived = true
@@ -153,7 +148,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
h264.NALUTypeMTAP24, h264.NALUTypeFUB:
d.fragments = d.fragments[:0] // discard pending fragments
d.firstPacketReceived = true
return nil, 0, fmt.Errorf("packet type not supported (%v)", typ)
return nil, fmt.Errorf("packet type not supported (%v)", typ)
default:
d.fragments = d.fragments[:0] // discard pending fragments
@@ -163,17 +158,17 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
nalus, err := d.removeAnnexB(nalus)
if err != nil {
return nil, 0, err
return nil, err
}
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
return nalus, nil
}
// Decode decodes an access unit from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
nalus, pts, err := d.decodeNALUs(pkt)
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
nalus, err := d.decodeNALUs(pkt)
if err != nil {
return nil, 0, err
return nil, err
}
l := len(nalus)
@@ -181,7 +176,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBuffer = nil
d.frameBufferLen = 0
d.frameBufferSize = 0
return nil, 0, fmt.Errorf("NALU count exceeds maximum allowed (%d)",
return nil, fmt.Errorf("NALU count exceeds maximum allowed (%d)",
h264.MaxNALUsPerAccessUnit)
}
@@ -195,7 +190,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBuffer = nil
d.frameBufferLen = 0
d.frameBufferSize = 0
return nil, 0, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
d.frameBufferSize+addSize, h264.MaxAccessUnitSize)
}
@@ -204,7 +199,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBufferSize += addSize
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
ret := d.frameBuffer
@@ -214,7 +209,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBufferLen = 0
d.frameBufferSize = 0
return ret, pts, nil
return ret, nil
}
// some cameras / servers wrap NALUs into Annex-B

View File

@@ -21,7 +21,7 @@ func TestDecode(t *testing.T) {
for _, pkt := range ca.pkts {
clone := pkt.Clone()
addNALUs, _, err := d.Decode(pkt)
addNALUs, err := d.Decode(pkt)
// test input integrity
require.Equal(t, clone, pkt)
@@ -44,7 +44,7 @@ func TestDecodeCorruptedFragment(t *testing.T) {
err := d.Init()
require.NoError(t, err)
_, _, err = d.Decode(&rtp.Packet{
_, err = d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@@ -63,7 +63,7 @@ func TestDecodeCorruptedFragment(t *testing.T) {
})
require.Equal(t, ErrMorePacketsNeeded, err)
nalus, _, err := d.Decode(&rtp.Packet{
nalus, err := d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@@ -89,7 +89,6 @@ func TestDecodeSTAPAWithPadding(t *testing.T) {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -100,7 +99,7 @@ func TestDecodeSTAPAWithPadding(t *testing.T) {
},
}
nalus, _, err := d.Decode(&pkt)
nalus, err := d.Decode(&pkt)
require.NoError(t, err)
require.Equal(t, [][]byte{
{0xaa, 0xbb},
@@ -113,7 +112,7 @@ func TestDecodeAnnexB(t *testing.T) {
err := d.Init()
require.NoError(t, err)
nalus, _, err := d.Decode(&rtp.Packet{
nalus, err := d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@@ -132,7 +131,7 @@ func TestDecodeAnnexB(t *testing.T) {
}, nalus)
for i := 0; i < 2; i++ {
nalus, _, err := d.Decode(&rtp.Packet{
nalus, err := d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@@ -161,7 +160,7 @@ func TestDecodeAccessUnit(t *testing.T) {
err := d.Init()
require.NoError(t, err)
nalus, _, err := d.Decode(&rtp.Packet{
nalus, err := d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: false,
@@ -175,7 +174,7 @@ func TestDecodeAccessUnit(t *testing.T) {
require.Equal(t, ErrMorePacketsNeeded, err)
require.Equal(t, [][]byte(nil), nalus)
nalus, _, err = d.Decode(&rtp.Packet{
nalus, err = d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@@ -196,7 +195,7 @@ func TestDecoderErrorLimit(t *testing.T) {
require.NoError(t, err)
for i := 0; i <= h264.MaxNALUsPerAccessUnit; i++ {
_, _, err = d.Decode(&rtp.Packet{
_, err = d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: false,

View File

@@ -3,11 +3,9 @@ package rtph264
import (
"crypto/rand"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
)
@@ -39,10 +37,6 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
@@ -50,7 +44,6 @@ type Encoder struct {
PacketizationMode int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -74,24 +67,16 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
return nil
}
// Encode encodes NALUs into RTP/H264 packets.
func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(nalus [][]byte) ([]*rtp.Packet, error) {
var rets []*rtp.Packet
var batch [][]byte
@@ -101,9 +86,9 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
// add to existing batch
batch = append(batch, nalu)
} else {
// write batch
// write current batch
if batch != nil {
pkts, err := e.writeBatch(batch, pts, false)
pkts, err := e.writeBatch(batch, false)
if err != nil {
return nil, err
}
@@ -117,7 +102,7 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
// write final batch
// marker is used to indicate when all NALUs with same PTS have been sent
pkts, err := e.writeBatch(batch, pts, true)
pkts, err := e.writeBatch(batch, true)
if err != nil {
return nil, err
}
@@ -126,27 +111,26 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
return rets, nil
}
func (e *Encoder) writeBatch(nalus [][]byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeBatch(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
if len(nalus) == 1 {
// the NALU fits into a single RTP packet
if len(nalus[0]) < e.PayloadMaxSize {
return e.writeSingle(nalus[0], pts, marker)
return e.writeSingle(nalus[0], marker)
}
// split the NALU into multiple fragmentation packet
return e.writeFragmented(nalus[0], pts, marker)
return e.writeFragmented(nalus[0], marker)
}
return e.writeAggregated(nalus, pts, marker)
return e.writeAggregated(nalus, marker)
}
func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeSingle(nalu []byte, marker bool) ([]*rtp.Packet, error) {
pkt := &rtp.Packet{
Header: rtp.Header{
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},
@@ -158,7 +142,7 @@ func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*r
return []*rtp.Packet{pkt}, nil
}
func (e *Encoder) writeFragmented(nalu []byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeFragmented(nalu []byte, marker bool) ([]*rtp.Packet, error) {
// use only FU-A, not FU-B, since we always use non-interleaved mode
// (packetization-mode=1)
avail := e.PayloadMaxSize - 2
@@ -170,7 +154,6 @@ func (e *Encoder) writeFragmented(nalu []byte, pts time.Duration, marker bool) (
}
ret := make([]*rtp.Packet, packetCount)
encPTS := e.timeEncoder.Encode(pts)
nri := (nalu[0] >> 5) & 0x03
typ := nalu[0] & 0x1F
@@ -202,7 +185,6 @@ func (e *Encoder) writeFragmented(nalu []byte, pts time.Duration, marker bool) (
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
SSRC: *e.SSRC,
Marker: (i == (packetCount-1) && marker),
},
@@ -231,7 +213,7 @@ func (e *Encoder) lenAggregated(nalus [][]byte, addNALU []byte) int {
return ret
}
func (e *Encoder) writeAggregated(nalus [][]byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeAggregated(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
payload := make([]byte, e.lenAggregated(nalus, nil))
// header
@@ -255,7 +237,6 @@ func (e *Encoder) writeAggregated(nalus [][]byte, pts time.Duration, marker bool
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},

View File

@@ -52,7 +52,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -77,7 +76,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -92,7 +90,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -108,7 +105,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -142,7 +138,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -187,7 +182,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -209,7 +203,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -236,7 +229,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -251,7 +243,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -266,7 +257,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -285,12 +275,11 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.nalus, 0)
pkts, err := e.Encode(ca.nalus)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -305,5 +294,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -1,6 +1,2 @@
// Package rtph264 contains a RTP/H264 decoder and encoder.
package rtph264
const (
rtpClockRate = 90000 // H264 always uses 90khz
)

View File

@@ -3,11 +3,9 @@ package rtph265
import (
"errors"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
)
@@ -36,7 +34,6 @@ type Decoder struct {
// indicates that NALUs have an additional field that specifies the decoding order.
MaxDONDiff int
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentsSize int
fragments [][]byte
@@ -52,15 +49,13 @@ func (d *Decoder) Init() error {
if d.MaxDONDiff != 0 {
return fmt.Errorf("MaxDONDiff != 0 is not supported (yet)")
}
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
return nil
}
func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, error) {
if len(pkt.Payload) < 2 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
typ := h265.NALUType((pkt.Payload[0] >> 1) & 0b111111)
@@ -74,7 +69,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
for len(payload) > 0 {
if len(payload) < 2 {
return nil, 0, fmt.Errorf("invalid aggregation unit (invalid size)")
return nil, fmt.Errorf("invalid aggregation unit (invalid size)")
}
size := uint16(payload[0])<<8 | uint16(payload[1])
@@ -85,7 +80,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
}
if int(size) > len(payload) {
return nil, 0, fmt.Errorf("invalid aggregation unit (invalid size)")
return nil, fmt.Errorf("invalid aggregation unit (invalid size)")
}
nalus = append(nalus, payload[:size])
@@ -93,7 +88,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
}
if nalus == nil {
return nil, 0, fmt.Errorf("aggregation unit doesn't contain any NALU")
return nil, fmt.Errorf("aggregation unit doesn't contain any NALU")
}
d.firstPacketReceived = true
@@ -101,7 +96,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
case h265.NALUType_FragmentationUnit:
if len(pkt.Payload) < 3 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
start := pkt.Payload[2] >> 7
@@ -111,7 +106,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
d.fragments = d.fragments[:0] // discard pending fragments
if end != 0 {
return nil, 0, fmt.Errorf("invalid fragmentation unit (can't contain both a start and end bit)")
return nil, fmt.Errorf("invalid fragmentation unit (can't contain both a start and end bit)")
}
typ := pkt.Payload[2] & 0b111111
@@ -120,27 +115,27 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
d.fragments = append(d.fragments, []byte{byte(head >> 8), byte(head)}, pkt.Payload[3:])
d.firstPacketReceived = true
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
if len(d.fragments) == 0 {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
return nil, 0, fmt.Errorf("invalid fragmentation unit (non-starting)")
return nil, fmt.Errorf("invalid fragmentation unit (non-starting)")
}
d.fragmentsSize += len(pkt.Payload[3:])
if d.fragmentsSize > h265.MaxAccessUnitSize {
d.fragments = d.fragments[:0]
return nil, 0, fmt.Errorf("NALU size (%d) is too big, maximum is %d", d.fragmentsSize, h265.MaxAccessUnitSize)
return nil, fmt.Errorf("NALU size (%d) is too big, maximum is %d", d.fragmentsSize, h265.MaxAccessUnitSize)
}
d.fragments = append(d.fragments, pkt.Payload[3:])
if end != 1 {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
@@ -149,7 +144,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
case h265.NALUType_PACI:
d.fragments = d.fragments[:0] // discard pending fragments
d.firstPacketReceived = true
return nil, 0, fmt.Errorf("PACI packets are not supported (yet)")
return nil, fmt.Errorf("PACI packets are not supported (yet)")
default:
d.fragments = d.fragments[:0] // discard pending fragments
@@ -157,14 +152,14 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
nalus = [][]byte{pkt.Payload}
}
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
return nalus, nil
}
// Decode decodes an access unit from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
nalus, pts, err := d.decodeNALUs(pkt)
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
nalus, err := d.decodeNALUs(pkt)
if err != nil {
return nil, 0, err
return nil, err
}
l := len(nalus)
@@ -172,7 +167,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBuffer = nil
d.frameBufferLen = 0
d.frameBufferSize = 0
return nil, 0, fmt.Errorf("NALU count exceeds maximum allowed (%d)",
return nil, fmt.Errorf("NALU count exceeds maximum allowed (%d)",
h265.MaxNALUsPerAccessUnit)
}
@@ -186,7 +181,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBuffer = nil
d.frameBufferLen = 0
d.frameBufferSize = 0
return nil, 0, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
d.frameBufferSize+addSize, h265.MaxAccessUnitSize)
}
@@ -195,7 +190,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBufferSize += addSize
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
ret := d.frameBuffer
@@ -205,5 +200,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.frameBufferLen = 0
d.frameBufferSize = 0
return ret, pts, nil
return ret, nil
}

View File

@@ -20,7 +20,7 @@ func TestDecode(t *testing.T) {
for _, pkt := range ca.pkts {
clone := pkt.Clone()
addNALUs, _, err := d.Decode(pkt)
addNALUs, err := d.Decode(pkt)
// test input integrity
require.Equal(t, clone, pkt)
@@ -44,7 +44,7 @@ func TestDecoderErrorLimit(t *testing.T) {
require.NoError(t, err)
for i := 0; i <= h265.MaxNALUsPerAccessUnit; i++ {
_, _, err = d.Decode(&rtp.Packet{
_, err = d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: false,

View File

@@ -3,11 +3,8 @@ package rtph265
import (
"crypto/rand"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -38,10 +35,6 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
@@ -50,7 +43,6 @@ type Encoder struct {
MaxDONDiff int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -74,24 +66,16 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
return nil
}
// Encode encodes NALUs into RTP/H265 packets.
func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(nalus [][]byte) ([]*rtp.Packet, error) {
var rets []*rtp.Packet
var batch [][]byte
@@ -103,7 +87,7 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
} else {
// write batch
if batch != nil {
pkts, err := e.writeBatch(batch, pts, false)
pkts, err := e.writeBatch(batch, false)
if err != nil {
return nil, err
}
@@ -116,8 +100,8 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
}
// write final batch
// marker is used to indicate when all NALUs with same PTS have been sent
pkts, err := e.writeBatch(batch, pts, true)
// marker is used to indicate that the entire access unit has been sent
pkts, err := e.writeBatch(batch, true)
if err != nil {
return nil, err
}
@@ -126,27 +110,26 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
return rets, nil
}
func (e *Encoder) writeBatch(nalus [][]byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeBatch(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
if len(nalus) == 1 {
// the NALU fits into a single RTP packet
if len(nalus[0]) < e.PayloadMaxSize {
return e.writeSingle(nalus[0], pts, marker)
return e.writeSingle(nalus[0], marker)
}
// split the NALU into multiple fragmentation packet
return e.writeFragmentationUnits(nalus[0], pts, marker)
return e.writeFragmentationUnits(nalus[0], marker)
}
return e.writeAggregationUnit(nalus, pts, marker)
return e.writeAggregationUnit(nalus, marker)
}
func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeSingle(nalu []byte, marker bool) ([]*rtp.Packet, error) {
pkt := &rtp.Packet{
Header: rtp.Header{
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},
@@ -158,7 +141,7 @@ func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*r
return []*rtp.Packet{pkt}, nil
}
func (e *Encoder) writeFragmentationUnits(nalu []byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeFragmentationUnits(nalu []byte, marker bool) ([]*rtp.Packet, error) {
avail := e.PayloadMaxSize - 3
le := len(nalu) - 2
n := le / avail
@@ -168,7 +151,6 @@ func (e *Encoder) writeFragmentationUnits(nalu []byte, pts time.Duration, marker
}
ret := make([]*rtp.Packet, n)
encPTS := e.timeEncoder.Encode(pts)
head := nalu[:2]
nalu = nalu[2:]
@@ -197,7 +179,6 @@ func (e *Encoder) writeFragmentationUnits(nalu []byte, pts time.Duration, marker
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
SSRC: *e.SSRC,
Marker: (i == (n-1) && marker),
},
@@ -226,7 +207,7 @@ func (e *Encoder) lenAggregationUnit(nalus [][]byte, addNALU []byte) int {
return ret
}
func (e *Encoder) writeAggregationUnit(nalus [][]byte, pts time.Duration, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeAggregationUnit(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
payload := make([]byte, e.lenAggregationUnit(nalus, nil))
// header
@@ -252,7 +233,6 @@ func (e *Encoder) writeAggregationUnit(nalus [][]byte, pts time.Duration, marker
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: marker,
},

View File

@@ -47,7 +47,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
@@ -68,7 +67,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -90,7 +88,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -105,7 +102,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -119,7 +115,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -138,12 +133,11 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.nalus, 0)
pkts, err := e.Encode(ca.nalus)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -158,5 +152,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -1,6 +1,2 @@
// Package rtph265 contains a RTP/H265 decoder and encoder.
package rtph265
const (
rtpClockRate = 90000 // H265 always uses 90khz
)

View File

@@ -2,38 +2,32 @@ package rtplpcm
import (
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// Decoder is a RTP/LPCM decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
type Decoder struct {
BitDepth int
SampleRate int
ChannelCount int
timeDecoder *rtptime.Decoder
sampleSize int
}
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
d.sampleSize = d.BitDepth * d.ChannelCount / 8
return nil
}
// Decode decodes audio samples from a RTP packet.
// It returns audio samples and PTS of the first sample.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
plen := len(pkt.Payload)
if (plen % d.sampleSize) != 0 {
return nil, 0, fmt.Errorf("received payload of wrong size")
return nil, fmt.Errorf("received payload of wrong size")
}
return pkt.Payload, d.timeDecoder.Decode(pkt.Timestamp), nil
return pkt.Payload, nil
}

View File

@@ -12,7 +12,6 @@ func TestDecode(t *testing.T) {
t.Run(ca.name, func(t *testing.T) {
d := &Decoder{
BitDepth: 24,
SampleRate: 48000,
ChannelCount: 2,
}
err := d.Init()
@@ -21,7 +20,7 @@ func TestDecode(t *testing.T) {
var samples []byte
for _, pkt := range ca.pkts {
partial, _, err := d.Decode(pkt)
partial, err := d.Decode(pkt)
require.NoError(t, err)
samples = append(samples, partial...)
}
@@ -35,7 +34,6 @@ func FuzzDecoder(f *testing.F) {
f.Fuzz(func(t *testing.T, b []byte) {
d := &Decoder{
BitDepth: 24,
SampleRate: 48000,
ChannelCount: 2,
}
d.Init() //nolint:errcheck

View File

@@ -3,11 +3,8 @@ package rtplpcm
import (
"crypto/rand"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -30,6 +27,12 @@ type Encoder struct {
// payload type of packets.
PayloadType uint8
// bit depth.
BitDepth int
// channel count.
ChannelCount int
// SSRC of packets (optional).
// It defaults to a random value.
SSRC *uint32
@@ -38,20 +41,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
BitDepth int
SampleRate int
ChannelCount int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
sampleSize int
maxPayloadSize int
}
@@ -73,19 +67,11 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
e.sampleSize = e.BitDepth * e.ChannelCount / 8
e.maxPayloadSize = (e.PayloadMaxSize / e.sampleSize) * e.sampleSize
return nil
@@ -101,7 +87,7 @@ func (e *Encoder) packetCount(slen int) int {
}
// Encode encodes audio samples into RTP packets.
func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(samples []byte) ([]*rtp.Packet, error) {
slen := len(samples)
if (slen % e.sampleSize) != 0 {
return nil, fmt.Errorf("invalid samples")
@@ -112,6 +98,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
i := 0
pos := 0
payloadSize := e.maxPayloadSize
timestamp := uint32(0)
for {
if payloadSize > len(samples[pos:]) {
@@ -123,7 +110,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
Timestamp: timestamp,
SSRC: *e.SSRC,
Marker: false,
},
@@ -133,7 +120,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
e.sequenceNumber++
i++
pos += payloadSize
pts += time.Duration(payloadSize/e.sampleSize) * time.Second / time.Duration(e.SampleRate)
timestamp += uint32(payloadSize / e.sampleSize)
if pos == slen {
break

View File

@@ -31,7 +31,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
@@ -48,7 +47,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 486),
@@ -59,7 +57,7 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526600,
Timestamp: 243,
SSRC: 0x9dbb7812,
},
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 194),
@@ -75,15 +73,13 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
BitDepth: 24,
SampleRate: 48000,
ChannelCount: 2,
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.samples, 0)
pkts, err := e.Encode(ca.samples)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -94,12 +90,10 @@ func TestEncodeRandomInitialState(t *testing.T) {
e := &Encoder{
PayloadType: 96,
BitDepth: 24,
SampleRate: 48000,
ChannelCount: 2,
}
err := e.Init()
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -3,11 +3,9 @@ package rtpmjpeg
import (
"errors"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/mediacommon/pkg/codecs/jpeg"
)
@@ -105,7 +103,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/M-JPEG decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
type Decoder struct {
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentsSize int
fragments [][]byte
@@ -115,27 +112,26 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
return nil
}
// Decode decodes an image from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
byts := pkt.Payload
var jh headerJPEG
n, err := jh.unmarshal(byts)
if err != nil {
return nil, 0, err
return nil, err
}
byts = byts[n:]
if jh.Width > maxDimension {
return nil, 0, fmt.Errorf("Width of %d is not supported", jh.Width)
return nil, fmt.Errorf("Width of %d is not supported", jh.Width)
}
if jh.Height > maxDimension {
return nil, 0, fmt.Errorf("Height of %d is not supported", jh.Height)
return nil, fmt.Errorf("Height of %d is not supported", jh.Height)
}
if jh.FragmentOffset == 0 {
@@ -147,7 +143,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
d.firstQTHeader = &headerQuantizationTable{}
n, err := d.firstQTHeader.unmarshal(byts)
if err != nil {
return nil, 0, err
return nil, err
}
byts = byts[n:]
} else {
@@ -160,12 +156,12 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
} else {
if int(jh.FragmentOffset) != d.fragmentsSize {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
d.fragments = d.fragments[:0] // discard pending fragments
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("received wrong fragment")
return nil, fmt.Errorf("received wrong fragment")
}
d.fragmentsSize += len(byts)
@@ -173,11 +169,11 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
}
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
if d.fragmentsSize < 2 {
return nil, 0, fmt.Errorf("invalid data")
return nil, fmt.Errorf("invalid data")
}
data := joinFragments(d.fragments, d.fragmentsSize)
@@ -242,5 +238,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
buf = append(buf, []byte{0xFF, jpeg.MarkerEndOfImage}...)
}
return buf, d.timeDecoder.Decode(pkt.Timestamp), nil
return buf, nil
}

View File

@@ -15,7 +15,7 @@ func TestDecode(t *testing.T) {
require.NoError(t, err)
for _, pkt := range ca.pkts {
image, _, err := d.Decode(pkt)
image, err := d.Decode(pkt)
if err == ErrMorePacketsNeeded {
continue
}

View File

@@ -4,11 +4,9 @@ import (
"crypto/rand"
"fmt"
"sort"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/mediacommon/pkg/codecs/jpeg"
)
@@ -37,16 +35,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -66,24 +59,16 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
return nil
}
// Encode encodes an image into RTP/M-JPEG packets.
func (e *Encoder) Encode(image []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(image []byte) ([]*rtp.Packet, error) {
l := len(image)
if l < 2 || image[0] != 0xFF || image[1] != jpeg.MarkerStartOfImage {
return nil, fmt.Errorf("SOI not found")
@@ -275,7 +260,6 @@ outer:
Version: rtpVersion,
PayloadType: 26,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: len(data) == 0,
},

View File

@@ -289,7 +289,6 @@ var cases = []struct {
Marker: false,
PayloadType: 26,
SequenceNumber: 17645,
Timestamp: 2289528607,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -484,7 +483,6 @@ var cases = []struct {
Marker: true,
PayloadType: 26,
SequenceNumber: 17646,
Timestamp: 2289528607,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -519,12 +517,11 @@ func TestEncode(t *testing.T) {
e := &Encoder{
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(2289528607),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.image, 0)
pkts, err := e.Encode(ca.image)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})

View File

@@ -2,6 +2,5 @@
package rtpmjpeg
const (
rtpClockRate = 90000
maxDimension = 2040
)

View File

@@ -3,12 +3,9 @@ package rtpmpeg1audio
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -33,7 +30,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/MPEG-1/2 Audio decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
type Decoder struct {
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragments [][]byte
fragmentsSize int
@@ -42,23 +38,22 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(90000)
return nil
}
// Decode decodes frames from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
if len(pkt.Payload) < 5 {
d.fragments = d.fragments[:0] // discard pending fragments
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
mbz := uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1])
if mbz != 0 {
d.fragments = d.fragments[:0] // discard pending fragments
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("invalid MBZ: %v", mbz)
return nil, fmt.Errorf("invalid MBZ: %v", mbz)
}
offset := uint16(pkt.Payload[2])<<8 | uint16(pkt.Payload[3])
@@ -75,7 +70,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
var h mpeg1audio.FrameHeader
err := h.Unmarshal(buf)
if err != nil {
return nil, 0, err
return nil, err
}
fl := h.FrameLen()
@@ -88,24 +83,24 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
}
} else {
if len(frames) != 0 {
return nil, 0, fmt.Errorf("invalid packet")
return nil, fmt.Errorf("invalid packet")
}
d.fragments = append(d.fragments, buf)
d.fragmentsSize = bl
d.fragmentsExpected = fl - bl
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
}
} else {
if int(offset) != d.fragmentsSize {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
d.fragments = d.fragments[:0] // discard pending fragments
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("unexpected offset %v, expected %v", offset, d.fragmentsSize)
return nil, fmt.Errorf("unexpected offset %v, expected %v", offset, d.fragmentsSize)
}
bl := len(pkt.Payload[4:])
@@ -115,13 +110,13 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
if d.fragmentsExpected < 0 {
d.fragments = d.fragments[:0] // discard pending fragments
d.fragmentsSize = 0
return nil, 0, fmt.Errorf("fragment is too big")
return nil, fmt.Errorf("fragment is too big")
}
d.fragments = append(d.fragments, pkt.Payload[4:])
if d.fragmentsExpected > 0 {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
frames = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
@@ -129,5 +124,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
d.fragmentsSize = 0
}
return frames, d.timeDecoder.Decode(pkt.Timestamp), nil
return frames, nil
}

View File

@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
var frames [][]byte
for _, pkt := range ca.pkts {
frames, _, err = d.Decode(pkt)
frames, err = d.Decode(pkt)
}
require.NoError(t, err)

View File

@@ -2,12 +2,9 @@ package rtpmpeg1audio
import (
"crypto/rand"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -43,16 +40,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -72,34 +64,27 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
return nil
}
// Encode encodes frames into RTP packets.
func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(frames [][]byte) ([]*rtp.Packet, error) {
var rets []*rtp.Packet
var batch [][]byte
timestamp := uint32(0)
for _, frame := range frames {
if lenAggregated(batch, frame) <= e.PayloadMaxSize {
batch = append(batch, frame)
} else {
// write last batch
// write current batch
if batch != nil {
pkts, err := e.writeBatch(batch, pts)
pkts, err := e.writeBatch(batch, timestamp)
if err != nil {
return nil, err
}
@@ -112,7 +97,7 @@ func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, err
return nil, err
}
pts += time.Duration(h.SampleCount()) * time.Second / time.Duration(h.SampleRate)
timestamp += uint32(h.SampleCount())
}
}
@@ -122,7 +107,7 @@ func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, err
}
// write last batch
pkts, err := e.writeBatch(batch, pts)
pkts, err := e.writeBatch(batch, timestamp)
if err != nil {
return nil, err
}
@@ -131,15 +116,15 @@ func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, err
return rets, nil
}
func (e *Encoder) writeBatch(frames [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) writeBatch(frames [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
if len(frames) != 1 || lenAggregated(frames, nil) < e.PayloadMaxSize {
return e.writeAggregated(frames, pts)
return e.writeAggregated(frames, timestamp)
}
return e.writeFragmented(frames[0], pts)
return e.writeFragmented(frames[0], timestamp)
}
func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) writeFragmented(frame []byte, timestamp uint32) ([]*rtp.Packet, error) {
avail := e.PayloadMaxSize - 4
le := len(frame)
packetCount := le / avail
@@ -150,7 +135,6 @@ func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packe
pos := 0
ret := make([]*rtp.Packet, packetCount)
encPTS := e.timeEncoder.Encode(pts)
for i := range ret {
var le int
@@ -171,7 +155,7 @@ func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packe
Version: rtpVersion,
PayloadType: 14,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
Timestamp: timestamp,
SSRC: *e.SSRC,
Marker: true,
},
@@ -184,7 +168,7 @@ func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packe
return ret, nil
}
func (e *Encoder) writeAggregated(frames [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) writeAggregated(frames [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
payload := make([]byte, lenAggregated(frames, nil))
n := 4
@@ -197,7 +181,7 @@ func (e *Encoder) writeAggregated(frames [][]byte, pts time.Duration) ([]*rtp.Pa
Version: rtpVersion,
PayloadType: 14,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
Timestamp: timestamp,
SSRC: *e.SSRC,
Marker: true,
},

View File

@@ -42,7 +42,6 @@ var cases = []struct {
Marker: true,
PayloadType: 14,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -100,7 +99,6 @@ var cases = []struct {
Marker: true,
PayloadType: 14,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -288,7 +286,6 @@ var cases = []struct {
Marker: true,
PayloadType: 14,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -350,7 +347,6 @@ var cases = []struct {
Marker: true,
PayloadType: 14,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -412,7 +408,6 @@ var cases = []struct {
Marker: true,
PayloadType: 14,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -474,13 +469,12 @@ func TestEncode(t *testing.T) {
e := &Encoder{
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
PayloadMaxSize: 400,
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.frames, 0)
pkts, err := e.Encode(ca.frames)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -493,5 +487,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -3,13 +3,10 @@ package rtpmpeg4audiogeneric
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/bits"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -27,9 +24,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/MPEG-4 Audio decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
type Decoder struct {
// sample rate of input packets.
SampleRate int
// The number of bits in which the AU-size field is encoded in the AU-header.
SizeLength int
@@ -39,7 +33,6 @@ type Decoder struct {
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
IndexDeltaLength int
timeDecoder *rtptime.Decoder
firstAUParsed bool
adtsMode bool
fragments [][]byte
@@ -48,24 +41,23 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
return nil
}
// Decode decodes AUs from a RTP packet.
// It returns the AUs and the PTS of the first AU.
// The PTS of subsequent AUs can be calculated by adding time.Second*mpeg4audio.SamplesPerAccessUnit/clockRate.
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
if len(pkt.Payload) < 2 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
// AU-headers-length (16 bits)
headersLen := int(uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1]))
if headersLen == 0 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("invalid AU-headers-length")
return nil, fmt.Errorf("invalid AU-headers-length")
}
payload := pkt.Payload[2:]
@@ -73,7 +65,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
dataLens, err := d.readAUHeaders(payload, headersLen)
if err != nil {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, err
return nil, err
}
pos := (headersLen / 8)
@@ -90,7 +82,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
aus = make([][]byte, len(dataLens))
for i, dataLen := range dataLens {
if len(payload) < int(dataLen) {
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
aus[i] = payload[:dataLen]
@@ -98,40 +90,40 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
}
} else {
if len(dataLens) != 1 {
return nil, 0, fmt.Errorf("a fragmented packet can only contain one AU")
return nil, fmt.Errorf("a fragmented packet can only contain one AU")
}
if len(payload) < int(dataLens[0]) {
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
d.fragmentsSize = int(dataLens[0])
d.fragments = append(d.fragments, payload[:dataLens[0]])
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
} else {
// we are decoding a fragmented AU
if len(dataLens) != 1 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("a fragmented packet can only contain one AU")
return nil, fmt.Errorf("a fragmented packet can only contain one AU")
}
if len(payload) < int(dataLens[0]) {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("payload is too short")
return nil, fmt.Errorf("payload is too short")
}
d.fragmentsSize += int(dataLens[0])
if d.fragmentsSize > mpeg4audio.MaxAccessUnitSize {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
d.fragmentsSize, mpeg4audio.MaxAccessUnitSize)
}
d.fragments = append(d.fragments, payload[:dataLens[0]])
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
aus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
@@ -140,10 +132,10 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
aus, err = d.removeADTS(aus)
if err != nil {
return nil, 0, err
return nil, err
}
return aus, d.timeDecoder.Decode(pkt.Timestamp), nil
return aus, nil
}
func (d *Decoder) readAUHeaders(buf []byte, headersLen int) ([]uint64, error) {

View File

@@ -11,7 +11,6 @@ func TestDecode(t *testing.T) {
for _, ca := range cases {
t.Run(ca.name, func(t *testing.T) {
d := &Decoder{
SampleRate: 48000,
SizeLength: ca.sizeLength,
IndexLength: ca.indexLength,
IndexDeltaLength: ca.indexDeltaLength,
@@ -24,7 +23,7 @@ func TestDecode(t *testing.T) {
for _, pkt := range ca.pkts {
clone := pkt.Clone()
addAUs, _, err := d.Decode(pkt)
addAUs, err := d.Decode(pkt)
// test input integrity
require.Equal(t, clone, pkt)
@@ -44,7 +43,6 @@ func TestDecode(t *testing.T) {
func TestDecodeADTS(t *testing.T) {
d := &Decoder{
SampleRate: 16000,
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
@@ -53,13 +51,12 @@ func TestDecodeADTS(t *testing.T) {
require.NoError(t, err)
for i := 0; i < 2; i++ {
aus, _, err := d.Decode(&rtp.Packet{
aus, err := d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -75,7 +72,6 @@ func TestDecodeADTS(t *testing.T) {
func FuzzDecoder(f *testing.F) {
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
d := &Decoder{
SampleRate: 16000,
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,

View File

@@ -2,11 +2,9 @@ package rtpmpeg4audiogeneric
import (
"crypto/rand"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/mediacommon/pkg/bits"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
)
@@ -31,25 +29,6 @@ type Encoder struct {
// payload type of packets.
PayloadType uint8
// SSRC of packets (optional).
// It defaults to a random value.
SSRC *uint32
// initial sequence number of packets (optional).
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
// sample rate of packets.
SampleRate int
// The number of bits in which the AU-size field is encoded in the AU-header.
SizeLength int
@@ -59,8 +38,19 @@ type Encoder struct {
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
IndexDeltaLength int
// SSRC of packets (optional).
// It defaults to a random value.
SSRC *uint32
// initial sequence number of packets (optional).
// It defaults to a random value.
InitialSequenceNumber *uint16
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -80,26 +70,19 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
return nil
}
// Encode encodes AUs into RTP packets.
func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(aus [][]byte) ([]*rtp.Packet, error) {
var rets []*rtp.Packet
var batch [][]byte
timestamp := uint32(0)
// split AUs into batches
for _, au := range aus {
@@ -107,14 +90,14 @@ func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error)
// add to existing batch
batch = append(batch, au)
} else {
// write last batch
// write current batch
if batch != nil {
pkts, err := e.writeBatch(batch, pts)
pkts, err := e.writeBatch(batch, timestamp)
if err != nil {
return nil, err
}
rets = append(rets, pkts...)
pts += time.Duration(len(batch)) * mpeg4audio.SamplesPerAccessUnit * time.Second / time.Duration(e.SampleRate)
timestamp += uint32(len(batch)) * mpeg4audio.SamplesPerAccessUnit
}
// initialize new batch
@@ -123,7 +106,7 @@ func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error)
}
// write last batch
pkts, err := e.writeBatch(batch, pts)
pkts, err := e.writeBatch(batch, timestamp)
if err != nil {
return nil, err
}
@@ -132,15 +115,15 @@ func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error)
return rets, nil
}
func (e *Encoder) writeBatch(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) writeBatch(aus [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
if len(aus) != 1 || e.lenAggregated(aus, nil) < e.PayloadMaxSize {
return e.writeAggregated(aus, pts)
return e.writeAggregated(aus, timestamp)
}
return e.writeFragmented(aus[0], pts)
return e.writeFragmented(aus[0], timestamp)
}
func (e *Encoder) writeFragmented(au []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) writeFragmented(au []byte, timestamp uint32) ([]*rtp.Packet, error) {
auHeadersLen := e.SizeLength + e.IndexLength
auHeadersLenBytes := auHeadersLen / 8
if (auHeadersLen % 8) != 0 {
@@ -156,7 +139,6 @@ func (e *Encoder) writeFragmented(au []byte, pts time.Duration) ([]*rtp.Packet,
}
ret := make([]*rtp.Packet, packetCount)
encPTS := e.timeEncoder.Encode(pts)
for i := range ret {
var le int
@@ -186,7 +168,7 @@ func (e *Encoder) writeFragmented(au []byte, pts time.Duration) ([]*rtp.Packet,
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
Timestamp: timestamp,
SSRC: *e.SSRC,
Marker: (i == (packetCount - 1)),
},
@@ -234,7 +216,7 @@ func (e *Encoder) lenAggregated(aus [][]byte, addAU []byte) int {
return ret
}
func (e *Encoder) writeAggregated(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) writeAggregated(aus [][]byte, timestamp uint32) ([]*rtp.Packet, error) {
payload := make([]byte, e.lenAggregated(aus, nil))
// AU-headers
@@ -271,7 +253,7 @@ func (e *Encoder) writeAggregated(aus [][]byte, pts time.Duration) ([]*rtp.Packe
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
Timestamp: timestamp,
SSRC: *e.SSRC,
Marker: true,
},

View File

@@ -100,7 +100,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -170,7 +169,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -196,7 +194,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -210,7 +207,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -224,7 +220,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -252,7 +247,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -267,7 +261,7 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289529429,
Timestamp: 3072,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -281,7 +275,7 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289529429,
Timestamp: 3072,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -306,7 +300,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -331,7 +324,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -357,7 +349,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -383,7 +374,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -398,7 +388,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -413,7 +402,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -438,7 +426,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -452,7 +439,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -466,7 +452,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
@@ -483,10 +468,8 @@ func TestEncode(t *testing.T) {
t.Run(ca.name, func(t *testing.T) {
e := &Encoder{
PayloadType: 96,
SampleRate: 48000,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
SizeLength: ca.sizeLength,
IndexLength: ca.indexLength,
IndexDeltaLength: ca.indexDeltaLength,
@@ -494,7 +477,7 @@ func TestEncode(t *testing.T) {
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.aus, 0)
pkts, err := e.Encode(ca.aus)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -504,7 +487,6 @@ func TestEncode(t *testing.T) {
func TestEncodeRandomInitialState(t *testing.T) {
e := &Encoder{
PayloadType: 96,
SampleRate: 48000,
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
@@ -513,5 +495,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -3,12 +3,9 @@ package rtpmpeg4audiolatm
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -26,10 +23,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/MPEG-4 Audio decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
type Decoder struct {
// StreamMuxConfig.
Config *mpeg4audio.StreamMuxConfig
timeDecoder *rtptime.Decoder
fragments [][]byte
fragmentsSize int
fragmentsExpected int
@@ -37,24 +30,19 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
if d.Config == nil || len(d.Config.Programs) != 1 || len(d.Config.Programs[0].Layers) != 1 {
return fmt.Errorf("unsupported StreamMuxConfig")
}
d.timeDecoder = rtptime.NewDecoder(d.Config.Programs[0].Layers[0].AudioSpecificConfig.SampleRate)
return nil
}
// Decode decodes an AU from a RTP packet.
// It returns the AU and its PTS.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
var au []byte
buf := pkt.Payload
if len(d.fragments) == 0 {
pl, n, err := payloadLengthInfoDecode(buf)
if err != nil {
return nil, 0, err
return nil, err
}
buf = buf[n:]
@@ -66,14 +54,14 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
} else {
if pl > mpeg4audio.MaxAccessUnitSize {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d",
pl, mpeg4audio.MaxAccessUnitSize)
}
d.fragments = append(d.fragments, buf)
d.fragmentsSize = pl
d.fragmentsExpected = pl - bl
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
} else {
bl := len(buf)
@@ -81,7 +69,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
if d.fragmentsExpected > bl {
d.fragments = append(d.fragments, buf)
d.fragmentsExpected -= bl
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
d.fragments = append(d.fragments, buf[:d.fragmentsExpected])
@@ -91,5 +79,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
d.fragments = d.fragments[:0]
}
return au, d.timeDecoder.Decode(pkt.Timestamp), nil
return au, nil
}

View File

@@ -3,7 +3,6 @@ package rtpmpeg4audiolatm
import (
"testing"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@@ -11,9 +10,7 @@ import (
func TestDecode(t *testing.T) {
for _, ca := range cases {
t.Run(ca.name, func(t *testing.T) {
d := &Decoder{
Config: ca.config,
}
d := &Decoder{}
err := d.Init()
require.NoError(t, err)
@@ -22,7 +19,7 @@ func TestDecode(t *testing.T) {
for _, pkt := range ca.pkts {
clone := pkt.Clone()
au, _, err = d.Decode(pkt)
au, err = d.Decode(pkt)
// test input integrity
require.Equal(t, clone, pkt)
@@ -40,32 +37,16 @@ func TestDecode(t *testing.T) {
}
func TestDecodeOtherData(t *testing.T) {
d := &Decoder{
Config: &mpeg4audio.StreamMuxConfig{
Programs: []*mpeg4audio.StreamMuxConfigProgram{{
Layers: []*mpeg4audio.StreamMuxConfigLayer{{
AudioSpecificConfig: &mpeg4audio.AudioSpecificConfig{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
LatmBufferFullness: 255,
}},
}},
OtherDataPresent: true,
OtherDataLenBits: 16,
},
}
d := &Decoder{}
err := d.Init()
require.NoError(t, err)
au, _, err := d.Decode(&rtp.Packet{
au, err := d.Decode(&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 2646308882,
},
Payload: []byte{
@@ -79,20 +60,7 @@ func TestDecodeOtherData(t *testing.T) {
func FuzzDecoder(f *testing.F) {
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
d := &Decoder{
Config: &mpeg4audio.StreamMuxConfig{
Programs: []*mpeg4audio.StreamMuxConfigProgram{{
Layers: []*mpeg4audio.StreamMuxConfigLayer{{
AudioSpecificConfig: &mpeg4audio.AudioSpecificConfig{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
LatmBufferFullness: 255,
}},
}},
},
}
d := &Decoder{}
d.Init() //nolint:errcheck
d.Decode(&rtp.Packet{ //nolint:errcheck

View File

@@ -2,13 +2,8 @@ package rtpmpeg4audiolatm
import (
"crypto/rand"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -31,9 +26,6 @@ type Encoder struct {
// payload type of packets.
PayloadType uint8
// StreamMuxConfig.
Config *mpeg4audio.StreamMuxConfig
// SSRC of packets (optional).
// It defaults to a random value.
SSRC *uint32
@@ -42,24 +34,15 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
func (e *Encoder) Init() error {
if e.Config == nil || len(e.Config.Programs) != 1 || len(e.Config.Programs[0].Layers) != 1 {
return fmt.Errorf("unsupported StreamMuxConfig")
}
if e.SSRC == nil {
v, err := randUint32()
if err != nil {
@@ -75,19 +58,11 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(e.Config.Programs[0].Layers[0].AudioSpecificConfig.SampleRate, *e.InitialTimestamp)
return nil
}
@@ -101,15 +76,14 @@ func (e *Encoder) packetCount(auLen int, plil int) int {
return packetCount
}
// Encode encodes AUs into RTP packets.
func (e *Encoder) Encode(au []byte, pts time.Duration) ([]*rtp.Packet, error) {
// Encode encodes an access unit into RTP packets.
func (e *Encoder) Encode(au []byte) ([]*rtp.Packet, error) {
auLen := len(au)
plil := payloadLengthInfoEncodeSize(auLen)
packetCount := e.packetCount(auLen, plil)
avail := e.PayloadMaxSize - plil
ret := make([]*rtp.Packet, packetCount)
encPTS := e.timeEncoder.Encode(pts)
for i := range ret {
var final bool
@@ -138,7 +112,6 @@ func (e *Encoder) Encode(au []byte, pts time.Duration) ([]*rtp.Packet, error) {
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
SSRC: *e.SSRC,
Marker: final,
},

View File

@@ -61,7 +61,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 2646308882,
},
Payload: []byte{
@@ -92,7 +91,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 2646308882,
},
Payload: mergeBytes(
@@ -108,7 +106,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 2646308882,
},
Payload: mergeBytes(
@@ -123,7 +120,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 2646308882,
},
Payload: mergeBytes(
@@ -140,15 +136,13 @@ func TestEncode(t *testing.T) {
t.Run(ca.name, func(t *testing.T) {
e := &Encoder{
PayloadType: 96,
Config: ca.config,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.au, 0)
pkts, err := e.Encode(ca.au)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -158,22 +152,9 @@ func TestEncode(t *testing.T) {
func TestEncodeRandomInitialState(t *testing.T) {
e := &Encoder{
PayloadType: 96,
Config: &mpeg4audio.StreamMuxConfig{
Programs: []*mpeg4audio.StreamMuxConfigProgram{{
Layers: []*mpeg4audio.StreamMuxConfigLayer{{
AudioSpecificConfig: &mpeg4audio.AudioSpecificConfig{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
LatmBufferFullness: 255,
}},
}},
},
}
err := e.Init()
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -3,11 +3,8 @@ package rtpmpeg4video
import (
"errors"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -29,19 +26,17 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/MPEG-4 Video decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
type Decoder struct {
timeDecoder *rtptime.Decoder
fragments [][]byte
fragmentsSize int
}
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(90000)
return nil
}
// Decode decodes a frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
var frame []byte
if len(d.fragments) == 0 {
@@ -50,24 +45,24 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
} else {
d.fragmentsSize = len(pkt.Payload)
d.fragments = append(d.fragments, pkt.Payload)
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
} else {
d.fragmentsSize += len(pkt.Payload)
if d.fragmentsSize > maxFrameSize {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("frame size (%d) is too big, maximum is %d", d.fragmentsSize, maxFrameSize)
return nil, fmt.Errorf("frame size (%d) is too big, maximum is %d", d.fragmentsSize, maxFrameSize)
}
d.fragments = append(d.fragments, pkt.Payload)
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
frame = joinFragments(d.fragments, d.fragmentsSize)
d.fragments = d.fragments[:0]
}
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
return frame, nil
}

View File

@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
var frame []byte
for _, pkt := range ca.pkts {
frame, _, err = d.Decode(pkt)
frame, err = d.Decode(pkt)
if err == ErrMorePacketsNeeded {
continue
}

View File

@@ -2,11 +2,8 @@ package rtpmpeg4video
import (
"crypto/rand"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -37,16 +34,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -66,19 +58,11 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
return nil
}
@@ -92,14 +76,13 @@ func packetCount(avail, le int) int {
}
// Encode encodes a frame into RTP packets.
func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
avail := e.PayloadMaxSize
le := len(frame)
packetCount := packetCount(avail, le)
pos := 0
ret := make([]*rtp.Packet, packetCount)
encPTS := e.timeEncoder.Encode(pts)
for i := range ret {
var le int
@@ -114,7 +97,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: encPTS,
SSRC: *e.SSRC,
Marker: (i == len(ret)-1),
},

View File

@@ -31,7 +31,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{
@@ -50,7 +49,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 100/4),
@@ -61,7 +59,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 50/4),
@@ -77,13 +74,12 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
PayloadMaxSize: 100,
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.frame, 0)
pkts, err := e.Encode(ca.frame)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -98,5 +94,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -1,27 +1,18 @@
package rtpsimpleaudio
import (
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// Decoder is a RTP/simple audio decoder.
type Decoder struct {
SampleRate int
timeDecoder *rtptime.Decoder
}
type Decoder struct{}
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
return nil
}
// Decode decodes an audio frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
return pkt.Payload, d.timeDecoder.Decode(pkt.Timestamp), nil
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
return pkt.Payload, nil
}

View File

@@ -9,13 +9,11 @@ import (
func TestDecode(t *testing.T) {
for _, ca := range cases {
t.Run(ca.name, func(t *testing.T) {
d := &Decoder{
SampleRate: 8000,
}
d := &Decoder{}
err := d.Init()
require.NoError(t, err)
frame, _, err := d.Decode(ca.pkt)
frame, err := d.Decode(ca.pkt)
require.NoError(t, err)
require.Equal(t, ca.frame, frame)
})

View File

@@ -3,11 +3,8 @@ package rtpsimpleaudio
import (
"crypto/rand"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -37,18 +34,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
SampleRate int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
}
// Init initializes the encoder.
@@ -68,24 +58,16 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
return nil
}
// Encode encodes an audio frame into a RTP packet.
func (e *Encoder) Encode(frame []byte, pts time.Duration) (*rtp.Packet, error) {
func (e *Encoder) Encode(frame []byte) (*rtp.Packet, error) {
if len(frame) > e.PayloadMaxSize {
return nil, fmt.Errorf("frame is too big")
}
@@ -95,7 +77,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) (*rtp.Packet, error) {
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: false,
},

View File

@@ -29,7 +29,6 @@ var cases = []struct {
Marker: false,
PayloadType: 0,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
@@ -42,15 +41,13 @@ func TestEncode(t *testing.T) {
t.Run(ca.name, func(t *testing.T) {
e := &Encoder{
PayloadType: 0,
SampleRate: 8000,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
}
err := e.Init()
require.NoError(t, err)
pkt, err := e.Encode(ca.frame, 0)
pkt, err := e.Encode(ca.frame)
require.NoError(t, err)
require.Equal(t, ca.pkt, pkt)
})
@@ -60,11 +57,9 @@ func TestEncode(t *testing.T) {
func TestEncodeRandomInitialState(t *testing.T) {
e := &Encoder{
PayloadType: 0,
SampleRate: 8000,
}
err := e.Init()
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -3,13 +3,10 @@ package rtpvp8
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/vp8"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -34,7 +31,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/VP8 decoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
type Decoder struct {
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentsSize int
fragments [][]byte
@@ -42,22 +38,21 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
return nil
}
// Decode decodes a VP8 frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
var vpkt codecs.VP8Packet
_, err := vpkt.Unmarshal(pkt.Payload)
if err != nil {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, err
return nil, err
}
if vpkt.PID != 0 {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("packets containing single partitions are not supported")
return nil, fmt.Errorf("packets containing single partitions are not supported")
}
var frame []byte
@@ -69,35 +64,35 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
if !pkt.Marker {
d.fragmentsSize = len(vpkt.Payload)
d.fragments = append(d.fragments, vpkt.Payload)
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
frame = vpkt.Payload
} else {
if len(d.fragments) == 0 {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
return nil, 0, fmt.Errorf("received a non-starting fragment")
return nil, fmt.Errorf("received a non-starting fragment")
}
d.fragmentsSize += len(vpkt.Payload)
if d.fragmentsSize > vp8.MaxFrameSize {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("frame size (%d) is too big, maximum is %d", d.fragmentsSize, vp8.MaxFrameSize)
return nil, fmt.Errorf("frame size (%d) is too big, maximum is %d", d.fragmentsSize, vp8.MaxFrameSize)
}
d.fragments = append(d.fragments, vpkt.Payload)
if !pkt.Marker {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
frame = joinFragments(d.fragments, d.fragmentsSize)
d.fragments = d.fragments[:0]
}
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
return frame, nil
}

View File

@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
var frame []byte
for _, pkt := range ca.pkts {
frame, _, err = d.Decode(pkt)
frame, err = d.Decode(pkt)
}
require.NoError(t, err)

View File

@@ -3,12 +3,9 @@ package rtpvp8
import (
"crypto/rand"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -39,16 +36,11 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
sequenceNumber uint16
timeEncoder *rtptime.Encoder
vp codecs.VP8Payloader
}
@@ -69,24 +61,16 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
return nil
}
// Encode encodes a VP8 frame into RTP/VP8 packets.
func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame)
if payloads == nil {
return nil, fmt.Errorf("payloader failed")
@@ -101,7 +85,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: i == (plen - 1),
},

View File

@@ -47,7 +47,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{0x10, 0x01, 0x02, 0x03, 0x04},
@@ -64,7 +63,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes([]byte{0x10}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02, 0x03}),
@@ -75,7 +73,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes([]byte{0x00, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02}),
@@ -86,7 +83,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes([]byte{0x00, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 294)),
@@ -102,12 +98,11 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.frame, 0)
pkts, err := e.Encode(ca.frame)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -122,5 +117,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -1,6 +1,2 @@
// Package rtpvp8 contains a RTP/VP8 decoder and encoder.
package rtpvp8
const (
rtpClockRate = 90000 // VP8 always uses 90khz
)

View File

@@ -3,13 +3,10 @@ package rtpvp9
import (
"errors"
"fmt"
"time"
"github.com/bluenviron/mediacommon/pkg/codecs/vp9"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -34,7 +31,6 @@ func joinFragments(fragments [][]byte, size int) []byte {
// Decoder is a RTP/VP9 decoder.
// Specification: https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
type Decoder struct {
timeDecoder *rtptime.Decoder
firstPacketReceived bool
fragmentsSize int
fragments [][]byte
@@ -42,17 +38,16 @@ type Decoder struct {
// Init initializes the decoder.
func (d *Decoder) Init() error {
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
return nil
}
// Decode decodes a VP9 frame from a RTP packet.
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, error) {
var vpkt codecs.VP9Packet
_, err := vpkt.Unmarshal(pkt.Payload)
if err != nil {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, err
return nil, err
}
var frame []byte
@@ -64,35 +59,35 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
if !vpkt.E {
d.fragmentsSize = len(vpkt.Payload)
d.fragments = append(d.fragments, vpkt.Payload)
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
frame = vpkt.Payload
} else {
if len(d.fragments) == 0 {
if !d.firstPacketReceived {
return nil, 0, ErrNonStartingPacketAndNoPrevious
return nil, ErrNonStartingPacketAndNoPrevious
}
return nil, 0, fmt.Errorf("received a non-starting fragment")
return nil, fmt.Errorf("received a non-starting fragment")
}
d.fragmentsSize += len(vpkt.Payload)
if d.fragmentsSize > vp9.MaxFrameSize {
d.fragments = d.fragments[:0] // discard pending fragments
return nil, 0, fmt.Errorf("frame size (%d) is too big, maximum is %d", d.fragmentsSize, vp9.MaxFrameSize)
return nil, fmt.Errorf("frame size (%d) is too big, maximum is %d", d.fragmentsSize, vp9.MaxFrameSize)
}
d.fragments = append(d.fragments, vpkt.Payload)
if !vpkt.E {
return nil, 0, ErrMorePacketsNeeded
return nil, ErrMorePacketsNeeded
}
frame = joinFragments(d.fragments, d.fragmentsSize)
d.fragments = d.fragments[:0]
}
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
return frame, nil
}

View File

@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
var frame []byte
for _, pkt := range ca.pkts {
frame, _, err = d.Decode(pkt)
frame, err = d.Decode(pkt)
}
require.NoError(t, err)

View File

@@ -3,12 +3,9 @@ package rtpvp9
import (
"crypto/rand"
"fmt"
"time"
"github.com/pion/rtp"
"github.com/pion/rtp/codecs"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
)
const (
@@ -39,10 +36,6 @@ type Encoder struct {
// It defaults to a random value.
InitialSequenceNumber *uint16
// initial timestamp of packets (optional).
// It defaults to a random value.
InitialTimestamp *uint32
// maximum size of packet payloads (optional).
// It defaults to 1460.
PayloadMaxSize int
@@ -52,7 +45,6 @@ type Encoder struct {
InitialPictureID *uint16
sequenceNumber uint16
timeEncoder *rtptime.Encoder
vp codecs.VP9Payloader
}
@@ -73,13 +65,6 @@ func (e *Encoder) Init() error {
v2 := uint16(v)
e.InitialSequenceNumber = &v2
}
if e.InitialTimestamp == nil {
v, err := randUint32()
if err != nil {
return err
}
e.InitialTimestamp = &v
}
if e.PayloadMaxSize == 0 {
e.PayloadMaxSize = defaultPayloadMaxSize
}
@@ -93,7 +78,6 @@ func (e *Encoder) Init() error {
}
e.sequenceNumber = *e.InitialSequenceNumber
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
e.vp.InitialPictureIDFn = func() uint16 {
return *e.InitialPictureID
@@ -103,7 +87,7 @@ func (e *Encoder) Init() error {
}
// Encode encodes a VP9 frame into RTP/VP9 packets.
func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error) {
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame)
if payloads == nil {
return nil, fmt.Errorf("payloader failed")
@@ -118,7 +102,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
Version: rtpVersion,
PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber,
Timestamp: e.timeEncoder.Encode(pts),
SSRC: *e.SSRC,
Marker: i == (plen - 1),
},

View File

@@ -47,7 +47,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: []byte{0x9c, 0xb5, 0xaf, 0x01, 0x02, 0x03, 0x04},
@@ -64,7 +63,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes([]byte{0x98, 0xb5, 0xaf}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01}),
@@ -75,7 +73,6 @@ var cases = []struct {
Marker: false,
PayloadType: 96,
SequenceNumber: 17646,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes([]byte{0x90, 0xb5, 0xaf, 0x02, 0x03, 0x04},
@@ -87,7 +84,6 @@ var cases = []struct {
Marker: true,
PayloadType: 96,
SequenceNumber: 17647,
Timestamp: 2289526357,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes([]byte{0x94, 0xb5, 0xaf, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 295)),
@@ -103,13 +99,12 @@ func TestEncode(t *testing.T) {
PayloadType: 96,
SSRC: uint32Ptr(0x9dbb7812),
InitialSequenceNumber: uint16Ptr(0x44ed),
InitialTimestamp: uint32Ptr(0x88776655),
InitialPictureID: uint16Ptr(0x35af),
}
err := e.Init()
require.NoError(t, err)
pkts, err := e.Encode(ca.frame, 0)
pkts, err := e.Encode(ca.frame)
require.NoError(t, err)
require.Equal(t, ca.pkts, pkts)
})
@@ -124,5 +119,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, nil, e.SSRC)
require.NotEqual(t, nil, e.InitialSequenceNumber)
require.NotEqual(t, nil, e.InitialTimestamp)
}

View File

@@ -1,6 +1,2 @@
// Package rtpvp9 contains a RTP/VP9 decoder and encoder.
package rtpvp9
const (
rtpClockRate = 90000 // VP9 always uses 90khz
)

View File

@@ -22,14 +22,14 @@ func TestVP8DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -22,14 +22,14 @@ func TestVP9DecEncoder(t *testing.T) {
enc, err := format.CreateEncoder()
require.NoError(t, err)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04}, 0)
pkts, err := enc.Encode([]byte{0x01, 0x02, 0x03, 0x04})
require.NoError(t, err)
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
dec, err := format.CreateDecoder()
require.NoError(t, err)
byts, _, err := dec.Decode(pkts[0])
byts, err := dec.Decode(pkts[0])
require.NoError(t, err)
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
}

View File

@@ -1,44 +0,0 @@
// Package rtptime contains a RTP timestamp decoder and encoder.
package rtptime
import (
"time"
)
// avoid an int64 overflow and preserve resolution by splitting division into two parts:
// first add the integer part, then the decimal part.
func multiplyAndDivide(v, m, d time.Duration) time.Duration {
secs := v / d
dec := v % d
return (secs*m + dec*m/d)
}
// Decoder is a RTP timestamp decoder.
type Decoder struct {
clockRate time.Duration
initialized bool
overall time.Duration
prev uint32
}
// NewDecoder allocates a Decoder.
func NewDecoder(clockRate int) *Decoder {
return &Decoder{
clockRate: time.Duration(clockRate),
}
}
// Decode decodes a timestamp.
func (d *Decoder) Decode(ts uint32) time.Duration {
if !d.initialized {
d.initialized = true
d.prev = ts
return 0
}
diff := int32(ts - d.prev)
d.prev = ts
d.overall += time.Duration(diff)
return multiplyAndDivide(d.overall, time.Second, d.clockRate)
}

View File

@@ -1,96 +0,0 @@
package rtptime
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestDecoderNegativeDiff(t *testing.T) {
d := NewDecoder(90000)
i := uint32(0)
pts := d.Decode(i)
require.Equal(t, time.Duration(0), pts)
i += 90000 * 2
pts = d.Decode(i)
require.Equal(t, 2*time.Second, pts)
i -= 90000 * 1
pts = d.Decode(i)
require.Equal(t, 1*time.Second, pts)
i += 90000 * 2
pts = d.Decode(i)
require.Equal(t, 3*time.Second, pts)
}
func TestDecoderOverflow(t *testing.T) {
d := NewDecoder(90000)
i := uint32(0xFFFFFFFF - 90000 + 1)
secs := time.Duration(0)
pts := d.Decode(i)
require.Equal(t, time.Duration(0), pts)
const stride = 1500
lim := uint32(uint64(0xFFFFFFFF + 1 - (stride * 90000)))
for n := 0; n < 100; n++ {
// overflow
i += 90000 * stride
secs += stride
pts = d.Decode(i)
require.Equal(t, secs*time.Second, pts)
// reach 2^32 slowly
secs += stride
i += 90000 * stride
for ; i < lim; i += 90000 * stride {
pts = d.Decode(i)
require.Equal(t, secs*time.Second, pts)
secs += stride
}
}
}
func TestDecoderOverflowAndBack(t *testing.T) {
d := NewDecoder(90000)
pts := d.Decode(0xFFFFFFFF - 90000 + 1)
require.Equal(t, time.Duration(0), pts)
pts = d.Decode(90000)
require.Equal(t, 2*time.Second, pts)
pts = d.Decode(0xFFFFFFFF - 90000 + 1)
require.Equal(t, time.Duration(0), pts)
pts = d.Decode(0xFFFFFFFF - 90000 + 1 - 90000)
require.Equal(t, -1*time.Second, pts)
pts = d.Decode(0xFFFFFFFF - 90000 + 1)
require.Equal(t, time.Duration(0), pts)
pts = d.Decode(90000)
require.Equal(t, 2*time.Second, pts)
}
func BenchmarkDecoder(b *testing.B) {
for i := 0; i < b.N; i++ {
func() {
d := NewDecoder(90000)
n := uint32(0)
for j := 0; j < 200; j++ {
if (j % 2) == 0 {
n += 90000
} else {
n -= 45000
}
d.Decode(n)
}
}()
}
}

View File

@@ -0,0 +1,115 @@
package rtptime
import (
"sync"
"time"
"github.com/pion/rtp"
)
var timeNow = time.Now
// avoid an int64 overflow and preserve resolution by splitting division into two parts:
// first add the integer part, then the decimal part.
func multiplyAndDivide(v, m, d time.Duration) time.Duration {
secs := v / d
dec := v % d
return (secs*m + dec*m/d)
}
type globalDecoderTrackData struct {
startPTS time.Duration
clockRate time.Duration
overall time.Duration
prev uint32
}
func newGlobalDecoderTrackData(
startPTS time.Duration,
clockRate int,
startTimestamp uint32,
) *globalDecoderTrackData {
return &globalDecoderTrackData{
startPTS: startPTS,
clockRate: time.Duration(clockRate),
prev: startTimestamp,
}
}
func (d *globalDecoderTrackData) decode(ts uint32) time.Duration {
diff := int32(ts - d.prev)
d.prev = ts
d.overall += time.Duration(diff)
return d.startPTS + multiplyAndDivide(d.overall, time.Second, d.clockRate)
}
// GlobalDecoderTrack is a track (RTSP format or WebRTC track) of a GlobalDecoder.
type GlobalDecoderTrack interface {
ClockRate() int
PTSEqualsDTS(*rtp.Packet) bool
}
// GlobalDecoder is a RTP timestamp decoder.
type GlobalDecoder struct {
mutex sync.Mutex
leadingTrack GlobalDecoderTrack
startNTP time.Time
startPTS time.Duration
tracks map[GlobalDecoderTrack]*globalDecoderTrackData
}
// NewGlobalDecoder allocates a GlobalDecoder.
func NewGlobalDecoder() *GlobalDecoder {
return &GlobalDecoder{
tracks: make(map[GlobalDecoderTrack]*globalDecoderTrackData),
}
}
// Decode decodes a timestamp.
func (d *GlobalDecoder) Decode(
track GlobalDecoderTrack,
pkt *rtp.Packet,
) (time.Duration, bool) {
d.mutex.Lock()
defer d.mutex.Unlock()
df, ok := d.tracks[track]
// track never seen before
if !ok {
if !track.PTSEqualsDTS(pkt) {
return 0, false
}
now := timeNow()
if d.leadingTrack == nil {
d.leadingTrack = track
d.startNTP = now
d.startPTS = 0
}
df = newGlobalDecoderTrackData(
d.startPTS+now.Sub(d.startNTP),
track.ClockRate(),
pkt.Timestamp)
d.tracks[track] = df
return df.startPTS, true
}
// update startNTP / startPTS
if d.leadingTrack == track && track.PTSEqualsDTS(pkt) {
pts := df.decode(pkt.Timestamp)
now := timeNow()
d.startNTP = now
d.startPTS = pts
return pts, true
}
return df.decode(pkt.Timestamp), true
}

View File

@@ -0,0 +1,140 @@
package rtptime
import (
"testing"
"time"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
func TestDecoderNegativeDiff(t *testing.T) {
i := uint32(0)
d := newGlobalDecoderTrackData(0, 90000, i)
i += 90000 * 2
pts := d.decode(i)
require.Equal(t, 2*time.Second, pts)
i -= 90000 * 1
pts = d.decode(i)
require.Equal(t, 1*time.Second, pts)
i += 90000 * 2
pts = d.decode(i)
require.Equal(t, 3*time.Second, pts)
}
func TestDecoderOverflow(t *testing.T) {
secs := time.Duration(0)
i := uint32(0xFFFFFFFF - 90000 + 1)
d := newGlobalDecoderTrackData(0, 90000, i)
const stride = 1500
lim := uint32(uint64(0xFFFFFFFF + 1 - (stride * 90000)))
for n := 0; n < 100; n++ {
// overflow
i += 90000 * stride
secs += stride
pts := d.decode(i)
require.Equal(t, secs*time.Second, pts)
// reach 2^32 slowly
secs += stride
i += 90000 * stride
for ; i < lim; i += 90000 * stride {
pts = d.decode(i)
require.Equal(t, secs*time.Second, pts)
secs += stride
}
}
}
func TestDecoderOverflowAndBack(t *testing.T) {
d := newGlobalDecoderTrackData(0, 90000, 0xFFFFFFFF-90000+1)
pts := d.decode(90000)
require.Equal(t, 2*time.Second, pts)
pts = d.decode(0xFFFFFFFF - 90000 + 1)
require.Equal(t, time.Duration(0), pts)
pts = d.decode(0xFFFFFFFF - 90000 + 1 - 90000)
require.Equal(t, -1*time.Second, pts)
pts = d.decode(0xFFFFFFFF - 90000 + 1)
require.Equal(t, time.Duration(0), pts)
pts = d.decode(90000)
require.Equal(t, 2*time.Second, pts)
}
func BenchmarkDecoder(b *testing.B) {
for i := 0; i < b.N; i++ {
func() {
n := uint32(0)
d := newGlobalDecoderTrackData(0, 90000, n)
for j := 0; j < 200; j++ {
if (j % 2) == 0 {
n += 90000
} else {
n -= 45000
}
d.decode(n)
}
}()
}
}
type dummyTrack struct {
clockRate int
ptsEqualsDTS bool
}
func (t *dummyTrack) ClockRate() int {
return t.clockRate
}
func (t *dummyTrack) PTSEqualsDTS(*rtp.Packet) bool {
return t.ptsEqualsDTS
}
func TestGlobalDecoder(t *testing.T) {
g := NewGlobalDecoder()
t1 := &dummyTrack{clockRate: 90000}
t2 := &dummyTrack{clockRate: 48000, ptsEqualsDTS: true}
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
}
_, ok := g.Decode(t1, &rtp.Packet{})
require.Equal(t, false, ok)
t1.ptsEqualsDTS = true
pts, ok := g.Decode(t1, &rtp.Packet{Header: rtp.Header{Timestamp: 22500}})
require.Equal(t, true, ok)
require.Equal(t, time.Duration(0), pts)
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
}
pts, ok = g.Decode(t1, &rtp.Packet{Header: rtp.Header{Timestamp: 22500 + 90000}})
require.Equal(t, true, ok)
require.Equal(t, 1*time.Second, pts)
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 25, 0, time.UTC)
}
pts, ok = g.Decode(t2, &rtp.Packet{Header: rtp.Header{Timestamp: 33100}})
require.Equal(t, true, ok)
require.Equal(t, 5*time.Second, pts)
pts, ok = g.Decode(t2, &rtp.Packet{Header: rtp.Header{Timestamp: 33100 + 48000}})
require.Equal(t, true, ok)
require.Equal(t, 6*time.Second, pts)
}

2
pkg/rtptime/rtptime.go Normal file
View File

@@ -0,0 +1,2 @@
// Package rtptime contains a time decoder and encoder.
package rtptime

View File

@@ -18,6 +18,7 @@ import (
"github.com/bluenviron/gortsplib/v4/pkg/headers"
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
"github.com/bluenviron/gortsplib/v4/pkg/media"
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
"github.com/bluenviron/gortsplib/v4/pkg/sdp"
"github.com/bluenviron/gortsplib/v4/pkg/url"
)
@@ -156,6 +157,7 @@ type ServerSession struct {
udpLastPacketTime *int64 // publish
udpCheckStreamTimer *time.Timer
writer asyncProcessor
timeDecoder *rtptime.GlobalDecoder
// in
chHandleRequest chan sessionRequestReq
@@ -876,6 +878,8 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
v := ss.s.timeNow().Unix()
ss.udpLastPacketTime = &v
ss.timeDecoder = rtptime.NewGlobalDecoder()
for _, sm := range ss.setuppedMedias {
sm.start()
}
@@ -968,6 +972,8 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
v := ss.s.timeNow().Unix()
ss.udpLastPacketTime = &v
ss.timeDecoder = rtptime.NewGlobalDecoder()
for _, sm := range ss.setuppedMedias {
sm.start()
}
@@ -1020,6 +1026,8 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
sm.stop()
}
ss.timeDecoder = nil
switch ss.state {
case ServerSessionStatePlay:
ss.state = ServerSessionStatePrePlay
@@ -1186,6 +1194,12 @@ func (ss *ServerSession) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) err
return nil
}
// PacketPTS returns the PTS of an incoming RTP packet.
// It is computed by decoding the packet timestamp and sychronizing it with other tracks.
func (ss *ServerSession) PacketPTS(forma format.Format, pkt *rtp.Packet) (time.Duration, bool) {
return ss.timeDecoder.Decode(forma, pkt)
}
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
// The NTP timestamp is computed from sender reports.
func (ss *ServerSession) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {