Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement video calls over Django Channels?

I would like to create an app that enables users to make video calls. I found some insight here, but unfortunately, the answer doesn't explain neither what third party services can be used, nor any meaningful insight on integrating WebRTC.

I have managed to create a Django WebSocket based live chat using Channels, and I figured the "get user media". But I was totally overwhelmed by Peer2Peer connection.

How can I integrate the WebRTC over Django Channels? Or is there a simpler way/third party service I could use?

my consumers.py:

from channels.generic.websocket import AsyncWebsocketConsumer
from channels.consumer import AsyncConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name
        self.user = self.scope["user"].username

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']
        user = text_data_json['user']

        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message,
                'user': user
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event['message'],
        user = event['user']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message,
            'user': user
        }))

my chat room:

 {% extends 'main/header.html' %}

{% block content %}

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>

<body>

<div class="container">
    <textarea id="chat-log"  class="materialize-textarea" ></textarea><br/>

<div class="input-field col s12 ">
    <input id="chat-message-input" type="text" />
    <a class="waves-effect waves-light btn prefix" id="chat-message-submit"><i class="material-icons right">send</i></a>

</div>

</div>

</body>
<script>
    var roomName = "{{ room_name|escapejs }}";

    var chatSocket = new WebSocket(
        'ws://' + window.location.host +
        '/ws/chat/' + roomName + '/');

    chatSocket.onmessage = function(e) {
        var data = JSON.parse(e.data);
        console.info(e)
        var message = data['message'];
        var user = data['user'];
        document.querySelector('#chat-log').value += (user +": " + message + '\n');
        elem = document.getElementById("chat-log")
        M.textareaAutoResize(elem);
    };

    chatSocket.onclose = function(e) {
        console.error('Chat socket closed unexpectedly');
    };

    document.querySelector('#chat-message-input').focus();
    document.querySelector('#chat-message-input').onkeyup = function(e) {
        if (e.keyCode === 13) {  // enter, return
            document.querySelector('#chat-message-submit').click();
        }
    };

    document.querySelector('#chat-message-submit').onclick = function(e) {
        var messageInputDom = document.querySelector('#chat-message-input');
        var message = messageInputDom.value;
        var user = "{{user.username}}"
        chatSocket.send(JSON.stringify({
            'message': message,
            'user': user

        }));

        messageInputDom.value = '';
    };
</script>

{% endblock  %}

my client.js:

window.onload = function () {
   var constraints = { audio: true, video: { width: 1280, height: 720 } }; 

navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) {
var video = document.querySelector('video');
video.srcObject = mediaStream;
video.onloadedmetadata = function(e) {
video.play();
};
})
.catch(function(err) { console.log(err.name + ": " + err.message); });
};

my video vindow:

<html>
 {% load static %}
   <head> 
      <meta charset = "utf-8"> 
      <script src = "{% static 'main/js/client.js' %}"></script>
   </head>

   <body> 
      <video controls="" id = 'video-player' name = 'video-player' autoplay></video> 

   </body> 

</html> 
like image 990
Petru Tanas Avatar asked Jan 21 '20 15:01

Petru Tanas


1 Answers

A simple realtime-chat application with no media transfer can be implemented using just WebSockets whereas a realtime media transfer (audio, video) needs different protocol altogether.

WebRTC is natively supported by all modern browsers and can be used for realtime media transfer.

While webRTC can be used to transfer media, the handshaking between devices has to happen outside of this.

Even though an api/http request of any sort is plausible for this, the realtime data-transfer ability of websockets are a good to have.

Enough of that being said, since you have already chosen django-channels, you can use that to make the handshake.

To establish a WebRTC connection, the client-A needs to generate an offer with its SDP (Session Description Protocol) and send it over to Client-B, to which the Client-B has to send back its SDP as an answer.

At this point both clients will be aware of what codecs and codec-parameters are to be used while transferring the data. However, both the clients are not aware or sure of what network settings needs to be used or how the data itself needs to be transmitted.

That is where the ICE (Interactive Connectivity Establishment) negotiations comes into picture. Both the clients will send back and forth the higher to lower negotiations until they agree upon one. And that is where the media stream starts to flow.

The above is just a drop from the ocean, however, it is not hard to get a working prototype if you follow through MDN's topic on signalling and video calling.

like image 187
Midhun C Nair Avatar answered Oct 06 '22 16:10

Midhun C Nair