From 849162af0639ef6eeb0d64e3e5e0be1532bdccee Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 00:35:05 +0100 Subject: [PATCH 1/7] add basic config file --- .gitignore | 1 + main.go | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 8977081..8f170d3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ prism dist/ .envrc +/outputs.txt \ No newline at end of file diff --git a/main.go b/main.go index 8ded7d7..64e983b 100644 --- a/main.go +++ b/main.go @@ -1,9 +1,11 @@ package main import ( + "bufio" "flag" "fmt" "os" + "strings" "time" // TODO: switch to joy5? @@ -12,7 +14,8 @@ import ( ) var ( - bind = flag.String("bind", ":1935", "bind address") + bind = flag.String("bind", ":1935", "bind address") + outputs_file = flag.String("outputs-file", "outputs.txt", "output URLs file") ) type RTMPConnection struct { @@ -111,13 +114,48 @@ func (r *RTMPConnection) Loop() error { return nil } +func readURLsFromFile(filename string) ([]string, error) { + file, err := os.Open(filename) + if err != nil { + return nil, err + } + defer file.Close() + + var urls []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + urls = append(urls, strings.TrimSpace(scanner.Text())) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return urls, nil +} + func main() { flag.Parse() - if len(flag.Args()) == 0 { - fmt.Println("usage: prism URL [URL...]") + var urls []string + var err1 error + if *outputs_file != "" { + if _, err := os.Stat("outputs.txt"); err == nil { + urls, err1 = readURLsFromFile(*outputs_file) + fmt.Println("Read", len(urls), "output URLs from", *outputs_file) + } + } + // if flag.args is over 0, add all args to urls + if len(flag.Args()) > 0 { + urls = append(urls, flag.Args()...) + } + + if err1 != nil { + fmt.Println("Error reading output URLs from file:", err1) os.Exit(1) } + fmt.Println("Found", len(urls), "output URLs") + fmt.Println("Starting RTMP server...") config := &rtmp.Config{ ChunkSize: 128, @@ -126,8 +164,9 @@ func main() { server := rtmp.NewServer(config) server.Addr = *bind - conns := make([]*RTMPConnection, len(flag.Args())) - for i, u := range flag.Args() { + conns := make([]*RTMPConnection, len(urls)) + for i, u := range urls { + fmt.Println(i, ":", u) conns[i] = NewRTMPConnection(u) } From d561fcb18c13895160e8ae1047c940a372466403 Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 00:46:38 +0100 Subject: [PATCH 2/7] changed config to json and prepare for transcoding options --- config.json | 9 ++++++ main.go | 83 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 config.json diff --git a/config.json b/config.json new file mode 100644 index 0000000..e0eb2f5 --- /dev/null +++ b/config.json @@ -0,0 +1,9 @@ +[ + { + "enabled": true, + "url": "rtmp://localhost/live/test", + "width": 1920, + "height": 1080, + "bitrate": 6000 + } +] diff --git a/main.go b/main.go index 64e983b..45ade7d 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,10 @@ package main import ( - "bufio" + "encoding/json" "flag" "fmt" "os" - "strings" "time" // TODO: switch to joy5? @@ -14,10 +13,18 @@ import ( ) var ( - bind = flag.String("bind", ":1935", "bind address") - outputs_file = flag.String("outputs-file", "outputs.txt", "output URLs file") + bind = flag.String("bind", ":1935", "bind address") + config_file = flag.String("config", "config.json", "config file") ) +type URLConfig struct { + Enabled bool `json:"enabled"` + URL string `json:"url"` + Width int `json:"width"` + Height int `json:"height"` + Bitrate int `json:"bitrate"` +} + type RTMPConnection struct { url string conn *rtmp.Conn @@ -114,60 +121,72 @@ func (r *RTMPConnection) Loop() error { return nil } -func readURLsFromFile(filename string) ([]string, error) { +func readConfigFromFile(filename string) ([]URLConfig, error) { file, err := os.Open(filename) if err != nil { return nil, err } defer file.Close() - var urls []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - urls = append(urls, strings.TrimSpace(scanner.Text())) - } - - if err := scanner.Err(); err != nil { + var configs []URLConfig + decoder := json.NewDecoder(file) + err = decoder.Decode(&configs) + if err != nil { return nil, err } - return urls, nil + return configs, nil } func main() { flag.Parse() - var urls []string - var err1 error - if *outputs_file != "" { - if _, err := os.Stat("outputs.txt"); err == nil { - urls, err1 = readURLsFromFile(*outputs_file) - fmt.Println("Read", len(urls), "output URLs from", *outputs_file) + var config []URLConfig + var err error + if *config_file != "" { + if _, err := os.Stat(*config_file); err == nil { + config, err = readConfigFromFile(*config_file) + fmt.Println("Read", len(config), "outputs from", *config_file) } } - // if flag.args is over 0, add all args to urls - if len(flag.Args()) > 0 { - urls = append(urls, flag.Args()...) + for _, arg := range flag.Args() { + config = append(config, URLConfig{Enabled: true, URL: arg}) } - if err1 != nil { - fmt.Println("Error reading output URLs from file:", err1) + if len(config) < 1 { + config = append(config, URLConfig{Enabled: true, URL: "rtmp://localhost/live/test", Width: 1920, Height: 1080, Bitrate: 6000}) + file, err := os.Create(*config_file) + if err != nil { + fmt.Println("Error creating config file:", err) + os.Exit(1) + } + defer file.Close() + encoder := json.NewEncoder(file) + encoder.SetIndent("", " ") + err = encoder.Encode(config) + + if err != nil { + fmt.Println("Error writing config file:", err) + os.Exit(1) + } + fmt.Println("Created default config file", *config_file, "with one example output") os.Exit(1) } - fmt.Println("Found", len(urls), "output URLs") + fmt.Println("Found", len(config), "outputs") fmt.Println("Starting RTMP server...") - config := &rtmp.Config{ + rtmp_config := &rtmp.Config{ ChunkSize: 128, BufferSize: 0, } - server := rtmp.NewServer(config) + server := rtmp.NewServer(rtmp_config) server.Addr = *bind - conns := make([]*RTMPConnection, len(urls)) - for i, u := range urls { - fmt.Println(i, ":", u) - conns[i] = NewRTMPConnection(u) + conns := make([]*RTMPConnection, len(config)) + for i, u := range config { + // print u object + fmt.Println(u) + conns[i] = NewRTMPConnection(u.URL) } server.HandlePublish = func(conn *rtmp.Conn) { @@ -214,7 +233,7 @@ func main() { } fmt.Println("Waiting for incoming connection...") - err := server.ListenAndServe() + err = server.ListenAndServe() if err != nil { fmt.Println(err) os.Exit(1) From 4443365b96dc8dbaf9913a05eba0db98b0f87488 Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 01:02:31 +0100 Subject: [PATCH 3/7] add webinterface --- .gitignore | 4 ++- config.example.json | 9 +++++ html/edit_config.html | 37 ++++++++++++++++++++ main.go | 81 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 config.example.json create mode 100644 html/edit_config.html diff --git a/.gitignore b/.gitignore index 8f170d3..ae4290a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,6 @@ prism dist/ .envrc -/outputs.txt \ No newline at end of file +/outputs.txt +/config.json +config.json diff --git a/config.example.json b/config.example.json new file mode 100644 index 0000000..e0eb2f5 --- /dev/null +++ b/config.example.json @@ -0,0 +1,9 @@ +[ + { + "enabled": true, + "url": "rtmp://localhost/live/test", + "width": 1920, + "height": 1080, + "bitrate": 6000 + } +] diff --git a/html/edit_config.html b/html/edit_config.html new file mode 100644 index 0000000..1146d6b --- /dev/null +++ b/html/edit_config.html @@ -0,0 +1,37 @@ + + + + Edit Config + + +

Edit Config

+
+ + {{range $index, $config := .}} +

URL {{$index}}

+ + + + + + + {{end}} + +
+ + \ No newline at end of file diff --git a/main.go b/main.go index 45ade7d..b8249e7 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,10 @@ import ( "encoding/json" "flag" "fmt" + "html/template" + "net/http" "os" + "strconv" "time" // TODO: switch to joy5? @@ -15,6 +18,7 @@ import ( var ( bind = flag.String("bind", ":1935", "bind address") config_file = flag.String("config", "config.json", "config file") + config []URLConfig ) type URLConfig struct { @@ -121,6 +125,75 @@ func (r *RTMPConnection) Loop() error { return nil } +func handleAddConfig(w http.ResponseWriter, r *http.Request) { + config = append(config, URLConfig{}) + saveConfig() + http.Redirect(w, r, "/", http.StatusSeeOther) +} +func handleRemoveConfig(w http.ResponseWriter, r *http.Request) { + indexStr := r.URL.Query().Get("index") + index, err := strconv.Atoi(indexStr) + if err != nil || index < 0 || index >= len(config) { + http.Error(w, "Invalid index", http.StatusBadRequest) + return + } + config = append(config[:index], config[index+1:]...) + saveConfig() + http.Redirect(w, r, "/", http.StatusSeeOther) +} +func handleEditConfig(w http.ResponseWriter, r *http.Request) { + tmpl := template.Must(template.ParseFiles("html/edit_config.html")) + tmpl.Execute(w, config) +} +func handleSaveConfig(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Redirect(w, r, "/", http.StatusSeeOther) + return + } + + // Parse the form data + err := r.ParseForm() + if err != nil { + http.Error(w, "Error parsing form", http.StatusInternalServerError) + return + } + + // Update the config with the form data + // ... + + // Save the config to the file + file, err := os.Create(*config_file) + if err != nil { + http.Error(w, "Error creating config file", http.StatusInternalServerError) + return + } + defer file.Close() + + encoder := json.NewEncoder(file) + encoder.SetIndent("", " ") + err = encoder.Encode(config) + if err != nil { + http.Error(w, "Error writing config file", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/", http.StatusSeeOther) +} + +func saveConfig() { + file, err := os.Create(*config_file) + if err != nil { + fmt.Println("Error creating config file:", err) + return + } + defer file.Close() + encoder := json.NewEncoder(file) + encoder.SetIndent("", " ") + err = encoder.Encode(config) + if err != nil { + fmt.Println("Error writing config file:", err) + } +} func readConfigFromFile(filename string) ([]URLConfig, error) { file, err := os.Open(filename) if err != nil { @@ -140,7 +213,6 @@ func readConfigFromFile(filename string) ([]URLConfig, error) { func main() { flag.Parse() - var config []URLConfig var err error if *config_file != "" { if _, err := os.Stat(*config_file); err == nil { @@ -232,6 +304,13 @@ func main() { } } + go func() { + fmt.Println("Starting web server on http://localhost:8080") + http.HandleFunc("/", handleEditConfig) + http.HandleFunc("/save", handleSaveConfig) + http.ListenAndServe(":8080", nil) + }() + fmt.Println("Waiting for incoming connection...") err = server.ListenAndServe() if err != nil { From fccb3788c2036fedd8d3ba3131ce1dced4ec773d Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 01:07:16 +0100 Subject: [PATCH 4/7] update .gitignore --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ae4290a..8f1a996 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,11 @@ # Dependency directories (remove the comment below to include it) # vendor/ +*.exe + prism dist/ .envrc -/outputs.txt -/config.json +outputs.txt config.json +/config.bak.json \ No newline at end of file From 794f6a693a6752a411926a9e4aa937e1e5482ab3 Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 01:07:25 +0100 Subject: [PATCH 5/7] use bootstrap --- html/edit_config.html | 62 +++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/html/edit_config.html b/html/edit_config.html index 1146d6b..c7db8e5 100644 --- a/html/edit_config.html +++ b/html/edit_config.html @@ -2,36 +2,40 @@ Edit Config + + -

Edit Config

-
- - {{range $index, $config := .}} -

URL {{$index}}

- - - - - - - {{end}} - -
+
+

Edit Config

+
+ {{range $index, $config := .}} +
+
+
URL {{$index}}
+
+ + + +
+
+ + + + + + +
+ +
+
+ {{end}} + +
+ +
+ + + \ No newline at end of file From de04a83a98847e2ed713d4d3d787adb4667326c9 Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 01:26:10 +0100 Subject: [PATCH 6/7] fix webserver --- html/edit_config.html | 37 +++++++++++++++++++++++++++++++------ main.go | 27 +++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/html/edit_config.html b/html/edit_config.html index c7db8e5..7e0a1c7 100644 --- a/html/edit_config.html +++ b/html/edit_config.html @@ -1,7 +1,7 @@ - Edit Config + Prism RTSP Splitter @@ -26,16 +26,41 @@
URL {{$index}}
- - + {{end}} - +   + - - + + \ No newline at end of file diff --git a/main.go b/main.go index b8249e7..9f1453d 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "flag" "fmt" "html/template" + "log" "net/http" "os" "strconv" @@ -17,6 +18,7 @@ import ( var ( bind = flag.String("bind", ":1935", "bind address") + bind_web = flag.String("bind_web", ":8080", "bind address for web server") config_file = flag.String("config", "config.json", "config file") config []URLConfig ) @@ -125,10 +127,21 @@ func (r *RTMPConnection) Loop() error { return nil } +// region webserver + +func logRequest(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Printf("%s %s %s %s", r.Method, r.URL.Path, r.URL.RawQuery, r.RemoteAddr) + next.ServeHTTP(w, r) + }) +} + func handleAddConfig(w http.ResponseWriter, r *http.Request) { config = append(config, URLConfig{}) saveConfig() http.Redirect(w, r, "/", http.StatusSeeOther) + fmt.Println("Added new config entry") + } func handleRemoveConfig(w http.ResponseWriter, r *http.Request) { indexStr := r.URL.Query().Get("index") @@ -139,6 +152,7 @@ func handleRemoveConfig(w http.ResponseWriter, r *http.Request) { } config = append(config[:index], config[index+1:]...) saveConfig() + fmt.Println("Removed config at index", index) http.Redirect(w, r, "/", http.StatusSeeOther) } func handleEditConfig(w http.ResponseWriter, r *http.Request) { @@ -194,6 +208,9 @@ func saveConfig() { fmt.Println("Error writing config file:", err) } } + +// endregion webserver + func readConfigFromFile(filename string) ([]URLConfig, error) { file, err := os.Open(filename) if err != nil { @@ -216,7 +233,7 @@ func main() { var err error if *config_file != "" { if _, err := os.Stat(*config_file); err == nil { - config, err = readConfigFromFile(*config_file) + config, _ = readConfigFromFile(*config_file) fmt.Println("Read", len(config), "outputs from", *config_file) } } @@ -306,9 +323,11 @@ func main() { go func() { fmt.Println("Starting web server on http://localhost:8080") - http.HandleFunc("/", handleEditConfig) - http.HandleFunc("/save", handleSaveConfig) - http.ListenAndServe(":8080", nil) + http.Handle("/", logRequest(http.HandlerFunc(handleEditConfig))) + http.Handle("/add", logRequest(http.HandlerFunc(handleAddConfig))) + http.Handle("/remove", logRequest(http.HandlerFunc(handleRemoveConfig))) + http.Handle("/save", logRequest(http.HandlerFunc(handleSaveConfig))) + http.ListenAndServe(*bind_web, nil) }() fmt.Println("Waiting for incoming connection...") From 3d57b7e17c2ab135697fdaa6b109fcdbf069141b Mon Sep 17 00:00:00 2001 From: Bluscream Date: Wed, 22 Nov 2023 01:35:44 +0100 Subject: [PATCH 7/7] Delete config.json --- config.json | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 config.json diff --git a/config.json b/config.json deleted file mode 100644 index e0eb2f5..0000000 --- a/config.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "enabled": true, - "url": "rtmp://localhost/live/test", - "width": 1920, - "height": 1080, - "bitrate": 6000 - } -]