diff --git a/README.FRONTEND b/README.FRONTEND index 0af679f..6503d7d 100644 --- a/README.FRONTEND +++ b/README.FRONTEND @@ -65,7 +65,7 @@ You typically join a group and request media in the `onconnected` callback: ```javascript serverConnection.onconnected = function() { this.join(group, 'join', username, password); - this.request('everything'); + this.request({'':['audio','video']}); } ``` @@ -110,10 +110,7 @@ serverConnection.ondownstream = function(stream) { } ``` -The `stream.labels` dictionary maps each track's id to one of `audio`, -`video` or `screenshare`. Since `stream.labels` is already available at -this point, you may set up an `audio` or `video` component straight away, -or you may choose to wait until the `ondowntrack` callback is called. +The `stream.label` field is one of `camera`, `screenshare` or `video`. After a new stream is created, `ondowntrack` will be called whenever a track is added. @@ -137,10 +134,10 @@ to push a stream to the server. Given a `MediaStream` called `localStream` ```javascript let stream = serverConnection.newUpStream(); +stream.label = ...; stream.onerror = ...; stream.onstatus = ...; localStream.getTracks().forEach(t => { - c.labels[t.id] = t.kind; c.pc.addTrack(t, c.stream); }); ``` @@ -150,8 +147,6 @@ the `localId` property of an existing stream, then the existing stream will be closed and the server will be informed that the new stream replaces the existing stream. -See above for information about setting up the `labels` dictionary. - ## Stream statistics Some statistics about streams are made available by calling the diff --git a/README.PROTOCOL b/README.PROTOCOL index d45ec11..5d05c44 100644 --- a/README.PROTOCOL +++ b/README.PROTOCOL @@ -135,7 +135,8 @@ A peer must explicitly request the streams that it wants to receive. ``` The field `request` is a dictionary that maps the labels of requested -tracks to the boolean `true`. +streams to a list containing either 'audio', 'video' or both. An entry +with an empty key `''` serves as default. ## Pushing streams @@ -145,11 +146,11 @@ A stream is created by the sender with the `offer` message: { type: 'offer', id: id, + label: label, replace: id, source: source-id, username: username, sdp: sdp, - labels: labels } ``` @@ -158,15 +159,14 @@ otherwise this message creates a new stream. If the field `replace` is not empty, then this request additionally requests that an existing stream with the given id should be closed, and the new stream should replace it. +The field `label` is one of `camera`, `screenshare` or `video`, as in the +`request` message. + The field `sdp` contains the raw SDP string (i.e. the `sdp` field of a JSEP session description). Galène will interpret the `nack`, `nack pli`, `ccm fir` and `goog-remb` RTCP feedback types, and act accordingly. -The field `labels` is a dictionary that maps track mids to one of `audio`, -`video` or `screenshare`. If a track does not appear in the `labels` -dictionary, it should be ignored. - The receiver may either abort the stream immediately (see below), or send an answer. diff --git a/conn/conn.go b/conn/conn.go index 322c663..1e7feda 100644 --- a/conn/conn.go +++ b/conn/conn.go @@ -16,6 +16,7 @@ type Up interface { AddLocal(Down) error DelLocal(Down) bool Id() string + Label() string User() (string, string) } @@ -23,7 +24,7 @@ type Up interface { type UpTrack interface { AddLocal(DownTrack) error DelLocal(DownTrack) bool - Label() string + Kind() webrtc.RTPCodecType Codec() webrtc.RTPCodecCapability // get a recent packet. Returns 0 if the packet is not in cache. GetRTP(seqno uint16, result []byte) uint16 diff --git a/rtpconn/rtpconn.go b/rtpconn/rtpconn.go index eec5d97..59fe835 100644 --- a/rtpconn/rtpconn.go +++ b/rtpconn/rtpconn.go @@ -313,6 +313,10 @@ func (up *rtpUpTrack) Label() string { return up.label } +func (up *rtpUpTrack) Kind() webrtc.RTPCodecType { + return up.track.Kind() +} + func (up *rtpUpTrack) Codec() webrtc.RTPCodecCapability { return up.track.Codec().RTPCodecCapability } @@ -328,10 +332,10 @@ func (up *rtpUpTrack) hasRtcpFb(tpe, parameter string) bool { type rtpUpConnection struct { id string + label string userId string username string pc *webrtc.PeerConnection - labels map[string]string iceCandidates []*webrtc.ICECandidateInit mu sync.Mutex @@ -363,6 +367,10 @@ func (up *rtpUpConnection) Id() string { return up.id } +func (up *rtpUpConnection) Label() string { + return up.label +} + func (up *rtpUpConnection) User() (string, string) { return up.userId, up.username } @@ -413,33 +421,6 @@ func (up *rtpUpConnection) flushICECandidates() error { return err } -func getTrackMid(pc *webrtc.PeerConnection, track *webrtc.TrackRemote) string { - for _, t := range pc.GetTransceivers() { - if t.Receiver() != nil && t.Receiver().Track() == track { - return t.Mid() - } - } - return "" -} - -// called locked -func (up *rtpUpConnection) complete() bool { - for mid := range up.labels { - found := false - for _, t := range up.tracks { - m := getTrackMid(up.pc, t.track) - if m == mid { - found = true - break - } - } - if !found { - return false - } - } - return true -} - // pushConnNow pushes a connection to all of the clients in a group func pushConnNow(up *rtpUpConnection, g *group.Group, cs []group.Client) { up.mu.Lock() @@ -460,17 +441,11 @@ func pushConnNow(up *rtpUpConnection, g *group.Group, cs []group.Client) { // pushConn schedules a call to pushConnNow func pushConn(up *rtpUpConnection, g *group.Group, cs []group.Client) { up.mu.Lock() - if up.complete() { - up.mu.Unlock() - pushConnNow(up, g, cs) - return - } - up.pushed = false up.mu.Unlock() go func(g *group.Group, cs []group.Client) { - time.Sleep(4 * time.Second) + time.Sleep(200 * time.Millisecond) up.mu.Lock() pushed := up.pushed up.pushed = true @@ -481,7 +456,7 @@ func pushConn(up *rtpUpConnection, g *group.Group, cs []group.Client) { }(g, cs) } -func newUpConn(c group.Client, id string, labels map[string]string, offer string) (*rtpUpConnection, error) { +func newUpConn(c group.Client, id string, label string, offer string) (*rtpUpConnection, error) { var o sdp.SessionDescription err := o.Unmarshal([]byte(offer)) if err != nil { @@ -506,31 +481,13 @@ func newUpConn(c group.Client, id string, labels map[string]string, offer string } } - up := &rtpUpConnection{id: id, pc: pc, labels: labels} + up := &rtpUpConnection{id: id, label: label, pc: pc} pc.OnTrack(func(remote *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { up.mu.Lock() - mid := getTrackMid(pc, remote) - if mid == "" { - log.Printf("Couldn't get track's mid") - return - } - - label, ok := up.labels[mid] - if !ok { - log.Printf("Couldn't get track's label") - isvideo := remote.Kind() == webrtc.RTPCodecTypeVideo - if isvideo { - label = "video" - } else { - label = "audio" - } - } - track := &rtpUpTrack{ track: remote, - label: label, cache: packetcache.New(minPacketCache(remote)), rate: estimator.New(time.Second), jitter: jitter.New(remote.Codec().ClockRate), diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index 43bdb5d..4e6595d 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -59,7 +59,7 @@ type webClient struct { password string permissions group.ClientPermissions status map[string]interface{} - requested map[string]uint32 + requested map[string][]string done chan struct{} writeCh chan interface{} writerDone chan struct{} @@ -122,53 +122,6 @@ func (c *webClient) PushClient(id, username string, permissions group.ClientPerm }) } -type rateMap map[string]uint32 - -func (v *rateMap) UnmarshalJSON(b []byte) error { - var m map[string]interface{} - - err := json.Unmarshal(b, &m) - if err != nil { - return err - } - - n := make(map[string]uint32, len(m)) - for k, w := range m { - switch w := w.(type) { - case bool: - if w { - n[k] = ^uint32(0) - } else { - n[k] = 0 - } - case float64: - if w < 0 || w >= float64(^uint32(0)) { - return errors.New("overflow") - } - n[k] = uint32(w) - default: - return errors.New("unexpected type in JSON map") - } - } - *v = n - return nil -} - -func (v rateMap) MarshalJSON() ([]byte, error) { - m := make(map[string]interface{}, len(v)) - for k, w := range v { - switch w { - case 0: - m[k] = false - case ^uint32(0): - m[k] = true - default: - m[k] = w - } - } - return json.Marshal(m) -} - type clientMessage struct { Type string `json:"type"` Kind string `json:"kind,omitempty"` @@ -187,8 +140,8 @@ type clientMessage struct { Time int64 `json:"time,omitempty"` SDP string `json:"sdp,omitempty"` Candidate *webrtc.ICECandidateInit `json:"candidate,omitempty"` - Labels map[string]string `json:"labels,omitempty"` - Request rateMap `json:"request,omitempty"` + Label string `json:"label,omitempty"` + Request map[string][]string `json:"request,omitempty"` RTCConfiguration *webrtc.Configuration `json:"rtcConfiguration,omitempty"` } @@ -216,7 +169,7 @@ func getUpConns(c *webClient) []*rtpUpConnection { return up } -func addUpConn(c *webClient, id string, labels map[string]string, offer string) (*rtpUpConnection, bool, error) { +func addUpConn(c *webClient, id, label string, offer string) (*rtpUpConnection, bool, error) { c.mu.Lock() defer c.mu.Unlock() @@ -232,7 +185,7 @@ func addUpConn(c *webClient, id string, labels map[string]string, offer string) return old, false, nil } - conn, err := newUpConn(c, id, labels, offer) + conn, err := newUpConn(c, id, label, offer) if err != nil { return nil, false, err } @@ -540,33 +493,16 @@ func negotiate(c *webClient, down *rtpDownConnection, restartIce bool, replace s return err } - labels := make(map[string]string) - for _, t := range down.pc.GetTransceivers() { - var track webrtc.TrackLocal - if t.Sender() != nil { - track = t.Sender().Track() - } - if track == nil { - continue - } - - for _, tr := range down.tracks { - if tr.track == track { - labels[t.Mid()] = tr.remote.Label() - } - } - } - source, username := down.remote.User() return c.write(clientMessage{ Type: "offer", Id: down.id, + Label: down.remote.Label(), Replace: replace, Source: source, Username: username, SDP: down.pc.LocalDescription().SDP, - Labels: labels, }) } @@ -582,8 +518,8 @@ func sendICE(c *webClient, id string, candidate *webrtc.ICECandidate) error { }) } -func gotOffer(c *webClient, id string, sdp string, labels map[string]string, replace string) error { - up, _, err := addUpConn(c, id, labels, sdp) +func gotOffer(c *webClient, id, label string, sdp string, replace string) error { + up, _, err := addUpConn(c, id, label, sdp) if err != nil { return err } @@ -681,7 +617,7 @@ func gotICE(c *webClient, candidate *webrtc.ICECandidateInit, id string) error { return conn.addICECandidate(candidate) } -func (c *webClient) setRequested(requested map[string]uint32) error { +func (c *webClient) setRequested(requested map[string][]string) error { if c.group == nil { return errors.New("attempted to request with no group joined") } @@ -701,8 +637,46 @@ func pushConns(c group.Client, g *group.Group) { } } -func (c *webClient) isRequested(label string) bool { - return c.requested[label] != 0 +func requestedTracks(c *webClient, up conn.Up, tracks []conn.UpTrack) []conn.UpTrack { + r, ok := c.requested[up.Label()] + if !ok { + r, ok = c.requested[""] + } + if !ok || len(r) == 0 { + return nil + } + + var audio, video bool + for _, s := range r { + switch s { + case "audio": + audio = true + case "video": + video = true + default: + log.Printf("client requested unknown value %v", s) + } + } + + var ts []conn.UpTrack + if audio { + for _, t := range tracks { + if t.Kind() == webrtc.RTPCodecTypeAudio { + ts = append(ts, t) + break + } + } + } + if video { + for _, t := range tracks { + if t.Kind() == webrtc.RTPCodecTypeVideo { + ts = append(ts, t) + break + } + } + } + + return ts } func (c *webClient) PushConn(g *group.Group, id string, up conn.Up, tracks []conn.UpTrack, replace string) error { @@ -871,16 +845,7 @@ func handleAction(c *webClient, a interface{}) error { } var tracks []conn.UpTrack if a.conn != nil { - tracks = make([]conn.UpTrack, - 0, len(a.tracks), - ) - for _, t := range a.tracks { - if c.isRequested(t.Label()) { - tracks = append( - tracks, t, - ) - } - } + tracks = requestedTracks(c, a.conn, a.tracks) } if len(tracks) == 0 { @@ -923,9 +888,6 @@ func handleAction(c *webClient, a interface{}) error { return nil } for _, u := range c.up { - if !u.complete() { - continue - } tracks := u.getTracks() replace := u.getReplace(false) @@ -1051,7 +1013,7 @@ func leaveGroup(c *webClient) { group.DelClient(c) c.permissions = group.ClientPermissions{} c.status = nil - c.requested = map[string]uint32{} + c.requested = make(map[string][]string) c.group = nil } @@ -1238,7 +1200,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { }) return c.error(group.UserError("not authorised")) } - err := gotOffer(c, m.Id, m.SDP, m.Labels, m.Replace) + err := gotOffer(c, m.Id, m.Label, m.SDP, m.Replace) if err != nil { log.Printf("gotOffer: %v", err) return failUpConnection(c, m.Id, "negotiation failed") diff --git a/static/galene.js b/static/galene.js index df4b642..9056e17 100644 --- a/static/galene.js +++ b/static/galene.js @@ -342,13 +342,7 @@ function gotDownStream(c) { setMedia(c, false); }; c.onnegotiationcompleted = function() { - let found = false; - for(let key in c.labels) { - if(c.labels[key] === 'video') - found = true; - } - if(!found) - resetMedia(c); + resetMedia(c); } c.onstatus = function(status) { setMediaStatus(c); @@ -384,7 +378,7 @@ getButtonElement('presentbutton').onclick = async function(e) { // button a second time before the stream is set up and the button hidden. button.disabled = true; try { - let id = findUpMedia('local'); + let id = findUpMedia('camera'); if(!id) await addLocalMedia(); } finally { @@ -394,12 +388,12 @@ getButtonElement('presentbutton').onclick = async function(e) { getButtonElement('unpresentbutton').onclick = function(e) { e.preventDefault(); - closeUpMediaKind('local'); + closeUpMedia('camera'); resizePeers(); }; function changePresentation() { - let c = findUpMedia('local'); + let c = findUpMedia('camera'); if(c) addLocalMedia(c.localId); } @@ -419,7 +413,7 @@ function setVisibility(id, visible) { function setButtonsVisibility() { let connected = serverConnection && serverConnection.socket; let permissions = serverConnection.permissions; - let local = !!findUpMedia('local'); + let local = !!findUpMedia('camera'); let video = !!findUpMedia('video'); let canWebrtc = !(typeof RTCPeerConnection === 'undefined'); let canFile = @@ -516,7 +510,7 @@ document.getElementById('sharebutton').onclick = function(e) { document.getElementById('stopvideobutton').onclick = function(e) { e.preventDefault(); - closeUpMediaKind('video'); + closeUpMedia('video'); resizePeers(); }; @@ -556,12 +550,37 @@ getSelectElement('sendselect').onchange = async function(e) { } }; +/** + * @param {string} what + * @returns {Object>} + */ + +function mapRequest(what) { + switch(what) { + case '': + return {}; + break; + case 'audio': + return {'': ['audio']}; + break; + case 'screenshare': + return {screenshare: ['audio','video'], '': ['audio']}; + break; + case 'everything': + return {'': ['audio','video']} + break; + default: + displayError(`Unknown value ${what} in request`); + return {}; + } +} + getSelectElement('requestselect').onchange = function(e) { e.preventDefault(); if(!(this instanceof HTMLSelectElement)) throw new Error('Unexpected type for this'); updateSettings({request: this.value}); - serverConnection.request(this.value); + serverConnection.request(mapRequest(this.value)); }; const activityDetectionInterval = 200; @@ -1069,8 +1088,8 @@ async function addLocalMedia(localId) { return; } - c.kind = 'local'; c.stream = stream; + c.label = 'camera'; if(filter) { try { @@ -1100,7 +1119,6 @@ async function addLocalMedia(localId) { let mute = getSettings().localMute; c.stream.getTracks().forEach(t => { - c.labels[t.id] = t.kind; if(t.kind == 'audio') { if(mute) t.enabled = false; @@ -1143,8 +1161,8 @@ async function addShareMedia() { } let c = newUpStream(); - c.kind = 'screenshare'; c.stream = stream; + c.label = 'screenshare'; c.onclose = replace => { stopStream(stream); if(!replace) @@ -1153,7 +1171,6 @@ async function addShareMedia() { stream.getTracks().forEach(t => { c.pc.addTrack(t, stream); t.onended = e => c.close(); - c.labels[t.id] = 'screenshare'; }); c.onstats = gotUpStats; c.setStatsInterval(2000); @@ -1184,8 +1201,8 @@ async function addFileMedia(file) { } let c = newUpStream(); - c.kind = 'video'; c.stream = stream; + c.label = 'video'; c.onclose = function(replace) { stopStream(c.stream); let media = /** @type{HTMLVideoElement} */ @@ -1201,7 +1218,7 @@ async function addFileMedia(file) { stream.onaddtrack = function(e) { let t = e.track; if(t.kind === 'audio') { - let presenting = !!findUpMedia('local'); + let presenting = !!findUpMedia('camera'); let muted = getSettings().localMute; if(presenting && !muted) { setLocalMute(true, true); @@ -1209,13 +1226,11 @@ async function addFileMedia(file) { } } c.pc.addTrack(t, stream); - c.labels[t.id] = t.kind; c.onstats = gotUpStats; c.setStatsInterval(2000); }; stream.onremovetrack = function(e) { let t = e.track; - delete(c.labels[t.id]); /** @type {RTCRtpSender} */ let sender; @@ -1229,7 +1244,12 @@ async function addFileMedia(file) { console.warn('Removing unknown track'); } - if(Object.keys(c.labels).length === 0) { + let found = false; + c.pc.getSenders().forEach(s => { + if(s.track) + found = true; + }); + if(!found) { stream.onaddtrack = null; stream.onremovetrack = null; c.close(); @@ -1254,28 +1274,28 @@ function stopStream(s) { } /** - * closeUpMediaKind closes all up connections that correspond to a given - * kind of media. If kind is null, it closes all up connections. + * closeUpMedia closes all up connections with the given label. If label + * is null, it closes all up connections. * - * @param {string} kind + * @param {string} label */ -function closeUpMediaKind(kind) { +function closeUpMedia(label) { for(let id in serverConnection.up) { let c = serverConnection.up[id]; - if(kind && c.kind != kind) + if(label && c.label !== label) continue c.close(); } } /** - * @param {string} kind + * @param {string} label * @returns {Stream} */ -function findUpMedia(kind) { +function findUpMedia(label) { for(let id in serverConnection.up) { let c = serverConnection.up[id] - if(c.kind === kind) + if(c.label === label) return c; } return null; @@ -1289,7 +1309,7 @@ function muteLocalTracks(mute) { return; for(let id in serverConnection.up) { let c = serverConnection.up[id]; - if(c.kind === 'local') { + if(c.label === 'camera') { let stream = c.stream; stream.getTracks().forEach(t => { if(t.kind === 'audio') { @@ -1370,7 +1390,7 @@ async function setMedia(c, isUp, mirror, video) { showVideo(); resizePeers(); - if(!isUp && isSafari() && !findUpMedia('local')) { + if(!isUp && isSafari() && !findUpMedia('camera')) { // Safari doesn't allow autoplay unless the user has granted media access try { let stream = await navigator.mediaDevices.getUserMedia({audio: true}); @@ -1429,10 +1449,10 @@ function addCustomControls(media, container, c) { let volume = getVideoButton(controls, 'volume'); let stopsharing = getVideoButton(topcontrols, 'video-stop'); - if (c.kind !== "screenshare") { + if (c.label !== "screenshare") { stopsharing.remove(); } - if(c.kind === 'local') { + if(c.label === 'camera') { volume.remove(); } else { setVolumeButton(media.muted, @@ -1820,9 +1840,9 @@ async function gotJoined(kind, group, perms, message) { if(typeof RTCPeerConnection === 'undefined') displayWarning("This browser doesn't support WebRTC"); else - this.request(getSettings().request); + this.request(mapRequest(getSettings().request)); - if(serverConnection.permissions.present && !findUpMedia('local')) { + if(serverConnection.permissions.present && !findUpMedia('camera')) { if(present) { if(present === 'mike') updateSettings({video: ''}); diff --git a/static/protocol.js b/static/protocol.js index a78f6b3..9bb60ad 100644 --- a/static/protocol.js +++ b/static/protocol.js @@ -212,8 +212,8 @@ function ServerConnection() { * @property {boolean} [noecho] * @property {string} [sdp] * @property {RTCIceCandidate} [candidate] - * @property {Object} [labels] - * @property {Object} [request] + * @property {string} [label] + * @property {Object>} [request] * @property {Object} [rtcConfiguration] */ @@ -296,7 +296,7 @@ ServerConnection.prototype.connect = async function(url) { case 'handshake': break; case 'offer': - sc.gotOffer(m.id, m.labels, m.source, m.username, + sc.gotOffer(m.id, m.label, m.source, m.username, m.sdp, m.replace); break; case 'answer': @@ -439,32 +439,14 @@ ServerConnection.prototype.leave = function(group) { /** * request sets the list of requested media types. * - * @param {string} what - One of '', 'audio', 'screenshare' or 'everything'. + * @param {Object>} what + * - A dictionary that maps labels to a sequence of 'audio' and 'video'. + * An entry with an empty label '' provides the default. */ ServerConnection.prototype.request = function(what) { - /** @type {Object} */ - let request = {}; - switch(what) { - case '': - request = {}; - break; - case 'audio': - request = {audio: true}; - break; - case 'screenshare': - request = {audio: true, screenshare: true}; - break; - case 'everything': - request = {audio: true, screenshare: true, video: true}; - break; - default: - console.error(`Unknown value ${what} in request`); - break; - } - this.send({ type: 'request', - request: request, + request: what, }); }; @@ -622,14 +604,14 @@ ServerConnection.prototype.groupAction = function(kind, message) { * Called when we receive an offer from the server. Don't call this. * * @param {string} id - * @param {Object} labels + * @param {string} label * @param {string} source * @param {string} username * @param {string} sdp * @param {string} replace * @function */ -ServerConnection.prototype.gotOffer = async function(id, labels, source, username, sdp, replace) { +ServerConnection.prototype.gotOffer = async function(id, label, source, username, sdp, replace) { let sc = this; if(sc.up[id]) { @@ -669,6 +651,7 @@ ServerConnection.prototype.gotOffer = async function(id, labels, source, usernam return; } c = new Stream(this, id, oldLocalId || newLocalId(), pc, false); + c.label = label; sc.down[id] = c; c.pc.onicecandidate = function(e) { @@ -689,32 +672,15 @@ ServerConnection.prototype.gotOffer = async function(id, labels, source, usernam }; c.pc.ontrack = function(e) { - let label = e.transceiver && c.labelsByMid[e.transceiver.mid]; - if(label) { - c.labels[e.track.id] = label; - } else { - console.warn("Couldn't find label for track"); - } c.stream = e.streams[0]; if(c.ondowntrack) { c.ondowntrack.call( - c, e.track, e.transceiver, label, e.streams[0], + c, e.track, e.transceiver, e.streams[0], ); } }; } - c.labelsByMid = labels; - c.labels = {}; - c.pc.getTransceivers().forEach(transceiver => { - let label = c.labelsByMid[transceiver.mid]; - let track = transceiver.receiver && transceiver.receiver.track; - if(label && track) { - c.labels[track.id] = label; - } else if(!track) { - console.warn("Couldn't find track for label"); - } - }); c.source = source; c.username = username; @@ -883,12 +849,6 @@ function Stream(sc, id, localId, pc, up) { * @const */ this.up = up; - /** - * For up streams, one of "local" or "screenshare". - * - * @type {string} - */ - this.kind = null; /** * For down streams, the id of the client that created the stream. * @@ -916,17 +876,11 @@ function Stream(sc, id, localId, pc, up) { */ this.stream = null; /** - * Track labels, indexed by track id. + * The label assigned by the originator to this stream. * - * @type {Object} + * @type {string} */ - this.labels = {}; - /** - * Track labels, indexed by mid. - * - * @type {Object} - */ - this.labelsByMid = {}; + this.label = null; /** * The id of the stream that we are currently replacing. * @@ -1004,7 +958,7 @@ function Stream(sc, id, localId, pc, up) { * If the stream parameter differs from its previous value, then it * indicates that the old stream has been discarded. * - * @type{(this: Stream, track: MediaStreamTrack, transceiver: RTCRtpTransceiver, label: string, stream: MediaStream) => void} + * @type{(this: Stream, track: MediaStreamTrack, transceiver: RTCRtpTransceiver, stream: MediaStream) => void} */ this.ondowntrack = null; /** @@ -1164,17 +1118,6 @@ Stream.prototype.negotiate = async function (restartIce) { throw(new Error("Didn't create offer")); await c.pc.setLocalDescription(offer); - // mids are not known until this point - c.pc.getTransceivers().forEach(t => { - if(t.sender && t.sender.track) { - let label = c.labels[t.sender.track.id]; - if(label) - c.labelsByMid[t.mid] = label; - else - console.warn("Couldn't find label for track"); - } - }); - c.sc.send({ type: 'offer', source: c.sc.id, @@ -1182,7 +1125,7 @@ Stream.prototype.negotiate = async function (restartIce) { kind: this.localDescriptionSent ? 'renegotiate' : '', id: c.id, replace: this.replace, - labels: c.labelsByMid, + label: c.label, sdp: c.pc.localDescription.sdp, }); this.localDescriptionSent = true;