Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Failed to set remote offer sdp: Session error code: ERROR_CONTENT

I have a problem connecting peer-to-peer video from native android browser to safari 11 desktop (vice versa), here is the error:

Unhandled Promise Rejection: OperationError (DOM Exception 34): Failed to set remote offer sdp: Session error code: ERROR_CONTENT. Session error description: Failed to set remote video description send parameters..

I'm currently stuck in this issue

and here is my whole client videochat code, thanks.

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: '[email protected]',
    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);
    });
  };
}
like image 926
Marcus Jason Avatar asked May 31 '26 01:05

Marcus Jason


1 Answers

There are two different issues making a WebRTC connection between Chrome on Android and iOS/Safari not working:

1) No H.264 implementation on device

Chrome for Android has only a hardware implementation for H.264 and there is no software implementation. At this moment H.264 works only with devices with a processor of Qualcomm (Kitkat and later) or Samsung Exynos (Lollipop and later). Since Apple only supports H.264 other Android devices can't connect with iOS and Safari.

2) There is bug in Chrome for Android:

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

Because Apple only supports H.264, Android/Chrome can not connect with iOS at this moment.

This problem will be solved in Chrome Android 65 (now Canary). See this for more information.

I see your error message that is exactly this bug so I am pretty sure this is the problem. But in the end it doesn't matter. But you should be aware of both problems.

like image 196
Herman Fransen Avatar answered Jun 02 '26 16:06

Herman Fransen