Skip to content

Commit

Permalink
use native timestamps instead of time.Duration
Browse files Browse the repository at this point in the history
this improves timestamp precision
  • Loading branch information
aler9 committed Oct 7, 2024
1 parent 5ec470c commit 35a32bc
Show file tree
Hide file tree
Showing 33 changed files with 220 additions and 109 deletions.
12 changes: 12 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ type Client struct {
writer asyncProcessor
reader *clientReader
timeDecoder *rtptime.GlobalDecoder
timeDecoder2 *rtptime.GlobalDecoder2
mustClose bool

// in
Expand Down Expand Up @@ -799,6 +800,7 @@ func (c *Client) startReadRoutines() {
}

c.timeDecoder = rtptime.NewGlobalDecoder()
c.timeDecoder2 = rtptime.NewGlobalDecoder2()

for _, cm := range c.medias {
cm.start()
Expand Down Expand Up @@ -1879,12 +1881,22 @@ func (c *Client) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error

// PacketPTS returns the PTS of an incoming RTP packet.
// It is computed by decoding the packet timestamp and sychronizing it with other tracks.
//
// Deprecated: replaced by PacketPTS2.
func (c *Client) PacketPTS(medi *description.Media, pkt *rtp.Packet) (time.Duration, bool) {
cm := c.medias[medi]
ct := cm.formats[pkt.PayloadType]
return c.timeDecoder.Decode(ct.format, pkt)
}

// 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) PacketPTS2(medi *description.Media, pkt *rtp.Packet) (int64, bool) {
cm := c.medias[medi]
ct := cm.formats[pkt.PayloadType]
return c.timeDecoder2.Decode(ct.format, pkt)
}

// PacketNTP returns the NTP timestamp of an incoming RTP packet.
// The NTP timestamp is computed from RTCP sender reports.
func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) {
Expand Down
2 changes: 1 addition & 1 deletion examples/client-play-format-av1/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
2 changes: 1 addition & 1 deletion examples/client-play-format-g711/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
2 changes: 1 addition & 1 deletion examples/client-play-format-g722/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func main() {
// called when a H264/RTP packet arrives
c.OnPacketRTP(h264Media, h264Format, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(h264Media, pkt)
pts, ok := c.PacketPTS2(h264Media, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down Expand Up @@ -112,7 +112,7 @@ func main() {
// called when a MPEG-4 audio / RTP packet arrives
c.OnPacketRTP(mpeg4AudioMedia, mpeg4AudioFormat, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(mpeg4AudioMedia, pkt)
pts, ok := c.PacketPTS2(mpeg4AudioMedia, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import (
"bufio"
"os"
"sync"
"time"

"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
)

func durationGoToMPEGTS(v time.Duration) int64 {
return int64(v.Seconds() * 90000)
func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d
dec := v % d
return (secs*m + dec*m/d)
}

// mpegtsMuxer allows to save a H264 / MPEG-4 audio stream into a MPEG-TS file.
Expand All @@ -26,7 +27,7 @@ type mpegtsMuxer struct {
w *mpegts.Writer
h264Track *mpegts.Track
mpeg4AudioTrack *mpegts.Track
dtsExtractor *h264.DTSExtractor
dtsExtractor *h264.DTSExtractor2
mutex sync.Mutex
}

Expand Down Expand Up @@ -61,7 +62,7 @@ func (e *mpegtsMuxer) close() {
}

// writeH264 writes a H264 access unit into MPEG-TS.
func (e *mpegtsMuxer) writeH264(au [][]byte, pts time.Duration) error {
func (e *mpegtsMuxer) writeH264(au [][]byte, pts int64) error {
e.mutex.Lock()
defer e.mutex.Unlock()

Expand Down Expand Up @@ -105,30 +106,27 @@ func (e *mpegtsMuxer) writeH264(au [][]byte, pts time.Duration) error {
au = append([][]byte{e.h264Format.SPS, e.h264Format.PPS}, au...)
}

var dts time.Duration

if e.dtsExtractor == nil {
// skip samples silently until we find one with a IDR
if !idrPresent {
return nil
}
e.dtsExtractor = h264.NewDTSExtractor()
e.dtsExtractor = h264.NewDTSExtractor2()
}

var err error
dts, err = e.dtsExtractor.Extract(au, pts)
dts, err := e.dtsExtractor.Extract(au, pts)
if err != nil {
return err
}

// encode into MPEG-TS
return e.w.WriteH264(e.h264Track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
return e.w.WriteH264(e.h264Track, pts, dts, idrPresent, au)
}

// writeMPEG4Audio writes MPEG-4 audio access units into MPEG-TS.
func (e *mpegtsMuxer) writeMPEG4Audio(aus [][]byte, pts time.Duration) error {
func (e *mpegtsMuxer) writeMPEG4Audio(aus [][]byte, pts int64) error {
e.mutex.Lock()
defer e.mutex.Unlock()

return e.w.WriteMPEG4Audio(e.mpeg4AudioTrack, durationGoToMPEGTS(pts), aus)
return e.w.WriteMPEG4Audio(e.mpeg4AudioTrack, multiplyAndDivide(pts, 90000, int64(e.mpeg4AudioFormat.ClockRate())), aus)
}
2 changes: 1 addition & 1 deletion examples/client-play-format-h264-save-to-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
18 changes: 5 additions & 13 deletions examples/client-play-format-h264-save-to-disk/mpegts_muxer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ package main
import (
"bufio"
"os"
"time"

"github.com/bluenviron/mediacommon/pkg/codecs/h264"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
)

func durationGoToMPEGTS(v time.Duration) int64 {
return int64(v.Seconds() * 90000)
}

// mpegtsMuxer allows to save a H264 stream into a MPEG-TS file.
type mpegtsMuxer struct {
fileName string
Expand All @@ -23,7 +18,7 @@ type mpegtsMuxer struct {
b *bufio.Writer
w *mpegts.Writer
track *mpegts.Track
dtsExtractor *h264.DTSExtractor
dtsExtractor *h264.DTSExtractor2
}

// initialize initializes a mpegtsMuxer.
Expand Down Expand Up @@ -51,7 +46,7 @@ func (e *mpegtsMuxer) close() {
}

// writeH264 writes a H264 access unit into MPEG-TS.
func (e *mpegtsMuxer) writeH264(au [][]byte, pts time.Duration) error {
func (e *mpegtsMuxer) writeH264(au [][]byte, pts int64) error {
var filteredAU [][]byte

nonIDRPresent := false
Expand Down Expand Up @@ -92,22 +87,19 @@ func (e *mpegtsMuxer) writeH264(au [][]byte, pts time.Duration) error {
au = append([][]byte{e.sps, e.pps}, au...)
}

var dts time.Duration

if e.dtsExtractor == nil {
// skip samples silently until we find one with a IDR
if !idrPresent {
return nil
}
e.dtsExtractor = h264.NewDTSExtractor()
e.dtsExtractor = h264.NewDTSExtractor2()
}

var err error
dts, err = e.dtsExtractor.Extract(au, pts)
dts, err := e.dtsExtractor.Extract(au, pts)
if err != nil {
return err
}

// encode into MPEG-TS
return e.w.WriteH264(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
return e.w.WriteH264(e.track, pts, dts, idrPresent, au)
}
2 changes: 1 addition & 1 deletion examples/client-play-format-h264/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
2 changes: 1 addition & 1 deletion examples/client-play-format-h265-save-to-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
18 changes: 5 additions & 13 deletions examples/client-play-format-h265-save-to-disk/mpegts_muxer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ package main
import (
"bufio"
"os"
"time"

"github.com/bluenviron/mediacommon/pkg/codecs/h265"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
)

func durationGoToMPEGTS(v time.Duration) int64 {
return int64(v.Seconds() * 90000)
}

// mpegtsMuxer allows to save a H265 stream into a MPEG-TS file.
type mpegtsMuxer struct {
fileName string
Expand All @@ -24,7 +19,7 @@ type mpegtsMuxer struct {
b *bufio.Writer
w *mpegts.Writer
track *mpegts.Track
dtsExtractor *h265.DTSExtractor
dtsExtractor *h265.DTSExtractor2
}

// initialize initializes a mpegtsMuxer.
Expand Down Expand Up @@ -52,7 +47,7 @@ func (e *mpegtsMuxer) close() {
}

// writeH265 writes a H265 access unit into MPEG-TS.
func (e *mpegtsMuxer) writeH265(au [][]byte, pts time.Duration) error {
func (e *mpegtsMuxer) writeH265(au [][]byte, pts int64) error {
var filteredAU [][]byte

isRandomAccess := false
Expand Down Expand Up @@ -93,22 +88,19 @@ func (e *mpegtsMuxer) writeH265(au [][]byte, pts time.Duration) error {
au = append([][]byte{e.vps, e.sps, e.pps}, au...)
}

var dts time.Duration

if e.dtsExtractor == nil {
// skip samples silently until we find one with a IDR
if !isRandomAccess {
return nil
}
e.dtsExtractor = h265.NewDTSExtractor()
e.dtsExtractor = h265.NewDTSExtractor2()
}

var err error
dts, err = e.dtsExtractor.Extract(au, pts)
dts, err := e.dtsExtractor.Extract(au, pts)
if err != nil {
return err
}

// encode into MPEG-TS
return e.w.WriteH265(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), isRandomAccess, au)
return e.w.WriteH265(e.track, pts, dts, isRandomAccess, au)
}
2 changes: 1 addition & 1 deletion examples/client-play-format-h265/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
2 changes: 1 addition & 1 deletion examples/client-play-format-lpcm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
2 changes: 1 addition & 1 deletion examples/client-play-format-mjpeg/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
3 changes: 2 additions & 1 deletion examples/client-play-format-mpeg4audio-save-to-disk/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func main() {
// setup MPEG-4 audio -> MPEG-TS muxer
mpegtsMuxer := &mpegtsMuxer{
fileName: "mystream.ts",
format: forma,
track: &mpegts.Track{
Codec: &mpegts.CodecMPEG4Audio{
Config: *forma.Config,
Expand All @@ -74,7 +75,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ package main
import (
"bufio"
"os"
"time"

"github.com/bluenviron/gortsplib/v4/pkg/format"
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
)

func durationGoToMPEGTS(v time.Duration) int64 {
return int64(v.Seconds() * 90000)
func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d
dec := v % d
return (secs*m + dec*m/d)
}

// mpegtsMuxer allows to save a MPEG-4 audio stream into a MPEG-TS file.
type mpegtsMuxer struct {
fileName string
format format.Format
track *mpegts.Track

f *os.File
Expand Down Expand Up @@ -43,6 +46,6 @@ func (e *mpegtsMuxer) close() {
}

// writeMPEG4Audio writes MPEG-4 audio access units into MPEG-TS.
func (e *mpegtsMuxer) writeMPEG4Audio(aus [][]byte, pts time.Duration) error {
return e.w.WriteMPEG4Audio(e.track, durationGoToMPEGTS(pts), aus)
func (e *mpegtsMuxer) writeMPEG4Audio(aus [][]byte, pts int64) error {
return e.w.WriteMPEG4Audio(e.track, multiplyAndDivide(pts, 90000, int64(e.format.ClockRate())), aus)
}
2 changes: 1 addition & 1 deletion examples/client-play-format-mpeg4audio/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func main() {
// called when a RTP packet arrives
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
// decode timestamp
pts, ok := c.PacketPTS(medi, pkt)
pts, ok := c.PacketPTS2(medi, pkt)
if !ok {
log.Printf("waiting for timestamp")
return
Expand Down
Loading

0 comments on commit 35a32bc

Please sign in to comment.