Guides / vision
Streaming Action 4 video into OpenCV
A DJI Osmo Action 4 with no cable to the ground, into Python at under a second of latency, over a phone hotspot. MediaMTX, RTMP in, RTSP out, YOLO on every frame.
vision20 min11 October 2026
Draft. Measured on an Apple Silicon Mac, October 2026.
The Action 4 has a livestream mode that pushes RTMP to any URL you give it. That is the whole trick: run an RTMP server on the laptop, point the camera at it, read the result in OpenCV.
What you need
- DJI Osmo Action 4 and the DJI Mimo app on a phone
- A WiFi network the camera and the laptop share. A phone hotspot works. Use 5 GHz and turn off “Maximise Compatibility” so it stays clear of the 2.4 GHz band an RC link uses.
- MediaMTX (one binary,
brew install mediamtx) - Python with
opencv-python-headlessand, for detection,ultralytics
1. The server
mediamtx.yml, the whole of it:
paths:
drone:
Start it with mediamtx mediamtx.yml. It listens for RTMP on 1935 and serves the same stream as RTSP on 8554. Find the Mac’s IP on the hotspot with ipconfig getifaddr en0.
2. The camera
In Mimo: connect the camera, Livestream, Custom RTMP, URL rtmp://<mac-ip>:1935/drone. 1080p30 is fine; 480p is plenty for detection, since the model resizes to 640 wide anyway. Start. The camera keeps streaming on its own once Mimo has started it, so the phone can go in your pocket.
3. OpenCV
import os, cv2
os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "rtsp_transport;tcp|fflags;nobuffer|flags;low_delay"
cap = cv2.VideoCapture("rtsp://127.0.0.1:8554/drone", cv2.CAP_FFMPEG)
while True:
ok, frame = cap.read()
if not ok:
break
# frame is a BGR numpy array
The environment variable is the important line. Without nobuffer OpenCV queues a couple of seconds of video and you are always looking at the past.
Read frames in a thread that only ever keeps the latest one. Any consumer that is slower than the camera, a detector, a model call, must drop frames, not queue them.
4. Detection
from ultralytics import YOLO
model = YOLO("yolo11n.pt")
results = model.predict(frame, imgsz=640, conf=0.5, device="mps", verbose=False)
YOLO11n on the Mac’s GPU runs at about twelve milliseconds a frame. The nano model is enough for “is there a person in view”; it will also happily call your own propeller blades “airplane”, which is worth knowing before you trust a class name.
Latency, measured
| Leg | Time |
|---|---|
| Camera encoder | about 0.5 s |
| RTMP in, RTSP out, OpenCV read | under 1 s |
| YOLO11n per frame | 12 ms |
Fine for “what do you see” and for a person guard. Not fine for steering anything from vision, which is why that stays off in the drone build.
Gotchas
- macOS camera permission. If you ever use the Action 4 in USB webcam mode, OpenCV can only trigger the permission prompt from a real Terminal window, not from tmux or an editor. Run once from Terminal, click Allow.
- Heat. The Action 4 throttles after 30 to 45 minutes of streaming in warm air. Battery is about an hour.
- Serve it to the audience. A tiny aiohttp server that returns the latest JPEG and an MJPEG stream of it with boxes drawn is a few dozen lines and makes the demo legible to people who are not reading the terminal.