8

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.

John
  • 2,551
  • 3
  • 30
  • 55
  • 1
    I seem to be running into issues in production when using StreamingHttpResponse with django channels. I'm seeing a runaway process that eats a ton of CPU and the last request it processed was returning a StreamingHttpResponse to the client via django channels. Did you ever get StreamingHttpResponse working with django channels? Or just decided it was the culprit and disabled django channels? – Sachin Rekhi May 15 '19 at 02:23
  • 1
    It's been a while since I was working on this, but if I recall, I never did fix this. I think I just got around it by using the channels websocket to return the camera frames instead of StreamingHttpResponse() – John May 16 '19 at 20:53
  • Hi @John canyou share a sample of how you used opencv to return the frames instead of StreamingHttpResponse()? – Remy Jun 07 '21 at 17:15

1 Answers1

1

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

Raydel Miranda
  • 13,825
  • 3
  • 38
  • 60
  • Seems to work perfectly fine without `django channels` enabled. The Streaming response gets interrupted, and the class' `__del__` method gets run, which is exactly what I was hoping for. I'm just not sure why it doesn't work with `channels` enabled – John Feb 19 '19 at 23:47