Skip to content

Commit

Permalink
Fix #191
Browse files Browse the repository at this point in the history
  • Loading branch information
waveform80 committed Sep 30, 2016
1 parent 0384a44 commit c92f65e
Showing 1 changed file with 134 additions and 0 deletions.
134 changes: 134 additions & 0 deletions docs/recipes2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,140 @@ on firmware #685. However, utilizing the MJPEG splitting demonstrated in
The above script achieves 30fps with ease.


.. _web_streaming:

Web streaming
=============

Streaming video over the web is surprisingly complicated. At the time of
writing, there are still no video standards that are universally supported by
all web browsers on all platforms. Furthermore, HTTP was originally designed as
a one-shot protocol for serving web-pages. Since its invention, various
additions have bolted on to cater for its ever increasing use cases (file
downloads, resumption, streaming, etc.) but the fact remains there's no
"simple" solution for video streaming at the moment.

If you want to have a play with streaming a "real" video format (specifically,
MPEG1) you may want to have a look at the `pistreaming`_ demo. However, for the
purposes of this recipe we'll be using a much simpler format: MJPEG. The
following script uses Python's built-in :mod:`http.server` module to make a
simple video streaming server::

import io
import picamera
import socketserver
from threading import Lock
from http import server

PAGE="""\
<html>
<head>
<title>picamera MJPEG streaming demo</title>
</head>
<body>
<h1>PiCamera MJPEG Streaming Demo</h1>
<img src="stream.mjpg" width="640" height="480" />
</body>
</html>
"""

class StreamingOutput(object):
def __init__(self):
self.lock = Lock()
self.frame = io.BytesIO()
self.clients = []

def write(self, buf):
died = []
if buf.startswith(b'\xff\xd8'):
# New frame, send old frame to all connected clients
size = self.frame.tell()
if size > 0:
self.frame.seek(0)
data = self.frame.read(size)
self.frame.seek(0)
with self.lock:
for client in self.clients:
try:
client.wfile.write(b'--FRAME\r\n')
client.send_header('Content-Type', 'image/jpeg')
client.send_header('Content-Length', size)
client.end_headers()
client.wfile.write(data)
client.wfile.write(b'\r\n')
except Exception as e:
died.append(client)
self.frame.write(buf)
if died:
self.remove_clients(died)

def flush(self):
with self.lock:
for client in self.clients:
client.wfile.close()

def add_client(self, client):
print('Adding streaming client %s:%d' % client.client_address)
with self.lock:
self.clients.append(client)

def remove_clients(self, clients):
with self.lock:
for client in clients:
try:
print('Removing streaming client %s:%d' % client.client_address)
self.clients.remove(client)
except ValueError:
pass # already removed

class StreamingHandler(server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.send_response(301)
self.send_header('Location', '/index.html')
self.end_headers()
elif self.path == '/index.html':
content = PAGE.encode('utf-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(content))
self.end_headers()
self.wfile.write(content)
elif self.path == '/stream.mjpg':
self.close_connection = False
self.send_response(200)
self.send_header('Age', 0)
self.send_header('Cache-Control', 'no-cache, private')
self.send_header('Pragma', 'no-cache')
self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=--FRAME')
self.end_headers()
output.add_client(self)
else:
self.send_error(404)
self.end_headers()

class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
pass

with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
output = StreamingOutput()
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8000)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()

Once the script is running, visit ``http://your-pi-address:8000/`` with your
web-browser to view the video stream.

.. note::

This recipe assumes Python 3.x (the ``http.server`` module was named
``SimpleHTTPServer`` in Python 2.x)


.. _record_and_capture:

Capturing images whilst recording
Expand Down

0 comments on commit c92f65e

Please sign in to comment.