init method

Future<void> init()

Implementation

Future<void> init() async {
  _peerConnection = await createPeerConnection(_configuration.toMap());

  if (_createDataChannel) {
    RTCDataChannelInit dataChannelDict = RTCDataChannelInit()
      ..ordered = true
      ..maxRetransmits = 3
      ..protocol = "json";

    _dataChannel = await _peerConnection.createDataChannel(SignalingClient.randomString(), dataChannelDict);
    _peerConnection.onDataChannel = (event) {
      event.onMessage = _dataChannelMessageReceived;
    };
  }

  _addIceCandidate = (candidate) async {
    if (candidate.clientId != _remoteClientId) {
      // All ICE candidates received over signaling will be received via this callback.
      // Ignore ICE candidates not directed for this PeerConnection (when multiple
      // viewer participants are connecting to the same signaling channel).
      return;
    }

    log('Received ICE candidate from ${candidate.clientId ?? 'remote'}');
    log('ICE candidate: ${candidate.iceCandidate.toMap()}');

    if (_inboundIceCandidateFilterFn(candidate.iceCandidate)) {
      // Add the ICE candidate received from the client to the peer connection
      await _peerConnection.addCandidate(candidate.iceCandidate);
    } else {
      log('Candidate rejected through filter. Not adding candidate from ${candidate.clientId ?? 'remote'}.');
    }
  };

  _signalingClient.onIceCandidate.listen(_addIceCandidate);

  _peerConnection.onIceCandidate = (event) async {
    RTCIceCandidate? candidate = event;

    if (candidate != null && candidate.candidate != null) {
      log('Generated ICE candidate for ${_remoteClientId ?? 'remote'}');
      log('ICE candidate: $candidate');

      // When trickle ICE is enabled, send the ICE candidates as they are generated.
      if (_trickleICE) {
        if (_outboundIceCandidateFilterFn(candidate)) {
          log('Sending ICE candidate to ${_remoteClientId ?? 'remote'}');
          _signalingClient.sendIceCandidate(candidate, recipientClientId: _remoteClientId);
        } else {
          log('Not sending ICE candidate to ${_remoteClientId ?? 'remote'}');
        }
      }
    } else if (candidate == null) {
      // Firefox special case: candidate with null candidate field
    } else {
      log('All ICE candidates have been generated for ${_remoteClientId ?? 'remote'}');

      // When trickle ICE is disabled, send the answer now that all the ICE candidates have been generated.
      if (!_trickleICE) {
        log('Sending SDP answer to ${_remoteClientId ?? 'remote'}');
        final correlationId = SignalingClient.randomString();

        final local = await _peerConnection.getLocalDescription() as RTCSessionDescription;
        log('SDP answer: ${local.toMap()} correlationId: $correlationId');
        _signalingClient.sendSdpAnswer(local, recipientClientId: _remoteClientId, correlationId: correlationId);
      }
    }
  };

  // We receive this event when the remote peer adds a new track to the PeerConnection
  _peerConnection.onTrack = (event) {
    log(
      'Received ${event.track.kind ?? 'unknown'} track from ${_remoteClientId ?? 'remote'} '
          'in mediaStream: ${event.streams.first.id ?? '[Error retrieving stream ID]'} with track id: ${event.track.id}',
    );
    _onMediaStreamsUpdated(event.streams);
  };

  // If there's no video/audio, this._mediaStream will be null. So, we should skip adding the tracks from it.
  if (_mediaStream != null) {
    _mediaStream.getTracks().forEach((track) {
      _peerConnection.addTrack(track, _mediaStream);
    });
  }

  await _peerConnection.setRemoteDescription(_offer);

  for (final transceiver in await _peerConnection.getTransceivers()) {
    if (transceiver.receiver.track?.kind == 'video' && videoCodecs.length > 0) {
      transceiver.setCodecPreferences(videoCodecs);
    } else if (transceiver.receiver.track?.kind == 'audio' && audioCodecs.length > 0) {
      transceiver.setCodecPreferences(audioCodecs);
    }
  }

  // Create an SDP answer to send back to the client
  log('Creating SDP answer for ${_remoteClientId ?? 'remote'}');
  final answer = await _peerConnection.createAnswer({'offerToReceiveAudio': true, 'offerToReceiveVideo': true});
  await _peerConnection.setLocalDescription(answer);

  // When trickle ICE is enabled, send the answer now and then send ICE candidates as they are generated. Otherwise wait on the ICE candidates.
  if (_trickleICE) {
    log('Sending SDP answer to ${_remoteClientId ?? 'remote'}');
    final correlationId = SignalingClient.randomString();
    log('SDP answer: ${_peerConnection.getLocalDescription()} correlationId: $correlationId');
    _signalingClient.sendSdpAnswer(
      await _peerConnection.getLocalDescription() as RTCSessionDescription,
      recipientClientId: _remoteClientId,
      correlationId: correlationId,
    );
  }

  log('Generating ICE candidates for ${_remoteClientId ?? 'remote'}');
}