【问题标题】:Failed to set remote offer sdp: Session error code: ERROR_CONTENT无法设置远程报价 sdp:会话错误代码:ERROR_CONTENT
【发布时间】:2018-03-09 16:11:12
【问题描述】:

我在将点对点视频从原生 android 浏览器连接到 safari 11 桌面时遇到问题(反之亦然),这是错误:

未处理的承诺拒绝:OperationError(DOM 异常 34):无法设置远程报价 sdp:会话错误代码:ERROR_CONTENT。会话错误描述:设置远程视频描述发送参数失败..

我目前陷入这个问题

这是我的整个客户端视频聊天代码,谢谢。

import app from '../../../config';

const videoChatService = app.service('participants/video-chat');

let localVid;
let remoteVid;
let localstream;
let rtcPeerConn;
let conversationId;
let userId;
let isSdpSent = false;
let hasAddTrack;

const configuration = {
  iceServers: [{
    urls: 'stun:stun.l.google.com:19302',
  },
  {
    urls: 'stun:stun.services.mozilla.com',
    username: 'louis@mozilla.com',
    credential: 'webrtcdemo',
  },
  // {
  //   urls: 'turn:mdn-samples.mozilla.org',
  //   username: 'webrtc',
  //   credential: 'turnserver' }
  ] };

function closeVideoCall() {
  if (rtcPeerConn) {
    rtcPeerConn.onaddstream = null;
    rtcPeerConn.ontrack = null;
    rtcPeerConn.onremovestream = null;
    rtcPeerConn.onicecandidate = null;

    if (remoteVid.srcObject) {
      remoteVid.srcObject.getTracks().forEach(track => track.stop());
      remoteVid.srcObject = null;
    }

    if (localVid.srcObject) {
      localVid.srcObject.getTracks().forEach(track => track.stop());
      localVid.srcObject = null;
    }

    rtcPeerConn.close();
    rtcPeerConn = null;
  }
}

// set local sdp
function setLocalSDP(desc) {
  console.log('>>> setLocalSDP', rtcPeerConn);
  return rtcPeerConn.setLocalDescription(desc);
}

function logError(error) {
  console.log(`>>>>logError, ${error.name}: ${error.message}`);
}

function handleNegotiatedNeededEvent() {
  console.log('>>>>> on negotiation called');
  console.log('query >>>', conversationId, userId);
  if (!isSdpSent) {
    rtcPeerConn.createOffer()
      .then(setLocalSDP)
      .then(() => {
        isSdpSent = true;
        videoChatService.patch(null, {
          data: {
            type: 'video-offer',
            message: JSON.stringify(rtcPeerConn.localDescription),
          },
        }, {
          query: {
            conversation_id: conversationId,
            user_id: userId,
          },
        }).then().catch(e => { console.log('patch error', e); });
      })
      .catch(logError);
  }
}

function handleRemoveStreamEvent() {
  closeVideoCall();
}

function handleTrackEvent (evt) {
  console.log('>>>>> going to add their stream...', evt);
  remoteVid = document.getElementById('remoteStream');
  if (!remoteVid.srcObject) {
    remoteVid.srcObject = evt.streams[0];
  }
}

function handleAddStreamEvent(evt) {
  console.log('>>>>> stream added');
  remoteVid = document.getElementById('remoteStream');
  remoteVid.srcObject = event.stream;
}

function handleICECandidateEvent(evt) {
  console.log('>>>> onicecandidate', evt);
  console.log('query >>>', conversationId, userId);
  if (evt.candidate) {
    videoChatService.patch(null, {
      data: {
        type: 'new-ice-candidate',
        message: JSON.stringify(evt.candidate),
      },
    }, {
      query: {
        conversation_id: conversationId,
        user_id: userId,
      },
    });
  }
}

function handleICEConnectionStateChangeEvent() {
  console.log(`>>>>> ICE connection state changed to ${rtcPeerConn.iceConnectionState}`);

  switch (rtcPeerConn.iceConnectionState) {
    case 'closed':
    case 'failed':
    case 'disconnected':
      console.log('>>>> disconnected');
      closeVideoCall();
      break;
  }
}

function handleSignalingStateChangeEvent() {
  console.log(`>>>>> WebRTC signaling state changed to: ${rtcPeerConn.signalingState}`);
  switch (rtcPeerConn.signalingState) {
    case 'closed':
    console.log('>>>> closed');
      closeVideoCall();
      break;
  }
}

function createPeerConnection() {
  rtcPeerConn = new RTCPeerConnection(configuration);
  console.log('>>>>> create peer connection', rtcPeerConn);

  hasAddTrack = (rtcPeerConn.addTrack !== undefined);

  rtcPeerConn.onicecandidate = handleICECandidateEvent;
  rtcPeerConn.onnegotiationneeded = handleNegotiatedNeededEvent;
  rtcPeerConn.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
  rtcPeerConn.onsignalingstatechange = handleSignalingStateChangeEvent;
  rtcPeerConn.onremovestream = handleRemoveStreamEvent;

  if (hasAddTrack) {
    rtcPeerConn.ontrack = handleTrackEvent;
  } else {
    rtcPeerConn.onaddstream = handleAddStreamEvent;
  }
}

function handleGetUSerMediaError(e) {
  switch (e.name) {
    case 'NotFoundError':
      alert('Unable to open your call because no camera and/or microphone were found.');
      break;
    case 'SecurityError':
    case 'PermissionDeniedError':
      // Do nothing; this is the same as the user canceling the call.
      break;
    default:
      alert(`Error opening your camera and/or microphone: ${e.message}`);
      break;
  }
}

// add video to local and add to track
function gotStream(stream) {
  console.log('>>>> gotStream', stream);
  localVid.srcObject = stream;
  localstream = stream;

  if (hasAddTrack) {
    stream.getTracks().forEach(track => rtcPeerConn.addTrack(track, localstream));
  } else {
    rtcPeerConn.addStream(localstream);
  }
}

// start signaling
export function startSignaling(conversation_id, user_id) {
  localVid = document.getElementById('localStream');
  remoteVid = document.getElementById('remoteStream');
  console.log('>>>>> startSignaling');

  conversationId = conversation_id;
  userId = user_id;

  return () => {
    if (!rtcPeerConn) {
      createPeerConnection();
      navigator.mediaDevices.getUserMedia({
        audio: true,
        video: {
          facingMode: 'user',
        },
      })
        .then(gotStream)
        .catch(handleGetUSerMediaError);
    }
  };
}

export function handleVideoOfferMsg(conversation_id, user_id, message) {
  console.log('>>>>> handleVideoOfferMsg');
  localstream = null;
  conversationId = conversation_id;
  userId = user_id;
  localVid = document.getElementById('localStream');
  remoteVid = document.getElementById('remoteStream');
  return () => {
    createPeerConnection();
    console.log('query >>>', conversationId, userId);
    const sdp = new RTCSessionDescription(message);
    // sdp.sdp = sdp.replace('a=setup:active', 'a=setup:passive');
    rtcPeerConn.setRemoteDescription(sdp)
      .then(() => (
        navigator.mediaDevices.getUserMedia({
          audio: true,
          video: {
            facingMode: 'user',
          },
        })
      ))
      .then(gotStream)
      .then(() => (
        rtcPeerConn.createAnswer()
      ))
      .then(setLocalSDP)
      .then(() => {
        videoChatService.patch(null, {
          data: {
            type: 'video-answer',
            message: JSON.stringify(rtcPeerConn.localDescription),
          },
        }, {
          query: {
            conversation_id: conversationId,
            user_id: userId,
          },
        }).then().catch(e => { console.log('patch error', e); });
      });
  };
}

export function handleVideoAnswerMsg(message) {
  console.log('>>>>> handle video answer message', message);
  return () => {
    const sdp = new RTCSessionDescription(message);
    rtcPeerConn.setRemoteDescription(sdp)
      .catch(logError);
  };
}

// Adding ice candidate
export function addIceCandidate(message) {
  console.log('>>>> addIceCandidate', message);
  return () => {
    const candidate = new RTCIceCandidate(message);
    rtcPeerConn.addIceCandidate(candidate)
    .then(() => {
      console.log('>>> candidate added ');
    })
    .catch(e => {
      console.log('Error candidate', e);
    });
  };
}

【问题讨论】:

    标签: node.js reactjs websocket safari webrtc


    【解决方案1】:

    有两个不同的问题导致 Android 上的 Chrome 和 iOS/Safari 之间的 WebRTC 连接无法正常工作:

    1) 设备上没有 H.264 实现

    Chrome for Android 只有 H.264 的硬件实现,没有软件实现。目前,H.264 仅适用于配备 Qualcomm(Kitkat 及更高版本)或三星 Exynos(Lollipop 及更高版本)处理器的设备。由于 Apple 仅支持 H.264,其他 Android 设备无法连接 iOS 和 Safari。

    2) Android 版 Chrome 存在错误:

    Chrome Android does not offer/answer H.264 Constrained Baseline Profile

    由于Apple只支持H.264,Android/Chrome暂时无法连接iOS。

    这个问题将在 Chrome Android 65(现在的 Canary)中得到解决。请参阅this 了解更多信息。

    我看到您的错误消息正是这个错误,所以我很确定这是问题所在。但最后也没关系。但是你应该意识到这两个问题。

    【讨论】:

      【解决方案2】:

      这可能与this issue有关。

      Android 上的 Chrome 并不总是支持 H264,Safari 只支持 H264。

      【讨论】:

      • 不是android上的chrome,而是android的原生浏览器。
      • 哪个,取决于 Android 版本,基于 Chrome。
      【解决方案3】:

      要验证它是其他答案提到的视频编解码器问题,您可以调整流约束:

      navigator.mediaDevices.getUserMedia({
        video: false,
        audio: true
      })
      

      如果在此更改后 ICE 处理成功,则可能 SDP 包含另一端不支持的编解码器。

      那么您应该将您的代码扩展为仅在您的 GUI 中显示错误消息后回退到音频。

      【讨论】:

        猜你喜欢
        • 2020-09-19
        • 1970-01-01
        • 2016-10-13
        • 2020-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-09
        • 1970-01-01
        相关资源
        最近更新 更多