Проверка состояния WebRTC - PullRequest
       9

Проверка состояния WebRTC

0 голосов
/ 03 декабря 2018

Я хотел бы соединить 2 устройства с WebRTC на localhost.Все устройства не имеют доступа в интернет.Они подключены к одному и тому же локальному Wi-Fi.

Я пробую это на React Native App.

В этом контексте в автономном режиме, нужно ли мне обмениваться кандидатами ICE и addIceCandidate?Если я правильно понимаю, кандидаты ICE для iceServer.Но в моем случае iceServer является нулевым (потому что я только в автономном режиме, подключен к тому же локальному Wi-Fi):

const configuration = { iceServers: [{ urls: [] }] };

Итак, я обмениваюсь предложением и ответом, но после setRemoteDescription ответ, connectionState остаться на checking.

Вы можете видеть мой React Component:

  constructor(props) {
    super(props);
    this.pc = new RTCPeerConnection(configuration);
  }

  state = initialState;

  componentDidMount() {
    const { pc } = this;

    if (pc) {
      this.setState({
        peerCreated: true
      });
    }

    this.setConnectionState();

    pc.oniceconnectionstatechange = () => this.setConnectionState();

    pc.onaddstream = ({ stream }) => {
      if (stream) {
        this.setState({
          receiverVideoURL: stream.toURL()
        });
      }
    };

    pc.onnegotiationneeded = () => {
      if (this.state.initiator) {
        this.createOffer();
      }
    };

    pc.onicecandidate = ({ candidate }) => {
      if (candidate === null) {
        const { offer } = this.state;
        const field = !offer ? 'offer' : 'data';

        setTimeout(() => {
          alert('setTimeout started');
          this.setState({
            [field]: JSON.stringify(pc.localDescription)
          });
        }, 2000);
      }
    };
  }

  @autobind
  setConnectionState() {
    this.setState({
      connectionState: this.pc.iceConnectionState
    });
  }

  getUserMedia() {
    MediaStreamTrack.getSources(() => {
      getUserMedia(
        {
          audio: false,
          video: true
        },
        this.getUserMediaSuccess,
        this.getUserMediaError
      );
    });
  }

  @autobind
  async getUserMediaSuccess(stream) {
    const { pc } = this;

    pc.addStream(stream);

    await this.setState({ videoURL: stream.toURL() });

    if (this.state.initiator) {
      return this.createOffer();
    }

    return this.createAnswer();
  }

  getUserMediaError(error) {
    console.log(error);
  }

  @autobind
  logError(error) {
    const errorArray = [...this.state.error, error];
    return this.setState({
      error: errorArray
    });
  }

  /**
   * Create offer
   *
   * @memberof HomeScreen
   */
  @autobind
  createOffer() {
    const { pc } = this;

    pc.createOffer()
      .then(offer => pc.setLocalDescription(offer))
      .then(() => {
        this.setState({
          offerCreated: true
        });
      })
      .catch(this.logError);
  }

  /**
   * Create anwser
   *
   * @memberof HomeScreen
   */
  @autobind
  async createAnswer() {
    const { pc } = this;
    const { data } = this.state;

    if (data) {
      const sd = new RTCSessionDescription(JSON.parse(data));

      await this.setState({
        offerImported: true
      });

      pc.setRemoteDescription(sd)
        .then(() => pc.createAnswer())
        .then(answer => pc.setLocalDescription(answer))
        .then(() => {
          this.setState({
            answerCreated: true
          });
        })
        .catch(this.logError);
    }
  }

  @autobind
  receiveAnswer() {
    const { pc } = this;
    const { data } = this.state;
    const sd = new RTCSessionDescription(JSON.parse(data));

    return pc
      .setRemoteDescription(sd)
      .then(() => {
        this.setState({
          answerImported: true
        });
      })
      .catch(this.logError);
  }

  /**
   * Start communication
   *
   * @param {boolean} [initiator=true]
   * @returns
   * @memberof HomeScreen
   */
  @autobind
  async start(initiator = true) {
    if (!initiator) {
      await this.setState({
        initiator: false
      });
    }

    return this.getUserMedia();
  }

Кто-нибудь может мне помочь?

1 Ответ

0 голосов
/ 03 декабря 2018

Нет iceServers - это нормально для локальной сети, но одноранговые узлы все равно должны обмениваться хотя бы одним кандидатом: их хост кандидат (в зависимости от IP-адреса локальной сети их машины).

Либо:

  1. Поток кандидатов, использующих onicecandidate -> сигнализация -> addIceCandidate как обычно, или

  2. Ожидание процесса ICE (aнесколько секунд) перед обменом pc.localDescription.

Похоже, вы пытаетесь последнее.Этот подход работает, потому что ...

Trickle ICE - это оптимизация.

Сигнализация (просачивание) отдельных кандидатов на лед, использующих onicecandidate, - это оптимизация, предназначенная для ускорения переговоров.После успешного выполнения setLocalDescription запускается встроенный в браузер Агент ICE браузера, который вставляет кандидатов ICE, как они были обнаружены, в сам localDescription.Подождите несколько секунд, чтобы договориться, и в этом нет необходимости: все предложения ICE будут в предложении и ответ будет передан.

Ваш код

Из вашего кода onicecandidate он выглядиткак будто вы уже пытаетесь собрать localDescription после завершения ICE (и вы написали его для работы с обоих концов):

pc.onicecandidate = ({ candidate }) => {
  if (!candidate) {
    const { offer } = this.state;
    const field = !offer ? 'offer' : 'data';

    this.setState({
      [field]: JSON.stringify(pc.localDescription)
    });
  }
};

На стороне отправителя вы правильно закомментировали эквиваленткод в createOffer:

pc.createOffer()
  .then(offer => pc.setLocalDescription(offer))
  .catch(this.logError);
// .then(() => {
//   this.setState({
//     offer: JSON.stringify(pc.localDescription)
//   });
// });

Но на стороне ответчика у вас нет, и это, вероятно, проблема:

createAnswer() {
    const { pc } = this;
    const { data } = this.state;

    if (data) {
      const sd = new RTCSessionDescription(JSON.parse(data));

      pc.setRemoteDescription(sd)
        .then(() => pc.createAnswer())
        .then(answer => pc.setLocalDescription(answer))
        .then(() => {
          this.setState({
            offer: JSON.stringify(pc.localDescription)
          });
        })
        .catch(this.logError);
    }
  }

Это означает, что он отправляет ответ немедленно, до того, какагент ICE ответчика успел вставить в ответ кандидатов. Возможно, именно поэтому он не работает.

На дополнительном примечании: Ничто не может дождаться завершения getUserMedia, поэтому ответы скорее всего не будут содержатьлюбое видео, в зависимости от времени вашей функции getUserMediaSuccess, которая не может добавить какие-либо дорожки или потоки к соединению.Но при условии, что вы просто делаете каналы данных, это должно работать с моими рекомендуемыми исправлениями.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...