Streaming MJPEG with GStreamer and Bottle
Several projects of mine have used this technique. By leveraging bottle for the server and GStreamer for video, this model is clean and extremely extendable. In this stripped down example, a video test pattern is streamed to the browser. By changing the gstreamer pipeline, you could easily stream live video from your Raspberry Pi robot, or stream video from your server.
Install Requirements
pygst isn’t “pipable” so, I’m pulling it from another package manager. Everything else I’m doing in a python virtual environment.
1 2 3 4 5 |
sudo apt-get install python-gst0.10-dev mkdir mjpeg_bottle virtualenv env --system-site-packages source env/bin/activate pip install -I gevent bottle |
Create Server
Bottle keeps the server code clean and simple. Here, the root page serves a bare page with an image tag. Inside the image tag is a link to our other route which will serve the images.
- Note: This MJPG through img tag technique will not work in IE.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# vserver.py from gevent import monkey; monkey.patch_all() import bottle from mjpeg import MJPEG @route('/') def index(): return '<html><body><img src="/mjpeg" width="640px" width="480px" /></body></html>' @route('/mjpeg') def mjpeg(): bottle.response.content_type = "multipart/x-mixed-replace; boundary=--boundary" return iter(MJPEG()) |
Here is where we actually generate the jpeg frames. Update the PIPELINE to your liking.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#mjpeg.py import pygst pygst.require("0.10") import gst PIPELINE = "videotestsrc ! video/x-raw-yuv, framerate=10/1, width=640, height=480 ! jpegenc ! multipartmux boundary=boundary ! appsink name=endpoint" class MJPEG(object): pipeline = object def __init__(self): self.pipeline = gst.parse_launch(PIPELINE) self.endpoint = self.pipeline.get_by_name('endpoint') self.pipeline.set_state(gst.STATE_PLAYING) def next(self): boundary = self.endpoint.emit('pull-buffer').data data = self.endpoint.emit('pull-buffer').data return boundary+data def stop(self): self.pipeline.set_state(gst.STATE_NULL) def __iter__(self): return self |
1 |
python vserver.py |