##  4.10. 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 been 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
](https://github.com/waveform80/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 [ ` http.server  `
](https://docs.python.org/3.4/library/http.server.html#module-http.server
"\(in Python v3.4\)") module to make a simple video streaming server:

    
    
    import io
    import picamera
    import logging
    import socketserver
    from threading import Condition
    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.frame = None
            self.buffer = io.BytesIO()
            self.condition = Condition()
    
        def write(self, buf):
            if buf.startswith(b'\xff\xd8'):
                # New frame, copy the existing buffer's content and notify all
                # clients it's available
                self.buffer.truncate()
                with self.condition:
                    self.frame = self.buffer.getvalue()
                    self.condition.notify_all()
                self.buffer.seek(0)
            return self.buffer.write(buf)
    
    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.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()
                try:
                    while True:
                        with output.condition:
                            output.condition.wait()
                            frame = output.frame
                        self.wfile.write(b'--FRAME\r\n')
                        self.send_header('Content-Type', 'image/jpeg')
                        self.send_header('Content-Length', len(frame))
                        self.end_headers()
                        self.wfile.write(frame)
                        self.wfile.write(b'\r\n')
                except Exception as e:
                    logging.warning(
                        'Removed streaming client %s: %s',
                        self.client_address, str(e))
            else:
                self.send_error(404)
                self.end_headers()
    
    class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
        allow_reuse_address = True
        daemon_threads = True
    
    wit