Files
web-log/container/site/content/posts/wildlife-stream/index.md
T
nicholas 2e9bd0e409
Build & Push Hugo Site Image / Build & Push Image (push) Successful in 9s
Build & Push Hugo Site Image / deploy (push) Successful in 16s
add tags, descriptions to posts (#38)
2026-08-05 16:24:33 -05:00

13 KiB
Raw Blame History

+++ categories = ["software","build"] tags = ["docker"] date = 2024-08-21T18:13:24Z description = "Turning a Nikon Z50 into a high-quality wildlife camera with Frigate, Docker, object detection, recording, and live streaming." draft = false slug = "wildlife-stream" title = "📷 Wildlife Cam Livestream with Frigate + Nikon Z50" author = "nicholas" +++

I want to capture images and video of animals that visit my deck (mostly squirrels, woodpeckers, and finches). Previously, I have used an IP camera to accomplish this, and while satisfying in a technical sense, the images were of too low quality to be of much artistic interest to me. The image that IP cameras produce are typically of very large field of view, which makes the subjects I am interested in appear very small. It has the distinct look of a security camera which is not associated with beauty or visual interest. I will need to use a better camera.

{{< image src="images/apartment-inside.jpg" caption="Apartment view" >}}

Equipment

  • Camera
    • I replaced my IP camera with an interchangeable-lens type camera the Nikon z50, with a Nikkor Z DX 50-250mm lens. The longer focal length range 50-250mm will allow me to place the camera far away from the subject and nearer to the computer that will capture the video and process it. This setup gives me plenty of flexibility, which is useful because the project is more artistic than utilitarian / well-defined and subject to the whims of the aesthetic preferences of the day.
  • Capture Device
    • I need a way to get the video feed to the server so I can process it, so I bought a USB HDMI capture device Elgato Camlink 4k.
  • Dummy Battery
  • I am using a dummy battery that supplies the camera with the same voltage and battery identifier signals as its proprietary battery, basically a 120v power adapter. This allows me to power the camera indefinitely.

The Scene

Here is the scene I am attempting to capture. I have placed a small container on the ledge of the deck railing that I fill each day with a handful of peanuts. This is where the animals come to eat. I have a whole cart of houseplants to contend with that may occlude the view, but I can make it work.

{{< image src="images/hinoki-bonsai.jpg" caption="Squirrel scene" >}}

I have placed the camera something like 15 ft. away from the subject right next to my server. Here is what this looks like:

{{< image src="images/camera-pov-1.jpg" caption="Camera POV" >}}

{{< image src="images/camera-pov-2.jpg" caption="Camera POV 2" >}}

Software

Here I run lsusb to be sure the system recognizes the HDMI capture card. This is successful

lsusb
Bus 002 Device 108: ID 0fd9:007b Elgato Systems GmbH Cam Link 4K
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 002: ID 8087:0aa7 Intel Corp. Wireless-AC 3168 Bluetooth
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Now I see where the video is mounted.

ls /dev/video*
/dev/video0  
/dev/video1

There are two video devices video0 and video1. I will need to determine which is the video feed I am interested in. I will run a Video4Linux v4l2-ctlcommand to discover the capabilities of each device.

device video0

/dev/video0  
    v4l2-ctl --device=/dev/video0 --all
    Driver Info:
        Driver name      : uvcvideo
        Card type        : Cam Link 4K: Cam Link 4K
        Bus info         : usb-0000:00:14.0-2
        Driver version   : 6.1.55
        Capabilities     : 0x84a00001
                Video Capture
                Metadata Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps      : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format
Media Driver Info:
        Driver name      : uvcvideo
        Model            : Cam Link 4K: Cam Link 4K
        Serial           : FX02M2A10475
        Bus info         : usb-0000:00:14.0-2
        Media version    : 6.1.55
        Hardware revision: 0x00000100 (256)
        Driver version   : 6.1.55
Interface Info:
        ID               : 0x03000002
        Type             : V4L Video
Entity Info:
        ID               : 0x00000001 (1)
        Name             : Cam Link 4K: Cam Link 4K
        Function         : V4L2 I/O
        Flags            : default
        Pad 0x01000007   : 0: Sink
          Link 0x02000010: from remote pad 0x100000a of entity 'Extension 3' (Video Pixel Formatter): Data, Enabled, Immutable
Priority: 2
Video input : 0 (Input 1: ok)
Format Video Capture:
        Width/Height      : 1920/1080
        Pixel Format      : 'NV12' (Y/UV 4:2:0)
        Field             : None
        Bytes per Line    : 1920
        Size Image        : 3110400
        Colorspace        : sRGB
        Transfer Function : Rec. 709
        YCbCr/HSV Encoding: Rec. 709
        Quantization      : Default (maps to Limited Range)
        Flags             :
Crop Capability Video Capture:
        Bounds      : Left 0, Top 0, Width 1920, Height 1080
        Default     : Left 0, Top 0, Width 1920, Height 1080
        Pixel Aspect: 1/1
Selection Video Capture: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
Streaming Parameters Video Capture:
        Capabilities     : timeperframe
        Frames per second: 23.976 (27021/1127)
        Read buffers     : 0

User Controls

                     brightness 0x00980900 (int)    : min=0 max=255 step=1 default=128 value=128
                       contrast 0x00980901 (int)    : min=0 max=255 step=1 default=128 value=128
                     saturation 0x00980902 (int)    : min=0 max=255 step=1 default=128 value=128
                            hue 0x00980903 (int)    : min=0 max=255 step=1 default=128 value=128

device video1

/dev/video1
    v4l2-ctl --device=/dev/video1 --all
    Driver Info:
        Driver name      : uvcvideo
        Card type        : Cam Link 4K: Cam Link 4K
        Bus info         : usb-0000:00:14.0-2
        Driver version   : 6.1.55
        Capabilities     : 0x84a00001
                Video Capture
                Metadata Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps      : 0x04a00000
                Metadata Capture
                Streaming
                Extended Pix Format
Media Driver Info:
        Driver name      : uvcvideo
        Model            : Cam Link 4K: Cam Link 4K
        Serial           : FX02M2A10475
        Bus info         : usb-0000:00:14.0-2
        Media version    : 6.1.55
        Hardware revision: 0x00000100 (256)
        Driver version   : 6.1.55
Interface Info:
        ID               : 0x03000005
        Type             : V4L Video
Entity Info:
        ID               : 0x00000004 (4)
        Name             : Cam Link 4K: Cam Link 4K
        Function         : V4L2 I/O
Priority: 2
Format Metadata Capture:
        Sample Format   : 'UVCH' (UVC Payload Header Metadata)
        Buffer Size     : 10240

Both of these outputs indicate they refer to the same device Cam Link 4k, but /dev/video0 looks like the video device node that I can use to access the device's video stream. /dev/video0 lists "Video Capture" under both Capabilities and Device Caps, confirming it can capture video. /dev/video1 lists only "Metadata Capture", which means it is only capturing metadata and not the video itself.

Docker

I will be using docker to run the NVR software (frigate). Most of the configuration file is defaults, but one thing I will need to do is pass the video capture device to the docker container.

services:
  frigate:
    container_name: frigate
    privileged: true
    restart: unless-stopped
    image: ghcr.io/blakeblackshear/frigate:stable
    shm_size: "512mb"
    devices:
      - /dev/video0:/dev/video0
      - /dev/dri/renderD128 # for intel hwaccel
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ./config:/config
      - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "8971:8971" # web interface authenticated
      - "5000:5000" # web interface
      - "8554:8554" # RTSP feeds
      - "8555:8555/tcp" # WebRTC over tcp
      - "8555:8555/udp" # WebRTC over udp
      - "1984:1984" # go2rtc

Frigate Configuration

Frigate requires its own configuration file: /config/config.yml. Using the built-in go2rtc tool I will publish a new video stream of the USB capture device /dev/video0 that I will ingest as a camera in frigate. First I will need some information about the video stream offered by the capture device.

I will use a v4l2-ctl command with the long option --list-formats-ext to display information about the video formats supported by my capture device. The output shows supported resolutions, pixel formats, and framerate. I will need this information to define my camera in frigate configuration.

    v4l2-ctl --device=/dev/video1 --list-formats-ext
    ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture

        [0]: 'YUYV' (YUYV 4:2:2)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.042s (23.976 fps)
        [1]: 'NV12' (Y/UV 4:2:0)
                Size: Discrete 1920x1080
                        Interval: Discrete 0.042s (23.976 fps)

This output pertains to a line I must add to the configuration file

ffmpeg:device?...

Variable Value Description
video dev/video0 USB capture device path
input_format nv12 Color format
video_size 1920x1080 Input resolution
video h264 Video codec
hardware vaapi Hardware acceleration

I have named my stream nikonz50 and defined it as a ffmpeg device using the table of variables above. I configured frigate to detect and record only animals.

go2rtc:
  streams:
    nikonz50:
       ffmpeg:device?video=/dev/video0&input_format=nv12&video_size=1920x1080#video=h264#hardware=vaapi

cameras:
  nikonz50:
    objects:
      track:
        - animal
    ffmpeg:
      inputs:
        - path: rtsp://127.0.0.1:8554/nikonz50
          roles:
            - record
            - detect
      hwaccel_args: preset-vaapi
      input_args: -tag:v hvc1
      
record:
  enabled: true
  events:
    pre_capture: 2
    post_capture: 2
    objects:
      - animal

snapshots:
  enabled: true
  
detect:
  enabled: true
  width: 1920
  height: 1080

detectors:
  ov:
    type: openvino
    device: AUTO
    model:
      path: /openvino-model/ssdlite_mobilenet_v2.xml

model:
  width: 300
  height: 300
  input_tensor: nhwc
  input_pixel_format: bgr
  labelmap_path: /openvino-model/coco_91cl_bkgr.txt
  labelmap:
    15: animal
    16: animal
    17: animal
version: 0.14

Now that I have a docker-compose.yml file and a frigate config.yml file I can start up Frigate and see how it looks. I run the compose command with short option -d (detached mode) to run the container in the background.

sudo docker compose up -d

It works!

{{< image src="images/frigate-ui.PNG" caption="Frigate UI" >}}

Object Detection

Frigate is capable of running object detection on the camera streams. Since my machine is running on an intel CPU, I can configure its object detection to use OpenVino, (Open Visual Inference and Neural Network Optimization). This uses Intel's integrated GPU to offload the work of inference from the CPU to the GPU. This improves performance on my machine a great deal. If I used CPU detector my CPU Intel Pentium Gold G5400 would be pegged at 100% utilization and the detection latency would be abysmal.

{{< image src="images/detect-squirrel.jpg" caption="Detect squirrel" >}}

Recording

I have configured frigate to capture a video each time an animal is detected. Here is one such video.

{{< video src="videos/squirrel.mp4" width="100%"

}}

Improvements

My CPU is my biggest bottleneck. It is possible to increase stream resolution to 2160p but this results in unacceptable CPU utilization. One potential solution is acquiring a secondhand Optiplex Micro or some other small computer dedicated to video streaming. I would not mind if the CPU usage was excessive. Facebook Marketplace has many that contain CPUs that exceed the specs of my current server CPU, all for ~$65. Another option is to upgrade my current CPU, but I do not want it to get any hotter as it hosts all my HDDs. I do not know. I may make an edit to this if I upgrade. The project is just for fun and not serious. 4k stream can wait.

{{< image src="images/usage.PNG" caption="Performance" >}}

Done.

UPDATE 2025-05-01: I have upgraded the streaming computer to a miniature computer