awslabs/amazon-kinesis-video-streams-webrtc-sdk-js

Signaling client error

Co2Link opened this issue · 3 comments

I'm getting error like this as viewer using Chrome on iphone.

[2021-09-27T07:05:15.095Z] [INFO] [VIEWER] Starting viewer connection
[2021-09-27T07:05:15.355Z] [INFO] [VIEWER] Connected to signaling service
[2021-09-27T07:05:15.356Z] [INFO] [VIEWER] Creating SDP offer
[2021-09-27T07:05:15.362Z] [INFO] [VIEWER] Sending SDP offer
[2021-09-27T07:05:15.363Z] [INFO] [VIEWER] Generating ICE candidates
[2021-09-27T07:09:04.113Z] [ERROR] [VIEWER] Signaling client error:  {
  "isTrusted": true
}
[2021-09-27T07:09:04.117Z] [INFO] [VIEWER] Disconnected from signaling channel

As described in #102 , it suggested that it could be a permission issue.

But i can view the stream normally without error at the same page using Chrome or Edge on my Windows PC.

I'm using

"amazon-kinesis-video-streams-webrtc": "^1.0.8",
"aws-sdk": "^2.989.0",

Thank you in advance.

Here is the code for startViewer()
Only receive video stream from master.

var AWS = require("aws-sdk");
let logConfigured = false;
const SignalingClient = require("amazon-kinesis-video-streams-webrtc")
  .SignalingClient;

const region = process.env.VUE_APP_AWS_REGION;
const accessKeyId = process.env.VUE_APP_AWS_ACCESS_KEY;
const secretAccessKey = process.env.VUE_APP_AWS_SECRET_ACCESS_KEY;

function getRandomClientId() {
  return Math.random()
    .toString(36)
    .substring(2)
    .toUpperCase();
}

export const startViewer = async (viewer, channelName, remoteView) => {
  viewer.remoteView = remoteView;

  const kinesisVideoClient = new AWS.KinesisVideo({
    region,
    accessKeyId,
    secretAccessKey,
    correctClockSkew: true,
  });

  const describeSignalingChannelResponse = await kinesisVideoClient
    .describeSignalingChannel({
      ChannelName: channelName, // channel name
    })
    .promise();
  const channelARN = describeSignalingChannelResponse.ChannelInfo.ChannelARN;
  console.log("[VIEWER] Channel ARN: ", channelARN);

  // Get signaling channel endpoints
  const getSignalingChannelEndpointResponse = await kinesisVideoClient
    .getSignalingChannelEndpoint({
      ChannelARN: channelARN,
      SingleMasterChannelEndpointConfiguration: {
        Protocols: ["WSS", "HTTPS"],
        Role: "VIEWER",
      },
    })
    .promise();
  const endpointsByProtocol = getSignalingChannelEndpointResponse.ResourceEndpointList.reduce(
    (endpoints, endpoint) => {
      endpoints[endpoint.Protocol] = endpoint.ResourceEndpoint;
      return endpoints;
    },
    {}
  );
  console.log("[VIEWER] Endpoints: ", endpointsByProtocol);

  const kinesisVideoSignalingChannelsClient = new AWS.KinesisVideoSignalingChannels(
    {
      region: region,
      accessKeyId: accessKeyId,
      secretAccessKey: secretAccessKey,
      endpoint: endpointsByProtocol.HTTPS,
      correctClockSkew: true,
    }
  );

  // Get ICE server configuration
  const getIceServerConfigResponse = await kinesisVideoSignalingChannelsClient
    .getIceServerConfig({
      ChannelARN: channelARN,
    })
    .promise();
  const iceServers = [];
  iceServers.push({
    urls: `stun:stun.kinesisvideo.${region}.amazonaws.com:443`,
  });
  getIceServerConfigResponse.IceServerList.forEach((iceServer) =>
    iceServers.push({
      urls: iceServer.Uris,
      username: iceServer.Username,
      credential: iceServer.Password,
    })
  );
  console.log("[VIEWER] ICE servers: ", iceServers);

  // Create Signaling Client
  viewer.signalingClient = new SignalingClient({
    channelARN,
    channelEndpoint: endpointsByProtocol.WSS,
    clientId: getRandomClientId(),
    role: "VIEWER",
    region: region,
    credentials: {
      // TODO
      //   region: region,
      accessKeyId: accessKeyId,
      secretAccessKey: secretAccessKey,
    },
    systemClockOffset: kinesisVideoClient.config.systemClockOffset,
  });
  // eslint-disable-next-line no-debugger
  //   debugger;

  const configuration = {
    iceServers,
    iceTransportPolicy: "all",
  };
  viewer.peerConnection = new RTCPeerConnection(configuration);

  viewer.signalingClient.on("sdpAnswer", async (answer) => {
    // Add the SDP answer to the peer connection
    console.log("[VIEWER] Received SDP answer");
    await viewer.peerConnection.setRemoteDescription(answer);
    // Indicate if the connection is successful
    viewer.SDP_RECEIVED = true;
  });

  viewer.signalingClient.on("iceCandidate", (candidate) => {
    // Add the ICE candidate received from the MASTER to the peer connection
    console.log("[VIEWER] Received ICE candidate");
    viewer.peerConnection.addIceCandidate(candidate);
  });

  viewer.signalingClient.on("close", () => {
    console.log("[VIEWER] Disconnected from signaling channel");
  });

  viewer.signalingClient.on("error", (error) => {
    console.error("[VIEWER] Signaling client error: ", error);
  });

  viewer.peerConnection.addEventListener("icecandidate", ({ candidate }) => {
    if (candidate) {
      console.log("[VIEWER] Generated ICE candidate");

      // When trickle ICE is enabled, send the ICE candidates as they are generated.
      console.log("[VIEWER] Sending ICE candidate");
      viewer.signalingClient.sendIceCandidate(candidate);
    } else {
      console.log("[VIEWER] All ICE candidates have been generated");

      // When trickle ICE is disabled, send the offer now that all the ICE candidates have ben generated.
      // console.log("[VIEWER] Sending SDP offer");
      // viewer.signalingClient.sendSdpOffer(
      //   viewer.peerConnection.localDescription
      // );
    }
  });
  viewer.peerConnection.addEventListener("track", (event) => {
    console.log("[VIEWER] Received remote track");
    if (remoteView.srcObject) {
      return;
    }
    viewer.remoteStream = event.streams[0];
    remoteView.srcObject = viewer.remoteStream;
  });

  viewer.signalingClient.on("open", async () => {
    console.log("[VIEWER] Connected to signaling service");

    // Create an SDP offer to send to the master
    console.log("[VIEWER] Creating SDP offer");
    const offer = await viewer.peerConnection.createOffer({
      offerToReceiveVideo: true,
      // offerToReceiveAudio: true,
    });

    await viewer.peerConnection.setLocalDescription(offer);
    //eslint-disable-next-line no-debugger
    // debugger;
    console.log("offer:", offer);
    console.log("[VIEWER] Sending SDP offer 4");
    viewer.signalingClient.sendSdpOffer(viewer.peerConnection.localDescription);
    console.log("[VIEWER] Generating ICE candidates");
  });

  console.log("[VIEWER] Starting viewer connection");
  viewer.signalingClient.open();
};

export const stopViewer = (viewer) => {
  console.log("[VIEWER] Stopping viewer connection");
  if (viewer.signalingClient) {
    viewer.signalingClient.close();
    viewer.signalingClient = null;
  }

  if (viewer.peerConnection) {
    viewer.peerConnection.close();
    viewer.peerConnection = null;
  }

  if (viewer.remoteStream) {
    viewer.remoteStream.getTracks().forEach((track) => track.stop());
    viewer.remoteStream = null;
  }

  if (viewer.remoteView) {
    viewer.remoteView.srcObject = null;
  }
};

export const configureLogging = (logs) => {
  function log(level, messages) {
    const text = messages
      .map((message) => {
        if (typeof message === "object") {
          return JSON.stringify(message, null, 2);
        } else {
          return message;
        }
      })
      .join(" ");
    logs.push({
      time: new Date().toISOString(),
      level: level,
      text: text,
    });
  }
  if (logConfigured) {
    console.error = console._error;
    console.warn = console._warn;
    console.log = console._log;
  }
  logConfigured = true;

  console._error = console.error;
  console.error = function(...rest) {
    log("ERROR", Array.prototype.slice.call(rest));
    console._error.apply(this, rest);
  };

  console._warn = console.warn;
  console.warn = function(...rest) {
    log("WARN", Array.prototype.slice.call(rest));
    console._warn.apply(this, rest);
  };

  console._log = console.log;
  console.log = function(...rest) {
    log("INFO", Array.prototype.slice.call(rest));
    console._log.apply(this, rest);
  };
};

Found out that offer do not contain any media if running on mobile chrome

    const offer = await viewer.peerConnection.createOffer({
      offerToReceiveVideo: true,
      // offerToReceiveAudio: true,
    });

    await viewer.peerConnection.setLocalDescription(offer);
    //eslint-disable-next-line no-debugger
    // debugger;
    console.log("offer:", offer);
[2021-10-05T09:09:01.567Z] [INFO] [VIEWER] Starting viewer connection
[2021-10-05T09:09:02.595Z] [INFO] [VIEWER] Connected to signaling service
[2021-10-05T09:09:02.596Z] [INFO] [VIEWER] Creating SDP offer
[2021-10-05T09:09:02.608Z] [INFO] {
  "sdp": "v=0\r\no=- 9007969078791235421 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=extmap-allow-mixed\r\na=msid-semantic: WMS\r\n",
  "type": "offer"
}
[2021-10-05T09:09:02.609Z] [INFO] [VIEWER] Sending SDP offer 4
[2021-10-05T09:09:02.609Z] [INFO] [VIEWER] Generating ICE candidates

Hi, apologies for the delay. Can you try using this sample as a viewer and see if you see any issues? Also, have you referred to the examples in this repository while creating your viewer? It can be found here

Closing due to inactivity, feel free to reopen it if you have any further questions/issues.