2013-05-25T08:27:10Z

Stream Video from the Raspberry Pi Camera to Web Browsers, Even on iOS and Android

I've been excited about the Raspberry Pi Camera Module since it was announced last year, so I went and ordered one from Element14 as soon as it came on sale.

I have a few ideas for cool things to build with this camera and I will be blogging about them as I get to develop them. Today, I will show you how to transform the Raspberry Pi into a webcam server. You will be able to watch the video stream from the camera on any device that has a web browser. And yes, this includes the iPad/iPhone and Android devices!

The official streaming method

The introductory article about the camera module in the Raspberry Pi blog shows a method to stream video from the Raspberry Pi to another computer. This method essentially works as follows:

  • On the Pi the raspivid utility is used to encode H.264 video from the camera
  • The video stream is piped to the nc utility, which pushes it out to the network address where the video player is.
  • On the player computer nc receives the stream and pipes it into mplayer to play.

This is an efficient method of streaming video from the Pi to another computer, but it has a few problems:

  • The Raspberry Pi needs to know the address of the computer that is playing the video
  • The playing computer needs to have an advanced player that can play a raw H.264 video stream. No mobile device that I know can do this, for example.
  • Since this system relies on a direct connection between the Pi and the player, it is impossible to have the player computer connect and/or disconnect from the stream, the connection needs to be on at all times.
  • What if there are two or three concurrent players? Things get awfully complicated for the Pi.

This ad hoc solution that the Raspberry Pi Camera team proposes isn't that useful to me, so I went to search for better options.

Streaming protocols

I think an important requirement for a streaming camera is that you can view it with ease. To me, this means that the stream should be playable from a web browser. Having to run a custom player is a complication, and puts it out of reach of most mobile devices.

There are a few modern streaming protocols for web browsers out there. For example, HLS is Apple's choice, so it has great support on iDevices but not much elsewhere. Another one, called Fragmented MP4 is supported by Adobe and Microsoft, but requires browser plugins from these companies on the player computer, so Windows and Mac computers can do it, but Linux and mobile cannot. HTML5 video is also based on the MP4 format but support is not that great.

Besides, for all the streaming protocols listed above there is a need to have a streaming server that prepares the video for streaming by segmenting it and packaging it, and while there are several open source utilities that can do this for a static video stream, I haven't found any that can do it on a live stream. Note: I have been corrected on this statement, more recent releases of ffmpeg than the binary available for Raspbian can generate an HLS live stream.

So what other options are there?

Motion JPEG to the rescue

I then investigated how IP webcams do it, and a lot of them use an older streaming protocol called Motion JPEG or MJPEG.

What is Motion JPEG? Pretty simple, it's just a stream of individual JPEG pictures, one after another. I was surprised to find that most modern browsers can play MJPEG streams natively.

The down side of MJPEG streams is that they are not as efficient as H.264, which greatly improves quality and reduces size by encoding only the differences from one frame to the next. With MJPEG each frame is encoded as an entire JPEG picture. For my needs this isn't a concern, though.

Continuing with my research I stumbled upon MJPG-streamer, a small open source MJPEG streaming server written in C that I was easily able to compile for the Raspberry Pi.

The following sections describe how I've used this tool to create a very flexible, play anywhere, streaming server for my Raspberry Pi camera.

Installing MJPEG-streamer

UPDATE: This section is outdated. Please use the instructions on my updated guide to build and install MJPG-Streamer.

Unfortunately there isn't a package for MJPEG-streamer that can be installed with apt-get, so it needs to be compiled from source.

MJPEG-streamer is hosted at sourceforge.net, so head over to the project's download page to get the source tarball.

To compile this application I used the following commands:

$ sudo apt-get install libjpeg8-dev
$ sudo apt-get install imagemagick
$ tar xvzf mjpg-streamer-r63.tar.gz
$ cd mjpg-streamer-r63
$ make

This tool requires libjpeg and the convert tool from ImageMagick, so I had to install those as well.

The makefile does not include an installer, if you want to have this utility properly installed you will need to copy the mjpg_streamer and its plugins input_*.so and output_*.so to a directory that is in the path, like /usr/local/bin. It is also possible to run this tool directly from the build directory.

Setting up the JPEG source stream

The streaming server needs a sequence of JPEG files to stream, and for this we are going to use the raspistill utility that is part of Raspbian. For those that are concerned about performance, keep in mind that the JPEG encoder used by raspistill runs in the GPU, the load required to generate JPEGs is pretty small.

To setup a constant stream of JPEG images the command is as follows:

$ mkdir /tmp/stream
$ raspistill -w 640 -h 480 -q 5 -o /tmp/stream/pic.jpg -tl 100 -t 9999999 -th 0:0:0 &

Let's go over the arguments to raspistill one by one:

  • -w sets the image width. For an HD stream use 1920 here.
  • -h sets the image height. For an HD stream use 1080 here.
  • -q sets the JPEG quality level, from 0 to 100. I use a pretty low quality, better quality generates bigger pictures, which reduces the frame rate.
  • -o sets the output filename for the JPEG pictures. I'm sending them to a temp directory. The same file will be rewritten with updated pictures.
  • -tl sets the timelapse interval, in milliseconds. With a value of 100 you get 10 frames per second.
  • -t sets the time the program will run. I put a large number here, that amounts to about two hours of run time.
  • -th sets the thumbnail picture options. Since I want the pictures to be as small as possible I disabled the thumbnails by setting everything to zero.
  • & puts the application to run in the background.

Starting the streaming server

Okay, so now we have a background task that is writing JPEGs from the camera at a rate of ten per second. All that is left is to start the streaming server. Assuming you are running it from the build directory the command is as follows:

$ LD_LIBRARY_PATH=./ ./mjpg_streamer -i "input_file.so -f /tmp/stream -n pic.jpg" -o "output_http.so -w ./www"

Let's break this command down to understand it:

  • LD_LIBRARY_PATH sets the path for dynamic link libraries to the current directory. This is so that the application can find the plugins, which are in the same directory.
  • -i sets the input plugin. We are using a plugin called input_file.so. This plugin watches a directory and any time it detects a JPEG file was written to it it streams that file. The folder and file to watch are given as the -f and -n arguments.
  • -o sets the output plugin. We are using the HTTP streaming plugin, which starts a web server that we can connect to to watch the video. The root directory of the web server is given as the -w argument. We will use the default web pages that come with the application for now, these can be changed and customized as necessary.

Watching the stream

Now everything is ready. Go to any device that has a web browser and connect to the following website:

http://<IP-address>:8080

Where IP-address is the IP address or hostname of your Raspberry Pi.

The default website served by the streaming server provides access to several players to watch the stream. I've found that the "Stream" option worked on most devices I tried. For a few that "Stream" didn't show video I went to "Javascript" and I was able to play the video just fine.

I tested playback of the stream from an iPad, an Android smartphone and a variety of web browsers on Windows and OS X, and I was able to play the stream in all of them.

I hope you find this method useful. Let me know in the comments below if you have a different method of streaming.

Miguel

133 comments

  • #26 Mitchell said 2013-07-27T04:46:31Z

    Hello, I followed the tutorial step by step but keep getting this error when I run mjpeg streamer like you suggest: pi@raspberrypi ~/mjpg-streamer-r63 $ LD_LIBRARY_PATH=./ ./mjpg_streamer -i "input_file.so -f /tmp/stream" -o "output_http.so -w ./www" MJPG Streamer Version.: 2.0 ERROR: could not find input plugin Perhaps you want to adjust the search path with: # export LD_LIBRARY_PATH=/path/to/plugin/folder dlopen: input_file.so: cannot open shared object file: No such file or directory Please advise how I can get this to work. Thank you!

  • #27 Miguel Grinberg said 2013-07-28T16:43:18Z

    @Mitchell: when you run "make" you are probably getting an error that interrupts the build. Look for that error and let me know what that is. You can run "make" again to see it.

  • #28 brox said 2013-08-05T14:49:07Z

    Don't you think that continuous writing to /tmp/stream/pic.jpg kills SD card and makes CPU busy?

  • #29 Miguel Grinberg said 2013-08-05T17:18:02Z

    @brox: this is a proof of concept. If you wanted to do this for long you could write the images to a ramdisk, for example. Your CPU claim I don't understand, writing a file to disk does not use CPU, and the jpegs are even encoded in the GPU.

  • #30 fabien legay said 2013-08-06T02:44:44Z

    hi johann for the black screen try with: sudo raspistill -w 640 -h 480 -q 5 -o /tmp/stream/pic.jpg -tl 100 -t 9999 -th 0:0:0 & i had the same problem with now its works!

  • #31 Axel said 2013-08-17T15:33:06Z

    Thank you for the time spent on this, I followed the instructions and all is working properly.

  • #32 Bert Huizinga said 2013-08-19T17:18:02Z

    I have the same problem as Peter Kula with the same error: fatal error: linux/videodev.h: No such file or directory Before i used the command 'make' i updated and upgraded the pi with apt-get update and apt-get upgrade

  • #33 Rod said 2013-08-19T22:27:05Z

    Thanks for the tutorial, really easy to follow and I got it running in a few minutes just using rich's suggestion to install the jpeg streamer like they said in http://ubuntuforums.org/showthread.php?t=1888233.

  • #34 Santos said 2013-08-23T05:02:51Z

    I am a beginner following the set of instructions, would you mind telling me what the following sentence means? "so head over to the project's download page to get the source tarball."

  • #35 Miguel Grinberg said 2013-08-23T05:50:04Z

    @Santos: you need to click the words "download page", that's a link that will take you to the download page for MJPEG-Streamer.

  • #36 Manne said 2013-08-29T15:40:18Z

    I got the following error, can you please give me a hint pi@raspberrypi ~/Downloads/mjpg-streamer-r63 $ make gcc -O2 -DLINUX -D_GNU_SOURCE -Wall -c -o mjpg_streamer.o mjpg_streamer.c mjpg_streamer.c:27:28: fatal error: linux/videodev.h: Datei oder Verzeichnis nicht gefunden compilation terminated. make: *** [mjpg_streamer.o] Fehler 1

  • #37 Miguel Grinberg said 2013-08-29T16:52:23Z

    @Manne: this is explained in previous comments. You need to update your Raspbian, you are running an old release.

  • #38 Manne said 2013-08-30T14:12:22Z

    thank you for the reply. the update and upgrade i did before already. I think ist because of the below mentioned files: May I ask you to give me an idea where to find them ? The makefile does not include an installer, if you want to have this utility properly installed you will need to copy the mjpg_streamer and its plugins input_*.so and output_*.so to a directory that is in the path, like /usr/local/bin. It is also possible to run this tool directly from the build directory.

  • #39 Miguel Grinberg said 2013-08-31T04:15:56Z

    @Manne: step 6 in the following page may be of help: http://raspberrytank.ianrenton.com/day-19-the-move-to-raspbian/

  • #40 Moe said 2013-09-01T05:11:50Z

    To fix "fatal error: linux/videodev.h: No such file or directory compilation terminated." pi@pi /usr/include/linux $ sudo ln -s videodev2.h videodev.h

  • #41 Chris said 2013-09-02T05:33:08Z

    I don't understand this part "The makefile does not include an installer, if you want to have this utility properly installed you will need to copy the mjpg_streamer and its plugins input_*.so and output_*.so to a directory that is in the path, like /usr/local/bin. It is also possible to run this tool directly from the build directory."

  • #42 Miguel Grinberg said 2013-09-02T05:38:32Z

    Chris: the easiest way to run the tool is to run it directly from the directory where it was built. If you want to install it then you have to do extra work, but that is not necessary to use the tool.

  • #43 Eric said 2013-09-13T12:18:41Z

    After having tried this tutorial I am stuck at the last step actually, getting the http output to work… I am using input_file.so and output_http.so and I was wondering if I try to call one of those (thus executing ./output_http.so” I get a Segmentation fault right away ! Same for input_file.so ! Anyone else who had this porblem or anyone who can double check this with the newest svn pull ?

  • #44 Miguel Grinberg said 2013-09-13T14:16:38Z

    @Eric: the .so files are not meant to be executed, you have to run "mjpg_streamer".

  • #45 Kirk Liu said 2013-09-14T05:48:39Z

    Thanks for the guide. I upgrade my raspbian to the latest version, use the tarball from here (http://sourceforge.net/p/mjpg-streamer/code/HEAD/tarball) as you comment, follow the steps to the last command (LD_LIBRARY_PATH=./ ./mjpg_streamer -i "input_file.so -f /tmp/stream" -o "output_http.so -w ./www") and get an error message "could not open file for reading: No such file or directory". However, after adding a parameter -n pic.jpg, it finally worked!! Thank you!!

  • #46 Wade said 2013-09-16T04:54:26Z

    Have download the streamer. Several .c files want to include "Linux/videodev.h" The current os includes "Linux/videodev2.h". I renamed "Linux/videodev2.h" as "Linux/videodev.h" There is no "input_file.so". "input_uvc.so" does not find the pi camera. Suggestions?

  • #47 Miguel Grinberg said 2013-09-16T05:57:54Z

    @Wade: instead of renaming it's better to create a symbolic link: $ ln -s /usr/include/linux/videodev2.h /usr/include/linux/videodev.h I'm not sure about the missing input_file.so, if the build completed you should have that file. The only two plugins that are needed are input_file.so and output_http.so, none of the other input_*.so are used, so you have to figure out why input_file.so isn't building.

  • #48 Raven_Squire said 2013-09-17T12:44:33Z

    Is there any chance you can elaborate or clarify this paragraph as I think its where I'm going wrong. """The makefile does not include an installer, if you want to have this utility properly installed you will need to copy the mjpg_streamer and its plugins input_*.so and output_*.so to a directory that is in the path, like /usr/local/bin. It is also possible to run this tool directly from the build directory.""" Do I move the uncompressed "mjpg-streamer-r63" folder to /usr/local/bin ? Where do I get the plugins input_*.so and outut_*.so from? I also had to do a "Make clean" is this right or if "make" doesn't work am I doing it wrong.

  • #49 Miguel Grinberg said 2013-09-17T14:32:03Z

    @Raven: You need to copy the binary files, "mjpg_streamer", "input_file.so", "output_http.so". You can copy these to /usr/local/bin, for example. You also need to copy the web server files (the "www" folder) to a system wide location, for example /usr/local/share/mjpg_streamer/www. If you are not comfortable doing this kind of thing I recommend that you run the software directly from the directory you unpacked it. This proved to be a very popular article, I'm thinking I need to write a follow up with more detailed instructions, or maybe provide a script that does all the work.

  • #50 Joachim Sturm said 2013-09-19T12:32:10Z

    make fails with the following error: --schnipp -- v4l2uvc.c: In function âv4l2GetControlâ: v4l2uvc.c:427:1: warning: control reaches end of non-void function [-Wreturn-type] make[1]: *** [v4l2uvc.lo] Error 1 make[1]: Leaving directory `/home/pi/mjpg-streamer-r63/plugins/input_uvc' make: *** [input_uvc.so] Error 2 ---schnapp -- Can somebody help me to solve this problem? TX Achim

Leave a Comment