Vidéostream à distance ne fonctionne pas avec WebRTC
EDIT: j'ai écrit un tutoriel détaillé expliquant comment construire une application Videochat simple comprenant un serveur de signalisation:
Tutorial: créez votre propre application Videochat avec HTML et JavaScript
s'il vous Plaît dites-moi si vous le trouvez utile et compréhensible. Merci!
j'essaie de faire fonctionner les flux via WebRTC et Websocket (nodejs-server). Aussi loin que je vous pouvez voir la poignée de main via les travaux SDP et la connexion est établie. Le problème est que la vidéo à distance ne fonctionne pas. L'attribut src obtient le Blob et autoplay est défini, mais il ne joue pas. Peut-être que je fais quelque chose de mal avec les ICE-candidats (ils sont utilisés pour les médias-streaming, Non?). Y a-t-il un moyen de vérifier si la connexion est configurée correctement?
EDIT: peut-être que je devrais expliquer comment le code fonctionne
-
à la charge du site Web une connexion au serveur web est établie, une connexion Internet utilisant googles STUN-server est créée et des flux vidéo et Audio sont collectés et ajoutés à la connexion Internet
-
Lorsqu'un utilisateur clique sur "create offer" - bouton un message contenant sa Session-Description (SDP) est envoyé au serveur (client func sendOffer ()), qui le diffuse à l'autre utilisateur
-
l'autre utilisateur reçoit le message et enregistre le SDP qu'il a reçu
-
si l'utilisateur clique sur "Accepter l'offre", le SDP est ajouté à la description distante (func createAnswer()) qui envoie alors un message-réponse (contenant le SDP de l'utilisateur répondant) à l'utilisateur offrant
-
du côté de l'utilisateur offrant le func offerAccepted () est exécuté, ce qui ajoute le SDP de l'autre utilisateur à son RemoteDesription.
Je ne suis pas sûr à quel moment exactement les manipulateurs icecandidate sont appelés, mais je pense qu'ils devraient fonctionner parce que je reçois les deux logs des deux côtés.
Voici mon Code (Ceci est juste pour tester, donc même s'il y a une fonction appelée broadcast cela signifie que seulement 2 utilisateurs peuvent être sur le même site à la fois):
Majoration de l'indice.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
#acceptOffer {
display: none;
}
</style>
</head>
<body>
<h2>Chat</h2>
<div>
<textarea class="output" name="" id="" cols="30" rows="10"></textarea>
</div>
<button id="createOffer">create Offer</button>
<button id="acceptOffer">accept Offer</button>
<h2>My Stream</h2>
<video id="myStream" autoplay src=""></video>
<h2>Remote Stream</h2>
<video id="remoteStream" autoplay src=""></video>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="websocketClient.js"></script>
</body>
</html>
voici le code du serveur:
"use strict";
var webSocketsServerPort = 61122;
var webSocketServer = require('websocket').server,
http = require('http'),
clients = [];
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
});
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
});
var wsServer = new webSocketServer({
httpServer: server
});
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
var connection = request.accept(null, request.origin),
index = clients.push(connection) - 1,
userName=false;
console.log((new Date()) + ' Connection accepted from '+connection.remoteAddress);
// user sent some message
connection.on('message', function(message) {
var json = JSON.parse(message.utf8Data);
console.log(json.type);
switch (json.type) {
case 'broadcast':
broadcast(json);
break;
case 'emit':
emit({type:'offer', data:json.data.data});
break;
case 'client':
respondToClient(json, clients[index]);
break;
default:
respondToClient({type:'error', data:'Sorry, i dont understand that.'}, clients[index]);
break;
}
});
connection.on('close', function(connection) {
clients.splice(index,1);
console.log((new Date()) + " Peer " + connection.remoteAddress + " disconnected.");
broadcast({type:'text', data: userName+' has left the channel.'});
});
var respondToClient = function(data, client){
client.sendUTF(JSON.stringify( data ));
};
var broadcast = function(data){
for(var i = 0; i < clients.length; i++ ) {
if(i != index ) {
clients[i].sendUTF(JSON.stringify( data ));
}
}
};
var emit = function(){
// TBD
};
});
et ici le code Client:
$(function () {
"use strict";
/**
* Websocket Stuff
**/
window.WebSocket = window.WebSocket || window.MozWebSocket;
// open connection
var connection = new WebSocket('ws://url-to-node-server:61122'),
myName = false,
mySDP = false,
otherSDP = false;
connection.onopen = function () {
console.log("connection to WebSocketServer successfull");
};
connection.onerror = function (error) {
console.log("WebSocket connection error");
};
connection.onmessage = function (message) {
try {
var json = JSON.parse(message.data),
output = document.getElementsByClassName('output')[0];
switch(json.callback) {
case 'offer':
otherSDP = json.data;
document.getElementById('acceptOffer').style.display = 'block';
break;
case 'setIceCandidate':
console.log('ICE CANDITATE ADDED');
peerConnection.addIceCandidate(json.data);
break;
case 'text':
var text = output.value;
output.value = json.data+'n'+output.value;
break;
case 'answer':
otherSDP = json.data;
offerAccepted();
break;
}
} catch (e) {
console.log('This doesn't look like a valid JSON or something else went wrong.');
return;
}
};
/**
* P2P Stuff
**/
navigator.getMedia = ( navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
// create Connection
var peerConnection = new webkitRTCPeerConnection(
{ "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] }
);
var remoteVideo = document.getElementById('remoteStream'),
myVideo = document.getElementById('myStream'),
// get local video-Stream and add to Peerconnection
stream = navigator.webkitGetUserMedia({ audio: false, video: true }, function (stream) {
myVideo.src = webkitURL.createObjectURL(stream);
console.log(stream);
peerConnection.addStream(stream);
});
// executes if other side adds stream
peerConnection.onaddstream = function(e){
console.log("stream added");
if (!e)
{
return;
}
remoteVideo.setAttribute("src",URL.createObjectURL(e.stream));
console.log(e.stream);
};
// executes if my icecandidate is received, then send it to other side
peerConnection.onicecandidate = function(candidate){
console.log('ICE CANDITATE RECEIVED');
var json = JSON.stringify( { type: 'broadcast', callback:'setIceCandidate', data:candidate});
connection.send(json);
};
// send offer via Websocket
var sendOffer = function(){
peerConnection.createOffer(function (sessionDescription) {
peerConnection.setLocalDescription(sessionDescription);
// POST-Offer-SDP-For-Other-Peer(sessionDescription.sdp, sessionDescription.type);
var json = JSON.stringify( { type: 'broadcast', callback:'offer',data:{sdp:sessionDescription.sdp,type:'offer'}});
connection.send(json);
}, null, { 'mandatory': { 'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true } });
};
// executes if offer is received and has been accepted
var createAnswer = function(){
peerConnection.setRemoteDescription(new RTCSessionDescription(otherSDP));
peerConnection.createAnswer(function (sessionDescription) {
peerConnection.setLocalDescription(sessionDescription);
// POST-answer-SDP-back-to-Offerer(sessionDescription.sdp, sessionDescription.type);
var json = JSON.stringify( { type: 'broadcast', callback:'answer',data:{sdp:sessionDescription.sdp,type:'answer'}});
connection.send(json);
}, null, { 'mandatory': { 'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true } });
};
// executes if other side accepted my offer
var offerAccepted = function(){
peerConnection.setRemoteDescription(new RTCSessionDescription(otherSDP));
console.log('it should work now');
};
$('#acceptOffer').on('click',function(){
createAnswer();
});
$('#createOffer').on('click',function(){
sendOffer();
});
});
j'ai aussi lu que le flux local-média doit être collecté avant toute offre est envoyé. Cela signifie-t-il que je dois l'ajouter lorsque la connexion est créée? I. e. quelque chose comme ceci:
// create Connection
var peerConnection = new webkitRTCPeerConnection(
{
"iceServers": [{ "url": "stun:stun.l.google.com:19302" }],
"mediaStream": stream // attach media stream here?
}
);
merci à l'avance, j'apprécie toute aide!
EDIT2: je suis un peu plus loin, maintenant. il semble que l'ajout des ICE-candidats à distance (switch-case setIceCandidate dans le code client) ne fonctionne pas à cause de "une chaîne invalide ou illégale a été spécifiée. ". le json.données.candidat-objet ressemble à ceci:
candidate: "a=candidate:1663431597 2 udp 1845501695 141.84.69.86 57538 typ srflx raddr 10.150.16.92 rport 57538 generation 0
↵"
sdpMLineIndex: 1
sdpMid: "video"
j'ai essayé de créer un nouveau candidat comme celui-ci
var remoteCandidate = new RTCIceCandidate(json.data.candidate);
peerConnection.addIceCandidate(remoteCandidate);
mais j'ai quand même une erreur de syntaxe
1 réponses
j'avais des problèmes avec essentiellement la même chose récemment, et le meilleur conseil que j'ai reçu de quelqu'un d'autre ici était de créer une version de mon programme dans lequel j'ai copié et collé manuellement les informations SDP et ICE d'un" pair " (c.-à-d., onglet navigateur) à un autre et vice versa.
En faisant cela, j'ai réalisé plusieurs choses:
-
vous devez appeler la méthode addStream de l'objet peer connection avant vous tentez de créer des offres/réponses.
-
en appelant la méthode createOffer ou createAnswer, les candidats ICE pour ce client sont instantanément générés. Cependant, une fois que vous avez envoyé les informations de glace à l'autre Pair, vous ne pouvez pas réellement définir les informations de glace jusqu'à ce qu'une description à distance est définie (en utilisant l'offre/réponse reçue).
-
assurez-vous que vous encodent correctement toutes les informations sur le point d'être envoyées sur le fil. Dans JS, cela signifie que vous devez utiliser la fonction encodeURIComponent sur toutes les données sur le point d'être envoyées sur le fil. J'ai eu un problème dans lequel SDP et ICE info seraient parfois réglés correctement et parfois pas. Ça a à voir avec le fait que je n'encodais pas les données, ce qui a conduit à des signes plus dans les données qui ont été transformées en espaces, ce qui a tout fichu en l'air.
en tout cas, comme je l'ai dit, Je recommande la création d'une version de votre programme dans lequel vous avez un tas de zones de texte pour cracher toutes les données à l'écran, et puis avoir d'autres zones de texte que vous pouvez coller des données copiées pour les définir pour l'Autre pair.
Faire cela a vraiment clarifié L'ensemble du processus WebRTC, qui honnêtement, n'est pas bien expliqué dans tous les documents/tutoriels que j'ai vu encore.
bonne chance, et dites-moi si je peux encore vous aider.