From 1ad5f4a3065dfe38c7a45864665a66d93e633350 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Sun, 7 Dec 2025 16:15:45 +0100 Subject: [PATCH] Initial commit --- README.md | 0 picam.service | 19 ++++ rpi_camera_surveillance_system.py | 38 ++++++++ rpi_camera_surveillance_system_mediatx.py | 101 ++++++++++++++++++++++ rpi_camera_surveillance_system_mjpeg.py | 96 ++++++++++++++++++++ run.sh | 2 + 6 files changed, 256 insertions(+) create mode 100644 README.md create mode 100755 picam.service create mode 100644 rpi_camera_surveillance_system.py create mode 100644 rpi_camera_surveillance_system_mediatx.py create mode 100644 rpi_camera_surveillance_system_mjpeg.py create mode 100755 run.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/picam.service b/picam.service new file mode 100755 index 0000000..1a7a691 --- /dev/null +++ b/picam.service @@ -0,0 +1,19 @@ +[Unit] +Description=RaspberryPi Camera Web Service + +# Optional: Wartet, bis das Netzwerk bereit ist +After=network.target + +[Service] +# Der vollständige Pfad zu deinem Skript +ExecStart=/home/jens/work/picam/run.sh +# Startet den Service bei einem Fehler neu +Restart=always +# Der Benutzer, unter dem der Service läuft (z.B. root, wenn nötig) +#User=dein_benutzername +# Optional: Arbeitsverzeichnis +#WorkingDirectory=/pfad/zum/projektordner + +[Install] +# Sorgt dafür, dass der Service beim Booten startet +WantedBy=multi-user.target diff --git a/rpi_camera_surveillance_system.py b/rpi_camera_surveillance_system.py new file mode 100644 index 0000000..b6f557f --- /dev/null +++ b/rpi_camera_surveillance_system.py @@ -0,0 +1,38 @@ +#!/usr/bin/python3 + +import socket +from threading import Event + +from picamera2 import Picamera2 +from picamera2.encoders import JpegEncoder +from picamera2.outputs import PyavOutput + +picam2 = Picamera2() +video_config = picam2.create_video_configuration({"size": (1280, 720), 'format': 'YUV420'}) +picam2.configure(video_config) + +encoder = H264Encoder(bitrate=10000000) +encoder.audio = True + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(("0.0.0.0", 8888)) + + while True: + print("Waiting") + sock.listen() + + conn, addr = sock.accept() + print("Connected") + + output = PyavOutput(f"pipe:{conn.fileno()}", format="mpeg") + event = Event() + output.error_callback = lambda e: event.set() # noqa + + picam2.start_recording(JpegEncoder(), output) + + event.wait() + print("Disconnected") + + picam2.stop_recording() + diff --git a/rpi_camera_surveillance_system_mediatx.py b/rpi_camera_surveillance_system_mediatx.py new file mode 100644 index 0000000..223450d --- /dev/null +++ b/rpi_camera_surveillance_system_mediatx.py @@ -0,0 +1,101 @@ +#!/usr/bin/python3 + +# Mostly copied from https://picamera.readthedocs.io/en/release-1.13/recipes2.html +# Run this script, then point a web browser at http::8000 +# Note: needs simplejpeg to be installed (pip3 install simplejpeg). + +import io +import logging +import socketserver +from http import server +from threading import Condition + +from picamera2 import Picamera2 +from picamera2.encoders import H264Encoder +from picamera2.outputs import PyavOutput + +PAGE = """\ + + +picamera2 MJPEG streaming demo + + +

Picamera2 MJPEG Streaming Demo

+ + + +""" + + +class StreamingOutput(io.BufferedIOBase): + def __init__(self): + self.frame = None + self.condition = Condition() + + def write(self, buf): + with self.condition: + self.frame = buf + self.condition.notify_all() + + +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 + + +picam2 = Picamera2() +picam2.configure(picam2.create_video_configuration(main={"size": (640, 480)})) +#output = StreamingOutput() + +encoder = H264Encoder(bitrate=10000000) +output = PyavOutput("rtsp://127.0.0.1:8554/cam", format="rtsp") + +picam2.start_recording(encoder, output) + +try: + address = ('', 8554) + server = StreamingServer(address, StreamingHandler) + server.serve_forever() +finally: + picam2.stop_recording() + diff --git a/rpi_camera_surveillance_system_mjpeg.py b/rpi_camera_surveillance_system_mjpeg.py new file mode 100644 index 0000000..8336bae --- /dev/null +++ b/rpi_camera_surveillance_system_mjpeg.py @@ -0,0 +1,96 @@ +#!/usr/bin/python3 + +# Mostly copied from https://picamera.readthedocs.io/en/release-1.13/recipes2.html +# Run this script, then point a web browser at http::8000 +# Note: needs simplejpeg to be installed (pip3 install simplejpeg). + +import io +import logging +import socketserver +from http import server +from threading import Condition + +from picamera2 import Picamera2 +from picamera2.encoders import JpegEncoder +from picamera2.outputs import FileOutput + +PAGE = """\ + + +picamera2 MJPEG streaming demo + + +

Picamera2 MJPEG Streaming Demo

+ + + +""" + + +class StreamingOutput(io.BufferedIOBase): + def __init__(self): + self.frame = None + self.condition = Condition() + + def write(self, buf): + with self.condition: + self.frame = buf + self.condition.notify_all() + + +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 + + +picam2 = Picamera2() +picam2.configure(picam2.create_video_configuration(main={"size": (640, 480)})) +output = StreamingOutput() +picam2.start_recording(JpegEncoder(), FileOutput(output)) + +try: + address = ('', 8000) + server = StreamingServer(address, StreamingHandler) + server.serve_forever() +finally: + picam2.stop_recording() diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..650171a --- /dev/null +++ b/run.sh @@ -0,0 +1,2 @@ +#!/bin/env bash +exec python3 /home/jens/work/picam/rpi_camera_surveillance_system_mjpeg.py