-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Public APIs for Media Renegotiation #1132
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 613ce32 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
expect(stats.outboundRTP).toHaveProperty('video') | ||
expect(stats.inboundRTP).toHaveProperty('video') | ||
|
||
await page.waitForTimeout(2000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this required? The expectMCUVisible
will wait by itself since it's using the Playwright's waitForSelector
API, we shouldn't need the waitForTimeout
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's weird... but it's
expect(stats.inboundRTP).toHaveProperty('video') | ||
|
||
await page.waitForTimeout(1000) | ||
expectMCUVisibleForAudience(page) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You meant expectMCUVisible
?
expectMCUVisibleForAudience(page) | |
expectMCUVisible(page) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, in this test, we do not expect the local overlay to be visible, but we expect the remote video to be visible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why? The local overlay should be visible unless the user passes the applyLocalOverlay flag set to false, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the local overlay is only created when you have a video track in the localStream. In this test case, we are recvOnly
. There is no video track and no local overlay.
Have you tested this with the Video SDK? Asking because the code that has been changed is a part of the both Video and CF SDK. Can you please integrate the API in the playgrounds so that we can reproduce and see the implementation in action? |
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
I added the feature to the playground, also the Video SDK e2e are passing. |
Thanks for the playground update. The Video SDK e2e would pass because it's a new feature. We might want to add a new e2e test for it as well, or we can decide to move functions to the CallFabricRoomSession class to ensure they are only exposed to the CF SDK for now. |
A few observations I noticed while testing the feature in the playground; While joining a room with two members I noticed that ;
{
"code": "ICE_GATHERING_FAILED",
"message": "Ice gathering timeout"
} Lastly, which is more important, in my opinion, is the usage of the The method named You can clearly see the difference in your code if you simply comment down one line and add one new line; async renegotiateMedia(params: UpdateMediaOptions): Promise<void> {
this.updateMediaOptions(params)
// await this.peer?.start({ isRenegotiate: true })
await this.updateConstraints(params) // new line
} |
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
Yes, I meant it's not causing a regression in the Video SDK. The product requested the feature to be available on Both SDK. |
I couldn't reproduce the issue you reported about I've noticed the ICE_GATHERING_FAILED issue, too, and I'm investigating, but it doesn't seem to be a client/SDK issue. Regarding the suggestion of |
I'm trying some changes to use the updateConstraints |
@iAmmar7 Tests are green with the changes required to use the Also, disableVideo seems to be working now. |
expect(stats.inboundRTP).toHaveProperty('video') | ||
|
||
await page.waitForTimeout(1000) | ||
expectMCUVisibleForAudience(page) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why? The local overlay should be visible unless the user passes the applyLocalOverlay flag set to false, no?
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
Co-authored-by: Ammar Ansari <iammaransari@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have also shared a video regarding my findings for the sendOnly
flag with you.
@@ -515,6 +507,10 @@ export class BaseConnection<EventTypes extends EventEmitter.ValidEventTypes> | |||
} | |||
|
|||
await this.updateStream(newStream) | |||
if((!this.options.video && this.options.negotiateVideo) || (!this.options.audio && this.options.negotiateAudio)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In which scenario does this require renegotiation? When a user no longer wishes to receive the incoming video/audio but keeps on sending his video/audio?
Regardless of the reason, do you think calling the peer.start
is okay here? The peer.start
method is meant to be used when starting the peer connection, it sets up the RTC peer connection, adds the transceiver, and starts the negotiation. I don't think we need all these things, we only want to update the current RTC peer with the new device direction, right? Perhaps we can go with a new function that only triggers re-negotiation with updated params without affecting or creating any other peer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, This is a case where the existing RTCPeerConnection fires the needed negotiation event.
When we are "removing" one or both media channels we need create a new RTCPeerConnection.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we are "removing" one or both media channels we need create a new RTCPeerConnection.
Umm.. .why? We can just update the current RTCPeer, can't we? As we are doing already in the case of attaching audio/video via the updateConstraints
method.
For eg; if we join the call without video and do not negotiate it, and then later call the updateConstraints
method with the video params, it will update the currently running RTC peer and send the verto.modify
as well for renegotiation. Will that be helpful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case RTCPeerConnection doesn't fire negotiationneeded
event.
You can test it by commenting out this part. Also, if you replace start()
with startNegotiation()
, you will see that the result is not what we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RTC peer automatically emits the negotiationneeded event, every time the track is being added via the addTrack
method or removed via the removeTrack
method.
https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/removeTrack
The observation you might have seen is because, the updateStream
is calling the addTrack
when you add the new track but it is not calling the removeTrack
when you remove it. We might need to see where should we call this removeTrack method, we can't just call the peer.start
method. It is not meant for renegotiation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There can be many reasons;
- Separation of concerns,
- Possibility of double renegotiation (remember that the
updateStream
triggers the renegotiation if require), - Possibility of unnecessary renegotiation (if video changes but the negotiateVideo flag is still the same), it is possible that both the
updateStream
and theupdateConstraints
due to thisstart
method begin the renegotiation, - Unnecessarily restarting the peer connection may cause media interruptions plus resource usage.
- Also, we can analyze each step of the start method and can clearly observe that many things that the start method is doing are not required for the renegotiation.
I shared with you the 4 requirements of this PR, if you could please share with me which one of those requirements is failing, I would be able to debug and might be able to find a different solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the test that will fail: https://github.com/signalwire/signalwire-js/pull/1132/files#diff-e1d7afe85b7f7137519ee6170abc4c8cbbd5d3af7e2852666fd8b6f36f3ac103R113
Another option requires more changes on the startSegotiation
... Since the code already considers the double negotiation IMO, calling start()
is cleaner. But I'll make the change in startNegotiation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The four requirements we discussed were:
- The user should be able to send and receive his video -> sendrecv
- The user should be able to only send his video -> sendonly
- The user should be able to stop his outgoing video -> recvonly
- The user should be able to stop both incoming and outgoing video -> remove the video track from the peer.
Can you please specify which one of the above requirements falls into this failing test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested changes to startNegotiation
But even with the correct SDPs, the test fails(we don't receive video)
The only way to do this is to create a new RTPPeerConnection.
The test scenario is that the user connects with audio-only
but wants to start receiving video, too.
enableVideo({video: false})
or enableVideo({video: false, sendOnly:false})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a new requirement that does not fall under any of the above requirements mentioned. I think I tried to convey this before as well; before starting to implement the solution, we should first clearly write the whole problem. Also, the enableVideo
with false video param does not make any sense.
In the meantime, I will try to see how can we solve this recvOnly
problem.
Description
Add Public APIs for Media Renegotiation, allowing a developer to change the media options and execute a renegotiation.
Type of change
Code snippets
In case of new feature or breaking changes, please include code snippets.