mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-20 02:00:05 -08:00
record: support storing timezone in recording segments (#3566) (#4597)
Some checks are pending
code_lint / golangci_lint (push) Waiting to run
code_lint / mod_tidy (push) Waiting to run
code_lint / api_docs (push) Waiting to run
code_test / test_64 (push) Waiting to run
code_test / test_32 (push) Waiting to run
code_test / test_e2e (push) Waiting to run
Some checks are pending
code_lint / golangci_lint (push) Waiting to run
code_lint / mod_tidy (push) Waiting to run
code_lint / api_docs (push) Waiting to run
code_test / test_64 (push) Waiting to run
code_test / test_32 (push) Waiting to run
code_test / test_e2e (push) Waiting to run
This commit is contained in:
parent
bf9df762ca
commit
dbc38a7aa6
4 changed files with 79 additions and 3 deletions
|
|
@ -1628,7 +1628,7 @@ pathDefaults:
|
||||||
# Path of recording segments.
|
# Path of recording segments.
|
||||||
# Extension is added automatically.
|
# Extension is added automatically.
|
||||||
# Available variables are %path (path name), %Y %m %d (year, month, day),
|
# Available variables are %path (path name), %Y %m %d (year, month, day),
|
||||||
# %H %M %S (hours, minutes, seconds), %f (microseconds), %s (unix epoch).
|
# %H %M %S (hours, minutes, seconds), %f (microseconds), %z (time zone), %s (unix epoch).
|
||||||
recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
|
recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,48 @@ func leadingZeros(v int, size int) string {
|
||||||
return out2 + out
|
return out2 + out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func timeLocationEncode(t time.Time) string {
|
||||||
|
_, off := t.Zone()
|
||||||
|
|
||||||
|
if off == 0 {
|
||||||
|
return "Z"
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret string
|
||||||
|
|
||||||
|
if off > 0 {
|
||||||
|
ret = "+"
|
||||||
|
} else {
|
||||||
|
ret = "-"
|
||||||
|
off = -off
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += leadingZeros(off/60/60, 2)
|
||||||
|
ret += leadingZeros((off/60)%60, 2)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeLocationDecode(s string) *time.Location {
|
||||||
|
if s == "Z" {
|
||||||
|
return time.UTC
|
||||||
|
}
|
||||||
|
|
||||||
|
var sign int
|
||||||
|
if s[0] == '+' {
|
||||||
|
sign = 1
|
||||||
|
} else {
|
||||||
|
sign = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
v1, _ := strconv.ParseInt(s[1:3], 10, 64)
|
||||||
|
v2, _ := strconv.ParseInt(s[3:5], 10, 64)
|
||||||
|
|
||||||
|
off := sign*int(v1)*3600 + int(v2)*3600
|
||||||
|
|
||||||
|
return time.FixedZone("myzone", off)
|
||||||
|
}
|
||||||
|
|
||||||
// PathAddExtension adds the file extension to the path.
|
// PathAddExtension adds the file extension to the path.
|
||||||
func PathAddExtension(path string, format conf.RecordFormat) string {
|
func PathAddExtension(path string, format conf.RecordFormat) string {
|
||||||
switch format {
|
switch format {
|
||||||
|
|
@ -99,6 +141,7 @@ func (p *Path) Decode(format string, v string) bool {
|
||||||
re = strings.ReplaceAll(re, "%M", "([0-9]{2})")
|
re = strings.ReplaceAll(re, "%M", "([0-9]{2})")
|
||||||
re = strings.ReplaceAll(re, "%S", "([0-9]{2})")
|
re = strings.ReplaceAll(re, "%S", "([0-9]{2})")
|
||||||
re = strings.ReplaceAll(re, "%f", "([0-9]{6})")
|
re = strings.ReplaceAll(re, "%f", "([0-9]{6})")
|
||||||
|
re = strings.ReplaceAll(re, "%z", "(Z|\\+[0-9]{4}|-[0-9]{4})")
|
||||||
re = strings.ReplaceAll(re, "%s", "([0-9]{10})")
|
re = strings.ReplaceAll(re, "%s", "([0-9]{10})")
|
||||||
r := regexp.MustCompile(re)
|
r := regexp.MustCompile(re)
|
||||||
|
|
||||||
|
|
@ -121,6 +164,7 @@ func (p *Path) Decode(format string, v string) bool {
|
||||||
"%M",
|
"%M",
|
||||||
"%S",
|
"%S",
|
||||||
"%f",
|
"%f",
|
||||||
|
"%z",
|
||||||
"%s",
|
"%s",
|
||||||
} {
|
} {
|
||||||
if strings.HasPrefix(cur, va) {
|
if strings.HasPrefix(cur, va) {
|
||||||
|
|
@ -150,6 +194,7 @@ func (p *Path) Decode(format string, v string) bool {
|
||||||
var second int
|
var second int
|
||||||
var micros int
|
var micros int
|
||||||
var unixSec int64 = -1
|
var unixSec int64 = -1
|
||||||
|
loc := time.Local
|
||||||
|
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
switch k {
|
switch k {
|
||||||
|
|
@ -184,6 +229,9 @@ func (p *Path) Decode(format string, v string) bool {
|
||||||
tmp, _ := strconv.ParseInt(v, 10, 64)
|
tmp, _ := strconv.ParseInt(v, 10, 64)
|
||||||
micros = int(tmp)
|
micros = int(tmp)
|
||||||
|
|
||||||
|
case "%z":
|
||||||
|
loc = timeLocationDecode(v)
|
||||||
|
|
||||||
case "%s":
|
case "%s":
|
||||||
unixSec, _ = strconv.ParseInt(v, 10, 64)
|
unixSec, _ = strconv.ParseInt(v, 10, 64)
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +240,7 @@ func (p *Path) Decode(format string, v string) bool {
|
||||||
if unixSec > 0 {
|
if unixSec > 0 {
|
||||||
p.Start = time.Unix(unixSec, int64(micros)*1000)
|
p.Start = time.Unix(unixSec, int64(micros)*1000)
|
||||||
} else {
|
} else {
|
||||||
p.Start = time.Date(year, month, day, hour, minute, second, micros*1000, time.Local)
|
p.Start = time.Date(year, month, day, hour, minute, second, micros*1000, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
@ -208,6 +256,7 @@ func (p Path) Encode(format string) string {
|
||||||
format = strings.ReplaceAll(format, "%M", leadingZeros(p.Start.Minute(), 2))
|
format = strings.ReplaceAll(format, "%M", leadingZeros(p.Start.Minute(), 2))
|
||||||
format = strings.ReplaceAll(format, "%S", leadingZeros(p.Start.Second(), 2))
|
format = strings.ReplaceAll(format, "%S", leadingZeros(p.Start.Second(), 2))
|
||||||
format = strings.ReplaceAll(format, "%f", leadingZeros(p.Start.Nanosecond()/1000, 6))
|
format = strings.ReplaceAll(format, "%f", leadingZeros(p.Start.Nanosecond()/1000, 6))
|
||||||
|
format = strings.ReplaceAll(format, "%z", timeLocationEncode(p.Start))
|
||||||
format = strings.ReplaceAll(format, "%s", strconv.FormatInt(p.Start.Unix(), 10))
|
format = strings.ReplaceAll(format, "%s", strconv.FormatInt(p.Start.Unix(), 10))
|
||||||
return format
|
return format
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,33 @@ var pathCases = []struct {
|
||||||
},
|
},
|
||||||
"mypath/1638447323.567324.mp4",
|
"mypath/1638447323.567324.mp4",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"timezone utc",
|
||||||
|
"%path/%Y-%m-%d_%H-%M-%S-%f_%z.mp4",
|
||||||
|
Path{
|
||||||
|
Start: time.Date(2021, 12, 2, 12, 15, 23, 567324000, time.UTC),
|
||||||
|
Path: "mypath",
|
||||||
|
},
|
||||||
|
"mypath/2021-12-02_12-15-23-567324_Z.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timezone plus",
|
||||||
|
"%path/%Y-%m-%d_%H-%M-%S-%f_%z.mp4",
|
||||||
|
Path{
|
||||||
|
Start: time.Date(2021, 12, 2, 12, 15, 23, 567324000, time.FixedZone("myzone", 7200)),
|
||||||
|
Path: "mypath",
|
||||||
|
},
|
||||||
|
"mypath/2021-12-02_12-15-23-567324_+0200.mp4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timezone minus",
|
||||||
|
"%path/%Y-%m-%d_%H-%M-%S-%f_%z.mp4",
|
||||||
|
Path{
|
||||||
|
Start: time.Date(2021, 12, 2, 12, 15, 23, 567324000, time.FixedZone("myzone", -7200)),
|
||||||
|
Path: "mypath",
|
||||||
|
},
|
||||||
|
"mypath/2021-12-02_12-15-23-567324_-0200.mp4",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPathDecode(t *testing.T) {
|
func TestPathDecode(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,7 @@ pathDefaults:
|
||||||
# Path of recording segments.
|
# Path of recording segments.
|
||||||
# Extension is added automatically.
|
# Extension is added automatically.
|
||||||
# Available variables are %path (path name), %Y %m %d (year, month, day),
|
# Available variables are %path (path name), %Y %m %d (year, month, day),
|
||||||
# %H %M %S (hours, minutes, seconds), %f (microseconds), %s (unix epoch).
|
# %H %M %S (hours, minutes, seconds), %f (microseconds), %z (time zone), %s (unix epoch).
|
||||||
recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
|
recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f
|
||||||
# Format of recorded segments.
|
# Format of recorded segments.
|
||||||
# Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS).
|
# Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS).
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue