Webrtc-pc: pc.addTrack/removeTrack can be described in terms of Transceivers. This may be good for understanding them.

Created on 1 Feb 2018  Â·  18Comments  Â·  Source: w3c/webrtc-pc

In APIs in general we want to avoid competing mental models at all cost. We have (had) three.

addTrack/removeTrack are mostly redundant stage 2 leaky abstractions with false symmetry. They're no longer the right abstraction to view the design through, causing confusion.

I'm not suggesting we remove them from the spec, but that we move them to a legacy section to avoid people being confused by them when looking for things that inform the design.

Why they're confusing

What they have in common with addStream/removeStream is that:

  1. They never quite fit, and were an iteration toward the addTransceiver API.
  2. If you try to understand WebRTC through them, you end up with the wrong model in your head.

Consider how poorly their names match what they do now:

  1. pc.addTrack(track, stream) adds a transceiver (or piggybacks on one from SRD).
  2. pc.removeTrack(sender) is a setter for sender.track (to null) and sender.direction.

Naively, one would expect one to undo the other, but this is wholly untrue. This makes them poor APIs.

Why they're redundant

Modern equivalents exist: pc.addTransceiver(track, {streams}), sender.direction = newdir and await sender.replaceTrack(track). Though beware they don't work exactly the same in edge-cases.

Editorial

Most helpful comment

Maybe we should take a step back in the discussion here.

I hope everyone agrees that we three API iterations in the spec right now. Is the intention to keep all three of them?

It sound to me like keeping multiple version of the APIs make things:

  1. very complicated for browser to implement, as multiple APIs will result in unpredictable behavior, especially if developers start mixing the different APIs (which they will do if they all co-exist).
  2. confusing to users of the API as they surely assume they can use all of them

So I always expected that at some point in the future we can turn stream and track based APIs off to avoid confusion and delete code which causes confusion.

That's why I'm in favor of marking stream and track based APIs as legacy to indicate to developers that they should not use them going forward.

If the intention here is to keep stream and track based APIs around, then I fear this working group still has a lot of work ahead of it defining all the edge case between the different API versions, because these are clearly underspecified right now.

All 18 comments

Competing mental models are not a Bad thing, inherently.

Breaking users' deployed code is a Bad Thing, unconditionally.
We should only move to a new model when we can fully implement the old API in terms of the old one, so that users who depend on the old functionality know EXACTLY how to keep their own applications working. Exceptions should only be allowed when the benefit of hurting the edge case is huge.

"Don't work exactly the same in edge cases" translates to "breaks my app in surprising ways".

If we can define old APIs in terms of new APIs, to a very high degree of fidelity, I'm OK with doing so - it simplifies the overall implementation model.

But if we make things that used to work in real applications impossible to achieve in the name of "more elegant APIs", I believe we have left the path of wisdom.

Again, I'm not talking about removing anything from the spec, so there's no "breaking deployed code".

While I agree with your technical assessment of addTrack/removeTrack , the question is what action we are trying to encourage by marking these APIs as legacy.

Are we saying that implementing those APIs is optional? Or that browsers that have not already shipped them should consider removing them from their future plans?

As Harald says, if there is deployed code depending on those APIs, either of those actions will impact developers negatively.

Developers such as @fippo have already given us some feedback about the costs of the stream -> track transition.

Not my intention. My concern is merely editorial, to guide people toward the "new" APIs, and not infer much from the old ones.

E.g. "Note: addTrack/removeTrack have been superseded by addTransceiver. Please go there." basically

Maybe we should take a step back in the discussion here.

I hope everyone agrees that we three API iterations in the spec right now. Is the intention to keep all three of them?

It sound to me like keeping multiple version of the APIs make things:

  1. very complicated for browser to implement, as multiple APIs will result in unpredictable behavior, especially if developers start mixing the different APIs (which they will do if they all co-exist).
  2. confusing to users of the API as they surely assume they can use all of them

So I always expected that at some point in the future we can turn stream and track based APIs off to avoid confusion and delete code which causes confusion.

That's why I'm in favor of marking stream and track based APIs as legacy to indicate to developers that they should not use them going forward.

If the intention here is to keep stream and track based APIs around, then I fear this working group still has a lot of work ahead of it defining all the edge case between the different API versions, because these are clearly underspecified right now.

So we got rid of the legacy streams API without specifiyng them. Philosophically that is bad but nobody volunteered to write the spec. It is very clear for developers that addStream is the legacy API.

addTrack/removeTrack is confusing because it is supported both in the "track-based" API (shipped in Firefox and recently Chrome) and the transceiver model (in FF59). And they behave very different in both models which creates a lot of confusion.

I dislike addTransceiver because it is much easier to explain a web developer

  • "you take your stream from getUserMedia and add the stream to the peerconnection" or
  • "you take your stream from getUserMedia and add all the tracks to the peerconnection"
    than to explain what a transceiver is.

I agree, if there is not a big technical issue we have to resolve or all
browser implementators decide it is just too difficult to implement and
support both models  I think we are too late in the process to make such
a big change.

I am all in for cleaner APIs, but then I would get rid of peerconnection
and transceivers altogether. Which by the way IMHO should be the scope
of next webrtc chapter.

Regards
Sergio

On 02/02/2018 10:17, Philipp Hancke wrote:
>

So we got rid of the legacy streams API without specifiyng them.
Philosophically that is bad but nobody volunteered to write the spec.
It is very clear for developers that addStream is the legacy API.

addTrack/removeTrack is confusing because it is supported both in the
"track-based" API (shipped in Firefox and recently Chrome) and the
transceiver model (in FF59). And they behave /very/ different in both
models which creates a lot of confusion.

I dislike addTransceiver because it is much easier to explain a web
developer

  • "you take your stream from getUserMedia and add the stream to the
    peerconnection" or
  • "you take your stream from getUserMedia and add all the tracks to
    the peerconnection"
    than to explain what a transceiver is.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/w3c/webrtc-pc/issues/1758#issuecomment-362530492,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABBW8yo8OKA44nVAc9WmAv-AKnf2o4g9ks5tQtKggaJpZM4R2DVK.

"you take your stream from getUserMedia and add a (send/receive) transceiver for each track."

Yes it's marginally harder than earlier abstractions, but then the earlier abstractions were wrong.

I think by far the hardest part of any of these mental models is how they differ and interact.

I feel the need to clarify again that this is only about marking the APIs as supplanted, not about removing any functionality or giving up on WebRTC 🙂

@fippo What do you recommend that we do here?

If possible, I'd prefer not to repeat the mistakes we made with the deprecation of the legacy streams API. For example, before marking addTrack/removeTrack as supplanted, don't we need to have more implementations of addTransceiver with test results showing consistent behavior?

Also, if we have evidence that developers are using addTrack/removeTrack, don't we have the obligation to clearly specify it?

While we might believe that addTransceiver will be better for developers, do we have enough data to indicate that it is strongly preferred on browsers that support it?

Short-term I'd suggest waiting until @henbos shipped transceivers in Chrome. The transceiver-transition in Firefox 59 went surprisingly smooth so I don't expect anything big to break even with addTrack. I hope he is writing tests in Firefox and running them in Chrome only when they are done :-)

On a larger scale lets look at numbers for the addTrack adoption... from Chrome which shipped replaceTrack in M64 end of january 2018:
https://www.chromestatus.com/metrics/feature/timeline/popularity/1641 -- shows addStream usage
https://www.chromestatus.com/metrics/feature/timeline/popularity/2254 -- shows addTrack usage
addStream is currently used on 0.04% of page loads, addTrack roughly 0.0025. That means addStream is used 16x as often as addTrack -- an adoption rate of 6.25%.
We see some adoption in February and March and then even more adoption in with M65 mid-march. Note that we decided to bump the native addTrack to M65 in adapter which I think we can see here.
What is odd about the addTrack numbers is that they show a very strong weekday/weekend cycle which is remarkably similar to the appear.in usage...

This suggests that developers don't care about addTrack much. This adoption rate is after shimming addTrack in adapter and updating https://github.com/webrtc/samples to use it. I don't think we can repeat this for addTransceiver which will have a slow adoption rate starting at 0.

Again, I'm not advocating we remove addTrack entirely, as it has some advantages when answering—It lets you add tracks ahead of an incoming offer in the trivial case, or in every case if you don't care about the number of m-lines produced. But it's behavior there is confusing, which can cause media to happen on new m-lines you weren't expecting.

In contrast, answering completely without addTrack is highly asymmetric, as I show in my blog if you scroll down a bit, but is also the only abstraction that doesn't leak.

I'm mostly concerned about users reading the spec, attempting to grok its model. We could start by deemphasizing use of addTrack in examples at least.

@jan-ivar do you want to redefine addTrack on top of addTransceiver and not change its behavior or do you want to change addTrack to simply invoke addTransceiver even if a sender could be reused?

Here's my attempt at shimming addTrack, do you want something like this but in language instead of code?

arguments: track, streams

const transceivers = pc.getTranceivers();
let transceiver = null;
for (let i = 0; i < transceivers.length; ++i) {
  if (transceivers[i].receiver.track.kind != track.kind)
    continue;
  if (transceivers[i].currentDirection has never had the value 'sendrecv' or 'sendonly') {
    transceiver = transceivers[i];
    break;
  }
}
if (transceiver == null) {
  transceiver = pc.addTransceiver(track, {streams:streams});
} else {
  await transceiver.sender.replaceTrack(track);
  transceiver.sender.setStreams(streams);
  if (transceiver.direction == 'recvonly')
    transceiver.direction = 'sendrecv';
  else if (transceiver.direction == 'inactive')
    transceiver.direction = 'sendonly';
}
return transceiver.sender;

A problem above is replaceTrack() is not synchronous, and performing "await replaceTrack" is observably different than "Set sender's [[SenderTrack]] to track."

Looking at the steps in the spec, it is only step 9. that can be replaced by "Call addTransceiver", and the fact that no equivalent of step 10. can be found for addTransceiver() and replaceTrack() is probably a bug.

Instead of redefining the steps, should we simply add a intro text to the method description saying "This is like addTransceiver, except that in such and such case the sender can be reused."?

@henbos I agree addTrack cannot be shimmed for the reason you mention, and its algorithm seems fine. Some well-placed notes around why addTrack still exists may be helpful.

I think we should deprecate removeTrack. That might be sufficient to break the spell.

I think (but have no data to confirm) removeTrack is not widely used. Besides, removeTrack(sender) was always awkward.

This issue seems to have an editorial component (adding an informative note to relate addTrack to addTransceiver), and a more subsantive one (deprecating removeTrack).

Given the history of the issue, I would suggest focus its resolution on the first bit, and leave it to @jan-ivar to raise a separate issue on deprecating removeTrack if that's still a reasonable thing to pursue.

I'm happy to see the examples have since been updated to use addTransceiver more, so I think this can be closed.

Was this page helpful?
0 / 5 - 0 ratings