diff options
Diffstat (limited to 'backends/sacn.c')
-rw-r--r-- | backends/sacn.c | 120 |
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"); |