mediamtx/internal/hls/client_timesync_fmp4.go
Alessandro Ros e5ab731d14
Improve HLS client (#1179)
* hls source: support fMP4s video streams

* hls source: start reading live streams from (end of playlist - starting point)

* hls client: wait processing of current fMP4 segment before downloading another one

* hls client: support fmp4 trun boxes with default sample duration, flags and size

* hls client: merge fmp4 init file reader and writer

* hls client: merge fmp4 part reader and writer

* hls client: improve precision of go <-> mp4 time conversion

* hls client: fix esds generation in go-mp4

* hls client: support audio in separate playlist

* hls client: support an arbitrary number of tracks in fmp4 init files

* hls client: support EXT-X-BYTERANGE

* hls client: support fmp4 segments with multiple parts at once

* hls client: support an arbitrary number of mpeg-ts tracks

* hls client: synchronize tracks around a primary track

* update go-mp4

* hls: synchronize track reproduction around a leading one

* hls client: reset stream if playback is too late

* hls client: add limit on DTS-RTC difference

* hls client: support again streams that don't provide codecs in master playlist
2022-10-23 14:04:33 +02:00

60 lines
1.4 KiB
Go

package hls
import (
"context"
"fmt"
"time"
)
func durationGoToMp4(v time.Duration, timeScale uint32) uint64 {
timeScale64 := uint64(timeScale)
secs := v / time.Second
dec := v % time.Second
return uint64(secs)*timeScale64 + uint64(dec)*timeScale64/uint64(time.Second)
}
func durationMp4ToGo(v uint64, timeScale uint32) time.Duration {
timeScale64 := uint64(timeScale)
secs := v / timeScale64
dec := v % timeScale64
return time.Duration(secs)*time.Second + time.Duration(dec)*time.Second/time.Duration(timeScale64)
}
type clientTimeSyncFMP4 struct {
startRTC time.Time
startDTS time.Duration
}
func newClientTimeSyncFMP4(timeScale uint32, baseTime uint64) *clientTimeSyncFMP4 {
return &clientTimeSyncFMP4{
startRTC: time.Now(),
startDTS: durationMp4ToGo(baseTime, timeScale),
}
}
func (ts *clientTimeSyncFMP4) convertAndSync(ctx context.Context, timeScale uint32,
rawDTS uint64, ptsOffset int32,
) (time.Duration, error) {
pts := durationMp4ToGo(rawDTS+uint64(ptsOffset), timeScale)
dts := durationMp4ToGo(rawDTS, timeScale)
pts -= ts.startDTS
dts -= ts.startDTS
elapsed := time.Since(ts.startRTC)
if dts > elapsed {
diff := dts - elapsed
if diff > clientMaxDTSRTCDiff {
return 0, fmt.Errorf("difference between DTS and RTC is too big")
}
select {
case <-time.After(diff):
case <-ctx.Done():
return 0, fmt.Errorf("terminated")
}
}
return pts, nil
}