mirror of
https://gitlab.crans.org/nounous/ghostream.git
synced 2024-12-22 10:22:19 +00:00
Merge branch 'ovenmediaengine' into 'dev'
OvenMediaEngine See merge request nounous/ghostream!8
This commit is contained in:
commit
45cb61e436
100
docs/Server-docker.xml
Normal file
100
docs/Server-docker.xml
Normal file
@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<Server version="7">
|
||||
<Name>OvenMediaEngine</Name>
|
||||
<Type>origin</Type>
|
||||
<IP>*</IP>
|
||||
|
||||
<Bind>
|
||||
<Providers>
|
||||
<RTMP>
|
||||
<Port>1915</Port>
|
||||
</RTMP>
|
||||
</Providers>
|
||||
<Publishers>
|
||||
<WebRTC>
|
||||
<Signalling>
|
||||
<Port>3333</Port>
|
||||
</Signalling>
|
||||
<IceCandidates>
|
||||
<IceCandidate>*:10006-10010/udp</IceCandidate>
|
||||
</IceCandidates>
|
||||
</WebRTC>
|
||||
<HLS>
|
||||
<Port>80</Port>
|
||||
</HLS>
|
||||
</Publishers>
|
||||
</Bind>
|
||||
|
||||
<VirtualHosts>
|
||||
<VirtualHost>
|
||||
<Name>default</Name>
|
||||
<Domain>
|
||||
<Names>
|
||||
<Name>*</Name>
|
||||
</Names>
|
||||
</Domain>
|
||||
<Applications>
|
||||
<Application>
|
||||
<Name>play</Name>
|
||||
<Type>live</Type>
|
||||
<Encodes>
|
||||
<Encode>
|
||||
<Name>opus_only</Name>
|
||||
<Audio>
|
||||
<Codec>opus</Codec>
|
||||
<Bitrate>128000</Bitrate>
|
||||
<Samplerate>48000</Samplerate>
|
||||
<Channel>2</Channel>
|
||||
</Audio>
|
||||
<Video>
|
||||
<Bypass>true</Bypass>
|
||||
</Video>
|
||||
</Encode>
|
||||
<Encode>
|
||||
<Name>BYPASS</Name>
|
||||
<Video>
|
||||
<Bypass>true</Bypass>
|
||||
</Video>
|
||||
<Audio>
|
||||
<Bypass>true</Bypass>
|
||||
</Audio>
|
||||
</Encode>
|
||||
</Encodes>
|
||||
<Streams>
|
||||
<Stream>
|
||||
<Name>${OriginStreamName}</Name>
|
||||
<Profiles>
|
||||
<Profile>opus_only</Profile>
|
||||
</Profiles>
|
||||
</Stream>
|
||||
<Stream>
|
||||
<Name>${OriginStreamName}_bypass</Name>
|
||||
<Profiles>
|
||||
<Profile>BYPASS</Profile>
|
||||
</Profiles>
|
||||
</Stream>
|
||||
</Streams>
|
||||
<Providers>
|
||||
<RTMP>
|
||||
<BlockDuplicateStreamName>true</BlockDuplicateStreamName>
|
||||
</RTMP>
|
||||
</Providers>
|
||||
<Publishers>
|
||||
<ThreadCount>2</ThreadCount>
|
||||
<WebRTC>
|
||||
<Timeout>30000</Timeout>
|
||||
</WebRTC>
|
||||
<HLS>
|
||||
<SegmentDuration>5</SegmentDuration>
|
||||
<SegmentCount>2</SegmentCount>
|
||||
<CrossDomain>
|
||||
<Url>*</Url>
|
||||
</CrossDomain>
|
||||
</HLS>
|
||||
</Publishers>
|
||||
</Application>
|
||||
</Applications>
|
||||
</VirtualHost>
|
||||
</VirtualHosts>
|
||||
</Server>
|
@ -26,11 +26,10 @@ services:
|
||||
- "--certificatesResolvers.mytlschallenge.acme.httpChallenge.entryPoint=web"
|
||||
|
||||
ghostream:
|
||||
build: ..
|
||||
build: https://gitlab.crans.org/nounous/ghostream.git
|
||||
restart: always
|
||||
ports:
|
||||
- 9710:9710/udp
|
||||
- 10000-11000:10000-11000/udp
|
||||
volumes:
|
||||
- ./ghostream_data:/etc/ghostream:ro
|
||||
labels:
|
||||
@ -40,3 +39,30 @@ services:
|
||||
- "traefik.http.routers.ghostream.tls.certresolver=mytlschallenge"
|
||||
- "traefik.http.routers.ghostream.service=ghostream"
|
||||
- "traefik.http.services.ghostream.loadbalancer.server.port=8080"
|
||||
|
||||
ovenmediaengine:
|
||||
image: airensoft/ovenmediaengine:0.10.8
|
||||
restart: always
|
||||
ports:
|
||||
# WebRTC ICE
|
||||
- 10006-10010:10006-10010/udp
|
||||
volumes:
|
||||
- ./ovenmediaengine_data/conf/Server-docker.xml:/opt/ovenmediaengine/bin/origin_conf/Server.xml:ro
|
||||
labels:
|
||||
- "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"
|
||||
|
||||
- "traefik.http.routers.ovenmediaengine.rule=Host(`stream.example.com`) && PathPrefix(`/play/`)"
|
||||
- "traefik.http.routers.ovenmediaengine.priority=101"
|
||||
- "traefik.http.routers.ovenmediaengine.entrypoints=websecure"
|
||||
- "traefik.http.routers.ovenmediaengine.tls.certresolver=mytlschallenge"
|
||||
- "traefik.http.services.ovenmediaengine.loadbalancer.server.port=3333"
|
||||
- "traefik.http.routers.ovenmediaengine.service=ovenmediaengine"
|
||||
- "traefik.http.routers.ovenmediaengine.middlewares=sslheader"
|
||||
|
||||
- "traefik.http.routers.ovenmediaengine-hls.rule=Host(`stream.example.com`) && Path(`/play/{app_name:.*}/{filename:.*}.{ext:(m3u8|mpd|ts)}`)"
|
||||
- "traefik.http.routers.ovenmediaengine-hls.priority=102"
|
||||
- "traefik.http.routers.ovenmediaengine-hls.entrypoints=websecure"
|
||||
- "traefik.http.routers.ovenmediaengine-hls.tls.certresolver=mytlschallenge"
|
||||
- "traefik.http.services.ovenmediaengine-hls.loadbalancer.server.port=80"
|
||||
- "traefik.http.routers.ovenmediaengine-hls.service=ovenmediaengine-hls"
|
||||
- "traefik.http.routers.ovenmediaengine-hls.middlewares=sslheader"
|
||||
|
@ -61,6 +61,19 @@ monitoring:
|
||||
# To limit access to only localhost, use 127.0.0.1:2112
|
||||
#listenAddress: :2112
|
||||
|
||||
## OvenMediaEngine ##
|
||||
# Send the stream data to OvenMediaEngine to handle properly the web client
|
||||
ome:
|
||||
# If you disable OME module, the laggy webrtc client will be used.
|
||||
#
|
||||
#enabled: true
|
||||
#
|
||||
# The URL where OME listens RTMP, without the prefix.
|
||||
#url: ovenmediaengine:1915
|
||||
#
|
||||
# The OME app where OME is waiting for the data of Ghostream.
|
||||
#app: play
|
||||
|
||||
## SRT server ##
|
||||
# The SRT server receive incoming stream and can also serve video to clients.
|
||||
srt:
|
||||
@ -164,7 +177,7 @@ web:
|
||||
webrtc:
|
||||
# If you disable webrtc module, the web client won't be able to play streams.
|
||||
#
|
||||
#enabled: true
|
||||
#enabled: false
|
||||
|
||||
# UDP port range used to stream
|
||||
# This range must be opened in your firewall.
|
||||
|
@ -2,6 +2,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
||||
"net"
|
||||
|
||||
"github.com/sherifabdlnaby/configuro"
|
||||
@ -23,6 +24,7 @@ type Config struct {
|
||||
Auth auth.Options
|
||||
Forwarding forwarding.Options
|
||||
Monitoring monitoring.Options
|
||||
OME ovenmediaengine.Options
|
||||
Srt srt.Options
|
||||
Telnet telnet.Options
|
||||
Transcoder transcoder.Options
|
||||
@ -49,6 +51,11 @@ func New() *Config {
|
||||
Enabled: true,
|
||||
ListenAddress: ":2112",
|
||||
},
|
||||
OME: ovenmediaengine.Options{
|
||||
Enabled: true,
|
||||
URL: "ovenmediaengine:1915",
|
||||
App: "play",
|
||||
},
|
||||
Srt: srt.Options{
|
||||
Enabled: true,
|
||||
ListenAddress: ":9710",
|
||||
@ -77,7 +84,7 @@ func New() *Config {
|
||||
ViewersCounterRefreshPeriod: 20000,
|
||||
},
|
||||
WebRTC: webrtc.Options{
|
||||
Enabled: true,
|
||||
Enabled: false,
|
||||
MaxPortUDP: 11000,
|
||||
MinPortUDP: 10000,
|
||||
STUNServers: []string{"stun:stun.l.google.com:19302"},
|
||||
|
4
main.go
4
main.go
@ -5,6 +5,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
||||
"log"
|
||||
|
||||
"github.com/pkg/profile"
|
||||
@ -49,9 +50,10 @@ func main() {
|
||||
go transcoder.Init(streams, &cfg.Transcoder)
|
||||
go forwarding.Serve(streams, cfg.Forwarding)
|
||||
go monitoring.Serve(&cfg.Monitoring)
|
||||
go ovenmediaengine.Serve(streams, &cfg.OME)
|
||||
go srt.Serve(streams, authBackend, &cfg.Srt)
|
||||
go telnet.Serve(streams, &cfg.Telnet)
|
||||
go web.Serve(streams, &cfg.Web)
|
||||
go web.Serve(streams, &cfg.Web, &cfg.OME)
|
||||
go webrtc.Serve(streams, &cfg.WebRTC)
|
||||
|
||||
// Wait for routines
|
||||
|
@ -40,6 +40,7 @@ func Serve(streams *messaging.Streams, cfg Options) {
|
||||
stream, err := streams.Get(name)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get stream '%s'", name)
|
||||
return
|
||||
}
|
||||
|
||||
// Get specific quality
|
||||
@ -74,8 +75,8 @@ func forward(streamName string, q *messaging.Quality, fwdCfg []string) {
|
||||
formattedURL = strings.ReplaceAll(formattedURL, "%S", fmt.Sprintf("%02d", now.Second()))
|
||||
formattedURL = strings.ReplaceAll(formattedURL, "%name", streamName)
|
||||
|
||||
params = append(params, "-f", "flv", "-preset", "ultrafast", "-tune", "zerolatency",
|
||||
"-c", "copy", formattedURL)
|
||||
params = append(params, "-f", "flv",
|
||||
"-c:v", "copy", "-c:a", "aac", "-b:a", "160k", "-ar", "44100", formattedURL)
|
||||
}
|
||||
ffmpeg := exec.Command("ffmpeg", params...)
|
||||
|
||||
|
112
stream/ovenmediaengine/ovenmediaengine.go
Normal file
112
stream/ovenmediaengine/ovenmediaengine.go
Normal file
@ -0,0 +1,112 @@
|
||||
// Package ovenmediaengine provides the forwarding to an ovenmediaengine server to handle the web client
|
||||
package ovenmediaengine
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
|
||||
"gitlab.crans.org/nounous/ghostream/messaging"
|
||||
)
|
||||
|
||||
// Options holds ovenmediaengine package configuration
|
||||
type Options struct {
|
||||
Enabled bool
|
||||
URL string
|
||||
App string
|
||||
}
|
||||
|
||||
var (
|
||||
cfg *Options
|
||||
)
|
||||
|
||||
// Serve handles incoming packets from SRT and forward them to OME
|
||||
func Serve(streams *messaging.Streams, c *Options) {
|
||||
cfg = c
|
||||
|
||||
if !c.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
// Subscribe to new stream event
|
||||
event := make(chan string, 8)
|
||||
streams.Subscribe(event)
|
||||
log.Printf("Stream forwarding to OME initialized")
|
||||
|
||||
// For each new stream
|
||||
for name := range event {
|
||||
// Get stream
|
||||
stream, err := streams.Get(name)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get stream '%s'", name)
|
||||
return
|
||||
}
|
||||
|
||||
qualityName := "source"
|
||||
quality, err := stream.GetQuality(qualityName)
|
||||
if err != nil {
|
||||
log.Printf("Failed to get quality '%s'", qualityName)
|
||||
}
|
||||
|
||||
// Start forwarding
|
||||
log.Printf("Starting forwarding to OME for '%s'", name)
|
||||
go forward(name, quality)
|
||||
}
|
||||
}
|
||||
|
||||
// Start a FFMPEG instance and redirect stream output to OME
|
||||
func forward(name string, q *messaging.Quality) {
|
||||
output := make(chan []byte, 1024)
|
||||
q.Register(output)
|
||||
|
||||
// TODO When a new OME version got released with SRT support, directly forward SRT packets, without using unwanted RTMP transport
|
||||
// Launch FFMPEG instance
|
||||
params := []string{"-hide_banner", "-loglevel", "error", "-i", "pipe:0", "-f", "flv", "-c:v", "copy",
|
||||
"-c:a", "aac", "-b:a", "160k", "-ar", "44100",
|
||||
fmt.Sprintf("rtmp://%s/%s/%s", cfg.URL, cfg.App, name)}
|
||||
ffmpeg := exec.Command("ffmpeg", params...)
|
||||
|
||||
// Open pipes
|
||||
input, err := ffmpeg.StdinPipe()
|
||||
if err != nil {
|
||||
log.Printf("Error while opening forwarding ffmpeg input pipe: %s", err)
|
||||
return
|
||||
}
|
||||
errOutput, err := ffmpeg.StderrPipe()
|
||||
if err != nil {
|
||||
log.Printf("Error while opening forwarding ffmpeg output pipe: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Start FFMpeg
|
||||
if err := ffmpeg.Start(); err != nil {
|
||||
log.Printf("Error while starting forwarding ffmpeg instance: %s", err)
|
||||
}
|
||||
|
||||
// Kill FFMPEG when stream is ended
|
||||
defer func() {
|
||||
_ = input.Close()
|
||||
_ = errOutput.Close()
|
||||
_ = ffmpeg.Process.Kill()
|
||||
q.Unregister(output)
|
||||
}()
|
||||
|
||||
// Log standard error output
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(errOutput)
|
||||
for scanner.Scan() {
|
||||
log.Printf("[FORWARDING OME FFMPEG %s] %s", name, scanner.Text())
|
||||
}
|
||||
}()
|
||||
|
||||
// Read stream output and redirect immediately to ffmpeg
|
||||
for data := range output {
|
||||
_, err := input.Write(data)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error while writing to forwarded stream: %s", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
@ -10,15 +10,21 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/markbates/pkger"
|
||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
||||
"gitlab.crans.org/nounous/ghostream/stream/webrtc"
|
||||
)
|
||||
|
||||
var (
|
||||
// Precompile regex
|
||||
validPath = regexp.MustCompile("^/[a-z0-9@_-]*$")
|
||||
|
||||
counterMutex = new(sync.Mutex)
|
||||
connectedClients = make(map[string]map[string]int64)
|
||||
)
|
||||
|
||||
// Handle site index and viewer pages
|
||||
@ -61,7 +67,8 @@ func viewerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Cfg *Options
|
||||
Path string
|
||||
WidgetURL string
|
||||
}{Path: path, Cfg: cfg, WidgetURL: ""}
|
||||
OMECfg *ovenmediaengine.Options
|
||||
}{Path: path, Cfg: cfg, WidgetURL: "", OMECfg: omeCfg}
|
||||
|
||||
// Load widget is user does not disable it with ?nowidget
|
||||
if _, ok := r.URL.Query()["nowidget"]; !ok {
|
||||
@ -88,14 +95,43 @@ 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]
|
||||
userCount := 0
|
||||
|
||||
// Clients have a unique generated identifier per session, that expires in 40 seconds.
|
||||
// Each time the client connects to this page, the identifier is renewed.
|
||||
// Yeah, that's not a good way to have stats, but it works...
|
||||
if connectedClients[name] == nil {
|
||||
counterMutex.Lock()
|
||||
connectedClients[name] = make(map[string]int64)
|
||||
counterMutex.Unlock()
|
||||
}
|
||||
currentTime := time.Now().Unix()
|
||||
if _, ok := r.URL.Query()["uid"]; ok {
|
||||
uid := r.URL.Query()["uid"][0]
|
||||
counterMutex.Lock()
|
||||
connectedClients[name][uid] = currentTime
|
||||
counterMutex.Unlock()
|
||||
}
|
||||
toDelete := make([]string, 0)
|
||||
counterMutex.Lock()
|
||||
for uid, oldTime := range connectedClients[name] {
|
||||
if currentTime-oldTime > 40 {
|
||||
toDelete = append(toDelete, uid)
|
||||
}
|
||||
}
|
||||
for _, uid := range toDelete {
|
||||
delete(connectedClients[name], uid)
|
||||
}
|
||||
counterMutex.Unlock()
|
||||
|
||||
// Get requested stream
|
||||
stream, err := streams.Get(name)
|
||||
if err == nil {
|
||||
userCount = stream.ClientCount()
|
||||
userCount += webrtc.GetNumberConnectedSessions(name)
|
||||
userCount += len(connectedClients[name])
|
||||
}
|
||||
|
||||
// Display connected users statistics
|
||||
|
@ -9,6 +9,7 @@ export class ViewerCounter {
|
||||
constructor(element, streamName) {
|
||||
this.element = element;
|
||||
this.url = "/_stats/" + streamName;
|
||||
this.uid = Math.floor(1e19 * Math.random()).toString(16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,7 +22,7 @@ export class ViewerCounter {
|
||||
}
|
||||
|
||||
refreshViewersCounter() {
|
||||
fetch(this.url)
|
||||
fetch(this.url + "?uid=" + this.uid)
|
||||
.then(response => response.json())
|
||||
.then((data) => this.element.innerText = data.ConnectedViewers)
|
||||
.catch(console.log);
|
||||
|
106
web/static/js/ovenplayer.js
Normal file
106
web/static/js/ovenplayer.js
Normal file
@ -0,0 +1,106 @@
|
||||
import { ViewerCounter } from "./modules/viewerCounter.js";
|
||||
|
||||
/**
|
||||
* Initialize viewer page
|
||||
*
|
||||
* @param {String} stream
|
||||
* @param {String} omeApp
|
||||
* @param {Number} viewersCounterRefreshPeriod
|
||||
* @param {String} posterUrl
|
||||
*/
|
||||
export function initViewerPage(stream, omeApp, viewersCounterRefreshPeriod, posterUrl) {
|
||||
// Create viewer counter
|
||||
const viewerCounter = new ViewerCounter(
|
||||
document.getElementById("connected-people"),
|
||||
stream,
|
||||
);
|
||||
viewerCounter.regularUpdate(viewersCounterRefreshPeriod);
|
||||
viewerCounter.refreshViewersCounter();
|
||||
|
||||
// Side widget toggler
|
||||
const sideWidgetToggle = document.getElementById("sideWidgetToggle");
|
||||
const sideWidget = document.getElementById("sideWidget");
|
||||
if (sideWidgetToggle !== null && sideWidget !== null) {
|
||||
// On click, toggle side widget visibility
|
||||
sideWidgetToggle.addEventListener("click", function () {
|
||||
if (sideWidget.style.display === "none") {
|
||||
sideWidget.style.display = "block";
|
||||
sideWidgetToggle.textContent = "»";
|
||||
} else {
|
||||
sideWidget.style.display = "none";
|
||||
sideWidgetToggle.textContent = "«";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create player
|
||||
let player = OvenPlayer.create("viewer", {
|
||||
title: stream,
|
||||
image: posterUrl,
|
||||
autoStart: true,
|
||||
mute: true,
|
||||
expandFullScreenUI: true,
|
||||
sources: [
|
||||
{
|
||||
"file": "wss://" + window.location.host + "/" + omeApp + "/" + stream,
|
||||
"type": "webrtc",
|
||||
"label": " WebRTC - Source"
|
||||
},
|
||||
{
|
||||
"type": "hls",
|
||||
"file": "https://" + window.location.host + "/" + omeApp + "/" + stream + "_bypass/playlist.m3u8",
|
||||
"label": " HLS"
|
||||
}
|
||||
]
|
||||
});
|
||||
player.on("stateChanged", function (data) {
|
||||
if (data.newstate === "loading") {
|
||||
document.getElementById("connectionIndicator").style.fill = '#ffc107'
|
||||
}
|
||||
if (data.newstate === "playing") {
|
||||
document.getElementById("connectionIndicator").style.fill = '#28a745'
|
||||
}
|
||||
if (data.newstate === "idle") {
|
||||
document.getElementById("connectionIndicator").style.fill = '#dc3545'
|
||||
}
|
||||
})
|
||||
player.on("error", function (error) {
|
||||
document.getElementById("connectionIndicator").style.fill = '#dc3545'
|
||||
if (error.code === 501 || error.code === 406) {
|
||||
// Clear messages
|
||||
const errorMsg = document.getElementsByClassName("op-message-text")[0]
|
||||
errorMsg.textContent = ""
|
||||
|
||||
const warningIcon = document.getElementsByClassName("op-message-icon")[0]
|
||||
warningIcon.textContent = ""
|
||||
|
||||
// Reload in 30s
|
||||
setTimeout(function () {
|
||||
player.load()
|
||||
}, 30000)
|
||||
} else {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Register keyboard events
|
||||
window.addEventListener("keydown", (event) => {
|
||||
switch (event.key) {
|
||||
case "f":
|
||||
// F key put player in fullscreen
|
||||
if (document.fullscreenElement !== null) {
|
||||
document.exitFullscreen()
|
||||
} else {
|
||||
document.getElementsByTagName("video")[0].requestFullscreen()
|
||||
}
|
||||
break;
|
||||
case "m":
|
||||
case " ":
|
||||
// M and space key mute player
|
||||
player.setMute(!player.getMute())
|
||||
event.preventDefault()
|
||||
player.play()
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
2
web/static/ovenplayer/ovenplayer.js
Normal file
2
web/static/ovenplayer/ovenplayer.js
Normal file
File diff suppressed because one or more lines are too long
1
web/static/ovenplayer/ovenplayer.js.LICENSE
Normal file
1
web/static/ovenplayer/ovenplayer.js.LICENSE
Normal file
@ -0,0 +1 @@
|
||||
/*! 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
@ -0,0 +1 @@
|
||||
/*! 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
@ -0,0 +1 @@
|
||||
/*! 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
@ -0,0 +1 @@
|
||||
/*! 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
@ -0,0 +1 @@
|
||||
/*! OvenPlayerv0.9.0 | (c)2020 AirenSoft Co., Ltd. | MIT license (https://github.com/AirenSoft/OvenPlayerPrivate/blob/master/LICENSE) | Github : https://github.com/AirenSoft/OvenPlayer */
|
@ -6,14 +6,14 @@
|
||||
|
||||
<!-- Links and settings under video -->
|
||||
<div class="controls">
|
||||
<span class="control-quality">
|
||||
<!-- <span class="control-quality">
|
||||
<select id="quality">
|
||||
<option value="source">Source</option>
|
||||
<option value="720p">720p</option>
|
||||
<option value="480p">480p</option>
|
||||
<option value="240p">240p</option>
|
||||
</select>
|
||||
</span>
|
||||
</span> -->
|
||||
<code class="control-srt-link">srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid={{.Path}}</code>
|
||||
<span class="control-viewers" id="connected-people">0</span>
|
||||
<svg class="control-indicator" id="connectionIndicator" fill="#dc3545" width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
@ -34,8 +34,17 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .OMECfg.Enabled}}
|
||||
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||
<script src="/static/ovenplayer/ovenplayer.js"></script>
|
||||
<script src="/static/js/ovenplayer.js"></script>
|
||||
{{end}}
|
||||
<script type="module">
|
||||
import { initViewerPage } from "/static/js/viewer.js";
|
||||
{{if .OMECfg.Enabled}}
|
||||
import { initViewerPage } from "/static/js/ovenplayer.js";
|
||||
{{else}}
|
||||
import { initViewerPage } from "/static/js/viewer.js";
|
||||
{{end}}
|
||||
|
||||
// Some variables that need to be fixed by web page
|
||||
const viewersCounterRefreshPeriod = Number("{{.Cfg.ViewersCounterRefreshPeriod}}");
|
||||
@ -45,6 +54,10 @@
|
||||
"{{$value}}",
|
||||
{{end}}
|
||||
]
|
||||
initViewerPage(stream, stunServers, viewersCounterRefreshPeriod)
|
||||
{{if .OMECfg.Enabled}}
|
||||
initViewerPage(stream, {{.OMECfg.App}}, viewersCounterRefreshPeriod, {{.Cfg.PlayerPoster}})
|
||||
{{else}}
|
||||
initViewerPage(stream, stunServers, viewersCounterRefreshPeriod)
|
||||
{{end}}
|
||||
</script>
|
||||
{{end}}
|
||||
|
@ -2,6 +2,7 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@ -32,6 +33,8 @@ type Options struct {
|
||||
var (
|
||||
cfg *Options
|
||||
|
||||
omeCfg *ovenmediaengine.Options
|
||||
|
||||
// Preload templates
|
||||
templates *template.Template
|
||||
|
||||
@ -70,9 +73,10 @@ func loadTemplates() error {
|
||||
}
|
||||
|
||||
// Serve HTTP server
|
||||
func Serve(s *messaging.Streams, c *Options) {
|
||||
func Serve(s *messaging.Streams, c *Options, ome *ovenmediaengine.Options) {
|
||||
streams = s
|
||||
cfg = c
|
||||
omeCfg = ome
|
||||
|
||||
if !cfg.Enabled {
|
||||
// Web server is not enabled, ignore
|
||||
|
Loading…
Reference in New Issue
Block a user