0

I'm building a production server on Raspberry Pi 4. Web app is written in Django. I used uWSGI as an application server and Nginx as a reverse proxy. I followed many tips from Digital Ocean. One of the tasks is to stream frames from USB camera connected to RPi. My inspirations was post from PyImageSearch, but I wanted to do it in Django on production server. My OpenCV version is 4.5.2.

  1. Class responsible for opening the camera looks as follows:
# views.py
# I added some 'lsof' commands to check usage of '/dev/video0'  

class VideoCamera(object):
    def __init__(self):
        lsof1 = subprocess.run(["lsof",  "/dev/video0"])
        print("lsof1 = ", lsof1)
        self.video = cv2.VideoCapture(0)
        lsof2 = subprocess.run(["lsof",  "/dev/video0"])
        print("lsof2 = ", lsof2)
        if not self.video:
            print("Can't instantiate VideoCapture")
        self.video.set(3, 640)
        self.video.set(4, 480)
        output = subprocess.run(["v4l2-ctl", "--list-devices"],  capture_output=True)
        print(output)
        sleep(2.0)
        (self.grabbed, self.frame) = self.video.read()
        if not self.grabbed:
            print("Can't open camera")
        threading.Thread(target=self.update, args=()).start()

    def __del__(self):
        self.video.release()

    def get_frame(self):
        image = self.frame
        _, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()

    def update(self):
        while True:
            (self.grabbed, self.frame) = self.video.read()

  1. This code works fine both on my laptop and on RPi 4, but only on development server of Django:
lsof1 =  CompletedProcess(args=['lsof', '/dev/video0'], returncode=1)
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
python  979   pi  mem    CHR   81,7           402 /dev/video0
python  979   pi   11u   CHR   81,7      0t0  402 /dev/video0
lsof2 =  CompletedProcess(args=['lsof', '/dev/video0'], returncode=0)
CompletedProcess(args=['v4l2-ctl', '--list-devices'], returncode=0, stdout=b'bcm2835-codec-decode (platform:bcm2835-codec):\n\t/dev/video10\n\t/dev/video11\n\t/dev/video12\n\nbcm2835-isp (platform:bcm2835-isp):\n\t/dev/video13\n\t/dev/video14\n\t/dev/video15\n\t/dev/video16\n\nC922 Pro Stream Webcam (usb-0000:01:00.0-1.4):\n\t/dev/video0\n\t/dev/video1\n\n', stderr=b'')
[27/May/2021 19:46:45] "GET /vision/camera-usb-streaming/ HTTP/1.1" 200 363405061

After running on production server (Nginx + uWSGI) I get following message after sudo systemctl status uwsgi:

uwsgi.service - uWSGI Emperor service
   Loaded: loaded (/etc/systemd/system/uwsgi.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2021-05-21 10:12:09 CEST; 58min ago
  Process: 371 ExecStartPre=/bin/bash -c mkdir -p /run/uwsgi; chown pi:video /run/uwsgi (code=exited, status=0/SUCCESS)
 Main PID: 428 (uwsgi)
   Status: "The Emperor is governing 1 vassals"
    Tasks: 13 (limit: 4915)
   CGroup: /system.slice/uwsgi.service
           ├─428 /home/pi/HomeGuard/my_env/bin/uwsgi --emperor /etc/uwsgi/sites
           ├─502 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─560 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─561 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─562 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─563 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─564 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─565 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─566 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─567 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           ├─568 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini
           └─569 /home/pi/HomeGuard/my_env/bin/uwsgi --ini homeguard.ini

May 21 11:07:07 raspberrypi uwsgi[428]: Can't open camera
May 21 11:07:07 raspberrypi uwsgi[428]: Traceback (most recent call last):
May 21 11:07:07 raspberrypi uwsgi[428]:   File "/home/pi/HomeGuard/my_env/lib/python3.7/site-packages/django/utils/text.py", line 300, in compress_sequence
May 21 11:07:07 raspberrypi uwsgi[428]:     for item in sequence:
May 21 11:07:07 raspberrypi uwsgi[428]:   File "/home/pi/HomeGuard/vision/views.py", line 36, in gen
May 21 11:07:07 raspberrypi uwsgi[428]:     frame = camera.get_frame()
May 21 11:07:07 raspberrypi uwsgi[428]:   File "/home/pi/HomeGuard/vision/views.py", line 26, in get_frame
May 21 11:07:07 raspberrypi uwsgi[428]:     _, jpeg = cv2.imencode('.jpg', image)
May 21 11:07:07 raspberrypi uwsgi[428]: cv2.error: OpenCV(4.5.2) /home/pi/opencv/modules/imgcodecs/src/loadsave.cpp:896: error: (-215:Assertion failed) !image.empty() in function 'imencode'
May 21 11:07:07 raspberrypi uwsgi[428]: [pid: 562|app: 0|req: 3/5] 192.168.178.43 () {42 vars in 761 bytes} [Fri May 21 09:07:05 2021] GET /vision/camera-usb-streaming/ => generated 10 bytes in 2034 msecs
  1. Camera can't start to work, probably due to some permissions problem. To use camera given user should be part of the 'video' group. I added users 'pi' and 'www-data' to the 'video' group.

  2. My uWSGI configuration file (homeguard.ini) looks as follows:

[uwsgi]

# Django-related settings
project = homeguard

# user
uid = pi
# the base directory (full path)
base = /home/pi/HomeGuard
chdir = %(base)

# the virtualenv (full path)
home = %(base)/my_env
virtualenv = %(home)
pythonpath = %(base)
env = DJANGO_SETTINGS_MODULE=homeguard.settings

# Django's wsgi file
module = %(project).wsgi:application

# process-related settings
# master
master          = true

# maximum number of worker processes
processes       = 10

# the socket (use the full path to be safe
socket = /run/uwsgi/%(project).sock

# ... with appropriate permissions - may be needed
chown-socket = %(uid):video
chmod-socket = 777
enable-threads = true
# clear environment on exit

vacuum          = true

I set 777 only to check if it was a problem, but it didn't help, and I will change it later.

  1. Permissions to socket:
ls -l /run/uwsgi/
srwxrwxrwx 1 pi video 0 May 21 10:12 homeguard.sock
  1. My Nginx configuration looks as follows:
# configuration of the server
server {
    # the port your site will be served on
    listen 80;
    # the domain name it will serve for
    server_name raspberrypi.local; #substitute your machine's IP address or FQDN
    location = /favicon.ico { access_log off; log_not_found off; }
    charset utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    # Django media
    location /media/  {
        alias /home/pi/HomeGuard/media/;  # your Django project's media files - amend as required
    }

    location /static/ {
        alias /home/pi/HomeGuard/static/; # your Django project's static files - amend as required
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  unix:/run/uwsgi/homeguard.sock;
        include     uwsgi_params; # the uwsgi_params file you installed
    }
}
  1. My uwsgi.service file looks as follows:
[Unit]
Description=uWSGI Emperor service

[Service]
ExecStartPre=/bin/bash -c 'mkdir -p /run/uwsgi; chown pi:video /run/uwsgi'
ExecStart=/home/pi/HomeGuard/my_env/bin/uwsgi --emperor /etc/uwsgi/sites
Restart=always
KillSignal=SIGQUIT
Type=notify
NotifyAccess=all

[Install]
WantedBy=multi-user.target
  1. After running sudo journalctl -u uwsgi command, the log is as follows:
May 21 10:12:08 raspberrypi systemd[1]: Starting uWSGI Emperor service...
May 21 10:12:09 raspberrypi uwsgi[428]: *** Starting uWSGI 2.0.19.1 (32bit) on [Fri May 21 10:12:09 2021] ***
May 21 10:12:09 raspberrypi uwsgi[428]: compiled with version: 8.3.0 on 17 June 2020 09:19:11
May 21 10:12:09 raspberrypi uwsgi[428]: os: Linux-5.10.17-v7l+ #1414 SMP Fri Apr 30 13:20:47 BST 2021
May 21 10:12:09 raspberrypi uwsgi[428]: nodename: raspberrypi
May 21 10:12:09 raspberrypi uwsgi[428]: machine: armv7l
May 21 10:12:09 raspberrypi uwsgi[428]: clock source: unix
May 21 10:12:09 raspberrypi uwsgi[428]: pcre jit disabled
May 21 10:12:09 raspberrypi uwsgi[428]: detected number of CPU cores: 4
May 21 10:12:09 raspberrypi uwsgi[428]: current working directory: /
May 21 10:12:09 raspberrypi uwsgi[428]: detected binary path: /home/pi/HomeGuard/my_env/bin/uwsgi
May 21 10:12:09 raspberrypi uwsgi[428]: uWSGI running as root, you can use --uid/--gid/--chroot options
May 21 10:12:09 raspberrypi uwsgi[428]: *** WARNING: you are running uWSGI as root !!! (use the --uid flag) ***
May 21 10:12:09 raspberrypi uwsgi[428]: *** WARNING: you are running uWSGI without its master process manager ***
May 21 10:12:09 raspberrypi uwsgi[428]: your processes number limit is 60527
May 21 10:12:09 raspberrypi uwsgi[428]: your memory page size is 4096 bytes
May 21 10:12:09 raspberrypi uwsgi[428]: detected max file descriptor number: 1024
May 21 10:12:09 raspberrypi systemd[1]: Started uWSGI Emperor service.
May 21 10:12:09 raspberrypi uwsgi[428]: *** starting uWSGI Emperor ***
May 21 10:12:09 raspberrypi uwsgi[428]: *** has_emperor mode detected (fd: 7) ***
May 21 10:12:09 raspberrypi uwsgi[428]: [uWSGI] getting INI configuration from homeguard.ini
May 21 10:12:09 raspberrypi uwsgi[428]: *** Starting uWSGI 2.0.19.1 (32bit) on [Fri May 21 10:12:09 2021] ***
May 21 10:12:09 raspberrypi uwsgi[428]: compiled with version: 8.3.0 on 17 June 2020 09:19:11
May 21 10:12:09 raspberrypi uwsgi[428]: os: Linux-5.10.17-v7l+ #1414 SMP Fri Apr 30 13:20:47 BST 2021
May 21 10:12:09 raspberrypi uwsgi[428]: nodename: raspberrypi
May 21 10:12:09 raspberrypi uwsgi[428]: machine: armv7l
May 21 10:12:09 raspberrypi uwsgi[428]: clock source: unix
May 21 10:12:09 raspberrypi uwsgi[428]: pcre jit disabled
May 21 10:12:09 raspberrypi uwsgi[428]: detected number of CPU cores: 4
May 21 10:12:09 raspberrypi uwsgi[428]: current working directory: /etc/uwsgi/sites
May 21 10:12:09 raspberrypi uwsgi[428]: detected binary path: /home/pi/HomeGuard/my_env/bin/uwsgi
May 21 10:12:09 raspberrypi uwsgi[428]: chdir() to /home/pi/HomeGuard
May 21 10:12:09 raspberrypi uwsgi[428]: your processes number limit is 60527
May 21 10:12:09 raspberrypi uwsgi[428]: your memory page size is 4096 bytes
May 21 10:12:09 raspberrypi uwsgi[428]: detected max file descriptor number: 1024
May 21 10:12:09 raspberrypi uwsgi[428]: lock engine: pthread robust mutexes
May 21 10:12:09 raspberrypi uwsgi[428]: thunder lock: disabled (you can enable it with --thunder-lock)
May 21 10:12:09 raspberrypi uwsgi[428]: uwsgi socket 0 bound to UNIX address /run/uwsgi/homeguard.sock fd 3
May 21 10:12:09 raspberrypi uwsgi[428]: setuid() to 1000
May 21 10:12:09 raspberrypi uwsgi[428]: Python version: 3.7.3 (default, Jan 22 2021, 20:04:44)  [GCC 8.3.0]
May 21 10:12:09 raspberrypi uwsgi[428]: PEP 405 virtualenv detected: /home/pi/HomeGuard/my_env
May 21 10:12:09 raspberrypi uwsgi[428]: Set PythonHome to /home/pi/HomeGuard/my_env
May 21 10:12:10 raspberrypi uwsgi[428]: Python main interpreter initialized at 0xcb8f20
May 21 10:12:10 raspberrypi uwsgi[428]: python threads support enabled
May 21 10:12:10 raspberrypi uwsgi[428]: your server socket listen backlog is limited to 100 connections
May 21 10:12:10 raspberrypi uwsgi[428]: your mercy for graceful operations on workers is 60 seconds
May 21 10:12:10 raspberrypi uwsgi[428]: mapped 708488 bytes (691 KB) for 10 cores
May 21 10:12:10 raspberrypi uwsgi[428]: *** Operational MODE: preforking ***
May 21 10:12:10 raspberrypi uwsgi[428]: added /home/pi/HomeGuard/ to pythonpath.
May 21 10:12:12 raspberrypi uwsgi[428]: WSGI app 0 (mountpoint='') ready in 2 seconds on interpreter 0xcb8f20 pid: 502 (default $
May 21 10:12:12 raspberrypi uwsgi[428]: *** uWSGI is running in multiple interpreter mode ***
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI master process (pid: 502)
May 21 10:12:12 raspberrypi uwsgi[428]: Fri May 21 10:12:12 2021 - [emperor] vassal homeguard.ini has been spawned
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 1 (pid: 560, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 2 (pid: 561, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: Fri May 21 10:12:12 2021 - [emperor] vassal homeguard.ini is ready to accept requests
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 3 (pid: 562, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 4 (pid: 563, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 5 (pid: 564, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 6 (pid: 565, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 7 (pid: 566, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 8 (pid: 567, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 9 (pid: 568, cores: 1)
May 21 10:12:12 raspberrypi uwsgi[428]: spawned uWSGI worker 10 (pid: 569, cores: 1)
May 21 10:19:30 raspberrypi uwsgi[428]: [pid: 567|app: 0|req: 1/1] 192.168.178.43 () {40 vars in 639 bytes} [Fri May 21 08:19:26$
May 21 10:19:30 raspberrypi uwsgi[428]: announcing my loyalty to the Emperor...
May 21 10:19:30 raspberrypi uwsgi[428]: Fri May 21 10:19:30 2021 - [emperor] vassal homeguard.ini is now loyal
May 21 10:56:45 raspberrypi uwsgi[428]: [pid: 567|app: 0|req: 2/2] 192.168.178.43 () {40 vars in 639 bytes} [Fri May 21 08:56:45$
May 21 11:05:42 raspberrypi uwsgi[428]: [pid: 562|app: 0|req: 1/3] 192.168.178.43 () {40 vars in 639 bytes} [Fri May 21 09:05:41$
May 21 11:05:42 raspberrypi uwsgi[428]: announcing my loyalty to the Emperor...
May 21 11:05:42 raspberrypi uwsgi[428]: Fri May 21 11:05:42 2021 - [emperor] vassal homeguard.ini is now loyal
May 21 11:07:04 raspberrypi uwsgi[428]: [pid: 562|app: 0|req: 2/4] 192.168.178.43 () {42 vars in 730 bytes} [Fri May 21 09:07:04$
May 21 11:07:05 raspberrypi uwsgi[428]: [ WARN:0] global /home/pi/opencv/modules/videoio/src/cap_v4l.cpp (890) open VIDEOIO(V4L2$
May 21 11:07:07 raspberrypi uwsgi[428]: Can't open camera
May 21 11:07:07 raspberrypi uwsgi[428]: Traceback (most recent call last):
May 21 11:07:07 raspberrypi uwsgi[428]:   File "/home/pi/HomeGuard/my_env/lib/python3.7/site-packages/django/utils/text.py", lin$
May 21 11:07:07 raspberrypi uwsgi[428]:     for item in sequence:
May 21 11:07:07 raspberrypi uwsgi[428]:   File "/home/pi/HomeGuard/vision/views.py", line 36, in gen
May 21 11:07:07 raspberrypi uwsgi[428]:     frame = camera.get_frame()
May 21 11:07:07 raspberrypi uwsgi[428]:   File "/home/pi/HomeGuard/vision/views.py", line 26, in get_frame
May 21 11:07:07 raspberrypi uwsgi[428]:     _, jpeg = cv2.imencode('.jpg', image)
May 21 11:07:07 raspberrypi uwsgi[428]: cv2.error: OpenCV(4.5.2) /home/pi/opencv/modules/imgcodecs/src/loadsave.cpp:896: error: $
May 21 11:07:07 raspberrypi uwsgi[428]: [pid: 562|app: 0|req: 3/5] 192.168.178.43 () {42 vars in 761 bytes} [Fri May 21 09:07:05$
  1. Camera is seen in the system:
bcm2835-codec-decode (platform:bcm2835-codec):
        /dev/video10
        /dev/video11
        /dev/video12

bcm2835-isp (platform:bcm2835-isp):
        /dev/video13
        /dev/video14
        /dev/video15
        /dev/video16

C922 Pro Stream Webcam (usb-0000:01:00.0-1.3):
        /dev/video0
        /dev/video1
  1. Permissions of my project's directory:
drwxr-xr-x 11 pi video 4096 May 16 19:49 HomeGuard
  1. When I use 'subprocess' module to run shell command 'v4l2-ctl --list-devices' (class VideoCamera from point 1) I get following message on production server:
CompletedProcess(args=['v4l2-ctl', '--list-devices'], returncode=1, stdout=b'', stderr=b'Failed to open /dev/video0: Permission denied\n')

So it must be something with permissions, but I don't know where to look for.

2
  • Have you added user www-data to the video group? sudo usermod -a -G video www-data;sudo systemctl restart nginx
    – Dougie
    May 26 at 23:36
  • @Dougie Yes, I've added Nginx's user 'www-data' to 'video' group: pi@raspberrypi:~ $ id www-data uid=33(www-data) gid=33(www-data) groups=33(www-data),27(sudo),44(video)
    – Artur
    May 28 at 9:23
0

I decided to re-compile OpenCV with the following flags:

cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \
    -D WITH_TBB=ON \
    -D WITH_V4L=ON \
    -D WITH_FFMPEG=ON \
    -D WITH_GSTREAMER=ON \
    -D ENABLE_VFPV3=ON \
    -D WITH_QT=ON \
    -D WITH_OPENGL=ON \
    -D BUILD_TESTS=OFF \
    -D INSTALL_PYTHON_EXAMPLES=OFF \
    -D OPENCV_ENABLE_NONFREE=ON \
    -D CMAKE_SHARED_LINKER_FLAGS=-latomic \
    -D BUILD_EXAMPLES=OFF ..

The only difference comparing to previous compilations are two flags: WITH_FFMPEG=ON and WITH_GTSTREAMER=ON.
According to documentation these flags are switched ON by default, but I didn't have 'gstreamer' installed. I also used few instruction from Learn OpenCV tutorial on instalation OpenCV on RPi 4 (Step 2):

sudo apt-get -y install libxine2-dev libv4l-dev  
cd /usr/include/linux  
sudo ln -s -f ../libv4l1-videodev.h videodev.h  

And now it works :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.