mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
decode RTP time globally
This commit is contained in:
47
client.go
47
client.go
@@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/media"
|
"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/sdp"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/url"
|
"github.com/bluenviron/gortsplib/v4/pkg/url"
|
||||||
)
|
)
|
||||||
@@ -316,6 +317,7 @@ type Client struct {
|
|||||||
writer asyncProcessor
|
writer asyncProcessor
|
||||||
reader *clientReader
|
reader *clientReader
|
||||||
connCloser *clientConnCloser
|
connCloser *clientConnCloser
|
||||||
|
timeDecoder *rtptime.GlobalDecoder
|
||||||
|
|
||||||
// in
|
// in
|
||||||
options chan optionsReq
|
options chan optionsReq
|
||||||
@@ -671,6 +673,23 @@ func (c *Client) playRecordStart() {
|
|||||||
c.connCloser.close()
|
c.connCloser.close()
|
||||||
c.connCloser = nil
|
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 {
|
if c.state == clientStatePlay {
|
||||||
c.keepaliveTimer = time.NewTimer(c.keepalivePeriod)
|
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)
|
c.reader = newClientReader(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,16 +718,17 @@ func (c *Client) playRecordStop(isClosing bool) {
|
|||||||
c.reader = nil
|
c.reader = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop timers
|
|
||||||
c.checkTimeoutTimer = emptyTimer()
|
c.checkTimeoutTimer = emptyTimer()
|
||||||
c.keepaliveTimer = emptyTimer()
|
c.keepaliveTimer = emptyTimer()
|
||||||
|
|
||||||
c.writer.stop()
|
|
||||||
|
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.medias {
|
||||||
cm.stop()
|
cm.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.writer.stop()
|
||||||
|
|
||||||
|
c.timeDecoder = nil
|
||||||
|
|
||||||
if !isClosing {
|
if !isClosing {
|
||||||
c.connCloser = newClientConnCloser(c.ctx, c.nconn)
|
c.connCloser = newClientConnCloser(c.ctx, c.nconn)
|
||||||
}
|
}
|
||||||
@@ -1700,6 +1705,12 @@ func (c *Client) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error {
|
|||||||
return nil
|
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.
|
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
|
||||||
// The NTP timestamp is computed from sender reports.
|
// The NTP timestamp is computed from sender reports.
|
||||||
func (c *Client) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
|
func (c *Client) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
|
||||||
|
@@ -24,7 +24,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer pc.Close()
|
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" +
|
"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")
|
" ! avenc_aac bitrate=128000 ! rtpmp4gpay ! udpsink host=127.0.0.1 port=9000")
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.Println("stream connected")
|
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{
|
medi := &media.Media{
|
||||||
Type: media.TypeAudio,
|
Type: media.TypeAudio,
|
||||||
Formats: []format.Format{&format.MPEG4Audio{
|
Formats: []format.Format{&format.MPEG4Audio{
|
||||||
|
@@ -57,15 +57,20 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract G711 frames from RTP packets
|
// extract G711 frames from RTP packets
|
||||||
op, _, err := rtpDec.Decode(pkt)
|
op, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// print
|
// 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
|
// start playing
|
||||||
|
@@ -57,15 +57,20 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract G722 frames from RTP packets
|
// extract G722 frames from RTP packets
|
||||||
op, _, err := rtpDec.Decode(pkt)
|
op, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// print
|
// 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
|
// start playing
|
||||||
|
@@ -97,11 +97,12 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// called when a RTP packet arrives
|
|
||||||
saveCount := 0
|
saveCount := 0
|
||||||
|
|
||||||
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
// extract access units from RTP packets
|
// extract access units from RTP packets
|
||||||
au, _, err := rtpDec.Decode(pkt)
|
au, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
|
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
|
@@ -64,8 +64,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract access unit from RTP packets
|
// extract access unit from RTP packets
|
||||||
au, pts, err := rtpDec.Decode(pkt)
|
au, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
|
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
@@ -74,7 +79,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encode the access unit into MPEG-TS
|
// 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
|
// start playing
|
||||||
|
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -25,7 +24,6 @@ type mpegtsMuxer struct {
|
|||||||
track *mpegts.Track
|
track *mpegts.Track
|
||||||
dtsExtractor *h264.DTSExtractor
|
dtsExtractor *h264.DTSExtractor
|
||||||
firstIDRReceived bool
|
firstIDRReceived bool
|
||||||
startDTS time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMPEGTSMuxer allocates a mpegtsMuxer.
|
// newMPEGTSMuxer allocates a mpegtsMuxer.
|
||||||
@@ -94,7 +92,7 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
|
|||||||
|
|
||||||
au = filteredNALUs
|
au = filteredNALUs
|
||||||
|
|
||||||
if !nonIDRPresent && !idrPresent {
|
if len(au) <= 1 || (!nonIDRPresent && !idrPresent) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,28 +117,14 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
e.startDTS = dts
|
|
||||||
dts = 0
|
|
||||||
pts -= e.startDTS
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
dts, err = e.dtsExtractor.Extract(au, pts)
|
dts, err = e.dtsExtractor.Extract(au, pts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dts -= e.startDTS
|
|
||||||
pts -= e.startDTS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encode into MPEG-TS
|
// encode into MPEG-TS
|
||||||
err := e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
|
return e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("wrote TS packet")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@@ -76,8 +76,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract access units from RTP packets
|
// extract access units from RTP packets
|
||||||
au, pts, err := rtpDec.Decode(pkt)
|
au, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
|
if err != rtph264.ErrNonStartingPacketAndNoPrevious && err != rtph264.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
@@ -97,7 +102,7 @@ func main() {
|
|||||||
continue
|
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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -58,8 +58,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract access units from RTP packets
|
// extract access units from RTP packets
|
||||||
au, pts, err := rtpDec.Decode(pkt)
|
au, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtph265.ErrNonStartingPacketAndNoPrevious && err != rtph265.ErrMorePacketsNeeded {
|
if err != rtph265.ErrNonStartingPacketAndNoPrevious && err != rtph265.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
|
@@ -57,15 +57,20 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract LPCM samples from RTP packets
|
// extract LPCM samples from RTP packets
|
||||||
op, _, err := rtpDec.Decode(pkt)
|
op, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// print
|
// 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
|
// start playing
|
||||||
|
@@ -61,8 +61,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract JPEG images from RTP packets
|
// extract JPEG images from RTP packets
|
||||||
enc, pts, err := rtpDec.Decode(pkt)
|
enc, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtpmjpeg.ErrNonStartingPacketAndNoPrevious && err != rtpmjpeg.ErrMorePacketsNeeded {
|
if err != rtpmjpeg.ErrNonStartingPacketAndNoPrevious && err != rtpmjpeg.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
@@ -76,7 +81,7 @@ func main() {
|
|||||||
panic(err)
|
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
|
// start playing
|
||||||
|
@@ -63,17 +63,26 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract access units from RTP packets
|
// extract access units from RTP packets
|
||||||
aus, pts, err := rtpDec.Decode(pkt)
|
aus, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, au := range aus {
|
// encode access units into MPEG-TS
|
||||||
// encode the access unit into MPEG-TS
|
err = mpegtsMuxer.encode(aus, pts)
|
||||||
mpegtsMuxer.encode(au, pts)
|
if err != nil {
|
||||||
|
log.Printf("ERR: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("saved TS packet")
|
||||||
})
|
})
|
||||||
|
|
||||||
// start playing
|
// start playing
|
||||||
|
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -54,14 +53,7 @@ func (e *mpegtsMuxer) close() {
|
|||||||
e.f.Close()
|
e.f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// encode encodes a MPEG4-audio access unit into MPEG-TS.
|
// encode encodes MPEG-4 audio access units into MPEG-TS.
|
||||||
func (e *mpegtsMuxer) encode(au []byte, pts time.Duration) error {
|
func (e *mpegtsMuxer) encode(aus [][]byte, pts time.Duration) error {
|
||||||
// encode into MPEG-TS
|
return e.w.WriteMPEG4Audio(e.track, durationGoToMPEGTS(pts), aus)
|
||||||
err := e.w.WriteMPEG4Audio(e.track, durationGoToMPEGTS(pts), [][]byte{au})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("wrote TS packet")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@@ -57,8 +57,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract access units from RTP packets
|
// extract access units from RTP packets
|
||||||
aus, _, err := rtpDec.Decode(pkt)
|
aus, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
return
|
return
|
||||||
@@ -66,7 +71,7 @@ func main() {
|
|||||||
|
|
||||||
// print AUs
|
// print AUs
|
||||||
for _, au := range 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))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -57,15 +57,20 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract Opus packets from RTP packets
|
// extract Opus packets from RTP packets
|
||||||
op, _, err := rtpDec.Decode(pkt)
|
op, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// print
|
// 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
|
// start playing
|
||||||
|
@@ -58,8 +58,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract VP8 frames from RTP packets
|
// extract VP8 frames from RTP packets
|
||||||
vf, _, err := rtpDec.Decode(pkt)
|
vf, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtpvp8.ErrNonStartingPacketAndNoPrevious && err != rtpvp8.ErrMorePacketsNeeded {
|
if err != rtpvp8.ErrNonStartingPacketAndNoPrevious && err != rtpvp8.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
@@ -67,7 +72,7 @@ func main() {
|
|||||||
return
|
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
|
// start playing
|
||||||
|
@@ -58,8 +58,13 @@ func main() {
|
|||||||
|
|
||||||
// called when a RTP packet arrives
|
// called when a RTP packet arrives
|
||||||
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
|
||||||
|
pts, ok := c.PacketPTS(forma, pkt)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract VP9 frames from RTP packets
|
// extract VP9 frames from RTP packets
|
||||||
vf, _, err := rtpDec.Decode(pkt)
|
vf, err := rtpDec.Decode(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != rtpvp9.ErrNonStartingPacketAndNoPrevious && err != rtpvp9.ErrMorePacketsNeeded {
|
if err != rtpvp9.ErrNonStartingPacketAndNoPrevious && err != rtpvp9.ErrMorePacketsNeeded {
|
||||||
log.Printf("ERR: %v", err)
|
log.Printf("ERR: %v", err)
|
||||||
@@ -67,7 +72,7 @@ func main() {
|
|||||||
return
|
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
|
// start playing
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
// 3. save the content of the H264 media into a file in MPEG-TS format
|
// 3. save the content of the H264 media into a file in MPEG-TS format
|
||||||
|
|
||||||
type serverHandler struct {
|
type serverHandler struct {
|
||||||
|
s *gortsplib.Server
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
publisher *gortsplib.ServerSession
|
publisher *gortsplib.ServerSession
|
||||||
media *media.Media
|
media *media.Media
|
||||||
@@ -115,13 +116,18 @@ func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*bas
|
|||||||
|
|
||||||
// called when receiving a RTP packet
|
// called when receiving a RTP packet
|
||||||
ctx.Session.OnPacketRTP(sh.media, sh.format, func(pkt *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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// encode H264 NALUs into MPEG-TS
|
// encode H264 access unit into MPEG-TS
|
||||||
sh.mpegtsMuxer.encode(nalus, pts)
|
sh.mpegtsMuxer.encode(au, pts)
|
||||||
})
|
})
|
||||||
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
@@ -131,8 +137,9 @@ func (sh *serverHandler) OnRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*bas
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// configure the server
|
// configure the server
|
||||||
s := &gortsplib.Server{
|
h := &serverHandler{}
|
||||||
Handler: &serverHandler{},
|
h.s = &gortsplib.Server{
|
||||||
|
Handler: h,
|
||||||
RTSPAddress: ":8554",
|
RTSPAddress: ":8554",
|
||||||
UDPRTPAddress: ":8000",
|
UDPRTPAddress: ":8000",
|
||||||
UDPRTCPAddress: ":8001",
|
UDPRTCPAddress: ":8001",
|
||||||
@@ -143,5 +150,5 @@ func main() {
|
|||||||
|
|
||||||
// start server and wait until a fatal error
|
// start server and wait until a fatal error
|
||||||
log.Printf("server is ready")
|
log.Printf("server is ready")
|
||||||
panic(s.StartAndWait())
|
panic(h.s.StartAndWait())
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -25,7 +24,6 @@ type mpegtsMuxer struct {
|
|||||||
track *mpegts.Track
|
track *mpegts.Track
|
||||||
dtsExtractor *h264.DTSExtractor
|
dtsExtractor *h264.DTSExtractor
|
||||||
firstIDRReceived bool
|
firstIDRReceived bool
|
||||||
startDTS time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMPEGTSMuxer allocates a mpegtsMuxer.
|
// newMPEGTSMuxer allocates a mpegtsMuxer.
|
||||||
@@ -58,7 +56,7 @@ func (e *mpegtsMuxer) close() {
|
|||||||
e.f.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 {
|
func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
|
||||||
// prepend an AUD. This is required by some players
|
// prepend an AUD. This is required by some players
|
||||||
filteredNALUs := [][]byte{
|
filteredNALUs := [][]byte{
|
||||||
@@ -94,7 +92,7 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
|
|||||||
|
|
||||||
au = filteredNALUs
|
au = filteredNALUs
|
||||||
|
|
||||||
if !nonIDRPresent && !idrPresent {
|
if len(au) <= 1 || (!nonIDRPresent && !idrPresent) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,28 +117,14 @@ func (e *mpegtsMuxer) encode(au [][]byte, pts time.Duration) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
e.startDTS = dts
|
|
||||||
dts = 0
|
|
||||||
pts -= e.startDTS
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
dts, err = e.dtsExtractor.Extract(au, pts)
|
dts, err = e.dtsExtractor.Extract(au, pts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
dts -= e.startDTS
|
|
||||||
pts -= e.startDTS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encode into MPEG-TS
|
// encode into MPEG-TS
|
||||||
err := e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
|
return e.w.WriteH26x(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("wrote TS packet")
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@@ -22,14 +22,14 @@ func TestAV1DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -56,9 +56,7 @@ func (f *G711) PTSEqualsDTS(*rtp.Packet) bool {
|
|||||||
|
|
||||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||||
func (f *G711) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
func (f *G711) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
||||||
d := &rtpsimpleaudio.Decoder{
|
d := &rtpsimpleaudio.Decoder{}
|
||||||
SampleRate: 8000,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,7 +70,6 @@ func (f *G711) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
|||||||
func (f *G711) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
func (f *G711) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
||||||
e := &rtpsimpleaudio.Encoder{
|
e := &rtpsimpleaudio.Encoder{
|
||||||
PayloadType: f.PayloadType(),
|
PayloadType: f.PayloadType(),
|
||||||
SampleRate: 8000,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
|
@@ -26,14 +26,14 @@ func TestG711DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkt.PayloadType)
|
require.Equal(t, format.PayloadType(), pkt.PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkt)
|
byts, err := dec.Decode(pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -46,9 +46,7 @@ func (f *G722) PTSEqualsDTS(*rtp.Packet) bool {
|
|||||||
|
|
||||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||||
func (f *G722) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
func (f *G722) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
||||||
d := &rtpsimpleaudio.Decoder{
|
d := &rtpsimpleaudio.Decoder{}
|
||||||
SampleRate: 8000,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,7 +60,6 @@ func (f *G722) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
|||||||
func (f *G722) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
func (f *G722) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
||||||
e := &rtpsimpleaudio.Encoder{
|
e := &rtpsimpleaudio.Encoder{
|
||||||
PayloadType: 9,
|
PayloadType: 9,
|
||||||
SampleRate: 8000,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
|
@@ -20,14 +20,14 @@ func TestG722DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkt.PayloadType)
|
require.Equal(t, format.PayloadType(), pkt.PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkt)
|
byts, err := dec.Decode(pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -50,14 +50,14 @@ func TestH264DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -53,14 +53,14 @@ func TestH265DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -100,7 +100,6 @@ func (f *LPCM) PTSEqualsDTS(*rtp.Packet) bool {
|
|||||||
func (f *LPCM) CreateDecoder() (*rtplpcm.Decoder, error) {
|
func (f *LPCM) CreateDecoder() (*rtplpcm.Decoder, error) {
|
||||||
d := &rtplpcm.Decoder{
|
d := &rtplpcm.Decoder{
|
||||||
BitDepth: f.BitDepth,
|
BitDepth: f.BitDepth,
|
||||||
SampleRate: f.SampleRate,
|
|
||||||
ChannelCount: f.ChannelCount,
|
ChannelCount: f.ChannelCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +116,6 @@ func (f *LPCM) CreateEncoder() (*rtplpcm.Encoder, error) {
|
|||||||
e := &rtplpcm.Encoder{
|
e := &rtplpcm.Encoder{
|
||||||
PayloadType: f.PayloadTyp,
|
PayloadType: f.PayloadTyp,
|
||||||
BitDepth: f.BitDepth,
|
BitDepth: f.BitDepth,
|
||||||
SampleRate: f.SampleRate,
|
|
||||||
ChannelCount: f.ChannelCount,
|
ChannelCount: f.ChannelCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,14 +30,14 @@ func TestLPCMDecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -281,7 +281,7 @@ func TestMJPEGDecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := enc.Encode(b, 0)
|
pkts, err := enc.Encode(b)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ func TestMJPEGDecEncoder(t *testing.T) {
|
|||||||
|
|
||||||
var byts []byte
|
var byts []byte
|
||||||
for _, pkt := range pkts {
|
for _, pkt := range pkts {
|
||||||
byts, _, _ = dec.Decode(pkt)
|
byts, _ = dec.Decode(pkt)
|
||||||
}
|
}
|
||||||
require.Equal(t, b, byts)
|
require.Equal(t, b, byts)
|
||||||
}
|
}
|
||||||
|
@@ -33,14 +33,14 @@ func TestMPEG1AudioDecEncoder(t *testing.T) {
|
|||||||
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
0x8d, 0x46, 0xfc, 0x8c, 0x73, 0xb9, 0x34, 0x3e,
|
||||||
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
0xb5, 0x03, 0x39, 0xc0, 0x04, 0x01, 0x98, 0x44,
|
||||||
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
0x38, 0xe0, 0x98, 0x10, 0x9b, 0xa8, 0x0f, 0xa8,
|
||||||
}}, 0)
|
}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [][]byte{{
|
require.Equal(t, [][]byte{{
|
||||||
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
0xff, 0xfb, 0x14, 0x64, 0x00, 0x0f, 0xf0, 0x00,
|
||||||
|
@@ -172,7 +172,6 @@ func (f *MPEG4AudioGeneric) PTSEqualsDTS(*rtp.Packet) bool {
|
|||||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||||
func (f *MPEG4AudioGeneric) CreateDecoder() (*rtpmpeg4audio.Decoder, error) {
|
func (f *MPEG4AudioGeneric) CreateDecoder() (*rtpmpeg4audio.Decoder, error) {
|
||||||
d := &rtpmpeg4audio.Decoder{
|
d := &rtpmpeg4audio.Decoder{
|
||||||
SampleRate: f.Config.SampleRate,
|
|
||||||
SizeLength: f.SizeLength,
|
SizeLength: f.SizeLength,
|
||||||
IndexLength: f.IndexLength,
|
IndexLength: f.IndexLength,
|
||||||
IndexDeltaLength: f.IndexDeltaLength,
|
IndexDeltaLength: f.IndexDeltaLength,
|
||||||
@@ -190,7 +189,6 @@ func (f *MPEG4AudioGeneric) CreateDecoder() (*rtpmpeg4audio.Decoder, error) {
|
|||||||
func (f *MPEG4AudioGeneric) CreateEncoder() (*rtpmpeg4audio.Encoder, error) {
|
func (f *MPEG4AudioGeneric) CreateEncoder() (*rtpmpeg4audio.Encoder, error) {
|
||||||
e := &rtpmpeg4audio.Encoder{
|
e := &rtpmpeg4audio.Encoder{
|
||||||
PayloadType: f.PayloadTyp,
|
PayloadType: f.PayloadTyp,
|
||||||
SampleRate: f.Config.SampleRate,
|
|
||||||
SizeLength: f.SizeLength,
|
SizeLength: f.SizeLength,
|
||||||
IndexLength: f.IndexLength,
|
IndexLength: f.IndexLength,
|
||||||
IndexDeltaLength: f.IndexDeltaLength,
|
IndexDeltaLength: f.IndexDeltaLength,
|
||||||
|
@@ -42,14 +42,14 @@ func TestMPEG4AudioGenericDecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
require.Equal(t, [][]byte{{0x01, 0x02, 0x03, 0x04}}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -159,9 +159,7 @@ func (f *MPEG4AudioLATM) PTSEqualsDTS(*rtp.Packet) bool {
|
|||||||
|
|
||||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||||
func (f *MPEG4AudioLATM) CreateDecoder() (*rtpmpeg4audiolatm.Decoder, error) {
|
func (f *MPEG4AudioLATM) CreateDecoder() (*rtpmpeg4audiolatm.Decoder, error) {
|
||||||
d := &rtpmpeg4audiolatm.Decoder{
|
d := &rtpmpeg4audiolatm.Decoder{}
|
||||||
Config: f.Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -175,7 +173,6 @@ func (f *MPEG4AudioLATM) CreateDecoder() (*rtpmpeg4audiolatm.Decoder, error) {
|
|||||||
func (f *MPEG4AudioLATM) CreateEncoder() (*rtpmpeg4audiolatm.Encoder, error) {
|
func (f *MPEG4AudioLATM) CreateEncoder() (*rtpmpeg4audiolatm.Encoder, error) {
|
||||||
e := &rtpmpeg4audiolatm.Encoder{
|
e := &rtpmpeg4audiolatm.Encoder{
|
||||||
PayloadType: f.PayloadTyp,
|
PayloadType: f.PayloadTyp,
|
||||||
Config: f.Config,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
|
@@ -51,14 +51,14 @@ func TestMPEG4AudioLATMDecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -26,14 +26,14 @@ func TestMPEG4VideoESDecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -88,9 +88,7 @@ func (f *Opus) PTSEqualsDTS(*rtp.Packet) bool {
|
|||||||
|
|
||||||
// CreateDecoder creates a decoder able to decode the content of the format.
|
// CreateDecoder creates a decoder able to decode the content of the format.
|
||||||
func (f *Opus) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
func (f *Opus) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
||||||
d := &rtpsimpleaudio.Decoder{
|
d := &rtpsimpleaudio.Decoder{}
|
||||||
SampleRate: 48000,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -104,7 +102,6 @@ func (f *Opus) CreateDecoder() (*rtpsimpleaudio.Decoder, error) {
|
|||||||
func (f *Opus) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
func (f *Opus) CreateEncoder() (*rtpsimpleaudio.Encoder, error) {
|
||||||
e := &rtpsimpleaudio.Encoder{
|
e := &rtpsimpleaudio.Encoder{
|
||||||
PayloadType: f.PayloadTyp,
|
PayloadType: f.PayloadTyp,
|
||||||
SampleRate: 48000,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
|
@@ -23,14 +23,14 @@ func TestOpusDecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkt.PayloadType)
|
require.Equal(t, format.PayloadType(), pkt.PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkt)
|
byts, err := dec.Decode(pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,10 @@ package rtpav1
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
|
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/pion/rtp/codecs"
|
"github.com/pion/rtp/codecs"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/AV1 decoder.
|
||||||
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
|
// Specification: https://aomediacodec.github.io/av1-rtp-spec/
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -47,39 +43,38 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(90000)
|
|
||||||
return nil
|
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
|
var av1header codecs.AV1Packet
|
||||||
_, err := av1header.Unmarshal(pkt.Payload)
|
_, err := av1header.Unmarshal(pkt.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.fragmentsSize = 0
|
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 {
|
for _, el := range av1header.OBUElements {
|
||||||
if len(el) == 0 {
|
if len(el) == 0 {
|
||||||
return nil, 0, fmt.Errorf("invalid OBU fragment")
|
return nil, fmt.Errorf("invalid OBU fragment")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if av1header.Z {
|
if av1header.Z {
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
if !d.firstPacketReceived {
|
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])
|
d.fragmentsSize += len(av1header.OBUElements[0])
|
||||||
if d.fragmentsSize > av1.MaxTemporalUnitSize {
|
if d.fragmentsSize > av1.MaxTemporalUnitSize {
|
||||||
d.fragments = d.fragments[:0]
|
d.fragments = d.fragments[:0]
|
||||||
d.fragmentsSize = 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])
|
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 {
|
if d.fragmentsSize > av1.MaxTemporalUnitSize {
|
||||||
d.fragments = d.fragments[:0]
|
d.fragments = d.fragments[:0]
|
||||||
d.fragmentsSize = 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])
|
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 {
|
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.
|
// Decode decodes a temporal unit from a RTP packet.
|
||||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||||
obus, pts, err := d.decodeOBUs(pkt)
|
obus, err := d.decodeOBUs(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l := len(obus)
|
l := len(obus)
|
||||||
|
|
||||||
@@ -137,7 +132,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBuffer = nil
|
d.frameBuffer = nil
|
||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 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)
|
av1.MaxOBUsPerTemporalUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +146,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBuffer = nil
|
d.frameBuffer = nil
|
||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 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)
|
d.frameBufferSize+addSize, av1.MaxOBUsPerTemporalUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +155,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBufferSize += addSize
|
d.frameBufferSize += addSize
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := d.frameBuffer
|
ret := d.frameBuffer
|
||||||
@@ -170,5 +165,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 0
|
d.frameBufferSize = 0
|
||||||
|
|
||||||
return ret, pts, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ func TestDecode(t *testing.T) {
|
|||||||
var obus [][]byte
|
var obus [][]byte
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
addOBUs, _, err := d.Decode(pkt)
|
addOBUs, err := d.Decode(pkt)
|
||||||
if err == ErrMorePacketsNeeded {
|
if err == ErrMorePacketsNeeded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ func TestDecoderErrorLimit(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i := 0; i <= av1.MaxOBUsPerTemporalUnit; i++ {
|
for i := 0; i <= av1.MaxOBUsPerTemporalUnit; i++ {
|
||||||
_, _, err = d.Decode(&rtp.Packet{
|
_, err = d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: false,
|
Marker: false,
|
||||||
|
@@ -2,12 +2,9 @@ package rtpav1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
|
"github.com/bluenviron/mediacommon/pkg/codecs/av1"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -38,16 +35,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -67,30 +59,21 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes OBUs into RTP packets.
|
// 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)
|
isKeyFrame, err := av1.ContainsKeyFrame(obus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ts := e.timeEncoder.Encode(pts)
|
|
||||||
var curPacket *rtp.Packet
|
var curPacket *rtp.Packet
|
||||||
var packets []*rtp.Packet
|
var packets []*rtp.Packet
|
||||||
curPayloadLen := 0
|
curPayloadLen := 0
|
||||||
@@ -101,7 +84,6 @@ func (e *Encoder) Encode(obus [][]byte, pts time.Duration) ([]*rtp.Packet, error
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: ts,
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
},
|
},
|
||||||
Payload: []byte{0},
|
Payload: []byte{0},
|
||||||
|
@@ -244,7 +244,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -265,7 +264,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -460,7 +458,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -502,7 +499,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -525,7 +521,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -720,7 +715,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -915,7 +909,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -988,12 +981,11 @@ func TestEncode(t *testing.T) {
|
|||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.obus, 0)
|
pkts, err := e.Encode(ca.obus)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -1008,5 +1000,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,7 +35,6 @@ type Decoder struct {
|
|||||||
// indicates the packetization mode.
|
// indicates the packetization mode.
|
||||||
PacketizationMode int
|
PacketizationMode int
|
||||||
|
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -54,15 +51,13 @@ func (d *Decoder) Init() error {
|
|||||||
if d.PacketizationMode >= 2 {
|
if d.PacketizationMode >= 2 {
|
||||||
return fmt.Errorf("PacketizationMode >= 2 is not supported")
|
return fmt.Errorf("PacketizationMode >= 2 is not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
|
|
||||||
return nil
|
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 {
|
if len(pkt.Payload) < 1 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
typ := h264.NALUType(pkt.Payload[0] & 0x1F)
|
||||||
@@ -71,7 +66,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
|
|||||||
switch typ {
|
switch typ {
|
||||||
case h264.NALUTypeFUA:
|
case h264.NALUTypeFUA:
|
||||||
if len(pkt.Payload) < 2 {
|
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
|
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
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
|
|
||||||
if end != 0 {
|
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
|
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.fragments = append(d.fragments, []byte{(nri << 5) | typ}, pkt.Payload[2:])
|
||||||
d.firstPacketReceived = true
|
d.firstPacketReceived = true
|
||||||
|
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
if !d.firstPacketReceived {
|
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:])
|
d.fragmentsSize += len(pkt.Payload[2:])
|
||||||
|
|
||||||
if d.fragmentsSize > h264.MaxAccessUnitSize {
|
if d.fragmentsSize > h264.MaxAccessUnitSize {
|
||||||
d.fragments = d.fragments[:0]
|
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:])
|
d.fragments = append(d.fragments, pkt.Payload[2:])
|
||||||
|
|
||||||
if end != 1 {
|
if end != 1 {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
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 {
|
for len(payload) > 0 {
|
||||||
if len(payload) < 2 {
|
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])
|
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) {
|
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])
|
nalus = append(nalus, payload[:size])
|
||||||
@@ -144,7 +139,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nalus == nil {
|
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
|
d.firstPacketReceived = true
|
||||||
@@ -153,7 +148,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
|
|||||||
h264.NALUTypeMTAP24, h264.NALUTypeFUB:
|
h264.NALUTypeMTAP24, h264.NALUTypeFUB:
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.firstPacketReceived = true
|
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:
|
default:
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
nalus, err := d.removeAnnexB(nalus)
|
||||||
if err != nil {
|
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.
|
// Decode decodes an access unit from a RTP packet.
|
||||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||||
nalus, pts, err := d.decodeNALUs(pkt)
|
nalus, err := d.decodeNALUs(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l := len(nalus)
|
l := len(nalus)
|
||||||
|
|
||||||
@@ -181,7 +176,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBuffer = nil
|
d.frameBuffer = nil
|
||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 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)
|
h264.MaxNALUsPerAccessUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +190,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBuffer = nil
|
d.frameBuffer = nil
|
||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 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)
|
d.frameBufferSize+addSize, h264.MaxAccessUnitSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +199,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBufferSize += addSize
|
d.frameBufferSize += addSize
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := d.frameBuffer
|
ret := d.frameBuffer
|
||||||
@@ -214,7 +209,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 0
|
d.frameBufferSize = 0
|
||||||
|
|
||||||
return ret, pts, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// some cameras / servers wrap NALUs into Annex-B
|
// some cameras / servers wrap NALUs into Annex-B
|
||||||
|
@@ -21,7 +21,7 @@ func TestDecode(t *testing.T) {
|
|||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
clone := pkt.Clone()
|
clone := pkt.Clone()
|
||||||
|
|
||||||
addNALUs, _, err := d.Decode(pkt)
|
addNALUs, err := d.Decode(pkt)
|
||||||
|
|
||||||
// test input integrity
|
// test input integrity
|
||||||
require.Equal(t, clone, pkt)
|
require.Equal(t, clone, pkt)
|
||||||
@@ -44,7 +44,7 @@ func TestDecodeCorruptedFragment(t *testing.T) {
|
|||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, _, err = d.Decode(&rtp.Packet{
|
_, err = d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
@@ -63,7 +63,7 @@ func TestDecodeCorruptedFragment(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.Equal(t, ErrMorePacketsNeeded, err)
|
require.Equal(t, ErrMorePacketsNeeded, err)
|
||||||
|
|
||||||
nalus, _, err := d.Decode(&rtp.Packet{
|
nalus, err := d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
@@ -89,7 +89,6 @@ func TestDecodeSTAPAWithPadding(t *testing.T) {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
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.NoError(t, err)
|
||||||
require.Equal(t, [][]byte{
|
require.Equal(t, [][]byte{
|
||||||
{0xaa, 0xbb},
|
{0xaa, 0xbb},
|
||||||
@@ -113,7 +112,7 @@ func TestDecodeAnnexB(t *testing.T) {
|
|||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
nalus, _, err := d.Decode(&rtp.Packet{
|
nalus, err := d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
@@ -132,7 +131,7 @@ func TestDecodeAnnexB(t *testing.T) {
|
|||||||
}, nalus)
|
}, nalus)
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
nalus, _, err := d.Decode(&rtp.Packet{
|
nalus, err := d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
@@ -161,7 +160,7 @@ func TestDecodeAccessUnit(t *testing.T) {
|
|||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
nalus, _, err := d.Decode(&rtp.Packet{
|
nalus, err := d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: false,
|
Marker: false,
|
||||||
@@ -175,7 +174,7 @@ func TestDecodeAccessUnit(t *testing.T) {
|
|||||||
require.Equal(t, ErrMorePacketsNeeded, err)
|
require.Equal(t, ErrMorePacketsNeeded, err)
|
||||||
require.Equal(t, [][]byte(nil), nalus)
|
require.Equal(t, [][]byte(nil), nalus)
|
||||||
|
|
||||||
nalus, _, err = d.Decode(&rtp.Packet{
|
nalus, err = d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
@@ -196,7 +195,7 @@ func TestDecoderErrorLimit(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i := 0; i <= h264.MaxNALUsPerAccessUnit; i++ {
|
for i := 0; i <= h264.MaxNALUsPerAccessUnit; i++ {
|
||||||
_, _, err = d.Decode(&rtp.Packet{
|
_, err = d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: false,
|
Marker: false,
|
||||||
|
@@ -3,11 +3,9 @@ package rtph264
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,10 +37,6 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
@@ -50,7 +44,6 @@ type Encoder struct {
|
|||||||
PacketizationMode int
|
PacketizationMode int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -74,24 +67,16 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes NALUs into RTP/H264 packets.
|
// 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 rets []*rtp.Packet
|
||||||
var batch [][]byte
|
var batch [][]byte
|
||||||
|
|
||||||
@@ -101,9 +86,9 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
// add to existing batch
|
// add to existing batch
|
||||||
batch = append(batch, nalu)
|
batch = append(batch, nalu)
|
||||||
} else {
|
} else {
|
||||||
// write batch
|
// write current batch
|
||||||
if batch != nil {
|
if batch != nil {
|
||||||
pkts, err := e.writeBatch(batch, pts, false)
|
pkts, err := e.writeBatch(batch, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -117,7 +102,7 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
|
|
||||||
// write final batch
|
// write final batch
|
||||||
// marker is used to indicate when all NALUs with same PTS have been sent
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -126,27 +111,26 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
return rets, nil
|
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 {
|
if len(nalus) == 1 {
|
||||||
// the NALU fits into a single RTP packet
|
// the NALU fits into a single RTP packet
|
||||||
if len(nalus[0]) < e.PayloadMaxSize {
|
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
|
// 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{
|
pkt := &rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: marker,
|
Marker: marker,
|
||||||
},
|
},
|
||||||
@@ -158,7 +142,7 @@ func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*r
|
|||||||
return []*rtp.Packet{pkt}, nil
|
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
|
// use only FU-A, not FU-B, since we always use non-interleaved mode
|
||||||
// (packetization-mode=1)
|
// (packetization-mode=1)
|
||||||
avail := e.PayloadMaxSize - 2
|
avail := e.PayloadMaxSize - 2
|
||||||
@@ -170,7 +154,6 @@ func (e *Encoder) writeFragmented(nalu []byte, pts time.Duration, marker bool) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret := make([]*rtp.Packet, packetCount)
|
ret := make([]*rtp.Packet, packetCount)
|
||||||
encPTS := e.timeEncoder.Encode(pts)
|
|
||||||
|
|
||||||
nri := (nalu[0] >> 5) & 0x03
|
nri := (nalu[0] >> 5) & 0x03
|
||||||
typ := nalu[0] & 0x1F
|
typ := nalu[0] & 0x1F
|
||||||
@@ -202,7 +185,6 @@ func (e *Encoder) writeFragmented(nalu []byte, pts time.Duration, marker bool) (
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: encPTS,
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: (i == (packetCount-1) && marker),
|
Marker: (i == (packetCount-1) && marker),
|
||||||
},
|
},
|
||||||
@@ -231,7 +213,7 @@ func (e *Encoder) lenAggregated(nalus [][]byte, addNALU []byte) int {
|
|||||||
return ret
|
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))
|
payload := make([]byte, e.lenAggregated(nalus, nil))
|
||||||
|
|
||||||
// header
|
// header
|
||||||
@@ -255,7 +237,6 @@ func (e *Encoder) writeAggregated(nalus [][]byte, pts time.Duration, marker bool
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: marker,
|
Marker: marker,
|
||||||
},
|
},
|
||||||
|
@@ -52,7 +52,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -77,7 +76,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -92,7 +90,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -108,7 +105,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -142,7 +138,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -187,7 +182,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -209,7 +203,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -236,7 +229,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -251,7 +243,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -266,7 +257,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -285,12 +275,11 @@ func TestEncode(t *testing.T) {
|
|||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.nalus, 0)
|
pkts, err := e.Encode(ca.nalus)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -305,5 +294,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,2 @@
|
|||||||
// Package rtph264 contains a RTP/H264 decoder and encoder.
|
// Package rtph264 contains a RTP/H264 decoder and encoder.
|
||||||
package rtph264
|
package rtph264
|
||||||
|
|
||||||
const (
|
|
||||||
rtpClockRate = 90000 // H264 always uses 90khz
|
|
||||||
)
|
|
||||||
|
@@ -3,11 +3,9 @@ package rtph265
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
|
"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.
|
// indicates that NALUs have an additional field that specifies the decoding order.
|
||||||
MaxDONDiff int
|
MaxDONDiff int
|
||||||
|
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -52,15 +49,13 @@ func (d *Decoder) Init() error {
|
|||||||
if d.MaxDONDiff != 0 {
|
if d.MaxDONDiff != 0 {
|
||||||
return fmt.Errorf("MaxDONDiff != 0 is not supported (yet)")
|
return fmt.Errorf("MaxDONDiff != 0 is not supported (yet)")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
|
|
||||||
return nil
|
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 {
|
if len(pkt.Payload) < 2 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
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 {
|
for len(payload) > 0 {
|
||||||
if len(payload) < 2 {
|
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])
|
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) {
|
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])
|
nalus = append(nalus, payload[:size])
|
||||||
@@ -93,7 +88,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nalus == nil {
|
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
|
d.firstPacketReceived = true
|
||||||
@@ -101,7 +96,7 @@ func (d *Decoder) decodeNALUs(pkt *rtp.Packet) ([][]byte, time.Duration, error)
|
|||||||
case h265.NALUType_FragmentationUnit:
|
case h265.NALUType_FragmentationUnit:
|
||||||
if len(pkt.Payload) < 3 {
|
if len(pkt.Payload) < 3 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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
|
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
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
|
|
||||||
if end != 0 {
|
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
|
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.fragments = append(d.fragments, []byte{byte(head >> 8), byte(head)}, pkt.Payload[3:])
|
||||||
d.firstPacketReceived = true
|
d.firstPacketReceived = true
|
||||||
|
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
if !d.firstPacketReceived {
|
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:])
|
d.fragmentsSize += len(pkt.Payload[3:])
|
||||||
if d.fragmentsSize > h265.MaxAccessUnitSize {
|
if d.fragmentsSize > h265.MaxAccessUnitSize {
|
||||||
d.fragments = d.fragments[:0]
|
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:])
|
d.fragments = append(d.fragments, pkt.Payload[3:])
|
||||||
|
|
||||||
if end != 1 {
|
if end != 1 {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
nalus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
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:
|
case h265.NALUType_PACI:
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.firstPacketReceived = true
|
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:
|
default:
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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}
|
nalus = [][]byte{pkt.Payload}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nalus, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return nalus, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes an access unit from a RTP packet.
|
// Decode decodes an access unit from a RTP packet.
|
||||||
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, error) {
|
||||||
nalus, pts, err := d.decodeNALUs(pkt)
|
nalus, err := d.decodeNALUs(pkt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l := len(nalus)
|
l := len(nalus)
|
||||||
|
|
||||||
@@ -172,7 +167,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBuffer = nil
|
d.frameBuffer = nil
|
||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 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)
|
h265.MaxNALUsPerAccessUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +181,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBuffer = nil
|
d.frameBuffer = nil
|
||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 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)
|
d.frameBufferSize+addSize, h265.MaxAccessUnitSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +190,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBufferSize += addSize
|
d.frameBufferSize += addSize
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := d.frameBuffer
|
ret := d.frameBuffer
|
||||||
@@ -205,5 +200,5 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
d.frameBufferLen = 0
|
d.frameBufferLen = 0
|
||||||
d.frameBufferSize = 0
|
d.frameBufferSize = 0
|
||||||
|
|
||||||
return ret, pts, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ func TestDecode(t *testing.T) {
|
|||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
clone := pkt.Clone()
|
clone := pkt.Clone()
|
||||||
|
|
||||||
addNALUs, _, err := d.Decode(pkt)
|
addNALUs, err := d.Decode(pkt)
|
||||||
|
|
||||||
// test input integrity
|
// test input integrity
|
||||||
require.Equal(t, clone, pkt)
|
require.Equal(t, clone, pkt)
|
||||||
@@ -44,7 +44,7 @@ func TestDecoderErrorLimit(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i := 0; i <= h265.MaxNALUsPerAccessUnit; i++ {
|
for i := 0; i <= h265.MaxNALUsPerAccessUnit; i++ {
|
||||||
_, _, err = d.Decode(&rtp.Packet{
|
_, err = d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: false,
|
Marker: false,
|
||||||
|
@@ -3,11 +3,8 @@ package rtph265
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -38,10 +35,6 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
@@ -50,7 +43,6 @@ type Encoder struct {
|
|||||||
MaxDONDiff int
|
MaxDONDiff int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -74,24 +66,16 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes NALUs into RTP/H265 packets.
|
// 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 rets []*rtp.Packet
|
||||||
var batch [][]byte
|
var batch [][]byte
|
||||||
|
|
||||||
@@ -103,7 +87,7 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
} else {
|
} else {
|
||||||
// write batch
|
// write batch
|
||||||
if batch != nil {
|
if batch != nil {
|
||||||
pkts, err := e.writeBatch(batch, pts, false)
|
pkts, err := e.writeBatch(batch, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -116,8 +100,8 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write final batch
|
// write final batch
|
||||||
// marker is used to indicate when all NALUs with same PTS have been sent
|
// marker is used to indicate that the entire access unit has been sent
|
||||||
pkts, err := e.writeBatch(batch, pts, true)
|
pkts, err := e.writeBatch(batch, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -126,27 +110,26 @@ func (e *Encoder) Encode(nalus [][]byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
return rets, nil
|
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 {
|
if len(nalus) == 1 {
|
||||||
// the NALU fits into a single RTP packet
|
// the NALU fits into a single RTP packet
|
||||||
if len(nalus[0]) < e.PayloadMaxSize {
|
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
|
// 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{
|
pkt := &rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: marker,
|
Marker: marker,
|
||||||
},
|
},
|
||||||
@@ -158,7 +141,7 @@ func (e *Encoder) writeSingle(nalu []byte, pts time.Duration, marker bool) ([]*r
|
|||||||
return []*rtp.Packet{pkt}, nil
|
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
|
avail := e.PayloadMaxSize - 3
|
||||||
le := len(nalu) - 2
|
le := len(nalu) - 2
|
||||||
n := le / avail
|
n := le / avail
|
||||||
@@ -168,7 +151,6 @@ func (e *Encoder) writeFragmentationUnits(nalu []byte, pts time.Duration, marker
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret := make([]*rtp.Packet, n)
|
ret := make([]*rtp.Packet, n)
|
||||||
encPTS := e.timeEncoder.Encode(pts)
|
|
||||||
|
|
||||||
head := nalu[:2]
|
head := nalu[:2]
|
||||||
nalu = nalu[2:]
|
nalu = nalu[2:]
|
||||||
@@ -197,7 +179,6 @@ func (e *Encoder) writeFragmentationUnits(nalu []byte, pts time.Duration, marker
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: encPTS,
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: (i == (n-1) && marker),
|
Marker: (i == (n-1) && marker),
|
||||||
},
|
},
|
||||||
@@ -226,7 +207,7 @@ func (e *Encoder) lenAggregationUnit(nalus [][]byte, addNALU []byte) int {
|
|||||||
return ret
|
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))
|
payload := make([]byte, e.lenAggregationUnit(nalus, nil))
|
||||||
|
|
||||||
// header
|
// header
|
||||||
@@ -252,7 +233,6 @@ func (e *Encoder) writeAggregationUnit(nalus [][]byte, pts time.Duration, marker
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: marker,
|
Marker: marker,
|
||||||
},
|
},
|
||||||
|
@@ -47,7 +47,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
|
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
|
||||||
@@ -68,7 +67,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -90,7 +88,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -105,7 +102,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -119,7 +115,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -138,12 +133,11 @@ func TestEncode(t *testing.T) {
|
|||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.nalus, 0)
|
pkts, err := e.Encode(ca.nalus)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -158,5 +152,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,2 @@
|
|||||||
// Package rtph265 contains a RTP/H265 decoder and encoder.
|
// Package rtph265 contains a RTP/H265 decoder and encoder.
|
||||||
package rtph265
|
package rtph265
|
||||||
|
|
||||||
const (
|
|
||||||
rtpClockRate = 90000 // H265 always uses 90khz
|
|
||||||
)
|
|
||||||
|
@@ -2,38 +2,32 @@ package rtplpcm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder is a RTP/LPCM decoder.
|
// Decoder is a RTP/LPCM decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
// Specification: https://datatracker.ietf.org/doc/html/rfc3190
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
BitDepth int
|
BitDepth int
|
||||||
SampleRate int
|
|
||||||
ChannelCount int
|
ChannelCount int
|
||||||
|
|
||||||
timeDecoder *rtptime.Decoder
|
sampleSize int
|
||||||
sampleSize int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
|
|
||||||
d.sampleSize = d.BitDepth * d.ChannelCount / 8
|
d.sampleSize = d.BitDepth * d.ChannelCount / 8
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes audio samples from a RTP packet.
|
// Decode decodes audio samples from a RTP packet.
|
||||||
// It returns audio samples and PTS of the first sample.
|
// 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)
|
plen := len(pkt.Payload)
|
||||||
if (plen % d.sampleSize) != 0 {
|
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
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,6 @@ func TestDecode(t *testing.T) {
|
|||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
d := &Decoder{
|
d := &Decoder{
|
||||||
BitDepth: 24,
|
BitDepth: 24,
|
||||||
SampleRate: 48000,
|
|
||||||
ChannelCount: 2,
|
ChannelCount: 2,
|
||||||
}
|
}
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
@@ -21,7 +20,7 @@ func TestDecode(t *testing.T) {
|
|||||||
var samples []byte
|
var samples []byte
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
partial, _, err := d.Decode(pkt)
|
partial, err := d.Decode(pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
samples = append(samples, partial...)
|
samples = append(samples, partial...)
|
||||||
}
|
}
|
||||||
@@ -35,7 +34,6 @@ func FuzzDecoder(f *testing.F) {
|
|||||||
f.Fuzz(func(t *testing.T, b []byte) {
|
f.Fuzz(func(t *testing.T, b []byte) {
|
||||||
d := &Decoder{
|
d := &Decoder{
|
||||||
BitDepth: 24,
|
BitDepth: 24,
|
||||||
SampleRate: 48000,
|
|
||||||
ChannelCount: 2,
|
ChannelCount: 2,
|
||||||
}
|
}
|
||||||
d.Init() //nolint:errcheck
|
d.Init() //nolint:errcheck
|
||||||
|
@@ -3,11 +3,8 @@ package rtplpcm
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -30,6 +27,12 @@ type Encoder struct {
|
|||||||
// payload type of packets.
|
// payload type of packets.
|
||||||
PayloadType uint8
|
PayloadType uint8
|
||||||
|
|
||||||
|
// bit depth.
|
||||||
|
BitDepth int
|
||||||
|
|
||||||
|
// channel count.
|
||||||
|
ChannelCount int
|
||||||
|
|
||||||
// SSRC of packets (optional).
|
// SSRC of packets (optional).
|
||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
SSRC *uint32
|
SSRC *uint32
|
||||||
@@ -38,20 +41,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
BitDepth int
|
|
||||||
SampleRate int
|
|
||||||
ChannelCount int
|
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
sampleSize int
|
sampleSize int
|
||||||
maxPayloadSize int
|
maxPayloadSize int
|
||||||
}
|
}
|
||||||
@@ -73,19 +67,11 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
|
|
||||||
e.sampleSize = e.BitDepth * e.ChannelCount / 8
|
e.sampleSize = e.BitDepth * e.ChannelCount / 8
|
||||||
e.maxPayloadSize = (e.PayloadMaxSize / e.sampleSize) * e.sampleSize
|
e.maxPayloadSize = (e.PayloadMaxSize / e.sampleSize) * e.sampleSize
|
||||||
return nil
|
return nil
|
||||||
@@ -101,7 +87,7 @@ func (e *Encoder) packetCount(slen int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes audio samples into RTP packets.
|
// 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)
|
slen := len(samples)
|
||||||
if (slen % e.sampleSize) != 0 {
|
if (slen % e.sampleSize) != 0 {
|
||||||
return nil, fmt.Errorf("invalid samples")
|
return nil, fmt.Errorf("invalid samples")
|
||||||
@@ -112,6 +98,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
i := 0
|
i := 0
|
||||||
pos := 0
|
pos := 0
|
||||||
payloadSize := e.maxPayloadSize
|
payloadSize := e.maxPayloadSize
|
||||||
|
timestamp := uint32(0)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if payloadSize > len(samples[pos:]) {
|
if payloadSize > len(samples[pos:]) {
|
||||||
@@ -123,7 +110,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
Timestamp: timestamp,
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: false,
|
Marker: false,
|
||||||
},
|
},
|
||||||
@@ -133,7 +120,7 @@ func (e *Encoder) Encode(samples []byte, pts time.Duration) ([]*rtp.Packet, erro
|
|||||||
e.sequenceNumber++
|
e.sequenceNumber++
|
||||||
i++
|
i++
|
||||||
pos += payloadSize
|
pos += payloadSize
|
||||||
pts += time.Duration(payloadSize/e.sampleSize) * time.Second / time.Duration(e.SampleRate)
|
timestamp += uint32(payloadSize / e.sampleSize)
|
||||||
|
|
||||||
if pos == slen {
|
if pos == slen {
|
||||||
break
|
break
|
||||||
|
@@ -31,7 +31,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
|
Payload: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06},
|
||||||
@@ -48,7 +47,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 486),
|
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 486),
|
||||||
@@ -59,7 +57,7 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526600,
|
Timestamp: 243,
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 194),
|
Payload: bytes.Repeat([]byte{0x41, 0x42, 0x43}, 194),
|
||||||
@@ -75,15 +73,13 @@ func TestEncode(t *testing.T) {
|
|||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
BitDepth: 24,
|
BitDepth: 24,
|
||||||
SampleRate: 48000,
|
|
||||||
ChannelCount: 2,
|
ChannelCount: 2,
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.samples, 0)
|
pkts, err := e.Encode(ca.samples)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -94,12 +90,10 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
BitDepth: 24,
|
BitDepth: 24,
|
||||||
SampleRate: 48000,
|
|
||||||
ChannelCount: 2,
|
ChannelCount: 2,
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,9 @@ package rtpmjpeg
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/jpeg"
|
"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.
|
// Decoder is a RTP/M-JPEG decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
// Specification: https://datatracker.ietf.org/doc/html/rfc2435
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -115,27 +112,26 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes an image from a RTP packet.
|
// 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
|
byts := pkt.Payload
|
||||||
|
|
||||||
var jh headerJPEG
|
var jh headerJPEG
|
||||||
n, err := jh.unmarshal(byts)
|
n, err := jh.unmarshal(byts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
byts = byts[n:]
|
byts = byts[n:]
|
||||||
|
|
||||||
if jh.Width > maxDimension {
|
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 {
|
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 {
|
if jh.FragmentOffset == 0 {
|
||||||
@@ -147,7 +143,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
d.firstQTHeader = &headerQuantizationTable{}
|
d.firstQTHeader = &headerQuantizationTable{}
|
||||||
n, err := d.firstQTHeader.unmarshal(byts)
|
n, err := d.firstQTHeader.unmarshal(byts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
byts = byts[n:]
|
byts = byts[n:]
|
||||||
} else {
|
} else {
|
||||||
@@ -160,12 +156,12 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
} else {
|
} else {
|
||||||
if int(jh.FragmentOffset) != d.fragmentsSize {
|
if int(jh.FragmentOffset) != d.fragmentsSize {
|
||||||
if !d.firstPacketReceived {
|
if !d.firstPacketReceived {
|
||||||
return nil, 0, ErrNonStartingPacketAndNoPrevious
|
return nil, ErrNonStartingPacketAndNoPrevious
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.fragmentsSize = 0
|
d.fragmentsSize = 0
|
||||||
return nil, 0, fmt.Errorf("received wrong fragment")
|
return nil, fmt.Errorf("received wrong fragment")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragmentsSize += len(byts)
|
d.fragmentsSize += len(byts)
|
||||||
@@ -173,11 +169,11 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.fragmentsSize < 2 {
|
if d.fragmentsSize < 2 {
|
||||||
return nil, 0, fmt.Errorf("invalid data")
|
return nil, fmt.Errorf("invalid data")
|
||||||
}
|
}
|
||||||
|
|
||||||
data := joinFragments(d.fragments, d.fragmentsSize)
|
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}...)
|
buf = append(buf, []byte{0xFF, jpeg.MarkerEndOfImage}...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ func TestDecode(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
image, _, err := d.Decode(pkt)
|
image, err := d.Decode(pkt)
|
||||||
if err == ErrMorePacketsNeeded {
|
if err == ErrMorePacketsNeeded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,9 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/jpeg"
|
"github.com/bluenviron/mediacommon/pkg/codecs/jpeg"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,16 +35,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -66,24 +59,16 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes an image into RTP/M-JPEG packets.
|
// 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)
|
l := len(image)
|
||||||
if l < 2 || image[0] != 0xFF || image[1] != jpeg.MarkerStartOfImage {
|
if l < 2 || image[0] != 0xFF || image[1] != jpeg.MarkerStartOfImage {
|
||||||
return nil, fmt.Errorf("SOI not found")
|
return nil, fmt.Errorf("SOI not found")
|
||||||
@@ -275,7 +260,6 @@ outer:
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: 26,
|
PayloadType: 26,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: len(data) == 0,
|
Marker: len(data) == 0,
|
||||||
},
|
},
|
||||||
|
@@ -289,7 +289,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 26,
|
PayloadType: 26,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289528607,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -484,7 +483,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 26,
|
PayloadType: 26,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289528607,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -519,12 +517,11 @@ func TestEncode(t *testing.T) {
|
|||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(2289528607),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.image, 0)
|
pkts, err := e.Encode(ca.image)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
|
@@ -2,6 +2,5 @@
|
|||||||
package rtpmjpeg
|
package rtpmjpeg
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rtpClockRate = 90000
|
|
||||||
maxDimension = 2040
|
maxDimension = 2040
|
||||||
)
|
)
|
||||||
|
@@ -3,12 +3,9 @@ package rtpmpeg1audio
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
|
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/MPEG-1/2 Audio decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
// Specification: https://datatracker.ietf.org/doc/html/rfc2250
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
@@ -42,23 +38,22 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(90000)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes frames from a RTP packet.
|
// 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 {
|
if len(pkt.Payload) < 5 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.fragmentsSize = 0
|
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])
|
mbz := uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1])
|
||||||
if mbz != 0 {
|
if mbz != 0 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.fragmentsSize = 0
|
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])
|
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
|
var h mpeg1audio.FrameHeader
|
||||||
err := h.Unmarshal(buf)
|
err := h.Unmarshal(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fl := h.FrameLen()
|
fl := h.FrameLen()
|
||||||
@@ -88,24 +83,24 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(frames) != 0 {
|
if len(frames) != 0 {
|
||||||
return nil, 0, fmt.Errorf("invalid packet")
|
return nil, fmt.Errorf("invalid packet")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragments = append(d.fragments, buf)
|
d.fragments = append(d.fragments, buf)
|
||||||
d.fragmentsSize = bl
|
d.fragmentsSize = bl
|
||||||
d.fragmentsExpected = fl - bl
|
d.fragmentsExpected = fl - bl
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if int(offset) != d.fragmentsSize {
|
if int(offset) != d.fragmentsSize {
|
||||||
if !d.firstPacketReceived {
|
if !d.firstPacketReceived {
|
||||||
return nil, 0, ErrNonStartingPacketAndNoPrevious
|
return nil, ErrNonStartingPacketAndNoPrevious
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.fragmentsSize = 0
|
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:])
|
bl := len(pkt.Payload[4:])
|
||||||
@@ -115,13 +110,13 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
if d.fragmentsExpected < 0 {
|
if d.fragmentsExpected < 0 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
d.fragmentsSize = 0
|
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:])
|
d.fragments = append(d.fragments, pkt.Payload[4:])
|
||||||
|
|
||||||
if d.fragmentsExpected > 0 {
|
if d.fragmentsExpected > 0 {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
frames = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
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
|
d.fragmentsSize = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return frames, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return frames, nil
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
|
|||||||
var frames [][]byte
|
var frames [][]byte
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
frames, _, err = d.Decode(pkt)
|
frames, err = d.Decode(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@@ -2,12 +2,9 @@ package rtpmpeg1audio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
|
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg1audio"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -43,16 +40,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -72,34 +64,27 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes frames into RTP packets.
|
// 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 rets []*rtp.Packet
|
||||||
var batch [][]byte
|
var batch [][]byte
|
||||||
|
timestamp := uint32(0)
|
||||||
|
|
||||||
for _, frame := range frames {
|
for _, frame := range frames {
|
||||||
if lenAggregated(batch, frame) <= e.PayloadMaxSize {
|
if lenAggregated(batch, frame) <= e.PayloadMaxSize {
|
||||||
batch = append(batch, frame)
|
batch = append(batch, frame)
|
||||||
} else {
|
} else {
|
||||||
// write last batch
|
// write current batch
|
||||||
if batch != nil {
|
if batch != nil {
|
||||||
pkts, err := e.writeBatch(batch, pts)
|
pkts, err := e.writeBatch(batch, timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -112,7 +97,7 @@ func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, err
|
|||||||
return nil, 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
|
// write last batch
|
||||||
pkts, err := e.writeBatch(batch, pts)
|
pkts, err := e.writeBatch(batch, timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -131,15 +116,15 @@ func (e *Encoder) Encode(frames [][]byte, pts time.Duration) ([]*rtp.Packet, err
|
|||||||
return rets, nil
|
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 {
|
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
|
avail := e.PayloadMaxSize - 4
|
||||||
le := len(frame)
|
le := len(frame)
|
||||||
packetCount := le / avail
|
packetCount := le / avail
|
||||||
@@ -150,7 +135,6 @@ func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packe
|
|||||||
|
|
||||||
pos := 0
|
pos := 0
|
||||||
ret := make([]*rtp.Packet, packetCount)
|
ret := make([]*rtp.Packet, packetCount)
|
||||||
encPTS := e.timeEncoder.Encode(pts)
|
|
||||||
|
|
||||||
for i := range ret {
|
for i := range ret {
|
||||||
var le int
|
var le int
|
||||||
@@ -171,7 +155,7 @@ func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packe
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: encPTS,
|
Timestamp: timestamp,
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
},
|
},
|
||||||
@@ -184,7 +168,7 @@ func (e *Encoder) writeFragmented(frame []byte, pts time.Duration) ([]*rtp.Packe
|
|||||||
return ret, nil
|
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))
|
payload := make([]byte, lenAggregated(frames, nil))
|
||||||
|
|
||||||
n := 4
|
n := 4
|
||||||
@@ -197,7 +181,7 @@ func (e *Encoder) writeAggregated(frames [][]byte, pts time.Duration) ([]*rtp.Pa
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
Timestamp: timestamp,
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
},
|
},
|
||||||
|
@@ -42,7 +42,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -100,7 +99,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -288,7 +286,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -350,7 +347,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -412,7 +408,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 14,
|
PayloadType: 14,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -474,13 +469,12 @@ func TestEncode(t *testing.T) {
|
|||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
PayloadMaxSize: 400,
|
PayloadMaxSize: 400,
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.frames, 0)
|
pkts, err := e.Encode(ca.frames)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -493,5 +487,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,10 @@ package rtpmpeg4audiogeneric
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/bits"
|
"github.com/bluenviron/mediacommon/pkg/bits"
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/MPEG-4 Audio decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
// Specification: https://datatracker.ietf.org/doc/html/rfc3640
|
||||||
type Decoder struct {
|
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.
|
// The number of bits in which the AU-size field is encoded in the AU-header.
|
||||||
SizeLength int
|
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.
|
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
|
||||||
IndexDeltaLength int
|
IndexDeltaLength int
|
||||||
|
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstAUParsed bool
|
firstAUParsed bool
|
||||||
adtsMode bool
|
adtsMode bool
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -48,24 +41,23 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes AUs from a RTP packet.
|
// Decode decodes AUs from a RTP packet.
|
||||||
// It returns the AUs and the PTS of the first AU.
|
// 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.
|
// 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 {
|
if len(pkt.Payload) < 2 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
// AU-headers-length (16 bits)
|
||||||
headersLen := int(uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1]))
|
headersLen := int(uint16(pkt.Payload[0])<<8 | uint16(pkt.Payload[1]))
|
||||||
if headersLen == 0 {
|
if headersLen == 0 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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:]
|
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)
|
dataLens, err := d.readAUHeaders(payload, headersLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pos := (headersLen / 8)
|
pos := (headersLen / 8)
|
||||||
@@ -90,7 +82,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
aus = make([][]byte, len(dataLens))
|
aus = make([][]byte, len(dataLens))
|
||||||
for i, dataLen := range dataLens {
|
for i, dataLen := range dataLens {
|
||||||
if len(payload) < int(dataLen) {
|
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]
|
aus[i] = payload[:dataLen]
|
||||||
@@ -98,40 +90,40 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([][]byte, time.Duration, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if len(dataLens) != 1 {
|
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]) {
|
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.fragmentsSize = int(dataLens[0])
|
||||||
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we are decoding a fragmented AU
|
// we are decoding a fragmented AU
|
||||||
if len(dataLens) != 1 {
|
if len(dataLens) != 1 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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]) {
|
if len(payload) < int(dataLens[0]) {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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])
|
d.fragmentsSize += int(dataLens[0])
|
||||||
if d.fragmentsSize > mpeg4audio.MaxAccessUnitSize {
|
if d.fragmentsSize > mpeg4audio.MaxAccessUnitSize {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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.fragmentsSize, mpeg4audio.MaxAccessUnitSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
d.fragments = append(d.fragments, payload[:dataLens[0]])
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
aus = [][]byte{joinFragments(d.fragments, d.fragmentsSize)}
|
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)
|
aus, err = d.removeADTS(aus)
|
||||||
if err != nil {
|
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) {
|
func (d *Decoder) readAUHeaders(buf []byte, headersLen int) ([]uint64, error) {
|
||||||
|
@@ -11,7 +11,6 @@ func TestDecode(t *testing.T) {
|
|||||||
for _, ca := range cases {
|
for _, ca := range cases {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
d := &Decoder{
|
d := &Decoder{
|
||||||
SampleRate: 48000,
|
|
||||||
SizeLength: ca.sizeLength,
|
SizeLength: ca.sizeLength,
|
||||||
IndexLength: ca.indexLength,
|
IndexLength: ca.indexLength,
|
||||||
IndexDeltaLength: ca.indexDeltaLength,
|
IndexDeltaLength: ca.indexDeltaLength,
|
||||||
@@ -24,7 +23,7 @@ func TestDecode(t *testing.T) {
|
|||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
clone := pkt.Clone()
|
clone := pkt.Clone()
|
||||||
|
|
||||||
addAUs, _, err := d.Decode(pkt)
|
addAUs, err := d.Decode(pkt)
|
||||||
|
|
||||||
// test input integrity
|
// test input integrity
|
||||||
require.Equal(t, clone, pkt)
|
require.Equal(t, clone, pkt)
|
||||||
@@ -44,7 +43,6 @@ func TestDecode(t *testing.T) {
|
|||||||
|
|
||||||
func TestDecodeADTS(t *testing.T) {
|
func TestDecodeADTS(t *testing.T) {
|
||||||
d := &Decoder{
|
d := &Decoder{
|
||||||
SampleRate: 16000,
|
|
||||||
SizeLength: 13,
|
SizeLength: 13,
|
||||||
IndexLength: 3,
|
IndexLength: 3,
|
||||||
IndexDeltaLength: 3,
|
IndexDeltaLength: 3,
|
||||||
@@ -53,13 +51,12 @@ func TestDecodeADTS(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
aus, _, err := d.Decode(&rtp.Packet{
|
aus, err := d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -75,7 +72,6 @@ func TestDecodeADTS(t *testing.T) {
|
|||||||
func FuzzDecoder(f *testing.F) {
|
func FuzzDecoder(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
|
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
|
||||||
d := &Decoder{
|
d := &Decoder{
|
||||||
SampleRate: 16000,
|
|
||||||
SizeLength: 13,
|
SizeLength: 13,
|
||||||
IndexLength: 3,
|
IndexLength: 3,
|
||||||
IndexDeltaLength: 3,
|
IndexDeltaLength: 3,
|
||||||
|
@@ -2,11 +2,9 @@ package rtpmpeg4audiogeneric
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/bits"
|
"github.com/bluenviron/mediacommon/pkg/bits"
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||||
)
|
)
|
||||||
@@ -31,25 +29,6 @@ type Encoder struct {
|
|||||||
// payload type of packets.
|
// payload type of packets.
|
||||||
PayloadType uint8
|
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.
|
// The number of bits in which the AU-size field is encoded in the AU-header.
|
||||||
SizeLength int
|
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.
|
// The number of bits in which the AU-Index-delta field is encoded in any non-first AU-header.
|
||||||
IndexDeltaLength int
|
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
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -80,26 +70,19 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes AUs into RTP packets.
|
// 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 rets []*rtp.Packet
|
||||||
var batch [][]byte
|
var batch [][]byte
|
||||||
|
timestamp := uint32(0)
|
||||||
|
|
||||||
// split AUs into batches
|
// split AUs into batches
|
||||||
for _, au := range aus {
|
for _, au := range aus {
|
||||||
@@ -107,14 +90,14 @@ func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error)
|
|||||||
// add to existing batch
|
// add to existing batch
|
||||||
batch = append(batch, au)
|
batch = append(batch, au)
|
||||||
} else {
|
} else {
|
||||||
// write last batch
|
// write current batch
|
||||||
if batch != nil {
|
if batch != nil {
|
||||||
pkts, err := e.writeBatch(batch, pts)
|
pkts, err := e.writeBatch(batch, timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
rets = append(rets, pkts...)
|
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
|
// initialize new batch
|
||||||
@@ -123,7 +106,7 @@ func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write last batch
|
// write last batch
|
||||||
pkts, err := e.writeBatch(batch, pts)
|
pkts, err := e.writeBatch(batch, timestamp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -132,15 +115,15 @@ func (e *Encoder) Encode(aus [][]byte, pts time.Duration) ([]*rtp.Packet, error)
|
|||||||
return rets, nil
|
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 {
|
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
|
auHeadersLen := e.SizeLength + e.IndexLength
|
||||||
auHeadersLenBytes := auHeadersLen / 8
|
auHeadersLenBytes := auHeadersLen / 8
|
||||||
if (auHeadersLen % 8) != 0 {
|
if (auHeadersLen % 8) != 0 {
|
||||||
@@ -156,7 +139,6 @@ func (e *Encoder) writeFragmented(au []byte, pts time.Duration) ([]*rtp.Packet,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret := make([]*rtp.Packet, packetCount)
|
ret := make([]*rtp.Packet, packetCount)
|
||||||
encPTS := e.timeEncoder.Encode(pts)
|
|
||||||
|
|
||||||
for i := range ret {
|
for i := range ret {
|
||||||
var le int
|
var le int
|
||||||
@@ -186,7 +168,7 @@ func (e *Encoder) writeFragmented(au []byte, pts time.Duration) ([]*rtp.Packet,
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: encPTS,
|
Timestamp: timestamp,
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: (i == (packetCount - 1)),
|
Marker: (i == (packetCount - 1)),
|
||||||
},
|
},
|
||||||
@@ -234,7 +216,7 @@ func (e *Encoder) lenAggregated(aus [][]byte, addAU []byte) int {
|
|||||||
return ret
|
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))
|
payload := make([]byte, e.lenAggregated(aus, nil))
|
||||||
|
|
||||||
// AU-headers
|
// AU-headers
|
||||||
@@ -271,7 +253,7 @@ func (e *Encoder) writeAggregated(aus [][]byte, pts time.Duration) ([]*rtp.Packe
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
Timestamp: timestamp,
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
},
|
},
|
||||||
|
@@ -100,7 +100,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -170,7 +169,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -196,7 +194,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -210,7 +207,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -224,7 +220,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -252,7 +247,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -267,7 +261,7 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289529429,
|
Timestamp: 3072,
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -281,7 +275,7 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289529429,
|
Timestamp: 3072,
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -306,7 +300,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -331,7 +324,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -357,7 +349,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -383,7 +374,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -398,7 +388,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -413,7 +402,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -438,7 +426,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -452,7 +439,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -466,7 +452,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -483,10 +468,8 @@ func TestEncode(t *testing.T) {
|
|||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SampleRate: 48000,
|
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
SizeLength: ca.sizeLength,
|
SizeLength: ca.sizeLength,
|
||||||
IndexLength: ca.indexLength,
|
IndexLength: ca.indexLength,
|
||||||
IndexDeltaLength: ca.indexDeltaLength,
|
IndexDeltaLength: ca.indexDeltaLength,
|
||||||
@@ -494,7 +477,7 @@ func TestEncode(t *testing.T) {
|
|||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.aus, 0)
|
pkts, err := e.Encode(ca.aus)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -504,7 +487,6 @@ func TestEncode(t *testing.T) {
|
|||||||
func TestEncodeRandomInitialState(t *testing.T) {
|
func TestEncodeRandomInitialState(t *testing.T) {
|
||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SampleRate: 48000,
|
|
||||||
SizeLength: 13,
|
SizeLength: 13,
|
||||||
IndexLength: 3,
|
IndexLength: 3,
|
||||||
IndexDeltaLength: 3,
|
IndexDeltaLength: 3,
|
||||||
@@ -513,5 +495,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,12 +3,9 @@ package rtpmpeg4audiolatm
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/MPEG-4 Audio decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
|
// Specification: https://datatracker.ietf.org/doc/html/rfc6416#section-7.3
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
// StreamMuxConfig.
|
|
||||||
Config *mpeg4audio.StreamMuxConfig
|
|
||||||
|
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragmentsExpected int
|
fragmentsExpected int
|
||||||
@@ -37,24 +30,19 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes an AU from a RTP packet.
|
// Decode decodes an AU from a RTP packet.
|
||||||
// It returns the AU and its PTS.
|
// 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
|
var au []byte
|
||||||
buf := pkt.Payload
|
buf := pkt.Payload
|
||||||
|
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
pl, n, err := payloadLengthInfoDecode(buf)
|
pl, n, err := payloadLengthInfoDecode(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = buf[n:]
|
buf = buf[n:]
|
||||||
@@ -66,14 +54,14 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
} else {
|
} else {
|
||||||
if pl > mpeg4audio.MaxAccessUnitSize {
|
if pl > mpeg4audio.MaxAccessUnitSize {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
pl, mpeg4audio.MaxAccessUnitSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragments = append(d.fragments, buf)
|
d.fragments = append(d.fragments, buf)
|
||||||
d.fragmentsSize = pl
|
d.fragmentsSize = pl
|
||||||
d.fragmentsExpected = pl - bl
|
d.fragmentsExpected = pl - bl
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bl := len(buf)
|
bl := len(buf)
|
||||||
@@ -81,7 +69,7 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
if d.fragmentsExpected > bl {
|
if d.fragmentsExpected > bl {
|
||||||
d.fragments = append(d.fragments, buf)
|
d.fragments = append(d.fragments, buf)
|
||||||
d.fragmentsExpected -= bl
|
d.fragmentsExpected -= bl
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
d.fragments = append(d.fragments, buf[:d.fragmentsExpected])
|
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]
|
d.fragments = d.fragments[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return au, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return au, nil
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ package rtpmpeg4audiolatm
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -11,9 +10,7 @@ import (
|
|||||||
func TestDecode(t *testing.T) {
|
func TestDecode(t *testing.T) {
|
||||||
for _, ca := range cases {
|
for _, ca := range cases {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
d := &Decoder{
|
d := &Decoder{}
|
||||||
Config: ca.config,
|
|
||||||
}
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -22,7 +19,7 @@ func TestDecode(t *testing.T) {
|
|||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
clone := pkt.Clone()
|
clone := pkt.Clone()
|
||||||
|
|
||||||
au, _, err = d.Decode(pkt)
|
au, err = d.Decode(pkt)
|
||||||
|
|
||||||
// test input integrity
|
// test input integrity
|
||||||
require.Equal(t, clone, pkt)
|
require.Equal(t, clone, pkt)
|
||||||
@@ -40,32 +37,16 @@ func TestDecode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeOtherData(t *testing.T) {
|
func TestDecodeOtherData(t *testing.T) {
|
||||||
d := &Decoder{
|
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
au, _, err := d.Decode(&rtp.Packet{
|
au, err := d.Decode(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
Version: 2,
|
Version: 2,
|
||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 2646308882,
|
SSRC: 2646308882,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -79,20 +60,7 @@ func TestDecodeOtherData(t *testing.T) {
|
|||||||
|
|
||||||
func FuzzDecoder(f *testing.F) {
|
func FuzzDecoder(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
|
f.Fuzz(func(t *testing.T, a []byte, am bool, b []byte, bm bool) {
|
||||||
d := &Decoder{
|
d := &Decoder{}
|
||||||
Config: &mpeg4audio.StreamMuxConfig{
|
|
||||||
Programs: []*mpeg4audio.StreamMuxConfigProgram{{
|
|
||||||
Layers: []*mpeg4audio.StreamMuxConfigLayer{{
|
|
||||||
AudioSpecificConfig: &mpeg4audio.AudioSpecificConfig{
|
|
||||||
Type: 2,
|
|
||||||
SampleRate: 48000,
|
|
||||||
ChannelCount: 2,
|
|
||||||
},
|
|
||||||
LatmBufferFullness: 255,
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
d.Init() //nolint:errcheck
|
d.Init() //nolint:errcheck
|
||||||
|
|
||||||
d.Decode(&rtp.Packet{ //nolint:errcheck
|
d.Decode(&rtp.Packet{ //nolint:errcheck
|
||||||
|
@@ -2,13 +2,8 @@ package rtpmpeg4audiolatm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -31,9 +26,6 @@ type Encoder struct {
|
|||||||
// payload type of packets.
|
// payload type of packets.
|
||||||
PayloadType uint8
|
PayloadType uint8
|
||||||
|
|
||||||
// StreamMuxConfig.
|
|
||||||
Config *mpeg4audio.StreamMuxConfig
|
|
||||||
|
|
||||||
// SSRC of packets (optional).
|
// SSRC of packets (optional).
|
||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
SSRC *uint32
|
SSRC *uint32
|
||||||
@@ -42,24 +34,15 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
func (e *Encoder) Init() error {
|
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 {
|
if e.SSRC == nil {
|
||||||
v, err := randUint32()
|
v, err := randUint32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,19 +58,11 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(e.Config.Programs[0].Layers[0].AudioSpecificConfig.SampleRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,15 +76,14 @@ func (e *Encoder) packetCount(auLen int, plil int) int {
|
|||||||
return packetCount
|
return packetCount
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes AUs into RTP packets.
|
// Encode encodes an access unit into RTP packets.
|
||||||
func (e *Encoder) Encode(au []byte, pts time.Duration) ([]*rtp.Packet, error) {
|
func (e *Encoder) Encode(au []byte) ([]*rtp.Packet, error) {
|
||||||
auLen := len(au)
|
auLen := len(au)
|
||||||
plil := payloadLengthInfoEncodeSize(auLen)
|
plil := payloadLengthInfoEncodeSize(auLen)
|
||||||
packetCount := e.packetCount(auLen, plil)
|
packetCount := e.packetCount(auLen, plil)
|
||||||
|
|
||||||
avail := e.PayloadMaxSize - plil
|
avail := e.PayloadMaxSize - plil
|
||||||
ret := make([]*rtp.Packet, packetCount)
|
ret := make([]*rtp.Packet, packetCount)
|
||||||
encPTS := e.timeEncoder.Encode(pts)
|
|
||||||
|
|
||||||
for i := range ret {
|
for i := range ret {
|
||||||
var final bool
|
var final bool
|
||||||
@@ -138,7 +112,6 @@ func (e *Encoder) Encode(au []byte, pts time.Duration) ([]*rtp.Packet, error) {
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: encPTS,
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: final,
|
Marker: final,
|
||||||
},
|
},
|
||||||
|
@@ -61,7 +61,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 2646308882,
|
SSRC: 2646308882,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -92,7 +91,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 2646308882,
|
SSRC: 2646308882,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -108,7 +106,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 2646308882,
|
SSRC: 2646308882,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -123,7 +120,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 2646308882,
|
SSRC: 2646308882,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes(
|
Payload: mergeBytes(
|
||||||
@@ -140,15 +136,13 @@ func TestEncode(t *testing.T) {
|
|||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
Config: ca.config,
|
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.au, 0)
|
pkts, err := e.Encode(ca.au)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -158,22 +152,9 @@ func TestEncode(t *testing.T) {
|
|||||||
func TestEncodeRandomInitialState(t *testing.T) {
|
func TestEncodeRandomInitialState(t *testing.T) {
|
||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 96,
|
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()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,8 @@ package rtpmpeg4video
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/MPEG-4 Video decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
|
// Specification: https://datatracker.ietf.org/doc/html/rfc6416
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(90000)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes a frame from a RTP packet.
|
// 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
|
var frame []byte
|
||||||
|
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
@@ -50,24 +45,24 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
} else {
|
} else {
|
||||||
d.fragmentsSize = len(pkt.Payload)
|
d.fragmentsSize = len(pkt.Payload)
|
||||||
d.fragments = append(d.fragments, pkt.Payload)
|
d.fragments = append(d.fragments, pkt.Payload)
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
d.fragmentsSize += len(pkt.Payload)
|
d.fragmentsSize += len(pkt.Payload)
|
||||||
if d.fragmentsSize > maxFrameSize {
|
if d.fragmentsSize > maxFrameSize {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
d.fragments = append(d.fragments, pkt.Payload)
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = joinFragments(d.fragments, d.fragmentsSize)
|
frame = joinFragments(d.fragments, d.fragmentsSize)
|
||||||
d.fragments = d.fragments[:0]
|
d.fragments = d.fragments[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
|
|||||||
var frame []byte
|
var frame []byte
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
frame, _, err = d.Decode(pkt)
|
frame, err = d.Decode(pkt)
|
||||||
if err == ErrMorePacketsNeeded {
|
if err == ErrMorePacketsNeeded {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,8 @@ package rtpmpeg4video
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -37,16 +34,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -66,19 +58,11 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(90000, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,14 +76,13 @@ func packetCount(avail, le int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes a frame into RTP packets.
|
// 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
|
avail := e.PayloadMaxSize
|
||||||
le := len(frame)
|
le := len(frame)
|
||||||
packetCount := packetCount(avail, le)
|
packetCount := packetCount(avail, le)
|
||||||
|
|
||||||
pos := 0
|
pos := 0
|
||||||
ret := make([]*rtp.Packet, packetCount)
|
ret := make([]*rtp.Packet, packetCount)
|
||||||
encPTS := e.timeEncoder.Encode(pts)
|
|
||||||
|
|
||||||
for i := range ret {
|
for i := range ret {
|
||||||
var le int
|
var le int
|
||||||
@@ -114,7 +97,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: encPTS,
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: (i == len(ret)-1),
|
Marker: (i == len(ret)-1),
|
||||||
},
|
},
|
||||||
|
@@ -31,7 +31,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{
|
Payload: []byte{
|
||||||
@@ -50,7 +49,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 100/4),
|
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 100/4),
|
||||||
@@ -61,7 +59,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 50/4),
|
Payload: bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 50/4),
|
||||||
@@ -77,13 +74,12 @@ func TestEncode(t *testing.T) {
|
|||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
PayloadMaxSize: 100,
|
PayloadMaxSize: 100,
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.frame, 0)
|
pkts, err := e.Encode(ca.frame)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -98,5 +94,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,27 +1,18 @@
|
|||||||
package rtpsimpleaudio
|
package rtpsimpleaudio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decoder is a RTP/simple audio decoder.
|
// Decoder is a RTP/simple audio decoder.
|
||||||
type Decoder struct {
|
type Decoder struct{}
|
||||||
SampleRate int
|
|
||||||
|
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(d.SampleRate)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes an audio frame from a RTP packet.
|
// Decode decodes an audio 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) {
|
||||||
return pkt.Payload, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return pkt.Payload, nil
|
||||||
}
|
}
|
||||||
|
@@ -9,13 +9,11 @@ import (
|
|||||||
func TestDecode(t *testing.T) {
|
func TestDecode(t *testing.T) {
|
||||||
for _, ca := range cases {
|
for _, ca := range cases {
|
||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
d := &Decoder{
|
d := &Decoder{}
|
||||||
SampleRate: 8000,
|
|
||||||
}
|
|
||||||
err := d.Init()
|
err := d.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
frame, _, err := d.Decode(ca.pkt)
|
frame, err := d.Decode(ca.pkt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.frame, frame)
|
require.Equal(t, ca.frame, frame)
|
||||||
})
|
})
|
||||||
|
@@ -3,11 +3,8 @@ package rtpsimpleaudio
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -37,18 +34,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
SampleRate int
|
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the encoder.
|
// Init initializes the encoder.
|
||||||
@@ -68,24 +58,16 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(e.SampleRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes an audio frame into a RTP packet.
|
// 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 {
|
if len(frame) > e.PayloadMaxSize {
|
||||||
return nil, fmt.Errorf("frame is too big")
|
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,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: false,
|
Marker: false,
|
||||||
},
|
},
|
||||||
|
@@ -29,7 +29,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 0,
|
PayloadType: 0,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{0x01, 0x02, 0x03, 0x04},
|
Payload: []byte{0x01, 0x02, 0x03, 0x04},
|
||||||
@@ -42,15 +41,13 @@ func TestEncode(t *testing.T) {
|
|||||||
t.Run(ca.name, func(t *testing.T) {
|
t.Run(ca.name, func(t *testing.T) {
|
||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 0,
|
PayloadType: 0,
|
||||||
SampleRate: 8000,
|
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkt, err := e.Encode(ca.frame, 0)
|
pkt, err := e.Encode(ca.frame)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkt, pkt)
|
require.Equal(t, ca.pkt, pkt)
|
||||||
})
|
})
|
||||||
@@ -60,11 +57,9 @@ func TestEncode(t *testing.T) {
|
|||||||
func TestEncodeRandomInitialState(t *testing.T) {
|
func TestEncodeRandomInitialState(t *testing.T) {
|
||||||
e := &Encoder{
|
e := &Encoder{
|
||||||
PayloadType: 0,
|
PayloadType: 0,
|
||||||
SampleRate: 8000,
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,10 @@ package rtpvp8
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/vp8"
|
"github.com/bluenviron/mediacommon/pkg/codecs/vp8"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/pion/rtp/codecs"
|
"github.com/pion/rtp/codecs"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/VP8 decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
|
// Specification: https://datatracker.ietf.org/doc/html/rfc7741
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -42,22 +38,21 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes a VP8 frame from a RTP packet.
|
// 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
|
var vpkt codecs.VP8Packet
|
||||||
_, err := vpkt.Unmarshal(pkt.Payload)
|
_, err := vpkt.Unmarshal(pkt.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if vpkt.PID != 0 {
|
if vpkt.PID != 0 {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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
|
var frame []byte
|
||||||
@@ -69,35 +64,35 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
d.fragmentsSize = len(vpkt.Payload)
|
d.fragmentsSize = len(vpkt.Payload)
|
||||||
d.fragments = append(d.fragments, vpkt.Payload)
|
d.fragments = append(d.fragments, vpkt.Payload)
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = vpkt.Payload
|
frame = vpkt.Payload
|
||||||
} else {
|
} else {
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
if !d.firstPacketReceived {
|
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)
|
d.fragmentsSize += len(vpkt.Payload)
|
||||||
|
|
||||||
if d.fragmentsSize > vp8.MaxFrameSize {
|
if d.fragmentsSize > vp8.MaxFrameSize {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
d.fragments = append(d.fragments, vpkt.Payload)
|
||||||
|
|
||||||
if !pkt.Marker {
|
if !pkt.Marker {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = joinFragments(d.fragments, d.fragmentsSize)
|
frame = joinFragments(d.fragments, d.fragmentsSize)
|
||||||
d.fragments = d.fragments[:0]
|
d.fragments = d.fragments[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
|
|||||||
var frame []byte
|
var frame []byte
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
frame, _, err = d.Decode(pkt)
|
frame, err = d.Decode(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@@ -3,12 +3,9 @@ package rtpvp8
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/pion/rtp/codecs"
|
"github.com/pion/rtp/codecs"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,16 +36,11 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
vp codecs.VP8Payloader
|
vp codecs.VP8Payloader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,24 +61,16 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes a VP8 frame into RTP/VP8 packets.
|
// 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)
|
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame)
|
||||||
if payloads == nil {
|
if payloads == nil {
|
||||||
return nil, fmt.Errorf("payloader failed")
|
return nil, fmt.Errorf("payloader failed")
|
||||||
@@ -101,7 +85,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: i == (plen - 1),
|
Marker: i == (plen - 1),
|
||||||
},
|
},
|
||||||
|
@@ -47,7 +47,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{0x10, 0x01, 0x02, 0x03, 0x04},
|
Payload: []byte{0x10, 0x01, 0x02, 0x03, 0x04},
|
||||||
@@ -64,7 +63,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes([]byte{0x10}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02, 0x03}),
|
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,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes([]byte{0x00, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01, 0x02}),
|
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,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes([]byte{0x00, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 294)),
|
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,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.frame, 0)
|
pkts, err := e.Encode(ca.frame)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -122,5 +117,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,2 @@
|
|||||||
// Package rtpvp8 contains a RTP/VP8 decoder and encoder.
|
// Package rtpvp8 contains a RTP/VP8 decoder and encoder.
|
||||||
package rtpvp8
|
package rtpvp8
|
||||||
|
|
||||||
const (
|
|
||||||
rtpClockRate = 90000 // VP8 always uses 90khz
|
|
||||||
)
|
|
||||||
|
@@ -3,13 +3,10 @@ package rtpvp9
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/pkg/codecs/vp9"
|
"github.com/bluenviron/mediacommon/pkg/codecs/vp9"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/pion/rtp/codecs"
|
"github.com/pion/rtp/codecs"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrMorePacketsNeeded is returned when more packets are needed.
|
// 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.
|
// Decoder is a RTP/VP9 decoder.
|
||||||
// Specification: https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
|
// Specification: https://datatracker.ietf.org/doc/html/draft-ietf-payload-vp9-16
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
timeDecoder *rtptime.Decoder
|
|
||||||
firstPacketReceived bool
|
firstPacketReceived bool
|
||||||
fragmentsSize int
|
fragmentsSize int
|
||||||
fragments [][]byte
|
fragments [][]byte
|
||||||
@@ -42,17 +38,16 @@ type Decoder struct {
|
|||||||
|
|
||||||
// Init initializes the decoder.
|
// Init initializes the decoder.
|
||||||
func (d *Decoder) Init() error {
|
func (d *Decoder) Init() error {
|
||||||
d.timeDecoder = rtptime.NewDecoder(rtpClockRate)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode decodes a VP9 frame from a RTP packet.
|
// 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
|
var vpkt codecs.VP9Packet
|
||||||
_, err := vpkt.Unmarshal(pkt.Payload)
|
_, err := vpkt.Unmarshal(pkt.Payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
d.fragments = d.fragments[:0] // discard pending fragments
|
||||||
return nil, 0, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var frame []byte
|
var frame []byte
|
||||||
@@ -64,35 +59,35 @@ func (d *Decoder) Decode(pkt *rtp.Packet) ([]byte, time.Duration, error) {
|
|||||||
if !vpkt.E {
|
if !vpkt.E {
|
||||||
d.fragmentsSize = len(vpkt.Payload)
|
d.fragmentsSize = len(vpkt.Payload)
|
||||||
d.fragments = append(d.fragments, vpkt.Payload)
|
d.fragments = append(d.fragments, vpkt.Payload)
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = vpkt.Payload
|
frame = vpkt.Payload
|
||||||
} else {
|
} else {
|
||||||
if len(d.fragments) == 0 {
|
if len(d.fragments) == 0 {
|
||||||
if !d.firstPacketReceived {
|
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)
|
d.fragmentsSize += len(vpkt.Payload)
|
||||||
|
|
||||||
if d.fragmentsSize > vp9.MaxFrameSize {
|
if d.fragmentsSize > vp9.MaxFrameSize {
|
||||||
d.fragments = d.fragments[:0] // discard pending fragments
|
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)
|
d.fragments = append(d.fragments, vpkt.Payload)
|
||||||
|
|
||||||
if !vpkt.E {
|
if !vpkt.E {
|
||||||
return nil, 0, ErrMorePacketsNeeded
|
return nil, ErrMorePacketsNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = joinFragments(d.fragments, d.fragmentsSize)
|
frame = joinFragments(d.fragments, d.fragmentsSize)
|
||||||
d.fragments = d.fragments[:0]
|
d.fragments = d.fragments[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return frame, d.timeDecoder.Decode(pkt.Timestamp), nil
|
return frame, nil
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ func TestDecode(t *testing.T) {
|
|||||||
var frame []byte
|
var frame []byte
|
||||||
|
|
||||||
for _, pkt := range ca.pkts {
|
for _, pkt := range ca.pkts {
|
||||||
frame, _, err = d.Decode(pkt)
|
frame, err = d.Decode(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@@ -3,12 +3,9 @@ package rtpvp9
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
"github.com/pion/rtp/codecs"
|
"github.com/pion/rtp/codecs"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/rtptime"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,10 +36,6 @@ type Encoder struct {
|
|||||||
// It defaults to a random value.
|
// It defaults to a random value.
|
||||||
InitialSequenceNumber *uint16
|
InitialSequenceNumber *uint16
|
||||||
|
|
||||||
// initial timestamp of packets (optional).
|
|
||||||
// It defaults to a random value.
|
|
||||||
InitialTimestamp *uint32
|
|
||||||
|
|
||||||
// maximum size of packet payloads (optional).
|
// maximum size of packet payloads (optional).
|
||||||
// It defaults to 1460.
|
// It defaults to 1460.
|
||||||
PayloadMaxSize int
|
PayloadMaxSize int
|
||||||
@@ -52,7 +45,6 @@ type Encoder struct {
|
|||||||
InitialPictureID *uint16
|
InitialPictureID *uint16
|
||||||
|
|
||||||
sequenceNumber uint16
|
sequenceNumber uint16
|
||||||
timeEncoder *rtptime.Encoder
|
|
||||||
vp codecs.VP9Payloader
|
vp codecs.VP9Payloader
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +65,6 @@ func (e *Encoder) Init() error {
|
|||||||
v2 := uint16(v)
|
v2 := uint16(v)
|
||||||
e.InitialSequenceNumber = &v2
|
e.InitialSequenceNumber = &v2
|
||||||
}
|
}
|
||||||
if e.InitialTimestamp == nil {
|
|
||||||
v, err := randUint32()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.InitialTimestamp = &v
|
|
||||||
}
|
|
||||||
if e.PayloadMaxSize == 0 {
|
if e.PayloadMaxSize == 0 {
|
||||||
e.PayloadMaxSize = defaultPayloadMaxSize
|
e.PayloadMaxSize = defaultPayloadMaxSize
|
||||||
}
|
}
|
||||||
@@ -93,7 +78,6 @@ func (e *Encoder) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
e.sequenceNumber = *e.InitialSequenceNumber
|
e.sequenceNumber = *e.InitialSequenceNumber
|
||||||
e.timeEncoder = rtptime.NewEncoder(rtpClockRate, *e.InitialTimestamp)
|
|
||||||
|
|
||||||
e.vp.InitialPictureIDFn = func() uint16 {
|
e.vp.InitialPictureIDFn = func() uint16 {
|
||||||
return *e.InitialPictureID
|
return *e.InitialPictureID
|
||||||
@@ -103,7 +87,7 @@ func (e *Encoder) Init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes a VP9 frame into RTP/VP9 packets.
|
// 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)
|
payloads := e.vp.Payload(uint16(e.PayloadMaxSize), frame)
|
||||||
if payloads == nil {
|
if payloads == nil {
|
||||||
return nil, fmt.Errorf("payloader failed")
|
return nil, fmt.Errorf("payloader failed")
|
||||||
@@ -118,7 +102,6 @@ func (e *Encoder) Encode(frame []byte, pts time.Duration) ([]*rtp.Packet, error)
|
|||||||
Version: rtpVersion,
|
Version: rtpVersion,
|
||||||
PayloadType: e.PayloadType,
|
PayloadType: e.PayloadType,
|
||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.timeEncoder.Encode(pts),
|
|
||||||
SSRC: *e.SSRC,
|
SSRC: *e.SSRC,
|
||||||
Marker: i == (plen - 1),
|
Marker: i == (plen - 1),
|
||||||
},
|
},
|
||||||
|
@@ -47,7 +47,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: []byte{0x9c, 0xb5, 0xaf, 0x01, 0x02, 0x03, 0x04},
|
Payload: []byte{0x9c, 0xb5, 0xaf, 0x01, 0x02, 0x03, 0x04},
|
||||||
@@ -64,7 +63,6 @@ var cases = []struct {
|
|||||||
Marker: false,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17645,
|
SequenceNumber: 17645,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes([]byte{0x98, 0xb5, 0xaf}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 364), []byte{0x01}),
|
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,
|
Marker: false,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17646,
|
SequenceNumber: 17646,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes([]byte{0x90, 0xb5, 0xaf, 0x02, 0x03, 0x04},
|
Payload: mergeBytes([]byte{0x90, 0xb5, 0xaf, 0x02, 0x03, 0x04},
|
||||||
@@ -87,7 +84,6 @@ var cases = []struct {
|
|||||||
Marker: true,
|
Marker: true,
|
||||||
PayloadType: 96,
|
PayloadType: 96,
|
||||||
SequenceNumber: 17647,
|
SequenceNumber: 17647,
|
||||||
Timestamp: 2289526357,
|
|
||||||
SSRC: 0x9dbb7812,
|
SSRC: 0x9dbb7812,
|
||||||
},
|
},
|
||||||
Payload: mergeBytes([]byte{0x94, 0xb5, 0xaf, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 295)),
|
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,
|
PayloadType: 96,
|
||||||
SSRC: uint32Ptr(0x9dbb7812),
|
SSRC: uint32Ptr(0x9dbb7812),
|
||||||
InitialSequenceNumber: uint16Ptr(0x44ed),
|
InitialSequenceNumber: uint16Ptr(0x44ed),
|
||||||
InitialTimestamp: uint32Ptr(0x88776655),
|
|
||||||
InitialPictureID: uint16Ptr(0x35af),
|
InitialPictureID: uint16Ptr(0x35af),
|
||||||
}
|
}
|
||||||
err := e.Init()
|
err := e.Init()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pkts, err := e.Encode(ca.frame, 0)
|
pkts, err := e.Encode(ca.frame)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.pkts, pkts)
|
require.Equal(t, ca.pkts, pkts)
|
||||||
})
|
})
|
||||||
@@ -124,5 +119,4 @@ func TestEncodeRandomInitialState(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotEqual(t, nil, e.SSRC)
|
require.NotEqual(t, nil, e.SSRC)
|
||||||
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
require.NotEqual(t, nil, e.InitialSequenceNumber)
|
||||||
require.NotEqual(t, nil, e.InitialTimestamp)
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,2 @@
|
|||||||
// Package rtpvp9 contains a RTP/VP9 decoder and encoder.
|
// Package rtpvp9 contains a RTP/VP9 decoder and encoder.
|
||||||
package rtpvp9
|
package rtpvp9
|
||||||
|
|
||||||
const (
|
|
||||||
rtpClockRate = 90000 // VP9 always uses 90khz
|
|
||||||
)
|
|
||||||
|
@@ -22,14 +22,14 @@ func TestVP8DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -22,14 +22,14 @@ func TestVP9DecEncoder(t *testing.T) {
|
|||||||
enc, err := format.CreateEncoder()
|
enc, err := format.CreateEncoder()
|
||||||
require.NoError(t, err)
|
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.NoError(t, err)
|
||||||
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
require.Equal(t, format.PayloadType(), pkts[0].PayloadType)
|
||||||
|
|
||||||
dec, err := format.CreateDecoder()
|
dec, err := format.CreateDecoder()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
byts, _, err := dec.Decode(pkts[0])
|
byts, err := dec.Decode(pkts[0])
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, byts)
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
|
||||||
}
|
|
@@ -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)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}
|
|
115
pkg/rtptime/global_decoder.go
Normal file
115
pkg/rtptime/global_decoder.go
Normal 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
|
||||||
|
}
|
140
pkg/rtptime/global_decoder_test.go
Normal file
140
pkg/rtptime/global_decoder_test.go
Normal 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
2
pkg/rtptime/rtptime.go
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Package rtptime contains a time decoder and encoder.
|
||||||
|
package rtptime
|
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/media"
|
"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/sdp"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/url"
|
"github.com/bluenviron/gortsplib/v4/pkg/url"
|
||||||
)
|
)
|
||||||
@@ -156,6 +157,7 @@ type ServerSession struct {
|
|||||||
udpLastPacketTime *int64 // publish
|
udpLastPacketTime *int64 // publish
|
||||||
udpCheckStreamTimer *time.Timer
|
udpCheckStreamTimer *time.Timer
|
||||||
writer asyncProcessor
|
writer asyncProcessor
|
||||||
|
timeDecoder *rtptime.GlobalDecoder
|
||||||
|
|
||||||
// in
|
// in
|
||||||
chHandleRequest chan sessionRequestReq
|
chHandleRequest chan sessionRequestReq
|
||||||
@@ -876,6 +878,8 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
v := ss.s.timeNow().Unix()
|
v := ss.s.timeNow().Unix()
|
||||||
ss.udpLastPacketTime = &v
|
ss.udpLastPacketTime = &v
|
||||||
|
|
||||||
|
ss.timeDecoder = rtptime.NewGlobalDecoder()
|
||||||
|
|
||||||
for _, sm := range ss.setuppedMedias {
|
for _, sm := range ss.setuppedMedias {
|
||||||
sm.start()
|
sm.start()
|
||||||
}
|
}
|
||||||
@@ -968,6 +972,8 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
v := ss.s.timeNow().Unix()
|
v := ss.s.timeNow().Unix()
|
||||||
ss.udpLastPacketTime = &v
|
ss.udpLastPacketTime = &v
|
||||||
|
|
||||||
|
ss.timeDecoder = rtptime.NewGlobalDecoder()
|
||||||
|
|
||||||
for _, sm := range ss.setuppedMedias {
|
for _, sm := range ss.setuppedMedias {
|
||||||
sm.start()
|
sm.start()
|
||||||
}
|
}
|
||||||
@@ -1020,6 +1026,8 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
sm.stop()
|
sm.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ss.timeDecoder = nil
|
||||||
|
|
||||||
switch ss.state {
|
switch ss.state {
|
||||||
case ServerSessionStatePlay:
|
case ServerSessionStatePlay:
|
||||||
ss.state = ServerSessionStatePrePlay
|
ss.state = ServerSessionStatePrePlay
|
||||||
@@ -1186,6 +1194,12 @@ func (ss *ServerSession) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) err
|
|||||||
return nil
|
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.
|
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
|
||||||
// The NTP timestamp is computed from sender reports.
|
// The NTP timestamp is computed from sender reports.
|
||||||
func (ss *ServerSession) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
|
func (ss *ServerSession) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
|
||||||
|
Reference in New Issue
Block a user