Streaming mjpeg video with a web2py and python server

Recently I investigated whether I could implement software streaming of mjpeg video on a camera that uses web2py and python as its HTTP interface. Web2py makes most common operations quite straightforward and streaming is no exception, although I had to go digging for some details, this post along with SergeyPo’s answer to this question were of immense help.

mjpeg or Motion JPEG streaming over HTTP is a popular way of streaming video from IP cameras. If your camera has already gone to the effort of jpeg’ing its frames then it is reasonably cheap to stream them out via software over HTTP as an mjpeg video stream (as opposed to encoding to h264 etc.).

Mjpeg can be viewed natively by most browsers (except Internet Explorer) and can also be viewed on video viewers like VLC etc. An mjpeg stream is quite simple, it really just contains the individual jpeg frames’ data separated by a defined frame boundary marker.

Web2py takes care of chunked streaming via the stream() function, we just have to write a file object that we pass to stream(), this will take care of loading the jpeg frames and providing the data back to stream(), which in turn will stream this down the line to the client.

So to create an mjpeg stream we:

1.) Add a web2py handler for the stream, for example /mjpeg

2.) Add the following to the response headers: “multipart/x-mixed-replace; boundary=the_answer_is_42”
“the_answer_is_42” is just a made-up boundary marker which we hope won’t appear in the jpeg frame data, it can be changed to something else.

3.) Call stream() passing an instance of our MJPEGStreamer file object (see #4!), and a fairly arbitrary chunk size.

4.) Define a file compatible object that will loop ‘forever’ and provide the data to stream(), it will provide the data for the individual jpeg frames as well as insert the frame boundaries (–the_answer_is_42) between frames, it also inserts the headers for the individual frames (Content-Type: image/jpeg).

The following provides an overview of a stream implementation. Here we imagine that the latest jpeg frame is to be found in a ram-disk file called image.jpg, we load this data so that we can stream it to the client.

So in our web2py controller we have something like:

# Proof of concept web2py mjpeg streamer
def mjpeg():
    # Set the initial response header
    response.headers['Content-Type'] = 'multipart/x-mixed-replace; boundary=the_answer_is_42'
    # stream the mjpeg data (via MJPEGStreamer) with a chunk size of 
    # 30000, the chuck size must be less than the size of the smallest 
    # jpeg frame or the stream will stall....
    return, 30000)
class MJPEGStreamer(): 
    def __init__(self): 
        self.iterator = self.make_iterator() 
    def make_iterator(self): 
        while True:
            out = ''
            # Read a jpeg frame from image.jpg
            data =  open('/media/ram/image.jpg', 'rb').read()
            # Add the frame boundary to the output
            out += "--the_answer_is_42\r\n"
            # Add the jpg frame header
            out += "Content-Type: image/jpeg\r\n"
            # Add the frame content length
            out += "Content-length: "+str(len(data))+"\r\n\r\n"
            # Add the actual binary jpeg frame data
            out += data
            yield out  
            # Sleep for a bit..
    # stream() calls read() to get data.
    def read(self, n):
        # Get some more stream data
        n = 
        return n 

To view the mjpeg stream, open VLC viewer and choose Media / Open Network Stream and provide the stream URL (e.g. http://my_streaming_host/mjpeg) and hit play…

I found that it was important to set the chunk size to be (a good bit?) less than the data size of the smallest frame to be sent or else the stream will stall…

web2py – External connections to your Web App from the network on port 80

If you don’t want to use apache or the like to publish your web2py app then you can use web2py’s own little web server (rocket) by running like this:


This won’t accept external connections however, and it also listens on port 8000 rather than port 80.


So, to allow external connections from web clients on port 80, run as follows:

python -i -p 80

I keep forgetting this, so it’s getting written down for once and for all!


Web2py – Make your app the default web application.

There are multiple ways of making your app the default web2py application, they are detailed here in the section called ‘Application Init’. When an app is the default web2py app, you don’t have to specify its name in its url when you visit it in your browser.


Here is a summary of the method that I prefer.


First create a file in your web2py directory called, you can rename to but if you do, remember to delete any example code that it may contain.


Add the following code to
routers = dict(
BASE = dict(
Replace your_app_name_here with the name of your web application, and the restart web2py. How you restart web2py depends on how you run it, for example in my case I restarted apache.


When web2py has restarted you should now be able to access your app via a naked url.


Web2py and python – Change to model definition may break web site

In Web2py if you want to change a model’s definition, you can first of all change the model’s software definition via its python code and then, (depending on your migration settings), the next time you launch your web app, web2py will attempt to alter your database structure to match your updated model.


If this works, then it works and your database will reflect your new model, however sometimes web2py can’t make the necessary changes and your app may not subsequently load any more at all. If this happens then I have found the best way to fix the app is to delete all of the files in the app’s database directory – the next time you launch your app web2py will recreate your DB from scratch and it should launch again.


Now this is fine assuming that your database didn’t have loads of important data in it that you didn’t want to loose. So, to get around this problem, before trying to change a model’s definition I export all of the data in the database to a file (using a tool like this) so that if the problem happens and I have to get web2py to recreate the database ‘in its own image’ I can later reimport the previously saved data…


Web2py – You don’t have permission to access /admin on this server.

If you try to access the web2py admin pages and get an error like the following:
You don’t have permission to access /admin on this server.
Then don’t panic immediately, by default the admin stuff is only available on HTTPS, so make sure that your URL starts with https: and try again, if this doesn’t work than feel free to panic… I keep forgetting this and it wrecks my head, software development can be trying!


Web2py / Apache – admin disabled because no admin password

If you are running Web2py on Apache and get this error message when you try to log into the admin interface (/admin):


admin disabled because no admin password


Then you may have forgotten to provide a password during set-up and will have to provide one now to proceed to the admin screens.


To do this you need to can do the following:


Stop Apache:
apachectl -k stop
cd to the web2py directory and run specifying a new password (greater than 4 characters long?) like this:
sudo python -a the_new_passsword
Then kill the web2py, e.g. via ctrl-c, and then copy to, like this:
sudo cp
Restart the apache server:
sudo apachectl -k restart
After doing all of this when you visit /admin you should be asked to provide your password and you should be able to login – well it worked for me anyway! ;-)


Web2py and Apache running on Raspberry Pi

As part of my smart-cam simulation project I have installed web2py and Apache on a Raspberry Pi, the idea here is to simulate the ARM platform on which the smart-cam web UI software will eventually run by using the Pi until the real hardware is sorted out and available. This will allow me to do some proof-of-concept software development sooner rather than later.


I was expecting a difficult enough install but it turned out to be very easy, I just executed the automated set-up script that is documented here, under the section called ‘One step production deployment’ (ubuntu).


To summarise the steps, with the Pi connected to the network, open a terminal window and execute the following:


chmod +x
sudo ./


Running took a good while (15 mins?), but when it was complete I was immediately able to view the web2py welcome page from another computer on the network!