0

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?

Paul Côté
  • 75
  • 1
  • 10

0 Answers0