Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you stop a StreamingHttpResponse in django when the client disconnects?

My django server serves a video feed, as a jpeg stream, one frame at a time.

It looks like this:

class VideoCamera():
    def __init__(self):
        # code

    def get_frame(self):
        # code
        return frame

def gen(camera):
    while True:
        yield camera.get_frame()

def view_cam(request):
    return StreamingHttpResponse(gen(VideoCamera()), content_type="multipart/x-mixed-replace;boundary=frame")

It's a live camera feed, so there's no end to the stream. I need it to be interrupted when the client disconnects, but so far, I can't figure out how to detect that a client disconnected.

Am I missing something?

Edit:

To eliminate anything to do with the camera, I did this:

def gen():
    for i in range(1000):
        time.sleep(1)
        print(i)
        yield i

def view_cam(request):
    return StreamingHttpResponse(gen(), content_type="multipart/x-mixed-replace;boundary=frame")

and connected to my view with curl -N http://localhost/my_app/view_cam/. It streams the numbers, and when I stop curl with Ctrl+C, the generator just keeps running indefinitely, not noticing that the client disappeared. If I run and stop curl a few more times, I have multiple instances of my gen() function running, which is exactly what happens with the camera.

Edit 2:

This project uses Django Channels. I just noticed that if I disable channels by commenting it out in my settings.py, the above example works perfectly. I didn't think channels was related to the problem, but apparently, it is - somehow.

The channels development server does actually detect the disconnect after 10 seconds (not immediately like the default django server), and show this:

Application instance call() running at /home/pi/paperless_clipboard/venv3/lib/python3.5/site-packages/channels/http.py:213> wait_for=._call_check_cancel() at /usr/lib/python3.5/asyncio/futures.py:452, Task._wakeup()]>> for connection took too long to shut down and was killed.

but despite the message that something was killed, gen() keeps running, printing numbers to the terminal.

like image 643
John Avatar asked Feb 19 '19 23:02

John


1 Answers

You can't according the docs:

Performance considerations Django is designed for short-lived requests. Streaming responses will tie a worker process for the entire duration of the response. This may result in poor performance.

Generally speaking, you should perform expensive tasks outside of the request-response cycle, rather than resorting to a streamed response.

https://docs.djangoproject.com/en/2.1/ref/request-response/#streaminghttpresponse-objects

like image 145
Raydel Miranda Avatar answered Nov 18 '22 17:11

Raydel Miranda