diff --git a/docs/2-usage/14-route-absolute-timestamps.md b/docs/2-usage/14-route-absolute-timestamps.md index 35463602..adaca45f 100644 --- a/docs/2-usage/14-route-absolute-timestamps.md +++ b/docs/2-usage/14-route-absolute-timestamps.md @@ -3,8 +3,8 @@ 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 -- RTSP (when the `useAbsoluteTimestamp` parameter is `true`) -- WebRTC (when the `useAbsoluteTimestamp` parameter is `true`) +- RTSP +- WebRTC - Raspberry Pi Camera and supports sending absolute timestamps with the following protocols: @@ -13,6 +13,14 @@ and supports sending absolute timestamps with the following protocols: - RTSP - WebRTC +By default, absolute timestamps of incoming frames are not used, instead they are replaced with the current timestamp. This prevents users from arbitrarily changing recording dates, and also allows to support sources that do not send absolute timestamps. It is possible to preserve original absolute timestamps by toggling the `useAbsoluteTimestamp` parameter: + +```yml +pathDefaults: + # Use absolute timestamp of frames, instead of replacing them with the current time. + useAbsoluteTimestamp: false +``` + ## Absolute timestamp in HLS In the HLS protocol, absolute timestamps are routed by adding a `EXT-X-PROGRAM-DATE-TIME` tag before each segment: diff --git a/internal/protocols/hls/to_stream.go b/internal/protocols/hls/to_stream.go index 919e6337..aa12027d 100644 --- a/internal/protocols/hls/to_stream.go +++ b/internal/protocols/hls/to_stream.go @@ -8,6 +8,7 @@ import ( "github.com/bluenviron/gohlslib/v2/pkg/codecs" "github.com/bluenviron/gortsplib/v5/pkg/description" "github.com/bluenviron/gortsplib/v5/pkg/format" + "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/ntpestimator" "github.com/bluenviron/mediamtx/internal/stream" @@ -20,7 +21,7 @@ const ( ntpStateInitial ntpState = iota ntpStateUnavailable ntpStateAvailable - ntpStateDegraded + ntpStateReplace ) func multiplyAndDivide(v, m, d int64) int64 { @@ -33,11 +34,16 @@ func multiplyAndDivide(v, m, d int64) int64 { func ToStream( c *gohlslib.Client, tracks []*gohlslib.Track, + pathConf *conf.Path, strm **stream.Stream, ) ([]*description.Media, error) { var ntpStat ntpState var ntpStatMutex sync.Mutex + if !pathConf.UseAbsoluteTimestamp { + ntpStat = ntpStateReplace + } + var medias []*description.Media //nolint:prealloc for _, track := range tracks { @@ -69,11 +75,11 @@ func ToStream( _, avail := c.AbsoluteTime(ctrack) if avail { (*strm).Parent.Log(logger.Warn, "absolute timestamp appeared after stream started, we are not using it") - ntpStat = ntpStateDegraded + ntpStat = ntpStateReplace } return ntpEstimator.Estimate(pts) - default: // ntpStateDegraded + default: // ntpStateReplace return ntpEstimator.Estimate(pts) } } diff --git a/internal/protocols/hls/to_stream_test.go b/internal/protocols/hls/to_stream_test.go index 73475cda..ee1b4a7d 100644 --- a/internal/protocols/hls/to_stream_test.go +++ b/internal/protocols/hls/to_stream_test.go @@ -11,6 +11,7 @@ import ( "github.com/bluenviron/gortsplib/v5/pkg/description" "github.com/bluenviron/gortsplib/v5/pkg/format" "github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts" + "github.com/bluenviron/mediamtx/internal/conf" "github.com/bluenviron/mediamtx/internal/stream" "github.com/bluenviron/mediamtx/internal/test" "github.com/bluenviron/mediamtx/internal/unit" @@ -18,7 +19,7 @@ import ( ) func TestToStreamNoSupportedCodecs(t *testing.T) { - _, err := ToStream(nil, []*gohlslib.Track{}, nil) + _, err := ToStream(nil, []*gohlslib.Track{}, &conf.Path{}, nil) require.Equal(t, ErrNoSupportedCodecs, err) } @@ -92,8 +93,11 @@ func TestToStream(t *testing.T) { c = &gohlslib.Client{ URI: "http://localhost:5781/stream.m3u8", OnTracks: func(tracks []*gohlslib.Track) error { - medias, err2 := ToStream(c, tracks, &strm) + medias, err2 := ToStream(c, tracks, &conf.Path{ + UseAbsoluteTimestamp: true, + }, &strm) require.NoError(t, err2) + require.Equal(t, []*description.Media{{ Type: description.MediaTypeVideo, Formats: []format.Format{&format.H264{ diff --git a/internal/staticsources/hls/source.go b/internal/staticsources/hls/source.go index 89d7f990..c9848739 100644 --- a/internal/staticsources/hls/source.go +++ b/internal/staticsources/hls/source.go @@ -94,7 +94,7 @@ func (s *Source) Run(params defs.StaticSourceRunParams) error { decodeErrors.Increase() }, OnTracks: func(tracks []*gohlslib.Track) error { - medias, err2 := hls.ToStream(c, tracks, &stream) + medias, err2 := hls.ToStream(c, tracks, params.Conf, &stream) if err2 != nil { return err2 } diff --git a/mediamtx.yml b/mediamtx.yml index f6275360..10917031 100644 --- a/mediamtx.yml +++ b/mediamtx.yml @@ -482,7 +482,7 @@ pathDefaults: # If the stream is not available, redirect readers to this path. # It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL. fallback: - # Route original absolute timestamps of RTSP and WebRTC frames, instead of replacing them. + # Use absolute timestamp of frames, instead of replacing them with the current time. useAbsoluteTimestamp: false ###############################################