streamsDone method

Future streamsDone (
  1. int handleId,
  2. RTCSessionDescription jsep,
  3. Map media,
  4. dynamic callbacks,
  5. MediaStream stream
)

Implementation

streamsDone(int handleId, RTCSessionDescription jsep, Map media, callbacks,
    MediaStream stream) async {
  Plugin pluginHandle = this.pluginHandles[handleId.toString()];
  if (pluginHandle == null) {
    Janus.warn("Invalid handle");
    callbacks.error("Invalid handle");
    return;
  }
  Janus.debug("streamsDone:" + stream.toString());
  if (stream != null) {
    Janus.debug("  -- Audio tracks:" + stream.getAudioTracks().toString());
    Janus.debug("  -- Video tracks:" + stream.getVideoTracks().toString());
  }
  // We're now capturing the new stream: check if we're updating or if it's a new thing
  bool addTracks = false;
  if (pluginHandle.myStream == null ||
      !media['update'] ||
      pluginHandle.streamExternal) {
    pluginHandle.myStream = stream;
    addTracks = true;
  } else {
    // We only need to update the existing stream
    if (((!media['update'] && isAudioSendEnabled(media)) ||
            (media['update'] &&
                (media['addAudio'] || media['replaceAudio']))) &&
        stream.getAudioTracks() != null &&
        stream.getAudioTracks().length > 0) {
      pluginHandle.myStream.addTrack(stream.getAudioTracks()[0]);
      if (Janus.unifiedPlan) {
        // Use Transceivers
        Janus.log((media['replaceAudio'] ? "Replacing" : "Adding") +
            " audio track:" +
            stream.getAudioTracks()[0].toString());
        Map<String, dynamic> audioTransceiver;
        // FIX ME
        // List transceivers = pc.getTransceivers();
        List transceivers = [];
        if (transceivers != null && transceivers.length > 0) {
          for (var t in transceivers) {
            // TODO sender is MediaStreamTrack
            if ((t['sender'] &&
                    t['sender'].track &&
                    t['sender'].track.kind == "audio") ||
                (t['receiver'] &&
                    t['receiver'].track &&
                    t['receiver'].track.kind == "audio")) {
              audioTransceiver = t;
              break;
            }
          }
        }
        if (audioTransceiver != null && audioTransceiver['sender'] != null) {
          // Todo implement replaceTrack
          audioTransceiver['sender'].replaceTrack(stream.getAudioTracks()[0]);
        } else {
          // FIX ME
          // pc.addTrack(stream.getAudioTracks()[0], stream);
          pluginHandle.pc.addStream(stream);
        }
      } else {
        Janus.log((media['replaceAudio'] ? "Replacing" : "Adding") +
            " audio track:" +
            stream.getAudioTracks()[0].toString());
        // FIX ME
        // pc.addTrack(stream.getAudioTracks()[0], stream);
        pluginHandle.pc.addStream(stream);
      }
    }
    if (((!media['update'] && isVideoSendEnabled(media)) ||
            (media['update'] &&
                (media['addVideo'] || media['replaceVideo']))) &&
        stream.getVideoTracks() != null &&
        stream.getVideoTracks().length > 0) {
      pluginHandle.myStream.addTrack(stream.getVideoTracks()[0]);
      if (Janus.unifiedPlan) {
        // Use Transceivers
        Janus.log((media['replaceVideo'] ? "Replacing" : "Adding") +
            " video track:" +
            stream.getVideoTracks()[0].toString());
        Map<String, dynamic> videoTransceiver;
        // List transceivers = pc.getTransceivers();
        List transceivers = [];
        if (transceivers != null && transceivers.length > 0) {
          for (var t in transceivers) {
            // TODO sender is MediaStreamTrack
            if ((t['sender'] &&
                    t['sender'].track &&
                    t['sender'].track.kind == "video") ||
                (t['receiver'] &&
                    t['receiver'].track &&
                    t['receiver'].track.kind == "video")) {
              videoTransceiver = t;
              break;
            }
          }
        }
        if (videoTransceiver != null && videoTransceiver['sender'] != null) {
          // Todo implement replaceTrack
          videoTransceiver['sender'].replaceTrack(stream.getVideoTracks()[0]);
        } else {
          // FIX ME
          // pc.addTrack(stream.getVideoTracks()[0], stream);
          pluginHandle.pc.addStream(stream);
        }
      } else {
        Janus.log((media['replaceVideo'] ? "Replacing" : "Adding") +
            " video track:" +
            stream.getVideoTracks()[0].toString());
        // FIX ME
        // pc.addTrack(stream.getVideoTracks()[0], stream);
        pluginHandle.pc.addStream(stream);
      }
    }
  }
  // If we still need to create a PeerConnection, let's do that
  if (pluginHandle.pc == null) {
    Map<String, dynamic> pcConfig = {"iceServers": this.iceServers};
    if (this.iceTransportPolicy != null)
      pcConfig["iceTransportPolicy"] = this.iceTransportPolicy;
    if (this.bundlePolicy != null)
      pcConfig["bundlePolicy"] = this.bundlePolicy;
    if (Janus.webRTCAdapter['browserDetails']['browser'] == "chrome") {
      // For Chrome versions before 72, we force a plan-b semantic, and unified-plan otherwise
      pcConfig["sdpSemantics"] =
          (Janus.webRTCAdapter['browserDetails']['version'] < 72)
              ? "plan-b"
              : "unified-plan";
    }
    Map<String, dynamic> pcConstraints = {
      "mandatory": {},
      "optional": [
        {"DtlsSrtpKeyAgreement": true}
      ]
    };
    if (this.ipv6Support) {
      pcConstraints['optional'].add({"googIPv6": true});
    }
    // Any custom constraint to add?
    if (callbacks.rtcConstraints != null) {
      Janus.debug("Adding custom PeerConnection constraints:" +
          callbacks.rtcConstraints.toString()); // FIX ME
      for (var i in callbacks.rtcConstraints) {
        pcConstraints['optional'].add(callbacks.rtcConstraints[i]);
      }
    }
    if (Janus.webRTCAdapter['browserDetails']['browser'] == "edge") {
      // This is Edge, enable BUNDLE explicitly
      pcConfig['bundlePolicy'] = "max-bundle";
    }
    Janus.log("Creating PeerConnection");
    Janus.debug(pcConstraints.toString());
    Janus.debug(pcConfig.toString());
    // From webrtc
    pluginHandle.pc = await createPeerConnection(pcConfig, pcConstraints);
    Janus.debug("Peer Connection is ready");
    Janus.debug(pluginHandle.pc.toString());
    // FIXME

    // pluginHandle.pc.getStats().then((List<StatsReport> stats) {
    //   if (stats != null) {
    //     Janus.log(
    //         "PC Stats: " + stats[1].type + stats[1].values.toString());
    //     pluginHandle.volume = {};
    //     pluginHandle.bitrate['value'] = "0 kbits/sec";
    //   }
    // }).catchError((error, StackTrace stackTrace) {
    //   Janus.error(error.toString());
    // });

    Janus.log("Preparing local SDP and gathering candidates (trickle=" +
        pluginHandle.trickle.toString() +
        ")");

    pluginHandle.pc.onIceConnectionState = (RTCIceConnectionState state) {
      if (pluginHandle.pc != null) pluginHandle.iceState(state);
    };

    pluginHandle.pc.onIceCandidate = (RTCIceCandidate candidate) {
      if (candidate == null ||
          (Janus.webRTCAdapter['browserDetails']['browser'] == 'edge' &&
              candidate.candidate.indexOf('endOfCandidates') > 0)) {
        Janus.log("End of candidates.");
        pluginHandle.iceDone = true;
        Janus.log(pluginHandle.trickle);
        if (pluginHandle.trickle == true) {
          // Notify end of candidates
          sendTrickleCandidate(handleId, {"completed": true});
        } else {
          // No trickle, time to send the complete SDP (including all candidates)
          sendSDP(handleId, callbacks);
        }
      } else {
        // JSON.stringify doesn't work on some WebRTC objects anymore
        // See https://code.google.com/p/chromium/issues/detail?id=467366
        // RTCIceCandidate candidate = RTCIceCandidate(iceCandidate.candidate,
        //     iceCandidate.sdpMid, iceCandidate.sdpMlineIndex);
        if (pluginHandle.trickle == true) {
          // Send candidate
          sendTrickleCandidate(handleId, candidate.toMap());
          Janus.debug(candidate.toMap());
        }
      }
    };

    pluginHandle.pc.onAddStream = (MediaStream stream) {
      Janus.log("Handling Remote Stream");
      Janus.debug(stream);
      if (stream == null) return;
      pluginHandle.remoteStream = stream;
      pluginHandle.onRemoteStream(stream);
    };

    pluginHandle.pc.onRemoveStream = (MediaStream stream) {
      Janus.log('onRemoveStream event call');
      Janus.log(stream.toString());
    };

    pluginHandle.pc.onAddTrack =
        (MediaStream stream, MediaStreamTrack track) {
      Janus.log("Handling Remote Track");
      Janus.debug(stream);
      if (stream == null) return;
      pluginHandle.remoteStream = stream;
      pluginHandle.onRemoteStream(stream);

      // FIX ME no equivalent call exists in flutter_webrtc
      // if (event.track.onended) return;
      // Janus.log("Adding onended callback to track:" + event.track);
      // event.track.onended = (ev) {
      //   Janus.log("Remote track muted/removed:" + ev);
      //   if (pluginHandle.remoteStream) {
      //     pluginHandle.remoteStream.removeTrack(ev.target);
      //     pluginHandle.onremotestream(pluginHandle.remoteStream);
      //   }
      // };

      // FIX ME no equivalent call exists in flutter_webrtc
      // event.track.onmute = event.track.onended;
      // event.track.onunmute = (ev) {
      //   Janus.log("Remote track flowing again:" + ev);
      //   try {
      //     pluginHandle.remoteStream.addTrack(ev.target);
      //     pluginHandle.onremotestream(pluginHandle.remoteStream);
      //   } catch (e) {
      //     Janus.error(e);
      //   }
      //   ;
      // };
    };

    // TODO connect addTrack
    if (addTracks && stream != null) {
      // var simulcast2 = (callbacks.simulcast2 == true);
      // FIX ME: janus.js  find out all the track from the stream and then add to the PC
      // There is no equivalnet call in flutter_webrtc. We will add the stream to PC
      pluginHandle.pc.addStream(stream).then((void v) {
        Janus.log("Stream added to PC");
      }).catchError((error, StackTrace stackTrace) {
        Janus.log(stackTrace);
        Janus.error(error.toString());
      });
      // // Get a list of audio and video tracks
      // List<MediaStreamTrack> tracks =
      //     stream.getAudioTracks() + stream.getVideoTracks();
      // tracks.forEach((MediaStreamTrack track) {
      //   Janus.log('Adding local track:' + track.toString());
      //   if (!simulcast2) {
      //     Janus.log('here i am');
      //     pluginHandle.pc.addTrack(track, stream);
      //   } else {
      //     if (track.kind == "audio") {
      //       pluginHandle.pc.addTrack(track, stream);
      //     } else {
      //       Janus.log(
      //           'Enabling rid-based simulcasting:' + track.toString());
      //       var maxBitrates =
      //           getMaxBitrates(callbacks.simulcastMaxBitrates);
      //       pc.addTransceiver(track, {
      //         'direction': "sendrecv",
      //         'streams': [stream],
      //         'sendEncodings': [
      //           {
      //             'rid': "h",
      //             'active': true,
      //             'maxBitrate': maxBitrates['high']
      //           },
      //           {
      //             'rid': "m",
      //             'active': true,
      //             'maxBitrate': maxBitrates['medium'],
      //             'scaleResolutionDownBy': 2
      //           },
      //           {
      //             'rid': "l",
      //             'active': true,
      //             'maxBitrate': maxBitrates['low'],
      //             'scaleResolutionDownBy': 4
      //           }
      //         ]
      //       });
      //     }
      //   }
      // });
    }

    // Any data channel to create?
    if (isDataEnabled(media) &&
        pluginHandle.dataChannels[Janus.dataChanDefaultLabel] == null) {
      Janus.log("Creating data channel");
      createDataChannel(handleId, Janus.dataChanDefaultLabel, null, null);
      Janus.log('failing here');
      pluginHandle.pc.onDataChannel = (var channel) {
        Janus.log(
            "Data channel created by Janus:" + channel.toString()); // FIX ME
        createDataChannel(handleId, "label", channel, null);
      };
    }

    // If there's a new local stream, let's notify the application
    if (pluginHandle.myStream != null) {
      pluginHandle.onLocalStream(pluginHandle.myStream);
    }

    // Create offer/answer now
    if (jsep == null) {
      createOffer(handleId, media, callbacks);
    } else {
      pluginHandle.pc.setRemoteDescription(jsep).then((void v) {
        Janus.log("Remote description accepted!");
        pluginHandle.remoteSdp = jsep.sdp;
        // Any trickle candidate we cached?
        if (pluginHandle.candidates != null &&
            pluginHandle.candidates.length > 0) {
          for (var i = 0; i < pluginHandle.candidates.length; i++) {
            RTCIceCandidate candidate = pluginHandle.candidates[i];
            Janus.debug("Adding remote candidate:" + candidate.toString());
            if (candidate == null) {
              // end-of-candidates
              pluginHandle.pc.addCandidate(Janus.endOfCandidates);
            } else {
              // New candidate
              pluginHandle.pc.addCandidate(candidate);
            }
          }
          pluginHandle.candidates = [];
        }
        // Create the answer now
        createAnswer(handleId, media, callbacks, null);
      }).catchError((error, StackTrace stackTrade) {
        callbacks.error(error);
      });
    }
  }
}