summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/ipcam-streaming.md6
-rw-r--r--localwebsite/config.php7
-rw-r--r--localwebsite/engine/tpl.php2
-rw-r--r--localwebsite/handlers/MiscHandler.php13
-rw-r--r--localwebsite/htdocs/assets/app.css15
-rw-r--r--localwebsite/htdocs/index.php1
-rw-r--r--localwebsite/templates-web/cams.twig64
-rw-r--r--localwebsite/templates-web/index.twig1
-rw-r--r--systemd/ipcam-rtsp2hls@.service13
-rwxr-xr-xtools/ipcam-rtsp2hls.sh95
10 files changed, 215 insertions, 2 deletions
diff --git a/doc/ipcam-streaming.md b/doc/ipcam-streaming.md
new file mode 100644
index 0000000..524e0e2
--- /dev/null
+++ b/doc/ipcam-streaming.md
@@ -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
+``` \ No newline at end of file
diff --git a/localwebsite/config.php b/localwebsite/config.php
index 47be2f7..262aeae 100644
--- a/localwebsite/config.php
+++ b/localwebsite/config.php
@@ -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
]
];
diff --git a/localwebsite/engine/tpl.php b/localwebsite/engine/tpl.php
index a12807d..3d18c9a 100644
--- a/localwebsite/engine/tpl.php
+++ b/localwebsite/engine/tpl.php
@@ -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();
diff --git a/localwebsite/handlers/MiscHandler.php b/localwebsite/handlers/MiscHandler.php
index 4f35981..314920a 100644
--- a/localwebsite/handlers/MiscHandler.php
+++ b/localwebsite/handlers/MiscHandler.php
@@ -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');
+ }
+
} \ No newline at end of file
diff --git a/localwebsite/htdocs/assets/app.css b/localwebsite/htdocs/assets/app.css
index cab674e..73e0667 100644
--- a/localwebsite/htdocs/assets/app.css
+++ b/localwebsite/htdocs/assets/app.css
@@ -150,4 +150,19 @@
@keyframes sk-circleFadeDelay {
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;
} \ No newline at end of file
diff --git a/localwebsite/htdocs/index.php b/localwebsite/htdocs/index.php
index 3961c2c..65afc72 100644
--- a/localwebsite/htdocs/index.php
+++ b/localwebsite/htdocs/index.php
@@ -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);
diff --git a/localwebsite/templates-web/cams.twig b/localwebsite/templates-web/cams.twig
new file mode 100644
index 0000000..2963fdb
--- /dev/null
+++ b/localwebsite/templates-web/cams.twig
@@ -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> \ No newline at end of file
diff --git a/localwebsite/templates-web/index.twig b/localwebsite/templates-web/index.twig
index d293cfd..1635459 100644
--- a/localwebsite/templates-web/index.twig
+++ b/localwebsite/templates-web/index.twig
@@ -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> \ No newline at end of file
diff --git a/systemd/ipcam-rtsp2hls@.service b/systemd/ipcam-rtsp2hls@.service
new file mode 100644
index 0000000..5ab205b
--- /dev/null
+++ b/systemd/ipcam-rtsp2hls@.service
@@ -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
diff --git a/tools/ipcam-rtsp2hls.sh b/tools/ipcam-rtsp2hls.sh
new file mode 100755
index 0000000..d52fb8a
--- /dev/null
+++ b/tools/ipcam-rtsp2hls.sh
@@ -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