mirror of
https://gitlab.crans.org/nounous/ghostream.git
synced 2025-07-01 11:11:13 +02:00
Compare commits
1 Commits
dev
...
ovenmediae
Author | SHA1 | Date | |
---|---|---|---|
91b7f3d789 |
@ -11,13 +11,6 @@
|
||||
|
||||
This project was developped at [Cr@ns](https://crans.org/) to stream events.
|
||||
|
||||
> **Note**
|
||||
> *This project is no longer maintained!*
|
||||
>
|
||||
> As an alternative, you should try [Galène](https://galene.org/) which supports [WebRTC-HTTP ingestion protocol (WHIP)](https://datatracker.ietf.org/doc/draft-ietf-wish-whip/) for low-latency streaming.
|
||||
> OBS Studio introduced WHIP output in version 30.0.
|
||||
> Galène supports WHIP since Galène 0.8.
|
||||
|
||||
Features:
|
||||
|
||||
- WebRTC playback with a lightweight web interface.
|
||||
|
@ -20,7 +20,7 @@ type Options struct {
|
||||
|
||||
// Backend to log user in
|
||||
type Backend interface {
|
||||
Login(string, string) (bool, string, error)
|
||||
Login(string, string) (bool, error)
|
||||
Close()
|
||||
}
|
||||
|
||||
|
@ -23,15 +23,15 @@ type Basic struct {
|
||||
|
||||
// Login hashs password and compare
|
||||
// Returns (true, nil) if success
|
||||
func (a Basic) Login(username string, password string) (bool, string, error) {
|
||||
func (a Basic) Login(username string, password string) (bool, error) {
|
||||
hash, ok := a.Cfg.Credentials[username]
|
||||
if !ok {
|
||||
return false, "", errors.New("user not found in credentials")
|
||||
return false, errors.New("user not found in credentials")
|
||||
}
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
|
||||
// Login succeeded if no error
|
||||
return err == nil, username, err
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// Close has no connection to close
|
||||
|
@ -10,19 +10,19 @@ func TestBasicLogin(t *testing.T) {
|
||||
|
||||
// Test good credentials
|
||||
backend, _ := New(&Options{Credentials: basicCredentials})
|
||||
ok, _, err := backend.Login("demo", "demo")
|
||||
ok, err := backend.Login("demo", "demo")
|
||||
if !ok {
|
||||
t.Error("Error while logging with the basic authentication:", err)
|
||||
}
|
||||
|
||||
// Test bad username
|
||||
ok, _, err = backend.Login("baduser", "demo")
|
||||
ok, err = backend.Login("baduser", "demo")
|
||||
if ok {
|
||||
t.Error("Authentification failed to fail:", err)
|
||||
}
|
||||
|
||||
// Test bad password
|
||||
ok, _, err = backend.Login("demo", "badpass")
|
||||
ok, err = backend.Login("demo", "badpass")
|
||||
if ok {
|
||||
t.Error("Authentification failed to fail:", err)
|
||||
}
|
||||
|
@ -3,15 +3,12 @@ package ldap
|
||||
|
||||
import (
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Options holds package configuration
|
||||
type Options struct {
|
||||
Aliases map[string]map[string]string
|
||||
URI string
|
||||
UserDn string
|
||||
URI string
|
||||
UserDn string
|
||||
}
|
||||
|
||||
// LDAP authentification backend
|
||||
@ -22,37 +19,13 @@ type LDAP struct {
|
||||
|
||||
// Login tries to bind to LDAP
|
||||
// Returns (true, nil) if success
|
||||
func (a LDAP) Login(username string, password string) (bool, string, error) {
|
||||
aliasSplit := strings.SplitN(username, "__", 2)
|
||||
potentialUsernames := []string{username}
|
||||
func (a LDAP) Login(username string, password string) (bool, error) {
|
||||
// Try to bind as user
|
||||
bindDn := "cn=" + username + "," + a.Cfg.UserDn
|
||||
err := a.Conn.Bind(bindDn, password)
|
||||
|
||||
if len(aliasSplit) == 2 {
|
||||
alias := aliasSplit[0]
|
||||
trueUsername := aliasSplit[1]
|
||||
// Resolve stream alias if necessary
|
||||
if aliases, ok := a.Cfg.Aliases[alias]; ok {
|
||||
if _, ok := aliases[trueUsername]; ok {
|
||||
log.Printf("[LDAP] Use stream alias %s for username %s", alias, trueUsername)
|
||||
potentialUsernames = append(potentialUsernames, trueUsername)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var err error = nil
|
||||
for _, username := range potentialUsernames {
|
||||
// Try to bind as user
|
||||
bindDn := "cn=" + username + "," + a.Cfg.UserDn
|
||||
log.Printf("[LDAP] Logging to %s...", bindDn)
|
||||
err = a.Conn.Bind(bindDn, password)
|
||||
if err == nil {
|
||||
// Login succeeded if no error
|
||||
return true, aliasSplit[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[LDAP] Logging failed: %s", err)
|
||||
// Unable to log in
|
||||
return err == nil, "", err
|
||||
// Login succeeded if no error
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// Close LDAP connection
|
||||
|
@ -21,11 +21,8 @@
|
||||
</IceCandidates>
|
||||
</WebRTC>
|
||||
<HLS>
|
||||
<Port>80</Port>
|
||||
</HLS>
|
||||
<DASH>
|
||||
<Port>80</Port>
|
||||
</DASH>
|
||||
</HLS>
|
||||
</Publishers>
|
||||
</Bind>
|
||||
|
||||
@ -55,15 +52,14 @@
|
||||
</Video>
|
||||
</Encode>
|
||||
<Encode>
|
||||
<Name>bypass</Name>
|
||||
<Audio>
|
||||
<Bypass>true</Bypass>
|
||||
</Audio>
|
||||
<Name>BYPASS</Name>
|
||||
<Video>
|
||||
<Bypass>true</Bypass>
|
||||
</Video>
|
||||
<Audio>
|
||||
<Bypass>true</Bypass>
|
||||
</Audio>
|
||||
</Encode>
|
||||
|
||||
</Encodes>
|
||||
<Streams>
|
||||
<Stream>
|
||||
@ -75,10 +71,9 @@
|
||||
<Stream>
|
||||
<Name>${OriginStreamName}_bypass</Name>
|
||||
<Profiles>
|
||||
<Profile>bypass</Profile>
|
||||
<Profile>BYPASS</Profile>
|
||||
</Profiles>
|
||||
</Stream>
|
||||
|
||||
</Streams>
|
||||
<Providers>
|
||||
<RTMP>
|
||||
@ -91,28 +86,15 @@
|
||||
<Timeout>30000</Timeout>
|
||||
</WebRTC>
|
||||
<HLS>
|
||||
<SegmentDuration>2</SegmentDuration>
|
||||
<SegmentDuration>5</SegmentDuration>
|
||||
<SegmentCount>2</SegmentCount>
|
||||
<CrossDomain>
|
||||
<Url>*</Url>
|
||||
</CrossDomain>
|
||||
</HLS>
|
||||
<DASH>
|
||||
<SegmentDuration>2</SegmentDuration>
|
||||
<SegmentCount>2</SegmentCount>
|
||||
<CrossDomain>
|
||||
<Url>*</Url>
|
||||
</CrossDomain>
|
||||
</DASH>
|
||||
<LLDASH>
|
||||
<SegmentDuration>2</SegmentDuration>
|
||||
<CrossDomain>
|
||||
<Url>*</Url>
|
||||
</CrossDomain>
|
||||
</LLDASH>
|
||||
</Publishers>
|
||||
</Application>
|
||||
</Applications>
|
||||
</VirtualHost>
|
||||
</VirtualHosts>
|
||||
</Server>
|
||||
</Server>
|
@ -34,13 +34,6 @@ auth:
|
||||
#ldap:
|
||||
# uri: ldap://127.0.0.1:389
|
||||
# userdn: cn=users,dc=example,dc=com
|
||||
#
|
||||
# # You can define aliases, to stream on stream.example.com/example with the credentials of the demo account.
|
||||
# # You will have to use the streamid example__demo:password
|
||||
# aliases:
|
||||
# example:
|
||||
# demo: ignored
|
||||
#
|
||||
|
||||
## Stream forwarding ##
|
||||
# Forward an incoming stream to other servers
|
||||
|
@ -42,9 +42,8 @@ func New() *Config {
|
||||
Credentials: make(map[string]string),
|
||||
},
|
||||
LDAP: ldap.Options{
|
||||
Aliases: make(map[string]map[string]string),
|
||||
URI: "ldap://127.0.0.1:389",
|
||||
UserDn: "cn=users,dc=example,dc=com",
|
||||
URI: "ldap://127.0.0.1:389",
|
||||
UserDn: "cn=users,dc=example,dc=com",
|
||||
},
|
||||
},
|
||||
Forwarding: make(map[string][]string),
|
||||
|
@ -79,22 +79,20 @@ func Serve(streams *messaging.Streams, authBackend auth.Backend, cfg *Options) {
|
||||
|
||||
if len(split) > 1 {
|
||||
// password was provided so it is a streamer
|
||||
name, password := strings.ToLower(split[0]), split[1]
|
||||
name, password := split[0], split[1]
|
||||
if authBackend != nil {
|
||||
// check password
|
||||
ok, username, err := authBackend.Login(name, password)
|
||||
if !ok || err != nil {
|
||||
if ok, err := authBackend.Login(name, password); !ok || err != nil {
|
||||
log.Printf("Failed to authenticate for stream %s", name)
|
||||
s.Close()
|
||||
continue
|
||||
}
|
||||
name = username
|
||||
}
|
||||
|
||||
go handleStreamer(s, streams, name)
|
||||
} else {
|
||||
// password was not provided so it is a viewer
|
||||
name := strings.ToLower(split[0])
|
||||
name := split[0]
|
||||
|
||||
// Send stream
|
||||
go handleViewer(s, streams, name)
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
|
||||
var (
|
||||
// Precompile regex
|
||||
validPath = regexp.MustCompile("^/[a-zA-Z0-9@_-]*$")
|
||||
validPath = regexp.MustCompile("^/[a-z0-9@_-]*$")
|
||||
|
||||
counterMutex = new(sync.Mutex)
|
||||
connectedClients = make(map[string]map[string]int64)
|
||||
@ -42,7 +42,7 @@ func viewerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Get stream ID from URL, or from domain name
|
||||
path := strings.ToLower(r.URL.Path[1:])
|
||||
path := r.URL.Path[1:]
|
||||
host := r.Host
|
||||
if strings.Contains(host, ":") {
|
||||
realHost, _, err := net.SplitHostPort(r.Host)
|
||||
@ -58,7 +58,7 @@ func viewerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if path == "about" {
|
||||
path = ""
|
||||
} else {
|
||||
path = strings.ToLower(streamID)
|
||||
path = streamID
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,7 +97,6 @@ func staticHandler() http.Handler {
|
||||
func statisticsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// Retrieve stream name from URL
|
||||
name := strings.SplitN(strings.Replace(r.URL.Path[7:], "/", "", -1), "@", 2)[0]
|
||||
name = strings.ToLower(name)
|
||||
userCount := 0
|
||||
|
||||
// Clients have a unique generated identifier per session, that expires in 40 seconds.
|
||||
|
@ -47,20 +47,10 @@ export function initViewerPage(stream, omeApp, viewersCounterRefreshPeriod, post
|
||||
"label": " WebRTC - Source"
|
||||
},
|
||||
{
|
||||
"file": "https://" + window.location.host + "/" + omeApp + "/" + stream + "_bypass/playlist.m3u8",
|
||||
"type": "hls",
|
||||
"file": "https://" + window.location.host + "/" + omeApp + "/" + stream + "_bypass/playlist.m3u8",
|
||||
"label": " HLS"
|
||||
},
|
||||
{
|
||||
"file": "https://" + window.location.host + "/" + omeApp + "/" + stream + "_bypass/manifest.mpd",
|
||||
"type": "dash",
|
||||
"label": "DASH"
|
||||
},
|
||||
{
|
||||
"file": "https://" + window.location.host + "/" + omeApp + "/" + stream + "_bypass/manifest_ll.mpd",
|
||||
"type": "dash",
|
||||
"label": "LL-DASH"
|
||||
},
|
||||
}
|
||||
]
|
||||
});
|
||||
player.on("stateChanged", function (data) {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
/*! OvenPlayerv0.9.0 | (c)2020 AirenSoft Co., Ltd. | MIT license (https://github.com/AirenSoft/OvenPlayerPrivate/blob/master/LICENSE) | Github : https://github.com/AirenSoft/OvenPlayer */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
/*! OvenPlayerv0.9.0 | (c)2020 AirenSoft Co., Ltd. | MIT license (https://github.com/AirenSoft/OvenPlayerPrivate/blob/master/LICENSE) | Github : https://github.com/AirenSoft/OvenPlayer */
|
@ -25,7 +25,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<b>Serveur :</b>
|
||||
<code>srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT:MOT_DE_PASSE</code>,
|
||||
<code>srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?IDENTIFIANT:MOT_DE_PASSE</code>,
|
||||
avec <code>IDENTIFIANT</code> et <code>MOT_DE_PASSE</code>
|
||||
vos identifiants.
|
||||
</li>
|
||||
|
@ -36,7 +36,6 @@
|
||||
|
||||
{{if .OMECfg.Enabled}}
|
||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/dashjs/2.9.3/dash.all.min.js"></script>
|
||||
<script src="/static/ovenplayer/ovenplayer.js"></script>
|
||||
<script src="/static/js/ovenplayer.js"></script>
|
||||
{{end}}
|
||||
|
Reference in New Issue
Block a user