#!/usr/bin/env python3 import http.server import socketserver import subprocess import threading import time BOUNDARY = b'--frame' LATEST_JPEG = None LATEST_LOCK = threading.Lock() WIDTH = 1280 HEIGHT = 720 FPS = 10 PORT = 8081 def camera_reader(): global LATEST_JPEG cmd = [ 'rpicam-vid', '--timeout', '0', '--nopreview', '--width', str(WIDTH), '--height', str(HEIGHT), '--framerate', str(FPS), '--codec', 'mjpeg', '-o', '-', ] while True: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, bufsize=0) buffer = bytearray() try: while True: chunk = proc.stdout.read(4096) if not chunk: break buffer.extend(chunk) while True: start = buffer.find(b'\xff\xd8') if start == -1: if len(buffer) > 1024 * 1024: del buffer[:-2] break end = buffer.find(b'\xff\xd9', start + 2) if end == -1: if start > 0: del buffer[:start] break jpeg = bytes(buffer[start:end + 2]) del buffer[:end + 2] with LATEST_LOCK: LATEST_JPEG = jpeg finally: proc.kill() proc.wait() time.sleep(1) class ReusableThreadingTCPServer(socketserver.ThreadingTCPServer): allow_reuse_address = True class Handler(http.server.BaseHTTPRequestHandler): def do_HEAD(self): if self.path in ('/', '/index.html'): self.send_response(200) self.send_header('Content-Type', 'text/html; charset=utf-8') self.end_headers() return if 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() return self.send_error(404) def do_GET(self): if self.path in ('/', '/index.html'): body = b'