I developed using django channels to implement video chat and message chat.(I was referring to a YouTuber's course.)
When I first completed the development, I noticed that webrtc works only in the local network, and I found out that a stun/turn server was needed.
So, I created a separate EC2 and built the stun/turn server and set this to the RTCPeerconnection in the web server.
Stun/turn server test in Trickle ICE is good.
But My video call still works only within the local network. And even within the same network, the connection was very slow.
Server overall configuration.(Webserver with SSL application loadbalancer)
Coturn config
# /etc/default/coturn
TURNSERVER_ENABLED=1
# /etc/turnserver.conf
# STUN server port is 3478 for UDP and TCP, and 5349 for TLS.
# Allow connection on the UDP port 3478
listening-port=3478
# and 5349 for TLS (secure)
tls-listening-port=5349
# Require authentication
fingerprint
lt-cred-mech
server-name=mysite.com
realm=mysite.com
# Important:
# Create a test user if you want
# You can remove this user after testing
user=myuser:userpassword
total-quota=100
stale-nonce=600
# Path to the SSL certificate and private key. In this example we will use
# the letsencrypt generated certificate files.
cert=/etc/letsencrypt/live/stun.mysite.com/cert.pem
pkey=/etc/letsencrypt/live/stun.mysite.com/privkey.pem
# Specify the allowed OpenSSL cipher list for TLS/DTLS connections
cipher-list="~~~~~-SHA512:~~~~~SHA512:~~~~~-SHA384:~~~~~SHA384:~~~-AES256-SHA384"
# Specify the process user and group
proc-user=turnserver
proc-group=turnserver
main.js in Web Server
let mapPeers = {};
let usernameInput = document.querySelector('#username');
let btnJoin = document.querySelector('#btn-join');
let username;
let webSocket;
const iceConfiguration = {
iceServers: [
{ urls:'stun:stun.mysite.com' },
{
username: 'myuser',
credential: 'userpassword',
urls: 'turn:turn.mysite.com'
},
]
}
function webSocketOnMessage(event) {
let parsedData = JSON.parse(event.data);
let peerUsername = parsedData['peer'];
let action = parsedData['action'];
if (username === peerUsername){
return;
}
let receiver_channel_name = parsedData['message']['receiver_channel_name'];
if (action === 'new-peer'){
createOfferer(peerUsername, receiver_channel_name);
return;
}
if (action === 'new-offer'){
let offer = parsedData['message']['sdp']
createAnswerer(offer, peerUsername, receiver_channel_name);
return;
}
if (action === 'new-answer'){
let answer = parsedData['message']['sdp'];
let peer = mapPeers[peerUsername][0];
peer.setRemoteDescription(answer);
return;
}
// console.log('message : ', message)
}
btnJoin.addEventListener('click', () => {
username = usernameInput.value;
console.log('username : ', username);
if (username === ''){
return;
}
usernameInput.value = '';
usernameInput.disabled = true;
usernameInput.style.visibility = 'hidden';
btnJoin.disabled = true;
btnJoin.style.visibility = 'hidden';
let labelUsername = document.querySelector('#label-username');
labelUsername.innerHTML = username;
let loc = window.location;
let wsStart = 'ws://';
if (loc.protocol === 'https:'){
wsStart = 'wss://';
}
let endpoint = wsStart + loc.host + loc.pathname + 'ws/';
console.log(loc.host)
console.log(loc.pathname)
console.log('endpoint: ', endpoint);
webSocket = new WebSocket(endpoint);
console.log('--------', webSocket)
webSocket.addEventListener('open', (e) => {
console.log('Connection opened!');
sendSignal('new-peer', {});
});
webSocket.addEventListener('message', webSocketOnMessage);
webSocket.addEventListener('close', (e) => {
console.log('Connection closed!', e)
});
})
// Media
let localStream = new MediaStream();
const constraints = {
'video': true,
'audio': true
}
const localVideo = document.querySelector('#local-video');
const btnToggleAudio = document.querySelector('#btn-toggle-audio');
const btnToggleVideo = document.querySelector('#btn-toggle-video');
let userMedia = navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
localStream = stream;
localVideo.srcObject = localStream;
localVideo.muted = true;
let audioTracks = stream.getAudioTracks();
let videoTracks = stream.getVideoTracks();
audioTracks[0].enabled = true;
videoTracks[0].enabled = true;
btnToggleAudio.addEventListener('click', () => {
audioTracks[0].enabled = !audioTracks[0].enabled;
if (audioTracks[0].enabled){
btnToggleAudio.innerHTML = 'Audio mute'
return;
}
btnToggleAudio.innerHTML = 'Audio unmute'
});
btnToggleVideo.addEventListener('click', () => {
videoTracks[0].enabled = !videoTracks[0].enabled;
if (videoTracks[0].enabled){
btnToggleVideo.innerHTML = 'Video off'
return;
}
btnToggleVideo.innerHTML = 'Video on'
});
})
.catch(error => {
console.log('Error accessing media devices', error);
});
// Message
let btnSendMsg = document.querySelector('#btn-send-msd');
let messageList = document.querySelector('#message-list');
let messageInput = document.querySelector('#msg');
btnSendMsg.addEventListener('click', sendMsgOnclick);
function sendMsgOnclick() {
let message = messageInput.value;
let li = document.createElement('li');
li.appendChild(document.createTextNode('Me: ' + message));
messageList.appendChild(li);
let dataChannels = getDataChannels();
message = username + ': ' + message;
console.log("---------console.log(dataChannels)----------")
console.log(dataChannels)
for (index in dataChannels){
console.log("---------console.log(index)----------")
console.log(index)
dataChannels[index].send(message);
}
messageInput.value = '';
}
function sendSignal(action, message){
let jsonStr = JSON.stringify({
'peer': username,
'action': action,
"message": message,
});
webSocket.send(jsonStr);
}
function createOfferer(peerUsername, receiver_channel_name) {
let peer = new RTCPeerConnection(iceConfiguration);
console.log('=================================')
console.log(peer)
console.log('=================================')
addLocalTracks(peer);
let dc = peer.createDataChannel('channel');
dc.addEventListener('open', () => {
console.log('connection opened!')
})
dc.addEventListener('message', dcOnMessage);
let remoteVideo = createVideo(peerUsername);
setOnTrack(peer, remoteVideo);
mapPeers[peerUsername] = [peer, dc];
peer.addEventListener('iceconnectionstatechange', () => {
let iceConnectionState = peer.iceConnectionState;
if (iceConnectionState === 'failed' || iceConnectionState === 'disconnected' || iceConnectionState === 'closed'){
delete mapPeers[peerUsername];
if (iceConnectionState !== 'closed'){
peer.close();
}
removeVideo(remoteVideo);
}
})
peer.addEventListener('icecandidate', (event) => {
if (event.candidate){
console.log('new ice candidate', JSON.stringify(peer.localDescription))
return;
}
sendSignal('new-offer', {
'sdp': peer.localDescription,
'receiver_channel_name': receiver_channel_name
});
});
peer.createOffer()
.then(o => peer.setLocalDescription(o))
.then(() => {
console.log('Local description set successfully!');
});
}
function createAnswerer(offer, peerUsername, receiver_channel_name) {
let peer = new RTCPeerConnection(iceConfiguration);
console.log('=================================')
console.log(peer)
console.log('=================================')
// let peer = new RTCPeerConnection(null);
addLocalTracks(peer);
let remoteVideo = createVideo(peerUsername);
setOnTrack(peer, remoteVideo);
peer.addEventListener('datachannel', e => {
peer.dc = e.channel;
peer.dc.addEventListener('open', () => {
console.log('connection opened!')
})
peer.dc.addEventListener('message', dcOnMessage);
mapPeers[peerUsername] = [peer, peer.dc];
});
peer.addEventListener('iceconnectionstatechange', () => {
let iceConnectionState = peer.iceConnectionState;
if (iceConnectionState === 'failed' || iceConnectionState === 'disconnected' || iceConnectionState === 'closed'){
delete mapPeers[peerUsername];
if (iceConnectionState !== 'closed'){
peer.close();
}
removeVideo(remoteVideo);
}
})
peer.addEventListener('icecandidate', (event) => {
if (event.candidate){
console.log('new ice candidate', JSON.stringify(peer.localDescription))
return;
}
sendSignal('new-answer', {
'sdp': peer.localDescription,
'receiver_channel_name': receiver_channel_name
});
});
peer.setRemoteDescription(offer)
.then(() => {
console.log('Remote description set successfully for %s.', peerUsername);
return peer.createAnswer();
})
.then(a => {
console.log('Answer created!')
peer.setLocalDescription(a);
})
// peer.createOffer()
// .then(o => peer.setLocalDescription(o))
// .then(() => {
// console.log('Local description set successfully!');
// });
}
function addLocalTracks(peer) {
localStream.getTracks().forEach(track => {
peer.addTrack(track, localStream);
return;
});
}
function dcOnMessage(event) {
let message = event.data;
let li = document.createElement('li')
li.appendChild(document.createTextNode(message));
messageList.appendChild(li);
}
function createVideo(peerUsername) {
let videoContainer = document.querySelector('#video-container');
let remoteVideo = document.createElement('video');
remoteVideo.id = peerUsername + '-video';
remoteVideo.autoplay = true;
remoteVideo.playsInline = true;
let videoWrapper = document.createElement('div');
videoContainer.appendChild(videoWrapper);
videoWrapper.appendChild(remoteVideo);
return remoteVideo;
}
function setOnTrack(peer, remoteVideo) {
let remoteStream = new MediaStream();
remoteVideo.srcObject = remoteStream;
peer.addEventListener('track', async (event) => {
remoteStream.addTrack(event.track, remoteStream);
});
}
function removeVideo(video) {
let videoWrapper = video.parentNode;
videoWrapper.parentNode.removeChild(videoWrapper);
}
function getDataChannels() {
let dataChannels = []
for (peerUsername in mapPeers){
let dataChannel = mapPeers[peerUsername][1];
dataChannels.push(dataChannel);
}
return dataChannels;
}
django channels code in webserver
# Consumer.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
print('connect!!')
self.room_group_name = 'test_room'
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
print(f"room_group_name : {self.room_group_name} and channel_name : {self.channel_name}")
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
print('disconnected!')
async def receive(self, text_data):
receive_dict = json.loads(text_data)
print(f"receive_data : {receive_dict}")
message = receive_dict['message']
action = receive_dict['action']
if (action == 'new-offer') or (action == 'new-answer'):
receiver_channel_name = receive_dict['message']['receiver_channel_name']
receive_dict['message']['receiver_channel_name'] = self.channel_name
await self.channel_layer.send(
receiver_channel_name,
{
'type': 'send.sdp',
'receive_dict': receive_dict
}
)
return
receive_dict['message']['receiver_channel_name'] = self.channel_name
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'send.sdp',
'receive_dict': receive_dict
}
)
async def send_sdp(self, event):
print('send_sdp!!')
receive_dict = event['receive_dict']
await self.send(text_data=json.dumps(receive_dict))
# asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Project.settings')
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
import video_app.routing
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AuthMiddlewareStack(
URLRouter(
video_app.routing.websocket_urlpatterns
)
),
})
#routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r"^ws/$", consumers.ChatConsumer.as_asgi()),
]
# settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("myredis.cache.amazonaws.com", 6379)]
},
},
}
What is my fault?
hello i wonder to upload images in flutter
i try to use http.MultipartRequest
like this
request.fields["name"] = "$RegisterName";
request.fields["description"] = "$RegisterDescription";
request.fields["caution"] = "$RegisterCaution";
request.fields["price"] = "$RegisterPrice";
request.fields["price_prop"] = "$RegisterPriceProp";
request.fields["user.id"] = "1";
request.fields["lend"] = "$RegisterCategory";
request.fields["category"] = "Digital";
request.fields["place_option"] = "true";
var multipartFile = http.MultipartFile.fromBytes(
'file',
(await rootBundle.load('assets/images/main_1.jpg')).buffer.asUint8List(),
filename: 'test01.jpg',
contentType: MediaType('image', 'jpg'),
);
request.files.add(multipartFile);
var response = await request.send();
if (response.statusCode == 200) print('Upload');
}
but this code is not working
if i use this code, upload only another data
upload things
then json type is this
json type image
i want upload images files ...:(
i use this to send picture with formData
var head = Api().bearerHeader; ////just bearerToken
var request = http.MultipartRequest(
'POST',
Uri.parse(
'https://c.....'));
request.files
.add(await http.MultipartFile.fromPath('TITLEOFFORMDATA', imageFile.path));
request.headers.addAll(head);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
String varo = await response.stream.bytesToString();
}
This is how you can send image to your server with MultipartRequest with http package
try {
final uri = Uri.parse(your_url);
final request = http.MultipartRequest('POST', uri);
final multipartFile =
await http.MultipartFile.fromPath('Image', 'your_path_of_image'); // Image is the parameter name
request.files.add(multipartFile);
request.fields['userId_if_required'] = value;
final response = await request.send();
if (response.statusCode == 200) {
print('success');
} else {
print('Something went wrong');
}
} catch (e) {
print('Something went wrong');
}
How to upload your image to a Django rest API server
this will work for sure, let me know if you have any issues.
Please be sure to add the necessary packages to your pubspec.yaml file
image_picker
http
if there is some I missed please ask me or add it and add as a reply
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:io';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
final _picker = ImagePicker();
File? _image;
// use this to send your image
Future<void>uploadImage(filePath) async {
// your token if needed
try{
var headers = {
'Authorization':
'Bearer ' + "token",
};
// your endpoint and request method
var request = http.MultipartRequest(
'POST',
Uri.parse("https://api.imgur.com/3/image"));
request.fields
.addAll({'yourFieldNameKey1': 'yourFieldNameValue1', 'yourFieldNameKey2': 'yourFieldNameValue2'});
request.files.add(await http.MultipartFile.fromPath(
'yourPictureKey', filePath));
request.headers.addAll(headers);
http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
print(await response.stream.bytesToString());
} else {
print(response.reasonPhrase);
}
}catch(e){
print(e);
}
}
// Use this to pick your image
Future<void> _openImagePicker() async {
try {
var pickedImage = await _picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
setState(() {
_image = File(pickedImage.path);
});
uploadImage(pickedImage.path);
}
} catch (e) {
//print(e);
}
}
I am in the process of building a simple chat application and have already managed to make a functioning chat that stores sent messages in a model. However, I am struggling to retrieve previous messages.
consumers.py
def old_messages(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
room = ChatRoom.objects.get(room_name=self.room_name)
messages = ChatMessage.objects.filter(room=room)
content = {
'command': 'old'
}
return content
room.html
chatSocket.onopen = function(e) {
console.log("open",e)
old_messages()
};
function old_messages() {
chatSocket.send(JSON.stringify({
'command': 'old_messages'
}));
};
chatSocket.onmessage = function(e) {
const data = JSON.parse(e.data);
document.querySelector('#chat-log').value += (data.message + '\n');
};
document.querySelector('#chat-message-submit').onclick = function(e) {
const messageInputDom = document.querySelector('#chat-message-input');
const message = messageInputDom.value;
chatSocket.send(JSON.stringify({
'message': message,
'command': 'fetch_messages'
}));
messageInputDom.value = '';
};
Whenever I call old_messages() I get the error message = text_data_json['message']
KeyError: 'message' which refers to this chunk of code
def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
when I use self.send(content) in a loop , all the messages are sent at once , instead of one my one.
the first self.send() in the if condition od connecting is executed perfectly
but all loop self.send() messages are recieved by client at once after a delay of about 60 second. how to make it one at a time?
consumer.py
from channels.generic.websockets import JsonWebsocketConsumer
class MyConsumer(JsonWebsocketConsumer):
# Set to True if you want it, else leave it out
strict_ordering = False
def connect(self, message, **kwargs):
super(MyConsumer,self).connect(message)
pass
def receive(self, content, **kwargs):
if content['status'] == "connecting":
content['status'] = "connected"
self.send(content)
elif content['status'] == "data":
for p in range(5):
content={
'status': 'sending',
'polygon': p
}
self.send(content)
time.sleep(15)
self.close()
def disconnect(self, message, **kwargs):
pass
clientside.js
socket = new WebSocket("ws://" + location.host + "/mahaplans/");
socket.onopen = function () {
var msg = {
status: "connecting"
};
socket.send(JSON.stringify(msg))
}
socket.onmessage = function (e) {
let status=JSON.parse(e.data)
if (status["status"]=="connected") {
var imageData={
#someddata
}
socket.send(JSON.stringify(imageData))
}
if (status["status"]=="sending") {
console.log(status["polygon"])
}
}
socket.onclose = function (event) {
console.log("bye bye")
}
if (socket.readyState == WebSocket.OPEN) socket.onopen();
we are able to make communication from sender to receiver.
but we can only send NUMBERS as a custom message ex: 123,45, etc
when we try to attach any string content eg: hello,hello122 or any character the receiver side does not get any message.
We have executed the following code :
try {
const channel = "urn:x-cast:testChannel";
const iframe = document.getElementById("frame");
const node = document.getElementById("message");
const ctx = cast.framework.CastReceiverContext.getInstance();
node.innerHTML = 'test1';
ctx.addCustomMessageListener(channel, (evt) => {
node.innerHTML = 'test...';
ctx.sendCustomMessage('urn:x-cast:testChannel', evt.senderId, 'Message Invoked v3', (data) => {
document.getElementById("message").innerHTML = 'message sent';
iframe.src = "https://duchy-messages.bradnin.ch/";
node.innerHTML = 'Message sent '
})
node.innerHTML = evt.senderId+ ' test ' +JSON.stringify(evt);
alert('here');
})
ctx.start();
} catch (e) {
document.getElementById("message").innerHTML = JSON.stringify(e);
}