Skip to content

Commit

Permalink
Merge pull request #85 from roflcoopter/dev
Browse files Browse the repository at this point in the history
1.7.0
  • Loading branch information
roflcoopter authored Nov 19, 2020
2 parents 5012060 + 2ae07aa commit ed12029
Show file tree
Hide file tree
Showing 23 changed files with 1,160 additions and 553 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ RUN apt-get update && \
curl \
git \
cmake \
tzdata \
# VAAPI drivers for Intel hardware accel
libva-drm2 libva2 i965-va-driver vainfo \
# dlib Optimizations
Expand Down
141 changes: 122 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Builds are tested and verified on the following platforms:
- [Getting started](#getting-started)
- [Configuration Options](#configuration-options)
- [Cameras](#cameras)
- [Substream](#substream)
- [Camera motion detection](#camera-motion-detection)
- [Mask](#mask)
- [Camera object detection](#camera-object-detection)
Expand Down Expand Up @@ -92,6 +93,8 @@ Choose the appropriate docker container for your machine. Builds are published t
I do not recommend using an RPi unless you have a Google Coral EdgeTPU, the CPU is not fast enough and you might run out of memory.
To make use of hardware accelerated decoding/encoding you might have to increase the allocated GPU memory.\
To do this edit ```/boot/config.txt``` and set ```gpu_mem=256``` and then reboot.

I also recommend configuring a [substream](#substream) if you plan on running Viseron on an RPi.
</details>


Expand Down Expand Up @@ -234,7 +237,7 @@ The command is built like this: \
| port | int | **required** | any integer | Port for the camera stream |
| username | str | optional | any string | Username for the camera stream |
| password | str | optional | any string | Password for the camera stream |
| path | str | optional | any string | Path to the camera stream, eg ```/Streaming/Channels/101/``` |
| path | str | **optional** | any string | Path to the camera stream, eg ```/Streaming/Channels/101/``` |
| width | int | optional | any integer | Width of the stream. Will use OpenCV to get this information if not given |
| height | int | optional | any integer | Height of the stream. Will use OpenCV to get this information if not given |
| fps | int | optional | any integer | FPS of the stream. Will use OpenCV to get this information if not given |
Expand All @@ -244,9 +247,10 @@ The command is built like this: \
| codec | str | optional | any supported decoder codec | FFMPEG video decoder codec, eg ```h264_cuvid``` |
| rtsp_transport | str | ```tcp``` | ```tcp```, ```udp```, ```udp_multicast```, ```http``` | Sets RTSP transport protocol. Change this if your camera doesnt support TCP |
| filter_args | list | optional | a valid list of FFMPEG arguments | See source code for default arguments |
| substream | dictionary | optional | see [Substream config](#substream) | Substream to perform image processing on |
| motion_detection | dictionary | optional | see [Camera motion detection config](#camera-motion-detection) | Overrides the global ```motion_detection``` config |
| object_detection | dictionary | optional | see [Camera object detection config](#camera-object-detection) below | Overrides the global ```object_detection``` config |
| zones | list | optional | see [Zones config](#zones) below | Allows you to specify zones to further filter detections |
| object_detection | dictionary | optional | see [Camera object detection config](#camera-object-detection) | Overrides the global ```object_detection``` config |
| zones | list | optional | see [Zones config](#zones) | Allows you to specify zones to further filter detections |
| publish_image | bool | false | true/false | If enabled, Viseron will publish an image to MQTT with drawn zones, objects, motion and masks.<br><b>Note: this will use some extra CPU and should probably only be used for debugging</b> |
| ffmpeg_loglevel | str | optional | ```quiet```, ```panic```, ```fatal```, ```error```, ```warning```, ```info```, ```verbose```, ```debug```, ```trace``` | Sets the loglevel for ffmpeg.<br> Should only be used in debugging purposes. |
| ffmpeg_recoverable_errors | list | optional | a list of strings | ffmpeg sometimes print errors that are not fatal.<br>If you get errors like ```Error starting decoder pipe!```, see below for details. |
Expand Down Expand Up @@ -293,6 +297,38 @@ ffmpeg_recoverable_errors:

---

### Substream
| Name | Type | Default | Supported options | Description |
| -----| -----| ------- | ----------------- |------------ |
| path | str | **required** | any string | Path to the camera substream, eg ```/Streaming/Channels/102/``` |
| width | int | optional | any integer | Width of the stream. Will use FFprobe to get this information if not given |
| height | int | optional | any integer | Height of the stream. Will use FFprobe to get this information if not given |
| fps | int | optional | any integer | FPS of the stream. Will use FFprobe to get this information if not given |
| input_args | list | optional | a valid list of FFMPEG arguments | See source code for default arguments |
| hwaccel_args | list | optional | a valid list of FFMPEG arguments | FFMPEG decoder hardware acceleration arguments |
| codec | str | optional | any supported decoder codec | FFMPEG video decoder codec, eg ```h264_cuvid``` |
| rtsp_transport | str | ```tcp``` | ```tcp```, ```udp```, ```udp_multicast```, ```http``` | Sets RTSP transport protocol. Change this if your camera doesnt support TCP |
| filter_args | list | optional | a valid list of FFMPEG arguments | See source code for default arguments |

Using the substream is a great way to reduce the system load from FFmpeg.\
When configured, two FFmpeg processes will spawn:\
\- One that reads the main stream and creates segments for recordings. Codec ```-c:v copy``` is used so practically no resources are used.\
\- One that reads the substream and pipes frames to Viseron for motion/object detection.

To really benefit from this you should reduce the framerate of the substream to match the lowest interval set for either motion or object detection.\
As an example:
```yaml
motion_detection:
interval: 0.5
object_detection:
interval: 1
```
The optimal FPS for this config would be 2, since it would output a frame every 0.5 seconds for the motion detector to consume.
You should also change the resolution to something lower than the main stream to benefit from this.
---
### Camera motion detection
| Name | Type | Default | Supported options | Description |
| -----| -----| ------- | ----------------- |------------ |
Expand Down Expand Up @@ -614,39 +650,71 @@ By using a running average, the "background" image will adjust to daylight, stat
| timeout | int | 10 | any integer | Number of seconds to record after all events are over |
| retain | int | 7 | any integer | Number of days to save recordings before deleting them |
| folder | path | ```/recordings``` | path to existing folder | What folder to store recordings in |
| segments_folder | path | ```/segments``` | any path | What folder to store ffmpeg segments in |
| extension | str | ```mp4``` | a valid video file extension | The file extension used for recordings. I don't recommend changing this |
| global_args | list | optional | a valid list of FFMPEG arguments | See source code for default arguments |
| hwaccel_args | list | optional | a valid list of FFMPEG arguments | FFMPEG encoder hardware acceleration arguments |
| codec | str | optional | any supported decoder codec | FFMPEG video encoder codec, eg ```h264_nvenc``` |
| filter_args | list | optional | a valid list of FFMPEG arguments | FFMPEG encoder filter arguments |
| thumbnail | dictionary | optional | see [Thumbnail](#thumbnail) | Options for the thumbnail created on start of a recording |
| logging | dictionary | optional | see [Logging](#logging) | Overrides the global log settings for the recorder. <br>This affects all logs named ```lib.recorder.<camera name>``` |

A default ffmpeg encoder command is generated, which varies a bit depending on the Docker container you use,
Viseron uses [ffmpeg segments](https://www.ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment) to handle recordings.\
This means Viseron will write small 5 second segments of the stream to disk, and in case of any recording starting, Viseron will find the appropriate segments and concatenate them together.\
The reason for using segments instead of just starting the recorder on an event, is to support to the ```lookback``` feature which makes it possible to record *before* an event actually happened.

<details>
<summary>For Nvidia GPU support in the <b>roflcoopter/viseron-cuda</b> image</summary>
<summary>The default concatenation command</summary>

```
ffmpeg -hide_banner -loglevel panic -f rawvideo -pix_fmt nv12 -s:v <width>x<height> -r <fps> -i pipe:0 -y -c:v h264_nvenc <file>
ffmpeg -hide_banner -loglevel error -y -protocol_whitelist file,pipe -f concat -safe 0 -i - -c:v copy <outfile.mp4>
```
</details>
If you want to re-encode the video you can choose ```codec```, ```filter_args``` and optionally ```hwaccel_args```.\
To place the segments in memory instead of writing to disk, you can mount a tmpfs disk in the container.
<details>
<summary>For VAAPI support in the <b>roflcoopter/viseron-vaapi</b> image</summary>
<summary>Example tmpfs configuration</summary>
```
ffmpeg -hide_banner -loglevel panic -hwaccel vaapi -vaapi_device /dev/dri/renderD128 -f rawvideo -pix_fmt nv12 -s:v <width>x<height> -r <fps> -i pipe:0 -y -c:v h264_vaapi -vf "format=nv12|vaapi,hwupload" <file>
```
</details>
Example Docker command
<details>
<summary>For RPi3 in the <b>roflcoopter/viseron-rpi</b> image</summary>
```bash
docker run --rm \
-v <recordings path>:/recordings \
-v <config path>:/config \
-v /etc/localtime:/etc/localtime:ro \
--tmpfs /tmp \
--name viseron \
roflcoopter/viseron:latest
```
Example docker-compose
```yaml
version: "2.4"

services:
viseron:
image: roflcoopter/viseron:latest
container_name: viseron
volumes:
- <recordings path>:/recordings
- <config path>:/config
- /etc/localtime:/etc/localtime:ro
tmpfs:
- /tmp
```
ffmpeg -hide_banner -loglevel panic -f rawvideo -pix_fmt nv12 -s:v <width>x<height> -r <fps> -i pipe:0 -y -c:v h264_omx <file>
config.yaml
```yaml
recorder:
segments_folder: /tmp
```
</details>
This means that you do **not** have to set ```hwaccel_args``` *unless* you have a specific need to change the default command (say you need to change ```h264_nvenc``` to ```hevc_nvenc```)
### Thumbnail
| Name | Type | Default | Supported options | Description |
| -----| -----| ------- | ----------------- |------------ |
| save_to_disk | boolean | False | True/False | If set to true, the thumbnail that is created on start of recording is saved to ```{folder}/{camera_name}/latest_thumbnail.jpg``` |
| send_to_mqtt | boolean | False | True/False | If set to true, the thumbnail that is created on start of recording is sent over MQTT. |

The default location for the thumbnail if ```save_to_disk: true``` is ```/recordings/{camera_name}/latest_thumbnail.jpg```

---

Expand Down Expand Up @@ -725,6 +793,24 @@ The folder structure of the faces folder is very strict. Here is an example of t
### Topics for each camera
<div style="margin-left: 1em;">

#### Camera status:
<div style="margin-left: 1em;">
<details>
<summary>Topic: <b><code>{client_id}/{mqtt_name from camera config}/sensor/status/state</b></code></summary>
<div style="margin-left: 1em;">
A JSON formatted payload is published to this topic to indicate the current status of the camera

```json
{"state": "scanning_for_objects", "attributes": {"last_recording_start": <timestamp>, "last_recording_end": <timestamp>}}
```

Possible values in ```state```: ```recording/scanning_for_motion/scanning_for_objects```

</div>
</details>
</details>
</div>

#### Camera control:
<div style="margin-left: 1em;">
<details>
Expand Down Expand Up @@ -766,6 +852,17 @@ The folder structure of the faces folder is very strict. Here is an example of t
</details>
</div>

<div style="margin-left: 1em;">
<details>
<summary>Topic: <b><code>{client_id}/{mqtt_name from camera config}/camera/latest_thumbnail/image</b></code></summary>
<div style="margin-left: 1em;">

An image is published to this topic on the start of a recording if ```send_to_mqtt``` under ```recorder``` is set to ```true```.\
The image is the same as the thumbnail saved along with the recording.
</div>
</details>
</div>

#### [Object detection](#camera-object-detection):
<div style="margin-left: 1em;">
<details>
Expand Down Expand Up @@ -880,9 +977,15 @@ All MQTT topics are largely inspired by Home Assistants way of organizing entiti
Viseron integrates into Home Assistant using MQTT discovery and is enabled by default if you configure MQTT.\
Viseron will create a number of entities depending on your configuration.

**Camera entity**\
A camera entity will be created for each camera.\
Used to debug zones, masks, objects and motion.
**Cameras**\
A variable amount of cameras will be created based on your configuration.
1) A camera entity to debug zones, masks, objects and motion.\
Images are only sent to this topic if ```publish_image: true```
2) If ```send_to_mqtt``` under ```recorder``` is set to ```true``` , a camera entity named ```camera.{client_id from mqtt config}_{mqtt_name from camera config}_latest_thumbnail``` is created

**Sensor**\
A sensor entity is created for each camera which indicates the status of Viseron.
The state is set to ```recording```, ```scanning_for_motion``` or ```scanning_for_objects``` depending on the situation.

**Binary Sensors**\
A variable amount of binary sensors will be created based on your configuration.
Expand Down
5 changes: 4 additions & 1 deletion docker/cuda/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
FROM roflcoopter/opencv-cuda:1.0 as opencv
FROM nvidia/cuda:10.2-cudnn7-devel-ubuntu18.04
FROM nvidia/cuda:11.1-cudnn8-devel-ubuntu18.04

COPY --from=opencv /usr/local /usr/local

Expand All @@ -10,6 +10,7 @@ ENV PATH=/usr/local/ffmpeg/bin:$PATH
# Specify FFMPEG library folder
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/ffmpeg/lib

ENV DEBIAN_FRONTEND="noninteractive"
RUN apt-get update && apt-get install -y --no-install-recommends \
wget gnupg2 ca-certificates && \
wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB && \
Expand All @@ -28,6 +29,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
python3-dev \
git \
wget \
tzdata \
# OpenCV runtime deps
libjpeg-dev libpng-dev libtiff-dev \
# # OpenCV video I/O
Expand Down Expand Up @@ -142,6 +144,7 @@ ENV VISERON_OPENCL_SUPPORTED=true
ENV VISERON_RASPBERRYPI3=false

VOLUME /recordings
RUN mkdir /segments && chmod a+rwx /segments

WORKDIR /src/viseron
COPY ./src /src/viseron/
Expand Down
12 changes: 9 additions & 3 deletions docker/cuda/Dockerfile.ffmpeg
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# docker run --runtime=nvidia -ti --rm --tmpfs /tmp roflcoopter/ffmpeg-cuda:1.0 bash
FROM nvidia/cuda:10.2-devel as ffmpeg
FROM nvidia/cuda:11.1-devel-ubuntu18.04

ENV DEBIAN_FRONTEND="noninteractive"
RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \
autogen \
Expand Down Expand Up @@ -38,22 +39,27 @@ RUN git clone https://github.com/intel/libva && \

# NVIDIA Codec Headers are not included since FFMPEG version 4.0
WORKDIR /usr/local/src
RUN git clone --depth 1 --branch n9.1.23.1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git \
RUN git clone --depth 1 --branch n10.0.26.1 https://git.videolan.org/git/ffmpeg/nv-codec-headers.git \
&& cd nv-codec-headers \
&& make \
&& make install \
&& cd .. \
&& rm -rf nv-codec-headers

# ffmpeg checks for nvcc using compute_30, which is not supported in CUDA 11.
# Lowest supported compute architecture is compute_52 so we patch this here.
# Architecture information is found here: https://en.wikipedia.org/wiki/CUDA (5.2=compute_52)
RUN git clone --depth 1 --branch n4.3 https://github.com/ffmpeg/ffmpeg ffmpeg && \
cd ffmpeg && \
sed -i -e 's/compute_30/compute_52/g' ./configure && \
sed -i -e 's/sm_30/sm_52/g' ./configure && \
./configure \
--enable-cuda \
--enable-cuvid \
--enable-nvenc \
--enable-nonfree \
--enable-libnpp \
--enable-cuda-sdk \
--enable-cuda-nvcc \
--enable-filter=scale_cuda \
--enable-filter=thumbnail_cuda \
--enable-vaapi \
Expand Down
5 changes: 3 additions & 2 deletions docker/cuda/Dockerfile.opencv
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
# docker run --runtime=nvidia -ti --rm opencv-gpu bash
FROM roflcoopter/ffmpeg-cuda:1.0 as ffmpeg

FROM nvidia/cuda:10.2-cudnn7-devel-ubuntu18.04
FROM nvidia/cuda:11.1-cudnn8-devel-ubuntu18.04

COPY --from=ffmpeg /usr/local/ffmpeg /usr/local/ffmpeg

ENV DEBIAN_FRONTEND="noninteractive"
RUN apt-get clean && apt-get update && apt-get install -y \
# Basic deps
build-essential cmake unzip pkg-config wget \
Expand All @@ -26,7 +27,7 @@ RUN apt-get clean && apt-get update && apt-get install -y \
# OpenCV suports CC Arch 5.3 and higher
ENV CUDA_ARCH_BIN "53 60 61 70"
ENV CUDA_ARCH_PTX "70"
ENV OPENCV_VERSION=master
ENV OPENCV_VERSION="4.5.0"

RUN mkdir -p /tmp && \
cd /tmp && \
Expand Down
1 change: 1 addition & 0 deletions docker/rpi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ RUN apt-get update && \
wget \
cmake \
git \
tzdata \
# OpenCV runtime deps
libjpeg-dev libpng-dev libtiff-dev \
# OpenCV video I/O
Expand Down
1 change: 1 addition & 0 deletions docker/vaapi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
unzip \
xz-utils \
tzdata \
# Intel MKL
intel-mkl-64bit-2020.3-111 \
# ffmpeg
Expand Down
16 changes: 16 additions & 0 deletions src/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@
]
CAMERA_HWACCEL_ARGS = []
CAMERA_OUTPUT_ARGS = ["-f", "rawvideo", "-pix_fmt", "nv12", "pipe:1"]
CAMERA_SEGMENT_DURATION = 5
CAMERA_SEGMENT_ARGS = [
"-f",
"segment",
"-segment_time",
str(CAMERA_SEGMENT_DURATION),
"-segment_format",
"mp4",
"-reset_timestamps",
"1",
"-strftime",
"1",
"-c",
"copy",
"-an",
]

ENCODER_CODEC = ""

Expand Down
Loading

0 comments on commit ed12029

Please sign in to comment.