Pure JS data channel implementation
Opened this issue · 47 comments
WebRTC let's us do browser<–>browser p2p communication.
But what about browser<–>server and server<–>server?
WebRTC everywhere
There are significant advantages to using the same transport (i.e. WebRTC) for all peer connections in a decentralized system, including:
- reduces the importance of centralized servers (they're just normal peers with excellent uptime)
- code re-use (don't need to write a TCP/UDP/WebSocket and WebRTC transport layer)
- option to use unreliable semantics (UDP-like) for browser<–>browser and browser<–>server
- free NAT hole punching (useful for desktop communications apps, which are like servers in almost all ways, except they're usually behind NATs)
Why a pure JS data channel implementation?
The data channel can be used in node.js via the wrtc module by @modeswitch. It's impressive work, but not without it's drawbacks.
It takes 25+ minutes to install on my system, and pre-built binaries are not available :(. It has to download and compile Google's libwebrtc from scratch. libwebrtc is huge! It contains libjingle, chromium's depot_tools, lots of video/audio codec stuff, and more! And there are install issues for users without a suitable compiler installed.
A pure JS implementation (relying only on node.js built-ins like dgram
and net
) would greatly simplify installation for end users.
Open questions
- Is a pure JS implementation even possible? If raw ip is required, then we'll need a module like raw-socket and we're depending on native modules again.
- What pieces are already available on npm? A quick search shows: stun and node_dtls (native).
- Would it be useful to have a mostly-JS implementation?
- Would it be useful to make a standalone c library for data channel (extracted from libwebrtc) that multiple languages could bind to? If this reduced compile time from 25 minutes to <1 minute, maybe that's enough and we don't need a pure JS implementation.
Related discussion: jbenet/random-ideas#13
Tagging people who I've discussed this with: @mafintosh, @substack, @fippo, @jbenet, @maxogden, @modeswitch, @DamonOehlman, @jhiesey.
I think we should setup a hangouts session for people interested in implementing this and discuss different approaches asap.
Sounds like a plan to me, TZ here is +11 so I'll try and participate if I can.
Thanks for settings this up.
The approach I took with wrtc
has been very mixed, largely for the reasons mentioned above by @feross. I'd like to avoid writing a full WebRTC implementation in JS at this point, in favour of adapting existing native libraries. The two big problems I have with libjingle
are 1) that it isn't very modular, and 2) the build process sucks a lot (gyp has not been a pleasure to use).
Mozilla's WebRTC implementation is tightly coupled to Firefox, so it would likely be as bad as libjingle
to work with. OpenWebRTC may be smaller and easier to build, but it doesn't have support for data channels (though maybe we can contribute code there?).
It would be nice to have a solution that can support media streams as well. I get feature requests for that all the time. If it's done in a modular way then it doesn't need to impact consumers who don't want it.
Another (crazy?) option is to work with emscripten to generate JS for some of the native library components. I have no idea how nasty that might be, but it's worth mentioning anyway.
Sorry I've been silent on these discussions. I have more time for this now and I intend to be involved. Count me in for a hangout session; I'm in GMT-5.
Hello everyone! excited this is shaping up -- thanks for including. As @feross mentioned, this discussion has some other people: jbenet/random-ideas#13 and links thoughts.
+1 for hangout.
It takes 25+ minutes to install on my system, and pre-built binaries are not available :(. It has to download and compile Google's libwebrtc from scratch. libwebrtc is huge! It contains libjingle, chromium's depot_tools, lots of video/audio codec stuff, and more! And there are install issues for users without a suitable compiler installed.
Agreed, let's get the UX (for both users and devs) to be great!
Would it be useful to make a standalone c library for data channel (extracted from libwebrtc) that multiple languages could bind to? If this reduced compile time from 25 minutes to <1 minute, maybe that's enough and we don't need a pure JS implementation.
My vote is for this. In terms of time investment, i think it it's both easier, and more far reaching.. Ripping out the sources (not compiling from chromium source but actually extracting the relevant code, removing the chaff) may be a lot simpler than an implementation from scratch (particularly if we can talk / ask questions to the people who implemented it -- the chromium lists are pretty active). Plus, making a {libdc, libdatachannel, libpeerdc, ...} would be extremely helpful to all the other languages in the world. We can do Node prebuilt binaries really well (e.g. @mafintosh's leveldown-prebuilt). (Of course, given plenty of time, i'm all up for helping with a pure js impl, but i think our mileage may go much further if we consider a broader community).
useful to make a standalone c library for data channel
+1
GMT+1 (Spain), but prefer to use email/github issues.
A pure JS is almost impossible, or impractical. There's a lot of libraries involved and to have some performance we would need to use compiled modules at some degree. As a teaching project it's ok, but not production ready. I think a better alternative is to have a dinamyc library that can be used by several languajes.
selfishly, can it (the C/whatever lib) please be something that also works on iOS?
I'm also interested in seeing something that allows browser < - > server communication using only data channels. I would love to see something like this be released a module where we can build applications upon. I think that going with an c library where Node can just bind in to makes more sense here as it would allow other languages leverage the work.
+1 to what @jbenet said - a small C library that builds fast and that any language could bind to would be awesome.
I'm interested in experimenting with this in a side project I have and helping/learning as much as I can.
I'd say for anyone interested in looking at what's involved in creating a more modular implementation utilizing C/C++ libraries, having a look at the prereqs for janus is a great place to start.
I've started thinking about this previously, and if I had managed to find the time I probably would have tackled the problem in two parts:
- Creating a binding to libnice. I just did a quick scan and there does seem to be one of these already (https://github.com/Innovailable/node-libnice), however, it is currently licensed using AGPL. cc @thammi
- Creating a binding to an SCTP implementation. The one referenced from Janus is sctp-refimpl. The "reference implementation" badge probably means we should exercise caution here, but I'm sure @lminiero can comment about how stable it is.
I'd say for anyone interested in looking at what's involved in creating a more modular implementation utilizing C/C++ libraries, having a look at the prereqs for janus is a great place to start.
There's also clib by which has a growing ecosystem of micro-libs and has a similar feel to npm.
I still think a pure JS version is worth trying, as well as a small c version
if you care only about browser<->server then you can probably get away with using ice-tcp (only available in chrome today) in pure js and bindings to usrsctp for now. The latter is easy enough to install, especially compared to the webrtc.org lib.
sctp-refimpl is an excellent library written by Michael Tuexen, one of the authors of the Data Channels specification in the IETF and SCTP expert. Besides, it's being used in both Chrome and Firefox, so we simply cannot go wrong doing the same :-)
As explained in https://github.com/meetecho/janus-gateway/blob/master/sctp.c (the code where I handle most of the Data Channels stuff) I based the implementation on a sample application that the library provided:
http://code.google.com/p/sctp-refimpl/source/browse/trunk/KERN/usrsctp/programs/rtcweb.c
This example shows how you can transfer SCTP messages on top of a different "transport", that is, not using SCTP directly but on top of, let's say UDP or something else. In my case, I obviously wanted to have something that I could have relayed up to the code handling DTLS connectivity, so that I could have it sent that way. Same thing for incoming messages, i.e., data retrieved through DTLS and to be passed to the SCTP stack somehow.
My integration in Janus currently only supports strings, but supporting octets should, at least in principle, just be a matter of using the binary PPID (53) instead of strings (51), as specified in https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-11#section-8
libnice+openssl+sctp-refimpl should be enough to cover the requirements.
Looks like a C library now exists: http://opentools.homeip.net/webrtc
I'm going to try ripping out all the mediastream non-datachannel stuff from libjingle, just to see where that gets us.
Quick update: I've made some good progress on stripping down libjingle. Data channels are working and all the media streams code is unreferenced. I'm now removing source files and updating build scripts to see how quick/easy this can get. There are very few system deps required now to build.
@modeswitch That's awesome news mate - very cool :)
I've released an update for wrtc
that uses a stripped-down version of libwebrtc. If you're on linux, try npm install wrtc
to check it out.
A few notes:
- Docs are not updated yet, so ask me directly if you're confused
- The library source tree is much smaller, but I'm sure there are still things that can be removed. If you see anything, please let me know (or better yet, make a pull request!). The library source is here: https://github.com/js-platform/libwebrtc
- The build process is probably fragile (git, python, etc are required). Maybe we can use JS implementations here to help reduce the burden on users. Pull requests welcome for this as well.
- There are some known issues with
wrtc
(mostly logged, but some maybe not) that I'm starting to investigate. This may lead to segmentation faults or other crashes. Most of these are intermittent, so try again. Contributions welcomed. - All copyright/attributions are left intact in the stripped-down libwebrtc. If something looks wrong here, please let me know. Anything missing is an honest mistake and I'll do my best to fix errors.
- The code in
libwebrtc
is pulled into a single repo for now. There's probably a nicer way to do this. Suggestions welcomed. - (addendum) For OSX users, please give this a try and let me know what doesn't work. No reason this shouldn't work for you, I just can't test it!
Any other feedback, please let me know!
just wanted to say this makes my heart smile! :)
I tried to install with Mac OS 10.9, but ran into issues because I'm using command line tools, (can't install xcode due to company policy). Not sure I want to log this as an issue, but might want to note that Xcode is required for Mac users? (for build obviously)
You can also install the command line tools without xcode. See https://github.com/kennethreitz/osx-gcc-installer for more details.
I've just tried it on OS X 10.10 and got the following error:
$ npm install wrtc
-
> wrtc@0.0.30 install /Users/szimek/Projects/apps/webrtc-test/node_modules/wrtc
> node-gyp rebuild
ACTION Run build script /dev/null
TARGET_ARCH=x64 Release
Preparing directories ... done
Cloning libwebrtc repository ... done
Generating build scripts ... .emake: *** [/dev/null] Error 255
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:267:23)
gyp ERR! stack at ChildProcess.emit (events.js:98:17)
gyp ERR! stack at Process.ChildProcess._handle.onexit (child_process.js:820:12)
gyp ERR! System Darwin 14.0.0
gyp ERR! command "node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/szimek/Projects/apps/webrtc-test/node_modules/wrtc
gyp ERR! node -v v0.10.35
gyp ERR! node-gyp -v v1.0.2
gyp ERR! not ok
npm ERR! Darwin 14.0.0
npm ERR! argv "node" "/usr/local/bin/npm" "install" "wrtc"
npm ERR! node v0.10.35
npm ERR! npm v2.1.14
npm ERR! code ELIFECYCLE
npm ERR! wrtc@0.0.30 install: `node-gyp rebuild`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the wrtc@0.0.30 install script 'node-gyp rebuild'.
npm ERR! This is most likely a problem with the wrtc package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-gyp rebuild
npm ERR! You can get their info via:
npm ERR! npm owner ls wrtc
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /Users/szimek/Projects/apps/webrtc-test/npm-debug.log
It doesn't really tell much what went wrong...
@szimek I'll have it dump messages directly to stdout.
@szimek Pushed a new version. Build output should stream to your console. I've also re-added depot_tools, since most people won't have ninja
.
$ npm install wrtc
|
> wrtc@0.0.31 install /Users/szimek/Projects/apps/webrtc-test/node_modules/wrtc
> node-gyp rebuild
ACTION Run build script /dev/null
TARGET_ARCH=x64 Release
: Preparing directories ...
: Cloning depot_tools ...
Cloning into 'depot_tools'...
POST git-upload-pack (189 bytes)
POST git-upload-pack (198 bytes)
remote: Sending approximately 13.98 MiB ...
remote: Counting objects: 807, done
remote: Finding sources: 100% (807/807)
remote: Total 807 (delta 75), reused 435 (delta 75)
Receiving objects: 100% (807/807), 2.74 MiB | 1.49 MiB/s, done.
Resolving deltas: 100% (75/75), done.
Checking connectivity... done.
: Cloning libwebrtc ...
Cloning into 'libwebrtc'...
POST git-upload-pack (205 bytes)
POST git-upload-pack (214 bytes)
remote: Counting objects: 7357, done.
remote: Compressing objects: 100% (6299/6299), done.
remote: Total 7357 (delta 913), reused 6279 (delta 754)
Receiving objects: 100% (7357/7357), 36.95 MiB | 6.43 MiB/s, done.
Resolving deltas: 100% (913/913), done.
Checking connectivity... done.
Checking out files: 100% (6879/6879), done.
: Generating build scripts ...
Updating projects from gyp files...
gyp: third_party/boringssl/boringssl.gyp not found (cwd: /Users/szimek/Projects/apps/webrtc-test/node_modules/wrtc/third_party/libwebrtc)
ERROR: 1 null
make: *** [/dev/null] Error 255
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:267:23)
gyp ERR! stack at ChildProcess.emit (events.js:98:17)
gyp ERR! stack at Process.ChildProcess._handle.onexit (child_process.js:820:12)
gyp ERR! System Darwin 14.0.0
gyp ERR! command "node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Users/szimek/Projects/apps/webrtc-test/node_modules/wrtc
gyp ERR! node -v v0.10.35
gyp ERR! node-gyp -v v1.0.2
gyp ERR! not ok
npm ERR! Darwin 14.0.0
npm ERR! argv "node" "/usr/local/bin/npm" "install" "wrtc"
npm ERR! node v0.10.35
npm ERR! npm v2.1.14
npm ERR! code ELIFECYCLE
npm ERR! wrtc@0.0.31 install: `node-gyp rebuild`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the wrtc@0.0.31 install script 'node-gyp rebuild'.
npm ERR! This is most likely a problem with the wrtc package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-gyp rebuild
npm ERR! You can get their info via:
npm ERR! npm owner ls wrtc
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /Users/szimek/Projects/apps/webrtc-test/npm-debug.log
Looks like some issue with boringssl.
@szimek I removed boringssl because it's not used in the linux build, but I'll add it back.
@szimek Fixed.
Also, I think we're detracting from the thread. Let's continue the discussion over here: js-platform/libwebrtc#1
Latest version of node-webrtc
works on both linux and osx.
One possibility for developing a pure JS implementation would be to start pulling parts of the native library into JS, iteratively. It sounds like a lot of work, though.
Ah, I am late to the story but I am really sad to see that community has decided to drop all the media stuff in favor of shorter compilation times. I just got an idea how great it would be that both the server and client would communicate over WebRTC and can have the same standard API to do that, both for data and for media. I wanted to create a WebRTC/mesh based community radio where servers would be doing recoding and clients would send their stream to them. But without the MediaStream implementation it means that one has to reimplement the whole audio processing in node.js and on top of data channels. Is it really so problematic that the compilation takes a lot of time? Maybe I just remember Qt times where 4 hours of compiling was completely normal. :-)
So probably what I wanted to say with my rant was that there is a case for interoperability. If majority of nodes in your P2P network speak MediaStream (browsers) it is really sad that servers (much more powerful devices) would not as well.
One more comment and then I will stop. The compilation issues also became simpler in the last year with Docker containerization. Now you get it to compile once inside a Docker image and then you have it. You can even cache it in a Docker layer.
@mitar why isn't this just a layering problem? i'm failing to see the problem. AFAIU, you should be able to do mediastreams just fine, and have a native library compiled in your code for it?
There are some speculations I am not sure about, but:
- are you sure that all channels have the same priority? audio streams might be prioritized differently in the browsers
- it seems that a very natural fit is to use Web Audio API + WebRTC, you can create a flow program where you connect nodes together and it is all running in audio thread without any JavaScript engine switch
- by requesting Web Audio API to come back to JavaScript, so that JavaScript splits the stuff into buffers and then send over the data channel you are introducing a different data path
- browsers do tend to deprioritize background/inactive tabs/threads, if are having this different data path things might slow down, while for audio path without JavaScript browsers might not do that
- WebRTC allows for unreliable transmission and for audio packets this might be useful, but I would hope that Web Audio API + WebRTC knows how to split stream into such audio packets that they are send over the wire inside the correct MTU, so that if one packet is drop, other packets have headers and stuff, not that you are cutting buffers arbitrary, I do not know if you can do that when you are doing by yourself in JavaScript, without knowing some information about the codec and format you are using
To me it just looks like duplication of the code in JS for things which have to be low-latency and are tricky to setup correctly.
Also, having exactly the same audio codecs on the client and server would help a lot. So I would anyway have to compile the audio codecs to use in node.js. So why not have them compiled together with WebRTC?
community has decided to drop all the media stuff in favor of shorter compilation times
If you want a complete WebRTC stack on the server, there are already multiple you can pick from. See http://webrtc.org/, http://www.openwebrtc.org/, and https://www.npmjs.com/package/wrtc (for use in node.js).
All we're saying is that there's lots of value to be gained by having a library that's lightweight and only does data channel. :)
There is nothing for node.js:
https://www.npmjs.com/package/wrtc (for use in node.js)
MediaStream APIs are not supported in order to reduce the number of external dependencies and to make compilation faster and easier.
Electron works for server side webrtc + node just fine using xvfb-run + electron-spawn
@maxogden that's really interesting! Do you know of any write ups or examples of doing that?
@xdumaine I figured it out when writing https://github.com/moose-team/peerbot#running-on-headless-ubuntu
He he, I was also thinking about that. That why is node.js just V8 on the server side, why not simply the whole Chrome. :-)
@mitar try running chrome on a server... there is a reason a certain browser with 300 million users doesn't support the latest and greatest web features ;-)
I was thinking about using it for a Pi drone project.
+1 to what @jbenet said - a small C library that builds fast and that any language could bind to would be awesome.
A bit late to the party, I've written a standalone C++ library with a C API implementing WebRTC data channels: https://github.com/paullouisageneau/libdatachannel
It should compile on POSIX systems (including MacOS) and Windows. If someone feels like writing bindings, you are very welcome to do so.
@paullouisageneau Are there Node.js bindings? We're open to exploring using an alternative to wrtc
in webtorrent-hyrbid
. More info here: webtorrent/webtorrent-hybrid#58 (comment)
@feross Yes, @murat-dogan has been writing node.js bindings here: https://github.com/murat-dogan/node-datachannel