srt icon indicating copy to clipboard operation
srt copied to clipboard

[FR] Separation of protocol from network code

Open oviano opened this issue 2 years ago • 7 comments

A bit of a long-shot this one, but something I've noticed with both SRT and RIST is that the protocol logic is intertwined with the socket code.

I guess this is easiest when writing a reference implementation, but if for example the SRT protocol was separate, either implemented through C++ classes with virtual functions etc or (maybe more performant) C-style callbacks you could then use it with other ASIO/event libraries.

So you could use these libraries for establishing connections, implementing security, etc. and then in the case of a sender you could pass the data to the SRT API which would eventually call a callback which you would then supply to actually send the data.

On the receiver side, you would pass received data to the SRT API and then at some point it would call another callback to give you the packet for you to decode etc.

There may be reasons why this isn't possible which I haven't thought of, and I doubt it's a priority but there are quite a few ASIO-style libraries out there and it would mean you could use SRT with things like IOCP on Windows, as one example.

oviano avatar Jun 28 '22 06:06 oviano

Not sure where exactly you'd like to use them.

As for communication with the system (UDP sockets in particular, at least what it is now) there is the CChannel class; this one could be abstracted out so that sending and receiving the piece of data being now the "UDP payload" can be implemented different way. That alone shouldn't be difficult, just no one so far seen any benefit for it.

As for "callbacks" and API - I had in mind some time ago a project to abstract out the "SRT epoll" part and I was researching possibilities here. The problem is that this facility is grown so strongly into the code that it's extremely difficult to tear it off - quite a big refax would be first required here. Doable, of course, but requires a lot of work.

ethouris avatar Jun 28 '22 07:06 ethouris

I have written (for personal use) a video streaming system a bit like how Slingbox used to work, i.e. it streams your private TV source to another location. I'm using SRT for the AV transport, and it works really well.

I'm using TCP for the control aspect of the project (changing channels, logging-in, that sort of thing) and lately, I had cleaned up a lot to my network code and wrote a cross-platform ASIO system, which uses IOCP on Windows, kqueue on Apple platforms, epoll on Android and worker threads to process connect/accept/send/receive. Previously I was using blocking sockets and poll().

It just struck me that it would be nice to also use these new classes for the SRT aspect in the manner described above.

But I can see that the evolution of SRT from UDT means it's already dragging in existing socket/kqueue/epoll code.

Maybe a new clean SRT "protocol-only" implementation built from the ground-up would be the way to achieve it, but I understand there probably isn't a demand for it.

Thanks for listening anyway!

oviano avatar Jun 28 '22 10:06 oviano

It's hard to speak about "protocol only" thing here.

There are several layers here involved, and by what you are saying it looks like I guessed partially correctly - you want that this library work with any event dispatching system. So, this isn't impossible to do, but there are lots of aspects to cover here, in particular the translation between the SRT events and the library's events and the readiness status updates - both these are deeply rooted into the SRT library. You'd need first some abstraction layer to replace the direct calls to the epoll system with the abstract event system, and then create the translation layer for at least some of the well known event systems, and of course, for the SRT epoll as default. I personally think it's worth it, but it's really a lot of work.

ethouris avatar Jun 28 '22 10:06 ethouris

But maybe you wouldn't have an event system as such at the SRT level.

On the sender side, you'd call a function to pass SRT a packet. Internally, SRT then does what it needs to do to add a header or whatever, register the packet with its own internal systems, and then at some point, maybe even straightaway, call the send callback asking for the packet to be sent across the network. Equally, the underlying SRT engine might at some arbitrary point call this callback again to service a retry request it has received from the receiver.

On the receiver side, the SRT system would expect to be given data as it arrives over the network. It then does all the voodoo stuff, e.g. is the packet out of order, is it too late, do I need to request a resend of something? etc etc but at some future point in time SRT, via another thread/threads calls a callback with the next packet to be handled by the application. Equally, if SRT on the receiver side wants to request packet(s) be resent, because they're missing and there is still time, or if it otherwise needs to notify the sender of its state or something, it can call another callback requesting to send some UDP data back to the sender.

So SRT still has various threads monitoring state etc, but instead of using events to indicate to the application it just has threads that call callbacks asking the application on either side to send data, and functions that can be called at the application level to provide the engine with data.

The end result would be SRT becomes focused only on the protocol, and its up to the user to essentially plug it into their event/socket/network system of choice.

I may be misunderstanding how close the SRT protocol is/needs to be to the underlying UDP network layer, however...

oviano avatar Jun 28 '22 10:06 oviano

Actually I'm even more confused now than before. First you say about the sender side (send request from the API actually stores the packet in the buffer, although in the live mode it would be picked up by the sender thread almost always immediately), but then a "callback" would be needed to send? I think a more general solution could make sense here by abstracting out the CChannel class - which is doable and would open a gate for some more features, but this isn't on the table yet.

ethouris avatar Jun 29 '22 07:06 ethouris

Sender side:

  1. application calls an API function with the data it wants sent to the receiver
  2. SRT adds any header and likely wants to send this right away so calls a callback with the modified (header added) buffer
  3. Callback implements actual socket send of the buffer
  4. SRT can also call the callback if it decides a packet needs resending
  5. application passes any received data from the socket to the API

Receiver side

  1. Application passes any received data from the socket to the API
  2. API processes packet, decides at some point that the data in the packet can be presented so calls a callback supplied by the application
  3. SRT might decide it needs to send something to the sender, so calls a callback with the data and the application once again does the actual socket sens

So SRT isn’t concerned at all with the physical sensing or receiving of data. Application manages the socket and all epoll etc and passes any received data to SRT. In return SRT passes data back to the application when time to present/decode. When SRT needs to send something to the other side it calls the callback.

oviano avatar Jun 29 '22 07:06 oviano

Ok, so again: the only part of code that is occupied with the communication with the network is the CChannel class. No other part of the code does anything with the network. Check for yourself.

Beside this it works more-less as you described, just a little bit more things are involved.

ethouris avatar Jun 29 '22 08:06 ethouris