mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-20 02:00:05 -08:00
rpi: route original absolute timestamp of packets (#1300) (#4382)
Some checks failed
code_lint / golangci_lint (push) Has been cancelled
code_lint / mod_tidy (push) Has been cancelled
code_lint / api_docs (push) Has been cancelled
code_test / test_64 (push) Has been cancelled
code_test / test_32 (push) Has been cancelled
code_test / test_highlevel (push) Has been cancelled
Some checks failed
code_lint / golangci_lint (push) Has been cancelled
code_lint / mod_tidy (push) Has been cancelled
code_lint / api_docs (push) Has been cancelled
code_test / test_64 (push) Has been cancelled
code_test / test_32 (push) Has been cancelled
code_test / test_highlevel (push) Has been cancelled
This commit is contained in:
parent
8b98c02903
commit
a05da3a205
4 changed files with 46 additions and 21 deletions
|
|
@ -1709,10 +1709,11 @@ The command inserted into `runOnDemand` will start only when a client requests t
|
||||||
|
|
||||||
### Route absolute timestamps
|
### Route absolute timestamps
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
* HLS (through the `EXT-X-PROGRAM-DATE-TIME` tag in playlists)
|
* HLS (through the `EXT-X-PROGRAM-DATE-TIME` tag in playlists)
|
||||||
* RTSP (through RTCP reports, when `rtspAbsoluteTimestamp` is `true` in settings)
|
* RTSP (through RTCP reports, when `rtspAbsoluteTimestamp` is `true` in settings)
|
||||||
|
* Raspberry Pi Camera
|
||||||
|
|
||||||
and supports sending absolute timestamps with the following protocols:
|
and supports sending absolute timestamps with the following protocols:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,34 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
|
"github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ntpTime() syscall.Timespec {
|
||||||
|
var t syscall.Timespec
|
||||||
|
syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 0, uintptr(unsafe.Pointer(&t)), 0)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func monotonicTime() syscall.Timespec {
|
||||||
|
var t syscall.Timespec
|
||||||
|
syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 1, uintptr(unsafe.Pointer(&t)), 0)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func multiplyAndDivide(v, m, d int64) int64 {
|
||||||
|
secs := v / d
|
||||||
|
dec := v % d
|
||||||
|
return (secs*m + dec*m/d)
|
||||||
|
}
|
||||||
|
|
||||||
type camera struct {
|
type camera struct {
|
||||||
Params params
|
params params
|
||||||
OnData func(time.Duration, [][]byte)
|
onData func(int64, time.Time, [][]byte)
|
||||||
|
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
pipeOut *pipe
|
pipeOut *pipe
|
||||||
|
|
@ -70,7 +90,7 @@ func (c *camera) initialize() error {
|
||||||
|
|
||||||
go c.run()
|
go c.run()
|
||||||
|
|
||||||
c.pipeOut.write(append([]byte{'c'}, c.Params.serialize()...))
|
c.pipeOut.write(append([]byte{'c'}, c.params.serialize()...))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -162,9 +182,8 @@ outer:
|
||||||
return fmt.Errorf(string(buf[1:]))
|
return fmt.Errorf(string(buf[1:]))
|
||||||
|
|
||||||
case 'b':
|
case 'b':
|
||||||
tmp := uint64(buf[8])<<56 | uint64(buf[7])<<48 | uint64(buf[6])<<40 | uint64(buf[5])<<32 |
|
dts := int64(buf[8])<<56 | int64(buf[7])<<48 | int64(buf[6])<<40 | int64(buf[5])<<32 |
|
||||||
uint64(buf[4])<<24 | uint64(buf[3])<<16 | uint64(buf[2])<<8 | uint64(buf[1])
|
int64(buf[4])<<24 | int64(buf[3])<<16 | int64(buf[2])<<8 | int64(buf[1])
|
||||||
dts := time.Duration(tmp) * time.Microsecond
|
|
||||||
|
|
||||||
var nalus h264.AnnexB
|
var nalus h264.AnnexB
|
||||||
err = nalus.Unmarshal(buf[9:])
|
err = nalus.Unmarshal(buf[9:])
|
||||||
|
|
@ -172,7 +191,18 @@ outer:
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.OnData(dts, nalus)
|
unixNTP := ntpTime()
|
||||||
|
unixMono := monotonicTime()
|
||||||
|
|
||||||
|
// subtract from NTP the delay from now to the moment the frame was taken
|
||||||
|
ntp := time.Unix(int64(unixNTP.Sec), int64(unixNTP.Nsec))
|
||||||
|
deltaT := time.Duration(unixMono.Nano()-dts*1e3) * time.Nanosecond
|
||||||
|
ntp = ntp.Add(-deltaT)
|
||||||
|
|
||||||
|
c.onData(
|
||||||
|
multiplyAndDivide(dts, 90000, 1e6),
|
||||||
|
ntp,
|
||||||
|
nalus)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected data from pipe: '0x%.2x'", buf[0])
|
return fmt.Errorf("unexpected data from pipe: '0x%.2x'", buf[0])
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type camera struct {
|
type camera struct {
|
||||||
Params params
|
params params
|
||||||
OnData func(time.Duration, [][]byte)
|
onData func(int64, time.Time, [][]byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *camera) initialize() error {
|
func (c *camera) initialize() error {
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,6 @@ import (
|
||||||
"github.com/bluenviron/mediamtx/internal/unit"
|
"github.com/bluenviron/mediamtx/internal/unit"
|
||||||
)
|
)
|
||||||
|
|
||||||
func multiplyAndDivide(v, m, d int64) int64 {
|
|
||||||
secs := v / d
|
|
||||||
dec := v % d
|
|
||||||
return (secs*m + dec*m/d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func paramsFromConf(logLevel conf.LogLevel, cnf *conf.Path) params {
|
func paramsFromConf(logLevel conf.LogLevel, cnf *conf.Path) params {
|
||||||
return params{
|
return params{
|
||||||
LogLevel: func() string {
|
LogLevel: func() string {
|
||||||
|
|
@ -95,7 +89,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||||
medias := []*description.Media{medi}
|
medias := []*description.Media{medi}
|
||||||
var stream *stream.Stream
|
var stream *stream.Stream
|
||||||
|
|
||||||
onData := func(dts time.Duration, au [][]byte) {
|
onData := func(pts int64, ntp time.Time, au [][]byte) {
|
||||||
if stream == nil {
|
if stream == nil {
|
||||||
res := s.Parent.SetReady(defs.PathSourceStaticSetReadyReq{
|
res := s.Parent.SetReady(defs.PathSourceStaticSetReadyReq{
|
||||||
Desc: &description.Session{Medias: medias},
|
Desc: &description.Session{Medias: medias},
|
||||||
|
|
@ -110,8 +104,8 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||||
|
|
||||||
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
|
stream.WriteUnit(medi, medi.Formats[0], &unit.H264{
|
||||||
Base: unit.Base{
|
Base: unit.Base{
|
||||||
NTP: time.Now(),
|
PTS: pts,
|
||||||
PTS: multiplyAndDivide(int64(dts), 90000, int64(time.Second)),
|
NTP: ntp,
|
||||||
},
|
},
|
||||||
AU: au,
|
AU: au,
|
||||||
})
|
})
|
||||||
|
|
@ -124,8 +118,8 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
cam := &camera{
|
cam := &camera{
|
||||||
Params: paramsFromConf(s.LogLevel, params.Conf),
|
params: paramsFromConf(s.LogLevel, params.Conf),
|
||||||
OnData: onData,
|
onData: onData,
|
||||||
}
|
}
|
||||||
err := cam.initialize()
|
err := cam.initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue