-
-
Notifications
You must be signed in to change notification settings - Fork 252
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
Support for specifying STUN server: configure client (2.5/3) #1657
Conversation
@mtlynch I don’t have a fully-fledged test setup for STUN, so I’m not sure how to ultimately verify this PR on my end. I was successfully able to trace down the ICE-Server/STUN configuration in the Janus client, but I could only see that the STUN config gets passed on correctly inside the Janus client, however I was not able to verify that it actually has the desired effect. It neither says something in the logs (not even in verbose/debug mode), nor does it reveal anything in the Network tab of the browser console. Would it make sense to loop in Charles here, and ask him to perform an end-to-end test of the STUN feature in its entirety? (I could set up a test bundle that contains all the things, including the yet-to-be-merged UI.) As an alternative, I could try to reproduce the test setup, but I’m not sure how long that takes. |
Yeah, that sounds like the best path here. |
Automated comment from CodeApprove ➜⏳ @jdeanwallace please review this Pull Request |
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.
Automated comment from CodeApprove ➜
⏳ Approval Pending (3 unresolved comments)
Approval will be granted automatically when all comments are resolved
In: app/static/js/webrtc-video.js:
> Line 208
const server = stunServer.includes(":") ? `[${stunServer}]` : stunServer;
Is the ipv6 stuff necessary? From what I can see, ipv6 is disabled by default:
https://github.com/meetecho/janus-gateway/blob/v1.0.1/conf/janus.jcfg.sample.in#L214
In: app/templates/index.html:
> Line 92
{% if janus_stun_server and janus_stun_port %}
Could we immediately initialize an ICE Servers variable? It doesn't seem like we need the separate server and port variables. Something like:
window.JANUS_ICE_SERVERS = [{urls: `stun:${stunSterver}:${stunPort}`}];
In: app/templates/index.html:
> Line 102
</script>
An alternative to the eslint issue can be something like this:
<script id="janus-stun-server" type="application/json">{{ janus_stun_server|tojson|safe }}</script>
<script id="janus-stun-port" type="application/json">{{ janus_stun_port|tojson|safe }}</script>
<script>
window.TINYPILOT_JANUS_STUN_SERVER = JSON.parse(document.getElementById("janus-stun-server").textContent);
window.TINYPILOT_JANUS_STUN_PORT = JSON.parse(document.getElementById("janus-stun-port").textContent);
</script>
👀 @jotaen4tinypilot it's your turn please take a look
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.
Automated comment from CodeApprove ➜
In: app/static/js/webrtc-video.js:
> Line 208
const server = stunServer.includes(":") ? `[${stunServer}]` : stunServer;
As far as I understand, Janus provides out-of-the-box support for IPv6, and the media.ipv6
option is for explicitly specifying one IP mode, instead of having Janus selecting that on its own, e.g. when gathering DNS candidates. To me, the config file documentation isn’t super clear here, unfortunately. The CLI docs seem a bit clearer on the semantics here (the --ipv6-candidates
CLI flag is the equivalent of the media.ipv6
file preference).
For example, even without media.ipv6
or --ipv6-candidates
, Janus still accepts and processes an IPv6 STUN address:
A few notes on that screenshot:
2607:f8b0:4023:1401::7f
isstun.l.google.com
- Janus doesn’t bother to format the IPv6 address correctly in the logs (by wrapping the server part in square brackets). The Janus frontend library would yield an error if we passed
2607:f8b0:4023:1401::7f:19302
instead of[2607:f8b0:4023:1401::7f]:19302
. IPv6 support is disabled
would beIPv6 support is enabled
ifmedia.ipv6
or--ipv6-candidates
was set. The STUN server connection seems to work regardless, either way.
In regards to IPv4/IPv6 in general, my thinking was to consider and provide IPv6 support for the STUN feature from the start, seeing that we don’t need much extra effort as far as Janus is concerned. E.g., the backend implementation also supports IPv6 all the way.
In: app/templates/index.html:
> Line 92
{% if janus_stun_server and janus_stun_port %}
My thinking was that the assembly of that ICE-server value was logic that’s closely related to Janus and our WebRTC initialization procedure in webrtc-video.js
, so despite the higher LOC cost, I’d think it makes for slightly better encapsulation. (Think: setting the global variables is like specifying method arguments on the call-side, and the implementation details are hidden inside the module of the method body.)
In: app/templates/index.html:
> Line 102
</script>
That’s an interesting approach as well – although, I feel that it probably also wouldn’t spare us a comment to explain why we take a detour through JSON-land, instead of assigning the values directly. I don’t feel strongly about either approach, so unless you do, I’d just keep the current code.
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.
Automated comment from CodeApprove ➜
Approved: I have approved this change on CodeApprove and all of my comments have been resolved.
Resolves #1460. Stacked on #1646, blocked on #1657 and tiny-pilot/tinypilotkvm.com#1053. This PR adds the UI for specifying a STUN server, and eventually makes the feature available to the end-user. → [Latest bundle off this branch](https://output.circle-artifacts.com/output/job/f2379490-a459-43d5-b1fb-ce009c971fcd/artifacts/0/bundler/dist/tinypilot-community-20231005T1841Z-1.9.1-27+3ff1062.tgz), for testing the entire PR stack. ## Demo https://github.com/tiny-pilot/tinypilot/assets/83721279/8108dcf2-1fe4-42d1-8c2a-4066cc669ad0 ## Notes - In contrast to the mockups in the ticket (e.g. [the latest one](#1460 (comment))), the proposed implementation here has a separate field for the host part and the port part of the STUN address. I realized that this simplifies a lot of things, because we don’t need to parse and split a unified address string (e.g. `stun.example.org:3478`), or serialize it again correctly. That would be especially tricky, since Janus supports IPv6, where the serialized format would look like e.g. `[12:c1:1832::c1:2]:3478`, so we can’t just naively split and join at the `:` character. - I separated the STUN input part into its own component, otherwise the `<video-settings-dialog>` would have become pretty large. - I debated whether to split off the entire “Advanced Settings” section, or just the STUN input. I eventually settled with the latter, because I felt it was conceptually more fitting, and I thought it would be the more granular approach, e.g. should we add more controls to the advanced settings in the future. I’m not married to it, though – I also think the entire `<video-settings-dialog>` component is borderline big anyway, so should we ever expand it further, it would probably be worth to consider a refactoring. - I had to [refactor (reverse) the CSS logic for showing/hiding the `.setting-...` classes](https://github.com/tiny-pilot/tinypilot/blob/e305ca104b54e485ee4e32876f1f8d181103b810/app/templates/custom-elements/video-settings-dialog.html#L30-L38). With the current implementation, it only allowed for `display: flex`, but with the new code, we also need `display: block` (on `.advanced-settings`). - Note that the `#stun-validation-error` is still part of `<video-settings-dialog>`, since validation errors are supposed to be “dialog-wide” errors, displayed at the bottom of the dialog, near the call-to-action button. If there were more inline / validation errors in the future, we’d also add them there, at top level. - One note about terminology: in the Janus config, the two address components are called [`server` and `port`](https://github.com/tiny-pilot/tinypilot/blob/0a03d2caf54aa083ef66c1606e28321f41aca781/debian-pkg/usr/share/tinypilot/templates/janus.jcfg.j2#L24-L25). I took over that terminology in the entire code, but in the UI I labelled the `server` fragment “Host”. To me, that’s technically more accurate, and since we already say “STUN Server” next to the dropdown button already, I thought “Server” might be confusing. I’m not sure there is a “right” answer here. We could otherwise also call it “Address”, but I feel that’s even broader. - The UI implementation has a few edge-cases with slightly unexpected/quirky behavior. We’d need to pay with more code and complexity to sort them out, though, and I felt that wouldn’t be worth it. Examples: - If you select “Custom” in the dropdown and then manually fill in the Google server details (`stun.l.google.com` + `19302`), then it would display the “Google” option the next time you open the dialog, and not the custom input fields. That’s because both ways look the same internally, and we’d need to maintain extra state to distinguish whether this was a custom input or one of the predefined selections. - If you select “Custom” and enter an invalid or incomplete address, and then choose “MJPEG” as streaming mode again, and then hit the “Apply” button, it still displays the STUN validation error. The reason is that the entire code around processing video settings is designed to always send and apply **all** values, regardless of what mode you eventually chose. <a data-ca-tag href="https://codeapprove.com/pr/tiny-pilot/tinypilot/1647"><img src="https://codeapprove.com/external/github-tag-allbg.png" alt="Review on CodeApprove" /></a>
Related #1460.
Now that the backend is able to re-write the Janus configuration to make Janus connect to a STUN server, this PR adds the client counterpart. That means when the user enabled STUN, the frontend’s WebRTC library will also be configured to use the same STUN server.
This PR complements #1647, and strictly speaking, it even should have preceded it.
Note that the PR review is only about the code itself, but it does not include an end-to-end test of the full STUN functionality on device. Charles thankfully did that separately. (To sum that up: the testing procedure turned out to be rather tricky, unfortunately… We still aren’t 100% confident that our STUN implementation reliably solves connection issues in remote access scenarios, but given the testing complexity, we decided that the status quo has yielded enough evidence for us to move forward.)
Some notes on the code:
createIceServerUrls
to account for IPv6 addresses.webrtc-video.js
is of type “module”, so it’s a bit tricky to pass parameters to it from the parent script. I think using global variables is not super beautiful, but it gets the job done. I’m open for better ideas, but the current mechanism would also be “good enough” for me.TINYPILOT_JANUS_STUN_PORT
assignment, where we have to apply a rather ugly workaround. As far as I can see, there is not much we can do about that.