GUI verbessert und Admin console eingebaut
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
#!/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'<html><body><img src="/stream.mjpg" /></body></html>'
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'text/html; charset=utf-8')
|
||||
self.send_header('Content-Length', str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
return
|
||||
|
||||
if self.path != '/stream.mjpg':
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
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()
|
||||
|
||||
while True:
|
||||
with LATEST_LOCK:
|
||||
frame = LATEST_JPEG
|
||||
if frame is None:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
try:
|
||||
self.wfile.write(BOUNDARY + b'\r\n')
|
||||
self.wfile.write(b'Content-Type: image/jpeg\r\n')
|
||||
self.wfile.write(f'Content-Length: {len(frame)}\r\n\r\n'.encode('ascii'))
|
||||
self.wfile.write(frame)
|
||||
self.wfile.write(b'\r\n')
|
||||
self.wfile.flush()
|
||||
time.sleep(1.0 / FPS)
|
||||
except (BrokenPipeError, ConnectionResetError):
|
||||
break
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
threading.Thread(target=camera_reader, daemon=True).start()
|
||||
with ReusableThreadingTCPServer(('0.0.0.0', PORT), Handler) as server:
|
||||
server.daemon_threads = True
|
||||
server.serve_forever()
|
||||
Reference in New Issue
Block a user