There are a number of blogs purporting to explain how to stream the video output of a web camera connected to a Raspberry Pi. As is often the case, quite a few are out of date. That is the fate of all instructions, things change. So I cannot say how long this post, showing how I added MJPG-Streamer to my home automation server, will be useful.
Table of Contents
- Hardware
- Software
- Security
- Other Clients: VLC, Kodi, and Domoticz
- A New Script
- Remote Start and Stop
- Slow Camera
- Downloads
Hardware
A Raspberry Pi Model B+ v1.2 is hosting my home automation system based on Domoticz. This is the fourth iteration (July 2014) of the initial single board computer from the Raspberry Pi Foundation. It uses a Broadcom BCM2835 system on a chip (SoC) along with 512 MB of on board RAM shared between the CPU (ARM1176jZF-S: 32 bit, single core, 700 MHZ, ARM v6) and the GPU (Broadcom BCM2837). Since the Raspberry Pi is operating as a headless server, the minimum 16 MB of RAM is allocated to the GPU. This was the first board to have four USB 2.0 ports and to use a microSD card for storage. While there is a 10/100 Mbit/s Ethernet port on the board, a WiFi dongle is used for connection to the local area network.
The operating system is Raspbian Jessie a version
of Debian 8 tweaked for the Raspberry Pi. There is a
newer version available but there were problems associated with the
. So I had to use last version of Jessie Lite which
is still available from the Raspberry Pi Foundation. libssl1
and libssl-dev
libraries when trying to
install Domoticz with Stretch (Debian 9). I could not
surmount them. It does seem that this known to the developers of Domoticz, see SSL
protocol issue #2062
However, I ran into problems with the secure connection of the video stream as described below. For some reason, I can get snapshots through the secure lighttpd proxy server but not the video stream. If I ever solve this mystery, I will update this post.
The camera is a
Logitech HD Webcam C270 bought a number of years ago for use with
a portable computer. It is capable of streaming video at a resolution of 720p
(1280x720 pixels which is a 16:9 aspect ratio). The producer does not provide
Linux drivers for the camera, but it does work with a humble Raspberry Pi
because of V4L2 (Video4Linux version 2).
After plugging the camera in a USB port, and waiting a short while, it shows up as a USB device.
Video4Linux is included in Raspbian so we can use it to ascertain which video formats the webcam supports.
The fact that the C270 supports MJPG format is very good. It means that the camera itself compresses each JPG image (frame) before passing it on to the computer over the USB link. Not all webcams support the MJPG format. In that case, the Raspberry Pi will have to perform compression of the images before streaming them over HTTP. This task overwhelms the single core Raspberry Pi Model B+. More on that later.
Software
The obvious source for instructions on installing MJPG-Streamer is the repository for the package. The latter is no longer hosted on SourceForge. I installed a forked version by Liam Jackson (I hope I am not making a mistake with the name) on github. Mr Jackson has simplified installation considerably.
That completes the basic installation. The executable is placed in the
/usr/local/bin/
directory which is included in the system
path environment variable. The input and output plugins
are in the /usr/local/lib/mjpg-streamer
directory, the
built-in web server pages are in the /usr/local/share/mjpg-streamer/www
directory.
I did not bother setting up the LD_LIBRARY_PATH environment variable as
described in the README
page. Starting
mjpg_streamer require defining one or more input
plugin and an output plugin. This is explained in the Usage paragraph. I used the input_uvc
and the output_http
plugings.
Putting those two together, I started the streamer with the
following command. Streaming is to be done at the full resolution of the
camera but at a relatively slow 10 frames per second. If the
-n
option is not specified, there will be quite a few
error messages, but streaming will work. The TCP port 8085 was specified
for output to make sure there was no interference with anything else.
Using top
in another ssh
session, I
could see that mjpg_streamer
was using less than 1%
of CPU time and less than 2% of the available RAM when idle.
I then looked at the video stream from my desktop machine with a web
browser at the following address:
http://domo.local:8085/stream_simple.html
. On my tablets I have
to use the explicit IP address of the Raspberry Pi, because Android doesn't
seem to use zero-configuration networking (avahi
). As can be
seen below, the streamer used a bit more than 8% of CPU time and no more
memory while active.
These are very good results which means that it should certainly be possible to run the video streamer as a daemon even with the relatively under-powered Raspberry Pi B+.
I should point out that the first time I looked at the video with the browser, the view was greatly distorted but the camera was clearly connected. The streamer can be stopped with the CtrlC combination. I restarted the streamer (it took a couple of tries) and then the image displayed on the desktop was good.
- The home automation software Domoticz has a built-in server listening on TCP ports 8080 and 443 by default.
- A stand-alone HTTP server lighttpd which listens on TCP port 80. While not done by default, should HTTPS be enabled I would probably use the default TCP port 443.
- The video streamer MJPG-Streamer has a built-in server listening on TCP port 8080 by default.
Security
The main reason to stream the video from the Webcam connected to my home automation server is to monitor the inside of the house when away. When exposing a local network to the mean and cold outside world, it is important to think about security. I seem to remember reading somewhere that IP cameras were among the most common cause of security breaches on the Internet and that's precisely what's being done here, turning a webcam into an IP camera.
At the minimum, the video streamer password protection must be enabled.
Look at the -c
or --credentials
options of the HTTP
output plugin.
When connecting to the MJPG_Streamer HTTP server there will now be a login window where the specified user name and password must be defined.
Alternatively, the user name and password can be specified in the
URL:
username:password@<ip_of_raspberry_pi>:<mjgp_streamer_http_port>
.
In this particular example it would be
stream:michel@192.168.1.22:8085
(or
stream:michel@domo.local
)
This first line of defence is not very secure. The user name and password
will be sent as plain text. It would be preferable to use TLS encryption.
Unfortunately, this is not quite as simple as one would wish because the
mjpg_streamer
server does not handle the HTTPS protocol. I have
found two potential workarounds but I have tested only one.
The untested solution is "stunnel [which] can be used to add TLS functionality to commonly used Inetd daemons like POP-2, POP-3, and IMAP servers, to standalone daemons like NNTP, SMTP and HTTP, and in tunneling PPP over network sockets without changes to the source code." Here a couple of references on the subject.
- Secure webcam streaming with mjpg-streamer and stunnel by Cristiano Urban (Cris' hacks).
- Securing mjpg streamer by Siva Dirisala.
The other solution, the one I am using, relies on the much greater capabilities of the lighttpd HTTP server also running on the Raspberry Pi. For those few unfortunate persons that have not read all my previous posts, it serves OTA firmware updates to the ESP8266 based switches used in my house. The idea is simple, access lighttpd with a secure HTTPS connection at a specified port and let that server pass on the request to the mjpg_streamer on its unsecured TCP port. I had often seen the terms "proxy" and "reverse proxy" but I had never touched those things. I got it to work and its deceptively easy, but do not ask me anything about it. This is the result of much searching on the web into the wee hours of the morning and much trial and error. I feel like one of those proverbial monkeys that manage to bang out a Shakespeare sonnet on a keyboard.
Lately (October 2021), I have revisited the subject. It turns out an NGINX reverse proxy is easily set up. You can see the details in the section entitled Secured Streaming in Additional Servers on a Raspberry Pi Based Home Automation System. As said at the start of this old post, I would suggest reading the whole section named Installing the MJPG-Streamer Video Streamer which updates many parts of this post.
Not much needs to be done; just a couple of additions to the lighttpd configuration file. First add the proxy module in the server modules at the start of the file and then add a proxy server definition at the end of the file.
Being the lazy sort, I used the self signed certificate that was installed with the domoticz server, but any signed SSL certificate can be used. If lighttpd were on another computer on the local network, then the host address should be changed to the IP of the Raspberry Pi on which MJPG-Streamer is running. Since both servers are on the same machine, I used the localhost IP number. It will not be necessary to modify this configuration file should the IP of Rapsberry Pi change.
Check that the configuration file is correct and once it is, restart the server for the changes to take effect. Might as well verify that lighttpd is running correctly after restarting it.
Time to check that the proxy set up works. On the desktop computer
connected to the same network as the Raspberry Pi, I pointed the web
browser to the following address: https://192.168.1.22:4433/index.html
(or https://domo.local:4433/
). Because a self signed SSL
certificate is used, the browser will baulk and
it will be necessary to create an exception. This only needs to be done
once per browser. From then on, the mjpg_streamer web
pages should be reached at once through a secure connection.
Chromium displays an ominous warning about the
link being unsecure.
Not to worry, the communications between the web browser and mjpg_streamer is encrypted; the browser just does not like the unsigned certificate.
All this has been done locally. To access the video stream from outside the local area network, a dynamic DNS address and the TCP port used by lighttpd (not the unencrypted port used by MJPG-Streamer) needs to be forwarded by the LAN router. I have already discussed all this before 3. Creating a dynamic domain name, 4. Updating the IP Destination Address, and 5. Forwarding TCP Ports in a previous post.
Then go to the router and forward a TCP port say 4433 for simplicity or whatever you want as long as there is no clash, to the IP address and the HTPPS port to which the lighttpd proxy is listening. In my case it is 192.168.1.22 and 4433.
My dynamic DNS address to reach the Raspberry Pi is
modom.twiligthparadox.com
. So to see the video streamed from
mpg_streamer
, I enter either of the following addresses in
a web browser.
https://stream:michel@modomo.twilightparadox.com:4433/stream.html
https://modomo.twilightparadox.com:4433/stream.html
.
As to be expected, I had to enable an exception in Firefox
because of the self signed certificate and the browser displayed a warning
over the HTTPS secure icon.
As said before, the communications between the web browser and mjpg_streamer is encrypted.
Other Clients: VLC, Kodi and Domoticz
Here quick explanations on how to access the video stream from three clients that are not web browsers.
VLC
As explained in the streamer documentation, the video can be
viewed with VLC from VideoLAN. In the GUI,
click on the Media
menu at the top of the window and
then select Open Network stream... (Ctrl-N)
.
Enter the following URL:
http://192.168.1.22:8085/?action=stream
A dialog window will pop up asking for the user name and password. Or you can specify the user name and password in the URL.
http://stream:michel@192.168.1.22:8085/?action=stream
The application can also be launched from a terminal:
On my desktop I can use local host name, domo.local
,
instead of the IP address. On my Android
tablets and TV box, this is not possible and I must use the explicit
numeric IP address.
Of course, if I wanted to use the encrypted stream, I would use the HTTPS protocol with port 4433. And if I were doing this from a computer not connected to my local network, I would use the dynamic domain name.
https://[stream:michel@]modomo.twilightparadox.com:4433/?action=stream
Since I am paranoid, I will probably not include the password and user name in the URL thus ensuring that these would not be transmitted in cleartext at any point.
Kodi
To show the stream in Kodi, I simply followed
the instructions on the Wiki. It could not be simpler
in principle. Since Kodi is on an Android
device on the local network, I created a simple text file, named
webcam.strm
, with the following content.
The filename can be anything reasonable I assume, but the
extension must remain .strm
I think.
The hardest bit was to copy that file on the device storage. I have used
two different methods. The simplest is to copy the file from my desktop to a
USB stick and then connect the latter to the Android
device and copy the file into a subdirectory of the SDCard
directory which can later be accessed from Kodi.
The other method was to install and start an SSH/SFTP Server and then use
Filezilla to copy over the file. I used SSH/SFTP Server - Terminal by Banana Studio. I logged
in on port 2222 as user ssh
without a password using the SFTP -
SSH File Transfer Protocol and it worked without a problem. And there was
no need to walk up and down the stairs.
Which ever way the strm
file is created on the device, it is
best to add it to your Favourites
for quick and easy access. Use
the Kodi menu system to find the file, highlight it
and press the C to add it to Favourites.
I really don't know much or anything at all to be honest, about Android TV devices and even less about Kodi so enough said.
Domoticz
As far as I know, Domoticz does not do video streaming. I only say that because, every now and then I see someone asking how to go about it. When the video stream icon is pressed, I believe that Domoticz is merely transmitting a sequence of snapshots. I do use the fact that the home automation system does take a snapshot with a connected camera when a specific event occurs and will send that picture by email. I have set it up so that that picture is taken about 2 seconds after a light is turned on or off (on is better). It's a cheap way to verify that everything is working when I am away.
I used to do that with uvccapture
as detailed in the Domoticz Wiki page entitled Camera Setup. I don't want to install uvccapture
since mjpg_streamer
will presumably control the web cam at all
times. That is no problem because, the video streamer will take snapshots.
Just like with uvccapture
and with video streaming this is done
with a URL, albeit a different one:
http://stream:michel@192.168.1.22:8085/?action=snapshot
The image shows how to set up the camera parameters in Domoticz.
A New Script
There are numerous examples of bash scripts for starting and stopping
MJPG-Streamer dating back quite a few years. I just
adapted one of those, but I don't recall exactly which one. I saved the file
under the name webcam-streamer
in a hidden directory called
.local/bin
in my home directory.
The script tries up to five times before giving up when starting or restarting the webcam streamer. That's because starting the daemon would often fails on the first few tries for some unknown reason.
The script needs to be made executable and the
directory in which it is located will be added to the environment search path
by editing the hidden file .profile
in my home directory.
The change will take effect only on the next reboot. The last step
is to add a reboot
entry in crontab
.
Note how the cron
reboot task starts the streamer and then
restarts it about five seconds later. The reason for this can be
gleaned from the following image which comes from the streamer with
just a single start after a reboot.
The web cam is working but something is wrong. Stopping and restarting the streamer stops the ghostly stream. Of course your mileage may vary with different equipment.
Both the need to try more than once to start the streamer and
the ghostly stream are persistent problems. I have reinstalled
MJPG-Streamer on a clean copy of a fully updated
and upgraded version of Rasbian Jessie
without any improvement. Tests with another camera suggest that this is a problem with
the Logitech C270 Webcam and not related to MJPG-Streamer. Accordingly
the cron
task with other cameras could be a single command.
References
- Raspberry Pi Webcam Over the Internet Using MJPG-Streamer by Jacob Salmela, May 31, 2014.
- Setup Guide: Raspberry Pi | MJPEG Streamer Install & Setup & FFMpeg Recording by Austin St. Aubin, March 11,2018.
- Raspberry Pi camera board video streaming by Miguel Mota, Sept. 25, 2013, Updated Jan. 19, 2014.
Remote Start and Stop
What happens if for some reason the web cam streamer is not working properly and I am away from the house? To my mind, the easiest solution was to create a virtual switch in my home automation system to activate the script.
This is easily done. Create a virtual switch in Domoticz. Here is a quick reminder of the procedure.
- Click on the Setup tab.
- Click on the Hardware item in the menu.
- If the virtual hardware
Dummy (Does nothing, ...)
already exists move on to step 4.- Complete the fields
Name: =Virtuel
, and
Type: =Dummy (Does nothing...
that is picked from the drop down list. - Click on the button at the bottom of the page. The virtual hardware will now show up in the table at the top of the page.
- Complete the fields
- Click on in the virtual (Dummy) hardware.
- Name the sensor, I used
Web Cam On/Off
and set the Sensor Type: toSwitch
. - Click on the OK button. A message saying where the switch was installed will briefly flash on the window.
- Click on the Setup tab again.
- Click on the Devices item in the menu.
- Lookup the idx number of the newly created switch in the index column (idx).
- Click on the Switches tab.
- Click on the button of the newly created switch.
- Enter
script:///home/pi/.local/bin/webcam-streamer start
in the On Action: field. - Enter
script:///home/pi/.local/bin/webcam-streamer stop
in the Off Action: field. - Click on the button.
- Click on the button to return to the Switches tab.
I didn't particularly like the light bulb icon or any of the other
icons available for this particular device. I created a webcam icon
from the one already in the www/images
directory of
Domoticz. See Domoticz: give a facelift by customizing the icons
for details on doing this.
So I can always turn on or off the web cam streamer using this control. There is one problem though. The status of the virtual device does not necessarily correspond to the status of the daemon. However this is easily solved by modifying the script file created in the previous section.
-l
(or
--led
) option of the input_uvc.so
plugin, in
practice that did not work. In any case, I had an uncomfortable feeling
of being a voyeur in my own home spying on family and friends. I would
rather that the camera only work when the house is empty. Strangely, I
have no qualms about the outside IP camera always being on.
First, add a couple of constants that will need to be adjusted for the true IP address of the Rasberry Pi hosting the home automation program and for the true index number of the virtual device created in Domoticiz at the top of the script file.
The end of the script is also changed. After the case..esac
statement, there are few extra lines of code that will update the status of
the Domoticz virtual device to reflect whether
MJPG-Streamer is running or not. And an update
option is added. It can be seen to do nothing in the case
statement except to go on to the lines that update the status of
the Domoticz virtual switch.
An HTML request will be sent to the Domoticz web server
whenever the script is invoked with a start
, stop
,
restart
or update
option. No request is send
when the status
option is selected or when there is no specified
option or when there is an error.
From now on the cron
deamon will only update
Domoticz on the status of
MJPG-Streamer. This is done at boot time and only once
an hour thereafter because it seems to be something with a low priority.
To ensure that the webcam video is streamed when the house is empty,
I modified the enable_timers.sh
script that controls
conditional timers in Domoticz. It is a straighforward thing to add
the following at the end of the script.
Slow Camera
When my web cam is operated in YUYV pixel format, MJPG-Streamer takes on the chore of compressing the video stream before sending it on. That will occupy more than half of the cpu time. This could be acceptable in a multicore system, such as the Raspberry Pi 3 or the Orange Pi Zero, but I would not do it with a single core Raspberry Pi B+.
However the last section does offer a partial solution. Do not start the
streamer when the computer is booted, in other words, make sure to stop the
streamer in the reboot
entry in crontab
and do not
start it in the conditional timers script. Remotely start and stop MJPG-Streamer only when you want to view the video feed.
Watching a video of one's own living room is not the most entertaining
activity, so I would assume that this will not be done for long. Hopefully,
the Raspberrry Pi will have enough oomph to process the HTTP request
to turn the streamer off when it is running, but that remains to be seen.
If someone does that, I would be happy to hear how this works in practice.
Downloads
Here are the complete files discussed in the above post.
- lighttpd configuration file: lighttpd.conf
- bash script to start/stop MJPG-Streamer: webcam-streamer-vanilla
- bash script with Domoticz integration: webcam-streamer
- Domoticz webcam on/off icons: webcam48_On.png, webcam48_Off.png