limit WebRTC bandwidth from the client side (browser) - javascript

I know it's possible to limit the upload (sent) bandwidth using "setParameter" on the peer-connection.
I'm looking for a way to limit the download (received) and couldn't find one.
(I don't have control
Am I missing the concept? or there's a way to do it?
Thanks

To "limit bandwidth" of realtime data means sending less of it.
There's no setParameters on an RTCRtpReceiver and no builtin back channel for this. But you can trivially make your own using e.g. createDataChannel("myBackchannel"), provided you have control of both sides. Then have the receiver send parameters back to the sender over it, which then sets them with setParameters.

This can be controlled by inserting a b= line into the SDP. Support for this varies, until recently Chrome only supported b=AS which specified the bandwidth in kilobits per second while Firefox only supported b=TIAS which specifies the bandwidth in bits per second.
Both variants ask a remote peer not to send more than this bandwidth.
One of the webrtc samples still shows the usage but you will need to deactivate the newer usage of setParameters.

Related

WebRTC DataChannel flow/control/back-pressure

The RTCDataChannel API does not provide any kind of flow/control or back-pressure, does this mean, that a sender could, theoretically, crash the browser of the receiver ? In my opinion the browser, (Chrome, Firefox, etc. all use SCTP under the hood), reads from the SCTP connection and schedules to run the js-callback consuming the packet. If the event queue cannot not keep up with the sender, the browser basically reads continuously packets while storing the packets in a buffer, which grows indefinitely. So when you connect two browsers, the sender can actually always overwhelm the other one, because there is no barrier like TCP receive windows or something similar.
This applies to the websocket api as well.
Do I just miss something or are these API just broken ? If I'm right, this would be a severe security issue when talking to unauthenticated browsers (in a torrent scenario for instance).
The webrtc data channel used to be based on UDP. During that time there was artificial throttling imposed by the browser in order to prevent network flooding. This was the case until chrome v32, I believe.
Nowadays the data channel is based on SCTP which has build-in flow control (FC) and there is no browser throttling any more (thank God). The parameters that control FC are not exposed through the API but that doesn't mean there is no FC.
I am not familiar with the implementation of webrtc in Chrome/FF but I don't think you can crash the browser with a simple flood attack. The "producer is faster than the consumer" is a pretty old problem.
That said, I have been working with the data channel, for more than an year now and have seen my browser crash almost on a daily basis, so there are probably many bugs in the webrtc implementation. Hopefully they won't pose any threat to security.
Sending big chunks of data useing webrtc data channel is not a particularly pleasant experience. The API doesn't offer a "channel is ready for write" callback or anything of the sort, so, yes!, you have to poll the bufferedamount value and try to keep it inside an optimal window. To add insult to injury bufferedamount used to be broken under Windows versions of Chrome, it was always 0. But I think they fixed this in chrome v37 or around that time.
IMHO the webrtc API is not very well thought through but it does the job and honestly I cannot think of any js API that is well thought through.

WebRTC - scalable live stream broadcasting / multicasting

PROBLEM:
WebRTC gives us peer-to-peer video/audio connections. It is perfect for p2p calls, hangouts. But what about broadcasting (one-to-many, for example, 1-to-10000)?
Lets say we have a broadcaster "B" and two attendees "A1", "A2". Of course it seems to be solvable: we just connect B with A1 and then B with A2. So B sends video/audio stream directly to A1 and another stream to A2. B sends streams twice.
Now lets imagine there are 10000 attendees: A1, A2, ..., A10000. It means B must send 10000 streams. Each stream is ~40KB/s which means B needs 400MB/s outgoing internet speed to maintain this broadcast. Unacceptable.
ORIGINAL QUESTION (OBSOLETE)
Is it possible somehow to solve this, so B sends only one stream on some server and attendees just pull this stream from this server? Yes, this means the outgoing speed on this server must be high, but I can maintain it.
Or maybe this means ruining WebRTC idea?
NOTES
Flash is not working for my needs as per poor UX for end customers.
SOLUTION (NOT REALLY)
26.05.2015 - There is no such a solution for scalable broadcasting for WebRTC at the moment, where you do not use media-servers at all. There are server-side solutions as well as hybrid (p2p + server-side depending on different conditions) on the market.
There are some promising techs though like https://github.com/muaz-khan/WebRTC-Scalable-Broadcast but they need to answer those possible issues: latency, overall network connection stability, scalability formula (they are not infinite-scalable probably).
SUGGESTIONS
Decrease CPU/Bandwidth by tweaking both audio and video codecs;
Get a media server.
As it was pretty much covered here, what you are trying to do here is not possible with plain, old-fashionned WebRTC (strictly peer-to-peer). Because as it was said earlier, WebRTC connections renegotiate encryption keys to encrypt data, for each session. So your broadcaster (B) will indeed need to upload its stream as many times as there are attendees.
However, there is a quite simple solution, which works very well: I have tested it, it is called a WebRTC gateway. Janus is a good example. It is completely open source (github repo here).
This works as follows: your broadcaster contacts the gateway (Janus) which speaks WebRTC. So there is a key negotiation: B transmits securely (encrypted streams) to Janus.
Now, when attendees connect, they connect to Janus, again: WebRTC negotiation, secured keys, etc. From now on, Janus will emit back the streams to each attendees.
This works well because the broadcaster (B) only uploads its stream once, to Janus. Now Janus decodes the data using its own key and have access to the raw data (that it, RTP packets) and can emit back those packets to each attendee (Janus takes care of encryption for you). And since you put Janus on a server, it has a great upload bandwidth, so you will be able to stream to many peer.
So yes, it does involve a server, but that server speaks WebRTC, and you "own" it: you implement the Janus part so you don't have to worry about data corruption or man in the middle. Well unless your server is compromised, of course. But there is so much you can do.
To show you how easy it is to use, in Janus, you have a function called incoming_rtp() (and incoming_rtcp()) that you can call, which gives you a pointer to the rt(c)p packets. You can then send it to each attendee (they are stored in sessions that Janus makes very easy to use). Look here for one implementation of the incoming_rtp() function, a couple of lines below you can see how to transmit the packets to all attendees and here you can see the actual function to relay an rtp packet.
It all works pretty well, the documentation is fairly easy to read and understand. I suggest you start with the "echotest" example, it is the simplest and you can understand the inner workings of Janus. I suggest you edit the echo test file to make your own, because there is a lot of redundant code to write, so you might as well start from a complete file.
Have fun! Hope I helped.
As #MuazKhan noted above:
https://github.com/muaz-khan/WebRTC-Scalable-Broadcast
works in chrome, and no audio-broadcast yet, but it seems to be a 1st Solution.
A Scalable WebRTC peer-to-peer broadcasting demo.
This module simply initializes socket.io and configures it in a way
that single broadcast can be relayed over unlimited users without any
bandwidth/CPU usage issues. Everything happens peer-to-peer!
This should definitely be possible to complete.
Others are also able to achieve this: http://www.streamroot.io/
AFAIK the only current implementation of this that is relevant and mature is Adobe Flash Player, which has supported p2p multicast for peer to peer video broadcasting since version 10.1.
http://tomkrcha.com/?p=1526.
"Scalable" broadcasting is not possible on the Internet, because the IP UDP multicasting is not allowed there. But in theory it's possible on a LAN. The problem with Websockets is that you don't have access to RAW UDP by design and it won't be allowed.
The problem with WebRTC is that it's data channels use a form of SRTP, where each session has own encryption key. So unless somebody "invents" or an API allows a way to share one session key between all clients, the multicast is useless.
There is the solution of peer-assisted delivery, meaning the approach is hybrid. Both server and peers help distribute the resource. That's the approach peer5.com and peercdn.com have taken.
If we're talking specifically about live broadcast it'll look something like this:
Broadcaster sends the live video to a server.
The server saves the video (usually also transcodes it to all the relevant formats).
A metadata about this live stream is being created, compatible with HLS or HDS or MPEG_DASH
Consumers browse to the relevant live stream there the player gets the metadata and knows which chunks of the video to get next.
At the same time the consumer is being connected to other consumers (via WebRTC)
Then the player downloads the relevant chunk either directly from the server or from peers.
Following such a model can save up to ~90% of the server's bandwidth depending on bitrate of the live stream and the collaborative uplink of the viewers.
disclaimer: the author is working at Peer5
My masters is focused on the development of a hybrid cdn/p2p live streaming protocol using WebRTC. I've published my first results at http://bem.tv
Everything is open source and I'm seeking for contributors! :-)
The answer from Angel Genchev seems to be correct, however, there is a theoretical architecture, that allows low-latency broadcasting via WebRTC. Imagine B (broadcaster) streams to A1 (attendee 1). Then A2 (attendee 2) connects. Instead of streaming from B to A2, A1 starts streaming video being received from B to A2. If A1 disconnects then A2 starts receiving from B.
This architecture could work if there are no latencies and connection timeouts. So theoretically it is right, but not practically.
At the moment I am using server side solution.
I'm developing WebRTC broadcasting system using the Kurento Media Server. Kurento Supports several kinds of streaming protocol such as RTSP, WebRTC, HLS. It works as well in term of real-time and scaling.
Hence, Kurento doesn't support RTMP which is used in Youtube or Twitch now. One of the problem with me is the number of user concurrent with this.
Hope it help.
You are describing using WebRTC with a one-to-many requirement. WebRTC is designed for peer-to-peer streaming, however there are configurations that will let you benefit from the low latency of WebRTC while delivering video to many viewers.
The trick is to not tax the streaming client with every viewer and, like you mentioned, have a "relay" media server. You can build this yourself but honestly the best solution is often to use something like Wowza's WebRTC Streaming product.
To stream efficiently from a phone you can use Wowza's GoCoder SDK but in my experience a more advanced SDK like StreamGears works best.

Chunking WebSocket Transmission

since I'm using WebSocket connections on more regular bases, I was interested in how things work under the hood. So I digged into the endless spec documents for a while, but so far I couldn't really find anything about chunking the transmission stream itself.
The WebSocket protocol calls it data frames (which describes the pure data stream, so its also called non-control frames). As far as I understood the spec, there is no defined max-length and no defined MTU (maximum transfer unit) value, that in turn means a single WebSocket data-frame may contain, by spec(!), an infinite amount of data (please correct me if I'm wrong here, I'm still a student on this).
After reading that, I instantly setup my little Node WebSocket server. Since I have a strong Ajax history (also on streaming and Comet), my expectations originaly were like, "there must be some kind of interactive mode for reading data while it is transfered". But I am wrong there, ain't I ?
I started out small, with 4kb of data.
server
testSocket.emit( 'data', new Array( 4096 ).join( 'X' ) );
and like expected this arrives on the client as one data-chunk
client
wsInstance.onmessage = function( data ) {
console.log( data.length ); // 4095
};
so I increased the payload and I actually was expecting again, that at some point, the client-side onmessage handler will fire repeatly, effectivley chunking the transmission. But to my shock, it never happened (node-server, tested on firefox, chrome and safari client-side). My biggest payload was 80 MB
testSocket.emit( 'data', new Array( 1024*1024*80 ).join( 'X' ) );
and it still arrived in one big data-chunk on the client. Of course, this takes a while even if you have a pretty good connection. Questions here are
is there any possiblity to chunk those streams, similar to the XHR readyState3 mode ?
is there any size limit for a single ws data-frame ?
are websockets not supposed to transfer such large payloads? (which would make me wonder again why there isn't a defined max-size)
I might still look from the wrong perspective on WebSockets, probably the need for sending large data-amounts is just not there and you should chunk/split any data logically yourself before sending ?
First, you need to differentiate between the WebSocket protocol and the WebSocket API within browsers.
The WebSocket protocol has a frame-size limit of 2^63 octets, but a WebSocket message can be composed of an unlimited number of frames.
The WebSocket API within browsers does not expose a frame-based or streaming API, but only a message-based API. The payload of an incoming message is always completely buffered up (within the browser's WebSocket implementation) before providing it to JavaScript.
APIs of other WebSocket implementations may provide frame- or streaming-based access to payload transferred via the WebSocket protocol. For example, AutobahnPython does. You can read more in the examples here https://github.com/tavendo/AutobahnPython/tree/master/examples/twisted/websocket/streaming.
Disclosure: I am original author of Autobahn and work for Tavendo.
More considerations:
As long as there is no frame/streaming API in browser JS WebSocket API, you can only receive/send complete WS messages.
A single (plain) WebSocket connection cannot interleave the payload of multiple messages. So i.e. if you use large messages, those are delivered in order, and you won't be able to send small messages in between while a big message is still on the fly.
There is an upcoming WebSocket extension (extensions are a builtin mechanism to extend the protocol): WebSocket multiplexing. This allows to have multiple (logical) WebSocket connections over a single underlying TCP connection, which has multiple advantages.
Note also: you can open multiple WS connections (over different underlying TCPs) to a single target server from a single JS / HTML page today.
Note also: you can do "chunking" yourself in application layer: send your stuff in smaller WS messages a reassemble yourself.
I agree, in an ideal world, you'd have message/frame/streaming API in browser plus WebSocket multiplexing. That would give all the power and convenience.
RFC 6455 Section 1.1:
This is what the WebSocket Protocol provides: [...] an alternative to HTTP polling for two-way communication from a web page to a remote server.
As stated, WebSockets are for commmunications between a web page and a server. Please note the difference between a web page and a web browser. Examples being used are browser games and chat applications, who excange many small messages.
If you want to send many MB's in one message, I think you're not using WebSockets the way they were intended. If you want to transfer files, then do so using a Plain Old Http Request, answered with Content-Disposition to let the browser download a file.
So if you explain why you want to send such large amounts of data, perhaps someone can help come up with a more elegant solution than using WebSockets.
Besides, a client or server may refuse too large messages (although it isn't explicitly stated how it'll refuse):
RFC 6455 Section 10.4:
Implementations that have implementation- and/or platform-specific
limitations regarding the frame size or total message size after
reassembly from multiple frames MUST protect themselves against
exceeding those limits. (For example, a malicious endpoint can try
to exhaust its peer's memory or mount a denial-of-service attack by
sending either a single big frame (e.g., of size 2**60) or by sending
a long stream of small frames that are a part of a fragmented
message.) Such an implementation SHOULD impose a limit on frame
sizes and the total message size after reassembly from multiple
frames.

How does Gmail do comet on Opera?

I would like to know how Gmail (or anyone else) does comet on Opera.
Here is what I know so far from my experiments.
It doesn't use the event-source tag which is broken in Opera 10.51.
It doesn't use iframe which displays a spinning throbber and a busy mouse cursor.
It doesn't use responseText on xmlhttprequest when readyState = 3 which is known to be broken on Opera.
I tried seeing how it was done in mibbit and etherpad, and I found that they both use long-polling.
Bounty
The bounty goes to whoever can tell me a method better than "event-source" for Opera comet streaming, or how gmail does streaming (or long-polling if it does that).
GMail uses BrowserChannel (Docs | Source), which is included in Google's Closure Library.
#fileoverview Definition of the BrowserChannel class. A BrowserChannel
simulates a bidirectional socket over HTTP. It is the basis of the
Gmail Chat IM connections to the server.
I really don't have any idea on what the answer is. But I know Opera supports server-events : http://my.opera.com/WebApplications/blog/show.dml/438711 . Maybe it's a step towards the anwser?
I'm not really sure either, but I think they use it within Opera Unite.
I think that rather cross-browser (including Opera) approach might be to stream data through an Adobe Flash application. Though it would introduce dependence on the Flash plugin and is not very popular because of that.
I am the author of an in progess C++ HTTP server that is compatible with goog.netBrowserChannel. You can find the docs I've written while studying the protocol here:
http://code.google.com/p/libevent-browserchannel-server/wiki/BrowserChannelProtocol
Long story short, BrowserChannel uses forever frames on IE and XHR streaming on all other browsers. The protocol is divided into several phases, the first of which is network testing:
1) test the network to ensure response "streaming" is supported (in other words no buffering proxy exists)
2) check access to a variety of network prefixes (to make sure the network admin has not blocked access to chat)
Then the actual data transmission can start. Data is divided into two channels (forward and back). The back channel is a series of long lived (about 4 mins each) requests used for the server to "stream" content to the client. To do so HTTP chunked encoding is used. The client does it's best to make sure that one backchannel is always open. The server will close it about every 4 mins after which the client will open a new backchannel. The forward channel is used to send data from the client to the server. This pushing of data is done as necessary.

Long held AJAX connections being blocked by Anti-Virus

Ok, this is downright bizarre. I am building a web application that relies on long held HTTP connection using COMET, and using this to stream data from the server to the application.
Now, the problem is that this does not seem to go well with some anti-virus programs. We are now on beta, and some users are facing problems with the application when the anti-virus is enabled. It's not just one specific anti-virus either.. I found this work around for Avast when I looked online: http://avricot.com/blog/index.php?post/2009/05/20/Comet-and-ajax-with-Avast-s-shield-web-:-The-salvation-or-not
However, anyone here has any suggestions on how to handled this? Should I send any specific header to please these security programs?
This is a tough one. The kind of anti-virus feature that causes this tries to prevent malicious code running in the browser from uploading your personal data to a remote server. To do that, the anti-virus tries to buffer all outgoing traffic before it hits the network, and scan it for pre-defined strings.
This works when the application sends a complete HTTP request on the socket, because the anti-virus sees the end of the the HTTP request and knows that it can stop scanning and send the data.
In your case, there's probably just a header without a length field, so until you send enough data to fill the anti-virus's buffer, nothing will be written to the network.
If that's not a good reason to turn that particular feature off, I don't know what is. I ran into this with AVast and McAfee - at this point, the rest of the anti-virus industry is probably doing something like that. Specifically, I ran into this with McAfee's Personal Information Protection feature, which as far as I can tell, is simply too buggy to use.
If you can, just keep sending data on the socket, or send the data in HTTP messages that have a length field. I tried reporting this to a couple of anti-virus vendors - one of them fixed it, the other one didn't, to the best of my knowledge.
Of course, this sort of feature is completely useless. All a malicious application would need to do to get around it is to ROT13 the data before sending it.
Try using https instead of http. There are scanners that intercept https, too, but they're less common and the feature defaulted to off last time I checked. It also broke Firefox SSL connectivity when activated, so I think very few people will activate it and the vendor will hopefully kill the feature.
The problem is that some files can't be scanned in order - later parts are required to determine if the earlier parts are malicious.
So scanners have a problem with channels that are streaming data. I doubt your stream of data is able to be recognised as a clean file type, so the scanner is attempting to scan the data as best it can, and I guess holding up your stream in the process.
The only think I can suggest is to do the data transfer in small transactions, and use the COMET connection for notification only (closing each channel after a single notification).
If you use a non-standard port for your web requests, you may be able to work around this, there are a number of other issues, namely that this will be considered cross-domain by many browsers. Not sure if I have a better suggestion to offer here. It really depends on how the AV program intercepts a given port's traffic.
I think you're going to be forced to break the connection and reconnect. What does your code do if the connection goes down in an outage situation? I had a similar problem with a firewall once. The code had to detect the disconnect, then reconnect. I like the answer about breaking up the data transfer.

Categories