diff --git a/cmd/livesim2/app/asset.go b/cmd/livesim2/app/asset.go
index 9d53650..201ae6c 100644
--- a/cmd/livesim2/app/asset.go
+++ b/cmd/livesim2/app/asset.go
@@ -38,7 +38,7 @@ func newAssetMgr(vodFS fs.FS, repDataDir string, writeRepData bool) *assetMgr {
type assetMgr struct {
vodFS fs.FS
- assets map[string]*asset
+ assets map[string]*asset // the key is the asset path
repDataDir string
writeRepData bool
}
@@ -100,7 +100,6 @@ func (am *assetMgr) loadAsset(mpdPath string) error {
return fmt.Errorf("read MPD: %w", err)
}
md.MPDStr = string(data)
- asset.MPDs[mpdName] = md
mpd, err := m.ReadFromString(md.MPDStr)
if err != nil {
@@ -115,6 +114,15 @@ func (am *assetMgr) loadAsset(mpdPath string) error {
return fmt.Errorf("mpd type is not static")
}
+ if len(mpd.ProgramInformation) > 0 {
+ pi := mpd.ProgramInformation[0]
+ if pi.Title != "" {
+ md.Title = pi.Title
+ }
+ }
+ md.Dur = mpd.MediaPresentationDuration.String()
+ asset.MPDs[mpdName] = md
+
for _, as := range mpd.Periods[0].AdaptationSets {
if as.SegmentTemplate == nil {
return fmt.Errorf("no SegmentTemplate in adaptation set")
diff --git a/cmd/livesim2/app/handler_assets.go b/cmd/livesim2/app/handler_assets.go
index 90acc41..09f4ca3 100644
--- a/cmd/livesim2/app/handler_assets.go
+++ b/cmd/livesim2/app/handler_assets.go
@@ -5,18 +5,31 @@
package app
import (
- "encoding/json"
"net/http"
- "path"
"sort"
+ "strings"
)
-type assetsInfo struct {
- MPDs []string
+type AssetsInfo struct {
+ Host string
+ Assets []*AssetInfo
+}
+
+type AssetInfo struct {
+ Path string
+ LoopDurMS int
+ MPDs []MPDInfo
+}
+
+type MPDInfo struct {
+ Path string
+ Desc string
+ Dur string
}
// assetHandlerFunc returns information about assets
func (s *Server) assetsHandlerFunc(w http.ResponseWriter, r *http.Request) {
+ forVod := strings.HasPrefix(r.URL.String(), "/vod")
assets := make([]*asset, 0, len(s.assetMgr.assets))
for _, a := range s.assetMgr.assets {
assets = append(assets, a)
@@ -24,20 +37,36 @@ func (s *Server) assetsHandlerFunc(w http.ResponseWriter, r *http.Request) {
sort.Slice(assets, func(i, j int) bool {
return assets[i].AssetPath < assets[j].AssetPath
})
- aInfo := assetsInfo{}
- mpds := make([]string, 0, len(assets))
+ aInfo := AssetsInfo{
+ Host: fullHost(s.Cfg.Host, r),
+ Assets: make([]*AssetInfo, 0, len(assets)),
+ }
for _, asset := range assets {
- for m := range asset.MPDs {
- fullURL := path.Join(asset.AssetPath, m)
- mpds = append(mpds, fullURL)
+ mpds := make([]MPDInfo, 0, len(asset.MPDs))
+ for _, mpd := range asset.MPDs {
+ mpds = append(mpds, MPDInfo{
+ Path: mpd.Name,
+ Desc: mpd.Title,
+ Dur: mpd.Dur,
+ })
+ }
+ sort.Slice(mpds, func(i, j int) bool {
+ return mpds[i].Path < mpds[j].Path
+ })
+ assetInfo := AssetInfo{
+ Path: asset.AssetPath,
+ LoopDurMS: asset.LoopDurMS,
+ MPDs: mpds,
}
+ aInfo.Assets = append(aInfo.Assets, &assetInfo)
+ }
+ w.Header().Set("Content-Type", "text/html")
+ templateName := "assets.html"
+ if forVod {
+ templateName = "assets_vod.html"
}
- sort.Strings(mpds)
- aInfo.MPDs = append(aInfo.MPDs, mpds...)
- body, err := json.MarshalIndent(aInfo, "", " ")
+ err := s.htmlTemplates.ExecuteTemplate(w, templateName, aInfo)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
- w.Header().Set("Content-Type", "application/json")
- _, _ = w.Write(body)
}
diff --git a/cmd/livesim2/app/handler_index.go b/cmd/livesim2/app/handler_index.go
index 90915ed..ef5265b 100644
--- a/cmd/livesim2/app/handler_index.go
+++ b/cmd/livesim2/app/handler_index.go
@@ -8,12 +8,20 @@ import (
"io/fs"
"net/http"
"strconv"
+
+ "github.com/Dash-Industry-Forum/livesim2/internal"
)
+type welcomeInfo struct {
+ Host string
+ Version string
+}
+
// indexHandlerFunc handles access to /.
func (s *Server) indexHandlerFunc(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
- err := s.htmlTemplates.ExecuteTemplate(w, "welcome.html", fullHost(s.Cfg.Host, r))
+ wi := welcomeInfo{Host: fullHost(s.Cfg.Host, r), Version: internal.GetVersion()}
+ err := s.htmlTemplates.ExecuteTemplate(w, "welcome.html", wi)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
diff --git a/cmd/livesim2/app/routes.go b/cmd/livesim2/app/routes.go
index 213759e..fdbc803 100644
--- a/cmd/livesim2/app/routes.go
+++ b/cmd/livesim2/app/routes.go
@@ -29,6 +29,7 @@ func (s *Server) Routes(ctx context.Context) error {
s.Router.MethodFunc("GET", "/favicon.ico", s.favIconFunc)
s.Router.MethodFunc("GET", "/config", s.configHandlerFunc)
s.Router.MethodFunc("GET", "/assets", s.assetsHandlerFunc)
+ s.Router.MethodFunc("GET", "/vod", s.assetsHandlerFunc)
s.Router.MethodFunc("GET", "/static/*", s.embeddedStaticHandlerFunc)
s.Router.MethodFunc("HEAD", "/static/*", s.embeddedStaticHandlerFunc)
// LiveRouter is mounted at /livesim2
diff --git a/cmd/livesim2/app/templates/assets_vod.html b/cmd/livesim2/app/templates/assets_vod.html
new file mode 100644
index 0000000..8c2e50d
--- /dev/null
+++ b/cmd/livesim2/app/templates/assets_vod.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ Local livesim2 assets
+
+
+
+ {{$h := .Host}}
+
+ Available livesim2 VoD assets!
+ host={{$h}}
+
+ The following is a list of assets and MPDs of these assets.
+ They can be streamed with URLs like {{(print $h "/vod//")}}
+
+ {{range $a := .Assets}}
+
+ {{$a.Path}}
+
+ MPD URL Description Duration
+ {{range $m := $a.MPDs}}
+
+ {{$url := (print $h "/vod/" $a.Path "/" $m.Path)}}
+ {{$m.Path}}
+ {{$m.Desc}}
+ {{$m.Dur}}
+
+ {{end}}
+
+
+ {{end}}
+
+
+
\ No newline at end of file
diff --git a/cmd/livesim2/app/templates/welcome.html b/cmd/livesim2/app/templates/welcome.html
index 6eaae94..ae8b020 100644
--- a/cmd/livesim2/app/templates/welcome.html
+++ b/cmd/livesim2/app/templates/welcome.html
@@ -1,25 +1,51 @@
-
+
-
-
-
-
- Welcome to livesim2
-
-
-
- Welcome to livesim2
-
-
- The following URLs should work on this server
- /assets to get a list of assets (each can have multiple MPDs)
- {{.}}/vod/... to stream any of the VoD assets directly
- {{.}}/livesim2/... to stream the VoD assets as they are converted to "live" with parameters
- /healthz to check if server is running
- /config to get the current config of the server
- /metrics to get Prometheus metrics for the system and all streaming content requests
- /static/features.html compares features between livesim2 and livesim1
- livesim2-wiki compares url parameters between livesim2 and livesim1
-
-
+
+
+
+
+ livesim2
+
+
+
+
+ Welcome to livesim2!
+ Running at {{.}}
+
+
+
+ livesim2 is a simulator for MPEG-DASH live streaming developed by DASH-IF. It uses VoD assets
+ and converts them to wall-clock synchronized "live" streams, by rewriting
+ timestamps, segment numbers, and segment URLs.
+
+
In addition, a plethora of properties of the stream can be changed by specifying
+ parameters in the URLs.
+
+ Local URLs
+
+ The following URLs should work at {{.Host}}:
+
+ /assets provides a list with assets and their MPDs with easy URL copying
+ livesim2-wiki compares url parameters between livesim2 and livesim1
+ /livesim2/ is the correct URL start to live stream an asset. Add parameters before the MPD.
+ /vod provides a list of VoD assets and MPDs with easy URL copying
+ /healthz to check if server is running
+ /config to get the current config of the server
+ /metrics to get Prometheus metrics for the system and all streaming content requests
+ /static/features.html compares features between livesim2 and livesim1
+
+
+ Further information
+
+ For more information about the project, see
+
+
+
+ Version: {{.Version}}
+
+
diff --git a/cmd/livesim2/app/templates_test.go b/cmd/livesim2/app/templates_test.go
index ed926f3..25ce21c 100644
--- a/cmd/livesim2/app/templates_test.go
+++ b/cmd/livesim2/app/templates_test.go
@@ -72,10 +72,11 @@ func TestHTMLTemplates(t *testing.T) {
templateRoot := os.DirFS("templates")
textTemplates, err := compileHTMLTemplates(templateRoot, "")
require.NoError(t, err)
- require.Equal(t, `; defined templates are: "welcome.html"`, textTemplates.DefinedTemplates())
+ require.Equal(t, 3, len(textTemplates.Templates()))
var buf bytes.Buffer
- err = textTemplates.ExecuteTemplate(&buf, "welcome.html", "/prefix")
+ wi := welcomeInfo{Host: "http://localhost:8888", Version: "1.2.3"}
+ err = textTemplates.ExecuteTemplate(&buf, "welcome.html", wi)
require.NoError(t, err)
welcomeStr := buf.String()
- require.Greater(t, strings.Index(welcomeStr, `href="/prefix/assets"`), 0)
+ require.Greater(t, strings.Index(welcomeStr, `href="http://localhost:8888/assets"`), 0)
}
diff --git a/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest.mpd b/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest.mpd
index c0fa084..4793f05 100644
--- a/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest.mpd
+++ b/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest.mpd
@@ -1,7 +1,7 @@
- Media Presentation Description from DASH-IF live simulator 2
+ 640x360@30 video, 48kHz audio, 2s segments
diff --git a/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_imsc1.mpd b/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_imsc1.mpd
index e18096d..efef96c 100644
--- a/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_imsc1.mpd
+++ b/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_imsc1.mpd
@@ -1,7 +1,7 @@
- Media Presentation Description from DASH-IF live simulator 2
+ 640x360@30 video, 48kHz audio, two imsc1 subtitle tracks, 2s segments
diff --git a/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_thumbs.mpd b/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_thumbs.mpd
index fb45bd5..d31ec61 100644
--- a/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_thumbs.mpd
+++ b/cmd/livesim2/app/testdata/assets/testpic_2s/Manifest_thumbs.mpd
@@ -1,7 +1,7 @@
-
+
- Media Presentation Description from DASH-IF live simulator
+ 640x360@30 video, 48kHz audio, DASH-IF thumnail track, 2s segments
diff --git a/cmd/livesim2/app/testdata/assets/testpic_8s/Manifest.mpd b/cmd/livesim2/app/testdata/assets/testpic_8s/Manifest.mpd
index ecb0a5b..9bd2133 100644
--- a/cmd/livesim2/app/testdata/assets/testpic_8s/Manifest.mpd
+++ b/cmd/livesim2/app/testdata/assets/testpic_8s/Manifest.mpd
@@ -1,5 +1,8 @@
+
+ 640x360@30 video, 48kHz audio, 8s segments
+
diff --git a/internal/mpddata.go b/internal/mpddata.go
index dfbb4ac..12c0c4b 100644
--- a/internal/mpddata.go
+++ b/internal/mpddata.go
@@ -19,7 +19,10 @@ const (
type MPDData struct {
Name string `json:"name"`
OrigURI string `json:"originURI"`
- MPDStr string `json:"-"`
+ Title string `json:"-"`
+ // Dur is MediaPresentationDuration
+ Dur string `json:"-"`
+ MPDStr string `json:"-"`
}
// WriteMPDData to file on disk.
@@ -41,7 +44,7 @@ func WriteMPDData(dirPath string, name, uri string) error {
return err
}
}
- mpds = append(mpds, MPDData{name, uri, ""})
+ mpds = append(mpds, MPDData{Name: name, OrigURI: uri})
outData, err := json.MarshalIndent(mpds, "", " ")
if err != nil {
return err