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.
- 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()
- 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
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.
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.
- Permissions to socket:
ls -l /run/uwsgi/
srwxrwxrwx 1 pi video 0 May 21 10:12 homeguard.sock
- 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
}
}
- 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
- 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$
- 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
- Permissions of my project's directory:
drwxr-xr-x 11 pi video 4096 May 16 19:49 HomeGuard
- 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.
sudo usermod -a -G video www-data;sudo systemctl restart nginx
pi@raspberrypi:~ $ id www-data uid=33(www-data) gid=33(www-data) groups=33(www-data),27(sudo),44(video)