mirror of
https://github.com/richard-austin/mp4f-ws-server
synced 2025-09-26 19:21:10 +08:00
Start ffmpeg automatically using cameras.json.
index.html modified to allow selection from multiple streams. index.html renamed to index.gohtml to enable syntax highlighting in IntelliJ Idea
This commit is contained in:
@@ -25,13 +25,13 @@
|
||||
"trigger_recording_on": ""
|
||||
},
|
||||
"netcam_uri": "rtsp://192.168.0.23:554/11",
|
||||
"nms_uri": "rtmp://localhost:1935/nms/stream1",
|
||||
"nms_uri": "http://localhost:8081/live/stream?suuid=stream1",
|
||||
"recording": {
|
||||
"enabled": true,
|
||||
"location": "rec1",
|
||||
"uri": "http://localhost:8084/recording/rec1/"
|
||||
},
|
||||
"uri": "http://localhost:8009/nms/stream1.flv",
|
||||
"uri": "http://localhost:8081/ws/stream/suuid=stream1",
|
||||
"video_height": 1440,
|
||||
"video_width": 2560
|
||||
},
|
||||
@@ -47,13 +47,13 @@
|
||||
"trigger_recording_on": ""
|
||||
},
|
||||
"netcam_uri": "rtsp://192.168.0.23:554/12",
|
||||
"nms_uri": "rtmp://localhost:1935/nms/stream2",
|
||||
"nms_uri": "http://localhost:8081/live/stream?suuid=stream2",
|
||||
"recording": {
|
||||
"enabled": false,
|
||||
"location": "rec2",
|
||||
"uri": "http://localhost:8084/recording/rec2/"
|
||||
},
|
||||
"uri": "http://localhost:8009/nms/stream2.flv",
|
||||
"uri": "http://localhost:8081/ws/stream/suuid=stream2",
|
||||
"video_height": 448,
|
||||
"video_width": 800
|
||||
}
|
||||
@@ -85,13 +85,13 @@
|
||||
"trigger_recording_on": ""
|
||||
},
|
||||
"netcam_uri": "rtsp://192.168.0.26:554/11",
|
||||
"nms_uri": "rtmp://localhost:1935/nms/stream3",
|
||||
"nms_uri": "http://localhost:8081/live/stream?suuid=stream3",
|
||||
"recording": {
|
||||
"enabled": true,
|
||||
"location": "rec3",
|
||||
"uri": "http://localhost:8084/recording/rec3/"
|
||||
},
|
||||
"uri": "http://localhost:8009/nms/stream3.flv",
|
||||
"uri": "http://localhost:8081/ws/stream/suuid=stream3",
|
||||
"video_height": 1080,
|
||||
"video_width": 1920
|
||||
},
|
||||
@@ -107,13 +107,13 @@
|
||||
"trigger_recording_on": "camera2.stream3"
|
||||
},
|
||||
"netcam_uri": "rtsp://192.168.0.26:554/12",
|
||||
"nms_uri": "rtmp://localhost:1935/nms/stream4",
|
||||
"nms_uri": "http://localhost:8081/live/stream?suuid=stream4",
|
||||
"recording": {
|
||||
"enabled": true,
|
||||
"location": "rec4",
|
||||
"uri": "http://localhost:8084/recording/rec4/"
|
||||
},
|
||||
"uri": "http://localhost:8009/nms/stream4.flv",
|
||||
"uri": "http://localhost:8081/ws/stream/suuid=stream4",
|
||||
"video_height": 352,
|
||||
"video_width": 640
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
)
|
||||
|
||||
type StreamC struct {
|
||||
Descr string `json:"descr"`
|
||||
AudioBitRate string `json:"audio_bitrate"`
|
||||
AudioEncoding string `json:"audio_encoding"`
|
||||
AudioSampleRate int `json:"audio_sample_rate"`
|
||||
@@ -31,6 +32,7 @@ type StreamC struct {
|
||||
}
|
||||
|
||||
type Camera struct {
|
||||
Name string `json:"name"`
|
||||
Address string `json:"address"`
|
||||
Streams map[string]StreamC `json:"streams"`
|
||||
}
|
||||
@@ -39,6 +41,16 @@ type Cameras struct {
|
||||
Cameras map[string]Camera `json:"{}"`
|
||||
}
|
||||
|
||||
func (c *Cameras) Suuids() (suuids map[string]string) {
|
||||
suuids = map[string]string{}
|
||||
for _, camera := range c.Cameras {
|
||||
for k, stream := range camera.Streams {
|
||||
suuids[camera.Name+" "+stream.Descr] = k
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func loadConfig() *Cameras {
|
||||
var tmp Cameras
|
||||
data, err := ioutil.ReadFile("src/cameras.json")
|
||||
|
@@ -1,28 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ffmpegFeed() {
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
cmd := exec.Command("bash", "-c", "/usr/bin/ffmpeg -hide_banner -loglevel error -stimeout 1000000 -fflags nobuffer -rtsp_transport tcp -i rtsp://192.168.0.55:554/11 -c:v copy -c:a aac -async 1 -movflags empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof -frag_size 10 -preset superfast -tune zerolatency -f mp4 http://localhost:8081/live/s1?suuid=stream1")
|
||||
stdout, err := cmd.Output()
|
||||
func ffmpegFeed(cameras *Cameras) {
|
||||
for _, camera := range cameras.Cameras {
|
||||
for _, stream := range camera.Streams {
|
||||
go func(camera Camera, stream StreamC) {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
var audio string
|
||||
if stream.AudioBitRate == "0" {
|
||||
audio = "-an"
|
||||
} else {
|
||||
audio = "-c:a aac -ar " + stream.AudioBitRate
|
||||
}
|
||||
cmdStr := fmt.Sprintf("/usr/bin/ffmpeg -hide_banner -loglevel error -stimeout 1000000 -fflags nobuffer -rtsp_transport tcp -i %s -c:v copy %s -async 1 -movflags empty_moov+omit_tfhd_offset+frag_keyframe+default_base_moof -frag_size 10 -preset superfast -tune zerolatency -f mp4 %s", stream.NetcamUri, audio, stream.NMSUri)
|
||||
cmd := exec.Command("bash", "-c", cmdStr)
|
||||
stdout, err := cmd.Output()
|
||||
|
||||
if err != nil {
|
||||
ee := err.(*exec.ExitError)
|
||||
if ee != nil {
|
||||
log.Errorf("ffmpeg error :- %s, %s", string(ee.Stderr), ee.Error())
|
||||
} else {
|
||||
log.Errorf("ffmpeg error :- %s", err.Error())
|
||||
if err != nil {
|
||||
ee := err.(*exec.ExitError)
|
||||
if ee != nil {
|
||||
log.Errorf("ffmpeg (%s:%s):- %s, %s", camera.Name, stream.Descr, string(ee.Stderr), ee.Error())
|
||||
} else {
|
||||
log.Errorf("ffmpeg (%s:%s):- :- %s", camera.Name, stream.Descr, err.Error())
|
||||
}
|
||||
} else if stdout != nil {
|
||||
log.Infof("ffmpeg output (%s:%s):- %s ", camera.Name, stream.Descr, string(stdout))
|
||||
}
|
||||
}
|
||||
} else if stdout != nil {
|
||||
log.Infof("ffmpeg output:- %s ", string(stdout))
|
||||
}
|
||||
}(camera, stream)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
36
src/http.go
36
src/http.go
@@ -63,17 +63,32 @@ func ReadBox(readCloser io.ReadCloser, data []byte, queue chan Packet) (numOfByt
|
||||
func serveHTTP() {
|
||||
router := gin.Default()
|
||||
gin.SetMode(gin.DebugMode)
|
||||
router.LoadHTMLFiles("web/index.html")
|
||||
|
||||
// For web page
|
||||
router.LoadHTMLFiles("web/index.gohtml")
|
||||
suuids := cameras.Suuids()
|
||||
// Get the name of the first stream
|
||||
var first Camera
|
||||
var firstStream string
|
||||
for _, first = range cameras.Cameras {
|
||||
for firstStream, _ = range first.Streams {
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
// For web page without suuid
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
//path, err := os.Getwd()
|
||||
//if err != nil {
|
||||
// log.Println(err)
|
||||
//}
|
||||
//fmt.Println(path)
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
// "suuid": c.Param("suuid"),
|
||||
|
||||
c.HTML(http.StatusOK, "index.gohtml", gin.H{
|
||||
"suuidMap": suuids,
|
||||
"suuid": firstStream,
|
||||
})
|
||||
})
|
||||
|
||||
// For web page with suuid
|
||||
router.GET("/:suuid", func(c *gin.Context) {
|
||||
|
||||
c.HTML(http.StatusOK, "index.gohtml", gin.H{
|
||||
"suuidMap": suuids,
|
||||
"suuid": c.Param("suuid"),
|
||||
})
|
||||
})
|
||||
// For ffmpeg to write to
|
||||
@@ -146,6 +161,7 @@ func serveHTTP() {
|
||||
log.Tracef("%d bytes received", numOfByte)
|
||||
}
|
||||
})
|
||||
router.StaticFS("/web", http.Dir("web"))
|
||||
|
||||
// For http connections from ffmpeg to read from (for recordings)
|
||||
// This does not send the codec info ahead of ftyp and moov
|
||||
|
@@ -8,6 +8,8 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var cameras *Cameras
|
||||
|
||||
func main() {
|
||||
var customFormatter = log.TextFormatter{}
|
||||
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
|
||||
@@ -24,8 +26,8 @@ func main() {
|
||||
Compress: true,
|
||||
}
|
||||
log.SetOutput(io.MultiWriter(os.Stdout, lumberjackLogger))
|
||||
cameras := loadConfig()
|
||||
cameras = loadConfig()
|
||||
_ = cameras
|
||||
ffmpegFeed()
|
||||
ffmpegFeed(cameras)
|
||||
serveHTTP()
|
||||
}
|
||||
|
3719
web/css/bootstrap-grid.css
vendored
Normal file
3719
web/css/bootstrap-grid.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
web/css/bootstrap-grid.css.map
Normal file
1
web/css/bootstrap-grid.css.map
Normal file
File diff suppressed because one or more lines are too long
7
web/css/bootstrap-grid.min.css
vendored
Normal file
7
web/css/bootstrap-grid.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
web/css/bootstrap-grid.min.css.map
Normal file
1
web/css/bootstrap-grid.min.css.map
Normal file
File diff suppressed because one or more lines are too long
331
web/css/bootstrap-reboot.css
vendored
Normal file
331
web/css/bootstrap-reboot.css
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 The Bootstrap Authors
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
-webkit-text-decoration-skip-ink: none;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button:not(:disabled),
|
||||
[type="button"]:not(:disabled),
|
||||
[type="reset"]:not(:disabled),
|
||||
[type="submit"]:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
1
web/css/bootstrap-reboot.css.map
Normal file
1
web/css/bootstrap-reboot.css.map
Normal file
File diff suppressed because one or more lines are too long
8
web/css/bootstrap-reboot.min.css
vendored
Normal file
8
web/css/bootstrap-reboot.min.css
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.3.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 The Bootstrap Authors
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
1
web/css/bootstrap-reboot.min.css.map
Normal file
1
web/css/bootstrap-reboot.min.css.map
Normal file
File diff suppressed because one or more lines are too long
10038
web/css/bootstrap.css
vendored
Normal file
10038
web/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
web/css/bootstrap.css.map
Normal file
1
web/css/bootstrap.css.map
Normal file
File diff suppressed because one or more lines are too long
7
web/css/bootstrap.min.css
vendored
Normal file
7
web/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
web/css/bootstrap.min.css.map
Normal file
1
web/css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
@@ -6,21 +6,47 @@ https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragme
|
||||
-->
|
||||
|
||||
|
||||
<html>
|
||||
<html lang="eng">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<meta http-equiv="Last-Modified" content="0">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, mustrevalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
|
||||
<link rel="stylesheet" href="web/css/bootstrap.min.css">
|
||||
<script src="web/js/bootstrap.min.js"></script>
|
||||
<script src="web/js/bootstrap.bundle.js"></script>
|
||||
<head>
|
||||
<title>WebSocket MSE demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<!-- muted attribute required for chrome autoplay-->
|
||||
<!-- <audio id="stream_live" controls autoplay>Your browser does not support the audio tag.</audio>-->
|
||||
<video id="stream_live"
|
||||
width="640" height="480"
|
||||
controls autoplay
|
||||
muted="muted"
|
||||
preload="auto">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
<h2 align=center>
|
||||
Stream = {{ .suuid }}
|
||||
</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-3">
|
||||
<div class="list-group">
|
||||
{{ range $k, $v := .suuidMap }}
|
||||
<a href="/{{ $v }}" id="{{ $v }}" class="list-group-item list-group-item-action">{{ $k }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<video id="stream_live"
|
||||
style="width: 100%"
|
||||
controls autoplay
|
||||
preload="auto">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<style>
|
||||
/*video::-webkit-media-controls {*/
|
||||
@@ -106,7 +132,7 @@ https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragme
|
||||
source_buffer = ms.addSourceBuffer(codecPars);
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/source_buffer/mode
|
||||
// let myMode = source_buffer.mode;
|
||||
// let myMode = source_buffer.mode;
|
||||
source_buffer.mode = 'sequence';
|
||||
// source_buffer.mode = 'segments'; // TODO: should we use this instead?
|
||||
|
||||
@@ -116,7 +142,7 @@ https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragme
|
||||
let latest = stream_live.duration;
|
||||
if ((stream_live.duration >= buffering_sec) &&
|
||||
((latest - stream_live.currentTime) > buffering_sec_seek)) {
|
||||
// let thisSeekTime = Date.now()
|
||||
// let thisSeekTime = Date.now()
|
||||
// if(lastSeekTime !== undefined && (thisSeekTime - lastSeekTime < 10000 )) {
|
||||
// console.log("Last seek time was ", (thisSeekTime-lastSeekTime)/1000," increasing buffering seconds seek from ", buffering_sec_seek," to " ,buffering_sec_seek *= 1.2);
|
||||
// }
|
||||
@@ -126,7 +152,7 @@ https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragme
|
||||
df = (stream_live.duration - stream_live.currentTime); // this much away from the last available frame
|
||||
if ((df > buffering_sec_seek)) {
|
||||
seek_to = stream_live.duration - buffering_sec_seek_distance;
|
||||
stream_live.currentTime = seek_to;
|
||||
stream_live.currentTime = seek_to;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,8 +203,13 @@ https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragme
|
||||
function opened() { // MediaSource object is ready to go
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/duration
|
||||
// ws = new WebSocket("ws://localhost:8089/ws/");
|
||||
let urlStr = window.location.href
|
||||
let url = new URL(urlStr);
|
||||
let suuid = {{.suuid}}
|
||||
if (suuid === undefined)
|
||||
suuid = "stream1";
|
||||
let host = window.location.host
|
||||
ws = new WebSocket("ws://" + host + "/ws/s1?suuid=stream1");
|
||||
ws = new WebSocket("ws://" + host + "/ws/stream?suuid=" + suuid);
|
||||
ws.binaryType = "arraybuffer";
|
||||
ws.onmessage = function (event) {
|
||||
putPacket(event.data);
|
||||
@@ -189,13 +220,18 @@ https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragme
|
||||
ms.addEventListener('sourceopen', opened, false);
|
||||
|
||||
// get reference to video
|
||||
stream_live = document.getElementById('stream_live');
|
||||
stream_live = document.getElementById('stream_live');
|
||||
//stream_live.latencyHint = 0.075
|
||||
// set mediasource as source of video
|
||||
stream_live.src = window.URL.createObjectURL(ms);
|
||||
// stream_live.controls = false;
|
||||
// stream_live.addEventListener("mouseover", () => {stream_live.controls=true;});
|
||||
// stream_live.addEventListener("mouseout", () => {stream_live.controls=false;});
|
||||
let activeLink = document.getElementById({{.suuid}});
|
||||
console.log(activeLink);
|
||||
console.log(activeLink.classList);
|
||||
activeLink.classList.add("active");
|
||||
|
||||
}
|
||||
|
||||
|
7013
web/js/bootstrap.bundle.js
vendored
Normal file
7013
web/js/bootstrap.bundle.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
web/js/bootstrap.min.js
vendored
Normal file
7
web/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user