From 5fe48c25875c7c927551c6439f13491b040c9ce2 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Sat, 6 Jul 2024 14:43:37 -0400 Subject: [PATCH] Add mediamtx vm. --- ansible/roles/media/files/cast_file_vaapi | 180 +++++- router/k8s-stream.yaml | 14 + router/launch_mediamtx.bash | 151 +++++ router/launch_opnsense.bash | 2 +- router/mediamtx_rc.bash | 47 ++ router/stream.yml | 727 ++++++++++++++++++++++ router/unifi_rc.bash | 18 +- 7 files changed, 1123 insertions(+), 16 deletions(-) create mode 100644 router/k8s-stream.yaml create mode 100644 router/launch_mediamtx.bash create mode 100644 router/mediamtx_rc.bash create mode 100644 router/stream.yml diff --git a/ansible/roles/media/files/cast_file_vaapi b/ansible/roles/media/files/cast_file_vaapi index 5706b48..7414046 100644 --- a/ansible/roles/media/files/cast_file_vaapi +++ b/ansible/roles/media/files/cast_file_vaapi @@ -1,11 +1,179 @@ #!/usr/bin/env bash # +set -euo pipefail +IFS=$'\n\t' +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ffmpeg -re -vaapi_device /dev/dri/renderD128 -i "$1" -vf 'format=nv12,hwupload' -c:v h264_vaapi -r 30 -g 30 -loop -1 -c:a aac -b:a 160k -ar 44100 -strict -2 -f flv rtmp:172.16.16.44/live/test & -ffmpegpid=$! -sleep 1 -castnow --exit 'https://broadcast.fizz.buzz/hls/hls/test.m3u8' -wait "$ffmpegpid" -sleep 10 +function main { + local cmd + cmd=$1 + shift + if [ "$cmd" = "copy" ]; then + copy "${@}" + elif [ "$cmd" = "h264" ]; then + h264 "${@}" + elif [ "$cmd" = "software_h264" ]; then + software_h264 "${@}" + elif [ "$cmd" = "preprocess_h264" ]; then + preprocess_h264 "${@}" + elif [ "$cmd" = "preprocess_vp8" ]; then + preprocess_vp8 "${@}" + elif [ "$cmd" = "webcam" ]; then + webcam "${@}" + elif [ "$cmd" = "encode_webcam" ]; then + encode_webcam "${@}" + else + (>&2 echo "Unknown command: $cmd") + exit 1 + fi +} + +function copy { + local file_to_cast + file_to_cast="$3" + + local USERNAME PASSWORD + USERNAME="$1" + PASSWORD="$2" + + exec ffmpeg \ + -re \ + -stream_loop -1 \ + -i "$file_to_cast" \ + -c copy \ + -f rtsp \ + -rtsp_transport udp \ + "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch" +} + +function h264 { + local file_to_cast + file_to_cast="$3" + + local USERNAME PASSWORD + USERNAME="$1" + PASSWORD="$2" + + # -bf 0 :: Disable b-frames because webrtc doesn't support h264 streams with b-frames. + exec ffmpeg \ + -re \ + -stream_loop -1 \ + -init_hw_device vaapi=foo:/dev/dri/renderD128 \ + -hwaccel vaapi \ + -hwaccel_output_format vaapi \ + -hwaccel_device foo \ + -i "$file_to_cast" \ + -filter_hw_device foo \ + -vf 'format=nv12|vaapi,hwupload' \ + -c:v h264_vaapi \ + -bf 0 \ + -c:a aac \ + -b:a 160k \ + -ar 44100 \ + -f rtsp \ + -rtsp_transport udp \ + "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch" +} + +function software_h264 { + local file_to_cast + file_to_cast="$3" + + local USERNAME PASSWORD + USERNAME="$1" + PASSWORD="$2" + + # -bf 0 :: Disable b-frames because webrtc doesn't support h264 streams with b-frames. + exec ffmpeg \ + -re \ + -stream_loop -1 \ + -i "$file_to_cast" \ + -c:v h264 \ + -bf 0 \ + -c:a aac \ + -b:a 160k \ + -ar 44100 \ + -f rtsp \ + -rtsp_transport udp \ + "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch" +} + +function preprocess_h264 { + local file_to_cast file_to_save + file_to_cast="$1" + file_to_save="$2" + + # -bf 0 :: Disable b-frames because webrtc doesn't support h264 streams with b-frames. + exec ffmpeg \ + -i "$file_to_cast" \ + -c:v h264 \ + -bf 0 \ + -c:a aac \ + -b:a 160k \ + -ar 44100 \ + "$file_to_save" +} + +function preprocess_vp8 { + local file_to_cast file_to_save + file_to_cast="$1" + file_to_save="$2" + + # -bf 0 :: Disable b-frames because webrtc doesn't support h264 streams with b-frames. + # -strict -2 :: Enable support for experimental codecs like opus. + # -b:v 1M :: Target 1 megabit/s + # -crf 10 :: Target a quality level and adjust bitrate accordingly. This should be preferred, but ideally both should be used. + exec ffmpeg \ + -i "$file_to_cast" \ + -c:v vp8 \ + -b:v 1M \ + -crf 10 \ + -bf 0 \ + -c:a opus \ + -b:a 320k \ + -ar 48000 \ + -strict -2 \ + "$file_to_save" +} + +function webcam { + # Uses on-webcam h264 encoding. + + local USERNAME PASSWORD + USERNAME="$1" + PASSWORD="$2" + + exec ffmpeg \ + -re \ + -input_format h264 \ + -video_size 1920x1080 \ + -i /dev/video0 \ + -c:v copy \ + -an \ + -f rtsp \ + -rtsp_transport udp \ + "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch" +} + +function encode_webcam { + # Uses hardware accelerated gpu-based encoding. + + local USERNAME PASSWORD + USERNAME="$1" + PASSWORD="$2" + + exec ffmpeg \ + -re \ + -vaapi_device /dev/dri/renderD128 \ + -i /dev/video0 \ + -vf 'format=nv12,hwupload' \ + -c:v h264_vaapi \ + -an \ + -f rtsp \ + -rtsp_transport udp \ + "rtsp://$USERNAME:$PASSWORD@172.16.16.251:8554/fetch" +} + +main "${@}" diff --git a/router/k8s-stream.yaml b/router/k8s-stream.yaml new file mode 100644 index 0000000..a552b24 --- /dev/null +++ b/router/k8s-stream.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: stream-cert + namespace: default +spec: + dnsNames: + - "home.fizz.buzz" + - "stream.fizz.buzz" + secretName: stream-cert + issuerRef: + name: letsencrypt-prd + kind: ClusterIssuer + group: cert-manager.io diff --git a/router/launch_mediamtx.bash b/router/launch_mediamtx.bash new file mode 100644 index 0000000..262a588 --- /dev/null +++ b/router/launch_mediamtx.bash @@ -0,0 +1,151 @@ +#!/usr/local/bin/bash +# +set -euo pipefail +IFS=$'\n\t' +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +: ${CD:=""} +: ${VNC_ENABLE:="NO"} +: ${VNC_LISTEN:="127.0.0.1:5900"} +: ${PID_FILE:="/var/run/mediamtx.pid"} + +############## Setup ######################### + +function cleanup { + for vm in "${vms[@]}"; do + log "Destroying bhyve vm $vm" + bhyvectl "--vm=$vm" --destroy + log "Destroyed bhyve vm $vm" + done +} +vms=() +for sig in EXIT INT QUIT HUP TERM; do + trap "set +e; sleep 10; cleanup" "$sig" +done + +function die { + local status_code="$1" + shift + (>&2 echo "${@}") + exit "$status_code" +} + +function log { + (>&2 echo "${@}") +} + +############## Program ######################### + +function main { + start_vm +} + +function start_vm { + local name="mediamtx" + + + + # -H release the CPU when guest issues HLT instruction. Otherwise 100% of core will be consumed. + # -s 3,ahci-cd,/vm/.iso/archlinux-2023.04.01-x86_64.iso \ + # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080,wait \ + # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080 \ + + # TODO: Look into using nmdm instead of stdio for serial console + if [ -n "$CD" ]; then + additional_args+=("-s" "5,ahci-cd,$CD") + fi + if [ "$VNC_ENABLE" = "YES" ]; then + additional_args+=("-s" "29,fbuf,tcp=$VNC_LISTEN,w=1920,h=1080") + fi + + local bridge_name="bridge_vm" + wait_for_bridge "$bridge_name" + + local mac_address + mac_address=$(calculate_mac_address "$name") + + local bridge_link_name + bridge_link_name=$(detect_available_link "${bridge_name}") + + # additional_args+=("-s" "2:0,virtio-net,netgraph,path=${bridge_name}:,peerhook=${bridge_link_name},mac=${mac_address}") + additional_args+=("-s" "2:0,virtio-net,netgraph,path=${bridge_name}:,peerhook=${bridge_link_name},mac=${mac_address}" "-s" "3,ahci-cd,/home/talexander/disk.iso") + vms+=("$name") + while true; do + set -x + set +e + bhyve \ + -D \ + -c 1 \ + -m 3G \ + -H \ + -o 'rtc.use_localtime=false' \ + -s 0,hostbridge \ + -s "4,nvme,/dev/zvol/zroot/vm/mediamtx/disk0" \ + -s 30,xhci,tablet \ + -s 31,lpc -l com1,stdio \ + -l "bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd,/vm/mediamtx/BHYVE_UEFI_VARS.fd" \ + "${additional_args[@]}" \ + "$name" + # local bhyvepid=$! + # echo "$bhyvepid" > "$PID_FILE" + # wait $bhyvepid + local exit_code=$? + set +x + set -e + if [ $exit_code -eq 0 ]; then + echo "Rebooting." + sleep 5 + elif [ $exit_code -eq 1 ]; then + echo "Powered off." + break + elif [ $exit_code -eq 2 ]; then + echo "Halted." + break + elif [ $exit_code -eq 3 ]; then + echo "Triple fault." + break + elif [ $exit_code -eq 4 ]; then + echo "Exited due to an error." + break + fi + done +} + +function ng_exists { + ngctl status "${1}" >/dev/null 2>&1 +} + +function wait_for_bridge { + local bridge_name="$1" + while ! ng_exists "${bridge_name}:"; do + echo "${bridge_name} does not yet exist, sleeping." + sleep 10 + done +} + +function detect_available_link { + local bridge_name="$1" + local linknum=1 + while true; do + local link_name="link${linknum}" + if ! ng_exists "${bridge_name}:${link_name}"; then + echo "$link_name" + return + fi + linknum=$((linknum + 1)) + if [ "$linknum" -gt 90 ]; then + (>&2 echo "No available links on bridge $bridge_name") + exit 1 + fi + done +} + +function calculate_mac_address { + local name="$1" + local source + source=$(md5 -r -s "$name" | awk '{print $1}') + echo "06:${source:0:2}:${source:2:2}:${source:4:2}:${source:6:2}:${source:8:2}" +} + + +main "${@}" diff --git a/router/launch_opnsense.bash b/router/launch_opnsense.bash index d50cca4..b0ec7f9 100644 --- a/router/launch_opnsense.bash +++ b/router/launch_opnsense.bash @@ -73,7 +73,7 @@ function start_vm { set +e bhyve \ -D \ - -c 6 \ + -c 4 \ -m 8G \ -H \ -o 'rtc.use_localtime=false' \ diff --git a/router/mediamtx_rc.bash b/router/mediamtx_rc.bash new file mode 100644 index 0000000..b740767 --- /dev/null +++ b/router/mediamtx_rc.bash @@ -0,0 +1,47 @@ +#!/bin/sh +# +# REQUIRE: FILESYSTEMS kld +# PROVIDE: mediamtx +# BEFORE: netif + +. /etc/rc.subr +name=mediamtx +rcvar=${name}_enable +start_cmd="${name}_start" +stop_cmd="${name}_stop" +status_cmd="${name}_status" +load_rc_config $name + +tmux_name="mediamtx" + +mediamtx_start() { + /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=YES VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_mediamtx.bash" + # /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=NO VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_mediamtx.bash" +} + +mediamtx_status() { + if /usr/local/bin/tmux has-session -t $tmux_name 2>/dev/null; then + echo "$tmux_name is running." + else + echo "$tmux_name is not running." + return 1 + fi +} + +mediamtx_stop() { + /usr/local/bin/tmux has-session -t $tmux_name 2>/dev/null && ( + /usr/local/bin/tmux kill-session -t $tmux_name + sleep 10 + bhyvectl --vm=mediamtx --destroy + # kill `cat /var/run/mediamtx.pid` + ) + mediamtx_wait_for_end +} + +mediamtx_wait_for_end() { + while /usr/local/bin/tmux has-session -t $tmux_name 2>dev/null; do + sleep 1 + done +} + +run_rc_command "$1" diff --git a/router/stream.yml b/router/stream.yml new file mode 100644 index 0000000..558706a --- /dev/null +++ b/router/stream.yml @@ -0,0 +1,727 @@ +# +# How to update cert: +# +# kubectl apply -f k8s-stream.yaml +# kubectl wait --for=condition=ready=true --timeout=300s -n default certificate stream-cert +# decrypt_k8s_secret -n default stream-cert | jq -r '.["tls.crt"]' | tee server.crt +# decrypt_k8s_secret -n default stream-cert | jq -r '.["tls.key"]' | tee server.key +# kdel -f k8s-stream.yaml +# gpg_auth scp stream.yml server.crt server.key 172.16.16.251:~/ ; gpg_auth ssh 172.16.16.251 doas install -o root -g root -m 0644 stream.yml server.crt /etc/mediamtx/ ; gpg_auth ssh 172.16.16.251 doas install -o root -g root -m 0600 server.key /etc/mediamtx/ + +############################################### +# Global settings + +# Settings in this section are applied anywhere. + +############################################### +# Global settings -> General + +# Verbosity of the program; available values are "error", "warn", "info", "debug". +logLevel: info +# Destinations of log messages; available values are "stdout", "file" and "syslog". +logDestinations: [stdout] +# If "file" is in logDestinations, this is the file which will receive the logs. +logFile: mediamtx.log + +# Timeout of read operations. +readTimeout: 10s +# Timeout of write operations. +writeTimeout: 10s +# Size of the queue of outgoing packets. +# A higher value allows to increase throughput, a lower value allows to save RAM. +writeQueueSize: 512 +# Maximum size of outgoing UDP packets. +# This can be decreased to avoid fragmentation on networks with a low UDP MTU. +udpMaxPayloadSize: 1472 + +# Command to run when a client connects to the server. +# This is terminated with SIGINT when a client disconnects from the server. +# The following environment variables are available: +# * RTSP_PORT: RTSP server port +# * MTX_CONN_TYPE: connection type +# * MTX_CONN_ID: connection ID +runOnConnect: +# Restart the command if it exits. +runOnConnectRestart: no +# Command to run when a client disconnects from the server. +# Environment variables are the same of runOnConnect. +runOnDisconnect: + +############################################### +# Global settings -> Authentication + +# Authentication method. Available values are: +# * internal: users are stored in the configuration file +# * http: an external HTTP URL is contacted to perform authentication +# * jwt: an external identity server provides authentication through JWTs +authMethod: internal + +# Internal authentication. +# list of users. +authInternalUsers: + # Default unprivileged user. + # Username. 'any' means any user, including anonymous ones. + - user: any + # Password. Not used in case of 'any' user. + pass: + # IPs or networks allowed to use this user. An empty list means any IP. + ips: [] + # List of permissions. + permissions: + [] + # # Available actions are: publish, read, playback, api, metrics, pprof. + # - action: publish + # # Paths can be set to further restrict access to a specific path. + # # An empty path means any path. + # # Regular expressions can be used by using a tilde as prefix. + # path: + # - action: read + # path: + # - action: playback + # path: + - user: heyheyyouyou + # Password. Not used in case of 'any' user. + pass: idontlikeyourgirlfriend + # IPs or networks allowed to use this user. An empty list means any IP. + ips: [] + # List of permissions. + permissions: + # Available actions are: publish, read, playback, api, metrics, pprof. + - action: publish + # Paths can be set to further restrict access to a specific path. + # An empty path means any path. + # Regular expressions can be used by using a tilde as prefix. + path: + - action: read + path: + - action: playback + path: + + # Default administrator. + # This allows to use API, metrics and PPROF without authentication, + # if the IP is localhost. + - user: any + pass: + ips: ["127.0.0.1", "::1"] + permissions: + - action: api + - action: metrics + - action: pprof + +# HTTP-based authentication. +# URL called to perform authentication. Every time a user wants +# to authenticate, the server calls this URL with the POST method +# and a body containing: +# { +# "user": "user", +# "password": "password", +# "ip": "ip", +# "action": "publish|read|playback|api|metrics|pprof", +# "path": "path", +# "protocol": "rtsp|rtmp|hls|webrtc|srt", +# "id": "id", +# "query": "query" +# } +# If the response code is 20x, authentication is accepted, otherwise +# it is discarded. +authHTTPAddress: +# Actions to exclude from HTTP-based authentication. +# Format is the same as the one of user permissions. +authHTTPExclude: + - action: api + - action: metrics + - action: pprof + +# JWT-based authentication. +# Users have to login through an external identity server and obtain a JWT. +# This JWT must contain the claim "mediamtx_permissions" with permissions, +# for instance: +# { +# ... +# "mediamtx_permissions": [ +# { +# "action": "publish", +# "path": "somepath" +# } +# ] +# } +# Users are expected to pass the JWT in the Authorization header or as a query parameter. +# This is the JWKS URL that will be used to pull (once) the public key that allows +# to validate JWTs. +authJWTJWKS: + +############################################### +# Global settings -> Control API + +# Enable controlling the server through the Control API. +api: no +# Address of the Control API listener. +apiAddress: :9997 +# Enable TLS/HTTPS on the Control API server. +apiEncryption: no +# Path to the server key. This is needed only when encryption is yes. +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +apiServerKey: /etc/mediamtx/server.key +# Path to the server certificate. +apiServerCert: /etc/mediamtx/server.crt +# Value of the Access-Control-Allow-Origin header provided in every HTTP response. +apiAllowOrigin: "*" +# List of IPs or CIDRs of proxies placed before the HTTP server. +# If the server receives a request from one of these entries, IP in logs +# will be taken from the X-Forwarded-For header. +apiTrustedProxies: [] + +############################################### +# Global settings -> Metrics + +# Enable Prometheus-compatible metrics. +metrics: no +# Address of the metrics HTTP listener. +metricsAddress: :9998 +# Enable TLS/HTTPS on the Metrics server. +metricsEncryption: no +# Path to the server key. This is needed only when encryption is yes. +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +metricsServerKey: /etc/mediamtx/server.key +# Path to the server certificate. +metricsServerCert: /etc/mediamtx/server.crt +# Value of the Access-Control-Allow-Origin header provided in every HTTP response. +metricsAllowOrigin: "*" +# List of IPs or CIDRs of proxies placed before the HTTP server. +# If the server receives a request from one of these entries, IP in logs +# will be taken from the X-Forwarded-For header. +metricsTrustedProxies: [] + +############################################### +# Global settings -> PPROF + +# Enable pprof-compatible endpoint to monitor performances. +pprof: no +# Address of the pprof listener. +pprofAddress: :9999 +# Enable TLS/HTTPS on the pprof server. +pprofEncryption: no +# Path to the server key. This is needed only when encryption is yes. +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +pprofServerKey: /etc/mediamtx/server.key +# Path to the server certificate. +pprofServerCert: /etc/mediamtx/server.crt +# Value of the Access-Control-Allow-Origin header provided in every HTTP response. +pprofAllowOrigin: "*" +# List of IPs or CIDRs of proxies placed before the HTTP server. +# If the server receives a request from one of these entries, IP in logs +# will be taken from the X-Forwarded-For header. +pprofTrustedProxies: [] + +############################################### +# Global settings -> Playback server + +# Enable downloading recordings from the playback server. +playback: no +# Address of the playback server listener. +playbackAddress: :9996 +# Enable TLS/HTTPS on the playback server. +playbackEncryption: no +# Path to the server key. This is needed only when encryption is yes. +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +playbackServerKey: /etc/mediamtx/server.key +# Path to the server certificate. +playbackServerCert: /etc/mediamtx/server.crt +# Value of the Access-Control-Allow-Origin header provided in every HTTP response. +playbackAllowOrigin: "*" +# List of IPs or CIDRs of proxies placed before the HTTP server. +# If the server receives a request from one of these entries, IP in logs +# will be taken from the X-Forwarded-For header. +playbackTrustedProxies: [] + +############################################### +# Global settings -> RTSP server + +# Enable publishing and reading streams with the RTSP protocol. +rtsp: yes +# List of enabled RTSP transport protocols. +# UDP is the most performant, but doesn't work when there's a NAT/firewall between +# server and clients, and doesn't support encryption. +# UDP-multicast allows to save bandwidth when clients are all in the same LAN. +# TCP is the most versatile, and does support encryption. +# The handshake is always performed with TCP. +protocols: [udp, multicast, tcp] +# Encrypt handshakes and TCP streams with TLS (RTSPS). +# Available values are "no", "strict", "optional". +encryption: "no" +# Address of the TCP/RTSP listener. This is needed only when encryption is "no" or "optional". +rtspAddress: :8554 +# Address of the TCP/TLS/RTSPS listener. This is needed only when encryption is "strict" or "optional". +rtspsAddress: :8322 +# Address of the UDP/RTP listener. This is needed only when "udp" is in protocols. +rtpAddress: :8000 +# Address of the UDP/RTCP listener. This is needed only when "udp" is in protocols. +rtcpAddress: :8001 +# IP range of all UDP-multicast listeners. This is needed only when "multicast" is in protocols. +multicastIPRange: 224.1.0.0/16 +# Port of all UDP-multicast/RTP listeners. This is needed only when "multicast" is in protocols. +multicastRTPPort: 8002 +# Port of all UDP-multicast/RTCP listeners. This is needed only when "multicast" is in protocols. +multicastRTCPPort: 8003 +# Path to the server key. This is needed only when encryption is "strict" or "optional". +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +serverKey: /etc/mediamtx/server.key +# Path to the server certificate. This is needed only when encryption is "strict" or "optional". +serverCert: /etc/mediamtx/server.crt +# Authentication methods. Available are "basic" and "digest". +# "digest" doesn't provide any additional security and is available for compatibility only. +rtspAuthMethods: [basic] + +############################################### +# Global settings -> RTMP server + +# Enable publishing and reading streams with the RTMP protocol. +rtmp: yes +# Address of the RTMP listener. This is needed only when encryption is "no" or "optional". +rtmpAddress: :1935 +# Encrypt connections with TLS (RTMPS). +# Available values are "no", "strict", "optional". +rtmpEncryption: "no" +# Address of the RTMPS listener. This is needed only when encryption is "strict" or "optional". +rtmpsAddress: :1936 +# Path to the server key. This is needed only when encryption is "strict" or "optional". +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +rtmpServerKey: /etc/mediamtx/server.key +# Path to the server certificate. This is needed only when encryption is "strict" or "optional". +rtmpServerCert: /etc/mediamtx/server.crt + +############################################### +# Global settings -> HLS server + +# Enable reading streams with the HLS protocol. +hls: yes +# Address of the HLS listener. +hlsAddress: :8888 +# Enable TLS/HTTPS on the HLS server. +# This is required for Low-Latency HLS. +hlsEncryption: yes +# Path to the server key. This is needed only when encryption is yes. +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +hlsServerKey: /etc/mediamtx/server.key +# Path to the server certificate. +hlsServerCert: /etc/mediamtx/server.crt +# Value of the Access-Control-Allow-Origin header provided in every HTTP response. +# This allows to play the HLS stream from an external website. +hlsAllowOrigin: "*" +# List of IPs or CIDRs of proxies placed before the HLS server. +# If the server receives a request from one of these entries, IP in logs +# will be taken from the X-Forwarded-For header. +hlsTrustedProxies: [] +# By default, HLS is generated only when requested by a user. +# This option allows to generate it always, avoiding the delay between request and generation. +hlsAlwaysRemux: no +# Variant of the HLS protocol to use. Available options are: +# * mpegts - uses MPEG-TS segments, for maximum compatibility. +# * fmp4 - uses fragmented MP4 segments, more efficient. +# * lowLatency - uses Low-Latency HLS. +hlsVariant: lowLatency +# Number of HLS segments to keep on the server. +# Segments allow to seek through the stream. +# Their number doesn't influence latency. +hlsSegmentCount: 7 +# Minimum duration of each segment. +# A player usually puts 3 segments in a buffer before reproducing the stream. +# The final segment duration is also influenced by the interval between IDR frames, +# since the server changes the duration in order to include at least one IDR frame +# in each segment. +hlsSegmentDuration: 1s +# Minimum duration of each part. +# A player usually puts 3 parts in a buffer before reproducing the stream. +# Parts are used in Low-Latency HLS in place of segments. +# Part duration is influenced by the distance between video/audio samples +# and is adjusted in order to produce segments with a similar duration. +hlsPartDuration: 200ms +# Maximum size of each segment. +# This prevents RAM exhaustion. +hlsSegmentMaxSize: 50M +# Directory in which to save segments, instead of keeping them in the RAM. +# This decreases performance, since reading from disk is less performant than +# reading from RAM, but allows to save RAM. +hlsDirectory: "" +# The muxer will be closed when there are no +# reader requests and this amount of time has passed. +hlsMuxerCloseAfter: 60s + +############################################### +# Global settings -> WebRTC server + +# Enable publishing and reading streams with the WebRTC protocol. +webrtc: yes +# Address of the WebRTC HTTP listener. +webrtcAddress: :8889 +# Enable TLS/HTTPS on the WebRTC server. +webrtcEncryption: yes +# Path to the server key. +# This can be generated with: +# openssl genrsa -out server.key 2048 +# openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 +webrtcServerKey: /etc/mediamtx/server.key +# Path to the server certificate. +webrtcServerCert: /etc/mediamtx/server.crt +# Value of the Access-Control-Allow-Origin header provided in every HTTP response. +# This allows to play the WebRTC stream from an external website. +webrtcAllowOrigin: "*" +# List of IPs or CIDRs of proxies placed before the WebRTC server. +# If the server receives a request from one of these entries, IP in logs +# will be taken from the X-Forwarded-For header. +webrtcTrustedProxies: [] +# Address of a local UDP listener that will receive connections. +# Use a blank string to disable. +webrtcLocalUDPAddress: :8189 +# Address of a local TCP listener that will receive connections. +# This is disabled by default since TCP is less efficient than UDP and +# introduces a progressive delay when network is congested. +webrtcLocalTCPAddress: "" +# WebRTC clients need to know the IP of the server. +# Gather IPs from interfaces and send them to clients. +webrtcIPsFromInterfaces: no +# List of interfaces whose IPs will be sent to clients. +# An empty value means to use all available interfaces. +webrtcIPsFromInterfacesList: [] +# List of additional hosts or IPs to send to clients. +webrtcAdditionalHosts: [68.197.252.15, stream.fizz.buzz] +# webrtcAdditionalHosts: [] +# ICE servers. Needed only when local listeners can't be reached by clients. +# STUN servers allows to obtain and share the public IP of the server. +# TURN/TURNS servers forces all traffic through them. +webrtcICEServers2: + [] + # - url: stun:stun.cloudflare.com:3478 + # - url: stun:stun.l.google.com:19302 + # if user is "AUTH_SECRET", then authentication is secret based. + # the secret must be inserted into the password field. + # username: '' + # password: '' + # clientOnly: false +# Time to wait for the WebRTC handshake to complete. +webrtcHandshakeTimeout: 10s +# Maximum time to gather video tracks. +webrtcTrackGatherTimeout: 2s + +############################################### +# Global settings -> SRT server + +# Enable publishing and reading streams with the SRT protocol. +srt: yes +# Address of the SRT listener. +srtAddress: :8890 + +############################################### +# Default path settings + +# Settings in "pathDefaults" are applied anywhere, +# unless they are overridden in "paths". +pathDefaults: + ############################################### + # Default path settings -> General + + # Source of the stream. This can be: + # * publisher -> the stream is provided by a RTSP, RTMP, WebRTC or SRT client + # * rtsp://existing-url -> the stream is pulled from another RTSP server / camera + # * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS + # * rtmp://existing-url -> the stream is pulled from another RTMP server / camera + # * rtmps://existing-url -> the stream is pulled from another RTMP server / camera with RTMPS + # * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera + # * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server / camera with HTTPS + # * udp://ip:port -> the stream is pulled with UDP, by listening on the specified IP and port + # * srt://existing-url -> the stream is pulled from another SRT server / camera + # * whep://existing-url -> the stream is pulled from another WebRTC server / camera + # * wheps://existing-url -> the stream is pulled from another WebRTC server / camera with HTTPS + # * redirect -> the stream is provided by another path or server + # * rpiCamera -> the stream is provided by a Raspberry Pi Camera + # If path name is a regular expression, $G1, G2, etc will be replaced + # with regular expression groups. + source: publisher + # If the source is a URL, and the source certificate is self-signed + # or invalid, you can provide the fingerprint of the certificate in order to + # validate it anyway. It can be obtained by running: + # openssl s_client -connect source_ip:source_port /dev/null | sed -n '/BEGIN/,/END/p' > server.crt + # openssl x509 -in server.crt -noout -fingerprint -sha256 | cut -d "=" -f2 | tr -d ':' + sourceFingerprint: + # If the source is a URL, it will be pulled only when at least + # one reader is connected, saving bandwidth. + sourceOnDemand: no + # If sourceOnDemand is "yes", readers will be put on hold until the source is + # ready or until this amount of time has passed. + sourceOnDemandStartTimeout: 10s + # If sourceOnDemand is "yes", the source will be closed when there are no + # readers connected and this amount of time has passed. + sourceOnDemandCloseAfter: 10s + # Maximum number of readers. Zero means no limit. + maxReaders: 0 + # SRT encryption passphrase require to read from this path + srtReadPassphrase: + # If the stream is not available, redirect readers to this path. + # It can be can be a relative path (i.e. /otherstream) or an absolute RTSP URL. + fallback: + + ############################################### + # Default path settings -> Record + + # Record streams to disk. + record: no + # Path of recording segments. + # Extension is added automatically. + # Available variables are %path (path name), %Y %m %d %H %M %S %f %s (time in strftime format) + recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f + # Format of recorded segments. + # Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS). + recordFormat: fmp4 + # fMP4 segments are concatenation of small MP4 files (parts), each with this duration. + # MPEG-TS segments are concatenation of 188-bytes packets, flushed to disk with this period. + # When a system failure occurs, the last part gets lost. + # Therefore, the part duration is equal to the RPO (recovery point objective). + recordPartDuration: 1s + # Minimum duration of each segment. + recordSegmentDuration: 1h + # Delete segments after this timespan. + # Set to 0s to disable automatic deletion. + recordDeleteAfter: 24h + + ############################################### + # Default path settings -> Publisher source (when source is "publisher") + + # Allow another client to disconnect the current publisher and publish in its place. + overridePublisher: yes + # SRT encryption passphrase required to publish to this path + srtPublishPassphrase: + + ############################################### + # Default path settings -> RTSP source (when source is a RTSP or a RTSPS URL) + + # Transport protocol used to pull the stream. available values are "automatic", "udp", "multicast", "tcp". + rtspTransport: automatic + # Support sources that don't provide server ports or use random server ports. This is a security issue + # and must be used only when interacting with sources that require it. + rtspAnyPort: no + # Range header to send to the source, in order to start streaming from the specified offset. + # available values: + # * clock: Absolute time + # * npt: Normal Play Time + # * smpte: SMPTE timestamps relative to the start of the recording + rtspRangeType: + # Available values: + # * clock: UTC ISO 8601 combined date and time string, e.g. 20230812T120000Z + # * npt: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + # * smpte: duration such as "300ms", "1.5m" or "2h45m", valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h" + rtspRangeStart: + + ############################################### + # Default path settings -> Redirect source (when source is "redirect") + + # RTSP URL which clients will be redirected to. + sourceRedirect: + + ############################################### + # Default path settings -> Raspberry Pi Camera source (when source is "rpiCamera") + + # ID of the camera + rpiCameraCamID: 0 + # width of frames + rpiCameraWidth: 1920 + # height of frames + rpiCameraHeight: 1080 + # flip horizontally + rpiCameraHFlip: false + # flip vertically + rpiCameraVFlip: false + # brightness [-1, 1] + rpiCameraBrightness: 0 + # contrast [0, 16] + rpiCameraContrast: 1 + # saturation [0, 16] + rpiCameraSaturation: 1 + # sharpness [0, 16] + rpiCameraSharpness: 1 + # exposure mode. + # values: normal, short, long, custom + rpiCameraExposure: normal + # auto-white-balance mode. + # values: auto, incandescent, tungsten, fluorescent, indoor, daylight, cloudy, custom + rpiCameraAWB: auto + # auto-white-balance fixed gains. This can be used in place of rpiCameraAWB. + # format: [red,blue] + rpiCameraAWBGains: [0, 0] + # denoise operating mode. + # values: off, cdn_off, cdn_fast, cdn_hq + rpiCameraDenoise: "off" + # fixed shutter speed, in microseconds. + rpiCameraShutter: 0 + # metering mode of the AEC/AGC algorithm. + # values: centre, spot, matrix, custom + rpiCameraMetering: centre + # fixed gain + rpiCameraGain: 0 + # EV compensation of the image [-10, 10] + rpiCameraEV: 0 + # Region of interest, in format x,y,width,height + rpiCameraROI: + # whether to enable HDR on Raspberry Camera 3. + rpiCameraHDR: false + # tuning file + rpiCameraTuningFile: + # sensor mode, in format [width]:[height]:[bit-depth]:[packing] + # bit-depth and packing are optional. + rpiCameraMode: + # frames per second + rpiCameraFPS: 30 + # period between IDR frames + rpiCameraIDRPeriod: 60 + # bitrate + rpiCameraBitrate: 1000000 + # H264 profile + rpiCameraProfile: main + # H264 level + rpiCameraLevel: "4.1" + # Autofocus mode + # values: auto, manual, continuous + rpiCameraAfMode: continuous + # Autofocus range + # values: normal, macro, full + rpiCameraAfRange: normal + # Autofocus speed + # values: normal, fast + rpiCameraAfSpeed: normal + # Lens position (for manual autofocus only), will be set to focus to a specific distance + # calculated by the following formula: d = 1 / value + # Examples: 0 moves the lens to infinity. + # 0.5 moves the lens to focus on objects 2m away. + # 2 moves the lens to focus on objects 50cm away. + rpiCameraLensPosition: 0.0 + # Specifies the autofocus window, in the form x,y,width,height where the coordinates + # are given as a proportion of the entire image. + rpiCameraAfWindow: + # enables printing text on each frame. + rpiCameraTextOverlayEnable: false + # text that is printed on each frame. + # format is the one of the strftime() function. + rpiCameraTextOverlay: "%Y-%m-%d %H:%M:%S - MediaMTX" + + ############################################### + # Default path settings -> Hooks + + # Command to run when this path is initialized. + # This can be used to publish a stream when the server is launched. + # This is terminated with SIGINT when the program closes. + # The following environment variables are available: + # * MTX_PATH: path name + # * RTSP_PORT: RTSP server port + # * G1, G2, ...: regular expression groups, if path name is + # a regular expression. + runOnInit: + # Restart the command if it exits. + runOnInitRestart: no + + # Command to run when this path is requested by a reader + # and no one is publishing to this path yet. + # This can be used to publish a stream on demand. + # This is terminated with SIGINT when there are no readers anymore. + # The following environment variables are available: + # * MTX_PATH: path name + # * MTX_QUERY: query parameters (passed by first reader) + # * RTSP_PORT: RTSP server port + # * G1, G2, ...: regular expression groups, if path name is + # a regular expression. + runOnDemand: + # Restart the command if it exits. + runOnDemandRestart: no + # Readers will be put on hold until the runOnDemand command starts publishing + # or until this amount of time has passed. + runOnDemandStartTimeout: 10s + # The command will be closed when there are no + # readers connected and this amount of time has passed. + runOnDemandCloseAfter: 10s + # Command to run when there are no readers anymore. + # Environment variables are the same of runOnDemand. + runOnUnDemand: + + # Command to run when the stream is ready to be read, whenever it is + # published by a client or pulled from a server / camera. + # This is terminated with SIGINT when the stream is not ready anymore. + # The following environment variables are available: + # * MTX_PATH: path name + # * MTX_QUERY: query parameters (passed by publisher) + # * RTSP_PORT: RTSP server port + # * G1, G2, ...: regular expression groups, if path name is + # a regular expression. + # * MTX_SOURCE_TYPE: source type + # * MTX_SOURCE_ID: source ID + runOnReady: + # Restart the command if it exits. + runOnReadyRestart: no + # Command to run when the stream is not available anymore. + # Environment variables are the same of runOnReady. + runOnNotReady: + + # Command to run when a client starts reading. + # This is terminated with SIGINT when a client stops reading. + # The following environment variables are available: + # * MTX_PATH: path name + # * MTX_QUERY: query parameters (passed by reader) + # * RTSP_PORT: RTSP server port + # * G1, G2, ...: regular expression groups, if path name is + # a regular expression. + # * MTX_READER_TYPE: reader type + # * MTX_READER_ID: reader ID + runOnRead: + # Restart the command if it exits. + runOnReadRestart: no + # Command to run when a client stops reading. + # Environment variables are the same of runOnRead. + runOnUnread: + + # Command to run when a recording segment is created. + # The following environment variables are available: + # * MTX_PATH: path name + # * RTSP_PORT: RTSP server port + # * G1, G2, ...: regular expression groups, if path name is + # a regular expression. + # * MTX_SEGMENT_PATH: segment file path + runOnRecordSegmentCreate: + + # Command to run when a recording segment is complete. + # The following environment variables are available: + # * MTX_PATH: path name + # * RTSP_PORT: RTSP server port + # * G1, G2, ...: regular expression groups, if path name is + # a regular expression. + # * MTX_SEGMENT_PATH: segment file path + # * MTX_SEGMENT_DURATION: segment duration + runOnRecordSegmentComplete: + +############################################### +# Path settings + +# Settings in "paths" are applied to specific paths, and the map key +# is the name of the path. +# Any setting in "pathDefaults" can be overridden here. +# It's possible to use regular expressions by using a tilde as prefix, +# for example "~^(test1|test2)$" will match both "test1" and "test2", +# for example "~^prefix" will match all paths that start with "prefix". +paths: + # example: + # my_camera: + # source: rtsp://my_camera + + # Settings under path "all_others" are applied to all paths that + # do not match another entry. + all_others: diff --git a/router/unifi_rc.bash b/router/unifi_rc.bash index 8647513..9e94151 100644 --- a/router/unifi_rc.bash +++ b/router/unifi_rc.bash @@ -5,7 +5,7 @@ # BEFORE: netif . /etc/rc.subr -name=opnsense +name=unifi rcvar=${name}_enable start_cmd="${name}_start" stop_cmd="${name}_stop" @@ -14,12 +14,12 @@ load_rc_config $name tmux_name="unifi" -opnsense_start() { - # /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=YES VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_opnsense.bash" - /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=NO VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_unifi.bash" +unifi_start() { + /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=YES VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_unifi.bash" + # /usr/local/bin/tmux new-session -d -s "$tmux_name" "/usr/bin/env VNC_ENABLE=NO VNC_LISTEN=0.0.0.0:5900 /usr/local/bin/bash /home/talexander/launch_unifi.bash" } -opnsense_status() { +unifi_status() { if /usr/local/bin/tmux has-session -t $tmux_name 2>/dev/null; then echo "$tmux_name is running." else @@ -28,17 +28,17 @@ opnsense_status() { fi } -opnsense_stop() { +unifi_stop() { /usr/local/bin/tmux has-session -t $tmux_name 2>/dev/null && ( /usr/local/bin/tmux kill-session -t $tmux_name sleep 10 bhyvectl --vm=unifi --destroy - # kill `cat /var/run/opnsense.pid` + # kill `cat /var/run/unifi.pid` ) - opnsense_wait_for_end + unifi_wait_for_end } -opnsense_wait_for_end() { +unifi_wait_for_end() { while /usr/local/bin/tmux has-session -t $tmux_name 2>dev/null; do sleep 1 done