This project demonstrates how to receive images from this camera and process them into still images and movies using OpenCV and FFMPEG on Ubuntu 20.04 running on a Raspberry Pi model 4b. The camera sends the following JSON message via HTTP POST:
{
"body": [
{
"name": "battery_percent",
"value": "96"
},
{
"name": "5MP_MC",
"frames": 0,
"content_type": "image/jpeg",
"value": ""
}
]
}
The battery data lands at the top of the image browsing page.
For more information about this page see the
Camera Settings Project. The rest of the
data are used to process the value
payload. (It has been elided for brevity. Imagine a very long base64 encoded
string in its place.) The name
field tells us which camera sent the data. If you have multiple cameras simply
give them distinct names and all will be well. The frames
counter lets us know if we should expect a single JPEG
or multiples. Frames are numbered from zero so 0
frames yields one picture. If we get multiples we split them
apart and order them by frame number. If we get the reserved value of 255
that tells us we've received an AVI/MJPEG
movie. If it passes magic number and FFMPEG validation we
convert it to MP4 and save it as a movie. If it does not, we attempt to save as many frames as we can and fallback to
saving frame numbered images. See mjpeg_utils::save_avi_frames
for how FFMPEG is used within OpenCV to accomplish
all this image processing.
I revised the project to build directly on Ubuntu 20.04 (64 bit) for the Raspberry Pi model 4b. If you are interested in the previous version which used Raspberry Pi OS (aka Raspbian) access the repository at commit 511c7b8
If you haven't already:
sudo apt-get install build-essential libcurl-dev libmagic-dev cmake
It's widely known that the hardest part of using OpenCV is getting it and its dependencies installed. Once you get past
that it's a pleasure to use. I built this project on a Raspberry Pi model 4B running the latest (October 2020) Ubuntu
20.04 64 bit. I did that because I wanted the project to be accessible to those on a limited budget. The Pi is
famously affordable and it has more than enough capability for the tasks at hand. That said, if you have access to a
laptop running Ubuntu you might find that a better option. To begin the building,
install Clang 9. BTW: You may be tempted to build with GCC; if you get it to
work you're a better build master than me. After that, install OpenCV and it's dependencies.
Lastly run cmake
in the usual way in the project root folder.
As root, do the following;
- Copy the CGI binary you built (jpeg_catcher) with cmake into
/usr/lib/cgi-bin/
If you renamed the project in CMakeLists.txt the binary will have a new name as well. Be sure to reflect this name inHTTP_HOST_URL
over in the camera project's platformio.ini. - Enable the CGI module.
sudo a2enmod cgi
- Restart Apache
sudo systemctl restart apache2
- As root, edit
/etc/apache2/ports.conf
and addListen 4444
on a line belowListen 80
and save the file.
-1. As root, open /etc/apache2/sites-available/000-default.conf
and paste the following in just below the closing
</VirtualHost>
tag.
<VirtualHost *:4444>
<Directory /var/www/html/motion_camera>
Options +Indexes
AddType image/svg+xml svg svgz
AddEncoding gzip svgz
<IfModule mod_autoindex.c>
IndexOptions IgnoreCase FancyIndexing HTMLTable SuppressHTMLPreamble FoldersFirst VersionSort NameWidth=* DescriptionWidth=* XHTML IconHeight=16 IconWidth=16
IndexIgnore ..
IndexOrderDefault Descending Name
IndexStyleSheet ./fancy-index/style.css
HeaderName ./fancy-index/header.html
ReadmeName ./fancy-index/footer.html
# IGNORE THESE FILES
IndexIgnoreReset ON
IndexIgnore fancy-index
# DEFAULT ICON
DefaultIcon ./fancy-index/icons/file-text.svg
AddIcon ./fancy-index/icons/back.svg ..
AddIcon ./fancy-index/icons/file-directory.svg ^^DIRECTORY^^
# https://github.com/file-icons/source
AddIcon ./fancy-index/icons/file-media.svg .jpg .jpeg
AddIcon ./fancy-index/icons/Video.svg .avi .mp4
# https://upload.wikimedia.org/wikipedia/commons/d/da/Battery-303889.svg
AddIcon ./fancy-index/icons/battery.svg .pwr
AddDescription "MPEG Layer 4 Format" .mp4
AddDescription "Joint Photographics Experts Group" .jpg .jpeg .jpe .jfif
AddDescription "Audio Video Interleave - Motion JPEG" .avi
AddDescription "Camera battery power available" .pwr
</IfModule>
</Directory>
</VirtualHost>
-2. As root, make the directory /var/www/html/motion_camera
and copy the contents of this project's
web_root folder into it.
-3. Fix the ownership, group, and permissions.
sudo mkdir /var/www/html/motion_camera
cd /var/www/html/motion_camera
sudo chown -R www-data:www-data
sudo find . -type f -exec chmod 0644 {} \;
sudo find . -type d -exec chmod 0755 {} \;
-4. When you're done /var/www/html/motion_camera
will contain:
ls -al /var/www/html/motion_camera/fancy-index/
total 72
drwxr-xr-x 3 www-data www-data 4096 Jun 9 09:37 .
drwxr-xr-x 3 www-data www-data 40960 Jun 15 09:06 ..
-rw-r--r-- 1 www-data www-data 66 Jun 5 15:53 footer.html
-rw-r--r-- 1 www-data www-data 295 Jun 5 16:05 header.html
drwxr-xr-x 2 www-data www-data 4096 Jun 8 15:35 icons
-rw-r--r-- 1 www-data www-data 6322 Jun 8 15:48 script.js
-rw-r--r-- 1 www-data www-data 3170 Jun 9 09:37 style.css
-5. You can now test the configuration by pointing your browser to http://your_server:4444/motion_camera
One of the handy things about the design of the CGI interface is that all error messages should be written to the
standard error stream; stderr
. The web server writes these into it's error log; /var/log/apache2/error.log
.
If this CGI encounters an error it will write a useful message about it to this file. To turn off this output edit
debug_output.hpp and comment out DEBUG_OUTPUT
Certainly not. The CGI interface standard is supported by many different HTTP servers. You should have little difficulty adapting this project to work with your chosen web server.
There are also tools you may find useful to you in other projects. For example;
- Http_parser::parse_query_string stores the HTML query string in a
std::map<std::string, std::vector<std::string>>
From this you can create a project that handles HTML GET requests. - Http_parser::parse_multipart_form stores multipart/form-data encoded HTTP message bodies in two
std::map<std::string, std::vector<std::string>>
- one for variables and another for files. From this you can create a project where forms are used to control a robot/embedded system or your Raspberry Pi. To help you get started there is an everything-but-the-kitchen-sink HTML form. Edit the<form>
tag at the top and change theaction
attribute to reflect your server name.
- Thank you to Vestride for the Fancy Index project from which I built the web page in this project.
- Thank you to the OpenCV project. I used one of the samples to learn how to do the image processing in this project.
- Thank you to hjiang for the jsonxx project. I tested several C++ JSON libraries including jsoncpp before deciding to use jsonxx for its simplicity and speed.
- Thank you to Ian Darwin and Christos Zoulas the inventor and maintainer (respectively) of all things magic number. I've been using the file(1) command all my life and was delighted to find the development library for it so easy to use.
If you find a bug please create an issue. If you'd like to contribute please send a pull request.
I used Doxygen to create the project documentation. You can read it here.
The following were helpful references in the development of this project.