# Abstract

The general concept of the socket groups means that a separate entity,
parallel to a socket, is provided, and the operation done on a group
will be implemented using appropriate operations done on underlying
sockets.

The groups types generally split into two categories:

1. Bonding groups.

This group category is meant to utilize multiple connections in order
to have a group-wise connection. How particular links are then utilized
to make a group-wise sending, depends on the particular group type. Within
this category we have the following group types:

   - Broadcast: send the stream over all links simultaneously
   - Backup: use one link, but be prepared for a quick switch if broken
   - Balancing: utilize all links, but one payload is sent only over one link

Bonding category groups predict that a group is mirrored on the peer network
node, so all particular links connect to the endpoint that always resolves to
the same target application. Just possibly every link uses a different network
path.

2. Dispatch groups.

This category contains currently only one Multicast type (**CONCEPT!**).

Multicast group has a behavior dependent on the connection side and it is
predicted to be only used in case when the listener side is a stream sender
with possibly multiple callers being stream receivers. It utilizes the UDP
multicast feature in order to send payloads, while the control communication
is still sent over the unicast link.

Details for the group types:


## 1. Broadcast

This is the simplest bonding group type. The payload sent for a group will be
then sent over every single link in the group simultaneously. On the reception
side the payloads will be sorted out and redundant packets that have arrived
over another link are simply discarded.

This group is predicted to solve the link disturbance problems with no latency
penalty - when one link gets broken, another still works with no extra delay
and no observable disturbances for the client, as long as at least one link is
up and running.

A drawback of this method is that it always utilizes the full capacity of all
links in the group, whereas only one link at a time delivers any useful data.
Every next link in this group gives then another 100% overhead.


## 2. Backup

This solution is more complicated and more challenging for the settings,
and in contradiction to Broadcast group, it costs some penalties.

In this group, only one link out of member links is used for transmission
in a normal situation. Other links may start being used when there's happening
an event of "disturbance" on a link, which makes it considered "unstable". This
term is introduced beside "broken" because SRT normally uses 5 seconds to be
sure that the link is broken, and this is way too much to be used as a latency
penalty, if you still want to have a relatively low latency.

Because of that there's a configurable timeout (with `SRTO_GROUPSTABTIMEO`
option), which is the maximum time distance between two consecutive responses
sent from the receiver back to the sender. If this time was exceeded, the link
is considered unstable. This can mean either some short-living minor
disturbance, as well as that the link is broken, just SRT hasn't a proof of
that yet.

At the moment when one link becomes unstable, another link is immediately
activated, and all packets that have been kept in the sender buffer since
the last ACK are first sent. Since this moment there are two links active
until the moment when the matter finally resolves - either the unstable
link will become stable again, or it will be broken.

The state maintenance always keep up to the following rules:

a) If you happen to have more than one link stable, choose the "best" one
and silence the others. Silencing means that the link is inactive and
payloads are not being sent over it, but the connection is still maintained
and remains ready to take over if there is a necessity.

b) Unstable links continue to be used no matter that it may mean parallel
sending for a short time. This state should last at most as long as it takes
for SRT to determie the link broken - either by getting the link broken by
itself, or by closing the link when it's remaining unstable too long time.

This mode allows also to set link priorities - the greater, the more preferred.
This priority decides mainly, which link is "best" and which is selected to
take over transmission over a broken link before others, as well as which
links should remain active should multiple links be stable at a time.
If you don't specify priorities, the second connected link need not
take over sending, although as this is resolved through sorting, then
whichever link out of those with the same priority would take over when
all links are stable is undefined.

Note that this group has an advantage over Broadcast in that it allows you
to implement link redundancy with a very little overhead, as it keeps the
extra link utilization at minimum. It costs you some penalties, however:

1. Latency penalty. The latency set on the connection used with backup
groups must be at minimum twice the value of `SRTO_GROUPSTABTIMEO` option,
or might even need to be higher in case of high bitrates - otherwise the
switch into the backup link connected with resending all non-ACK-ed packets
might not be on time as required to play them. Your latency setting must be
able to compensate not only usual loss-retransmission time, but also the
time to realize that the link might be broken and time required for resending
all unacknowledged packets, before the time to play comes for the received
packets. If this time isn't met, packets will be dropped and your advantage
of having the backup link might be impaired. According to the tests on the
local network it turns out that the most sensible unstability timeout is about
50ms, while normally ACK timeout is 30ms, so extra 100ms latency tax seems to
be an absolute minimum.

2. Bandwidth penalty. Note that in case when the Backup group activates
another link, it must resend all packets that haven't been acknowledged,
which is simply the least risk taken for a case that a link got suddenly
broken. However, how many packets have been collected, depends on a luck,
and worst case scenario it may need to resend as many packets as it is
normally collected between two ACK events - in case when the link got broken
exactly at the moment when packets were about to be acknowledged. The
link switch always means a large burst of packets to be sent at that
moment - so the mechanism needs large enough time to send them and to
consider them for delivery. However, if your bandwidth limit is too strong,
sending these packets might be dampened possibly too much to live up to
the required time to play. It is unknown as to what recommendations should
be used for that case, although it is usually more required than to
compensate a burst for retransmission and also the maximum burst size
is dependent on the bitrate, in particular, how many packets would be
collected between two acknowledgement events. It might be not that tough
as it seems from this description, as it's about starting a transmission
over an earlier not used link, so there's some chance that the link will
withstand the initial high burst of packets, while then the bitrate will
become stable - but still, some extra latency might be needed to compensate
any quite probable packet loss that may occur during this process.


## 3. Balancing

The idea of balancing means that there are multiple network links used for
carrying out the same transmission, however a single input signal should
distribute the incoming packets between the links so that one link can
leverage the bandwith burden of the other. Note that this group is not
directly used as protection - it is normally intended to work with a
condition that a single link out of all links in the group would not be
able to withstand the bitrate of the signal. In order to utilize a
protection, the mechanism should quickly detect a link as broken so
that packets lost on the broken link can be resent over the others,
but no such mechanism has been provided for balancing group.

As there could be various ways as to how to implement balancing
algorithm, there's a framework provided to implement various methods,
and two algorithms are currently provided:

1. `plain` (default). This is a simple round-robin - next link selected
to send the next packet is the oldest used so far.

2. `window`. This algorithm is performing cyclic measurement of the
minimum flight window and this way determines the "cost of sending"
of a packet over particular link. The link is then "paid" for sending
a packet appropriate "price", which is collected in the link's "pocket".
To send the next packet the link with lowest state of the "pocket" is
selected. The "cost of sending" measurement is being repeated once per
a time with a distance of 16 packets on each link.

There are possible also other methods and algorithms, like:

a) Explicit share definition. You declare, how much bandwidth you expect
the links to withstand as a percentage of the signal's bitrate. This
shall not exceed 100%. This is merely like the above Window algorithm,
but the "cost of sending" is defined by this percentage.

b) Bandwidth measurement. This relies on the fact that the current
sending on particular link should use only some percentage of its
overall possible bandwidth. This requires a reliable way of measuring
the bandwidth, which is currently not good enough yet. This needs to
use a similar method as in "window" algorithm, that is, start with
equal round-robin and then perform actively a measurement and update
the cost of sending by assigning so much of a share of the signal
bitrte as it is represented by the share of the link in the sum of
all maximum bandwidth values from every link.


## 4. Multicast (NOT IMPLEMENTED - a concept)

This group - unlike all others - is not intended to send one signal
between two network nodes over multiple links, but rather a method of
receiving a data stream sent from a stream server by multiple receivers.

Multicast sending is using the feature of UDP multicast, however the
connection concept is still in force. The concept of multicast groups
is predicted to facilitate the multicast abilities provided by the router
in the LAN, while still maintain the advantages of SRT.

When you look at the difference that UDP multicast provides you towards
a dedicated peer-to-peer sending, there are two:

* You can join a running transmission at any time, without having
the sender do something especially for you (the router IGMP subscription
does the whole job).

* The data stream is sent exactly ONCE from the stream sender to the
router, while the router sends also one data stream to the switch. How
much of a burden to the rest it is, depends then on the switch: older
ones get one signal to be picked up by those interested, newer ones
pass through this signal to only those nodes that have IGMP subscription.
Nevertheless, the advantage here is that the same data stream is sent
once instead of being sent multiple times over the same link, at least
between the stream sender and the router.

The multicast groups in SRT are intended to use this very advantage.

While the connection still must be maintained as before, the dedicated
UDP link that results from it is to carry out only the control traffic. For the
data traffic there would be a UDP multicast group IP address established and
all nodes that connect to the stream sender using a multicast group will then
receive the data stream from the multicast group.

This method has limitations on the connection setup. You should then
make a listener on the side where you want to have a stream sender, and
set it up for multicast group. Then, a connection is established over
an individual link, as usual. But beside the data that would be sent
over a dedicated link, the data being sent to the group on the sender
side will be actually sent to the multicast address (unlike in Backup
and Broadcast groups, where these are normally sent over the channel
that particular socket is using). The connecting receiver party is then
automatically subscribed to this group and it receives the data packets
over there, just as if this would be a second channel over which the
group is able to receive data.

Note that sending the data over a single link is still possible and
likely used for retransmission. The retransmission feature is still
handled on a single link, although most likely it can be allowed that
if more than 2 links report a loss of the exactly same packet, the
retransmission may use the multicast link instead of the individual
link - whichever would spare more bandwidth would be used.

Potential implementation:

Instead of `groupconnect` option set to true, you have to set the
`multicast` option to a nonempty string of any contents (just limited
to 512 characters). This string value will be a key under which the
group ID is recorded. The first connecting client will be identified
as joining this group and this way a new group will be created and
its ID recorded under this key. This is in order to allow library
users to use the feature of listener callback so that a group name
may be decided upon per user or stream resource specified.

Once the group is known, and the connection is to be accepted, the
newly accepted socket is joined to the group. The accept conditions
are just like before in the groups: the group is reported from
`srt_accept` when the first client has connected.

Once the group connection is made, the sender side can use the
group ID to send the data. Note that the group on the listener side
collects multiple sockets representing particular clients, so
sending will be for the sake of each of them. However the sending
function will not send to every individual socket, as it is with
broadcast group, but rather an extra socket will be created
inside the group, and this one will be used to send packets to
the multicast group, as configured during the handshake.

It should be decided on the listener side, which IGMP group will
be used for transmission. Clients do not know it, and:

 - for simple single-signal cases, you have just one IGMP
group configured for the listener and it's used for every
client
 - listener callback may decide particular IGNMP group to
be used for particular signal

The listener side will inform the client in the handshake,
which IGMP group will be used for transmission so that the
client can set up listening on it (join IGMP group in particular).

The client side should have then two things:

1. The group with its own socket (and queues).
2. The group will contain always one member, this time.
3. When reading packets from the queue of the socket bound
to the IGMP group, the packet will be translated and dispatched
to the socket.

What is important here is that the packet sent by the listener
side will look exactly the same no matter to which client it is
predicted - which means that the SRT header will contain the same
data. This is where groups come in: the "target ID" in the header
will have the value of the group.

In total, the listener sends the following data to the client:

1. IGMP group's IP address and port
2. group ID that will be the same on both sides (if the group ID
already exists on the client's application, the connection will be
rejected).

The listener side will then send payload packets to the IGMP group,
however all control packets will be still sent the same way as before,
that is, over a direct connection.


# Socket groups in SRT

The general idea of groups is that there can be multiple sockets belonging
to a group, and various operations, normally done on single sockets, can
be simply done on a group. How an operation done on a group is then 
implemented by doing operations on sockets, depends on the group type and
the operation itself.

Groups have IDs exactly the same as sockets do. For group support there's a
feature added: the SRTSOCKET type is alias to `int32_t`, and it has appropriate
bit set to define that this ID designates a group, known as `SRTGROUP_MASK`.
You can test it by `id & SRTGROUP_MASK` to know if the value of `SRTSOCKET`
type designates a single socket or a group.

For groups you simply use the same operations as for single socket - it will
be internally dispatched appropriate way, depending on what kind of entity
was used. For example, when you send a payload, it will be effectively sent:

- For Broadcast group, over all sockets
- For Backup group, over all currently active links
- For Balancing group, over a currently selected link
- For Multicast group, over an extra socket to the multicast group

Similarly, the reading operation will read over all links and due to
synchronized sequence numbers use them to decide the payload order: when
the very next packet has been receiver over any link, it will be delivered,
and when older than that, it will be discarded. The TSBPD mechanism is used to
determine the order in case when a packet was decided to be dropped on
particular link. That is, if a packet drop occurs, then simply the same
packet received over another link will be still earlier ready to play.

The difference in reading between groups is that:

- For Broadcast and Backup groups, sequence numbers are synchronized and
used to sort packets out

- For Balancing group, message numbers are used to sort packets out
(sequence numbers are not synchronized)

- For Multicast group, there's only one link at the receiver side group,
just the group contains additional socket that should read from the multicast
group, in case when packets are expected to be read. By having the target
specified as the group id, it gets correctly dispatched to this channel's own
buffer and delivered. For this purpose, however, payloads sent over the
multicast link must have the target defined as the group ID so that all data in
the header look exactly the same way depite being intended to be received by
various different network nodes.


# How to prepare connection for bonded links

In the listener-caller setup, you have to take care of the side separately.

First of all, the caller should require minimum version from the peer
(see `SRTO_MINVERSION` flag) or otherwise the listener won't understand the
handshake extension information concerning socket groups.

The listener socket must have `SRTO_GROUPCONNECT` flag set. There are two
reasons as to why it is required:

1.  This flag **allows** the socket to accept bonded connections. Without this
flag the connection that attempts to be bonded will be rejected.

2. When `srt_accept` function is being called on a listener socket that has
this flag set, the returned numeric ID identifies not a socket, but a group.

The group returned by `srt_accept` is a local group mirroring the remote group
which's ID is received in the handshake. That's also apparently the exact ID
that you should next use for sending or receiving data. Note also that
`srt_accept` returns the group only once upon connection - once there exist at
least one connected socket in the group, there will be no new connections
reported in `srt_accept`, just rather a new socket will suddenly appear in the
group data next time you anywhow read them (data reported from `srt_recvmsg2`,
`srt_sendmsg2` or `srt_group_data`).

When an accepted-off socket is being created and the group for which the request
has come in the handshake is already mirrored in the application, then this
link will be added to the existing group. Otherwise the group is created anew.
This group has a range for a single application.

You rather don't have to maintain the list of bonded connections at the
listener side because still you have no influence on who and when will connect
to you.

On the caller the matter is a little bit more complicated.


# Connect bonded

At first, please remember that the official function to create a socket is now
`srt_create_socket` and it gets no arguments. All previous functions to create
a socket are deprecated. This is also interconnected with a change that you
don't have to know the IP domain of the socket when you create it - it will be
decided only upon the first call of `srt_bind` or `srt_connect`.

In order to have a bonded connection, instead of creating a socket, you have
to create a group. Groups may have various purposes, and not every type of
group is meant to be self-managed. For groups that are not self-managed, the
socket can be only simply added to the group by application, as well as the
application should take care of closing it when broken or no longer needed. The
groups for bonding are always self-managed, that is, they create sockets
automatically upon necessity, and automatically delete sockets that got broken.

For example, this way you create a bonding group of type "broadcast":

	grpid = srt_create_group(SRT_GTYPE_BROADCAST);

This returns a value of SRTSOCKET, which can be then normally used in most of
the socket operations, just like socket ids. In order to establish a connection
within the bonding group, simply do:

	sockid = srt_connect(grpid, address...);

Mind that in distinction to `srt_connect` used on a socket, where it returns 0
when succeeded, this time it returns the socket ID of the socket that has been
created for the purpose of this connection. This is just informative and the
application is given this socket just when it would need it, although it should
never try to do any operation on the socket, including closing it.

In order to create a second link within the bonding group, simply call
`srt_connect` again with the other address that eventually refers to the
same endpoint (note that SRT has no ability to verify it prematurely). Unlike
sockets, a group can be connected many times, and every call to this function
creates a new connection within the frames of this group. Also, as a managed
group, it always creates a new socket when you connect "the group" to the given
address.

The library, on the other hand, doesn't know how many connections you'd like
to maintain, whether the list is constant or can be dynamically modified, or
whether a dead link is not to be revived by some reason - all these things are
out of the interest of the library. It's up to the application to decide
when and by what reason the connection is to be established. All that your
application has to do is to monitor the conenctions (that is, be conscious
about that particular links are up and running or get broken) and take
appropriate action in response.

Therefore it's recommended that your application maintain the bonded link
table. It is completely up to you, if your list of bonded links is static or
may change in time during transmission; what matters is that you can always add
a new connection to the bonding group at any time by calling `srt_connect`,
and when a connection is dead, you'll be informed about it, but the link won't
be automatically revived.

There are some convenience function added though because of inability to do
operations on a single socket in case of groups at the moment when they are
required.

1. `srt_connect_group`. This does something similar to calling `srt_connect`
in a loop for multiple endpoints. However the latter is inappropriate in case
when you use the blocking mode because this would block on the first connection
attempt and will not try the next one until the previous one is connected or
the connection finally fails. This function will then try to connect to all
given endpoints at once and will block until the "group connection state" is
achieved (in case of broadcast group it simply means that at least one link
is connected - other groups may use sometimes more complicated conditions
to be satisfied for it). In non-blocking mode it will simply behave the same
as `srt_connect` run in a loop.

You have to make yourself an array with endpoints, then prepare every endpoint
using `srt_prepare_endpoint` function.

2. `srt_connect_bind`. It does the same as calling first `srt_bind` on the
source address, then `srt_connect` on the destination address, when it's
called for a single socket. When it's called for a group, then the binding
procedure is done on the newly created socket for that connection (and that's
the only way how you can define the outgoing port for a socket that belongs
to a managed group).


# Maintaining link activity

A link can get broken, and the only thing that the library does about it is
make you aware of it. The bonding group, as managed, will simply delete the
broken socket and that's all. Reconnecting of the broken link is completely up
to the application. Your application may also state that the link need not be
revived, so this isn't interesting for the application. If you want to revive
the link and you believe that the connection can be still made, or it's only
broken temporarily, or the link should work, simply connect to this address
again using `srt_connect`.

The simplest way to maintain the status of the sockets in the group is to call:

	srt_group_data(grpid, &sockdata, &sockdata_size);

You have to prepare an array of `SRT_SOCKGROUPDATA` type by yourself, and the
size must be properly set to `sockdata_size` before calling to at least the
number of sockets in the group. The latter is input-output, that is, it will
return the actual size of the array, possibly less than given size. If you pass
too small size, then the required size will be returned in `sockdata_size`, but
the array will not be modified at all. Therefore you must check the returned
size always and catch that case, as if this happens, you should not read data
from the array.

When you call `srt_group_data` and the size of the group is less than your last
remembered one, it means that one of the sockets got broken, which one, you can
check by seeing which of the sockets, that you remembered at the time of
connection, is lacking in the socket group data. Note that socket IDs are
created using a random number and decreasing, so dangling socket IDs will not
be reassigned to correct sockets in a "predictable time" (you'll have to create
and delete sockets about a million times to make it happen).

A recommended way is, however, to use `srt_sendmsg2` and `srt_recvmsg2`
functions, which require `SRT_MSGCTRL` structure. You should place a
`SRT_SOCKGROUPDATA` array into `SRT_MSGCTRL::grpdata` field together with its
size in `SRT_MSGCTRL::grpdata_size`, and the socket information for every
socket will be placed there, including (just once) sockets that were lately
broken and have been deleted. This last information is not present in the
result returned by `srt_group_data` - that is, sockets found broken during
the operation will be only present if you review the array that was filled
by `srt_sendmsg2` or `srt_recvmsg2`.



# Writing data to a bonded link

This is very simple. Call the sending function (recommended is `srt_sendmsg2`)
to send the data, passing group ID in the place of socket ID. By recognizing
the ID as group ID, this will be resolved internally as sending the payload
by approprately using the bonded links as defined for particular group type.

The current implementation for most of the bonding groups (broadcast and
backup) relies on synchronizing the sequence numbers of the packets so that
particular payload goes always with the same sequence number on all links.

Every next link, when the group is already connected, will be first created as
"idle", and it will be activated when the opportunity and the need comes,
and at this very time will be the sequence number adjusted to match the master
sequence number in the group. The same payload will then have the same
sequence number in all sockets in the bonding group, which allows then the
payload to be retrieved in order.

Exceptionally, the current implementation of the balancing group type is
using message numbers because packets must go in order of the sequence numbers
on particular link - and in this group type packets are distributed
throughout the link and never go in the order of scheduling on one link.
Therefore this group uses message numbers for ordering.


# Reading data from a bonded link

This is also simple from the user's perspective. Simply call the reading
function, such as `srt_recvmsg2`, passing the group ID instead of socket
ID.

Also the dillema of blocking and nonblocking is the same thing. With blocking
mode (`SRTO_RCVSYN`), simply wait until your payload is retrieved. The internal
group reading facility will take care that you get your payload in the right
order and at the time to play, and the redundant payloads retrieved over
different links simultaneously will be discarded.


# Checking the status

If you call `srt_sendmsg2` or `srt_recvmsg2`, you'll get the status of every
socket in the group in a part of the `SRT_MSGCTRL` structure, where you should
set the pointer to your array of `SRT_SOCKGROUPDATA` type, and its size, so
that the status can be filled. The size of the array should simply correspond
to the number of bonded links that you use. If the passed size is too small,
then the `grpdata` field will be set to `NULL` by the call, whereas `grpdata_size`
will be always set to the required size.

In this structure you have:

- `id`: socket ID
- `status`: the `SRT_SOCKSTATUS` value (same as obtained by `srt_getsockstate`)
- `result`: result of the operation; if you can see -1 there, it means that you
can see this socket for the last time here, and it is **already closed**.
- `peeraddr`: the address to which the socket was connected

The whole idea of bonding is that a link may get broken at any time and
despite this, your application should be still capable of sending or receiving
data, when at least one link is still alive. It means then that when reading
or writing, the "sub-operation" on one of the sockets in the bond might have
failed due to that the link got broken. But if the operation can continue
successfully on another link, the overall operation is still considered
succeeded, though you might be interested of what happened on the link. Only if
**all links** get broken is the operation considered failed.

There are some differences as to what exactly happens in this case, depending
on the socket group type:

1. Broadcast: the data are being sent over all links anyway, so it doesn't make
much difference except that the broken socket must be taken care of.

2. Backup: usually when a socket is broken, there has been a disturbance
notified much earlier and therefore another link already active. The bonding
group is allowed to keep as many active links as required for at least one
link to remain stable. A broken socket is then simply a possible resolution for
a volatile "unstable" state of the member socket.

3. Balancing: the exact implementation of this group is still underway and
what exactly happens may depend on possible subtype of this group. The current
proof-of-concept implementation behaves like the broadcast group - if one
of the links goes broken, then there are less members to distribute packets
through. Usually the group may have defined some critical conditions that must
be satisfied so that the transmission can continue, mainly basing on that the
critical network capacity needed for transmission is provided. In this case, if
the bonded capacity drops below critical capacity, the whole bonded link should
get broken.

On the listener side, the situation is similar. When you read as listener, you
still read if at least one link is alive, when you send - sending succeeds when
at least one link is alive. When the link gets broken, though, you can't do
anything anyway, so the listener doesn't have to worry about anything, except
the situation that all links are gone, but this is then reported just like in
a situation of one socket - the reading or writing operation fails. Only then
is the connection lost completely and is sending or receiving impossible.

Most important is what the caller side should do. When your link gets broken,
it's up to you to restore it, so you should do `srt_connect` for that link
again and count on that it will be re-established, while simultaneously the
transmission continues over existing links.

A single call to `srt_connect` may also break, like any other operation. When
it happens while another link is running, this link will simply never reach
the state of "idle", and will be deleted before it could be used.

And finally, a group can be closed. In this case, it internally closes first
all sockets that are members of this group, then the group itself is deleted.


# Application support

Currently only the `srt-test-live` application is supporting a syntax for
socket groups.

The syntax is as usual with "source" and "target", however you can specify
multiple sources or multiple targets when you want you want to utilize
socket groups. For this case, the `-g` option is predicted, which should
be best understood as a split-point between specification of source and
target.

The general syntax (there will be also a simplified syntax, so read on) when
you want to have a source signal as a group:

```
./srt-test-live <SRT-link1> <SRT-link2> -g <target-URI>
```

and for sending over a groupwise link:

```
./srt-test-live <source-URI> -g <SRT-link1> <SRT-link2> ...
```

Using multiple SRT URI specifications here is still simplified because the
most direct (but hardest in use) method to specify a groupwise link is:

```
srt:////group?type=<grouptype>&nodes=host1:port1,host2:port2 (&other=options...)
```

Which would be the same as specifying:

```
srt://host1:port1 srt://host2:port2
```

except that options that are group related must be specified the same way
in every URI.

But, as this can be handled with SRT type URI only, and as usually single
socket options apply the same for every link anyway, there's a simplified
syntax - HIGHLY RECOMMENDED - for specifying the group - let's take an
example with additionally setting the `latency` option (REMEMBER: when
specifying the argument with `&` inside in the POSIX shell, you need to enclose
it with apostrophes or put backslash before it):

```
srt://*?type=broadcast&latency=500 host1:5000 host2:5000 host3:5000
```

By specifying the SRT URI with placing `*` as a host, you define this as
a "header URI" for the whole group. The nodes themselves are then specified
in the arguments following this one. The list of nodes is terminated either
by the end of arguments or other options, including the `-g` option that
can be followed by the target URI specification, in case when the group
was specified as a source.

So, a complete command line to read from a group connected over links
to hosts "alpha" and "beta", both with port 5000, and then resending it
to local UDP multicast `239.255.133.10:5999` would be:

```
./srt-test-live srt://*?type=broadcast alpha:5000 beta:5000 -g udp://239.255.133.10:5999
```

Note that this specifies the caller. On the side where you want to
set up a listener where you'd receive a caller's connection you must
set the `groupconnect` option (here let's say you get the source signal
from a device that streams to this machine to port 5555):

```
./srt-test-live udp://:5555 srt://:5000?groupconnect=true
```

At the caller side you can also use some group-member specific options.
Currently there exists only one option dedicated for the Backup group
type, which is priority parameter with a `weight` key. In the simplified
syntax it should be attached to the member parameter:

```
./srt-test-live srt://*?type=backup alpha:5000?weight=0 beta:5000?weight=1 -g udp://239.255.133.10:5999
```

Priorities in the Backup group type define which links should be preferred
over the others when deciding to silence links in a situation of multiple
stable links. Also at the moment when the group is connected, the link with
highest priority is preferred for activation (greatest weight value), and if
another link is connected with higher priority it also takes over.

Here the `beta` host has higher priority than `alpha`, so when both
links are established, it should use the host `beta` to send the data,
switch to `alpha` when this one is broken, and then switch back to `beta`,
when this link is back online.

The stability timeout can be configured through `groupstabtimeo` option.
Note that with increased stability timeout, the necessary latency penalty
grows as well.


