Skip to content

Commit a05da3a

Browse files
authored
rpi: route original absolute timestamp of packets (#1300) (#4382)
1 parent 8b98c02 commit a05da3a

File tree

4 files changed

+46
-21
lines changed

4 files changed

+46
-21
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1709,10 +1709,11 @@ The command inserted into `runOnDemand` will start only when a client requests t
17091709

17101710
### Route absolute timestamps
17111711

1712-
Some streaming protocols allow to route absolute timestamps, associated with each frame, that are useful for synchronizing several video or data streams together. In particular, _MediaMTX_ supports receiving absolute timestamps with the following protocols:
1712+
Some streaming protocols allow to route absolute timestamps, associated with each frame, that are useful for synchronizing several video or data streams together. In particular, _MediaMTX_ supports receiving absolute timestamps with the following protocols and devices:
17131713

17141714
* HLS (through the `EXT-X-PROGRAM-DATE-TIME` tag in playlists)
17151715
* RTSP (through RTCP reports, when `rtspAbsoluteTimestamp` is `true` in settings)
1716+
* Raspberry Pi Camera
17161717

17171718
and supports sending absolute timestamps with the following protocols:
17181719

internal/staticsources/rpicamera/camera.go

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,34 @@ import (
88
"os/exec"
99
"path/filepath"
1010
"strconv"
11+
"syscall"
1112
"time"
13+
"unsafe"
1214

1315
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
1416
)
1517

18+
func ntpTime() syscall.Timespec {
19+
var t syscall.Timespec
20+
syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 0, uintptr(unsafe.Pointer(&t)), 0)
21+
return t
22+
}
23+
24+
func monotonicTime() syscall.Timespec {
25+
var t syscall.Timespec
26+
syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 1, uintptr(unsafe.Pointer(&t)), 0)
27+
return t
28+
}
29+
30+
func multiplyAndDivide(v, m, d int64) int64 {
31+
secs := v / d
32+
dec := v % d
33+
return (secs*m + dec*m/d)
34+
}
35+
1636
type camera struct {
17-
Params params
18-
OnData func(time.Duration, [][]byte)
37+
params params
38+
onData func(int64, time.Time, [][]byte)
1939

2040
cmd *exec.Cmd
2141
pipeOut *pipe
@@ -70,7 +90,7 @@ func (c *camera) initialize() error {
7090

7191
go c.run()
7292

73-
c.pipeOut.write(append([]byte{'c'}, c.Params.serialize()...))
93+
c.pipeOut.write(append([]byte{'c'}, c.params.serialize()...))
7494

7595
return nil
7696
}
@@ -162,17 +182,27 @@ outer:
162182
return fmt.Errorf(string(buf[1:]))
163183

164184
case 'b':
165-
tmp := uint64(buf[8])<<56 | uint64(buf[7])<<48 | uint64(buf[6])<<40 | uint64(buf[5])<<32 |
166-
uint64(buf[4])<<24 | uint64(buf[3])<<16 | uint64(buf[2])<<8 | uint64(buf[1])
167-
dts := time.Duration(tmp) * time.Microsecond
185+
dts := int64(buf[8])<<56 | int64(buf[7])<<48 | int64(buf[6])<<40 | int64(buf[5])<<32 |
186+
int64(buf[4])<<24 | int64(buf[3])<<16 | int64(buf[2])<<8 | int64(buf[1])
168187

169188
var nalus h264.AnnexB
170189
err = nalus.Unmarshal(buf[9:])
171190
if err != nil {
172191
return err
173192
}
174193

175-
c.OnData(dts, nalus)
194+
unixNTP := ntpTime()
195+
unixMono := monotonicTime()
196+
197+
// subtract from NTP the delay from now to the moment the frame was taken
198+
ntp := time.Unix(int64(unixNTP.Sec), int64(unixNTP.Nsec))
199+
deltaT := time.Duration(unixMono.Nano()-dts*1e3) * time.Nanosecond
200+
ntp = ntp.Add(-deltaT)
201+
202+
c.onData(
203+
multiplyAndDivide(dts, 90000, 1e6),
204+
ntp,
205+
nalus)
176206

177207
default:
178208
return fmt.Errorf("unexpected data from pipe: '0x%.2x'", buf[0])

internal/staticsources/rpicamera/camera_disabled.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
)
99

1010
type camera struct {
11-
Params params
12-
OnData func(time.Duration, [][]byte)
11+
params params
12+
onData func(int64, time.Time, [][]byte)
1313
}
1414

1515
func (c *camera) initialize() error {

internal/staticsources/rpicamera/source.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@ import (
1414
"github.com/bluenviron/mediamtx/internal/unit"
1515
)
1616

17-
func multiplyAndDivide(v, m, d int64) int64 {
18-
secs := v / d
19-
dec := v % d
20-
return (secs*m + dec*m/d)
21-
}
22-
2317
func paramsFromConf(logLevel conf.LogLevel, cnf *conf.Path) params {
2418
return params{
2519
LogLevel: func() string {
@@ -95,7 +89,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
9589
medias := []*description.Media{medi}
9690
var stream *stream.Stream
9791

98-
onData := func(dts time.Duration, au [][]byte) {
92+
onData := func(pts int64, ntp time.Time, au [][]byte) {
9993
if stream == nil {
10094
res := s.Parent.SetReady(defs.PathSourceStaticSetReadyReq{
10195
Desc: &description.Session{Medias: medias},
@@ -110,8 +104,8 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
110104

111105
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
112106
Base: unit.Base{
113-
NTP: time.Now(),
114-
PTS: multiplyAndDivide(int64(dts), 90000, int64(time.Second)),
107+
PTS: pts,
108+
NTP: ntp,
115109
},
116110
AU: au,
117111
})
@@ -124,8 +118,8 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
124118
}()
125119

126120
cam := &camera{
127-
Params: paramsFromConf(s.LogLevel, params.Conf),
128-
OnData: onData,
121+
params: paramsFromConf(s.LogLevel, params.Conf),
122+
onData: onData,
129123
}
130124
err := cam.initialize()
131125
if err != nil {

0 commit comments

Comments
 (0)