- Info
- NAT types
- Network
address
translation (wp)
-
|
|
|
description
|
|
example
|
cone
|
use the same port
numbers for internal and external IP tuples
|
full cone
|
allows inbound
connections from any source IP address and any
source port, as long as the destination tuple exists
in a previously created rule.
|
{NAT
internal side} | {NAT
external side} | {Remote machine}
|
|
1. (INT_ADDR, INT_PORT) => [ (EXT_ADDR,
INT_PORT) -> (REM_ADDR, REM_PORT) ]
2. (INT_ADDR, INT_PORT) <= [ (EXT_ADDR,
INT_PORT) <- ( *
, * ) ]
|
port forwarding |
|
|
(address-) restricted
cone
|
|
|
|
|
|
port-restricted cone
|
|
|
|
symmetric |
always use different
numbers for each side of the NAT
|
|
|
|
|
- STUN/TURN (NAT
Types
and NAT Traversal)
-
|
symmetric |
port-restricted
cone
|
address-restricted
cone
|
full cone
|
symmetric
|
TURN
|
TURN |
STUN
|
STUN
|
port-restricted
cone |
TURN |
STUN |
STUN |
STUN |
address-restricted
cone |
STUN |
STUN |
STUN |
STUN |
full cone |
STUN |
STUN |
STUN |
STUN |
- Tools to determine the type of NAT
- Eines i protocols / Tools and protocols
-
|
|
RFC
|
port
|
programari / software
|
description
|
|
|
|
UDP
|
TCP
|
(D)TLS
|
|
|
ICE
|
Interactive
Connectivity Establishment
|
5245
|
|
|
|
|
"ICE protocol
provides a structured mechanism to determine the
optimal communication path between two peers." (wp)
"Coordinates the optimal method of establishing
connectivity between two peers using STUN and TURN"
(en)
|
STUN |
Session Traversal
Utilities for NAT |
3489
(CLASSIC-STUN)
5389
(STUN)
|
3478
|
5349 |
|
"The client,
typically operating inside a private network, sends
a binding request to a STUN server on the public
Internet. The STUN server responds with a success
response that contains the IP address and port
number of the client, as observed from the server's
perspective." (wp) |
TURN |
Traversal Using Relay
NAT |
5766
6156
7065
|
|
|
|
|
"TURN places a
third-party server to relay messages between two
clients when direct media traffic between peers is
not allowed by a firewall." (wp)
"Allows an application or client to obtain IP
addresses and ports from an external relay server in
order to communicate with a peer" (en)
|
- ICE
- ICE
(wp)
- RFC 5245
- Candidate transport
addresses
-
|
|
|
host address
|
host
|
directly
attached to a network interface
|
server
reflexive address
|
srflx |
public side
of a NAT (STUN)
|
relay
|
relay
|
allocated
from a TURN server
|
- server reflexive (srflx)
- peer reflexive (prflx)
- relay
- 2.2
Connectivity
checks
- Sort the candidate pairs in priority order.
- Send checks on each candidate pair in priority
order.
- Acknowledge checks received from the other agent.
- ICE selects a valid pair of candidates
- 4.
Sending the Initial Offer
- agent must:
- gather candidates
- prioritize them
- eliminate redundant candidates
- choose default candidates
- formulate and send the SDP offer
- STUN
- STUN
(wp)
- List
of 267 public STUN servers from EmerCoin project.
Tested 2017-08-08
- STUN DTLS
- works with NAT types (Limitations):
- full cone NAT
- restricted cone NAT
- port restricted cone NAT
- does not work with NAT types:
- symmetric (bi-directional) NAT
- Wireshark
- filter: classicstun (CLASSIC-STUN) (RFC
3489)
- stunc ... -b
- -> Binding Request
- <- Binding Response
- from bash
- How can I get my external IP address in a shell
script?
- clients
- WebRTC
samples
Trickle ICE
- If you test a STUN server, it works if you
can gather a candidate with type "srflx".
- If you test a TURN server, it works if you
can gather a candidate with type "relay".
- stunc (Sofia-SIP)
stunc stun.l.google.com:19302 -b
stunc stun.voip.eutelia.it:3478 -b
- NAT type
stunc stun.voip.eutelia.it:3478
-n
- TLS
- on server, TCP/3478 must be open
- Stuntman
- STUN server and client
- Compilation
- Dependencies
- Mageia
- urpmi install lib64boost-devel
- Steps
- Installation
- Debian /Ubuntu
apt-get install
stuntman-client
- Ús / Usage
./stunclient stun.e-fon.ch 3478
- TURN
- TURN
(wp)
- RFC 8656
(5766)
- extension to STUN
- relays media
- works with:
- symmetric (bi-directional) NAT
- Info
-
TURN
client |
client
host
transport address |
NAT |
client
server-reflexive
transport address |
- TURN commands: ALLOCATION, ...
- TURN message: application data
|
TURN
server
transport address |
TURN
server |
relayed
transport address |
|
peer
transport address |
PEER |
|
|
|
|
- UDP
- TCP
- TLS over TCP
- DTLS over UDP
|
|
|
local candidate:
|
|
remote candidate |
|
Node, address,
traffic, transport.
- Servidor / Server
- config
- /etc/janus/...
turn_server =
turn_port =
...
- TCP
- from client to server:
- from server to peer (relay):
- RFC
6062
?transport=tcp
- this will also force TCP between TURN
client and TURN server
- TCP
and TLS transport support for TURN
- "There is an extension to TURN, RFC 6062, that
defines the "full" TCP support: the data "after"
TURN can be relayed over TCP (not TLS). The
connection between the client and the TURN
server must be TCP or TLS if we want the traffic
to be relayed over TCP."
- What
happens when WebRTC shifts to TURN over TCP
- Autenticació / Authentication
- Ús / Usage
- Janus clients
- Using
TURN server with Janus
- echotest.js
janus = new Janus({
iceServers: [{urls:
"turn:myturnserver:5349", username:
"guest", credential: "somepassword"},
{urls:
"turn:myturnserver:5349?transport=tcp",
username: "guest", credential:
"somepassword"} ],
})
- verify with
about:webrtc
(Firefox):
- force TURN (relay)
- Servidors / Servers
- rfc5766-turn-server
- coTURN
- Instal·lació / Installation
-
|
AWS |
coturn config |
test from client
|
|
security group |
/etc/coturn/turnserver.conf |
protocol |
stunc (sofia) |
turnutils
(coturn) |
TrickleICE |
non secure |
TCP, UDP 3478 |
|
STUN |
stunc myturnserver -b |
turnutils_stunclient myturnserver
|
- STUN or TURN URI: stun:myturnserver
|
user=guest:somepassword |
TURN |
|
turnutils_uclient -v -u guest -w
somepassword -p 3478 myturnserver |
- STUN or TURN URI: turn:myturnserver
- TURN username: guest
- TURN password: somepassword
|
secure |
TCP, UDP 5349 |
#tls-listening-port=5349
cert=/etc/letsencrypt/live/toto.org/fullchain.pem
pkey=/etc/letsencrypt/live/toto.org/privkey.pem |
STUN |
stunc myturnserver:5349 -b |
turnutils_stunclient -p 5349
myturnserver |
- STUN or TURN URI: stun:myturnserver:5349
|
#tls-listening-port=5349
cert=/etc/letsencrypt/live/toto.org/fullchain.pem
pkey=/etc/letsencrypt/live/toto.org/privkey.pem
user=guest:somepassword
|
TURN |
|
turnutils_uclient -v -u guest -w
somepassword -p 5349 myturnserver |
- STUN or TURN URI: turn:myturnserver:5349
- TURN username: guest
- TURN password: somepassword
|
- Config
- Transport between TURN client and TURN server (RFC
8656):
|
client
|
turnserver.conf |
|
|
|
|
turnutils_uclient |
test.webrtc.org |
Firefox |
|
|
|
no-udp
|
no-dtls
|
no-tcp
|
no-tls |
|
|
about:webrtc |
UDP |
turn:myturnserver:3478
|
listening-port=3478 |
-
|
|
|
|
[-p 3478] |
Network: UDP enabled |
|
DTLS over UDP |
turns:myturnserver:5349 |
tls-listening-port=5349 |
|
- |
|
|
-p 5349 |
Network: UDP enabled |
|
TCP |
turn:myturnserver:3478 |
listening-port=3478 |
|
|
-
|
|
[-p 3478] -t |
Network: TCP enabled |
|
TLS over TCP |
turns:myturnserver:5349 |
tls-listening-port=5349 |
|
|
|
-
|
-p 5349 -t |
Network: TCP enabled |
|
Note: on server, use ss -tulpn | grep turn
to check open ports.
Gathered candidates (relayed transport address) can
be UDP, even if transport between TURN client and
TURN server is established over TCP.
- Transport between TURN server and peer (candidate
of type relay) (RFC
6062): (is transport=tcp also needed to
specify tcp transport between TURN client and TURN
server?)
|
client |
turnserver.conf |
|
turnutils_uclient |
Firefox |
|
|
no-udp-relay
(also disables udp, tcp communication
between client/server,
even if, according to ss, ports are
available) |
no-tcp-relay
(not working?) |
|
about:webrtc |
|
|
|
|
|
local candidate |
UDP (RFC 8656) |
turn:myturnserver:...[?transport=udp] |
- |
|
|
x.x.x.x:p/udp (relay-udp) |
DTLS over UDP |
turns:myturnserver:...[?transport=udp] |
|
|
|
x.x.x.x:p/udp (relay-tls) |
TCP (RFC 6062) |
turn:myturnserver:...?transport=tcp |
|
- |
-T (implies -t) |
x.x.x.x:p/udp (relay-tcp) |
TLS over TCP (RFC 6062) |
turns:myturnserver:...?transport=tcp |
|
|
|
x.x.x.x:p/udp (relay-tls) |
- activate TLS/DTLS
- /etc/coturn/turnserver.conf
#tls-listening-port=5349
cert=/etc/letsencrypt/live/toto.org/fullchain.pem
pkey=/etc/letsencrypt/live/toto.org/privkey.pem
- check from server
tail -n 200
/var/log/coturn/turnserver.log
ss -tulnp
- check from remote client
stunc turn.watchity.net:5349 -b
turnutils_stunclient -p 5349
myturnserver
- TrickleICE
- STUN or TURN URI:
stun:myturnserver:5349
- Gather candidates:
- srflx udp
<my_external_ip_address>
- non default ports (e.g. 443 for TLS)
- coturn.service
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE
...
- After
update of coturn can not connect to 443 #421
- without
AmbientCapabilities , you
will see, in /var/log/coturn/turnserver.log
ERROR: Fatal final failure: cannot
bind DTLS/UDP listener socket to addr
127.0.0.1:443
- add a user/password
- option 1 (these credentials will not work with
-u and -w options in turnutils_uclient):
- /etc/coturn/turnserver.conf
- option 2:
sudo turnadmin -a -r mycompany.org
-u convidat -p contrasenya
- check from remote client
turnutils_uclient -v -p 5349 -u
convidat -w contrasenya myturnserver
- TrickleICE
- STUN or TURN URI:
turns:myturnserver:5349
- TURN username: guest
- TURN password: somepassword
- Gather candidates:
- relay udp
<turn_server_ip_address>
- ...
- dynamic credentials:
- /etc/coturn/turnserver.conf
#lt-cred-mech
use-auth-secret
static-auth-secret=north
- somewhere (e.g. a completely different
server), generate temporary credentials,
calculated using the shared secret:
- usercombo -> "timestamp_when_password_will_expire:userid"
turn_user -> usercombo
turn_password -> base64(hmac_sha1(secret,
usercombo))
- put them when accessing the coturn server
(e.g. from Janus echotest.js):
iceServers: [{urls:
"turn:myturnserver:5349", username:
turn_user, credential: turn_password},
- test with TrickleICE:
- STUN or TURN URI: turns:myturnserver:5349
- TURN username:
turn_user
- TURN password:
turn_password
- Gather candidates (you can also filter by
"relay" only):
- relay udp
<turn_server_ip_address>
- logs
# sudo mkdir -p /var/log/coturn
# sudo chown turnserver.turnserver
/var/log/coturn
log-file=/var/log/coturn/turnserver.log
#syslog
# Enable full ISO-8601 timestamp in all logs
new-log-timestamp
- AWS EC2
- Servidor / Server
sudo systemctl enable coturn.service
sudo systemctl start coturn.service
sudo systemctl status coturn.service
- Debug and logs
- /var/log/coturn/turnserver.log
- Utils and test (turnutils)
- Instal·lació / Installation
- from source
- CentOS
sudo dnf install coturn-utils
- Ús / Usage
- coturn/examples/scripts
- turnadmin
- man turnutils
- turnutils_natdiscovery
- turnutils_oauth
- turnutils_peer
- turnutils_stunclient
turnutils_stunclient myturnserver
- turnutils_uclient
- Debug
- Demos
- AppRTC (see logs,
about:webrtc)
|
- Info
- Dependències
/ Dependencies
- Ubuntu
- CentOS
yum install epel-release git gcc
yum install libmicrohttpd-devel
jansson-devel openssl-devel libsrtp-devel glib-devel
opus-devel libogg-devel libsoup-devel pkgconfig
gengetopt libtool autoconf automake libnice-devel
yum install doxygen graphviz
- Mageia
- Mageia 6
urpmi libmicrohttpd-devel
gnutls-devel gengetopt
jansson-devel lib64lus-devel
- Mageia 5
urpmi libmicrohttpd-devel
gnutls-devel gengetopt
- Jansson
- to compile Janus >=v0.2.5 do not use
jansson-devel package version 2.4, as it has an
error in /usr/include/jansson.h
- Compilation:
wget
http://www.digip.org/jansson/releases/jansson-2.10.tar.gz
tar xvzf jansson-2.10.tar.gz
cd jansson-2.10
./configure
make
make check
sudo make install
sudo ldconfig
- libmicrohttpd
- version >=0.9.59 is needed by Janus >=0.9.2
- IMPORTANT: check that no system libmicrohttpd-devel is
installed
- from tar file:
wget
https://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.71.tar.gz
tar xvf libmicrohttpd-0.9.71.tar.gz
cd libmicrohttpd-0.9.71
./configure
make
sudo
make install
- Nice
- WARNING: package provided by CentOS EPEL repositories
(libnice-0.1.3-4.el7.x86_64) is too old and will give problems
- Dependències / Dependencies
- CentOS
sudo yum install glib2-devel
- from tar file:
- from git:
- GitLab:
git clone
https://gitlab.freedesktop.org/libnice/libnice.git
- GitHub:
git clone
https://github.com/libnice/libnice.git
cd libnice
- compilació / compilation
./configure
make
sudo make install
- Sofia-SIP
- Files
wget
http://downloads.sourceforge.net/project/sofia-sip/sofia-sip/1.12.11/sofia-sip-1.12.11.tar.gz
tar xvzf sofia-sip-1.12.11.tar.gz
cd sofia-sip-1.12.11
./configure
make
sudo make install # will install it in /usr/local/lib
sudo ldconfig
- libsrtp
- libusrsctp (needed when compiling
Janus with
--enable-data-channels )
- Dependències / Dependencies
git clone https://github.com/sctplab/usrsctp
cd usrsctp
./bootstrap
./configure
make
sudo make install
sudo ldconfig
- libwebsockets (needed when
compiling Janus with no
--disable-websockets )
- Dependències / Dependencies
- CentOS
sudo yum install cmake openssl-devel
git clone
https://libwebsockets.org/repo/libwebsockets
cd libwebsockets
# If you want the stable version of libwebsockets,
uncomment the next line
# git checkout v2.4-stable
mkdir build
cd build
# See
https://github.com/meetecho/janus-gateway/issues/732
re: LWS_MAX_SMP
cmake -DLWS_MAX_SMP=1 -DCMAKE_INSTALL_PREFIX:PATH=/usr
-DCMAKE_C_FLAGS="-fpic" ..
make && sudo make install
- libconfig (needed to have bash tool ls-config)
- Dependències / Dependencies
- CentOS
sudo yum install texinfo flex gcc-c++
bison
...
autoreconf
./configure
make
sudo make install
- Janus compilation
# activate /usr/local/lib (nice,
sofia-sip) for
pkg-config
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/
git clone https://github.com/meetecho/janus-gateway
cd janus-gateway
git checkout v0.9.1
sh autogen.sh
./configure --disable-websockets --disable-data-channels
--disable-rabbitmq --prefix=/usr --sysconfdir=/etc
--localstatedir=/var
make
sudo make install
- websockets
- install libwebsockets
./configure --disable-data-channels
--disable-rabbitmq --prefix=/usr --sysconfdir=/etc
--localstatedir=/var
- WebSockets
API
- install config files in /etc/janus
- Eclipse
- New / C Project: GNU Autotools / Empty project
- Forks
- Problemes /
Problems
- when compiling with --enable-post-processing
postprocessing/pp-h264.c: In function
‘janus_pp_h264_create’:
postprocessing/pp-h264.c:99:29: error: assignment of
member ‘video_codec’ in read-only object
fctx->oformat->video_codec =
codec->id;
^
- Solució / Solution
- check for different installed versions of
ffmpeg (e.g. from ffmpeg compilation and
gstreamer compilation)
- compare:
- ffmpeg -version
- grep "define LIBAVCODEC_VERSION_"
/usr/local/include/libavcodec/version.h
- reinstall ffmpeg after gstreamer
compilation
- /usr/include/jansson.h:53:3: error: conflicting types
for 'json_t'
- Mageia: jansson-devel-2.4-4.1.mga5
- Solució / Solution
- /usr/include/jansson.h
typedef struct json_t
{
json_type type;
size_t refcount;
} json_t;
- Service
- Janus
as a daemon/service
- janus.service
(/usr/lib/systemd/system/janus.service)
[Unit]
Description=Janus WebRTC Gateway
Wants=network.target
After=syslog.target network.target remote-fs.target
nss-lookup.target cloud-init.service
[Service]
Type=simple
ExecStart=/usr/local/bin/janus
#User=janus
#Group=janus
Restart=on-failure
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
- manage service
sudo systemctl status janus.service
sudo systemctl start janus.service
sudo systemctl stop janus.service
- Configuració / Setup
- /etc/janus/janus.jcfg
- debug
general: {
log_to_file =
"/var/log/janus/janus.log"
# Whether to use a log file or not
debug_level = 5
# Debug/logging level, valid values are 0-7
...
}
- /etc/janus/janus.cfg
- debug
[general]
log_to_file = /var/log/janus/janus.log
debug_level =
7
;
Debug/logging level, valid values are 0-7
debug_timestamps = yes
[nat]
# maybe nice debug messages are only available
when janus is called from command line, with:
# export NICE_DEBUG=all
# export G_MESSAGES_DEBUG=all
# https://nice.freedesktop.org/libnice/libnice-Debug-messages.html
nice_debug = true
- nat
(these values are for Janus itself. For final clients,
modify the variable iceServers in javascript)
[nat]
stun_server = stun.voip.eutelia.it
stun_port = 3478
turn_server =
turn_port =
turn_type =
turn_user =
turn_pwd =
nice_debug
= true
- DTLS
; Certificate and key to
use for DTLS.
[certificates]
cert_pem = /usr/share/janus/certs/mycert.pem
cert_key = /usr/share/janus/certs/mycert.key
[media]
;dtls_mtu = 1200
- admin
[general]
admin_secret = mysecret
- NACK
[media]
max_nack_queue = 500
- /etc/janus/janus.transport.http.cfg
- CORS
[cors]
allow_origin =
https://<your_demos_server>:<your_demos_port>
# e.g. https://192.168.1.100:8080
- enable HTTPS
on port 8089
[general]
https = yes
; Whether to enable HTTPS
(default=no)
secure_port = 8089
; Web server
HTTPS port, if enabled
; Certificate and key to use for HTTPS.
[certificates]
cert_pem = /usr/share/janus/certs/mycert.pem
cert_key = /usr/share/janus/certs/mycert.key
- Let's
Encrypt certificates
; Certificate and key
to use for HTTPS.
[certificates]
cert_pem =
/etc/letsencrypt/live/www.example.org/fullchain.pem
cert_key = / etc/letsencrypt/live/www.example.org/privkey.pem
- Problemes / Problems
/var/log/janus/janus.log
Couldn't start secure webserver
on port 8089...
- Manually start janus as user janus,
with high debug level
sudo -i
systemctl stop janus.service
su janus -s /bin/bash -c
"/bin/janus -d 7"
- Check that your user has access to
certificate file:
su janus -s /bin/bash -c "ls
-l /etc/letsencrypt/live/www.example.org/fullchain.pem"
- Solució / Solution
- check that the user that is
running the service has permission
to access dirs:
/etc/letsencrypt/live,
/etc/letsencrypt/archive
- Non-root
access to Letsencrypt certificates
- install latest versions of:
sofia_sip, libnice, libsrtp,
libmicrohttpd (I don't know which of
them solves the problem)
- admin
[admin]
admin_http = yes
admin_port = 7088
admin_https = yes
admin_secure_port = 7889
- Amazon AWS
EC2
- Janus >=v0.4.4
- echo test demo needs a libnice version >0.1.14
(e.g. 0.1.16 master
28th June 2018 from git)
- you need to specify NAT-1-1 with the public IP address
(STUN server for Janus is not needed):
- open ports in security group
- TCP: 8088-8089
- UDP: ... (same ports as defined in
[media]
rtp_port_range , to avoid problems with
mobile networks)
- demos
- admin
- Debug
- Configuració
/
Setup
- logs
tail -n 200 -f /var/log/janus/janus.log
- admin
- Understanding
the
Janus admin API
- Admin/Monitor
API
- Configuració
/ Setup
- open port 7088 in firewall (and/or AWS security group)
- /etc/janus/janus.cfg
[general]
admin_secret = mysecret
- /etc/janus/janus.transport.http.cfg
[admin]
admin_http = yes
admin_port = 7088
systemctl restart janus.service
- Ús / Usage
curl -X POST -H 'Content-Type:
application/json' --data-binary
'{"janus":"list_sessions","transaction":"1234","admin_secret":"mysecret"}'
http://<janus_server_ip_address>:7088/admin
curl -X POST -H 'Content-Type:
application/json' --data-binary
'{"janus":"list_handles","transaction":"1234","admin_secret":"mysecret"}'
http://<janus_server_ip_address>:7088/admin/<session>
curl -X POST -H 'Content-Type:
application/json' --data-binary
'{"janus":"handle_info","transaction":"1234","admin_secret":"mysecret"}'
http://<janus_server_ip_address>:7088/admin/<session>/<handle>
- Resposta / Response
-
Header
|
info
|
|
|
|
|
Plugin
specific
|
|
plugin_specific
|
...
|
|
|
Handle flags
|
|
flags
|
|
|
|
SDPs
|
|
sdps
|
|
|
|
PeerConnection
|
|
streams
|
Header
|
|
|
|
|
|
SSRC
|
ssrc
|
|
|
|
|
ICE
components
|
components
|
state: connecting, ready,
disconnected, failed
local-candidates
remote-candidates
selected-pair
dtls
in-stats
out-stats
|
- Troubleshooting
|
processing
|
|
|
|
|
succeeded
|
|
|
|
|
|
server
|
|
client
|
|
|
server
|
|
client
|
|
|
|
admin
|
tshark
|
wireshark
|
chrome://webrtc-internals |
about:webrtc
|
admin
|
tshark
|
wireshark
|
chrome://webrtc-internals |
about:webrtc |
ICE
|
streams/components/
|
|
filter: stun
|
iceconnectionstatechange:
checking
|
|
streams/components/
- state: ready
- connected: xxxxx
|
|
|
|
|
DTLS
|
streams/components/dtls/
- dtls-state: created
- valid: false
- ready: false
|
|
filter: dtls
|
|
|
streams/components/dtls/
- dtls-state: connected
- valid: true
- ready: true
|
|
|
|
|
- tshark
- server
- get destination port:
- client: from Firefox about:webrtc "Remote
candidate"
- server /var/log/janus/janus.log: a=candidate
tshark -d
udp.port==<destination_port>,rtp udp port
<destination_port>
- 8857
14.757747410 192.168.0.101 -> 192.168.0.108
RTP 153 PT=DynamicRTP-Type-109,
SSRC=0xDE951CF3, Seq=61918, Time=1643530196
8862
14.779327125 192.168.0.101 -> 192.168.0.108
RTP 1126 PT=DynamicRTP-Type-120,
SSRC=0xC0C0D699, Seq=31111, Time=617518567
...
- ...
- Janus plugins
- For old custom plugins, use v0.1.x (instead of v0.2.x)
- v0.1.x
- v0.2.x
include janus/plugins/plugin.h
- Migration from v0.1.x to v0.2.x:
- 3rd party plugins
- Included plugins
- streaming
- janus.plugin.streaming.c
(github)
- Streaming API
- CURL
examples
-
|
request
|
js
|
request
json
|
response
json
|
event
|
janus_...c
|
synchronous
(immediate response)
|
- list
- create
- destroy
- recording
- enable
- disable
|
|
{
"transaction":
"21bGZvoTdbG8Ab6X",
"janus": "message",
"body":
{"request": "list"}
{"request": "create",
"type": "rtp",
"description": "First test",
"audio_port": 8006,
"video_port": 8004
...}
}
|
{
"janus": "success",
"session_id":
8566092198416479,
"sender": 6220156789031370,
"transaction":
"21bGZvoTdbG8Ab6X",
"plugindata": {
"plugin":
"janus.plugin.streaming",
"data":
{"streaming": "list",
"list": [
{
"id": 1,
"description": "Opus/VP8 live
stream coming from gstreamer",
"type": "live",
"audio_age_ms": 66673351,
"video_age_ms": 66673351
},
...
]
}
{"streaming": "created",
"created": "3346122389194671",
"permanent": false,
"stream": {
"id": 3346122389194671,
"description": "First test",
"type": "live",
"is_private": false,
"audio_port": 8006,
"video_port": 8004
}
}
- ...
} |
-
|
|
asynchronous
(response in an event)
|
- watch
- start
- pause
- switch
- stop
|
startStream
|
{
"transaction":
"21bGZvoTdbG8Ab6X",
"janus": "message",
"body":
{"request":
"watch",
"id":
...}
|
|
- preparing
- starting
- started
- stopped
|
|
onmessage:
function(msg, jsep) {
if(jsep !== undefined
&& jsep !== null) {
streaming.createAnswer (...)
},
|
|
{
"janus": "event",
"session_id":
8566092198416479,
"sender": 6220156789031370,
"transaction":
"21bGZvoTdbG8Ab6X",
"plugindata": {
"plugin":
"janus.plugin.streaming",
"data": {
"streaming": "event",
"result": {"status": "preparing"}
}
"jsep": {
"type": "offer",
"sdp": "..."
} }
|
|
{
"transaction":
"21bGZvoTdbG8Ab6X",
"janus": "message",
"body":
{"request": "start"}
"jsep": {"type": "answer",
"sdp": "..."}
|
|
|
onmessage:
function(msg, jsep) {
var result = msg["result"];
if(result != undefined
&& result != null) {... }
},
|
|
{
"janus": "event",...
"plugindata": {
"plugin":
"janus.plugin.streaming",
"data": {
"streaming": "event",
"result":
{"status": "starting"} |
|
|
{
"janus": "event",...
"plugindata": {
"plugin":
"janus.plugin.streaming",
"data": {
"streaming": "event",
"result":
{"status": "started"} |
|
onremotestream:
function(stream)
{
$('#videoremote').append('<video
id="remotevideo" width=320
height=240 autoplay/>');
Janus.attachMediaStream($('#remotevideo').get(0),
stream);
},
|
|
|
|
- config
- /etc/janus/janus.plugin.streaming.cfg
-
|
config file |
config
file
|
streamer
|
browser
notes
|
|
/etc/janus/
janus.plugin.streaming.jcfg |
/etc/janus/janus.plugin.streaming.cfg
(OBSOLETE) |
|
Chrome
|
Firefox
|
VP8 / Opus
|
|
[gstreamer-sample]
type = rtp
id = 1
description = Opus/VP8 live stream
coming from gstreamer
audio = yes
video = yes
audioport = 5002
audiopt = 111
audiortpmap = opus/48000/2
videoport = 5004
videopt = 100
videortpmap = VP8/90000
secret = adminpwd |
|
|
|
|
[opus-vp8-multicast]
type = rtp
id = 20
description = Opus/VP8 live multicast
stream
audio = yes
video = yes
audioport = 5002
audiomcast = 232.3.4.5
audiopt = 111
audiortpmap = opus/48000/2
videoport = 5004
videomcast = 232.3.4.5
videopt = 100
videortpmap = VP8/90000
|
- streaming
RTP VP8/Opus with Gstreamer
- streaming
RTP VP8/Opus with ffmpeg
ffmpeg -re -f lavfi -i
"testsrc=size=320x180:rate=24"
-f lavfi -i
"sine=frequency=440:sample_rate=48000,
aformat=channel_layouts=stereo"
-c:v vp8 -b:v 700k -an -f rtp
-payload_type 100
rtp://232.3.4.5:5004 ?pkt_size=1400
-c:a opus -strict -2 -b:a 64k
-vn -f rtp -payload_type 111
rtp://232.3.4.5:5002
?pkt_size=1400
-sdp_file
/tmp/vp8_opus_multicast.sdp
|
ok
|
ok
|
H.264
(unicast) |
h264-sample: {
type = "rtp"
id = 10
description = "H.264 live stream
coming from gstreamer"
audio = false
video = true
videoport = 8004
videopt = 126
videocodec = "h264"
videofmtp = "profile-level-id=42e01f;packetization-mode=1"
#secret = "adminpwd"
}
|
|
ffmpeg -re -f lavfi -i
"testsrc=size=320x180:rate=24"
-c:v libx264 -b:v 700k -pix_fmt
yuv420p -profile:v
baseline -level:v 31
-an -f rtp -payload_type 126
rtp://127.0.0.1:8004 -sdp_file
/tmp/h264.sdp
- gst-launch-1.0
-v videotestsrc ! videoconvert !
x264enc ! video/x-h264,profile=baseline
! rtph264pay config-interval=10
pt=126 ! udpsink host=127.0.0.1
port=8004
- gst-launch-1.0
-v videotestsrc !
video/x-raw,width=1280,height=720
! videoconvert ! x264enc !
video/x-h264,profile=baseline
! rtph264pay config-interval=10
pt=126 ! udpsink host=127.0.0.1
port=8004
|
janus.plugin.streaming.jcfg
# All browsers also support H.264,
often through Cisco's OpenH264 plugin.
# The only profile that is
definitely supported is the baseline
one, which
# means that if you try a higher one
it might or might not work. No matter
# which profile you encode, though,
you can put a custom one in the SDP if
# you override the fmtp SDP attribute
via 'videofmtp'. The following is an
# example of how to create a simple
H.264 mountpoint: you can feed it via
# an x264enc+rtph264pay pipeline in
gstreamer, an ffmpeg script or other.
#
h264-sample: {...
profile-level-id |
ffmpeg |
Chrome |
Firefox |
42e01f |
-profile:v baseline
-level:v 31 |
ok |
ok |
4d0033 |
-profile:v main -bf 0
-level:v 51 |
ok |
not ok |
4de033
|
-profile:v main -bf 0
-level:v 51 |
ok
|
ok
|
IMPORTANT: in order to
have it working in Firefox,
second byte cannot be 00 (not
constrained); it must be e0
(constrained) |
H.264
(multicast)
|
|
[h264-multicast]
type = rtp
id = 10
description = H.264 live multicast
stream
audio = no
video = yes
videoport = 8004
videomcast = 232.6.7.8
videopt = 126
videortpmap = H264/90000
videofmtp =
profile-level-id=42e01f\;packetization-mode=1
|
ffmpeg -re -f lavfi -i
"testsrc=size=320x180:rate=24"
-c:v libx264 -b:v 700k -pix_fmt
yuv420p -profile:v baseline
-level:v 31 -an -f rtp
-payload_type 126
rtp://232.6.7.8:8004 -sdp_file
/tmp/h264.sdp
ffmpeg -re -i sintel.mp4 -an
-c copy -bsf:v h264_mp4toannexb -f
rtp -payload_type 126
rtp://232.6.7.8:8004 ?pkt_size=1400
-sdp_file /tmp/sintel.sdp
|
ok
|
if
profile.level-id is not specified,
Firefox interprets offered sdp as: a=fmtp:126
profile-level-id=420010;level-asymmetry-allowed=0;packetization-mode=1
and Firefox response only contains VP8:
a=rtpmap:120 VP8/90000
It does not work.
|
H.264 / Opus |
|
[h264-opus-multicast]
type = rtp
id = 4246246658112756
description = h264_opus_multicast
audio = yes
audioport = 8002
audiomcast = 232.3.4.5
audiopt = 111
audiortpmap = opus/48000/2
audiofmtp = sprop-stereo=1
video = yes
videoport = 8004
videomcast = 232.6.7.8
videopt = 126
videortpmap = H264/90000
videofmtp =
packetization-mode=1\;profile-level-id=42e01f
data = no |
ffmpeg -re -f lavfi -i
"testsrc=size=320x180:rate=24" -f
lavfi -i
"sine=frequency=440:sample_rate=48000,
aformat=channel_layouts=stereo"
-c:v libx264 -b:v 700k
-pix_fmt yuv420p -profile:v
baseline -level:v 31 -an -f rtp
-payload_type 126
rtp://232.6.7.8:8004
?pkt_size=1400
-c:a opus -strict
-2 -b:a 64k -vn -f rtp
-payload_type 111
rtp://232.3.4.5:8002
?pkt_size=1400
-sdp_file
/tmp/h264_opus_multicast.sdp
|
|
|
- Problemes / Problems
- videoroom
- janus.plugin.videoroom.c (github)
- Video Room API (VideoRoom
plugin documentation)
-
|
request
|
event
|
synchronous
|
- create
- destroy
- edit
- exists
- list
- allowed
- kick
- listparticipants
|
-
|
asynchronous
|
- join
- joinandconfigure
- configure
- publish
- unpublish
- start
- pause
- switch
- stop
- add
- remove
- leave
|
|
- Problemes
/
Problems
- plain RTP
- DTLS
- janus.cfg
; Certificate and key to use
for DTLS.
[certificates]
cert_pem = /usr/share/janus/certs/mycert.pem
cert_key = /usr/share/janus/certs/mycert.key
[media]
;dtls_mtu = 1200
- Problems
with DTLS
- Problemes / Problems
- Compilació
/
Compilation
- AWS EC2
- Plugins
- Accés des de client / Access from client (Javascript
console)
- Javascript console
hangup_media (plugin.h)
not called in Firefox
- solucionat a
Firefox 55 / solved in Firefox 55
- meetecho-janus Google groups: hangup,
destroy_session, DTLS-SRTP
WebRTC error...
{"name":"InternalError","message":"Starting video
failed"} (Firefox)
WebRTC error...
{"name":"NotReadableError","message":"Could not
start video source"} (Chrome)
- Solució / Solution
- No podeu fer servir alhora la mateixa
webcam des de dos navegadors diferents / You
cannot use the same webcam from two browsers
(Firefox/Chrome) at the same time
- iOS
- Demos
- Echo test not working (always spinning)
- Browser debug logs
[...:WARNING:nack_module.cc(237)] Sequence
number xxxx removed from NACK
list due to max retries.
- Missatges al servidor / Messages on server
(/var/log/janus/janus.log)
[ERR] [dtls.c:janus_dtls_srtp_incoming_msg:923]
... Oops, error creating inbound SRTP session for
component 1 in stream 1??
[ERR] [dtls.c:janus_dtls_srtp_incoming_msg:924]
... -- 1 (srtp_err_status_fail)
Not video and not audio? dropping (SSRC...)
- (debug_level = 5)
Retransmitted ... packets due to NACK (video stream #0)
- when using streaming plugin
- Issue
with
streaming plugins: tons of video NACK
- everytime at the same point in video (video
freezes for a short time), you get some NACK
retransmissions
- Solution: reduce the size of rtp packet of
the source that feeds the mountpoint. E.g.
if using ffmpeg:
ffmpeg ... -f rtp
rtp://230.15.129.230:10956?pkt_size=1400
Waiting for candidates-done callback...
- Only visible when:
- Debug
nat
libnice-stun-DEBUG: STUN transaction
retransmitted (timeout 397ms).
libnice-stun-DEBUG: STUN transaction
retransmitted (timeout 793ms).
libnice-stun-DEBUG: STUN transaction
retransmitted (timeout 3194ms).
libnice-stun-DEBUG: STUN transaction
retransmitted (timeout 6389ms).
libnice-stun-DEBUG: STUN transaction
retransmitted (timeout 12784ms).
libnice-DEBUG: Agent 0x7fe068009050 : bind
discovery timed out, aborting discovery item.
- Solucions / Solutions
- do NOT use the following settings:
- janus.cfg
[nat]
stun_port = 19302
stun_server = stun3.l.google.com
- if you have more than one network device
- If you have a network device with no
internet connection (e.g. mcd in aws),
add it to the black list:
- janus.cfg
ice_ignore_list
= vmnet,172.16.
[ERR]
[ice.c:janus_ice_cb_component_state_changed:1383]
[3053596813] ICE failed for component 1 in stream 1...
[ERR] [ice.c:janus_ice_check_failed:1548]
[5472388112306727] ICE failed for component 1 in
stream 1...
[WARN] [janus.transport.http]: Error in GNU
libmicrohttpd daemon.c:1826: close failed or
[Wed May 16 10:32:21 2018] [WARN]
[janus.transport.http]: Error in GNU libmicrohttpd
daemon.c:3092: Close socket failed.
[Wed May 16 10:32:21 2018] [WARN]
[janus.transport.http]: Error in GNU libmicrohttpd
daemon.c:3028: Failed to lock mutex.
[Wed May 16 10:32:21 2018] [WARN]
[janus.transport.http]: Error in GNU libmicrohttpd
daemon.c:3101: Failed to unlock mutex.
[Wed May 16 10:32:21 2018] [WARN]
[janus.transport.http]: Error in GNU libmicrohttpd
daemon.c:3081: Failed to remove FD from epoll set
[WARN]
nice_agent_set_port_range unavailable, port range
disabled
Transport plugins folder:
/usr/lib/janus/transports
Loading transport plugin 'libjanus_pfunix.so'...
[WARN] Unix Sockets server disabled (Janus API)
[WARN] Unix Sockets server disabled (Admin API)
[WARN] No Unix Sockets server started, giving up...
[WARN] The 'janus.transport.pfunix' plugin could not
be initialized
[FATAL] [janus.c:main:4167] No Janus API transport is
available... enable at least one and restart Janus
- Solució / Solution
- install package libmicrohttpd
routines:dtls1_read_bytes:tlsv1 alert protocol
version
- Accés des de client / Access
from client
- Info
- API
- with curl
-
|
|
curl |
using janus.js
(either from html or an
intermediate js) |
general
values
|
|
janus_url_base=http://192.168.1.130:8088
janus_url_base=https://192.168.1.130:8089
janus_url=${janus_url_base}/janus
cookies_options='-b galetes.txt -c
galetes.txt'
touch galetes.txt
origin_url= https://192.168.1.130:8080
origin_header="-H 'Origin: ${origin_url}'"
headers=${origin_header}
cookies_options='-b galetes.txt -c
galetes.txt'
# accept self-signed certificates
cert_options='-k' transaction=1234567890
|
var server =
"http://192.168.1.128:8088/janus";
Janus.init({...})
|
get info
|
|
curl -i
-X GET ${cert_options}
${janus_url}/info |
|
check CORS
|
|
curl -i
-X OPTIONS ${cert_options}
${cookies_options} "$headers"
"${janus_url}"
|
|
get a new
session
|
|
response=$(curl
-X
POST ${cert_options} ${cookies_options}
"$headers" -H 'Content-Type:
application/json' --data "{\"janus\":\"create\",\"transaction\":\"${transaction}\"}"
"${janus_url}"
)
cat galetes.txt
session_id=$(echo "$response" | jq
'.data.id')
echo "session_id: $session_id"
session _url="${janus_url_base}/janus/${session_id}"
|
var janus = new Janus({...}) |
attach to a plugin
(e.g.: janus.plugin.streaming)
|
|
plugin_name="janus.plugin.streaming"
response=$(curl -X POST ${cert_options}
${cookies_options} "$headers" -H
'Content-Type: application/json' --data
"{\"janus\":\"attach\",\"plugin\":\"${plugin_name}\",\"transaction\":\"${transaction}\"}"
"${session_url}"
)
plugin_session_id=$(echo "$response" | jq
'.data.id')
plugin_url="${session_url}/${plugin_session_id}"
|
janus.attach({...}) |
send
a message to the plugin
(e.g.: janus.plugin.streaming)
|
e.g. list
of existing mountpoints (available streams)
in janus.plugin.streaming |
message_body="{\"request\":\"list\"}"
|
|
create new
mountpoint in janus.plugin.streaming |
[id=...]
admin_key=... # defined in
janus.plugin.streaming.cfg: [general]
admin_key
message_body="{\"request\":\"create\",\" type\":\"rtp\",\"id\":99,\" description\":\"stream_ description\",\"audio\":true,\" video\":true,\"audioport\":1111,\" audiopt\":111,\"audiortpmap\":\" opus/48000/2\",\"videoport\": 2222,\"videopt\":100,\" videortpmap\":\"VP8/90000\",\" permanent\":false,\"admin_key\":\" ${admin_key}\"}"
|
|
destroy
mountpoint in janus.plugin.streaming |
id=99
admin_secret="adminpwd"
message_body="{\"request\":\"destroy\",\"id\":${id},\"secret\":\"${admin_secret}\"}"
|
|
(send
message_body)
|
curl -i
-k -X POST -H 'Content-Type:
application/json' --data-binary
"{\"janus\":\"message\",
\"transaction\":\"${transaction}\", \"body\":${message_body}
}" "${plugin_url}" |
pluginHandle.send({...}) |
keep session
active.
If not called, the session will expire after
value specified in janus.cfg session_timeout
(default: 60 seconds)
|
|
rid=$(date
+%s%03N)
get_url="${session_url}?rid=${rid}&maxev=1"
echo "url: $url"
curl -v -i -X GET ${cert_options}
${cookies_options} "$headers" "${get_url}"
cat galetes.txt |
|
close session
|
|
curl -i
-X POST ${cert_options} ${cookies_options}
"$headers" -H 'Content-Type:
application/json' --data "{\"janus\":\"destroy\",\"transaction\":\"${transaction}\"}"
"${session_url}"
cat galetes.txt
|
janus.destroy({...}) |
- with
Javascript
- janus.js
-
Janus.init()
debug: ...
dependencies:
Janus.useDefaultDependencies
callback:
function(){...}
|
var
janus = new Janus({...})
server: ...
iceServers: ...
ipv6: ...
withCredentials: ...
max_poll_events: ...
destroyOnUnload: ...
token: ...
apisecret: ...
success:
function() {...}
error:
function(cause) {...}
destroyed:
function() {...}
janus.getServer()
janus.isConnected()
janus.getSessionId()
janus.attach({...})
plugin: ...
opaqueId: ...
success:
function(pluginHandle) {...}
pluginHandle.getId()
pluginHandle.getPlugin()
pluginHandle.send({...})
pluginHandle.createOffer(callbacks)
media: {...}
audioSend:
true/false
audioRecv:
true/false
audio:
true/false
audio: {...}
videoSend:
true/false
videoRecv:
true/false
video:
true/false
video:
"lowres"/"lowres-16:9"/"stdres"/"stdres-16:9"/"hires"/"hires-16:9"
//
320x240/320x180/640x480/640x360/1280x720
video:
"screen"
video: {...}
deviceId:
...
//
Janus.listDevices(callback)
width:
...
height:
...
data:
true/false
failIfNoAudio:
true/false
failIfNoVideo:
true/false
screenshareFrameRate:
...
trickle:
true/false
stream: ...
success:
function(jsep) {...}
// got sdp
error:
function() {...}
pluginHandle.createAnswer(callbacks)
- (same as CreateOffer)
jsep: ...
pluginHandle.handleRemoteJsep(callbacks)
pluginHandle.dtmf(parameters)
pluginHandle.data(parameters)
pluginHandle.getBitrate()
pluginHandle.hangup(sendRequest)
pluginHandle.detach(parameters)
error:
function(cause) {...}
consentDialog:
function(on) {...}
webrtcState:
function() {...}
iceState:
function() {...}
mediaState:
function() {...}
slowLink:
function() {...}
onmessage:
function(msg, jsep) {...}
onlocalstream:
function(stream) {...}
onremotestream:
function(stream) {...}
ondataopen:
function() {...}
ondata:
function() {...}
oncleanup:
function() {...}
detached:
function() {...}
janus.destroy({...})
|
Janus.debug(...)
|
- Exemples / Examples
- basic session creation (all inside the
document ready function)
<!doctype
html>
<head>
<meta charset="utf-8"/>
<script
type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.0.0/adapter.min.js"></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script
type="text/javascript"
src="janus.js"></script>
<script
type="text/javascript">
var server =
"http://192.168.1.128:8088/janus";
$(document).ready(function() {
Janus.init({
debug: true,
dependencies:
Janus.useDefaultDependencies(), // or:
Janus.useOldDependencies() to get the
behaviour of previous Janus versions
callback: function() {
//
init completed
//
create gateway session
var
janus = new Janus(
{
server:
server,
withCredentials:
true,
success:
function() {
//
gateway session created
Janus.debug("----
is connected: " + janus.isConnected()
);
Janus.debug("----
session id: " + janus.getSessionId()
);
},
error:
function(cause) {
//
error when creating gateway session
},
destroyed:
function() {
//
gateway session destroyed
}
});
Janus.debug("----
server: " + janus.getServer() );
}
});
});
</script>
</head>
<body>
Janus minimal example.
</body>
- basic attach to a plugin (function
plugin_client, called from document ready)
<!doctype
html>
<head>
<meta charset="utf-8"/>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.0.0/adapter.min.js"></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script
type="text/javascript"
src="janus.js"></script>
<script
type="text/javascript">
/**
* This script could
be placed in a file.
*/
var plugin_client =
function(params) {
var PLUGIN_PACKAGE =
"janus.plugin.streaming";
var janus = null;
var myPluginHandle = null;
this.start = function() {
Janus.init({
debug:
true,
dependencies:
Janus.useDefaultDependencies(), // or:
Janus.useOldDependencies() to get the
behaviour of previous Janus versions
callback:
function() {
//
init completed
//
create gateway session
janus
= new Janus(
{
server:
params.server,
withCredentials:
true,
success:
function() {
//
gateway session created
Janus.log("Janus
session id: " + janus.getSessionId()
);
//
attach to plugin
janus.attach(
{
plugin:
PLUGIN_PACKAGE,
success:
_onAttachSuccess,
error:
_onError,
onmessage:
_onMessage,
onlocalstream:
_onLocalStream,
onremotestream:
_onRemoteStream,
oncleanup:
_onCleanUp
});
},
error:
function(cause) {
//
error when creating gateway session
},
destroyed:
function() {
//
gateway session destroyed
}
});
Janus.debug("Janus
server: " + janus.getServer() );
}
});
};
this.stop = function() {
if (janus) {
janus.destroy();
janus
= null;
}
};
function
_onAttachSuccess(pluginHandle) {
myPluginHandle
= pluginHandle;
Janus.log("Plugin
attached: " +
myPluginHandle.getPlugin() + ", id="
+ myPluginHandle.getId() );
}
function _onError(message) {
Janus.error(message);
}
function _onMessage(msg, jsep) {
Janus.debug("Got
a message");
Janus.debug(JSON.stringify(msg));
}
function _onLocalStream(stream) {
Janus.debug("Got
a local stream");
Janus.debug(JSON.stringify(stream));
}
function _onRemoteStream(stream) {
Janus.debug("Got
a remote stream");
Janus.debug(JSON.stringify(stream));
}
function _onCleanUp() {
Janus.log("Got
a cleanup notification");
}
};
</script>
<script
type="text/javascript">
var janus_server =
"http://192.168.1.128:8088/janus";
$(document).ready(function() {
client = new plugin_client(
{
server:
janus_server
}
);
client.start();
});
</script>
</head>
<body>
Janus minimal example.
</body>
- multiple player from streaming plugin
<!doctype
html>
<head>
<meta charset="utf-8"/>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.0.0/adapter.min.js"></script>
<script
type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script
type="text/javascript"
src="janus.js"></script>
<script
type="text/javascript">
/**
* This script could
be placed in a file.
*/
var plugin_client =
function(params) {
var PLUGIN_PACKAGE =
"janus.plugin.streaming";
var janus = null;
var streaming = null;
// visible players
var number_feeds = 6;
var feeds = [];
// available mountpoints in Janus
streaming plugin
var mountpoints = [];
this.start = function() {
Janus.init({
debug: true,
dependencies:
Janus.useDefaultDependencies(), // or:
Janus.useOldDependencies() to get the
behaviour of previous Janus versions
callback: function() {
// init completed
// create gateway session
janus = new Janus(
{
server: params.server,
withCredentials: true,
success: function() {
// gateway session created
Janus.log("## Janus session id: " +
janus.getSessionId() );
// attach to plugin
janus.attach(
{
plugin: PLUGIN_PACKAGE,
success: _onAttachSuccess,
error: _onError,
onmessage: _onMessage,
onlocalstream: _onLocalStream,
onremotestream: _onRemoteStream,
oncleanup: _onCleanUp
});
},
error: function(cause) {
// error when creating gateway session
},
destroyed: function() {
// gateway session destroyed
}
});
Janus.debug("## Janus server: " +
janus.getServer() );
}
});
};
this.stop = function() {
if (janus) {
janus.destroy();
janus = null;
}
};
function
_onAttachSuccess(pluginHandle) {
streaming = pluginHandle;
Janus.log("## Plugin attached: " +
streaming.getPlugin() + ", id=" +
streaming.getId() );
updateStreamsList();
}
function _onError(message) {
Janus.error(message);
}
function _onMessage(msg, jsep) {
Janus.debug("## Got a message");
Janus.debug(msg);
if(jsep !== undefined && jsep
!== null) {
Janus.debug("## Handling SDP as
well...");
Janus.debug(jsep);
}
}
function _onLocalStream(stream) {
Janus.debug("## Got a local stream");
Janus.debug(JSON.stringify(stream));
}
function _onRemoteStream(stream) {
Janus.debug("## Got a remote stream");
Janus.debug(JSON.stringify(stream));
}
function _onCleanUp() {
Janus.log("## Got a cleanup
notification");
}
function updateStreamsList() {
// get list of available mountpoints
in streaming plugin
var body = { "request": "list" };
streaming.send(
{
"message": body,
success: function(result) {
if(result === null || result ===
undefined) {
bootbox.alert("Got no response to our
query for available streams");
return;
}
if(result["list"] !== undefined
&& result["list"] !== null) {
mountpoints = result["list"];
Janus.debug(mountpoints);
populateAll(mountpoints);
}
}
});
}
function populateAll (mountpoints) {
// start a player for each mountpoint
Janus.log("######## [populateAll]");
for(var m in mountpoints) {
var mountpoint_id =
mountpoints[m]["id"];
var type = mountpoints[m]["type"];
var description =
mountpoints[m]["description"];
newRemoteFeed(mountpoint_id, type,
description);
}
}
function newRemoteFeed(mountpoint_id,
type, description) {
// start a player
Janus.log("######## [newRemoteFeed]
mountpoint_id: "+ mountpoint_id + ";
type: " + type + "; description: " +
description );
var remoteFeed = null;
janus.attach(
{
plugin: "janus.plugin.streaming",
success: function(pluginHandle) {
remoteFeed =
pluginHandle;
Janus.log("######## [newRemoteFeed]
Plugin attached! (" +
remoteFeed.getPlugin() + ", id=" +
remoteFeed.getId() + ")");
// assign a player
for(var i=1;i<6;i++) {
if(feeds[i] === undefined || feeds[i]
=== null) {
feeds[i] = remoteFeed;
remoteFeed.rfindex = i;
break;
}
}
// send watch request
var body = { "request": "watch", "id":
mountpoint_id };
remoteFeed.send({"message": body});
},
error: function(error) {},
onmessage: function(msg, jsep) {
// received a message
Janus.debug("########
[newRemoteFeed.onmessage] Got a
message (mountpoint_id: " +
mountpoint_id + ")");
Janus.debug(msg);
// regular message
var result = msg["result"];
if(result != undefined &&
result != null) {
Janus.debug(result);
if(result["status"] !== undefined
&& result["status"] !== null)
{
var status = result["status"];
if(status === 'starting')
Janus.debug("########
[newRemoteFeed.onmessage] Starting,
please wait...");
else if(status === 'started')
Janus.debug("########
[newRemoteFeed.onmessage] Started");
else if(status === 'stopped')
Janus.debug("########
[newRemoteFeed.onmessage] Stopped");
} else if(msg["streaming"] ===
"event") {
// Is simulcast in place?
var substream = result["substream"];
var temporal = result["temporal"];
if((substream !== null &&
substream !== undefined) || (temporal
!== null && temporal !==
undefined)) {
if(!simulcastStarted) {
simulcastStarted = true;
addSimulcastButtons();
}
// We just received notice that
there's been a switch, update the
buttons
updateSimulcastButtons(substream,
temporal);
}
}
} else if(msg["error"] !== undefined
&& msg["error"] !== null) {
Janus.error("######## [newRemoteFeed]
Error: ", msg["error"]);
}
// message with an offer sdp
if(jsep !== undefined && jsep
!== null) {
Janus.debug("########
[newRemoteFeed.onmessage] Received an
offer sdp (mountpoint_id: " +
mountpoint_id + ")");
Janus.debug(jsep);
// create an answer with an sdp
remoteFeed.createAnswer(
{
jsep: jsep,
media: { audioSend: false, videoSend:
false
},
// We want recvonly audio/video
success: function(jsep) {
Janus.debug("########
[newRemoteFeed.onmessage] Sending an
answer sdp (mountpoint_id: " +
mountpoint_id + ")");
Janus.debug(jsep);
var body = { "request": "start" };
remoteFeed.send({"message": body,
"jsep": jsep});
//$('#watch').html("Stop").removeAttr('disabled').click(stopStream);
},
error: function(error) {
Janus.error("WebRTC error:", error);
bootbox.alert("WebRTC error... " +
JSON.stringify(error));
}
});
}
},
onremotestream: function(stream) {
// a remote stream is available
Janus.debug("########
[newRemoteFeed.onremotestream] Remote
stream is available (mountpoint_id: "
+ mountpoint_id + ")");
// first time?
if($('#remotevideo'+remoteFeed.rfindex).length
> 0) {
// Been here already: let's see if
anything changed
Janus.debug("########
[newRemoteFeed.onremotestream] Been
there already");
var videoTracks =
stream.getVideoTracks();
if(videoTracks &&
videoTracks.length > 0 &&
!videoTracks[0].muted) {
Janus.debug(videoTracks);
//$('#novideo'+remoteFeed.rfindex).remove();
//if($("#remotevideo"+remoteFeed.rfindex).get(0).videoWidth)
//
$('#remotevideo'+remoteFeed.rfindex).show();
}
return;
}
// create the html5 player
$('#videoremote' +
remoteFeed.rfindex).append(
'<h2>' + description +
'</h2> <video class="rounded
centered" id="remotevideo' +
remoteFeed.rfindex + '" width=320
height=240 controls
autoplay/><hr>');
// attach the remote stream to the
html5 video player
Janus.attachMediaStream($('#remotevideo'
+ remoteFeed.rfindex).get(0), stream);
},
oncleanup: function() {}
}
);
}
};
</script>
<script
type="text/javascript">
// var janus_server =
"https://192.168.1.114:8089/janus";
var janus_server = null;
if(window.location.protocol ===
'http:')
janus_server = "http://" +
window.location.hostname +
":8088/janus";
else
janus_server = "https://" +
window.location.hostname +
":8089/janus";
$(document).ready(function() {
client = new plugin_client(
{
server: janus_server
}
);
client.start();
});
</script>
</head>
<body>
<h1>Multiple
streams</h1>
<div
id="logs"></div>
<div
id="videoremote1"></div>
<div
id="videoremote2"></div>
<div
id="videoremote3"></div>
<div
id="videoremote4"></div>
<div
id="videoremote5"></div>
<div
id="videoremote6"></div>
</body>
- Demos (test web server)
- Available demos
- Streaming
- Problemes / Problems
- llista buida al desplegable «Stream list»
- ...
- http-server (NodeJS)
- Install NodeJS via nvm, and then:
- install http server
npm install -g http-server
- configure firewall
sudo systemctl start firewalld.service
sudo firewall-cmd --permanent --zone=public
--add-port=8080/tcp
sudo firewall-cmd --reload
- install html dir into
/usr/share/janus/demos/
cd janus-gateway
sudo make install html
- non-secure
- run http-server
nvm use default
http-server /usr/share/janus/demos
- check it:
http://<your_demos_server_ip>:8080/
http://<your_demos_server_ip>:8080/demos/
- secure
- server
nvm use default
http-server --ssl --cert
/usr/share/janus/certs/mycert.pem --key
/usr/share/janus/certs/mycert.key
/usr/share/janus/demos
- client
https://<your_demos_server_ip>:8080/
https://<your_demos_server_ip>:8080/demos/
- Problemes /
Problems
Probably a network error, is the gateway
down?: [object Object]
- Solució / Solution:
- Des del navegador, accepteu manualment
el certificat autosignat / From your
browser, manually accept self-signed
certificate in
https://<your_janus_server_ip>:8089/
- Reviseu l'apartat [cors]
del fitxer /etc/janus/janus.cfg
- nginx
- server
- Janus through Nginx
- Deploying
Janus behind a web frontend
- /etc/nginx/default.d/janus.conf
# janus (webrtc
server)
location /janus {
proxy_pass
http://127.0.0.1:8088/janus;
}
- SELinux
setsebool -P
httpd_can_network_connect 1
- chcon
-u system_u -t httpd_config_t
/etc/nginx/default.d/janus.conf
- access to demos
- /etc/nginx/default.d/janus_demos.conf
# janus demos
(webrtc server)
location /demos {
root
/usr/share/janus;
}
- SELinux
- chcon -u
system_u -t httpd_config_t
/etc/nginx/default.d/janus_demos.conf
- demos must point to Janus through nginx (instead
of ports 8088/8089):
cd /usr/share/demos
sed
-i '/var janus = null;/ i var server =
"/janus";' *.js
- https
- generate
self-signed certificate and key
(/etc/pki/nginx/server.crt,
/etc/pki/nginx/private/server.key)
mkdir -p /etc/pki/nginx/private
cd /etc/pki/nginx
openssl req -new -nodes -keyout
private/server.key -sha256 -x509 -out
server.crt
- /etc/nginx/conf.d/ssl_8080.conf
# Settings for a
TLS enabled server.
server {
listen
8080 ssl http2 default_server;
listen
[::]:8080 ssl http2 default_server;
server_name _;
root
/usr/share/nginx/html;
ssl_certificate
"/etc/pki/nginx/server.crt";
ssl_certificate_key
"/etc/pki/nginx/private/server.key";
ssl_session_cache
shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers
HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Load configuration
files for the default server block.
include
/etc/nginx/default.d/*.conf;
location / {
}
error_page 404
/404.html;
location = /40x.html {
}
error_page 500 502 503
504 /50x.html;
location = /50x.html {
}
}
- SELinux
- chcon
-u system_u -t httpd_config_t
/etc/nginx/default.d/ssl_8080.conf
sudo systemctl start nginx.service
- client
https://<your_demos_server_ip>/janus/info
- Problemes / Problems
- on server logs:
connect() to
127.0.0.1:8088 failed (13: Permission
denied) while connecting to upstream,
client: ...
https://<your_demos_server_ip>/demos
|