Skip to content

Instantly share code, notes, and snippets.

@dlueth
Last active March 2, 2021 21:43
Show Gist options
  • Save dlueth/f62a5ec5311d4c63e18221c7ecabe03b to your computer and use it in GitHub Desktop.
Save dlueth/f62a5ec5311d4c63e18221c7ecabe03b to your computer and use it in GitHub Desktop.
Jetson Nano deinterlacing
#!/bin/bash
if [[ $EUID -ne 0 ]]; then
echo "Script must be run as root" 2>&1
exit 1
fi
BASE=$(pwd)
TEMP="$(mktemp -d -t update.XXXXXXXXXX)"
git clone https://git.code.sf.net/p/gentrans/code ${TEMP}
cd ${TEMP}
./autogen.sh && make -j1 && make install
mv /usr/local/lib/gstreamer-1.0/* /usr/lib/aarch64-linux-gnu/gstreamer-1.0/
make uninstall
cd ${BASE}
rm -rf ${TEMP}
# /etc/systemd/system/rtsp-simple-server.service
[Unit]
Description=Simple RTSP Server/Proxy
After=network.target
[Service]
ExecStart=/usr/local/bin/rtsp-simple-server /usr/local/etc/rtsp-simple-server.yml
[Install]
WantedBy=multi-user.target
# /usr/local/etc/rtsp-simple-server.yml
###############################################
# General options
# sets the verbosity of the program; available values are "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: rtsp-simple-server.log
# listen IP. If provided, all listeners will listen on this specific IP.
listenIP:
# timeout of read operations.
readTimeout: 10s
# timeout of write operations.
writeTimeout: 10s
# number of read buffers.
# a higher number allows a higher throughput,
# a lower number allows to save RAM.
readBufferCount: 512
# enable Prometheus-compatible metrics on port 9998.
metrics: no
# enable pprof on port 9999 to monitor performances.
pprof: no
# command to run when a client connects to the server.
# this is terminated with SIGINT when a client disconnects from the server.
# the server port is available in the RTSP_PORT variable.
runOnConnect:
# the restart parameter allows to restart the command if it exits suddenly.
runOnConnectRestart: no
###############################################
# RTSP options
# supported RTSP stream protocols.
# UDP is the most performant, but can cause problems if there's a NAT between
# server and clients, and doesn't support encryption.
# TCP is the most versatile, and does support encryption.
# The handshake is always performed with TCP.
protocols: [udp, tcp]
# encrypt handshake and TCP streams with TLS (RTSPS).
# available values are "no", "strict", "optional".
encryption: no
# port of the TCP/RTSP listener. This is used only if encryption is "no" or "optional".
rtspPort: 8554
# port of the TCP/TLS/RTSPS listener. This is used only if encryption is "strict" or "optional".
rtspsPort: 8555
# port of the UDP/RTP listener. This is used only if "udp" is in protocols.
rtpPort: 8000
# port of the UDP/RTCP listener. This is used only if "udp" is in protocols.
rtcpPort: 8001
# path to the server key. This is used only if encryption is "strict" or "optional".
serverKey: server.key
# path to the server certificate. This is used only if encryption is "strict" or "optional".
serverCert: server.crt
# authentication methods.
authMethods: [basic, digest]
# read buffer size.
# this doesn't influence throughput and shouldn't be touched unless the server
# reports errors about the buffer size.
readBufferSize: 2048
###############################################
# RTMP options
# enable a RTMP listener that allows to receive streams with RTMP.
rtmpEnable: no
# port of the RTMP listener.
rtmpPort: 1935
###############################################
# Path options
# these settings are path-dependent.
# 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".
# the settings under the path "all" are applied to all paths that do not match
# another entry.
paths:
all:
# source of the stream - this can be:
# * record -> the stream is published by a RTSP or RTMP client
# * rtsp://existing-url -> the stream is pulled from another RTSP server
# * rtsps://existing-url -> the stream is pulled from another RTSP server
# * rtmp://existing-url -> the stream is pulled from a RTMP server
# * redirect -> the stream is provided by another path or server
source: record
# if the source is an RTSP URL, this is the protocol that will be used to
# pull the stream. available options are "automatic", "udp", "tcp".
# the tcp protocol can help to overcome the error "no UDP packets received recently".
sourceProtocol: automatic
# if the source is an RTSP or RTMP 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
# if the source is "redirect", this is the RTSP URL which clients will be
# redirected to.
sourceRedirect:
# fallback stream to redirect clients to when nobody is publishing to this path.
# this can be a relative path (i.e. /otherstream) or an absolute RTSP URL.
fallback:
# username required to publish.
# sha256-hashed values can be inserted with the "sha256:" prefix.
publishUser:
# password required to publish.
# sha256-hashed values can be inserted with the "sha256:" prefix.
publishPass:
# ips or networks (x.x.x.x/24) allowed to publish.
publishIps: []
# username required to read.
# sha256-hashed values can be inserted with the "sha256:" prefix.
readUser:
# password required to read.
# sha256-hashed values can be inserted with the "sha256:" prefix.
readPass:
# ips or networks (x.x.x.x/24) allowed to read.
readIps: []
# command to run when this path is initialized.
# this can be used to publish a stream and keep it always opened.
# this is terminated with SIGINT when the program closes.
# the path name is available in the RTSP_PATH variable.
# the server port is available in the RTSP_PORT variable.
runOnInit:
# the restart parameter allows to restart the command if it exits suddenly.
runOnInitRestart: no
# command to run when this path is requested.
# this can be used to publish a stream on demand.
# this is terminated with SIGINT when the path is not requested anymore.
# the path name is available in the RTSP_PATH variable.
# the server port is available in the RTSP_PORT variable.
runOnDemand:
# the restart parameter allows to restart the command if it exits suddenly.
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 runOnDemand command will be closed when there are no
# readers connected and this amount of time has passed.
runOnDemandCloseAfter: 10s
# command to run when a client starts publishing.
# this is terminated with SIGINT when a client stops publishing.
# the path name is available in the RTSP_PATH variable.
# the server port is available in the RTSP_PORT variable.
runOnPublish:
# the restart parameter allows to restart the command if it exits suddenly.
runOnPublishRestart: no
# command to run when a clients starts reading.
# this is terminated with SIGINT when a client stops reading.
# the path name is available in the RTSP_PATH variable.
# the server port is available in the RTSP_PORT variable.
runOnRead:
# the restart parameter allows to restart the command if it exits suddenly.
runOnReadRestart: no
pro7:
runOnDemand: /usr/local/sbin/stream.start -i 232.0.10.120 -c $RTSP_PATH -a ac3
runOnDemandRestart: yes
# /usr/local/sbin/stream.dump
#!/usr/bin/env python3
import socket
import sys
import time
import struct
import argparse
import signal
import errno
if not hasattr(socket, 'IP_MULTICAST_TTL'):
setattr(socket, 'IP_MULTICAST_TTL', 33)
if not hasattr(socket, 'IP_ADD_SOURCE_MEMBERSHIP'):
setattr(socket, 'IP_ADD_SOURCE_MEMBERSHIP', 39)
class Unbuffered(object):
def __init__(self, stream):
self.stream = stream
def write(self, data):
self.stream.buffer.write(data)
self.stream.flush()
def writelines(self, datas):
self.stream.writelines(datas)
self.stream.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
sys.stdout = Unbuffered(sys.stdout)
def main():
eseq = 0
flag = 0
buf = ''
sock = get_socket()
while True:
try:
data = bytearray(b" " * 2048)
size = sock.recv_into(data)
if data and not buf:
sys.stderr.write('Start capturing\n')
buf = data[:size]
except socket.timeout:
buf = ''
sock.close()
sys.stderr.write('No data, reconnecting...\n')
sock = get_socket()
continue
except socket.error as e:
if e.errno != errno.EINTR:
raise
else:
continue
if buf[0] == 71: # UDP 0x47
data = buf
else: # RTP
header_size = 12 + 4 * (buf[0] & 16)
seq = (buf[2] << 8) + buf[3]
if not flag:
eseq = seq
flag = 1
if eseq != seq:
sys.stderr.write('Network congestion - expected %d, received %d\n' % (eseq, seq))
eseq = seq
eseq += 1
if eseq > 65535:
eseq = 0
data = buf[header_size:]
sys.stdout.write(data)
def signal_handler(_signal, _frame):
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
def get_socket():
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if args.receiver_address:
imr = (socket.inet_pton(socket.AF_INET, args.ip_address) +
socket.inet_pton(socket.AF_INET, "0.0.0.0") +
socket.inet_pton(socket.AF_INET, args.receiver_address))
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_SOURCE_MEMBERSHIP, imr)
else:
mreq = struct.pack(">4sl", socket.inet_aton(args.ip_address), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
sock.bind((args.ip_address, args.port))
sock.settimeout(10)
except socket.error as e:
sys.stderr.write(e.strerror + ', waiting 30s\n')
time.sleep(30)
return get_socket()
return sock
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Dumpstream - works with RTP and UDP streams')
parser.add_argument('-a', '--ip_address', help='ip address')
parser.add_argument('-r', '--receiver_address', help='receiver address')
parser.add_argument('-p', '--port', help='port', type=int)
args = parser.parse_args()
date_format = '%Y%m%d-%H'
sys.stderr.write('Dumpstream\n')
sys.stderr.write('Usage: %s@%s:%d\n' % (args.receiver_address, args.ip_address, args.port))
main()
# /usr/local/sbin/stream.start
#!/usr/bin/env bash
set -e
COLOR_NOTICE='\e[95m'
COLOR_ERROR='\e[91m'
COLOR_NONE='\e[39m'
while getopts ":i:c:a:" options; do
case "${options}" in
i)
IP="${OPTARG}"
;;
c)
CHANNEL="${OPTARG}"
;;
a)
AUDIO="${OPTARG}"
;;
esac
done
logDefault()
{
echo -e "$1"
}
logNotice()
{
echo -e "${COLOR_NOTICE}$1${COLOR_NONE}"
}
logError()
{
echo -e "${COLOR_ERROR}$1${COLOR_NONE}" 1>&2
}
if [[ -z "${IP}" ]]; then
logError "Usage: start.stream -i [ip] -c [channel] [-a [mp2|ac3]]"
exit 1
fi
if [[ -z "${CHANNEL}" ]]; then
logError "Usage: start.stream -i [ip] -c [channel] [-a [mp2|ac3]]"
exit 1
fi
if [[ -z "${AUDIO}" ]]; then
AUDIO="mp2"
fi
if [[ "${AUDIO}" == "mp2" ]]; then
AUDIO_CAPS="audio/mpeg, stream-format=byte-stream"
AUDIO_PARSER="mpegaudioparse"
AUDIO_PARSER="mpegaudioparse ! mpg123audiodec ! queue ! audioconvert ! avenc_mp2"
fi
if [[ "${AUDIO}" == "ac3" ]]; then
AUDIO_CAPS="audio/x-ac3, stream-format=byte-stream"
AUDIO_PARSER="ac3parse"
# AUDIO_PARSER="ac3parse ! a52dec mode=2 ! queue ! audiorate ! audioconvert ! avenc_mp2"
fi
teardown() {
for child in $(ps -o pid= --ppid $$); do
kill -0 ${child} 2>/dev/null && kill -TERM ${child}
done
exit 0
}
trap 'teardown' TERM INT HUP
python3 /usr/local/sbin/stream.dump -a ${IP} -p 10000 -r 87.141.215.251 | \
gst-launch-1.0 -e \
rtspclientsink name=out location=rtsp://127.0.0.1:8554/${CHANNEL} latency=1000 udp-buffer-size=5242880 do-rtsp-keep-alive=0 \
fdsrc \
! queue \
! video/mpegts \
! tsparse set-timestamps=true \
! tsdemux name=in \
in. \
! queue \
! video/x-h264, stream-format=byte-stream \
! h264parse config-interval=1 \
! nvv4l2decoder \
! nvvidconv interpolation-method=5 \
! 'video/x-raw, format=(string)I420, width=(int)720, height=(int)576' \
! deinterlace mode=3 method=0 fields=0 \
! entranshqdn3d luma-spatial=2 chroma-spatial=2 luma-temp=1 chroma-temp=1 \
! nvvidconv interpolation-method=5 \
! 'video/x-raw(memory:NVMM), format=(string)RGBA, width=(int)720, height=(int)612' \
! nvvidconv interpolation-method=5 \
! 'video/x-raw, format=(string)I420, width=(int)720, height=(int)576' \
! entransxsharpen strength=128 threshold=6 \
! nvvidconv interpolation-method=5 \
! 'video/x-raw(memory:NVMM), format=(string)I420, width=(int)720, height=(int)576' \
! nvv4l2h264enc insert-aud=1 iframeinterval=25 preset-level=1 profile=4 control-rate=1 EnableTwopassCBR=1 bitrate=3500000 insert-sps-pps=1 idrinterval=25 insert-vui=1 bit-packetization=1 EnableMVBufferMeta=1 \
! video/x-h264, stream-format=byte-stream \
! h264parse config-interval=1 \
! queue \
! out.sink_0 \
in. \
! queue \
! ${AUDIO_CAPS} \
! ${AUDIO_PARSER} \
! queue \
! out.sink_1 \
> /dev/null &
wait
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment