Javascript 远程视频流根本不显示

Javascript 远程视频流根本不显示,javascript,socket.io,webrtc,Javascript,Socket.io,Webrtc,我一直在尝试使用.ontrack来显示远程视频流,它在我的代码中位于对等连接函数下。到目前为止,.ontrack仅在调用方端激发,而在被调用方上,甚至在调用函数时也不会激发 检查.ontrack是否激发的日志将声明“Get Remote Stream”,但仅在调用方上,这可能是这里的问题,但我不确定当另一方没有该语句正在检查的事件.Stream[0]时,为什么不进入保存.ontrack的if语句 我在下面添加了来自调用者和被调用者的控制台日志。图中未显示的是,经过一段时间后,候选用户将显示nul

我一直在尝试使用.ontrack来显示远程视频流,它在我的代码中位于对等连接函数下。到目前为止,.ontrack仅在调用方端激发,而在被调用方上,甚至在调用函数时也不会激发

检查.ontrack是否激发的日志将声明“Get Remote Stream”,但仅在调用方上,这可能是这里的问题,但我不确定当另一方没有该语句正在检查的事件.Stream[0]时,为什么不进入保存.ontrack的if语句

我在下面添加了来自调用者和被调用者的控制台日志。图中未显示的是,经过一段时间后,候选用户将显示null,但两个用户仍保持连接

main.js

'use strict';

var isInitiator;
var configuration = {
  iceServers: [
    {
      urls: 'stun:stun.l.google.com:19302'
    }
  ]
};
var pc = new RTCPeerConnection(configuration);

// Define action buttons.
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');

/////////////////////////////////////////////

window.room = prompt('Enter room name:');

var socket = io.connect();

if (room !== '') {
  console.log('Message from client: Asking to join room ' + room);
  socket.emit('create or join', room);
}

socket.on('created', function(room) {
  console.log('Created room ' + room);
  isInitiator = true;
  startVideo();
});

socket.on('full', function(room) {
  console.log('Message from client: Room ' + room + ' is full :^(');
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  startVideo();
  callButton.disabled = true;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

////////////////////////////////////////////////

async function sendMessage(message) {
  console.log('Client sending message: ', message);
  await socket.emit('message', message);
}

// This client receives a message
socket.on('message', async function(message) {
  try {
    if (message.type === 'offer') {
      await pc.setRemoteDescription(new RTCSessionDescription(message));
      await pc
        .setLocalDescription(await pc.createAnswer())
        .then(function() {
          sendMessage(pc.localDescription);
        })
        .catch(function(err) {
          console.log(err.name + ': ' + err.message);
        });
      createPeerConnection();
    } else if (message.type === 'answer') {
      await pc.setRemoteDescription(new RTCSessionDescription(message));
    } else if (message.type === 'candidate') {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
});

////////////////////////////////////////////////////

const localVideo = document.querySelector('#localVideo');
const remoteVideo = document.querySelector('#remoteVideo');

// Set up initial action buttons status: disable call and hangup.
callButton.disabled = true;
hangupButton.disabled = true;

// Add click event handlers for buttons.
callButton.addEventListener('click', callStart);
hangupButton.addEventListener('click', hangupCall);

function startVideo() {
  navigator.mediaDevices
    .getUserMedia({
      audio: true,
      video: true
    })
    .then(function(stream) {
      localVideo.srcObject = stream;
      stream.getTracks().forEach(track => pc.addTrack(track, stream));
    })
    .catch(function(err) {
      console.log('getUserMedia() error: ' + err.name);
    });
  callButton.disabled = false;
}

async function callStart() {
  createPeerConnection();
  callButton.disabled = true;
  hangupButton.disabled = false;
  if (isInitiator) {
    console.log('Sending offer to peer');
    await pc
      .setLocalDescription(await pc.createOffer())
      .then(function() {
        sendMessage(pc.localDescription);
      })
      .catch(function(err) {
        console.log(err.name + ': ' + err.message);
      });
  }
}

/////////////////////////////////////////////////////////

function createPeerConnection() {
  try {
    pc.ontrack = event => {
      if (remoteVideo.srcObject !== event.streams[0]) {
        remoteVideo.srcObject = event.streams[0];
        console.log('Got remote stream');
      }
    };
    pc.onicecandidate = ({ candidate }) => sendMessage({ candidate });
    console.log('Created RTCPeerConnnection');
  } catch (e) {
    console.log('Failed to create PeerConnection, exception: ' + e.message);
    alert('Cannot create RTCPeerConnection object.');
    return;
  }
}

function hangupCall() {
  pc.close();
  pc = null;
  callButton.disabled = false;
  hangupButton.disabled = true;
  console.log('Call Ended');
}
index.js

'use strict';

var express = require('express');
var app = (module.exports.app = express());
var path = require('path');

var server = require('http').createServer(app);
var io = require('socket.io')(server);
const PORT_NO = process.env.APP_PORT || 3000;
server.listen(PORT_NO);

app.get('/', function(request, response) {
  response.sendFile(path.resolve('./index.html'));
});

app.use(express.static('.'));
io.on('connection', socket => {
  function log() {
    const array = ['Message from server:'];
    for (let i = 0; i < arguments.length; i++) {
      array.push(arguments[i]);
    }
    socket.emit('log', array);
  }

  socket.on('message', message => {
    log('Got message:', message);
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', room => {
    var clientsInRoom = io.sockets.adapter.rooms[room];
    var numClients = clientsInRoom
      ? Object.keys(clientsInRoom.sockets).length
      : 0;

    // max two clients
    if (numClients === 2) {
      socket.emit('full', room);
      return;
    }

    log('Room ' + room + ' now has ' + (numClients + 1) + ' client(s)');

    if (numClients === 0) {
      socket.join(room);
      log('Client ID ' + socket.id + ' created room ' + room);
      socket.emit('created', room, socket.id);
    } else {
      log('Client ID ' + socket.id + ' joined room ' + room);
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room, socket.id);
      io.sockets.in(room).emit('ready');
    }
  });
});
“严格使用”;
var express=需要(“express”);
var app=(module.exports.app=express());
var path=require('path');
var server=require('http')。createServer(应用程序);
var io=require('socket.io')(服务器);
const PORT|u NO=process.env.APP|u PORT | 3000;
监听(端口号);
app.get('/',函数(请求,响应){
sendFile(path.resolve('./index.html');
});
应用程序使用(快速静态('.');
io.on('连接',套接字=>{
函数日志(){
常量数组=['来自服务器的消息:'];
for(设i=0;i{
日志('Got message:',message);
socket.broadcast.emit('message',message);
});
socket.on('create or join',room=>{
var clientsInRoom=io.sockets.adapter.rooms[room];
var numClients=clientsInRoom
?对象。钥匙(客户房间。插座)。长度
: 0;
//最多两个客户
if(numClients==2){
插座发出(“满”,房间);
返回;
}
日志('Room'+Room+'现在有'+(numClients+1)+'客户端');
if(numClients===0){
插座连接(房间);
日志('Client ID'+socket.ID+'created room'+room);
socket.emit('created',room,socket.id);
}否则{
日志('Client ID'+socket.ID+'joined room'+room);
io.sockets.in(房间).emit('join',房间);
插座连接(房间);
插座.emit('joined',房间,插座.id);
io.sockets.in(房间).emit('ready');
}
});
});


让加入者成为发起人

我猜
'created'
发生在
'joined'
之前吧?即,一方在另一方加入之前创建房间

因为你的
startVideo()
不仅仅是启动本地视频,它实际上开始了连接协商——我怀疑你在第二方准备好之前就开始了协商,这是一场比赛。相反,请尝试:

socket.on('created', function(room) {
  console.log('Created room ' + room);
  startVideo();
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  isInitiator = true; // <-- begin negotiating once 2nd party arrives.
  startVideo();
});
socket.on('created',函数(房间){
console.log('创建的房间'+房间);
startVideo();
});
插座上的('连接',功能(房间){
控制台日志('已加入:'+房间);

isInitiator=true;//您错过了应答器端对
createPeerConnection()
的调用,这意味着应答器未正确设置为向ICE候选者发送信号或启动径赛

您只能从
startCall()
调用它,因此只有在几乎完全相同的时间点击两端的调用按钮时,这才有效

createPeerConnection()
用词不当。相反,只需在页面加载时使用其
ontrack
onicecandidate
回调来初始化
pc

还是不工作? 您向我们展示的其他与WebRTC相关的代码看起来不错,只是您在应答器端调用了两次
getUserMedia
,这是多余的,但应该不会有问题

我怀疑您的服务器逻辑存在错误。例如,您没有向我们展示发出的
'create或join'
如何变成
'created'
'joined'
套接字消息。您还试图预先确定在提供/应答交换中哪一方是哪一方,这很好,但这意味着您有一个非工作的
呼叫ode>回答者侧的按钮。大多数演示只允许先按下按钮的人成为报价人,尽管这可能会引起眩光。仅供参考

这是双向呼叫。remoteVideo在哪个方向不工作? 另外,您在这里有一个双向呼叫,向两个方向发送视频,但您没有提到您没有看到哪个remoteVideo


例如,请查看my。在同一浏览器中的两个相邻窗口中打开它,然后单击其中一个窗口中的
Call
按钮进行连接。您应该可以看到(相同的)视频是双向发送的。它依赖于使用本地存储的
localSocket
hack。

startVideo()不做谈判,不是吗?我有启动电话()函数,用于向对等方创建要约。您认为在加入之前创建要约是正确的,但通过将isInitiator更改为加入的对等方,创建者现在无法按下调用按钮,因为它不会触发createOffer。我附上一个图像,供您查看createOffer的结果。@RjTng Ah是的。我已经习惯了人们使用
onnegotiationneeded
。很抱歉,我不太明白你在说什么。pc一开始就已经用
var pc=new-rtpeerconnection(configuration);
配置好了;
但我确实尝试添加了另一个
createPeerConnection()
接收到优惠信息但未创建远程视频流的地方。@RjTng这就是我的意思。远程视频的两侧都不工作。我试图用电话启动房间,本地视频可以显示,但按下呼叫按钮后,它会立即关闭。这是在添加PeerConn之后我也在本地主机上尝试过这样做,并将我的代码与您的代码进行了比较,结果似乎是正确的。对于socket.io信令,我