Files
3e09466c18 bump golangci-lint to 2.13.1 (#6129)
* bump golangci-lint to 2.13.1

* additional changes

---------

Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com>
2026-08-22 16:57:28 +00:00

1086 lines
24 KiB
Go

package playback
import (
"bytes"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"time"
amp4 "github.com/abema/go-mp4"
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
"github.com/bluenviron/mediacommon/v2/pkg/formats/fmp4"
"github.com/bluenviron/mediacommon/v2/pkg/formats/fmp4/seekablebuffer"
mcodecs "github.com/bluenviron/mediacommon/v2/pkg/formats/mp4/codecs"
"github.com/bluenviron/mediacommon/v2/pkg/formats/pmp4"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/recordstore"
"github.com/bluenviron/mediamtx/internal/test"
)
func writeSegment1(t *testing.T, fpath string) {
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{
{
ID: 1,
TimeScale: 90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
},
{
ID: 2,
TimeScale: 48000,
Codec: &mcodecs.MPEG4Audio{
Config: mpeg4audio.AudioSpecificConfig{
Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000,
ChannelCount: 2, //nolint:staticcheck
ChannelConfig: 2,
},
},
},
},
}
var buf1 seekablebuffer.Buffer
err := init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{
{
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 30 * 90000,
Samples: []*fmp4.Sample{
{
Duration: 30 * 90000,
Payload: []byte{1, 2},
},
{
Duration: 1 * 90000,
PTSOffset: 90000,
Payload: []byte{3, 4},
},
{
Duration: 1 * 90000,
PTSOffset: -90000,
IsNonSyncSample: true,
Payload: []byte{5, 6},
}, // 62 secs
},
},
{
ID: 2,
BaseTime: 31 * 48000,
Samples: []*fmp4.Sample{
{
Duration: 29 * 48000,
Payload: []byte{1, 2},
},
{
Duration: 1 * 48000,
Payload: []byte{3, 4},
}, // 61 secs
},
},
},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}
func writeSegment2(t *testing.T, fpath string) {
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{
{
ID: 1,
TimeScale: 90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
},
{
ID: 2,
TimeScale: 48000,
Codec: &mcodecs.MPEG4Audio{
Config: mpeg4audio.AudioSpecificConfig{
Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000,
ChannelConfig: 2,
ChannelCount: 2, //nolint:staticcheck
},
},
},
},
}
var buf1 seekablebuffer.Buffer
err := init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{ //nolint:dupl
{
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{7, 8},
}, // 1 sec
},
}},
},
{
Tracks: []*fmp4.PartTrack{{
ID: 2,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 48000,
Payload: []byte{5, 6},
},
{
Duration: 1 * 48000,
Payload: []byte{7, 8},
}, // 2 secs
},
}},
},
{
SequenceNumber: 5,
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 1 * 90000,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{9, 10},
},
{
Duration: 1 * 90000,
Payload: []byte{11, 12},
},
{
Duration: 1 * 90000,
Payload: []byte{13, 14},
}, // 4 secs
},
}},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}
func writeSegment3(t *testing.T, fpath string) {
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{
{
ID: 1,
TimeScale: 90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
},
},
}
var buf1 seekablebuffer.Buffer
err := init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{
{
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{13, 14},
},
},
}},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}
func TestOnGet(t *testing.T) {
for _, format := range []string{
"fmp4",
"mp4",
} {
for _, mode := range []string{
"no_mtxi",
"mtxi",
} {
t.Run(format+"_"+mode, func(t *testing.T) {
dir := t.TempDir()
err := os.Mkdir(filepath.Join(dir, "mypath"), 0o755)
require.NoError(t, err)
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{
{
ID: 1,
TimeScale: 90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
},
{
ID: 2,
TimeScale: 48000,
Codec: &mcodecs.MPEG4Audio{
Config: mpeg4audio.AudioSpecificConfig{
Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000,
ChannelCount: 2, //nolint:staticcheck
ChannelConfig: 2,
},
},
},
},
}
// segment 1
func() {
if mode == "mtxi" {
init.UserData = []amp4.IBox{&recordstore.Mtxi{
StreamID: uuid.MustParse("31564107-9e7e-4923-bf2f-631371a35397"),
SegmentNumber: 4,
DTS: int64(11 * time.Second),
}}
}
var buf1 seekablebuffer.Buffer
err = init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{
{
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 30 * 90000,
Samples: []*fmp4.Sample{
{
Duration: 30 * 90000,
Payload: []byte{1, 2},
},
{
Duration: 1 * 90000,
PTSOffset: 90000,
Payload: []byte{3, 4},
},
{
Duration: 1 * 90000,
PTSOffset: -90000,
IsNonSyncSample: true,
Payload: []byte{5, 6},
}, // 62 secs
},
},
{
ID: 2,
BaseTime: 31 * 48000,
Samples: []*fmp4.Sample{
{
Duration: 29 * 48000,
Payload: []byte{1, 2},
},
{
Duration: 1 * 48000,
Payload: []byte{3, 4},
}, // 61 secs
},
},
},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(dir, "mypath", "2008-11-07_11-22-00-500000.mp4"),
append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}()
// segment 2
func() { //nolint:dupl
if mode == "mtxi" {
init.UserData = []amp4.IBox{&recordstore.Mtxi{
StreamID: uuid.MustParse("31564107-9e7e-4923-bf2f-631371a35397"),
SegmentNumber: 5,
DTS: int64(73 * time.Second),
}}
}
var buf1 seekablebuffer.Buffer
err = init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{ //nolint:dupl
{
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{7, 8},
}, // 1 sec
},
}},
},
{
Tracks: []*fmp4.PartTrack{{
ID: 2,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 48000,
Payload: []byte{5, 6},
},
{
Duration: 1 * 48000,
Payload: []byte{7, 8},
}, // 2 secs
},
}},
},
{
SequenceNumber: 5,
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 1 * 90000,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{9, 10},
},
{
Duration: 1 * 90000,
Payload: []byte{11, 12},
},
{
Duration: 1 * 90000,
Payload: []byte{13, 14},
}, // 4 secs
},
}},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(dir, "mypath", "2008-11-07_11-23-02-500000.mp4"),
append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}()
// segment 3
func() { //nolint:dupl
if mode == "mtxi" {
init.UserData = []amp4.IBox{&recordstore.Mtxi{
StreamID: uuid.MustParse("31564107-9e7e-4923-bf2f-631371a35397"),
SegmentNumber: 6,
DTS: int64(75 * time.Second),
}}
}
var buf1 seekablebuffer.Buffer
err = init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{ //nolint:dupl
{
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{7, 8},
}, // 1 sec
},
}},
},
{
Tracks: []*fmp4.PartTrack{{
ID: 2,
BaseTime: 0,
Samples: []*fmp4.Sample{
{
Duration: 1 * 48000,
Payload: []byte{5, 6},
},
{
Duration: 1 * 48000,
Payload: []byte{7, 8},
}, // 2 secs
},
}},
},
{
SequenceNumber: 5,
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 1 * 90000,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
Payload: []byte{9, 10},
},
{
Duration: 1 * 90000,
Payload: []byte{11, 12},
},
{
Duration: 1 * 90000,
Payload: []byte{13, 14},
}, // 4 secs
},
}},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(dir, "mypath", "2008-11-07_11-23-04-500000.mp4"),
append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}()
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"mypath": {
Name: "mypath",
RecordPath: filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f"),
RecordFormat: conf.RecordFormatFMP4,
},
},
AuthManager: test.NilAuthManager,
Parent: test.NilLogger,
}
err = s.Initialize()
require.NoError(t, err)
defer s.Close()
u, err := url.Parse("http://myuser:mypass@localhost:9996/get")
require.NoError(t, err)
v := url.Values{}
v.Set("path", "mypath")
v.Set("start", time.Date(2008, 11, 7, 11, 23, 1, 500000000, time.Local).Format(time.RFC3339Nano))
v.Set("duration", "3")
v.Set("format", format)
u.RawQuery = v.Encode()
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
require.NoError(t, err)
res, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusOK, res.StatusCode)
buf, err := io.ReadAll(res.Body)
require.NoError(t, err)
switch format {
case "fmp4":
var parts fmp4.Parts
err = parts.Unmarshal(buf)
require.NoError(t, err)
require.Equal(t, fmp4.Parts{
{
SequenceNumber: 0,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Samples: []*fmp4.Sample{
{
Duration: 0,
PTSOffset: 90000,
Payload: []byte{3, 4},
},
{
Duration: 90000,
PTSOffset: -90000,
IsNonSyncSample: true,
Payload: []byte{5, 6},
},
},
},
},
},
{
SequenceNumber: 1,
Tracks: []*fmp4.PartTrack{
{
ID: 2,
BaseTime: 48000,
Samples: []*fmp4.Sample{
{
Duration: 48000,
Payload: []byte{5, 6},
},
},
},
},
},
{
SequenceNumber: 2,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 90000,
Samples: []*fmp4.Sample{
{
Duration: 90000,
Payload: []byte{7, 8},
},
},
},
},
},
{
SequenceNumber: 3,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 2 * 90000,
Samples: []*fmp4.Sample{
{
Duration: 90000,
Payload: []byte{9, 10},
},
},
},
{
ID: 2,
BaseTime: 2 * 48000,
Samples: []*fmp4.Sample{
{
Duration: 48000,
Payload: []byte{7, 8},
},
},
},
},
},
}, parts)
case "mp4":
var p pmp4.Presentation
err = p.Unmarshal(bytes.NewReader(buf))
require.NoError(t, err)
sampleData := make(map[int][][]byte)
for _, track := range p.Tracks {
samples := make([][]byte, len(track.Samples))
for i, sample := range track.Samples {
buf, err = sample.GetPayload()
require.NoError(t, err)
samples[i] = buf
sample.GetPayload = nil
}
sampleData[track.ID] = samples
}
require.Equal(t, pmp4.Presentation{
Tracks: []*pmp4.Track{
{
ID: 1,
TimeScale: 90000,
TimeOffset: -90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
Samples: []*pmp4.Sample{
{
Duration: 90000,
PayloadSize: 2,
},
{
Duration: 90000,
PTSOffset: -90000,
IsNonSyncSample: true,
PayloadSize: 2,
},
{
Duration: 90000,
PayloadSize: 2,
},
{
Duration: 90000,
PayloadSize: 2,
},
},
},
{
ID: 2,
TimeScale: 48000,
TimeOffset: 48000,
Codec: &mcodecs.MPEG4Audio{
Config: mpeg4audio.AudioSpecificConfig{
Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000,
ChannelCount: 2, //nolint:staticcheck
ChannelConfig: 2,
},
},
Samples: []*pmp4.Sample{
{
Duration: 48000,
PayloadSize: 2,
},
{
Duration: 48000,
PayloadSize: 2,
},
},
},
},
}, p)
require.Equal(t, map[int][][]byte{
1: {
{3, 4},
{5, 6},
{7, 8},
{9, 10},
},
2: {
{5, 6},
{7, 8},
},
}, sampleData)
}
})
}
}
}
func TestOnGetDifferentInit(t *testing.T) {
dir := t.TempDir()
err := os.Mkdir(filepath.Join(dir, "mypath"), 0o755)
require.NoError(t, err)
writeSegment1(t, filepath.Join(dir, "mypath", "2008-11-07_11-22-00-500000.mp4"))
writeSegment3(t, filepath.Join(dir, "mypath", "2008-11-07_11-23-02-500000.mp4"))
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"mypath": {
Name: "mypath",
RecordPath: filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f"),
RecordFormat: conf.RecordFormatFMP4,
},
},
AuthManager: test.NilAuthManager,
Parent: test.NilLogger,
}
err = s.Initialize()
require.NoError(t, err)
defer s.Close()
u, err := url.Parse("http://myuser:mypass@localhost:9996/get")
require.NoError(t, err)
v := url.Values{}
v.Set("path", "mypath")
v.Set("start", time.Date(2008, 11, 7, 11, 23, 1, 500000000, time.Local).Format(time.RFC3339Nano))
v.Set("duration", "2")
v.Set("format", "fmp4")
u.RawQuery = v.Encode()
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
require.NoError(t, err)
res, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusOK, res.StatusCode)
buf, err := io.ReadAll(res.Body)
require.NoError(t, err)
var parts fmp4.Parts
err = parts.Unmarshal(buf)
require.NoError(t, err)
require.Equal(t, fmp4.Parts{
{
SequenceNumber: 0,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Samples: []*fmp4.Sample{
{
Duration: 0,
PTSOffset: 90000,
Payload: []byte{3, 4},
},
{
Duration: 90000,
PTSOffset: -90000,
IsNonSyncSample: true,
Payload: []byte{5, 6},
},
},
},
},
},
}, parts)
}
func TestOnGetInMiddleOfLastSample(t *testing.T) {
for _, format := range []string{"fmp4", "mp4"} {
t.Run(format, func(t *testing.T) {
dir := t.TempDir()
err := os.Mkdir(filepath.Join(dir, "mypath"), 0o755)
require.NoError(t, err)
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{
{
ID: 1,
TimeScale: 90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
},
},
}
func() {
fpath := filepath.Join(dir, "mypath", "2008-11-07_11-22-00-000000.mp4")
var buf1 seekablebuffer.Buffer
err = init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{
{
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
IsNonSyncSample: false,
Payload: []byte{1, 2},
},
},
},
},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}()
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"mypath": {
Name: "mypath",
RecordPath: filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f"),
RecordFormat: conf.RecordFormatFMP4,
},
},
AuthManager: test.NilAuthManager,
Parent: test.NilLogger,
}
err = s.Initialize()
require.NoError(t, err)
defer s.Close()
u, err := url.Parse("http://myuser:mypass@localhost:9996/get")
require.NoError(t, err)
v := url.Values{}
v.Set("path", "mypath")
v.Set("start", time.Date(2008, 11, 7, 11, 22, 0, 500000000, time.Local).Format(time.RFC3339Nano))
v.Set("duration", "3")
v.Set("format", format)
u.RawQuery = v.Encode()
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
require.NoError(t, err)
res, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusNotFound, res.StatusCode)
})
}
}
func TestOnGetInvalidPath(t *testing.T) {
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"all_others": {
Name: "all_others",
RecordPath: filepath.Join(t.TempDir(), "%path/%Y-%m-%d_%H-%M-%S-%f"),
RecordFormat: conf.RecordFormatFMP4,
},
},
AuthManager: test.NilAuthManager,
Parent: test.NilLogger,
}
err := s.Initialize()
require.NoError(t, err)
defer s.Close()
u, err := url.Parse("http://localhost:9996/get")
require.NoError(t, err)
v := url.Values{}
v.Set("path", "group/../cam1")
v.Set("start", time.Date(2008, 11, 7, 11, 23, 1, 500000000, time.Local).Format(time.RFC3339Nano))
v.Set("duration", "3")
u.RawQuery = v.Encode()
res, err := http.Get(u.String())
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusBadRequest, res.StatusCode)
}
func TestOnGetBetweenSegments(t *testing.T) {
for _, ca := range []string{
"idr before",
"idr after",
} {
t.Run(ca, func(t *testing.T) {
dir := t.TempDir()
err := os.Mkdir(filepath.Join(dir, "mypath"), 0o755)
require.NoError(t, err)
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{
{
ID: 1,
TimeScale: 90000,
Codec: &mcodecs.H264{
SPS: test.FormatH264.SPS,
PPS: test.FormatH264.PPS,
},
},
},
}
func() {
fpath := filepath.Join(dir, "mypath", "2008-11-07_11-22-00-000000.mp4")
var buf1 seekablebuffer.Buffer
err = init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{
{
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
IsNonSyncSample: false,
Payload: []byte{1, 2},
},
},
},
},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}()
func() {
fpath := filepath.Join(dir, "mypath", "2008-11-07_11-22-01-000000.mp4")
var buf1 seekablebuffer.Buffer
err = init.Marshal(&buf1)
require.NoError(t, err)
var buf2 seekablebuffer.Buffer
parts := fmp4.Parts{
{
Tracks: []*fmp4.PartTrack{
{
ID: 1,
Samples: []*fmp4.Sample{
{
Duration: 1 * 90000,
IsNonSyncSample: (ca == "idr before"),
Payload: []byte{3, 4},
},
},
},
},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}()
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.Duration(10 * time.Second),
WriteTimeout: conf.Duration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"mypath": {
Name: "mypath",
RecordPath: filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f"),
RecordFormat: conf.RecordFormatFMP4,
},
},
AuthManager: test.NilAuthManager,
Parent: test.NilLogger,
}
err = s.Initialize()
require.NoError(t, err)
defer s.Close()
u, err := url.Parse("http://myuser:mypass@localhost:9996/get")
require.NoError(t, err)
v := url.Values{}
v.Set("path", "mypath")
v.Set("start", time.Date(2008, 11, 7, 11, 22, 0, 500000000, time.Local).Format(time.RFC3339Nano))
v.Set("duration", "3")
v.Set("format", "fmp4")
u.RawQuery = v.Encode()
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
require.NoError(t, err)
res, err := http.DefaultClient.Do(req)
require.NoError(t, err)
defer res.Body.Close()
require.Equal(t, http.StatusOK, res.StatusCode)
buf, err := io.ReadAll(res.Body)
require.NoError(t, err)
var parts fmp4.Parts
err = parts.Unmarshal(buf)
require.NoError(t, err)
switch ca {
case "idr before":
require.Equal(t, fmp4.Parts{
{
SequenceNumber: 0,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 45000,
Samples: []*fmp4.Sample{
{
Duration: 0,
Payload: []byte{1, 2},
},
{
Duration: 90000,
Payload: []byte{3, 4},
IsNonSyncSample: true,
},
},
},
},
},
}, parts)
case "idr after":
require.Equal(t, fmp4.Parts{
{
SequenceNumber: 0,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 45000,
Samples: []*fmp4.Sample{
{
Duration: 90000,
Payload: []byte{3, 4},
},
},
},
},
},
}, parts)
}
})
}
}