Skip to content

Commit

Permalink
feat: Much better information page and list of assets for both live a…
Browse files Browse the repository at this point in the history
…nd vod
  • Loading branch information
tobbee committed Aug 24, 2023
1 parent 6b27e0b commit 3ef6635
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 49 deletions.
12 changes: 10 additions & 2 deletions cmd/livesim2/app/asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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")
Expand Down
57 changes: 43 additions & 14 deletions cmd/livesim2/app/handler_assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,68 @@
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)
}
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)
}
10 changes: 9 additions & 1 deletion cmd/livesim2/app/handler_index.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions cmd/livesim2/app/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 37 additions & 0 deletions cmd/livesim2/app/templates/assets_vod.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
<title>Local livesim2 assets</title>
</head>
<body>
<main class="container">
{{$h := .Host}}
<hgroup>
<h1>Available livesim2 VoD assets!</h1>
<p>host={{$h}}</p>
</hgroup>
<p>The following is a list of assets and MPDs of these assets.<br>
They can be streamed with URLs like {{(print $h "/vod/<asset>/<mpd>")}}</p>

{{range $a := .Assets}}
<section>
<p><strong>{{$a.Path}}</strong></p>
<table role="grid">
<tr><th>MPD URL</th><th>Description</th><th>Duration</th></tr>
{{range $m := $a.MPDs}}
<tr>
{{$url := (print $h "/vod/" $a.Path "/" $m.Path)}}
<td><span data-tooltip="Copy to clipboard" onclick="navigator.clipboard.writeText({{$url}})">{{$m.Path}}</span></td>
<td>{{$m.Desc}}</td>
<td>{{$m.Dur}}</td>
</tr>
{{end}}
</table>
</section>
{{end}}
</main>
</body>
</html>
72 changes: 49 additions & 23 deletions cmd/livesim2/app/templates/welcome.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to livesim2</title>
<link rel="stylesheet" href="static/table.css" />
</head>
<body>
<h1>Welcome to livesim2</h1>

<table>
<tr><th>The following URLs should work on this server</th></tr>
<tr><td><a href="{{.}}/assets">/assets</a> to get a list of assets (each can have multiple MPDs)</td></tr>
<tr><td><strong>{{.}}/vod/...</strong> to stream any of the VoD assets directly</td></tr>
<tr><td><strong>{{.}}/livesim2/...</strong> to stream the VoD assets as they are converted to "live" with parameters</td></tr>
<tr><td><a href="{{.}}/healthz">/healthz</a> to check if server is running</td></tr>
<tr><td><a href="{{.}}/config">/config<a> to get the current config of the server</td></tr>
<tr><td><a href="{{.}}/metrics">/metrics</a> to get Prometheus metrics for the system and all streaming content requests</td></tr>
<tr><td><a href="{{.}}/static/features.html">/static/features.html</a> compares features between livesim2 and livesim1</td></tr>
<tr><td><a href="https://github.com/Dash-Industry-Forum/livesim2/wiki/URL-Parameters">livesim2-wiki</a> compares url parameters between livesim2 and livesim1</td></tr>
</table>
</body>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
<title>livesim2</title>
</head>
<body>
<main class="container">
<hgroup>
<h1>Welcome to livesim2!</h1>
<p>Running at {{.}}</p>
</hgroup>


<p>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.<p>

<p>In addition, a plethora of properties of the stream can be changed by specifying
<a href="https://github.com/Dash-Industry-Forum/livesim2/wiki/URL-Parameters" target="_blank">parameters</a> in the URLs.</p>

<h4>Local URLs</h4>

The following URLs should work at {{.Host}}:<br>
<ul>
<li><a href="{{.Host}}/assets">/assets</a> provides a list with assets and their MPDs with easy URL copying</li>
<li><a href="https://github.com/Dash-Industry-Forum/livesim2/wiki/URL-Parameters">livesim2-wiki</a> compares url parameters between livesim2 and livesim1</li>
<li><strong>/livesim2/</strong> is the correct URL start to live stream an asset. Add parameters before the MPD.</li>
<li><a href="{{.Host}}/vod">/vod</a> provides a list of VoD assets and MPDs with easy URL copying</li>
<li><a href="{{.Host}}/healthz">/healthz</a> to check if server is running</li>
<li><a href="{{.Host}}/config">/config</a> to get the current config of the server</li>
<li><a href="{{.Host}}/metrics">/metrics</a> to get Prometheus metrics for the system and all streaming content requests</li>
<li><a href="{{.Host}}/static/features.html">/static/features.html</a> compares features between livesim2 and livesim1</li>
</ul>

<h4>Further information</h4>

For more information about the project, see

<ul>
<li><a href="https://github.com/Dash-Industry-Forum/livesim2" target="_blank">livesim2 Github project</a></li>
<li><a href="https://github.com/Dash-Industry-Forum/livesim2/wiki" target="_blank">livesim2 Github Wiki</a></li>
<li><a href="https://github.com/Dash-Industry-Forum/livesim2/wiki/URL-Parameters" target="_blank">wiki list of url parameters</a></li>
</ul>

<p>Version: {{.Version}}</p>
</main>
</body>
</html>
7 changes: 4 additions & 3 deletions cmd/livesim2/app/templates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
2 changes: 1 addition & 1 deletion cmd/livesim2/app/testdata/assets/testpic_2s/Manifest.mpd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash-if-simple" maxSegmentDuration="PT2S" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT8S">
<ProgramInformation>
<Title>Media Presentation Description from DASH-IF live simulator 2</Title>
<Title>640x360@30 video, 48kHz audio, 2s segments</Title>
</ProgramInformation>
<Period id="one" start="PT0S">
<AdaptationSet contentType="audio" mimeType="audio/mp4" lang="en" segmentAlignment="true" startWithSAP="1">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash-if-simple" maxSegmentDuration="PT2S" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT8S">
<ProgramInformation>
<Title>Media Presentation Description from DASH-IF live simulator 2</Title>
<Title>640x360@30 video, 48kHz audio, two imsc1 subtitle tracks, 2s segments</Title>
</ProgramInformation>
<Period id="one" start="PT0S">
<AdaptationSet contentType="audio" mimeType="audio/mp4" lang="en" segmentAlignment="true" startWithSAP="1">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011,urn:com:dashif:dash264" maxSegmentDuration="PT2S" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT1H">
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011,urn:com:dashif:dash264" maxSegmentDuration="PT2S" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT8S">
<ProgramInformation>
<Title>Media Presentation Description from DASH-IF live simulator</Title>
<Title>640x360@30 video, 48kHz audio, DASH-IF thumnail track, 2s segments</Title>
</ProgramInformation>
<Period id="precambrian" start="PT0S">
<AdaptationSet contentType="audio" mimeType="audio/mp4" lang="en" segmentAlignment="true" startWithSAP="1">
Expand Down
3 changes: 3 additions & 0 deletions cmd/livesim2/app/testdata/assets/testpic_8s/Manifest.mpd
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?xml version="1.0"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" minBufferTime="PT1S" type="static" mediaPresentationDuration="PT8S" maxSegmentDuration="PT8S" profiles="urn:mpeg:dash:profile:full:2011">
<ProgramInformation>
<Title>640x360@30 video, 48kHz audio, 8s segments</Title>
</ProgramInformation>
<Period id="livesim">
<AdaptationSet contentType="audio" segmentAlignment="true" lang="eng">
<SegmentTemplate timescale="48000" media="$RepresentationID$/$Number$.m4s" startNumber="1" duration="384000" initialization="$RepresentationID$/init.mp4"/>
Expand Down
7 changes: 5 additions & 2 deletions internal/mpddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down

0 comments on commit 3ef6635

Please sign in to comment.