Skip to content
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

Windows support #53

Open
darkuranium opened this issue Nov 3, 2023 · 5 comments
Open

Windows support #53

darkuranium opened this issue Nov 3, 2023 · 5 comments

Comments

@darkuranium
Copy link

darkuranium commented Nov 3, 2023

Hi. I was wondering if there is any interest in adding Windows support.
I know libpeer is primarily intended for embedded devices (ESP32, rPi, etc), but I do believe it has potential for other uses.

My own use-case is to use DataChannels for video game networking. (and perhaps at some point: audio for voice chat)

I'm more than willing to contribute towards such an implementation, though I reckon I'll need your help troubleshooting.
In fact, I already worked on a port myself, and I believe I have a mostly-working version, though I get the following error:

ERROR <...>/libpeer/src/dtls_srtp.c 381 failed! mbedtls_ssl_handshake returned -0x7280

(that error code corresponds to MBEDTLS_ERR_SSL_CONN_EOF)

Update: Above issue was in Waterfox (& confirmed with Firefox), but Chrome seems to work — though only after a long delay (connection finally succeeds ~40-50 seconds after page load).

I suspect the difference between WF/FF & Chrome is one between aggressive nomination vs regular (which is the same issue affecting Firefox's non-compliance with ICE-lite, though as far as I can tell, libpeer doesn't advertise itself as ICE-lite?).
Firefox, in particular, appears to send a ton of messages with USE_CANDIDATE attribute set, which might be an indicator to help out with the problem (at least according to versatica/mediasoup#650).

Quick inspection reveals Firefox to send 30 messages with such an attribute, whereas Chrome sends none.


The good news, however, is that Chrome does work — with libpeer running in Windows! So my port was successful after all. I haven't yet tested it, but I've a sneaking suspicion Firefox would fail to work even with libpeer running on a POSIX system.

@sepfy
Copy link
Owner

sepfy commented Nov 5, 2023

good job! i never try on the windows. if the windows supports BSD sockets API, i think we can port to this.
about firefox, you are right. libpeer cannot work with firefox now. In my experience, there is some error in STUN packet. But i don't have time to check it. If I have time I will look into this issue

@darkuranium
Copy link
Author

darkuranium commented Nov 5, 2023

Windows does in fact use Berkley sockets (from actual Berkley/BSD, in fact — obviously probably modified by Microsoft at various points). They are only a few (trivial) differences:

  • Instead of <sys/*.h> & friends, the includes are <winsock2.h> and <ws2tcpip.h>; likewise, one has to link with the WS2_32 library.
  • The socket API itself has to be initialized with WSAStartup(), and deinitialized with WSACleanup(). Fortunately, this can be done in peer_{init,deinit} (or simply by the user, but there is no harm in calling WSAStartup & WSACleanup multiple times, as it's internally reference-counted).
  • There is no close(), but instead a closesocket(). Same exact use & signature, just a different name (since Windows sockets are really handles, and not FDs).
  • For some reason, send(), recv(), and friends use [const] char* arguments instead of [const] void*, necessitating casts.

Other than that, I had to manually implement gettimeofday() & strndup(). The former exists in MinGW as-is, but I wanted to eventually support MSVC.
ports.c is also different in Windows from POSIX, though the Windows implementation should at least in theory be usable in POSIX systems as-is — it's also much simpler! It relies on gethostbyname, using our own hostname (obtained via gethostname) as the parameter. As an aside, this also needs linking with iphlpapi in Windows.

My initial implementation replaced the necessary heads in-place, but I've since moved the compatibility parts into platform/{address,endian,socket,misc}.h, which is mostly just "forwarding macros"; one exception being gettimeofday(), strdup(), and initialization (the latter being a no-op outside of Windows).
I'd like your feedback on what makes the most sense here. I personally think that having this stuff in a separate platform folder (or, alternatively, platform_* files) centralizes the platform-specific parts. The only remaining #ifdef inside the codebase is for handling ports_get_host_addr in ports.c.


I've also started working on a web/Emscripten wrapper for libpeer. Essentially, the idea is to provide the exact same API, but to make use of the browsers' WebRTC APIs internally. That one's progressing a little slowly because there are some API mismatches between the two, plus I've been having trouble with signaling.
Not sure how much interest you would have in this, but it's a necessity for me (it's the whole reason as to why I'm using WebRTC for games instead of pure UDP implementations such as ENet).

There were a few other minor fixups, for example:

  • Making CMake builds work when the default generator isn't make (in my case, it's ninja)
    • make -j4 is replaced with cmake --build . (cmake then runs whatever build system was generated)
    • make install is replaced with cmake --build . --target install
    • (as an aside: I reckon it would be relatively easy to make all of this built via CMake, if you'd be interested)
  • mbedTLS was failing to build because of compiler warnings — apparently, the warnings were pedantic enough that clang was warning me about using __builtin_* GNU extensions, and since mbedTLS is by default configured with -Werror ...
  • In the library itself, Google's STUN server would commonly send ERROR-CODE (0x0009) attributes, which libpeer does not recognize. I've added support for that, mostly by simply having libpeer recognize it, log it as an error, and log its payload (part of which is a human-readable error string).
  • The sample example declared reader_init(), but used reader_init("./media/"); that has now been fixed by making the call also have no parameters.

A few things I'd like to see for my own use-case (though I understand if some of these might not fit your goals):

  • Firefox support. Waterfox (which is based on Firefox, and thus has the same problem) is my daily driver, and it would be nice to support it. I'm definitely willing to investigate this. That said, the fact that Chrome needs upwards of 40 seconds to connect is definitely suspicious ... something's off here.
  • Support for custom signaling (plus not hardcoding signaling host URLs, but rather parameterizing them) — so that I could use WebSockets for it, as they make far more sense for my use-case. I feel like this would aid embedded uses, too, by saving program memory space for features that aren't used (e.g. if someone wants to use WebSockets with ESP32, there's no point in compiling & loading all of MQTT).
    • Stretch goal: WebSockets support! There exist third-party libraries, but it would be convenient to have it builtin.
  • IPv6 support. I can see there's already some support in the library (e.g. struct Address has an ipv6 field), but others like ports_get_host_addr() are IPv4-only. Mind, I don't strictly speaking need this at all, it's more one of those "might as well" situations.
  • This is a bit of a nitpick, but the codebase has a few typos, trailing whitespace (which for me causes "false edits" when I save the modified code), some CMakeLists strangeness, and such.

I'm more than willing to help with all of these (though I won't be able to actually test IPv6 support), but it's your library, so I'd like your comments on this first!

And sorry about the wall of text, I just figured you'd appreciate the feedback, updates, and clarifications.

@richlegrand
Copy link
Contributor

richlegrand commented Nov 19, 2023

I'm seeing the long delays in the browser also. It happens when the browser is waiting for all of the ice candidates to come in. My thinking is that there is some logic in the browser that waits for a timeout period for "the right kind of candidate". This makes sense (for example) when there are only ipv6 candidates available (because the browser can see that the offer's candidates are ipv4-only) -- the browser will wait until the timeout period, which makes sense. What makes less sense is when the browser waits even when there are valid/compatible candidates in the offer.

I haven't seen long delays in the browser when using an aiortc (python) peer, for example. There may be a way to tweak the candidates' attributes such that the browser doesn't wait. The other idea is to use "trickle ice" -- this should reduce the connection times in all scenarios. Just thoughts though... :)

Regarding custom signaling, I'm using libpeer with different signaling and it's not a problem. The main discovery for me was that peer_connection.h is basically a simplified WebRTC API.

@darkuranium
Copy link
Author

I'm seeing the long delays in the browser also. It happens when the browser is waiting for all of the ice candidates to come in. My thinking is that there is some logic in the browser that waits for a timeout period for "the right kind of candidate". This makes sense (for example) when there are only ipv6 candidates available (because the browser can see that the offer's candidates are ipv4-only) -- the browser will wait until the timeout period, which makes sense. What makes less sense is when the browser waits even when there are valid/compatible candidates in the offer.

I haven't seen long delays in the browser when using an aiortc (python) peer, for example. There may be a way to tweak the candidates' attributes such that the browser doesn't wait. The other idea is to use "trickle ice" -- this should reduce the connection times in all scenarios. Just thoughts though... :)

Regarding custom signaling, I'm using libpeer with different signaling and it's not a problem. The main discovery for me was that peer_connection.h is basically a simplified WebRTC API.

So, I've made progress in both Firefox & Chrome. I got Firefox working --- see #59!

Chrome, at least with trickle-ICE on seems to be instant when it has no microphone permission ... but takes ages when it does. So it must be something related to that.

@darkuranium
Copy link
Author

@sepfy @richlegrand #60

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants