mirror of
https://gitlab.crans.org/nounous/ghostream.git
synced 2025-07-01 22:31:13 +02:00
Compare commits
1 Commits
ovenmediae
...
multi-qual
Author | SHA1 | Date | |
---|---|---|---|
86dac0f929 |
@ -12,6 +12,6 @@ FROM alpine:3.12
|
|||||||
RUN apk add --no-cache -X https://dl-cdn.alpinelinux.org/alpine/edge/community/ ffmpeg libsrt
|
RUN apk add --no-cache -X https://dl-cdn.alpinelinux.org/alpine/edge/community/ ffmpeg libsrt
|
||||||
COPY --from=build_base /code/out/ghostream /app/ghostream
|
COPY --from=build_base /code/out/ghostream /app/ghostream
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
# 2112 for monitoring, 8023 for Telnet, 8080 for Web, 9710 for SRT, 10000-11000 (UDP) for WebRTC
|
# 2112 for monitoring, 8023 for Telnet, 8080 for Web, 9710 for SRT, 10000-10005 (UDP) for WebRTC
|
||||||
EXPOSE 2112 8023 8080 9710/udp 10000-11000/udp
|
EXPOSE 2112 8023 8080 9710/udp 10000-10005/udp
|
||||||
CMD ["/app/ghostream"]
|
CMD ["/app/ghostream"]
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
func TestBasicLogin(t *testing.T) {
|
func TestBasicLogin(t *testing.T) {
|
||||||
basicCredentials := make(map[string]string)
|
basicCredentials := make(map[string]string)
|
||||||
basicCredentials["demo"] = "$2b$10$xuU7XFwmRX2CMgdSaA8rM.4Y8.BtRNzhUedwN0G8tCegDRNUERTCS"
|
basicCredentials["demo"] = "$2b$15$LRnG3eIHFlYIguTxZOLH7eHwbQC/vqjnLq6nDFiHSUDKIU.f5/1H6"
|
||||||
|
|
||||||
// Test good credentials
|
// Test good credentials
|
||||||
backend, _ := New(&Options{Credentials: basicCredentials})
|
backend, _ := New(&Options{Credentials: basicCredentials})
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
<?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,10 +26,11 @@ services:
|
|||||||
- "--certificatesResolvers.mytlschallenge.acme.httpChallenge.entryPoint=web"
|
- "--certificatesResolvers.mytlschallenge.acme.httpChallenge.entryPoint=web"
|
||||||
|
|
||||||
ghostream:
|
ghostream:
|
||||||
build: https://gitlab.crans.org/nounous/ghostream.git
|
build: ..
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- 9710:9710/udp
|
- 9710:9710/udp
|
||||||
|
- 10000-10005:10000-10005/udp
|
||||||
volumes:
|
volumes:
|
||||||
- ./ghostream_data:/etc/ghostream:ro
|
- ./ghostream_data:/etc/ghostream:ro
|
||||||
labels:
|
labels:
|
||||||
@ -39,30 +40,3 @@ services:
|
|||||||
- "traefik.http.routers.ghostream.tls.certresolver=mytlschallenge"
|
- "traefik.http.routers.ghostream.tls.certresolver=mytlschallenge"
|
||||||
- "traefik.http.routers.ghostream.service=ghostream"
|
- "traefik.http.routers.ghostream.service=ghostream"
|
||||||
- "traefik.http.services.ghostream.loadbalancer.server.port=8080"
|
- "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"
|
|
||||||
|
@ -22,12 +22,12 @@ auth:
|
|||||||
|
|
||||||
# Basic backend configuration
|
# Basic backend configuration
|
||||||
# To generate bcrypt hashed password from Python, use:
|
# To generate bcrypt hashed password from Python, use:
|
||||||
# python3 -c 'import bcrypt; print(bcrypt.hashpw(b"PASSWORD", bcrypt.gensalt(rounds=12)).decode("ascii"))'
|
# python3 -c 'import bcrypt; print(bcrypt.hashpw(b"PASSWORD", bcrypt.gensalt(rounds=15)).decode("ascii"))'
|
||||||
#
|
#
|
||||||
#basic:
|
#basic:
|
||||||
# credentials:
|
# credentials:
|
||||||
# # Demo user with password "demo"
|
# # Demo user with password "demo"
|
||||||
# demo: $2b$10$xuU7XFwmRX2CMgdSaA8rM.4Y8.BtRNzhUedwN0G8tCegDRNUERTCS
|
# demo: $2b$15$LRnG3eIHFlYIguTxZOLH7eHwbQC/vqjnLq6nDFiHSUDKIU.f5/1H6
|
||||||
|
|
||||||
# LDAP backend configuration
|
# LDAP backend configuration
|
||||||
#
|
#
|
||||||
@ -61,19 +61,6 @@ monitoring:
|
|||||||
# To limit access to only localhost, use 127.0.0.1:2112
|
# To limit access to only localhost, use 127.0.0.1:2112
|
||||||
#listenAddress: :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 ##
|
## SRT server ##
|
||||||
# The SRT server receive incoming stream and can also serve video to clients.
|
# The SRT server receive incoming stream and can also serve video to clients.
|
||||||
srt:
|
srt:
|
||||||
@ -173,29 +160,17 @@ web:
|
|||||||
#
|
#
|
||||||
#widgetURL: ""
|
#widgetURL: ""
|
||||||
|
|
||||||
# IMPORTANT, CHANGE THIS
|
|
||||||
# You need to declare which entity you are and to specify an address to claim some content.
|
|
||||||
legalMentionsEntity: "l'association Crans"
|
|
||||||
legalMentionsAddress: "61 Avenue du Président Wilson, 94235 Cachan Cedex, France"
|
|
||||||
legalMentionsFullAddress:
|
|
||||||
- Association Cr@ns - ENS Paris-Saclay
|
|
||||||
- Notification de Contenus Illicites
|
|
||||||
- 4, avenue des Sciences
|
|
||||||
- 91190 Gif-sur-Yvette
|
|
||||||
- France
|
|
||||||
legalMentionsEmail: "bureau[at]crans.org"
|
|
||||||
|
|
||||||
## WebRTC server ##
|
## WebRTC server ##
|
||||||
webrtc:
|
webrtc:
|
||||||
# If you disable webrtc module, the web client won't be able to play streams.
|
# If you disable webrtc module, the web client won't be able to play streams.
|
||||||
#
|
#
|
||||||
#enabled: false
|
#enabled: true
|
||||||
|
|
||||||
# UDP port range used to stream
|
# UDP port range used to stream
|
||||||
# This range must be opened in your firewall.
|
# This range must be opened in your firewall.
|
||||||
#
|
#
|
||||||
#minPortUDP: 10000
|
#minPortUDP: 10000
|
||||||
#maxPortUDP: 11000
|
#maxPortUDP: 10005
|
||||||
|
|
||||||
# STUN servers, you should host your own Coturn instance
|
# STUN servers, you should host your own Coturn instance
|
||||||
#
|
#
|
||||||
|
9
go.mod
9
go.mod
@ -5,12 +5,11 @@ go 1.13
|
|||||||
require (
|
require (
|
||||||
github.com/go-ldap/ldap/v3 v3.2.3
|
github.com/go-ldap/ldap/v3 v3.2.3
|
||||||
github.com/gorilla/websocket v1.4.0
|
github.com/gorilla/websocket v1.4.0
|
||||||
github.com/haivision/srtgo v0.0.0-20201025191851-67964e8f497a
|
github.com/haivision/srtgo v0.0.0-20200731151239-e00427ae473a
|
||||||
github.com/markbates/pkger v0.17.1
|
github.com/markbates/pkger v0.17.1
|
||||||
github.com/pion/rtp v1.6.1
|
github.com/pion/rtp v1.6.0
|
||||||
github.com/pion/webrtc/v3 v3.0.0-beta.10
|
github.com/pion/webrtc/v3 v3.0.0-beta.5
|
||||||
github.com/pkg/profile v1.5.0
|
|
||||||
github.com/prometheus/client_golang v1.7.1
|
github.com/prometheus/client_golang v1.7.1
|
||||||
github.com/sherifabdlnaby/configuro v0.0.2
|
github.com/sherifabdlnaby/configuro v0.0.2
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||||
)
|
)
|
||||||
|
47
go.sum
47
go.sum
@ -120,8 +120,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg
|
|||||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/haivision/srtgo v0.0.0-20201025191851-67964e8f497a h1:54abJQezjMoiP+xMQ3ZQbcDXFjqytAYm/n0EVqrYeXg=
|
github.com/haivision/srtgo v0.0.0-20200731151239-e00427ae473a h1:JliMkv/mAqM5+QzG6Hkw1XcVl1crU8yIQGnhppMv7s0=
|
||||||
github.com/haivision/srtgo v0.0.0-20201025191851-67964e8f497a/go.mod h1:7izzTiCO3zc9ZIVTFMjxUiYL+kgryFP9rl3bsweqdmc=
|
github.com/haivision/srtgo v0.0.0-20200731151239-e00427ae473a/go.mod h1:yVZ4oACfcnUAcxrh+0b6IuIWfkHLK3IAQ99tuuhRx54=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
@ -192,30 +192,29 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
|
|||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
||||||
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
||||||
github.com/pion/dtls/v2 v2.0.3 h1:3qQ0s4+TXD00rsllL8g8KQcxAs+Y/Z6oz618RXX6p14=
|
github.com/pion/dtls/v2 v2.0.2 h1:FHCHTiM182Y8e15aFTiORroiATUI16ryHiQh8AIOJ1E=
|
||||||
github.com/pion/dtls/v2 v2.0.3/go.mod h1:TUjyL8bf8LH95h81Xj7kATmzMRt29F/4lxpIPj2Xe4Y=
|
github.com/pion/dtls/v2 v2.0.2/go.mod h1:27PEO3MDdaCfo21heT59/vsdmZc0zMt9wQPcSlLu/1I=
|
||||||
github.com/pion/ice/v2 v2.0.9 h1:oHbiN6Q9tgb8Gfu3I4cbr5mHRE1uqiuFABQ8CbWjIyk=
|
github.com/pion/ice/v2 v2.0.6 h1:7Jf3AX6VIjgO2tGRyT0RGGxkDYOF4m5I5DQzf34IN1Y=
|
||||||
github.com/pion/ice/v2 v2.0.9/go.mod h1:NK+o39ynb+N1YSj9fPgWs3vjVcrsWw0KCr/311MqVq8=
|
github.com/pion/ice/v2 v2.0.6/go.mod h1:xOXvVRlQC/B7FPJeJYKY6IepFRAKb3t1un1K9boYaaQ=
|
||||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
|
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
|
||||||
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
||||||
github.com/pion/quic v0.1.4 h1:bNz9sCJjlM3GqMdq7Fne57FiWfdyiJ++yHVbuqeoD3Y=
|
github.com/pion/quic v0.1.4 h1:bNz9sCJjlM3GqMdq7Fne57FiWfdyiJ++yHVbuqeoD3Y=
|
||||||
github.com/pion/quic v0.1.4/go.mod h1:dBhNvkLoQqRwfi6h3Vqj3IcPLgiW7rkZxBbRdp7Vzvk=
|
github.com/pion/quic v0.1.4/go.mod h1:dBhNvkLoQqRwfi6h3Vqj3IcPLgiW7rkZxBbRdp7Vzvk=
|
||||||
|
github.com/pion/randutil v0.0.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
github.com/pion/rtcp v1.2.4 h1:NT3H5LkUGgaEapvp0HGik+a+CpflRF7KTD7H+o7OWIM=
|
github.com/pion/rtcp v1.2.3 h1:2wrhKnqgSz91Q5nzYTO07mQXztYPtxL8a0XOss4rJqA=
|
||||||
github.com/pion/rtcp v1.2.4/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
|
github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
|
||||||
github.com/pion/rtp v1.6.1 h1:2Y2elcVBrahYnHKN2X7rMHX/r1R4TEBMP1LaVu/wNhk=
|
github.com/pion/rtp v1.6.0 h1:4Ssnl/T5W2LzxHj9ssYpGVEQh3YYhQFNVmSWO88MMwk=
|
||||||
github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
|
github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI=
|
||||||
github.com/pion/sctp v1.7.10 h1:o3p3/hZB5Cx12RMGyWmItevJtZ6o2cpuxaw6GOS4x+8=
|
github.com/pion/sctp v1.7.10 h1:o3p3/hZB5Cx12RMGyWmItevJtZ6o2cpuxaw6GOS4x+8=
|
||||||
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||||
github.com/pion/sctp v1.7.11 h1:UCnj7MsobLKLuP/Hh+JMiI/6W5Bs/VF45lWKgHFjSIE=
|
github.com/pion/sdp/v3 v3.0.1 h1:we4OyeTT6s+6sxrAKy/LVywskx1dzUQlh4xu3b+iAs0=
|
||||||
github.com/pion/sctp v1.7.11/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
github.com/pion/sdp/v3 v3.0.1/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
||||||
github.com/pion/sdp/v3 v3.0.2 h1:UNnSPVaMM+Pdu/mR9UvAyyo6zkdYbKeuOooCwZvTl/g=
|
github.com/pion/srtp v1.5.1 h1:9Q3jAfslYZBt+C69SI/ZcONJh9049JUHZWYRRf5KEKw=
|
||||||
github.com/pion/sdp/v3 v3.0.2/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
|
github.com/pion/srtp v1.5.1/go.mod h1:B+QgX5xPeQTNc1CJStJPHzOlHK66ViMDWTT0HZTCkcA=
|
||||||
github.com/pion/srtp v1.5.2 h1:25DmvH+fqKZDqvX64vTwnycVwL9ooJxHF/gkX16bDBY=
|
|
||||||
github.com/pion/srtp v1.5.2/go.mod h1:NiBff/MSxUwMUwx/fRNyD/xGE+dVvf8BOCeXhjCXZ9U=
|
|
||||||
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg=
|
||||||
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA=
|
||||||
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
||||||
@ -226,14 +225,12 @@ github.com/pion/turn/v2 v2.0.4 h1:oDguhEv2L/4rxwbL9clGLgtzQPjtuZwCdoM7Te8vQVk=
|
|||||||
github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog=
|
github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog=
|
||||||
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
||||||
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
||||||
github.com/pion/webrtc/v3 v3.0.0-beta.10 h1:1aBn9jv/oe4v2Uf47HutWIjg2i2ZP/O7HqpgKPqSuhE=
|
github.com/pion/webrtc/v3 v3.0.0-beta.5 h1:A1hApeo77Kp2sHWwd53rIHZZm5W9JXEn//5jHj9+fdc=
|
||||||
github.com/pion/webrtc/v3 v3.0.0-beta.10/go.mod h1:GlriYYHJ5KkNsCunm3oFDPql4TDTrrNoI9iSWWSnafA=
|
github.com/pion/webrtc/v3 v3.0.0-beta.5/go.mod h1:HjQaNPq5SdNazGlwiim6/lWBkZD97mPPyDttr7byNA4=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
|
|
||||||
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
@ -352,8 +349,6 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf
|
|||||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@ -389,8 +384,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
|
|||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
|
|
||||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@ -423,10 +416,6 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200610111108-226ff32320da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c h1:38q6VNPWR010vN82/SB121GujZNIfAUb4YttE2rhGuc=
|
|
||||||
golang.org/x/sys v0.0.0-20200926100807-9d91bd62050c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -456,8 +445,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/sherifabdlnaby/configuro"
|
"github.com/sherifabdlnaby/configuro"
|
||||||
@ -24,7 +23,6 @@ type Config struct {
|
|||||||
Auth auth.Options
|
Auth auth.Options
|
||||||
Forwarding forwarding.Options
|
Forwarding forwarding.Options
|
||||||
Monitoring monitoring.Options
|
Monitoring monitoring.Options
|
||||||
OME ovenmediaengine.Options
|
|
||||||
Srt srt.Options
|
Srt srt.Options
|
||||||
Telnet telnet.Options
|
Telnet telnet.Options
|
||||||
Transcoder transcoder.Options
|
Transcoder transcoder.Options
|
||||||
@ -51,11 +49,6 @@ func New() *Config {
|
|||||||
Enabled: true,
|
Enabled: true,
|
||||||
ListenAddress: ":2112",
|
ListenAddress: ":2112",
|
||||||
},
|
},
|
||||||
OME: ovenmediaengine.Options{
|
|
||||||
Enabled: true,
|
|
||||||
URL: "ovenmediaengine:1915",
|
|
||||||
App: "play",
|
|
||||||
},
|
|
||||||
Srt: srt.Options{
|
Srt: srt.Options{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
ListenAddress: ":9710",
|
ListenAddress: ":9710",
|
||||||
@ -82,15 +75,10 @@ func New() *Config {
|
|||||||
MapDomainToStream: make(map[string]string),
|
MapDomainToStream: make(map[string]string),
|
||||||
PlayerPoster: "/static/img/no_stream.svg",
|
PlayerPoster: "/static/img/no_stream.svg",
|
||||||
ViewersCounterRefreshPeriod: 20000,
|
ViewersCounterRefreshPeriod: 20000,
|
||||||
LegalMentionsEntity: "l'association Crans",
|
|
||||||
LegalMentionsAddress: "61 Avenue du Président Wilson, 94235 Cachan Cedex, France",
|
|
||||||
LegalMentionsFullAddress: []string{"Association Cr@ns - ENS Paris-Saclay",
|
|
||||||
"Notification de Contenus Illicites", "4, avenue des Sciences", "91190 Gif-sur-Yvette", "France"},
|
|
||||||
LegalMentionsEmail: "bureau[at]crans.org",
|
|
||||||
},
|
},
|
||||||
WebRTC: webrtc.Options{
|
WebRTC: webrtc.Options{
|
||||||
Enabled: false,
|
Enabled: true,
|
||||||
MaxPortUDP: 11000,
|
MaxPortUDP: 10005,
|
||||||
MinPortUDP: 10000,
|
MinPortUDP: 10000,
|
||||||
STUNServers: []string{"stun:stun.l.google.com:19302"},
|
STUNServers: []string{"stun:stun.l.google.com:19302"},
|
||||||
},
|
},
|
||||||
@ -129,7 +117,7 @@ func Load() (*Config, error) {
|
|||||||
|
|
||||||
// If no credentials register, add demo account with password "demo"
|
// If no credentials register, add demo account with password "demo"
|
||||||
if len(cfg.Auth.Basic.Credentials) < 1 {
|
if len(cfg.Auth.Basic.Credentials) < 1 {
|
||||||
cfg.Auth.Basic.Credentials["demo"] = "$2b$10$xuU7XFwmRX2CMgdSaA8rM.4Y8.BtRNzhUedwN0G8tCegDRNUERTCS"
|
cfg.Auth.Basic.Credentials["demo"] = "$2b$15$LRnG3eIHFlYIguTxZOLH7eHwbQC/vqjnLq6nDFiHSUDKIU.f5/1H6"
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
|
8
main.go
8
main.go
@ -5,10 +5,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/pkg/profile"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/auth"
|
"gitlab.crans.org/nounous/ghostream/auth"
|
||||||
"gitlab.crans.org/nounous/ghostream/internal/config"
|
"gitlab.crans.org/nounous/ghostream/internal/config"
|
||||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||||
@ -22,9 +20,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// TODO Don't always profile if not needed
|
|
||||||
defer profile.Start().Stop()
|
|
||||||
|
|
||||||
// Configure logger
|
// Configure logger
|
||||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
|
||||||
@ -50,10 +45,9 @@ func main() {
|
|||||||
go transcoder.Init(streams, &cfg.Transcoder)
|
go transcoder.Init(streams, &cfg.Transcoder)
|
||||||
go forwarding.Serve(streams, cfg.Forwarding)
|
go forwarding.Serve(streams, cfg.Forwarding)
|
||||||
go monitoring.Serve(&cfg.Monitoring)
|
go monitoring.Serve(&cfg.Monitoring)
|
||||||
go ovenmediaengine.Serve(streams, &cfg.OME)
|
|
||||||
go srt.Serve(streams, authBackend, &cfg.Srt)
|
go srt.Serve(streams, authBackend, &cfg.Srt)
|
||||||
go telnet.Serve(streams, &cfg.Telnet)
|
go telnet.Serve(streams, &cfg.Telnet)
|
||||||
go web.Serve(streams, &cfg.Web, &cfg.OME)
|
go web.Serve(streams, &cfg.Web)
|
||||||
go webrtc.Serve(streams, &cfg.WebRTC)
|
go webrtc.Serve(streams, &cfg.WebRTC)
|
||||||
|
|
||||||
// Wait for routines
|
// Wait for routines
|
||||||
|
@ -10,6 +10,12 @@ import (
|
|||||||
// Quality holds a specific stream quality.
|
// Quality holds a specific stream quality.
|
||||||
// It makes packages able to subscribe to an incoming stream.
|
// It makes packages able to subscribe to an incoming stream.
|
||||||
type Quality struct {
|
type Quality struct {
|
||||||
|
// Type of the quality
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Source Stream
|
||||||
|
Stream *Stream
|
||||||
|
|
||||||
// Incoming data come from this channel
|
// Incoming data come from this channel
|
||||||
Broadcast chan<- []byte
|
Broadcast chan<- []byte
|
||||||
|
|
||||||
@ -27,8 +33,9 @@ type Quality struct {
|
|||||||
WebRtcRemoteSdp chan webrtc.SessionDescription
|
WebRtcRemoteSdp chan webrtc.SessionDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
func newQuality() (q *Quality) {
|
func newQuality(name string, stream *Stream) (q *Quality) {
|
||||||
q = &Quality{}
|
q = &Quality{Name: name}
|
||||||
|
q.Stream = stream
|
||||||
broadcast := make(chan []byte, 1024)
|
broadcast := make(chan []byte, 1024)
|
||||||
q.Broadcast = broadcast
|
q.Broadcast = broadcast
|
||||||
q.outputs = make(map[chan []byte]struct{})
|
q.outputs = make(map[chan []byte]struct{})
|
||||||
|
@ -40,7 +40,7 @@ func (s *Stream) CreateQuality(name string) (quality *Quality, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.lockQualities.Lock()
|
s.lockQualities.Lock()
|
||||||
quality = newQuality()
|
quality = newQuality(name, s)
|
||||||
s.qualities[name] = quality
|
s.qualities[name] = quality
|
||||||
s.lockQualities.Unlock()
|
s.lockQualities.Unlock()
|
||||||
return quality, nil
|
return quality, nil
|
||||||
|
@ -40,7 +40,6 @@ func Serve(streams *messaging.Streams, cfg Options) {
|
|||||||
stream, err := streams.Get(name)
|
stream, err := streams.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get stream '%s'", name)
|
log.Printf("Failed to get stream '%s'", name)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get specific quality
|
// Get specific quality
|
||||||
@ -75,8 +74,8 @@ func forward(streamName string, q *messaging.Quality, fwdCfg []string) {
|
|||||||
formattedURL = strings.ReplaceAll(formattedURL, "%S", fmt.Sprintf("%02d", now.Second()))
|
formattedURL = strings.ReplaceAll(formattedURL, "%S", fmt.Sprintf("%02d", now.Second()))
|
||||||
formattedURL = strings.ReplaceAll(formattedURL, "%name", streamName)
|
formattedURL = strings.ReplaceAll(formattedURL, "%name", streamName)
|
||||||
|
|
||||||
params = append(params, "-f", "flv",
|
params = append(params, "-f", "flv", "-preset", "ultrafast", "-tune", "zerolatency",
|
||||||
"-c:v", "copy", "-c:a", "aac", "-b:a", "160k", "-ar", "44100", formattedURL)
|
"-c", "copy", formattedURL)
|
||||||
}
|
}
|
||||||
ffmpeg := exec.Command("ffmpeg", params...)
|
ffmpeg := exec.Command("ffmpeg", params...)
|
||||||
|
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,6 +24,17 @@ func handleStreamer(socket *srtgo.SrtSocket, streams *messaging.Streams, name st
|
|||||||
socket.Close()
|
socket.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create sub-qualities
|
||||||
|
for _, qualityName := range []string{"audio", "480p", "360p", "240p"} {
|
||||||
|
_, err := stream.CreateQuality(qualityName)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error on quality creating: %s", err)
|
||||||
|
socket.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("New SRT streamer for stream '%s' quality 'source'", name)
|
log.Printf("New SRT streamer for stream '%s' quality 'source'", name)
|
||||||
|
|
||||||
// Read RTP packets forever and send them to the WebRTC Client
|
// Read RTP packets forever and send them to the WebRTC Client
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
// Package srt serves a SRT server
|
// Package srt serves a SRT server
|
||||||
package srt
|
package srt
|
||||||
|
|
||||||
|
// #include <srt/srt.h>
|
||||||
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@ -59,7 +62,7 @@ func Serve(streams *messaging.Streams, authBackend auth.Backend, cfg *Options) {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
// Wait for new connection
|
// Wait for new connection
|
||||||
s, _, err := sck.Accept()
|
s, err := sck.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Something wrong happened
|
// Something wrong happened
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
@ -70,7 +73,7 @@ func Serve(streams *messaging.Streams, authBackend auth.Backend, cfg *Options) {
|
|||||||
// Without this, the SRT buffer might get full before reading it
|
// Without this, the SRT buffer might get full before reading it
|
||||||
|
|
||||||
// streamid can be "name:password" for streamer or "name" for viewer
|
// streamid can be "name:password" for streamer or "name" for viewer
|
||||||
streamID, err := s.GetSockOptString(srtgo.SRTO_STREAMID)
|
streamID, err := s.GetSockOptString(C.SRTO_STREAMID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Failed to get socket streamid")
|
log.Print("Failed to get socket streamid")
|
||||||
continue
|
continue
|
||||||
|
@ -3,9 +3,7 @@ package webrtc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
@ -16,36 +14,61 @@ import (
|
|||||||
|
|
||||||
func ingest(name string, q *messaging.Quality) {
|
func ingest(name string, q *messaging.Quality) {
|
||||||
// Register to get stream
|
// Register to get stream
|
||||||
videoInput := make(chan []byte, 1024)
|
input := make(chan []byte, 1024)
|
||||||
q.Register(videoInput)
|
// FIXME Stream data should already be transcoded
|
||||||
|
source, _ := q.Stream.GetQuality("source")
|
||||||
|
source.Register(input)
|
||||||
|
|
||||||
// FIXME Mux into RTP without having multiple UDP listeners
|
// FIXME Bad code
|
||||||
firstPort := int(rand.Int31n(63535)) + 2000
|
port := 5000
|
||||||
|
var tracks map[string][]*webrtc.Track
|
||||||
// Open UDP listeners for RTP Packets
|
qualityName := ""
|
||||||
audioListener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: firstPort})
|
switch q.Name {
|
||||||
if err != nil {
|
case "audio":
|
||||||
log.Printf("Faited to open UDP listener %s", err)
|
port = 5004
|
||||||
return
|
tracks = audioTracks
|
||||||
|
break
|
||||||
|
case "source":
|
||||||
|
port = 5005
|
||||||
|
tracks = videoTracks
|
||||||
|
qualityName = "@source"
|
||||||
|
break
|
||||||
|
case "480p":
|
||||||
|
port = 5006
|
||||||
|
tracks = videoTracks
|
||||||
|
qualityName = "@480p"
|
||||||
|
break
|
||||||
|
case "360p":
|
||||||
|
port = 5007
|
||||||
|
tracks = videoTracks
|
||||||
|
qualityName = "@360p"
|
||||||
|
break
|
||||||
|
case "240p":
|
||||||
|
port = 5008
|
||||||
|
tracks = videoTracks
|
||||||
|
qualityName = "@240p"
|
||||||
|
break
|
||||||
}
|
}
|
||||||
videoListener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: firstPort + 1})
|
|
||||||
|
// Open a UDP Listener for RTP Packets
|
||||||
|
listener, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: port})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Faited to open UDP listener %s", err)
|
log.Printf("Faited to open UDP listener %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start ffmpag to convert videoInput to video and audio UDP
|
// Start ffmpag to convert input to video and audio UDP
|
||||||
ffmpeg, err := startFFmpeg(videoInput, firstPort)
|
ffmpeg, err := startFFmpeg(q, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error while starting ffmpeg: %s", err)
|
log.Printf("Error while starting ffmpeg: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive video
|
// Receive stream
|
||||||
go func() {
|
go func() {
|
||||||
inboundRTPPacket := make([]byte, 1500) // UDP MTU
|
inboundRTPPacket := make([]byte, 1500) // UDP MTU
|
||||||
for {
|
for {
|
||||||
n, _, err := videoListener.ReadFromUDP(inboundRTPPacket)
|
n, _, err := listener.ReadFromUDP(inboundRTPPacket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to read from UDP: %s", err)
|
log.Printf("Failed to read from UDP: %s", err)
|
||||||
break
|
break
|
||||||
@ -56,49 +79,13 @@ func ingest(name string, q *messaging.Quality) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if videoTracks[name] == nil {
|
// Write RTP srtPacket to all tracks
|
||||||
videoTracks[name] = make([]*webrtc.Track, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write RTP srtPacket to all video tracks
|
|
||||||
// Adapt payload and SSRC to match destination
|
// Adapt payload and SSRC to match destination
|
||||||
for _, videoTrack := range videoTracks[name] {
|
for _, track := range tracks[name+qualityName] {
|
||||||
packet.Header.PayloadType = videoTrack.PayloadType()
|
packet.Header.PayloadType = track.PayloadType()
|
||||||
packet.Header.SSRC = videoTrack.SSRC()
|
packet.Header.SSRC = track.SSRC()
|
||||||
if writeErr := videoTrack.WriteRTP(packet); writeErr != nil {
|
if writeErr := track.WriteRTP(packet); writeErr != nil {
|
||||||
log.Printf("Failed to write to video track: %s", err)
|
log.Printf("Failed to write to track: %s", writeErr)
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Receive audio
|
|
||||||
go func() {
|
|
||||||
inboundRTPPacket := make([]byte, 1500) // UDP MTU
|
|
||||||
for {
|
|
||||||
n, _, err := audioListener.ReadFromUDP(inboundRTPPacket)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to read from UDP: %s", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
packet := &rtp.Packet{}
|
|
||||||
if err := packet.Unmarshal(inboundRTPPacket[:n]); err != nil {
|
|
||||||
log.Printf("Failed to unmarshal RTP srtPacket: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if audioTracks[name] == nil {
|
|
||||||
audioTracks[name] = make([]*webrtc.Track, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write RTP srtPacket to all audio tracks
|
|
||||||
// Adapt payload and SSRC to match destination
|
|
||||||
for _, audioTrack := range audioTracks[name] {
|
|
||||||
packet.Header.PayloadType = audioTrack.PayloadType()
|
|
||||||
packet.Header.SSRC = audioTrack.SSRC()
|
|
||||||
if writeErr := audioTrack.WriteRTP(packet); writeErr != nil {
|
|
||||||
log.Printf("Failed to write to audio track: %s", err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,24 +97,47 @@ func ingest(name string, q *messaging.Quality) {
|
|||||||
log.Printf("Faited to wait for ffmpeg: %s", err)
|
log.Printf("Faited to wait for ffmpeg: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close UDP listeners
|
// Close UDP listener
|
||||||
if err = videoListener.Close(); err != nil {
|
if err = listener.Close(); err != nil {
|
||||||
log.Printf("Faited to close UDP listener: %s", err)
|
log.Printf("Faited to close UDP listener: %s", err)
|
||||||
}
|
}
|
||||||
if err = audioListener.Close(); err != nil {
|
q.Unregister(input)
|
||||||
log.Printf("Faited to close UDP listener: %s", err)
|
|
||||||
}
|
|
||||||
q.Unregister(videoInput)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startFFmpeg(in <-chan []byte, listeningPort int) (ffmpeg *exec.Cmd, err error) {
|
func startFFmpeg(q *messaging.Quality, in <-chan []byte) (ffmpeg *exec.Cmd, err error) {
|
||||||
ffmpegArgs := []string{"-hide_banner", "-loglevel", "error", "-i", "pipe:0",
|
// FIXME Use transcoders to downscale, then remux in RTP
|
||||||
// Audio
|
ffmpegArgs := []string{"-hide_banner", "-loglevel", "error", "-i", "pipe:0"}
|
||||||
"-vn", "-c:a", "libopus", "-b:a", "96k",
|
switch q.Name {
|
||||||
"-f", "rtp", fmt.Sprintf("rtp://127.0.0.1:%d", listeningPort),
|
case "audio":
|
||||||
// Source
|
ffmpegArgs = append(ffmpegArgs, "-vn", "-c:a", "libopus", "-b:a", "160k",
|
||||||
"-an", "-c:v", "copy",
|
"-f", "rtp", "rtp://127.0.0.1:5004")
|
||||||
"-f", "rtp", fmt.Sprintf("rtp://127.0.0.1:%d", listeningPort+1)}
|
break
|
||||||
|
case "source":
|
||||||
|
ffmpegArgs = append(ffmpegArgs, "-an", "-c:v", "copy",
|
||||||
|
"-f", "rtp", "rtp://127.0.0.1:5005")
|
||||||
|
break
|
||||||
|
case "480p":
|
||||||
|
ffmpegArgs = append(ffmpegArgs,
|
||||||
|
"-an", "-c:v", "libx264", "-b:v", "1200k", "-maxrate", "2000k", "-bufsize", "3000k",
|
||||||
|
"-preset", "ultrafast", "-profile", "main", "-tune", "zerolatency",
|
||||||
|
"-vf", "scale=854:480",
|
||||||
|
"-f", "rtp", "rtp://127.0.0.1:5006")
|
||||||
|
break
|
||||||
|
case "360p":
|
||||||
|
ffmpegArgs = append(ffmpegArgs,
|
||||||
|
"-an", "-c:v", "libx264", "-b:v", "800k", "-maxrate", "1200k", "-bufsize", "1500k",
|
||||||
|
"-preset", "ultrafast", "-profile", "main", "-tune", "zerolatency",
|
||||||
|
"-vf", "scale=480:360",
|
||||||
|
"-f", "rtp", "rtp://127.0.0.1:5007")
|
||||||
|
break
|
||||||
|
case "240p":
|
||||||
|
ffmpegArgs = append(ffmpegArgs,
|
||||||
|
"-an", "-c:v", "libx264", "-b:v", "500k", "-maxrate", "800k", "-bufsize", "1000k",
|
||||||
|
"-preset", "ultrafast", "-profile", "main", "-tune", "zerolatency",
|
||||||
|
"-vf", "scale=360:240",
|
||||||
|
"-f", "rtp", "rtp://127.0.0.1:5008")
|
||||||
|
break
|
||||||
|
}
|
||||||
ffmpeg = exec.Command("ffmpeg", ffmpegArgs...)
|
ffmpeg = exec.Command("ffmpeg", ffmpegArgs...)
|
||||||
|
|
||||||
// Handle errors output
|
// Handle errors output
|
||||||
|
@ -40,7 +40,7 @@ func removeTrack(tracks []*webrtc.Track, track *webrtc.Track) []*webrtc.Track {
|
|||||||
|
|
||||||
// GetNumberConnectedSessions get the number of currently connected clients
|
// GetNumberConnectedSessions get the number of currently connected clients
|
||||||
func GetNumberConnectedSessions(streamID string) int {
|
func GetNumberConnectedSessions(streamID string) int {
|
||||||
return len(videoTracks[streamID])
|
return len(audioTracks[streamID])
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPeerHandler is called when server receive a new session description
|
// newPeerHandler is called when server receive a new session description
|
||||||
@ -117,21 +117,20 @@ func newPeerHandler(name string, localSdpChan chan webrtc.SessionDescription, re
|
|||||||
quality = split[1]
|
quality = split[1]
|
||||||
}
|
}
|
||||||
log.Printf("New WebRTC session for stream %s, quality %s", streamID, quality)
|
log.Printf("New WebRTC session for stream %s, quality %s", streamID, quality)
|
||||||
// TODO Consider the quality
|
|
||||||
|
|
||||||
// Set the handler for ICE connection state
|
// Set the handler for ICE connection state
|
||||||
// This will notify you when the peer has connected/disconnected
|
// This will notify you when the peer has connected/disconnected
|
||||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||||
log.Printf("Connection State has changed %s \n", connectionState.String())
|
log.Printf("Connection State has changed %s \n", connectionState.String())
|
||||||
if videoTracks[streamID] == nil {
|
if videoTracks[streamID+"@"+quality] == nil {
|
||||||
videoTracks[streamID] = make([]*webrtc.Track, 0, 1)
|
videoTracks[streamID+"@"+quality] = make([]*webrtc.Track, 0, 1)
|
||||||
}
|
}
|
||||||
if audioTracks[streamID] == nil {
|
if audioTracks[streamID] == nil {
|
||||||
audioTracks[streamID] = make([]*webrtc.Track, 0, 1)
|
audioTracks[streamID] = make([]*webrtc.Track, 0, 1)
|
||||||
}
|
}
|
||||||
if connectionState == webrtc.ICEConnectionStateConnected {
|
if connectionState == webrtc.ICEConnectionStateConnected {
|
||||||
// Register tracks
|
// Register tracks
|
||||||
videoTracks[streamID] = append(videoTracks[streamID], videoTrack)
|
videoTracks[streamID+"@"+quality] = append(videoTracks[streamID+"@"+quality], videoTrack)
|
||||||
audioTracks[streamID] = append(audioTracks[streamID], audioTrack)
|
audioTracks[streamID] = append(audioTracks[streamID], audioTrack)
|
||||||
monitoring.WebRTCConnectedSessions.Inc()
|
monitoring.WebRTCConnectedSessions.Inc()
|
||||||
} else if connectionState == webrtc.ICEConnectionStateDisconnected {
|
} else if connectionState == webrtc.ICEConnectionStateDisconnected {
|
||||||
@ -205,16 +204,17 @@ func Serve(streams *messaging.Streams, cfg *Options) {
|
|||||||
|
|
||||||
// Get specific quality
|
// Get specific quality
|
||||||
// FIXME: make it possible to forward other qualities
|
// FIXME: make it possible to forward other qualities
|
||||||
qualityName := "source"
|
for _, qualityName := range []string{"source", "audio", "480p", "360p", "240p"} {
|
||||||
quality, err := stream.GetQuality(qualityName)
|
quality, err := stream.GetQuality(qualityName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to get quality '%s'", qualityName)
|
log.Printf("Failed to get quality '%s'", qualityName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start forwarding
|
// Start forwarding
|
||||||
log.Printf("Starting webrtc for '%s' quality '%s'", name, qualityName)
|
log.Printf("Starting webrtc for '%s' quality '%s'", name, qualityName)
|
||||||
go ingest(name, quality)
|
go ingest(name, quality)
|
||||||
go listenSdp(name, quality.WebRtcLocalSdp, quality.WebRtcRemoteSdp, cfg)
|
go listenSdp(name, quality.WebRtcLocalSdp, quality.WebRtcRemoteSdp, cfg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,21 +10,15 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/markbates/pkger"
|
"github.com/markbates/pkger"
|
||||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/webrtc"
|
"gitlab.crans.org/nounous/ghostream/stream/webrtc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Precompile regex
|
// Precompile regex
|
||||||
validPath = regexp.MustCompile("^/[a-z0-9@_-]*$")
|
validPath = regexp.MustCompile("^/[a-z0-9@_-]*$")
|
||||||
|
|
||||||
counterMutex = new(sync.Mutex)
|
|
||||||
connectedClients = make(map[string]map[string]int64)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handle site index and viewer pages
|
// Handle site index and viewer pages
|
||||||
@ -67,8 +61,7 @@ func viewerHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
Cfg *Options
|
Cfg *Options
|
||||||
Path string
|
Path string
|
||||||
WidgetURL string
|
WidgetURL string
|
||||||
OMECfg *ovenmediaengine.Options
|
}{Path: path, Cfg: cfg, WidgetURL: ""}
|
||||||
}{Path: path, Cfg: cfg, WidgetURL: "", OMECfg: omeCfg}
|
|
||||||
|
|
||||||
// Load widget is user does not disable it with ?nowidget
|
// Load widget is user does not disable it with ?nowidget
|
||||||
if _, ok := r.URL.Query()["nowidget"]; !ok {
|
if _, ok := r.URL.Query()["nowidget"]; !ok {
|
||||||
@ -95,43 +88,14 @@ func staticHandler() http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func statisticsHandler(w http.ResponseWriter, r *http.Request) {
|
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.SplitN(strings.Replace(r.URL.Path[7:], "/", "", -1), "@", 2)[0]
|
||||||
userCount := 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
|
// Get requested stream
|
||||||
stream, err := streams.Get(name)
|
stream, err := streams.Get(name)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
userCount = stream.ClientCount()
|
userCount = stream.ClientCount()
|
||||||
userCount += webrtc.GetNumberConnectedSessions(name)
|
userCount += webrtc.GetNumberConnectedSessions(name)
|
||||||
userCount += len(connectedClients[name])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display connected users statistics
|
// Display connected users statistics
|
||||||
|
@ -9,7 +9,6 @@ export class ViewerCounter {
|
|||||||
constructor(element, streamName) {
|
constructor(element, streamName) {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.url = "/_stats/" + streamName;
|
this.url = "/_stats/" + streamName;
|
||||||
this.uid = Math.floor(1e19 * Math.random()).toString(16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,7 +21,7 @@ export class ViewerCounter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshViewersCounter() {
|
refreshViewersCounter() {
|
||||||
fetch(this.url + "?uid=" + this.uid)
|
fetch(this.url)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then((data) => this.element.innerText = data.ConnectedViewers)
|
.then((data) => this.element.innerText = data.ConnectedViewers)
|
||||||
.catch(console.log);
|
.catch(console.log);
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@ -14,7 +14,7 @@ export function initViewerPage(stream, stunServers, viewersCounterRefreshPeriod)
|
|||||||
const viewer = document.getElementById("viewer");
|
const viewer = document.getElementById("viewer");
|
||||||
|
|
||||||
// Default quality
|
// Default quality
|
||||||
let quality = "source";
|
let quality = "240p";
|
||||||
|
|
||||||
// Create WebSocket and WebRTC
|
// Create WebSocket and WebRTC
|
||||||
const websocket = new GsWebSocket();
|
const websocket = new GsWebSocket();
|
||||||
|
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
@ -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
@ -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
@ -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
@ -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 */
|
|
@ -9,11 +9,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Comment je diffuse ?</h2>
|
<h2>Comment je diffuse ?</h2>
|
||||||
<p>
|
<p>Pour diffuser un contenu vous devez être adhérent Crans.</p>
|
||||||
Pour diffuser un contenu vous devez avoir des identifiants valides.
|
|
||||||
Si le service est hébergé par une association, il est probable que
|
|
||||||
vous deviez être membre de cette association.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Avec Open Broadcaster Software</h3>
|
<h3>Avec Open Broadcaster Software</h3>
|
||||||
<p>
|
<p>
|
||||||
@ -25,7 +21,11 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<b>Serveur :</b>
|
<b>Serveur :</b>
|
||||||
<code>srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?IDENTIFIANT:MOT_DE_PASSE</code>,
|
<code>srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}</code>,
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b>Clé de stream :</b>
|
||||||
|
<code>IDENTIFIANT:MOT_DE_PASSE</code>
|
||||||
avec <code>IDENTIFIANT</code> et <code>MOT_DE_PASSE</code>
|
avec <code>IDENTIFIANT</code> et <code>MOT_DE_PASSE</code>
|
||||||
vos identifiants.
|
vos identifiants.
|
||||||
</li>
|
</li>
|
||||||
@ -46,104 +46,29 @@
|
|||||||
<p>
|
<p>
|
||||||
<code>
|
<code>
|
||||||
{{/* FIXME replace with good SRT params */}}
|
{{/* FIXME replace with good SRT params */}}
|
||||||
ffmpeg -re -i mavideo.webm -vcodec libx264
|
ffmpeg -re -i mavideo.webm -vcodec libx264 -vprofile baseline
|
||||||
-preset:v veryfast -vprofile baseline -tune zerolatency
|
|
||||||
-acodec aac -strict -2 -f flv
|
-acodec aac -strict -2 -f flv
|
||||||
srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT:MOT_DE_PASSE
|
srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT:MOT_DE_PASSE
|
||||||
</code>
|
</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>Comment lire un flux depuis un lecteur externe ?</h2>
|
|
||||||
<p>
|
|
||||||
À l'heure actuelle, la plupart des lecteurs vidéos ne supportent
|
|
||||||
pas le protocole SRT, ou le supportent mal. Un travail est en
|
|
||||||
cours pour les rendre un maximum compatibles. Liste non exhaustive
|
|
||||||
des lecteurs vidéos testés :
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>FFPlay</h3>
|
|
||||||
<p>
|
|
||||||
Si FFMpeg est installé sur votre machine, il est accompagné d'un
|
|
||||||
lecteur vidéo nommé <code>ffplay</code>. Si FFMpeg est compilé
|
|
||||||
avec le support de SRT (c'est le cas sur la plupart des distributions,
|
|
||||||
sauf cas ci-dessous), il vous suffira d'exécuter :
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<code>
|
|
||||||
ffplay -fflags nobuffer srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT
|
|
||||||
</code>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>MPV</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
MPV supporte officiellement SRT depuis le 16 octobre 2020.
|
|
||||||
Néanmoins, la version stable de MPV est beaucoup plus vieille.
|
|
||||||
Vous devez alors utiliser une version de développement pour
|
|
||||||
pouvoir lire un flux SRT depuis MPV. L'installation se fait
|
|
||||||
depuis <a href="https://mpv.io/installation/"> cette page</a>.
|
|
||||||
Sous Arch Linux, il vous suffit de récupérer le paquet
|
|
||||||
<code>mpv-git</code> dans l'AUR. Pour lire le flux, il suffit
|
|
||||||
d'exécuter :
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<code>
|
|
||||||
mpv srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT
|
|
||||||
</code>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>VLC Media Player</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Bien que VLC supporte officiellement le protocole SRT,
|
|
||||||
toutes les options ne sont pas encore implémentées,
|
|
||||||
notamment l'option pour choisir son stream.
|
|
||||||
Cette option n'est supportée que dans la version de développement
|
|
||||||
depuis très récemment, grâce à un patch de l'un des développeurs
|
|
||||||
de Ghostream. Sous Arch Linux, il suffit de récupérer
|
|
||||||
le paquet <code>vlc-git</code> de l'AUR. Avec un VLC à jour,
|
|
||||||
il suffit d'exécuter :
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<code>
|
|
||||||
vlc srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT
|
|
||||||
</code>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Ou bien d'aller dans Média -> Ouvrir un flux réseau et d'entrer l'URL
|
|
||||||
<code>srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid=IDENTIFIANT</code>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3>Le protocole n'existe pas ou n'est pas supporté.</h3>
|
|
||||||
<p>
|
|
||||||
La technologie SRT est très récente et n'est pas supportée par
|
|
||||||
les dépôts stables. Ainsi, si vous avez Ubuntu ≤ 20.04 ou
|
|
||||||
Debian ≤ Buster, vous ne pourrez pas utiliser de lecteur vidéo
|
|
||||||
ni même diffuser avec votre machine. Vous devrez vous mettre à
|
|
||||||
jour vers Ubuntu 20.10 ou Debian Bullseye.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Mentions légales</h2>
|
<h2>Mentions légales</h2>
|
||||||
<p>
|
<p>
|
||||||
Le service de diffusion vidéo du Crans est un service d'hébergement
|
Le service de diffusion vidéo du Crans est un service d'hébergement
|
||||||
au sens de l'article 6, I, 2e de la loi 2004-575 du 21 juin 2004.
|
au sens de l'article 6, I, 2e de la loi 2004-575 du 21 juin 2004.
|
||||||
Conformément aux dispositions de l'article 6, II du même,
|
Conformément aux dispositions de l'article 6, II du même,
|
||||||
conserve les données de nature à permettre
|
l'association Crans conserve les données de nature à permettre
|
||||||
l'identification des auteurs du contenu diffusé.
|
l'identification des auteurs du contenu diffusé.
|
||||||
Ce service est hébergé par {{.Cfg.LegalMentionsEntity}}, au
|
Ce service est hébergé par l'association Crans, au
|
||||||
{{.Cfg.LegalMentionsAddress}}.
|
61 Avenue du Président Wilson, 94235 Cachan Cedex, France.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>En cas de réclamation sur le contenu diffusé</b>,
|
<b>En cas de réclamation sur le contenu diffusé</b>,
|
||||||
la loi vous autorise à contacter directement l'hébergeur à
|
la loi vous autorise à contacter directement l'hébergeur à
|
||||||
l'adresse suivante :
|
l'adresse suivante :
|
||||||
<pre>{{range $i, $element := .Cfg.LegalMentionsFullAddress}}{{$element}}<br/>{{end}}</pre>
|
<pre>Association Cr@ns - ENS Paris-Saclay<br/>Notification de Contenus Illicites<br/>4, avenue des Sciences<br/>91190 Gif-sur-Yvette<br/>France</pre>
|
||||||
Vous pouvez également envoyer directement vos réclamations par
|
Vous pouvez également envoyer directement vos réclamations par
|
||||||
courrier électronique à l'adresse <code>{{.Cfg.LegalMentionsEmail}}</code>.
|
courrier électronique à l'adresse <code>bureau[at]crans.org</code>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
<!-- Links and settings under video -->
|
<!-- Links and settings under video -->
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<!-- <span class="control-quality">
|
<span class="control-quality">
|
||||||
<select id="quality">
|
<select id="quality">
|
||||||
<option value="source">Source</option>
|
<option value="240p">Source</option>
|
||||||
<option value="720p">720p</option>
|
|
||||||
<option value="480p">480p</option>
|
<option value="480p">480p</option>
|
||||||
|
<option value="360p">360p</option>
|
||||||
<option value="240p">240p</option>
|
<option value="240p">240p</option>
|
||||||
</select>
|
</select>
|
||||||
</span> -->
|
</span>
|
||||||
<code class="control-srt-link">srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid={{.Path}}</code>
|
<code class="control-srt-link">srt://{{.Cfg.Hostname}}:{{.Cfg.SRTServerPort}}?streamid={{.Path}}</code>
|
||||||
<span class="control-viewers" id="connected-people">0</span>
|
<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">
|
<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,17 +34,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</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">
|
<script type="module">
|
||||||
{{if .OMECfg.Enabled}}
|
import { initViewerPage } from "/static/js/viewer.js";
|
||||||
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
|
// Some variables that need to be fixed by web page
|
||||||
const viewersCounterRefreshPeriod = Number("{{.Cfg.ViewersCounterRefreshPeriod}}");
|
const viewersCounterRefreshPeriod = Number("{{.Cfg.ViewersCounterRefreshPeriod}}");
|
||||||
@ -54,10 +45,6 @@
|
|||||||
"{{$value}}",
|
"{{$value}}",
|
||||||
{{end}}
|
{{end}}
|
||||||
]
|
]
|
||||||
{{if .OMECfg.Enabled}}
|
initViewerPage(stream, stunServers, viewersCounterRefreshPeriod)
|
||||||
initViewerPage(stream, {{.OMECfg.App}}, viewersCounterRefreshPeriod, {{.Cfg.PlayerPoster}})
|
|
||||||
{{else}}
|
|
||||||
initViewerPage(stream, stunServers, viewersCounterRefreshPeriod)
|
|
||||||
{{end}}
|
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
10
web/web.go
10
web/web.go
@ -2,7 +2,6 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitlab.crans.org/nounous/ghostream/stream/ovenmediaengine"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@ -28,17 +27,11 @@ type Options struct {
|
|||||||
STUNServers []string
|
STUNServers []string
|
||||||
ViewersCounterRefreshPeriod int
|
ViewersCounterRefreshPeriod int
|
||||||
WidgetURL string
|
WidgetURL string
|
||||||
LegalMentionsEntity string
|
|
||||||
LegalMentionsAddress string
|
|
||||||
LegalMentionsFullAddress []string
|
|
||||||
LegalMentionsEmail string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfg *Options
|
cfg *Options
|
||||||
|
|
||||||
omeCfg *ovenmediaengine.Options
|
|
||||||
|
|
||||||
// Preload templates
|
// Preload templates
|
||||||
templates *template.Template
|
templates *template.Template
|
||||||
|
|
||||||
@ -77,10 +70,9 @@ func loadTemplates() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serve HTTP server
|
// Serve HTTP server
|
||||||
func Serve(s *messaging.Streams, c *Options, ome *ovenmediaengine.Options) {
|
func Serve(s *messaging.Streams, c *Options) {
|
||||||
streams = s
|
streams = s
|
||||||
cfg = c
|
cfg = c
|
||||||
omeCfg = ome
|
|
||||||
|
|
||||||
if !cfg.Enabled {
|
if !cfg.Enabled {
|
||||||
// Web server is not enabled, ignore
|
// Web server is not enabled, ignore
|
||||||
|
@ -36,8 +36,7 @@ func websocketHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
err = conn.ReadJSON(c)
|
err = conn.ReadJSON(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to receive client description: %s", err)
|
log.Printf("Failed to receive client description: %s", err)
|
||||||
_ = conn.Close()
|
continue
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get requested stream
|
// Get requested stream
|
||||||
|
Reference in New Issue
Block a user