Невозможно ограничить WebRTC P2P многопользовательский принимающий трафик

Я пытаюсь изменить пропускную способность на лету для видеозвонка P2P WebRTC, используя этот образец, объединяющий мой существующий код, который представляет собой видеозвонок с несколькими участниками:

Пример: https://webrtc.github.io/samples/src/content/peerconnection/bandwidth/

Когда я заглядываю в WebRTC Internals через Chrome,

bitsReceivedPerSecond для send (ssrc) (video) сброшено до выбранной полосы пропускания. Однако bitsReceivedPerSecond для recv (ssrc) (video) по-прежнему остается неизменным. Могу ли я узнать, как сделать, чтобы изменения пропускной способности применялись как для отправки, так и для приема?

Ниже приведены мои коды, было бы здорово, если бы вы помогли указать на мои ошибки, заранее спасибо.

Обновление 14.12.2018: в коды добавлен 1-й вариант для получателя.

Проблема: Uncaught TypeError: Receiver.getParameters не является функцией

const bandwidthSelector = document.querySelector('select#bandwidth');

bandwidthSelector.disabled = false;

// renegotiate bandwidth on the fly.
bandwidthSelector.onchange = () => {
  bandwidthSelector.disabled = true;
  const bandwidth = bandwidthSelector.options[bandwidthSelector.selectedIndex].value;

  // In Chrome, use RTCRtpSender.setParameters to change bandwidth without
  // (local) renegotiation. Note that this will be within the envelope of
  // the initial maximum bandwidth negotiated via SDP.
  if ((adapter.browserDetails.browser === 'chrome' ||
       (adapter.browserDetails.browser === 'firefox' &&
        adapter.browserDetails.version >= 64)) &&
      'RTCRtpSender' in window &&
      'setParameters' in window.RTCRtpSender.prototype) {

        $.each(peers, function( index, value ) {
            const sender = value.getSenders()[0];
            const parameters = sender.getParameters();
            if (!parameters.encodings) {
              parameters.encodings = [{}];
            }
            if (bandwidth === 'unlimited') {
              delete parameters.encodings[0].maxBitrate;
            } else {
              parameters.encodings[0].maxBitrate = bandwidth * 1000;
            }
            sender.setParameters(parameters)
            .then(() => {
              bandwidthSelector.disabled = false;
            })
            .catch(e => console.error(e));

            /* 1st Option - Start */
            const receiver = value.getReceivers()[0];
            const recParameters = receiver.getParameters();

            if (!recParameters.encodings) {
              recParameters.encodings = [{}];
            }
            if (bandwidth === 'unlimited') {
              delete recParameters.encodings[0].maxBitrate;
            } else {
              recParameters.encodings[0].maxBitrate = bandwidth * 1000;
            }
            receiver.setParameters(recParameters)
            .then(() => {
              bandwidthSelector.disabled = false;
            })
            .catch(e => console.error(e));

            /* 1st Option - End */

            return;

        });             
  }

  // Fallback to the SDP munging with local renegotiation way of limiting
  // the bandwidth.
  function onSetSessionDescriptionError(error) {
      console.log('Failed to set session description: ' + error.toString());
    }
};

function updateBandwidthRestriction(sdp, bandwidth) {
  let modifier = 'AS';
  if (adapter.browserDetails.browser === 'firefox') {
    bandwidth = (bandwidth >>> 0) * 1000;
    modifier = 'TIAS';
  }
  if (sdp.indexOf('b=' + modifier + ':') === -1) {
    // insert b= after c= line.
    sdp = sdp.replace(/c=IN (.*)\r\n/, 'c=IN $1\r\nb=' + modifier + ':' + bandwidth + '\r\n');
  } else {
    sdp = sdp.replace(new RegExp('b=' + modifier + ':.*\r\n'), 'b=' + modifier + ':' + bandwidth + '\r\n');
  }
  return sdp;
}

function removeBandwidthRestriction(sdp) {
  return sdp.replace(/b=AS:.*\r\n/, '').replace(/b=TIAS:.*\r\n/, '');
}

Обновление от 14 декабря 2018 г. 2-й вариант createOffer

Проблема: не удалось установить описание сеанса: InvalidStateError: не удалось выполнить setRemoteDescription на «RTCPeerConnection»: не удалось установить удаленный ответ sdp: вызывается в неправильном состоянии: kStable

const bandwidthSelector = document.querySelector('select#bandwidth');

bandwidthSelector.disabled = false;

// renegotiate bandwidth on the fly.
bandwidthSelector.onchange = () => {
  bandwidthSelector.disabled = true;
  const bandwidth = bandwidthSelector.options[bandwidthSelector.selectedIndex].value;

  // In Chrome, use RTCRtpSender.setParameters to change bandwidth without
  // (local) renegotiation. Note that this will be within the envelope of
  // the initial maximum bandwidth negotiated via SDP.
  if ((adapter.browserDetails.browser === 'chrome' ||
       (adapter.browserDetails.browser === 'firefox' &&
        adapter.browserDetails.version >= 64)) &&
      'RTCRtpSender' in window &&
      'setParameters' in window.RTCRtpSender.prototype) {

        $.each(peers, function( index, value ) {
            const sender = value.getSenders()[0];
            const parameters = sender.getParameters();
            if (!parameters.encodings) {
              parameters.encodings = [{}];
            }
            if (bandwidth === 'unlimited') {
              delete parameters.encodings[0].maxBitrate;
            } else {
              parameters.encodings[0].maxBitrate = bandwidth * 1000;
            }
            sender.setParameters(parameters)
            .then(() => {
              bandwidthSelector.disabled = false;
            })
            .catch(e => console.error(e));

            /* 2nd option - Start */
            value.createOffer(
                    function (local_description) {
                        console.log("Local offer description is: ", local_description);
                        value.setLocalDescription(local_description,
                            function () {
                                signaling_socket.emit('relaySessionDescription', {
                                    'peer_id': index,
                                    'session_description': local_description
                                });
                                console.log("Offer setLocalDescription succeeded");
                            },
                            function () {
                                Alert("Offer setLocalDescription failed!");
                            }
                        );
                    },
                    function (error) {
                        console.log("Error sending offer: ", error);
                    }).then(() => {
                  const desc = {
                    type: value.remoteDescription.type,
                    sdp: bandwidth === 'unlimited'
                      ? removeBandwidthRestriction(value.remoteDescription.sdp)
                      : updateBandwidthRestriction(value.remoteDescription.sdp, bandwidth)
                  };
                  console.log('Applying bandwidth restriction to setRemoteDescription:\n' +
                    desc.sdp);
                  return value.setRemoteDescription(desc);
                })
                .then(() => {
                  bandwidthSelector.disabled = false;
                })
                .catch(onSetSessionDescriptionError);

            /* 2nd option - End */

            return;

        });             
  }

  // Fallback to the SDP munging with local renegotiation way of limiting
  // the bandwidth.
  function onSetSessionDescriptionError(error) {
      console.log('Failed to set session description: ' + error.toString());
    }
};

function updateBandwidthRestriction(sdp, bandwidth) {
  let modifier = 'AS';
  if (adapter.browserDetails.browser === 'firefox') {
    bandwidth = (bandwidth >>> 0) * 1000;
    modifier = 'TIAS';
  }
  if (sdp.indexOf('b=' + modifier + ':') === -1) {
    // insert b= after c= line.
    sdp = sdp.replace(/c=IN (.*)\r\n/, 'c=IN $1\r\nb=' + modifier + ':' + bandwidth + '\r\n');
  } else {
    sdp = sdp.replace(new RegExp('b=' + modifier + ':.*\r\n'), 'b=' + modifier + ':' + bandwidth + '\r\n');
  }
  return sdp;
}

function removeBandwidthRestriction(sdp) {
  return sdp.replace(/b=AS:.*\r\n/, '').replace(/b=TIAS:.*\r\n/, '');
}

person Jessica    schedule 13.12.2018    source источник


Ответы (1)


RTCRtpSender контролирует только пропускную способность отправки. Если вы хотите ограничить полосу пропускания приема, вам нужно использовать либо способ b = AS / b = TIAS, либо заставить приемник использовать setParameters.

person Philipp Hancke    schedule 13.12.2018
comment
Привет @Philipp, я пробовал два варианта, однако я не знаком с WebRTC, оба варианта возвращали ошибки, могу ли я узнать, какой вариант подходит и как заставить его работать? Я обновил приведенные выше коды вместе с проблемами. Спасибо - person Jessica; 14.12.2018
comment
ах ... под приемником я имел в виду сторону, принимающую вызов, а не RTCRtpReceiver. Терминология непростая. Ошибка вызова в неправильном состоянии, вероятно, связана с использованием onnegotiationeeded. По этому поводу есть несколько вопросов по stackoverflow. - person Philipp Hancke; 14.12.2018
comment
Привет, @Philipp, я наконец решил эту проблему, отправив получателю запрос на изменение пропускной способности. Большое спасибо! - person Jessica; 14.12.2018