module-rtp-sink: Add ability to add / remove receivers through commands

This makes it possible to dynamically add / remove receivers, which is
necesary for sending to multiple receivers. Mixed multi- and unicast
receivers are possible. Example pw-cli calls (56 is the ID of the RTP
sink node):

pw-cli c 56 User '{ extra="{ \"command.id\" : \"add-receiver\" , \"destination.ip\" : \"10.42.0.1\", \"destination.port\" : 55001 }" }'
pw-cli c 56 User '{ extra="{ \"command.id\" : \"remove-receiver\", \"destination.ip\" : \"10.42.0.1\" }" }'
pw-cli c 56 User '{ extra="{ \"command.id\" : \"clear-receivers\" }" }'

Commands and their arguments:

* "add-receiver" : Adds a receiver to the sink's list. If the given
  IP address <-> port combination was already added, the command is
  logged, but otherwise ignored. Arguments:
  - "destination.ip" : IP address to send data to. Can be a uni- or
    multicast address, but must be a valid address.
  - "destination.port" : Port to send data to. Must be valid.
  - "local.ifname", "source.ip", "net.ttl", "net.dscp", "net.loop" :
    These are all optional, and work just like in the RTP sink
    module's properties.

* "remove-receiver" : Removes a receiver from the sink's list. The
  receiver is identified by the given IP address. A port can optionally
  be specified as well. If it isn't, then the first receiver with that IP
  address is removed. If no matching receiver is in the sink's list,
  this command does nothing. Arguments:
  - "destination.ip" : IP address to send data to. Can be a uni- or
    multicast address, but must be a valid address.
  - "destination.port" : Port to send data to. This is optional. But, if
    it is set, it must be a valid port number.

* "clear-receivers" : Removes all receivers from the sink's list. If the
  list is empty, this does nothing. This command has no arguments.

If the RTP sink module is created with the "destination.ip" and
"destination.port" properties set, it behaves as if "add-receiver" were
called right after the module was initialized. This means that if none
of these commands are used, the module behaves just as it did prior to
this patch. Note that the "remove-receivers" command can remove this
initial receiver as well.

If no receivers are added, the module continues to work normally.
Adding and removing receivers mid-operation is supported.

NOTE: "destination.ip") handling in stream_props_changed() is removed,
since it never really did anything other than change the param value.
This commit is contained in:
Carlos Rafael Giani 2026-06-18 22:07:30 +02:00 committed by Wim Taymans
parent f2ccfe12c2
commit d8f5ed0c13
3 changed files with 626 additions and 80 deletions

View file

@ -45,6 +45,7 @@ PW_LOG_TOPIC_EXTERN(mod_topic);
#define rtp_stream_emit_open_connection(s,r) rtp_stream_emit(s, open_connection, 0,r)
#define rtp_stream_emit_close_connection(s,r) rtp_stream_emit(s, close_connection, 0,r)
#define rtp_stream_emit_param_changed(s,i,p) rtp_stream_emit(s, param_changed,0,i,p)
#define rtp_stream_emit_command(s,cmd) rtp_stream_emit(s, command,0,cmd)
#define rtp_stream_call(s,m,v,...) spa_callbacks_call_fast(&s->rtp_callbacks, \
struct rtp_stream_events, m, v, ##__VA_ARGS__)
@ -584,11 +585,18 @@ static void on_stream_param_changed (void *d, uint32_t id, const struct spa_pod
}
};
static void on_stream_command(void *d, const struct spa_command *command)
{
struct impl *impl = d;
rtp_stream_emit_command(impl, command);
}
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.destroy = stream_destroy,
.state_changed = on_stream_state_changed,
.param_changed = on_stream_param_changed,
.command = on_stream_command,
.io_changed = stream_io_changed,
};
@ -1225,3 +1233,10 @@ void rtp_stream_update_process_latency(struct rtp_stream *s,
update_latency_params(impl);
}
int rtp_stream_run_in_data_loop(struct rtp_stream *s, spa_invoke_func_t func,
uint32_t seq, const void *data, size_t size, void *user_data)
{
struct impl *impl = (struct impl*)s;
return pw_loop_locked(impl->data_loop, func, seq, data, size, user_data);
}