Arducam USB camera live video lags ~50-100ms in browser on macOS despite MJPEG output

Hi everyone, I’m trying to stream live video from a USB camera to a localhost webpage and display it in the browser in near real time. Right now I’m seeing maybe 50-100 ms of lag, and most of it seems to come from converting the OpenCV frames to JPEG. The weird part is the camera already outputs MJPEG/JPEG, so that conversion should not be necessary.

The camera is an Arducam USB Fisheye Camera Module, 2MP CMOS IMX291 160 degree fisheye mini UVC USB2.0 webcam board with microphone. I’m on macOS, but this also needs to work on Linux later.

Here’s roughly what I’m running:

camera = cv2.VideoCapture(0)
while True:
ret, frame = camera.read()
_, jpeg_frame = cv2.imencode(“.jpg”, frame)
jpeg_bytes = jpeg_frame.tobytes()
yield (b"–frame\r\n" b"Content-Type: image/jpeg\r\n\r\n" + jpeg_bytes + b"\r\n")

I already tried ffmpeg and gstreamer but couldn’t get either working. Any ideas?

From what you’re describing, this is likely because OpenCV isn’t actually reading the raw MJPEG data from the camera. The Arducam outputs MJPEG, but OpenCV reads frames through its own decode path and doesn’t grab that raw MJPEG stream directly, so you end up re-encoding to JPEG and paying for it in latency.

If/when you’re on Linux, the cleaner approach is to use the v4l2 interface to read the MJPEG data directly instead of going through OpenCV’s VideoCapture path. If you’d rather not write that code yourself, gstreamer is available on Linux and can do the same thing. Arducam has a walkthrough here:

https://docs.arducam.com/UVC-Camera/Appilcation-Note/OpenCV-Python-GStreamer-on-linux/#4-streaming-the-video-using-gstreamer

For Linux, v4l2 or gstreamer is the way to go to avoid OpenCV’s MJPEG limitations.

Thanks. I’m on macOS right now for testing, but the final setup will be Linux. I did try gstreamer before and just couldn’t get the pipeline right - maybe I’ll revisit it once I move to the Linux box.

One thing I should mention: my main goal is showing the stream in a browser (localhost page). Is there something simpler than writing my own Flask/OpenCV streaming loop for that?

For the browser part, I’d actually look at mjpg-streamer. It’s built for exactly this - taking the MJPEG from the camera and serving it to a browser with low latency:

That would let you skip the OpenCV encode step entirely and just point the browser at the stream URL.

Quick follow-up on a related issue. Some of my Arducam camera models show the video stream off center. I’m trying to identify the exact serial number or model of the camera so I can figure out which ones behave this way, but I only have Linux and Mac available right now - no Windows PC.

I did get the lowest latency working using navigator.mediaDevices.enumerateDevices() in JavaScript to list the video devices:

const videoDevices = await navigator.mediaDevices.enumerateDevices();

I have several different Arducam models here and most work fine, but a couple have this off-center issue.

On that identification question - unfortunately Arducam only provides software for reading the camera serial number on Windows. There isn’t currently a Linux or Mac utility for reading the serial number/model from these cameras.

So as things stand, there’s no Linux/Mac solution for that right now. You’d need a Windows machine to read the serial number. The model identification itself won’t be possible from Linux/Mac with Arducam’s tools at this time.