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