SimpleWebRTC, videoAdded event isn't being emitted

39 views Asked by At

Currently building an video calling app with simpleWebRTC, however, I am not getting any videoAdded event emitted from SimpleWebRTC whenever a peer (user) tries to join a session... I have got no clues why this is happening and most importantly how come this is happening.

this is my joining function (with the handlers for the videoAdded and videoRemoved, the functions that should be called whenever the events are emitted and caught):

const WebRTCService = new SimpleWebRTCService('simpleWebRTC');
const remoteStreamsContainerId = 'remoteStreamsContainer';

const getMedia = async (constraints) => {
  let mediaStream = null;
  try {
    mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
    /* use the stream */
    return mediaStream;
  } catch (err) {
    /* handle the error */
    console.error("Failed to get media stream:", err);
    return null;
  }
};

// Ping P2P session
const pingP2PSession = async (joinSessionId) => {
  const type_ping = actionTypes_P2P.PING_P2P;
  const typePingWithParameters1 = type_ping.replace('{mediaControllerId}','p2p');
  const typePingWithParameters2 = typePingWithParameters1.replace('{conversationId}',joinSessionId);

  connectionInstance.emit(
    typePingWithParameters2,
    null,
    (response) => {
      console.log('Response:', response);
    }
  );
};

export const joinStreamingSession = (joinSessionId) => async (dispatch, getState) => {

  //Remember to call the actual webRTCFunction
  WebRTCService.connect(connectionInstance, {
    remoteVideoEl: remoteStreamsContainerId,
  })
  const isConnected = selectIsConnected(getState());

  if (!isConnected) {
    dispatch(actionCreators_SessionMgr.NotifyStreamingSessionJoiningFailed());
    return;
  }

  WebRTCService.serviceMessageHandlers = {
    videoAdded: videoAdded(dispatch),
    videoRemoved: videoRemoved(dispatch),
  }


  try {
    await WebRTCService.openEventChannel();
    console.log('Event channel opened!');

  } catch (error) {
    console.error('Error in openEventChannelAndListenForMessages:', error);
    // Dispatch any necessary error actions here
  }

  try{
    await WebRTCService.listenForMessagesFromServiceEventChannel(backendMessageHandlers);

  } catch(error){
    console.error('Error in listenForMessagesFromServiceEventChannel:', error);
  }


  try {
    const getVideo = true;
    const getAudio = true;
    const mediaStream = await getMedia({ video: getVideo, audio: getAudio });

    const videoTracks = mediaStream.getVideoTracks();
    console.log("Number of video tracks:", videoTracks.length);
    if (videoTracks.length > 0) {
      console.log("Video track details:", videoTracks[0]);
    }

    // Check if the MediaStream has audio tracks
    const audioTracks = mediaStream.getAudioTracks();
    console.log("Number of audio tracks:", audioTracks.length);
    if (audioTracks.length > 0) {
      console.log("Audio track details:", audioTracks[0]);
    }

    // Add event listeners for 'addtrack' and 'removetrack' events
    mediaStream.addEventListener('addtrack', event => {
      console.log('Track added:', event.track);
    });

    mediaStream.addEventListener('removetrack', event => {
      console.log('Track removed:', event.track);
    });
    
    WebRTCService.setStream(mediaStream);
  } catch (err) {
    console.error(err);
    dispatch(actionCreators_SessionMgr.NotifyStreamingSessionJoiningFailed());
    return false;
  }

  // try {
    const joinRoomResult =  await WebRTCService.joinRoom(joinSessionId);
    console.log(joinRoomResult);
    if (!joinRoomResult || typeof joinRoomResult === 'string') {
      throw new Error(JSON.stringify(joinRoomResult));
    }
    // const { clients } = joinRoomResult;
    // const seats = seatList(getState());
    // console.log('MY SEAT IS: ', seats);
    // console.log('CLIENTS ARE: ', clientId);
    // for (clientId of Object.keys(clients)) {
    //   const seat = seats.find((seat) => seat.userId === clientId);
    //   if (!seat) throw new Error(`No seat for client ${clientId}`);
    //   dispatch(actionCreators_SessionMgr.NotifyPeerJoined(clientId, seat.seatId, null));
    // }
  // } catch (err) {
  //   dispatch(actionCreators_P2P.NotifyJoinSignallingServerRoomError(err));
  //   dispatch(actionCreators_SessionMgr.NotifyStreamingSessionJoiningFailed());
  // }

  // Set streaming options
  const type1 = actionTypes_P2P.SET_P2P_STREAMING_OPTIONS;
  const type2 = type1.replace('{mediaControllerId}','p2p');
  const type3 = type2.replace('{conversationId}',joinSessionId);


  const response = await new Promise((resolve) => {
    connectionInstance.emit(
      type3,
      {
        shareAudio: true ,
        shareVideo: true,
        shareScreen: false,
      },
       resolve
    );
  });

  if (response !== null){
    return false;
  }

  console.log('Streaming options set!');
  dispatch(actionCreators_SessionMgr.NotifyStreamingSessionJoined());

  console.log('whats the return outcome?')
  return true;

  // // Start pinging the P2P session
  // while (selectIsConnected(getState())) {
  //   await pingP2PSession(sessionId);
  // }
};


export const videoAdded = (action) => (dispatch) => {
  console.log('videoAdded action:', action)
  const { payload: [mediaElmt, webRtcPeer] } = action
  const userId = webRtcPeer.id
  const hasVideo = !!webRtcPeer.stream.getVideoTracks().length
  const hasAudio = !!webRtcPeer.stream.getAudioTracks().length
  dispatch(PeerRenderer.actionCreators.RenderPeer(userId, {
    videoElementId: hasVideo ? mediaElmt.id : null, // null to reset, undefined to not touch current settings.
    audioElementId: hasAudio ? mediaElmt.id : null, // null to reset, undefined to not touch current settings.
  }))
}


export const videoRemoved = (action) => (dispatch) => {
  const { payload: [videoElmt, webRtcPeer] } = action
  const userId = webRtcPeer.id
  dispatch(PeerRenderer.actionCreators.RenderPeer(userId, {
    videoElementId: '',
    audioElementId: '',
  }))
}

and this is my SimpleWebRTC.js file:

/* eslint-disable */
import SimpleWebRTC from "simplewebrtc";
import { EventEmitter } from 'events';

const SIMPLEWEBRTC_TOPIC_MAP = (sessionId, mediaControllerId = "p2p") => ({
  message: `mediacontroller.${mediaControllerId}.${sessionId}.message`,
});
const MAPPED_TOPICS = Object.keys(SIMPLEWEBRTC_TOPIC_MAP("DUMMY"));

export default class SimpleWebRTCService {
  constructor(id, options) {
    this.id = id;
    this.options = options;
    this._messageCallbacks = {};
    this._roomId = null;
    this._eventEmitter = new EventEmitter();
    this._eventChannel = undefined;
    this.serviceMessageHandlers = {};
    this.serviceMessageToActionFilter = (action) => false;
    this.actionCreators = {
      NotifyDisconnected: () => ({ type: '', serviceId: '' }),
      NotifyConnectionError: (error) => ({ type: '', serviceId: '', error }),
      NotifyConnecting: () => ({ type: '', serviceId: '' }),
      NotifyConnected: () => ({ type: '', serviceId: '' }),
    };
  }

  connect(socketIOConnection, connectionOptions = {}) {
    const { media, ...restSimpleWebRTCOptions } = connectionOptions;
    return new Promise((resolve, reject) => {
      if (!socketIOConnection) {
        throw new Error("Missing socketio connection instance");
      }
      // Apply SimpleWebRTC patches to socketio
      socketIOConnection.getSessionid = function () {
        return this.id;
      };
      // Monkey-patch SimpleWebRTCs emit to reformat its own messages
      const origEmit = socketIOConnection.emit;
      socketIOConnection.emit = function emit(topic, data, ack) {
        if (!data) {
          origEmit.bind(this)(topic, data, ack);
          return;
        }

        const { type, to, sid, roomType, prefix, payload } = data;
        const msg = !!sid
          ? {
              type: topic,
              payload: {
                type,
                to,
                sid,
                roomType,
                prefix,
                payload,
              },
            }
          : data;
        origEmit.bind(this)(topic, msg, ack);
      };

      // Monkey-patch the .on method so we can pass in P2P messages from React back into the SimpleWebRTC instance
      const oldOn = socketIOConnection.on;
      const callbackCollection = this._messageCallbacks;
      socketIOConnection.on = function on(topic, cb) {
        console.log("Binding SWRTC Socket.io callback for", topic);
        if (callbackCollection[topic])
          console.log("Warning: overwriting callback for", topic);
        callbackCollection[topic] = cb;
        oldOn.bind(this)(topic, cb);
      };

      this._SimpleWebRTCConnectionInstance = new SimpleWebRTC({
        url: "N/A: proxied",
        media: {
          // This is only used internally by SimpleWebRTC.
          audio: true,
          video: true,
          ...media,
        },
        adjustPeerVolume: false,
        autoRequestMedia: false,
        enableDataChannels: false,
        debug: false, // Set to true to log all emitted events.
        connection: socketIOConnection,
        ...restSimpleWebRTCOptions,
      });

      if (socketIOConnection.connected) {
        resolve(true);
        return;
      }
      this._SimpleWebRTCConnectionInstance.connection.on("connect", resolve);
      this._SimpleWebRTCConnectionInstance.connection.on("connect_error", reject);
    });
  }

  sendMessageToConnectionInstance(topic, payload, ack) {
    console.log('did we reach this point??, topic: ', topic, 'with payload: ', payload )
    let mappedTopic = topic;
    if (MAPPED_TOPICS.includes(topic)) {
      if (!this._roomId) {
        throw new Error(
          `Unable to map topic ${topic}: Session/Room id unknown (was the SimpleWebRTC session joined?`
        );
      }
      mappedTopic = SIMPLEWEBRTC_TOPIC_MAP(this._roomId)[topic];
      console.log('mappedTopic: ', mappedTopic)
    }
    if (!this._messageCallbacks[mappedTopic]) {
      console.error("No callback registered yet for topic", mappedTopic);
      return;
    }

    this._messageCallbacks[mappedTopic](payload, ack);
  }

  isConnected() {
    return (
      this._SimpleWebRTCConnectionInstance && this._SimpleWebRTCConnectionInstance.connection.connected
    );
  }

  getWebRTCId() {
    return this.isConnected()
      ? this._SimpleWebRTCConnectionInstance.connection.getSessionid()
      : undefined;
  }

  setStream(mediaStream) {
    const base = this._SimpleWebRTCConnectionInstance.webrtc;
    if (!base.localStreams.includes(mediaStream)) {
      base.localStreams.push(mediaStream);
    }
    base.emit("localStream", mediaStream);
  }

  stopLocalStreams() {
    if (!this._SimpleWebRTCConnectionInstance) {
      return;
    }
    const localmediaInstance = this._SimpleWebRTCConnectionInstance.webrtc;
    for (const stream of localmediaInstance.localStreams) {
      stream.getTracks().forEach((track) => track.stop());
      localmediaInstance.localStreams.splice(
        localmediaInstance.localStreams.indexOf(stream),
        1
      );
      localmediaInstance.emit("localStreamStopped", stream);
    }
  }

  joinRoom(roomId) {
    return new Promise((resolve, reject) => {
      this._SimpleWebRTCConnectionInstance.joinRoom(roomId, (error, roomDescription) => {
        if (error) {
          reject(error);
          return;
        }
        this._roomId = roomId;
        resolve(roomDescription);
      });
    });
  }

  leaveRoom() {
    this._roomId = null;
    this._SimpleWebRTCConnectionInstance && this._SimpleWebRTCConnectionInstance.leaveRoom();
  }


  openEventChannel() {
    // Define the listener for 'disconnect' event
    const disconnectListener = () => this._eventEmitter.emit('end');
  
    // Define the listener for '*' event
    const messageListener = (msg, ...args) => {
      this._eventEmitter.emit('message', {
        type: msg,
        payload: args,
        meta: {
          toRemote: false, // To explicitly prevent loop backs.
          fromRemote: true,
          fromService: this.id,
        },
      });
    };

    this._videoAddedListener = (videoElmt, webRtcPeer) => {
      console.log('videoAdded', videoElmt);
      this._eventEmitter.emit('videoAdded', {
        type: 'videoAdded',
        payload: [videoElmt, webRtcPeer]
      });
    };
  
    this._videoRemovedListener = (videoElmt, webRtcPeer) => {
      this._eventEmitter.emit('videoRemoved', {
        type: 'videoRemoved',
        payload: [videoElmt, webRtcPeer]
      });
    };
  
    // Add the listeners
    this._SimpleWebRTCConnectionInstance.connection.on('disconnect', disconnectListener);
    this._SimpleWebRTCConnectionInstance.on('*', messageListener);
    this._SimpleWebRTCConnectionInstance.on('videoAdded', this._videoAddedListener);
    this._SimpleWebRTCConnectionInstance.on('videoRemoved', this._videoRemovedListener);
  
    this._eventChannel = {
      listen: (event, callback) => {
        this._eventEmitter.on(event, callback);
      },
      stopListening: (event, callback) => {
        this._eventEmitter.off(event, callback);
      },
    };
  
    // Remember the listeners
    this._disconnectListener = disconnectListener;
    this._messageListener = messageListener;

  }
  

  async listenForMessagesFromServiceEventChannel(backendMessageHandlers, { dontDispatchOnDisconnect = false } = {}) {
    console.log("listenForMessagesFromServiceEventChannel called"); 
    try {
        this._eventChannel.listen('message', action => {
            console.log("received message from simplewebrtc event channel:", action)

            const msgTitle = action.type;
            const msgData = action.payload;
            console.log("Payload received from the event channel:", msgTitle, msgData);

            if (msgTitle in backendMessageHandlers) {
              console.log("Found handler for message type:", msgTitle);
              backendMessageHandlers[msgTitle](msgData)
            }

            // Handle these with the previously set message handlers.
            const hs = this.serviceMessageHandlers;
            for (const h of [hs[action.type], hs['*']].flat().filter(Boolean)) {
                h.call(this, action);
            }
        });


        this._eventChannel.listen('videoAdded', action => {
          console.log("received 'videoAdded' message from event channel:", action)
          // Additional handling...
          const hs = this.serviceMessageHandlers;
          for (const h of [hs[action.type], hs['*']].flat().filter(Boolean)) {
            h.call(this, action);
          }
        });
      
        this._eventChannel.listen('videoRemoved', action => {
          console.log("received 'videoRemoved' message from event channel:", action)
          // Additional handling...
          const hs = this.serviceMessageHandlers;
          for (const h of [hs[action.type], hs['*']].flat().filter(Boolean)) {
            h.call(this, action);
          }
        });


    } catch (err) {
        console.error(err);
    } finally {
      this._eventChannel.stopListening('videoAdded', this._videoAddedListener);
      this._eventChannel.stopListening('videoRemoved', this._videoRemovedListener);
      this._eventChannel.stopListening('message', this._messageListener);
      this._eventChannel.stopListening('disconnect', this._disconnectListener);
      if (!dontDispatchOnDisconnect) {
        this.actionCreators.NotifyDisconnected();
      }
    }

    console.log("listenForMessagesFromServiceEventChannel finished");
}


}
0

There are 0 answers