How can I have a stream like rtsp, that is accesible in a program on another computer?
3 Answers
I solved the problem by using the following code from here.
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
with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
output = StreamingOutput()
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8000)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()
Now I have a constantly updating mjpg-file at a local server and I can e.g. refer to the mjpg link (something like http://raspberrypi:8000/stream.mjpg) in a html/node/electron application from somewhere else, which is exactly what I wanted. First I thought it would be better to do it via rtsp or some streaming protocol, but it turned out, that the html/mjpg way has actually much less delay (almost zero being streamed over local network) than the rtsp way (maybe 3 seconds).
Cheers
If both the Pi and the computer are on the same network, this can easily be done using ffmpeg, to both capture the stream from the camera and broadcast it over the network.
I send a stream of JPEG files using zmq which is a kind of wrapper over sockets that handles most of the ugly for you. It's not a standard protocol though.
from picamera import PiCamera
import io
import zmq
context = zmq.Context()
socket = context.socket(zmq.PUB)
# CONFLATE=1 disables the send queue so it will drop frames instead of creating latency.
socket.setsockopt(zmq.CONFLATE, 1)
socket.bind("tcp://*:12103")
camera = PiCamera()
camera.resolution = (320, 240)
camera.framerate = 10
camera.exposure_mode = 'sports'
camera.rotation = 0
stream = io.BytesIO()
for _ in camera.capture_continuous(stream, 'jpeg', use_video_port=True):
stream.truncate()
stream.seek(0)
socket.send(stream.read())
stream.seek(0)
At the receiving end:
import zmq
context = zmq.Context()
video_socket = .context.socket(zmq.SUB)
video_socket.setsockopt(zmq.CONFLATE, 1)
video_socket.setsockopt(zmq.SUBSCRIBE, b"")
video_socket.connect("tcp://192.168.1.2" + ":12103")
def get_image():
# Read the latest image. None if there are no new images since the last read.
if video_socket.poll(timeout=0) == zmq.POLLIN:
return video_socket.recv()
else:
return None
You can also use raspivid to streams video from the camera without writing your own code.