I’m very new to Python and have been struggling with the code for a wildlife camera project on the Pi. I’ve managed to get the PIR sensor and automatic camera capture working, but I’d like to have a streaming mode in the same script which allows me to view the camera feed on my phone when I’m first positioning the camera to check it’s got a good view (toggled by a button wired to one of the GPIO pins).
I've managed to get the button to work to move from capture mode to streaming mode, and the streaming script appears to work perfectly. But the serve_forever command means that it’s permanently streaming (unsurprisingly!) and won't move out of it to go back to capture. I've tried various edits, but it won’t respond to a GPIO input to tell it to stop streaming and then move on to the camera capture part of the script.
This is the code I've adapted (https://randomnerdtutorials.com/video-streaming-with-raspberry-pi-camera/):
import io import picamera import logging import socketserver from threading import Condition from http import server PAGE="""\ <html> <head> <title>Raspberry Pi - Surveillance Camera</title> </head> <body> <center><h1>Raspberry Pi - Surveillance Camera</h1></center> <center><img src="stream.mjpg" width="640" height="480"></center> </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 with picamera.PiCamera(resolution='640x480', framerate=24) as camera: output = StreamingOutput() #Uncomment the next line to change your Pi's Camera rotation (in degrees) #camera.rotation = 90 camera.start_recording(output, format='mjpeg') try: address = ('', 8000) server = StreamingServer(address, StreamingHandler) server.serve_forever() finally: camera.stop_recording()
Is there a way I can adapt this script so I can end the stream when there’s a GPIO input? Or a way of implementing a timeout on the server, so it only streams for a few seconds, and then moves on to check the GPIO input and either repeat the streaming or go onto capture mode?
I’ve seen a few examples using KeyboardInterrupt but I think this might work differently, with Python always listening out for that, rather than the GPIO signals which need to have a line of code to check their status (and obviously can't be run while serve_forever is being executed).
I’ve seen lots of references to threading for ending streaming servers. But every example I’ve seen is far too technical for me, and I have no clue how to adapt it for my current project. So if anyone is able to explain precisely what I need to do with my code to use threading for this, that would be incredibly helpful.
Many thanks in advance, and apologies for my complete lack of knowledge!