support street cameras

This commit is contained in:
Evgeny Zinoviev 2022-05-22 23:38:33 +03:00
parent 02f676029a
commit 225ad92fda
10 changed files with 215 additions and 2 deletions

6
doc/ipcam-streaming.md Normal file
View File

@ -0,0 +1,6 @@
Let's assume IP cameras stream h264 via rtsp.
To `/etc/fstab`:
```
tmpfs /var/ipcamfs tmpfs mode=1755,uid=1000,gid=1000 0 0
```

View File

@ -49,10 +49,15 @@ return [
],
'static' => [
'app.css' => 6,
'app.css' => 8,
'app.js' => 1,
'polyfills.js' => 1,
'modem.js' => 1,
'inverter.js' => 2,
],
'cam_hls_host' => '192.168.1.1',
'cam_list' => [
// fill me with names
]
];

View File

@ -20,7 +20,7 @@ abstract class base_tpl {
public function __construct($templates_dir, $cache_dir) {
global $config;
$cl = get_called_class();
// $cl = get_called_class();
$this->twig = self::twig_instance($templates_dir, $cache_dir, $config['is_dev']);
$this->static_time = time();

View File

@ -49,4 +49,17 @@ class MiscHandler extends RequestHandler
$this->tpl->render_page('pump.twig');
}
public function GET_cams() {
global $config;
$this->tpl->add_external_static('js', 'https://cdn.jsdelivr.net/npm/hls.js@latest');
$this->tpl->set([
'hls_host' => $config['cam_hls_host'],
'cams' => $config['cam_list']
]);
$this->tpl->set_title('Камеры');
$this->tpl->render_page('cams.twig');
}
}

View File

@ -151,3 +151,18 @@
0%, 39%, 100% { opacity: 0; }
40% { opacity: 1; }
}
/* cams page */
.camfeeds {
display: flex;
flex-wrap: wrap;
flex-direction: row;
}
.camfeeds > video {
display: flex;
flex-basis: calc(50% - 20px);
justify-content: center;
flex-direction: column;
width: calc(50% - 10px);
margin: 5px;
}

View File

@ -25,6 +25,7 @@ $router->add('/', 'Misc main');
$router->add('sensors/', 'Misc sensors_page');
$router->add('pump/', 'Misc pump_page');
$router->add('phpinfo/', 'Misc phpinfo');
$router->add('cams/', 'Misc cams');
$route = routerFind($router);

View File

@ -0,0 +1,64 @@
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Главная</a></li>
<li class="breadcrumb-item active" aria-current="page">Камеры</li>
</ol>
</nav>
<div id="videos" class="camfeeds"></div>
<video height="300" id="video"></video>
<script>
function hasFallbackSupport() {
var video = document.createElement('video');
return video.canPlayType('application/vnd.apple.mpegurl');
}
function setupHls(video, name, useHls) {
var src = 'http://{{ hls_host }}/ipcam/'+name+'/live.m3u8';
// hls.js is not supported on platforms that do not have Media Source Extensions (MSE) enabled.
// When the browser has built-in HLS support (check using `canPlayType`), we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video element through the `src` property.
// This is using the built-in support of the plain video element, without using hls.js.
if (useHls) {
var hls = new Hls({
// debug: true,
startPosition: -1,
});
hls.loadSource(src);
hls.attachMedia(video);
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
video.muted = true;
video.play();
});
} else {
video.src = src;
video.addEventListener('canplay', function () {
video.play();
});
}
}
function init() {
let useHls = Hls.isSupported();
if (!useHls && !hasFallbackSupport()) {
alert('Neither HLS nor vnd.apple.mpegurl is not supported by your browser.');
return;
}
var cams = {{ cams|json_encode|raw }};
for (var i = 0; i < cams.length; i++) {
var name = cams[i];
var video = document.createElement('video');
// video.setAttribute('height', '400');
video.setAttribute('id', 'video-'+name);
document.getElementById('videos').appendChild(video);
setupHls(video, name, useHls);
}
}
init();
</script>

View File

@ -17,5 +17,6 @@
<li class="list-group-item"><a href="/inverter/">Инвертор</a></li>
<li class="list-group-item"><a href="/pump/">Насос</a></li>
<li class="list-group-item"><a href="/sensors/">Датчики</a></li>
<li class="list-group-item"><a href="/cams/">Камеры</a></li>
</ul>
</div>

View File

@ -0,0 +1,13 @@
[Unit]
Description=convert rtsp to hls for viewing live camera feeds in browser
After=network-online.target
[Service]
Restart=on-failure
User=user
Group=user
EnvironmentFile=/etc/ipcam-rtsp2hls.conf.d/%i.conf
ExecStart=/home/user/homekit/tools/ipcam-rtsp2hls.sh --name %i --user $USER --password $PASSWORD --ip $IP --port $PORT $ARGS
[Install]
WantedBy=multi-user.target

95
tools/ipcam-rtsp2hls.sh Executable file
View File

@ -0,0 +1,95 @@
#!/bin/bash
PROGNAME="$0"
OUTDIR=/var/ipcamfs # should be tmpfs
PORT=554
NAME=
IP=
USER=
PASSWORD=
DEBUG=0
CHANNEL=1
die() {
echo >&2 "error: $@"
exit 1
}
usage() {
cat <<EOF
usage: $PROGNAME [OPTIONS] COMMAND
Options:
--ip camera IP
--port RTSP port (default: 554)
--name camera name (chunks will be stored under $OUTDIR/{name}/)
--user
--password
--debug
--channel 1|2
EOF
exit
}
validate_channel() {
local c="$1"
case "$c" in
1|2)
:
;;
*)
die "Invalid channel"
;;
esac
}
[ -z "$1" ] && usage
while [[ $# -gt 0 ]]; do
case "$1" in
--ip|--port|--name|--user|--password)
_var=${1:2}
_var=${_var^^}
printf -v "$_var" '%s' "$2"
shift
;;
--debug)
DEBUG=1
;;
--channel)
CHANNEL="$2"
shift
;;
*)
die "Unrecognized argument: $1"
;;
esac
shift
done
[ -z "$IP" ] && die "You must specify camera IP address (--ip)."
[ -z "$PORT" ] && die "Port can't be empty."
[ -z "$NAME" ] && die "You must specify camera name (--name)."
[ -z "$USER" ] && die "You must specify username (--user)."
[ -z "$PASSWORD" ] && die "You must specify username (--password)."
validate_channel "$CHANNEL"
if [ ! -d "${OUTDIR}/${NAME}" ]; then
mkdir "${OUTDIR}/${NAME}" || die "Failed to create ${OUTDIR}/${NAME}!"
fi
if [ "$DEBUG" = "1" ]; then
ffmpeg_args="-v info"
else
ffmpeg_args="-nostats -loglevel error"
fi
ffmpeg $ffmpeg_args -i rtsp://${USER}:${PASSWORD}@${IP}:${PORT}/Streaming/Channels/${CHANNEL} \
-c:v copy -c:a copy -bufsize 1835k \
-pix_fmt yuv420p \
-flags -global_header -hls_time 5 -hls_list_size 6 -hls_wrap 5 \
${OUTDIR}/${NAME}/live.m3u8