server-sdk-go icon indicating copy to clipboard operation
server-sdk-go copied to clipboard

room.LocalParticipant.UnpublishTrack(sid) does not completely unpublish a simulcast track

Open neilyoung opened this issue 2 years ago • 5 comments

Details here. There is a significant behavioural difference, if just one track is unpublished or a simulcast track. On the other hand I haven't found any other unpublish function for simulcast

https://livekit-users.slack.com/archives/C01KVTJH6BX/p1706698776125179

neilyoung avatar Jan 31 '24 17:01 neilyoung

Hack which "fixes" it here:

https://livekit-users.slack.com/archives/C01KVTJH6BX/p1706782394301419?thread_ts=1706698776.125179&cid=C01KVTJH6BX

neilyoung avatar Feb 01 '24 10:02 neilyoung

Some comments? Solution is available in Slack

neilyoung avatar Feb 05 '24 22:02 neilyoung

this is on our backlog. However, if you'd like to submit a PR for it, we'd welcome it.

davidzhao avatar Feb 06 '24 06:02 davidzhao

Neither my GO nor my deeper understanding of the purpose of the failing code allows me to do that :)

I just know, that this patch works for both, but I guess, it's. not good enough:

	var err error
	//if localTrack, ok := pub.track.(webrtc.TrackLocal); ok {
		for _, sender := range p.engine.publisher.pc.GetSenders() {
			//if sender.Track() == localTrack {
				err = p.engine.publisher.pc.RemoveTrack(sender)
				//break
			//}
		}
		p.engine.publisher.Negotiate()
	//}

Also - VSCode's debugging support is not sufficient to have a full understanding for what happens here, I'm sorry

neilyoung avatar Feb 06 '24 08:02 neilyoung

  1. The root cause of the issue is that when "LocalParticipant.PublishSimulcastTrack.NewLocalTrackPublication" is called, the main track is not passed, resulting in "UnpublishTrack" not successfully invoking "p.engine.publisher.pc.RemoveTrack". pub := NewLocalTrackPublication(KindFromRTPType(mainTrack.Kind()), nil, *opts, p.engine.client)

  2. The minimal change solution is as follows:

        // [origin code in v1.1.2]
	var err error
	if localTrack, ok := pub.track.(webrtc.TrackLocal); ok {
		for _, sender := range p.engine.publisher.pc.GetSenders() {
			if sender.Track() == localTrack {
				err = p.engine.publisher.pc.RemoveTrack(sender)
				break
			}
		}
		p.engine.publisher.Negotiate()
	}



        // [fixed code in v1.1.2]
	var localTracks []webrtc.TrackLocal
	if localTrack, ok := pub.track.(webrtc.TrackLocal); ok {
		localTracks = append(localTracks, localTrack)
	}
	if pub.simulcastTracks != nil {
		for _, track := range pub.simulcastTracks {
			localTracks = append(localTracks, track)
		}
	}

	var err error
	if localTracks != nil && len(localTracks) > 0 {
		for _, localTrack := range localTracks {
			for _, sender := range p.engine.publisher.pc.GetSenders() {
				if sender.Track() == localTrack {
					e := p.engine.publisher.pc.RemoveTrack(sender)
					if e != nil {
						err = e
					}
					break
				}
			}
		}
		p.engine.publisher.Negotiate()
	}
  1. I found that when publishing with simulcast tracks, the trackPublicationBase.track was not set. This not only affects the UnpublishTrack function but may also impact the following logic. However, I have not deeply understood it, so I have not made any changes.
func (r *Room) sendSyncState() {
        ...

	var publishedTracks []*livekit.TrackPublishedResponse
	for _, t := range r.LocalParticipant.Tracks() {
		if t.Track() != nil {
			publishedTracks = append(publishedTracks, &livekit.TrackPublishedResponse{
				Cid:   t.Track().ID(),
				Track: t.TrackInfo(),
			})
		}
	}

        ...
}

patstar123 avatar Apr 25 '24 16:04 patstar123