aboutsummaryrefslogtreecommitdiffhomepage
path: root/backends/sacn.c
diff options
context:
space:
mode:
Diffstat (limited to 'backends/sacn.c')
-rw-r--r--backends/sacn.c120
1 files changed, 81 insertions, 39 deletions
diff --git a/backends/sacn.c b/backends/sacn.c
index ff2b61e..bd5c75a 100644
--- a/backends/sacn.c
+++ b/backends/sacn.c
@@ -28,12 +28,14 @@ static struct /*_sacn_global_config*/ {
size_t fds;
sacn_fd* fd;
uint64_t last_announce;
+ uint32_t next_frame;
} global_cfg = {
.source_name = "MIDIMonster",
.cid = {'M', 'I', 'D', 'I', 'M', 'o', 'n', 's', 't', 'e', 'r'},
.fds = 0,
.fd = NULL,
- .last_announce = 0
+ .last_announce = 0,
+ .next_frame = 0
};
MM_PLUGIN_API int init(){
@@ -46,6 +48,7 @@ MM_PLUGIN_API int init(){
.handle = sacn_set,
.process = sacn_handle,
.start = sacn_start,
+ .interval = sacn_interval,
.shutdown = sacn_shutdown
};
@@ -63,6 +66,13 @@ MM_PLUGIN_API int init(){
return 0;
}
+static uint32_t sacn_interval(){
+ if(global_cfg.next_frame){
+ return global_cfg.next_frame;
+ }
+ return SACN_KEEPALIVE_INTERVAL;
+}
+
static int sacn_listener(char* host, char* port, uint8_t flags){
int fd = -1, yes = 1;
if(global_cfg.fds >= MAX_FDS){
@@ -87,7 +97,6 @@ static int sacn_listener(char* host, char* port, uint8_t flags){
global_cfg.fd[global_cfg.fds].fd = fd;
global_cfg.fd[global_cfg.fds].universes = 0;
global_cfg.fd[global_cfg.fds].universe = NULL;
- global_cfg.fd[global_cfg.fds].last_frame = NULL;
if(flags & mcast_loop){
//set IP_MCAST_LOOP to allow local applications to receive output
@@ -190,24 +199,31 @@ static int sacn_configure_instance(instance* inst, char* option, char* value){
data->unicast_input = strtoul(value, NULL, 10);
return 0;
}
+ else if(!strcmp(option, "realtime")){
+ data->realtime = strtoul(value, NULL, 10);
+ return 0;
+ }
LOGPF("Unknown instance configuration option %s for instance %s", option, inst->name);
return 1;
}
-static instance* sacn_instance(){
- instance* inst = mm_instance();
- if(!inst){
- return NULL;
- }
+static int sacn_instance(instance* inst){
+ sacn_instance_data* data = calloc(1, sizeof(sacn_instance_data));
+ size_t u;
- inst->impl = calloc(1, sizeof(sacn_instance_data));
- if(!inst->impl){
+ if(!data){
LOG("Failed to allocate memory");
- return NULL;
+ return 1;
+ }
+
+ for(u = 0; u < sizeof(data->data.channel) / sizeof(channel); u++){
+ data->data.channel[u].ident = u;
+ data->data.channel[u].instance = inst;
}
- return inst;
+ inst->impl = data;
+ return 0;
}
static channel* sacn_channel(instance* inst, char* spec, uint8_t flags){
@@ -215,7 +231,7 @@ static channel* sacn_channel(instance* inst, char* spec, uint8_t flags){
char* spec_next = spec;
unsigned chan_a = strtoul(spec, &spec_next, 10), chan_b = 0;
-
+
//range check
if(!chan_a || chan_a > 512){
LOGPF("Channel out of range on instance %s: %s", inst->name, spec);
@@ -223,6 +239,11 @@ static channel* sacn_channel(instance* inst, char* spec, uint8_t flags){
}
chan_a--;
+ //check output capabilities
+ if((flags & mmchannel_output) && !data->xmit_prio){
+ LOGPF("Channel %s.%s mapped for output, but instance is not configured for output (no priority set)", inst->name, spec);
+ }
+
//if wide channel, mark fine
if(*spec_next == '+'){
chan_b = strtoul(spec_next + 1, NULL, 10);
@@ -251,7 +272,7 @@ static channel* sacn_channel(instance* inst, char* spec, uint8_t flags){
}
data->data.map[chan_a] = (*spec_next == '+') ? (MAP_COARSE | chan_b) : (MAP_SINGLE | chan_a);
- return mm_channel(inst, chan_a, 1);
+ return data->data.channel + chan_a;
}
static int sacn_transmit(instance* inst){
@@ -294,10 +315,11 @@ static int sacn_transmit(instance* inst){
LOGPF("Failed to output frame for instance %s: %s", inst->name, strerror(errno));
}
- //update last transmit timestamp
+ //update last transmit timestamp, unmark instance
for(u = 0; u < global_cfg.fd[data->fd_index].universes; u++){
- if(global_cfg.fd[data->fd_index].universe[u] == data->uni){
- global_cfg.fd[data->fd_index].last_frame[u] = mm_timestamp();
+ if(global_cfg.fd[data->fd_index].universe[u].universe == data->uni){
+ global_cfg.fd[data->fd_index].universe[u].last_frame = mm_timestamp();
+ global_cfg.fd[data->fd_index].universe[u].mark = 0;
}
}
return 0;
@@ -305,6 +327,7 @@ static int sacn_transmit(instance* inst){
static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){
size_t u, mark = 0;
+ uint32_t frame_delta = 0;
sacn_instance_data* data = (sacn_instance_data*) inst->impl;
if(!num){
@@ -338,6 +361,25 @@ static int sacn_set(instance* inst, size_t num, channel** c, channel_value* v){
//send packet if required
if(mark){
+ if(!data->realtime){
+ //find output instance data
+ for(u = 0; u < global_cfg.fd[data->fd_index].universes; u++){
+ if(global_cfg.fd[data->fd_index].universe[u].universe == data->uni){
+ break;
+ }
+ }
+
+ frame_delta = mm_timestamp() - global_cfg.fd[data->fd_index].universe[u].last_frame;
+
+ //check if ratelimiting engaged
+ if(frame_delta < SACN_FRAME_TIMEOUT){
+ global_cfg.fd[data->fd_index].universe[u].mark = 1;
+ if(!global_cfg.next_frame || global_cfg.next_frame > (SACN_FRAME_TIMEOUT - frame_delta)){
+ global_cfg.next_frame = (SACN_FRAME_TIMEOUT - frame_delta);
+ }
+ return 0;
+ }
+ }
sacn_transmit(inst);
}
@@ -389,16 +431,9 @@ static int sacn_process_frame(instance* inst, sacn_frame_root* frame, sacn_frame
if(inst_data->data.map[u] & MAP_MARK){
//unmark and get channel
inst_data->data.map[u] &= ~MAP_MARK;
+ chan = inst_data->data.channel + u;
if(inst_data->data.map[u] & MAP_FINE){
- chan = mm_channel(inst, MAPPED_CHANNEL(inst_data->data.map[u]), 0);
- }
- else{
- chan = mm_channel(inst, u, 0);
- }
-
- if(!chan){
- LOGPF("Active channel %" PRIsize_t " on %s not known to core", u, inst->name);
- return 1;
+ chan = inst_data->data.channel + MAPPED_CHANNEL(inst_data->data.map[u]);
}
//generate value
@@ -473,6 +508,7 @@ static void sacn_discovery(size_t fd){
static int sacn_handle(size_t num, managed_fd* fds){
size_t u, c;
uint64_t timestamp = mm_timestamp();
+ uint32_t synthesize_delta = 0;
ssize_t bytes_read;
char recv_buf[SACN_RECV_BUF];
instance* inst = NULL;
@@ -482,7 +518,7 @@ static int sacn_handle(size_t num, managed_fd* fds){
sacn_frame_root* frame = (sacn_frame_root*) recv_buf;
sacn_frame_data* data = (sacn_frame_data*) (recv_buf + sizeof(sacn_frame_root));
- if(mm_timestamp() - global_cfg.last_announce > SACN_DISCOVERY_TIMEOUT){
+ if(timestamp - global_cfg.last_announce > SACN_DISCOVERY_TIMEOUT){
//send universe discovery pdu
for(u = 0; u < global_cfg.fds; u++){
if(global_cfg.fd[u].universes){
@@ -492,17 +528,29 @@ static int sacn_handle(size_t num, managed_fd* fds){
global_cfg.last_announce = timestamp;
}
- //check for keepalive frames
+ //check for keepalive frames, synthesize frames if necessary
+ global_cfg.next_frame = 0;
for(u = 0; u < global_cfg.fds; u++){
for(c = 0; c < global_cfg.fd[u].universes; c++){
- if(timestamp - global_cfg.fd[u].last_frame[c] >= SACN_KEEPALIVE_INTERVAL){
+ synthesize_delta = timestamp - global_cfg.fd[u].universe[c].last_frame;
+
+ if((global_cfg.fd[u].universe[c].mark
+ && synthesize_delta >= SACN_FRAME_TIMEOUT + SACN_SYNTHESIZE_MARGIN)
+ || synthesize_delta >= SACN_KEEPALIVE_INTERVAL){
instance_id.fields.fd_index = u;
- instance_id.fields.uni = global_cfg.fd[u].universe[c];
+ instance_id.fields.uni = global_cfg.fd[u].universe[c].universe;
inst = mm_instance_find(BACKEND_NAME, instance_id.label);
if(inst){
sacn_transmit(inst);
}
}
+
+ //update next frame request
+ if(global_cfg.fd[u].universe[c].mark
+ && (!global_cfg.next_frame || global_cfg.next_frame > SACN_FRAME_TIMEOUT + SACN_SYNTHESIZE_MARGIN - synthesize_delta)){
+ global_cfg.next_frame = SACN_FRAME_TIMEOUT + SACN_SYNTHESIZE_MARGIN - synthesize_delta;
+ }
+
}
}
@@ -562,7 +610,6 @@ static int sacn_start(size_t n, instance** inst){
if(!global_cfg.fds){
LOG("Failed to start, no descriptors bound");
- free(inst);
return 1;
}
@@ -595,13 +642,15 @@ static int sacn_start(size_t n, instance** inst){
if(data->xmit_prio){
//add to list of advertised universes for this fd
- global_cfg.fd[data->fd_index].universe = realloc(global_cfg.fd[data->fd_index].universe, (global_cfg.fd[data->fd_index].universes + 1) * sizeof(uint16_t));
+ global_cfg.fd[data->fd_index].universe = realloc(global_cfg.fd[data->fd_index].universe, (global_cfg.fd[data->fd_index].universes + 1) * sizeof(sacn_output_universe));
if(!global_cfg.fd[data->fd_index].universe){
LOG("Failed to allocate memory");
goto bail;
}
- global_cfg.fd[data->fd_index].universe[global_cfg.fd[data->fd_index].universes] = data->uni;
+ global_cfg.fd[data->fd_index].universe[global_cfg.fd[data->fd_index].universes].universe = data->uni;
+ global_cfg.fd[data->fd_index].universe[global_cfg.fd[data->fd_index].universes].last_frame = 0;
+ global_cfg.fd[data->fd_index].universe[global_cfg.fd[data->fd_index].universes].mark = 0;
global_cfg.fd[data->fd_index].universes++;
//generate multicast destination address if none set
@@ -617,12 +666,6 @@ static int sacn_start(size_t n, instance** inst){
LOGPF("Registering %" PRIsize_t " descriptors to core", global_cfg.fds);
for(u = 0; u < global_cfg.fds; u++){
- //allocate memory for storing last frame transmission timestamp
- global_cfg.fd[u].last_frame = calloc(global_cfg.fd[u].universes, sizeof(uint64_t));
- if(!global_cfg.fd[u].last_frame){
- LOG("Failed to allocate memory");
- goto bail;
- }
if(mm_manage_fd(global_cfg.fd[u].fd, BACKEND_NAME, 1, (void*) u)){
goto bail;
}
@@ -643,7 +686,6 @@ static int sacn_shutdown(size_t n, instance** inst){
for(p = 0; p < global_cfg.fds; p++){
close(global_cfg.fd[p].fd);
free(global_cfg.fd[p].universe);
- free(global_cfg.fd[p].last_frame);
}
free(global_cfg.fd);
LOG("Backend shut down");