[webrtc-extensions] When is negotiation complete? (#45)

jan-ivar has just created a new issue for https://github.com/w3c/webrtc-extensions:

== When is negotiation complete? ==
A problem arose while writing [perfect negotiation](https://w3c.github.io/webrtc-pc/#perfect-negotiation-example) [wpt tests](https://phabricator.services.mozilla.com/D66817#C2294894NL1): When is negotiation complete? 
You know, something like:
```js
const transceiver = pc.addTransceiver("video");
await /* some event or promise */
assert_equals(transceiver.currentDirection, "sendonly", "negotiates to sendonly");
```

Naively, one might think this works:
```js
const state = (pc, s) => new Promise(r => pc.onsignalingstatechange =
                                          () => pc.signalingState == s && r());

const transceiver = pc.addTransceiver("video");
await state(pc, "stable");
assert_equals(transceiver.currentDirection, "sendonly", "negotiates to sendonly");
```
Unfortunately, outside a lab, →`"stable"` might come from rollback, or answering a remote offer.
Or even from _juuust_ missing our own negotiation train triggered by a previous local action.

This leaves us stuck writing action-specific spin-tests, not a great API:
```js
const transceiver = pc.addTransceiver("video");
while (!transceiver.currentDirection) {
  await state(pc, "stable");
}
assert_true(true, "we didn't time out!");
assert_equals(transceiver.currentDirection, "sendonly", "negotiates to sendonly");
```
So I tried solving this in JS by dispatching my own `negotiated` event in SRD(answer):
```js
// - The perfect negotiation logic, separated from the rest of the application ---

signaling.onmessage = async ({data: {description, candidate}}) => {
  try {
    if (description) {
      const offerCollision = description.type == "offer" &&
                             (makingOffer || pc.signalingState != "stable");

      ignoreOffer = !polite && offerCollision;
      if (ignoreOffer) {
        return;
      }
      await pc.setRemoteDescription(description); // SRD rolls back as needed
      if (description.type == "offer") {
        await pc.setLocalDescription();
        signaling.send({description: pc.localDescription});
      } else {
        pc.dispatchEvent(new Event("negotiated")); // <--- here!
      }
    } else if (candidate) {
```
This avoids rollbacks and remote offers, but we _still_ have to account for just missing a local train:
```js
const negotiated = pc => new Promise(r => pc.addEventListener("negotiated", r,
                                                              {once: true});

const transceiver = pc.addTransceiver("video");
await negotiated(pc);
if (!transceiver.currentDirection) {
  await negotiated(pc); // catch the next train
}
assert_equals(transceiver.currentDirection, "sendonly", "We're negotiated!");
```
This avoids the dreaded `while` loop. But who's going to remember this over some intermittent?

Please view or discuss this issue at https://github.com/w3c/webrtc-extensions/issues/45 using your GitHub account

Received on Thursday, 9 July 2020 15:21:52 UTC