Skip to content

Commit 1403bd0

Browse files
committed
use native timestamps instead of time.Duration
this improves timestamp precision
1 parent 5ec470c commit 1403bd0

File tree

33 files changed

+219
-109
lines changed

33 files changed

+219
-109
lines changed

client.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ type Client struct {
317317
writer asyncProcessor
318318
reader *clientReader
319319
timeDecoder *rtptime.GlobalDecoder
320+
timeDecoder2 *rtptime.GlobalDecoder2
320321
mustClose bool
321322

322323
// in
@@ -799,6 +800,7 @@ func (c *Client) startReadRoutines() {
799800
}
800801

801802
c.timeDecoder = rtptime.NewGlobalDecoder()
803+
c.timeDecoder2 = rtptime.NewGlobalDecoder2()
802804

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

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

1892+
// PacketPTS returns the PTS of an incoming RTP packet.
1893+
// It is computed by decoding the packet timestamp and sychronizing it with other tracks.
1894+
func (c *Client) PacketPTS2(medi *description.Media, pkt *rtp.Packet) (int64, bool) {
1895+
cm := c.medias[medi]
1896+
ct := cm.formats[pkt.PayloadType]
1897+
return c.timeDecoder2.Decode(ct.format, pkt)
1898+
}
1899+
18881900
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
18891901
// The NTP timestamp is computed from RTCP sender reports.
18901902
func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) {

examples/client-play-format-av1/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func main() {
5959
// called when a RTP packet arrives
6060
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
6161
// decode timestamp
62-
pts, ok := c.PacketPTS(medi, pkt)
62+
pts, ok := c.PacketPTS2(medi, pkt)
6363
if !ok {
6464
log.Printf("waiting for timestamp")
6565
return

examples/client-play-format-g711/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func main() {
5858
// called when a RTP packet arrives
5959
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
6060
// decode timestamp
61-
pts, ok := c.PacketPTS(medi, pkt)
61+
pts, ok := c.PacketPTS2(medi, pkt)
6262
if !ok {
6363
log.Printf("waiting for timestamp")
6464
return

examples/client-play-format-g722/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func main() {
5858
// called when a RTP packet arrives
5959
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
6060
// decode timestamp
61-
pts, ok := c.PacketPTS(medi, pkt)
61+
pts, ok := c.PacketPTS2(medi, pkt)
6262
if !ok {
6363
log.Printf("waiting for timestamp")
6464
return

examples/client-play-format-h264-mpeg4audio-save-to-disk/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func main() {
8484
// called when a H264/RTP packet arrives
8585
c.OnPacketRTP(h264Media, h264Format, func(pkt *rtp.Packet) {
8686
// decode timestamp
87-
pts, ok := c.PacketPTS(h264Media, pkt)
87+
pts, ok := c.PacketPTS2(h264Media, pkt)
8888
if !ok {
8989
log.Printf("waiting for timestamp")
9090
return
@@ -112,7 +112,7 @@ func main() {
112112
// called when a MPEG-4 audio / RTP packet arrives
113113
c.OnPacketRTP(mpeg4AudioMedia, mpeg4AudioFormat, func(pkt *rtp.Packet) {
114114
// decode timestamp
115-
pts, ok := c.PacketPTS(mpeg4AudioMedia, pkt)
115+
pts, ok := c.PacketPTS2(mpeg4AudioMedia, pkt)
116116
if !ok {
117117
log.Printf("waiting for timestamp")
118118
return

examples/client-play-format-h264-mpeg4audio-save-to-disk/mpegts_muxer.go

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ import (
44
"bufio"
55
"os"
66
"sync"
7-
"time"
87

98
"github.com/bluenviron/gortsplib/v4/pkg/format"
109
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
1110
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
1211
)
1312

14-
func durationGoToMPEGTS(v time.Duration) int64 {
15-
return int64(v.Seconds() * 90000)
13+
func multiplyAndDivide(v, m, d int64) int64 {
14+
secs := v / d
15+
dec := v % d
16+
return (secs*m + dec*m/d)
1617
}
1718

1819
// mpegtsMuxer allows to save a H264 / MPEG-4 audio stream into a MPEG-TS file.
@@ -26,7 +27,7 @@ type mpegtsMuxer struct {
2627
w *mpegts.Writer
2728
h264Track *mpegts.Track
2829
mpeg4AudioTrack *mpegts.Track
29-
dtsExtractor *h264.DTSExtractor
30+
dtsExtractor *h264.DTSExtractor2
3031
mutex sync.Mutex
3132
}
3233

@@ -61,7 +62,7 @@ func (e *mpegtsMuxer) close() {
6162
}
6263

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

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

108-
var dts time.Duration
109-
110109
if e.dtsExtractor == nil {
111110
// skip samples silently until we find one with a IDR
112111
if !idrPresent {
113112
return nil
114113
}
115-
e.dtsExtractor = h264.NewDTSExtractor()
114+
e.dtsExtractor = h264.NewDTSExtractor2()
116115
}
117116

118-
var err error
119-
dts, err = e.dtsExtractor.Extract(au, pts)
117+
dts, err := e.dtsExtractor.Extract(au, pts)
120118
if err != nil {
121119
return err
122120
}
123121

124122
// encode into MPEG-TS
125-
return e.w.WriteH264(e.h264Track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
123+
return e.w.WriteH264(e.h264Track, pts, dts, idrPresent, au)
126124
}
127125

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

133-
return e.w.WriteMPEG4Audio(e.mpeg4AudioTrack, durationGoToMPEGTS(pts), aus)
131+
return e.w.WriteMPEG4Audio(e.mpeg4AudioTrack, multiplyAndDivide(pts, 90000, int64(e.mpeg4AudioFormat.ClockRate())), aus)
134132
}

examples/client-play-format-h264-save-to-disk/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func main() {
7171
// called when a RTP packet arrives
7272
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
7373
// decode timestamp
74-
pts, ok := c.PacketPTS(medi, pkt)
74+
pts, ok := c.PacketPTS2(medi, pkt)
7575
if !ok {
7676
log.Printf("waiting for timestamp")
7777
return

examples/client-play-format-h264-save-to-disk/mpegts_muxer.go

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,11 @@ package main
33
import (
44
"bufio"
55
"os"
6-
"time"
76

87
"github.com/bluenviron/mediacommon/pkg/codecs/h264"
98
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
109
)
1110

12-
func durationGoToMPEGTS(v time.Duration) int64 {
13-
return int64(v.Seconds() * 90000)
14-
}
15-
1611
// mpegtsMuxer allows to save a H264 stream into a MPEG-TS file.
1712
type mpegtsMuxer struct {
1813
fileName string
@@ -23,7 +18,7 @@ type mpegtsMuxer struct {
2318
b *bufio.Writer
2419
w *mpegts.Writer
2520
track *mpegts.Track
26-
dtsExtractor *h264.DTSExtractor
21+
dtsExtractor *h264.DTSExtractor2
2722
}
2823

2924
// initialize initializes a mpegtsMuxer.
@@ -51,7 +46,7 @@ func (e *mpegtsMuxer) close() {
5146
}
5247

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

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

95-
var dts time.Duration
96-
9790
if e.dtsExtractor == nil {
9891
// skip samples silently until we find one with a IDR
9992
if !idrPresent {
10093
return nil
10194
}
102-
e.dtsExtractor = h264.NewDTSExtractor()
95+
e.dtsExtractor = h264.NewDTSExtractor2()
10396
}
10497

105-
var err error
106-
dts, err = e.dtsExtractor.Extract(au, pts)
98+
dts, err := e.dtsExtractor.Extract(au, pts)
10799
if err != nil {
108100
return err
109101
}
110102

111103
// encode into MPEG-TS
112-
return e.w.WriteH264(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), idrPresent, au)
104+
return e.w.WriteH264(e.track, pts, dts, idrPresent, au)
113105
}

examples/client-play-format-h264/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func main() {
7878
// called when a RTP packet arrives
7979
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
8080
// decode timestamp
81-
pts, ok := c.PacketPTS(medi, pkt)
81+
pts, ok := c.PacketPTS2(medi, pkt)
8282
if !ok {
8383
log.Printf("waiting for timestamp")
8484
return

examples/client-play-format-h265-save-to-disk/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func main() {
7272
// called when a RTP packet arrives
7373
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
7474
// decode timestamp
75-
pts, ok := c.PacketPTS(medi, pkt)
75+
pts, ok := c.PacketPTS2(medi, pkt)
7676
if !ok {
7777
log.Printf("waiting for timestamp")
7878
return

examples/client-play-format-h265-save-to-disk/mpegts_muxer.go

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,11 @@ package main
33
import (
44
"bufio"
55
"os"
6-
"time"
76

87
"github.com/bluenviron/mediacommon/pkg/codecs/h265"
98
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
109
)
1110

12-
func durationGoToMPEGTS(v time.Duration) int64 {
13-
return int64(v.Seconds() * 90000)
14-
}
15-
1611
// mpegtsMuxer allows to save a H265 stream into a MPEG-TS file.
1712
type mpegtsMuxer struct {
1813
fileName string
@@ -24,7 +19,7 @@ type mpegtsMuxer struct {
2419
b *bufio.Writer
2520
w *mpegts.Writer
2621
track *mpegts.Track
27-
dtsExtractor *h265.DTSExtractor
22+
dtsExtractor *h265.DTSExtractor2
2823
}
2924

3025
// initialize initializes a mpegtsMuxer.
@@ -52,7 +47,7 @@ func (e *mpegtsMuxer) close() {
5247
}
5348

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

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

96-
var dts time.Duration
97-
9891
if e.dtsExtractor == nil {
9992
// skip samples silently until we find one with a IDR
10093
if !isRandomAccess {
10194
return nil
10295
}
103-
e.dtsExtractor = h265.NewDTSExtractor()
96+
e.dtsExtractor = h265.NewDTSExtractor2()
10497
}
10598

106-
var err error
107-
dts, err = e.dtsExtractor.Extract(au, pts)
99+
dts, err := e.dtsExtractor.Extract(au, pts)
108100
if err != nil {
109101
return err
110102
}
111103

112104
// encode into MPEG-TS
113-
return e.w.WriteH265(e.track, durationGoToMPEGTS(pts), durationGoToMPEGTS(dts), isRandomAccess, au)
105+
return e.w.WriteH265(e.track, pts, dts, isRandomAccess, au)
114106
}

examples/client-play-format-h265/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func main() {
8181
// called when a RTP packet arrives
8282
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
8383
// decode timestamp
84-
pts, ok := c.PacketPTS(medi, pkt)
84+
pts, ok := c.PacketPTS2(medi, pkt)
8585
if !ok {
8686
log.Printf("waiting for timestamp")
8787
return

examples/client-play-format-lpcm/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func main() {
5858
// called when a RTP packet arrives
5959
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
6060
// decode timestamp
61-
pts, ok := c.PacketPTS(medi, pkt)
61+
pts, ok := c.PacketPTS2(medi, pkt)
6262
if !ok {
6363
log.Printf("waiting for timestamp")
6464
return

examples/client-play-format-mjpeg/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func main() {
6262
// called when a RTP packet arrives
6363
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
6464
// decode timestamp
65-
pts, ok := c.PacketPTS(medi, pkt)
65+
pts, ok := c.PacketPTS2(medi, pkt)
6666
if !ok {
6767
log.Printf("waiting for timestamp")
6868
return

examples/client-play-format-mpeg4audio-save-to-disk/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func main() {
5353
// setup MPEG-4 audio -> MPEG-TS muxer
5454
mpegtsMuxer := &mpegtsMuxer{
5555
fileName: "mystream.ts",
56+
format: forma,
5657
track: &mpegts.Track{
5758
Codec: &mpegts.CodecMPEG4Audio{
5859
Config: *forma.Config,
@@ -74,7 +75,7 @@ func main() {
7475
// called when a RTP packet arrives
7576
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
7677
// decode timestamp
77-
pts, ok := c.PacketPTS(medi, pkt)
78+
pts, ok := c.PacketPTS2(medi, pkt)
7879
if !ok {
7980
log.Printf("waiting for timestamp")
8081
return

examples/client-play-format-mpeg4audio-save-to-disk/mpegts_muxer.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@ package main
33
import (
44
"bufio"
55
"os"
6-
"time"
76

7+
"github.com/bluenviron/gortsplib/v4/pkg/format"
88
"github.com/bluenviron/mediacommon/pkg/formats/mpegts"
99
)
1010

11-
func durationGoToMPEGTS(v time.Duration) int64 {
12-
return int64(v.Seconds() * 90000)
11+
func multiplyAndDivide(v, m, d int64) int64 {
12+
secs := v / d
13+
dec := v % d
14+
return (secs*m + dec*m/d)
1315
}
1416

1517
// mpegtsMuxer allows to save a MPEG-4 audio stream into a MPEG-TS file.
1618
type mpegtsMuxer struct {
1719
fileName string
20+
format format.Format
1821
track *mpegts.Track
1922

2023
f *os.File
@@ -43,6 +46,6 @@ func (e *mpegtsMuxer) close() {
4346
}
4447

4548
// writeMPEG4Audio writes MPEG-4 audio access units into MPEG-TS.
46-
func (e *mpegtsMuxer) writeMPEG4Audio(aus [][]byte, pts time.Duration) error {
47-
return e.w.WriteMPEG4Audio(e.track, durationGoToMPEGTS(pts), aus)
49+
func (e *mpegtsMuxer) writeMPEG4Audio(aus [][]byte, pts int64) error {
50+
return e.w.WriteMPEG4Audio(e.track, multiplyAndDivide(pts, 90000, int64(e.format.ClockRate())), aus)
4851
}

examples/client-play-format-mpeg4audio/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func main() {
5858
// called when a RTP packet arrives
5959
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
6060
// decode timestamp
61-
pts, ok := c.PacketPTS(medi, pkt)
61+
pts, ok := c.PacketPTS2(medi, pkt)
6262
if !ok {
6363
log.Printf("waiting for timestamp")
6464
return

0 commit comments

Comments
 (0)