【问题标题】:WebRTC renegotiate the Peer Connection to switch streamsWebRTC 重新协商对等连接以切换流
【发布时间】:2015-09-18 20:18:39
【问题描述】:

我有这个脚本在哪里。两个用户可以使用 webrtc 聊天。当两个用户进入聊天室时。文字聊天会自动开始。 我想添加一个按钮以允许视频聊天。例如有两个用户。 user_1和user_2进入聊天室后,开始文字聊天,可以互相发送短信,当user_1点击视频图标时,user_2可以看到user_1。当他单击视频图标时,user_2 也是如此。 这是我现在使用的代码。但是这段代码不能正常工作,当我与某人聊天并点击视频图标时,我可以看到我自己,但其他用户看不到我。我没有发布所有代码,因为它超过 300 行,但我认为你不能帮助我修改它以使其工作,并提前感谢大家。

var pc_config = webrtcDetectedBrowser === 'firefox' ?
{'iceServers':[{'url':'stun:23.21.150.121'}]} : // IP address
{'iceServers': [{'url': 'stun:stun.l.google.com:19302'}]};
var pc_constraints = {
  'optional': [
    {'DtlsSrtpKeyAgreement': true},
    {'RtpDataChannels': true}
  ]};
var sdpConstraints = {'mandatory': {
  'OfferToReceiveAudio':true,
  'OfferToReceiveVideo':true }};
var constraints = {video: true, audio: true};
var v_on_off = false;
v_call.on('click', function(){
  if (!v_on_off) {
        navigator.getUserMedia(constraints, handleUserMedia, handleUserMediaError);
        if (isInitiator) {
            maybeStart();
        };
      v_on_off = true;
  } else {
      // stop stream
  }
});
function handleUserMedia(stream) {
        localStream = stream;
        attachMediaStream(localVideo, stream);
        sendMessage('got user media');
}
var socket = io.connect();
if (room !== '') {
  socket.emit('create or join', room);
}
socket.on('created', function (room){
  isInitiator = true;
});
socket.on('join', function (room){
  isChannelReady = true;
});
socket.on('joined', function (room){
  isChannelReady = true;
});
function sendMessage(message){
  socket.emit('message', message);
}
// this will start a text chat between too peers
sendMessage('got user media');
if (isInitiator) {
    maybeStart();
  }
socket.on('message', function (message){
  console.log('Received message:', message);
  if (message === 'got user media') {
    maybeStart();
  } else if (message.type === 'offer') {
    if (!isInitiator && !isStarted) {
      maybeStart();
    }
    pc.setRemoteDescription(new RTCSessionDescription(message));
    doAnswer();
  } else if (message.type === 'answer' && isStarted) {
    pc.setRemoteDescription(new RTCSessionDescription(message));
  } else if (message.type === 'candidate' && isStarted) {
    var candidate = new RTCIceCandidate({sdpMLineIndex:message.label,
      candidate:message.candidate});
    pc.addIceCandidate(candidate);
  } else if (message === 'bye' && isStarted) {
    handleRemoteHangup();
  }
});
function maybeStart() {
  if (!isStarted && isChannelReady) {
    createPeerConnection();
    isStarted = true;
    if (isInitiator) {
      doCall();
    }
  }
}
function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(pc_config, pc_constraints);
    if (typeof localStream != 'undefined') {
        pc.addStream(localStream);
    }
    pc.onicecandidate = handleIceCandidate;
  } catch (e) {
    alert('Cannot create RTCPeerConnection object.');
      return;
  }
  pc.onaddstream = handleRemoteStreamAdded;
  pc.onremovestream = handleRemoteStreamRemoved;
  if (isInitiator) {
    try {
      // Reliable Data Channels not yet supported in Chrome
      sendChannel = pc.createDataChannel("sendDataChannel",
        {reliable: false});
      sendChannel.onmessage = handleMessage;
      trace('Created send data channel');
    } catch (e) {
      alert('Failed to create data channel. ' +
            'You need Chrome M25 or later with RtpDataChannel enabled');
      trace('createDataChannel() failed with exception: ' + e.message);
    }
    sendChannel.onopen = handleSendChannelStateChange;
    sendChannel.onclose = handleSendChannelStateChange;
  } else {
    pc.ondatachannel = gotReceiveChannel;
  }
}
function gotReceiveChannel(event) {
  trace('Receive Channel Callback');
  sendChannel = event.channel;
  sendChannel.onmessage = handleMessage;
  sendChannel.onopen = handleReceiveChannelStateChange;
  sendChannel.onclose = handleReceiveChannelStateChange;
}
function handleSendChannelStateChange() {
  var readyState = sendChannel.readyState;
  trace('Send channel state is: ' + readyState);
  enableMessageInterface(readyState == "open");
}
function handleReceiveChannelStateChange() {
  var readyState = sendChannel.readyState;
  trace('Receive channel state is: ' + readyState);
  enableMessageInterface(readyState == "open");
}
function handleIceCandidate(event) {
  console.log('handleIceCandidate event: ', event);
  if (event.candidate) {
    sendMessage({
      type: 'candidate',
      label: event.candidate.sdpMLineIndex,
      id: event.candidate.sdpMid,
      candidate: event.candidate.candidate});
  } else {
    console.log('End of candidates.');
  }
}
function doCall() {
  var constraints = {'optional': [], 'mandatory': {'MozDontOfferDataChannel': true}};
  // temporary measure to remove Moz* constraints in Chrome
  if (webrtcDetectedBrowser === 'chrome') {
    for (var prop in constraints.mandatory) {
      if (prop.indexOf('Moz') !== -1) {
        delete constraints.mandatory[prop];
      }
     }
   }
  constraints = mergeConstraints(constraints, sdpConstraints);
  console.log('Sending offer to peer, with constraints: \n' +
    '  \'' + JSON.stringify(constraints) + '\'.');
  pc.createOffer(setLocalAndSendMessage, null, constraints);
}
function doAnswer() {
  console.log('Sending answer to peer.');
  pc.createAnswer(setLocalAndSendMessage, null, sdpConstraints);
}
function handleRemoteStreamAdded(event) {
  console.log('Remote stream added.');
  attachMediaStream(remoteVideo, event.stream);
  remoteStream = event.stream;
}  

这是一个服务器端代码:

socket.on('message', function (message) {
    // channel-only broadcast...
    socket.broadcast.to(message.channel).emit('message', message);
});
// Handle 'create or join' messages
socket.on('create or join', function (room) {
    var numClients = io.sockets.clients(room).length;
    // First client joining...
    if (numClients == 0){
        socket.join(room);
        socket.emit('created', room);
    } else if (numClients == 1) {
        io.sockets.in(room).emit('join', room);
        socket.join(room);
        socket.emit('joined', room);
    } else { 
    socket.emit('full', room);
    }
});

【问题讨论】:

    标签: javascript node.js video webrtc


    【解决方案1】:

    重新协商

    简而言之,为了向现有连接添加视频或音频,您需要在每次进行媒体更改时重新协商连接。基本上你注册一个监听器:

    pc.onnegotiationneeded = function() {
      pc.createOffer(setLocalAndSendMessage, null);
    };
    

    这会触发另一个往返的提议/答案交换,就像发起连接的那个一样。

    一旦你有了这个,negotiationneeded 事件将从需要重新协商的操作中触发。例如,“添加视频”按钮:

    AddVideoButton.onclick = function() {
      navigator.getUserMedia(constraints, handleUserMedia, handleUserMediaError);
    };
    

    一旦双方都更新了,你就应该开始营业了。

    完整示例请参见 my answer to a similar question(仅限 Firefox,由于箭头功能,抱歉)。

    规格尼特:

    看起来您正在使用 adapter.js 跨浏览器 webrtc polyfill 来处理浏览器之间的差异,这很棒!但是您示例的其他部分是特定于 Chrome 的或已过时,除非您关注 the standard,否则无法在其他浏览器上运行。您没有将您的问题标记为特定于 Chrome,所以如果您不介意:

    从 Firefox 32(一年多以前)开始,您的 pc_config 中的浏览器检测不再需要。相反,我会使用(注意urls 复数):

    var config = { iceServers: [{urls: 'stun:stun.l.google.com:19302'}] };
    

    pc_constraints(早期的 Chrome)和sdpConstraints 是非标准的。 createOffer 现在采用 RTCOfferOptions,一个简单的字典,而不是约束(还要注意小写的 'o'):

    var options = { offerToReceiveAudio: true, offerToReceiveVideo: true };
    

    如果您使用的是最新版本的 adapter.js,那么这应该可以正常工作。

    最后,RTP 上的数据通道是非标准的(我认为不再需要?)

    【讨论】:

    • 你能告诉我要在我的代码中修改什么吗,我只是一个初学者,我没有编写此代码,我只是从网站上复制它,并对其进行了一些更改。所以我想添加该功能(按钮视频)。你能帮帮我吗
    • 我已经稍微调整了我的答案。希望它可以帮助您朝着正确的方向前进。尝试从首先发起呼叫的一侧添加视频,因为这很可能没有问题。
    • 没用。我确实花了 3 个月的时间试图让它工作我确实阅读了 3 本书关于 webrtc 我学习 nodejs 只是为了制作这个聊天网站。我想是时候放弃了。无论如何感谢您的帮助
    • 您尝试过我链接到的有效 Firefox 示例吗?它可以满足您的要求。您应该能够根据自己的需要进行调整。在 Web 控制台中查找任何错误。
    【解决方案2】:

    只要将 pc_constraint 中的 DtlsSrtpKeyAgreement 设置为 true,就可以正常工作了

     var pc_constraints = {
        optional: [{ 'DtlsSrtpKeyAgreement': true }]
      };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-07
      • 2017-08-25
      • 1970-01-01
      • 2023-03-20
      相关资源
      最近更新 更多