django / daphne

Django Channels HTTP/WebSocket server
BSD 3-Clause "New" or "Revised" License
2.32k stars 256 forks source link

issuse when i using daphe in my django app with streaminghttpresponse #463

Closed ilayasis closed 5 months ago

ilayasis commented 1 year ago

I add channels to my project and it's creating a problem, I have a stream func that streama video with streaminghttpresponse , and that work well before I add asgi/channels to run the server . I saw that it's some bug that will be fixed in django 4.2 ,but I look for solution for now/ or if someone suggests another library for a socket that will not cause a similar problem

this is my code

Django v - 4.0 channels - 3.0.5


views.py

   def stream(source,startVideo):
        if startvideo : 
            url = source + str('.mp4')
            cap = cv2.VideoCapture(url)
            while (cap.isOpened()):
                ret, frame = cap.read()
                if not ret:
                    check = False
                    break
                results = model(frame,augment=False,size=320)
                det = results.pred[0]
                results.render()
                annotator = Annotator(frame,line_width=2, pil=not ascii)
                im0 = annotator.result()
                image_bytes = cv2.imencode('.jpg', im0)[1].tobytes()

                yield (b'--frame\r\n'
                      b'Content-Type: image/jpeg\r\n\r\n' + image_bytes + b'\r\n')

            cap.release()
            cv2.destroyAllWindows()

@csrf_exempt
def video_feed(request):
    if request.method == 'POST':
        global startvideo,start,content
        startvideo=True
        content = request.GET.get('url','not')
    return StreamingHttpResponse(stream(content,startvideo), content_type='multipart/x-mixed-replace; boundary=frame')  
ASGI.PY

application = ProtocolTypeRouter({
  'http': get_asgi_application(),
  'websocket':AuthMiddlewareStack(URLRouter(
    webcam.routing.websocket_urlpatterns
  ))
})
setting.py

INSTALLED_APPS = [
    'channels',
     ......
    'webcam',
     ......

]
ASGI_APPLICATION = 'stream.asgi.application'
routing.py

websocket_urlpatterns = [
     re_path(r'ws/socket-server/',consumers.ChatConsumer.as_asgi()),

]
consumers.py

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_group_name = 'test'
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def receive(self,text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        await self.channel_layer.group_send(
            self.room_group_name, {
                'type':'chat_message',
                'message':message
            }
        )

    async def chat_message(self,event):
        message = event['message']

        await self.send(text_data=json.dumps({
            'type':'chat',
            'message':message
        }))
ilayasis commented 1 year ago

Edit with more info

carltongibson commented 1 year ago

Async support for StreamingHTTPResponse is being added in Django 4.2.

ilayasis commented 1 year ago

Async support for StreamingHTTPResponse is being added in Django 4.2.

Hi, tnx for the quick response, Django 4.2 version by the docs is UNDER DEVELOPMENT and I looking for a solution for right now.

carltongibson commented 1 year ago

Then you'd need to patch The response object (and asgi handler) in Django.

If you find the commit in Django you can get the patch you need.

ilayasis commented 1 year ago

I'm actually not familiar with how to do it actually, u have some suggestions on how I can do it in my code?

ilayasis commented 1 year ago

Or maybe you have some idea which library I can use in Django if want to create a socket, in the end, my goal is to "listen" for only one variable. the consumer that In my code for now it's just for the example.

themagicalmammal commented 5 months ago

You have to make the whole code async so test with a initial for loop and yield function and slowly start adding more code to it. And split it into more functions and use await with run_in_executor to make the sync code kind of like a async function.

And cap has to be used like

with concurrent.futures.ThreadPoolExecutor():
    while cap.isOpened():
        success, frame = await asyncio.to_thread(cap.read)

else you will get the async_generator error.