I'm building a web service where I display the stream from an IP camera. I'm using rtsp and opencv-python
to read from the camera. I'm using WebSockets to send the frames from the backend to the frontend. For my backend server, I'm using FastAPI and Uvicorn. Here is what my current WebSocket endpoint looks like.
@app.websocket("/ws/video_feed")
async def websocket_video_feed(websocket: WebSocket):
await websocket.accept()
camera = cv2.VideoCapture(camera_source)
if not camera.isOpened():
raise RuntimeError("Could not start camera.")
try:
while True:
ret, img = camera.read()
if ret:
try:
_, frame = cv2.imencode(".jpg", img)
await websocket.send_json(
{
"type": "frame",
"payload": str(base64.b64encode(frame).decode("utf-8")),
}
)
except:
continue
else:
camera = cv2.VideoCapture(camera_source)
if not camera.isOpened():
raise RuntimeError("Could not start camera.")
continue
except WebSocketDisconnect:
print("WS Client disconnected.")
return
The issue is that when I switch pages on the front end and the WebSocket connection is closed, the backend will sometimes freeze and not respond to any other API requests, particularly after encountering some h264 decoding issues. I found a stack overflow thread talking about splitting up the image reading and writing into separate threads and using a simple first-in-first-out queue but I still encounter the same issue.
q = queue.Queue()
def Receive():
camera = cv2.VideoCapture(camera_source)
if not camera.isOpened():
raise RuntimeError("Could not start camera.")
while True:
if camera.isOpened():
ret, img = camera.read()
if ret:
q.put(img)
else:
camera.release()
camera = cv2.VideoCapture(camera_source)
if not camera.isOpened():
raise RuntimeError("Could not start camera.")
continue
else:
camera.release()
camera = cv2.VideoCapture(camera_source)
if not camera.isOpened():
raise RuntimeError("Could not start camera.")
continue
@app.websocket("/ws/video_feed")
async def websocket_video_feed(websocket: WebSocket):
await websocket.accept()
try:
while True:
try:
if q.empty() != True:
img = q.get()
_, frame = cv2.imencode(".jpg", img)
await websocket.send_json(
{
"type": "frame",
"payload": str(base64.b64encode(frame).decode("utf-8")),
}
)
except:
continue
ack = await websocket.receive_json()
if ack["type"] == "close":
return
except WebSocketDisconnect:
print("WS Client disconnected.")
return
if __name__ == '__main__':
p1 = threading.Thread(target=Receive)
p1.start()
uvicorn.run(app, host="0.0.0.0", port=5000)
What is the proper way to do this?