mediamtx/internal/playback/server_test.go

277 lines
6.0 KiB
Go

package playback
import (
"encoding/json"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"testing"
"time"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4"
"github.com/bluenviron/mediacommon/pkg/formats/fmp4/seekablebuffer"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/test"
"github.com/stretchr/testify/require"
)
func writeSegment1(t *testing.T, fpath string) {
init := fmp4.Init{
Tracks: []*fmp4.InitTrack{{
ID: 1,
TimeScale: 90000,
Codec: &fmp4.CodecH264{
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{
{
SequenceNumber: 1,
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 0,
Samples: []*fmp4.PartSample{},
}},
},
{
SequenceNumber: 1,
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 30 * 90000,
Samples: []*fmp4.PartSample{
{
Duration: 30 * 90000,
IsNonSyncSample: false,
Payload: []byte{1, 2},
},
{
Duration: 1 * 90000,
IsNonSyncSample: false,
Payload: []byte{3, 4},
},
{
Duration: 1 * 90000,
IsNonSyncSample: true,
Payload: []byte{5, 6},
},
},
}},
},
}
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: &fmp4.CodecH264{
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{
{
SequenceNumber: 1,
Tracks: []*fmp4.PartTrack{{
ID: 1,
BaseTime: 0,
Samples: []*fmp4.PartSample{
{
Duration: 1 * 90000,
IsNonSyncSample: false,
Payload: []byte{7, 8},
},
{
Duration: 1 * 90000,
IsNonSyncSample: false,
Payload: []byte{9, 10},
},
},
}},
},
}
err = parts.Marshal(&buf2)
require.NoError(t, err)
err = os.WriteFile(fpath, append(buf1.Bytes(), buf2.Bytes()...), 0o644)
require.NoError(t, err)
}
func TestServerGet(t *testing.T) {
dir, err := os.MkdirTemp("", "mediamtx-playback")
require.NoError(t, err)
defer os.RemoveAll(dir)
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"))
writeSegment2(t, filepath.Join(dir, "mypath", "2008-11-07_11-23-02-500000.mp4"))
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.StringDuration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"mypath": {
Playback: true,
RecordPath: filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f"),
},
},
Parent: &test.NilLogger{},
}
err = s.Initialize()
require.NoError(t, err)
defer s.Close()
v := url.Values{}
v.Set("path", "mypath")
v.Set("start", time.Date(2008, 11, 0o7, 11, 23, 1, 500000000, time.Local).Format(time.RFC3339Nano))
v.Set("duration", "2")
v.Set("format", "fmp4")
u := &url.URL{
Scheme: "http",
Host: "localhost:9996",
Path: "/get",
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.PartSample{
{
Duration: 0,
Payload: []byte{3, 4},
},
{
Duration: 90000,
IsNonSyncSample: true,
Payload: []byte{5, 6},
},
},
},
},
},
{
SequenceNumber: 0,
Tracks: []*fmp4.PartTrack{
{
ID: 1,
BaseTime: 90000,
Samples: []*fmp4.PartSample{
{
Duration: 90000,
Payload: []byte{7, 8},
},
},
},
},
},
}, parts)
}
func TestServerList(t *testing.T) {
dir, err := os.MkdirTemp("", "mediamtx-playback")
require.NoError(t, err)
defer os.RemoveAll(dir)
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"))
writeSegment2(t, filepath.Join(dir, "mypath", "2008-11-07_11-23-02-500000.mp4"))
writeSegment2(t, filepath.Join(dir, "mypath", "2009-11-07_11-23-02-500000.mp4"))
s := &Server{
Address: "127.0.0.1:9996",
ReadTimeout: conf.StringDuration(10 * time.Second),
PathConfs: map[string]*conf.Path{
"mypath": {
Playback: true,
RecordPath: filepath.Join(dir, "%path/%Y-%m-%d_%H-%M-%S-%f"),
},
},
Parent: &test.NilLogger{},
}
err = s.Initialize()
require.NoError(t, err)
defer s.Close()
v := url.Values{}
v.Set("path", "mypath")
u := &url.URL{
Scheme: "http",
Host: "localhost:9996",
Path: "/list",
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)
var out interface{}
err = json.NewDecoder(res.Body).Decode(&out)
require.NoError(t, err)
require.Equal(t, []interface{}{
map[string]interface{}{
"duration": float64(64),
"start": time.Date(2008, 11, 0o7, 11, 22, 0, 500000000, time.Local).Format(time.RFC3339Nano),
},
map[string]interface{}{
"duration": float64(2),
"start": time.Date(2009, 11, 0o7, 11, 23, 2, 500000000, time.Local).Format(time.RFC3339Nano),
},
}, out)
}