.\"
-.\" Copyright (C) 2004-2009 Richard Kettlewell
+.\" Copyright (C) 2004-2011, 2013 Richard Kettlewell
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
New values of this option may be picked up from the configuration file even
without a reload.
.TP
+.B rtp_mode \fIMODE\fR
+The network transmission mode for the \fBrtp\fR backend.
+Possible values are:
+.RS
+.TP
+.B unicast
+Unicast transmission to the address given by \fBbroadcast\fR.
+.TP
+.B broadcast
+Broadcast transmission to the address given by \fBbroadcast\fR.
+.TP
+.B multicast
+Multicast transmission to the address given by \fBbroadcast\fR.
+.TP
+.B request
+Unicast transmission to addresses requested by clients.
+.TP
+.B auto
+Choose one of the above based on the destination address.
+This is the default, for backwards compatibility reasons.
+.RE
+.TP
.B sample_format \fIBITS\fB/\fIRATE\fB/\fICHANNELS
Describes the sample format expected by the \fBspeaker_command\fR (below).
The components of the format specification are as follows:
.\"
-.\" Copyright (C) 2004-2011 Richard Kettlewell
+.\" Copyright (C) 2004-2011, 2013 Richard Kettlewell
.\"
.\" This program is free software: you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.B rtp\-address
Report the RTP broadcast (or multicast) address, in the form \fIADDRESS
PORT\fR.
+If the server is in RTP request mode then the result is \fB- -\fR.
This command does not require authentication.
.TP
+.B rtp\-cancel
+Cancel the unicast RTP stream associated with this connection.
+.TP
+.B rtp\-request \fIADDRESS PORT\fR
+Request that an RTP stream be transmitted to a given destination address.
+Only one unicast stream may be requested per connection.
+WHen the connection is closed the stream is terminated.
+.TP
.B scratch \fR[\fIID\fR]
Remove the track identified by \fIID\fR, or the currently playing track if no
\fIID\fR is specified.
return 0;
}
+int disorder_rtp_cancel(disorder_client *c) {
+ return disorder_simple(c, NULL, "rtp-cancel", (char *)NULL);
+}
+
+int disorder_rtp_request(disorder_client *c, const char *address, const char *port) {
+ return disorder_simple(c, NULL, "rtp-request", address, port, (char *)NULL);
+}
+
int disorder_scratch(disorder_client *c, const char *id) {
return disorder_simple(c, NULL, "scratch", id, (char *)NULL);
}
/** @file lib/client-stubs.h
* @brief Generated client API
*
- * Don't include this file directly - use @ref client.h instead.
+ * Don't include this file directly - use @ref lib/client.h instead.
*/
/** @brief Adopt a track
*/
int disorder_rtp_address(disorder_client *c, char **addressp, char **portp);
+/** @brief Cancel RTP stream
+ *
+ *
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_rtp_cancel(disorder_client *c);
+
+/** @brief Request a unicast RTP stream
+ *
+ *
+ *
+ * @param c Client
+ * @param address Destination address
+ * @param port Destination port number
+ * @return 0 on success, non-0 on error
+ */
+int disorder_rtp_request(disorder_client *c, const char *address, const char *port);
+
/** @brief Terminate the playing track.
*
* Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.
/*
* This file is part of DisOrder.
- * Copyright (C) 2004-2010 Richard Kettlewell
+ * Copyright (C) 2004-2011, 2013 Richard Kettlewell
* Portions copyright (C) 2007 Mark Wooding
*
* This program is free software: you can redistribute it and/or modify
{ C(remote_userman), &type_boolean, validate_any },
{ C(replay_min), &type_integer, validate_non_negative },
{ C(rtp_delay_threshold), &type_integer, validate_positive },
+ { C(rtp_mode), &type_string, validate_any },
{ C(rtp_verbose), &type_boolean, validate_any },
{ C(sample_format), &type_sample_format, validate_sample_format },
{ C(scratch), &type_string_accum, validate_isreg },
c->broadcast_from.af = -1;
c->listen.af = -1;
c->connect.af = -1;
+ c->rtp_mode = xstrdup("auto");
return c;
}
/** @brief Rescan on (un)mount */
int mount_rescan;
+ /** @brief RTP mode */
+ const char *rtp_mode;
+
/* derived values: */
int nparts; /* number of distinct name parts */
char **parts; /* name part list */
return simple(c, no_response_opcallback, (void (*)())completed, v, "revoke", (char *)0);
}
+int disorder_eclient_rtp_cancel(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "rtp-cancel", (char *)0);
+}
+
+int disorder_eclient_rtp_request(disorder_eclient *c, disorder_eclient_no_response *completed, const char *address, const char *port, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "rtp-request", address, port, (char *)0);
+}
+
int disorder_eclient_scratch(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v) {
return simple(c, no_response_opcallback, (void (*)())completed, v, "scratch", id, (char *)0);
}
/** @file lib/client-stubs.h
* @brief Generated asynchronous client API
*
- * Don't include this file directly - use @ref client.h instead.
+ * Don't include this file directly - use @ref lib/eclient.h instead.
*/
/** @brief Adopt a track
*/
int disorder_eclient_revoke(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+/** @brief Cancel RTP stream
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_rtp_cancel(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Request a unicast RTP stream
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param address Destination address
+ * @param port Destination port number
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_rtp_request(disorder_eclient *c, disorder_eclient_no_response *completed, const char *address, const char *port, void *v);
+
/** @brief Terminate the playing track.
*
* Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.
static void rtp_configure(void) {
char buffer[64];
+ uaudio_set("rtp-mode", config->rtp_mode);
rtp_set_netconfig("rtp-destination-af",
"rtp-destination",
"rtp-destination-port", &config->broadcast);
#! /usr/bin/perl -w
#
# This file is part of DisOrder.
-# Copyright (C) 2010-11 Richard Kettlewell
+# Copyright (C) 2010-11, 13 Richard Kettlewell
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
[["string", "address", "Where to store hostname or address"],
["string", "port", "Where to store service name or port number"]]);
+simple("rtp-cancel",
+ "Cancel RTP stream",
+ "",
+ []);
+
+simple("rtp-request",
+ "Request a unicast RTP stream",
+ "",
+ [["string", "address", "Destination address"],
+ ["string", "port", "Destination port number"]]);
+
simple("scratch",
"Terminate the playing track.",
"Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.",
int server_stop(ev_source *ev, int fd);
/* Stop listening on @fd@ */
+void rtp_request(const struct sockaddr_storage *sa);
+void rtp_request_cancel(const struct sockaddr_storage *sa);
+
extern int volume_left, volume_right; /* last known volume */
extern int wideopen; /* blindly accept all logins */
void *body_u;
/** @brief Accumulating body */
struct vector body[1];
+
+ /** @brief Nonzero if an active RTP request exists */
+ int rtp_requested;
+
+ /** @brief RTP destination (if @ref rtp_requested is nonzero) */
+ struct sockaddr_storage rtp_destination;
};
/** @brief Linked list of connections */
static const char *noyes[] = { "no", "yes" };
-/** @brief Remove a connection from the connection list */
+/** @brief Remove a connection from the connection list
+ *
+ * This is a good place for cleaning things up when connections are closed for
+ * any reason.
+ */
static void remove_connection(struct conn *c) {
struct conn **cc;
+ if(c->rtp_requested) {
+ rtp_request_cancel(&c->rtp_destination);
+ c->rtp_requested = 0;
+ }
for(cc = &connections; *cc && *cc != c; cc = &(*cc)->next)
;
if(*cc)
if(api == &uaudio_rtp) {
char **addr;
- netaddress_format(&config->broadcast, NULL, &addr);
- sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
- quoteutf8(addr[1]),
- quoteutf8(addr[2]));
+ if(!strcmp(config->rtp_mode, "request"))
+ sink_printf(ev_writer_sink(c->w), "252 - -\n");
+ else {
+ netaddress_format(&config->broadcast, NULL, &addr);
+ sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
+ quoteutf8(addr[1]),
+ quoteutf8(addr[2]));
+ }
} else
sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
return 1;
}
+static int c_rtp_cancel(struct conn *c,
+ char attribute((unused)) **vec,
+ int attribute((unused)) nvec) {
+ if(!c->rtp_requested) {
+ sink_writes(ev_writer_sink(c->w), "550 No active RTP stream\n");
+ return 1;
+ }
+ rtp_request_cancel(&c->rtp_destination);
+ c->rtp_requested = 0;
+ sink_writes(ev_writer_sink(c->w), "250 Cancelled RTP stream\n");
+ return 1;
+}
+
+static int c_rtp_request(struct conn *c,
+ char **vec,
+ int attribute((unused)) nvec) {
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ .ai_flags = AI_NUMERICHOST|AI_NUMERICSERV,
+ };
+ struct addrinfo *res;
+ int rc = getaddrinfo(vec[0], vec[1], &hints, &res);
+ if(rc) {
+ disorder_error(0, "%s port %s: %s",
+ vec[0], vec[1], gai_strerror(rc));
+ sink_writes(ev_writer_sink(c->w), "550 Invalid address\n");
+ return 1;
+ }
+ disorder_info("%s requested RTP stream to %s %s", c->who, vec[0], vec[1]);
+ /* TODO might be useful to tighten this up to restrict clients to targetting
+ * themselves only */
+ if(c->rtp_requested) {
+ rtp_request_cancel(&c->rtp_destination);
+ c->rtp_requested = 0;
+ }
+ memcpy(&c->rtp_destination, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ rtp_request(&c->rtp_destination);
+ c->rtp_requested = 1;
+ sink_writes(ev_writer_sink(c->w), "250 Initiated RTP stream\n");
+ // TODO teardown on connection close
+ return 1;
+}
+
static int c_cookie(struct conn *c,
char **vec,
int attribute((unused)) nvec) {
{ "resume", 0, 0, c_resume, RIGHT_PAUSE },
{ "revoke", 0, 0, c_revoke, RIGHT_READ },
{ "rtp-address", 0, 0, c_rtp_address, 0 },
+ { "rtp-cancel", 0, 0, c_rtp_cancel, 0 },
+ { "rtp-request", 2, 2, c_rtp_request, RIGHT_READ },
{ "schedule-add", 3, INT_MAX, c_schedule_add, RIGHT_READ },
{ "schedule-del", 1, 1, c_schedule_del, RIGHT_READ },
{ "schedule-get", 1, 1, c_schedule_get, RIGHT_READ },