Rpi Camera Websocket Setup

jasonr

Fri Jun 27 2025 03:59:55 PM PDT

Streaming MJPEG from Raspberry Pi to Windows via UDP

Overview

This guide explains how to stream MJPEG video from a Raspberry Pi using `libcamera-jpeg` to a Windows machine using UDP sockets, and display it in real-time using FastAPI and HTML.

Requirements

- Raspberry Pi with camera module and libcamera installed
- Windows machine with Python 3.12 and FastAPI installed
- Local network connection between the two devices

Step 1: Raspberry Pi Setup

On the Raspberry Pi, create and run this script:

#!/usr/bin/env python3
import socket
import time

UDP_IP = "192.168.0.x"  # Windows machine IP
UDP_PORT = 5005
CHUNK_SIZE = 4096
PIPE_PATH = "/tmp/stream.mjpeg"

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

def find_frame_end(buffer):
    try:
        return buffer.index(b'\xff\xd9') + 2
    except ValueError:
        return None

buffer = bytearray()

with open(PIPE_PATH, 'rb') as f:
    while True:
        chunk = f.read(CHUNK_SIZE)
        if not chunk:
            continue
        buffer.extend(chunk)
        eof = find_frame_end(buffer)
        if eof:
            frame = buffer[:eof]
            buffer = buffer[eof:]
            for i in range(0, len(frame), CHUNK_SIZE):
                sock.sendto(frame[i:i+CHUNK_SIZE], (UDP_IP, UDP_PORT))
        time.sleep(0.01)

Run this command to start the MJPEG stream:

libcamera-jpeg -t 0 --width 320 --height 240 --quality 50 -o /tmp/stream.mjpeg

Step 2: Windows FastAPI Receiver

Save and run this FastAPI script on your Windows machine:

import socket
import asyncio
import threading
import base64
import queue
import numpy as np
import cv2
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse
import uvicorn

UDP_IP = "0.0.0.0"
UDP_PORT = 5005
BUFFER_SIZE = 65536
frame_queue = queue.Queue(maxsize=10)

def udp_receiver():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((UDP_IP, UDP_PORT))
    buffer = bytearray()
    def find_jpeg_end(buf):
        try:
            return buf.index(b'\xff\xd9') + 2
        except ValueError:
            return None
    while True:
        data, _ = sock.recvfrom(BUFFER_SIZE)
        buffer.extend(data)
        while True:
            eof = find_jpeg_end(buffer)
            if eof is None:
                break
            jpeg = buffer[:eof]
            buffer = buffer[eof:]
            frame = cv2.imdecode(np.frombuffer(jpeg, dtype=np.uint8), 1)
            if frame is not None:
                ret, encoded = cv2.imencode('.jpg', frame)
                if ret:
                    if frame_queue.qsize() > 5:
                        frame_queue.get()
                    frame_queue.put(encoded.tobytes())

app = FastAPI()

@app.get("/")
async def get_page():
    return HTMLResponse("""    <!DOCTYPE html>
    <html>
    <head><title>UDP Stream Viewer</title></head>
    <body style="background:black;text-align:center;">
        <h2 style="color:white;">Live Stream</h2>
        <img id="stream" width="640"/>
        <p style="color:lime;">Bytes: <span id="info">0</span></p>
        <script>
            const img = document.getElementById('stream');
            const info = document.getElementById('info');
            const ws = new WebSocket(`ws://${location.host}/ws`);
            ws.onmessage = event => {
                img.src = 'data:image/jpeg;base64,' + event.data;
                info.textContent = event.data.length;
            };
        </script>
    </body>
    </html>
    """)

@app.websocket("/ws")
async def ws(websocket: WebSocket):
    await websocket.accept()
    while True:
        if not frame_queue.empty():
            frame = frame_queue.get()
            b64 = base64.b64encode(frame).decode()
            await websocket.send_text(b64)
        await asyncio.sleep(0.01)

if __name__ == "__main__":
    threading.Thread(target=udp_receiver, daemon=True).start()
    uvicorn.run(app, host="0.0.0.0", port=8080)

Result

Once everything is set up and running, you can access the live stream by opening:

http://192.168.0.x:8080



programming Raspberry Pi