From 809f8ac2e0fc17e956022051782a3f2203cdbfb4 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sat, 1 Feb 2014 22:07:03 +0100 Subject: Working on the new UDP protocol --- host/config.h | 12 ++++ host/gif.c | 190 ------------------------------------------------- host/gif.h | 13 ---- host/main.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++------------ host/net.h | 18 +++++ 5 files changed, 207 insertions(+), 248 deletions(-) create mode 100644 host/config.h delete mode 100644 host/gif.c delete mode 100644 host/gif.h create mode 100644 host/net.h (limited to 'host') diff --git a/host/config.h b/host/config.h new file mode 100644 index 0000000..64a792c --- /dev/null +++ b/host/config.h @@ -0,0 +1,12 @@ +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#define DISPLAY_WIDTH 40 +#define DISPLAY_HEIGHT 16 +#define FRAME_TIMEOUT 5000 /* ms */ +#define UDP_PORT 2323 +#define FRAMERATE 20 /* fps */ +#define TEXT_LOOP_COUNT 3 +#define TEXT_SCROLL_SPEED 3 + +#endif//__CONFIG_H__ diff --git a/host/gif.c b/host/gif.c deleted file mode 100644 index c62a5e2..0000000 --- a/host/gif.c +++ /dev/null @@ -1,190 +0,0 @@ - -#include "gif.h" -#include -#include -#include - -typedef struct { - uint8_t *data; - unsigned int index; - unsigned int length; -} readBuffer_t; - -int gif_buffer_input (GifFileType *gif, GifByteType *dest, int n){ - readBuffer_t *rb = gif->UserData; - if(rb->index+n > rb->length) - n = rb->length - rb->index; - memcpy(dest, rb->data + rb->index, n); - rb->index += n; - return n; -} - -struct _gifAnimationState { - int idx; - GifFileType *gif; - framebuffer_t *frame; -}; - -void color_fill(color_t *dest, color_t src, size_t size){ - /* Some magic to reduce the amount of calls to small-block memcpys */ - dest[0] = src; - size_t i = 1; - size_t j = 2; - while(j < size){ - memcpy(dest+i, dest, i*sizeof(color_t)); - i = j; - j *= 2; - } - memcpy(dest+i, dest, (size-i)*sizeof(color_t)); -} - -gifAnimationState_t *gif_read(uint8_t *buf, size_t buflength){ - color_t *fb = 0; - gifAnimationState_t *st = malloc(sizeof(gifAnimationState_t)); - if(!st){ - fprintf(stderr, "Failed to allocate %lu bytes\n", sizeof(*st)); - return 0; - } - - readBuffer_t readBuf = {buf, 0, buflength}; - int err = 0; - GifFileType *gif = DGifOpen(&readBuf, gif_buffer_input, &err); - if(err){ - fprintf(stderr, "Could not open GIF: %s\n", GifErrorString(err)); - goto error; - } - - err = DGifSlurp(gif); - if(err){ - fprintf(stderr, "Could not read GIF data: %s\n", GifErrorString(err)); - goto error; - } - - unsigned int framesize = gif->SWidth*gif->SHeight; - if(framesize == 0){ /* Can this actually happen? */ - fprintf(stderr, "Invalid 0*0px gif\n"); - goto error; - } - fb = calloc(framesize, sizeof(color_t)); - if(!fb){ - fprintf(stderr, "Failed to allocate framebuffer for GIF (%lu bytes)\n", framesize*sizeof(color_t)); - goto error; - } - if(gif->SColorMap){ /* Initially fill framebuffer with background color */ - GifColorType *col = gif->SColorMap[gif->SBackGroundColor].Colors; - color_t c = {col->Red, col->Green, col->Blue, 255}; - color_fill(fb, c, framesize); - }else{ - /* Set all pixels to a transparent black */ - memset(fb, 0, framesize*sizeof(color_t)); - } - - st->gif = gif; - st->idx = 0; - st->frame = malloc(sizeof(framebuffer_t)); - if(!st->frame){ - fprintf(stderr, "Failed to allocate %lu bytes\n", sizeof(framebuffer_t)); - goto error; - } - st->frame->data = fb; - st->frame->w = gif->SWidth; - st->frame->h = gif->SHeight; - return st; -error: - free(fb); - free(st); - return 0; -} - -void gifAnimationState_free(gifAnimationState_t *st){ - if(st) - framebuffer_free(st->frame); - free(st); -} - -/* buf ⇜ input data - * state ⇜ internal state, initialize as NULL - * delay ⇜ is filled with this frame's delay value in milliseconds (if it is not NULL). -1 means "No delay given". - * Small note: I mean, rendering a GIF in python is not quite trivial, either (about 20loc). But come on, this is just ridiculous. */ -framebuffer_t *framebuffer_render_gif(uint8_t *buf, size_t buflength, gifAnimationState_t **state, int *delay){ - gifAnimationState_t *st; - if(*state == NULL){ - /* On first invocation, parse gif and store it in the state struct */ - st = gif_read(buf, buflength); - if(!st) - goto error; - *state = st; - }else{ - st = *state; - } - - GifFileType *gif = st->gif; - - /* Find this image's color map */ - SavedImage *img = gif->SavedImages + st->idx; - ColorMapObject *cmo = img->ImageDesc.ColorMap; - if(!cmo){ - cmo = gif->SColorMap; - if(!cmo){ - fprintf(stderr, "Missing color table for GIF frame %d\n", st->idx); - goto error; - } - } - - /* Extract and validate image's bounds*/ - unsigned int ix = img->ImageDesc.Left; - unsigned int iy = img->ImageDesc.Top; - unsigned int iw = img->ImageDesc.Width; - unsigned int ih = img->ImageDesc.Height; - if((iy+ih)*gif->SWidth + ix+iw > st->frame->w*st->frame->h){ - fprintf(stderr, "Invalid gif: Image %d (x %d y %d w %d h %d) out of bounds (w %d h %d)\n", - st->idx, ix, iy, iw, ih, gif->SWidth, gif->SHeight); - goto error; - } - - /* Find and parse this image's GCB (if it exists) */ - int transparent = -1; - int disposal = DISPOSAL_UNSPECIFIED; - if(delay) - *delay = -1; /* in milliseconds */ - for(unsigned int i=0; iExtensionBlockCount; i++){ - ExtensionBlock *eb = img->ExtensionBlocks + i; - if(eb->Function == GRAPHICS_EXT_FUNC_CODE){ - GraphicsControlBlock *gcb = (GraphicsControlBlock *)eb->Bytes; - transparent = gcb->TransparentColor; - disposal = gcb->DisposalMode; - if(delay) - *delay = gcb->DelayTime * 10; /* 10ms → 1ms */ - } - } - - color_t *fb = st->frame->data; - if(disposal == DISPOSE_BACKGROUND || disposal == DISPOSAL_UNSPECIFIED){ - GifColorType *gc = gif->SColorMap[gif->SBackGroundColor].Colors; - color_t c = {gc->Red, gc->Green, gc->Blue, 255}; - color_fill(fb, c, st->frame->w*st->frame->h); - } - /* FIXME DISPOSE_PREVIOUS is currently unhandled. */ - - /* Render gif bitmap to RGB */ - uint8_t *p = img->RasterBits; - for(unsigned int y = iy; y < iy+ih; y++){ - for(unsigned int x = ix; x < ix+iw; x++){ - int c = *p++; - if(c != transparent){ - GifColorType *col = cmo[c].Colors; - color_t ct = {col->Red, col->Green, col->Blue, 255}; - fb[y*st->frame->w + x] = ct; - } - } - } - - st->idx++; - if(st->idx >= gif->ImageCount) - st->idx = 0; - return st->frame; -error: - gifAnimationState_free(st); - return 0; -} - diff --git a/host/gif.h b/host/gif.h deleted file mode 100644 index fde10b4..0000000 --- a/host/gif.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __GIF_H__ -#define __GIF_H__ - -#include -#include "color.h" - -typedef struct _gifAnimationState gifAnimationState_t; - -gifAnimationState_t *gif_read(uint8_t *buf, size_t buflength); -framebuffer_t* framebuffer_render_gif(uint8_t *buf, size_t buflength, gifAnimationState_t **state, int *delay); -void gifAnimationState_free(gifAnimationState_t *st); - -#endif//__GIF_H__ diff --git a/host/main.c b/host/main.c index aa782aa..d8f5062 100644 --- a/host/main.c +++ b/host/main.c @@ -1,8 +1,9 @@ +#include "config.h" #include "main.h" #include "color.h" #include "font.h" -#include "gif.h" +#include "net.h" #include #include #include @@ -11,8 +12,13 @@ #include #include #include +#include +#include +#include +#include #include + /* CAUTION: REQUIRES INPUT TO BE \0-TERMINATED * ...also, it does a hardcodes setlocale of LC_CTYPE to en_US.utf8 for... reasons. */ framebuffer_t *framebuffer_render_text(char *s, glyph_t **glyph_table, unsigned int glyph_table_size){ @@ -347,12 +353,18 @@ void console_render_buffer(framebuffer_t *fb){ int main(int argc, char **argv){ FILE *f = NULL; + int udpfd = 0; + int udp6fd = 0; + uint8_t *udpbuf = NULL; + uint8_t *fbdata = NULL; + framebuffer_t *fb = NULL; if(argc != 2){ fprintf(stderr, "No or too much input text given\n"); return 1; } + /* Read font file */ FILE *fontfile = fopen("unifont.bdf", "r"); if(!fontfile){ fprintf(stderr, "Error opening font file: %s\n", strerror(errno)); @@ -366,61 +378,181 @@ int main(int argc, char **argv){ } fclose(fontfile); - int delay = 20; - framebuffer_t *fb; - gifAnimationState_t *gifstate = NULL; - char *p = strstr(argv[1], ".gif"); - if(p && p[4] == '\0'){ /* Argument ends with ".gif", try to read it as a gif file */ - f = fopen(argv[1], "r"); - if(!f){ - fprintf(stderr, "Error opening gif file from argument (\"%s\"): %s\n", argv[1], strerror(errno)); - goto error; - } - uint8_t *buf = NULL; - size_t size = 0; - size_t read = 0; - const size_t READ_INC = 1024; - do{ - size_t newsize = size+READ_INC; - uint8_t *oldbuf = buf; - buf = realloc(buf, newsize); - if(!buf){ - free(oldbuf); - fclose(f); - fprintf(stderr, "Error opening gif file from argument (\"%s\"): Cannot allocate %lu bytes.\n", argv[1], newsize); - goto error; - } - read += fread(buf+size, 1, READ_INC, f); - size = newsize; - }while(read == size); - fb = framebuffer_render_gif(buf, read, &gifstate, &delay); - free(buf); - fclose(f); - }else{ - fb = framebuffer_render_text(argv[1], glyph_table, BLP_SIZE); + /* Set up framebuffer */ + fbdata = malloc(DISPLAY_WIDTH*DISPLAY_HEIGHT*sizeof(color_t)); + if(!fbdata){ + fprintf(stderr, "Cannot alloccate framebuffer\n"); + goto error; } + fb = malloc(sizeof(*fb)); + if(!fb){ + fprintf(stderr, "Cannot alloccate framebuffer\n"); + goto error; + } + fb->w = DISPLAY_WIDTH; + fb->h = DISPLAY_HEIGHT; + fb->data = fbdata; + + /* Set up UDP server */ + udp4fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + udp6fd = socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + if(udp4fd < 0 || udp6fd < 0){ + fprintf(stderr, "Cannot open UDP sockets: %s\n", strerror(errno)); + goto error; + } + struct sockaddr_in udp_addr = { + AF_INET, + INADDR_ANY, + htons(UDP_PORT) + }; + bind(udp4fd, &udp_addr, sizeof(udp_addr)); + struct sockaddr_in udp6_addr = { + AF_INET6, + IN6ADDR_ANY_INIT, + htons(UDP_PORT) + }; + bind(udp6fd, &udp_addr, sizeof(udp_addr)); + /* Set up UDP receive buffer */ + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + udpbuf = malloc(UDP_BUF_SIZE); + if(!buf){ + fprintf(stderr, "Cannot allocate UDP buffer"); + goto error; + } + struct iovec iov[1] = {{udpbuf, sizeof(UDP_BUF_SIZE)}}; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + /* List of currently active UDP streams */ + LIST_HEAD(udp_client_entry, udp_client) udp_client_head; + struct udp_client { + uint64_t last_timestamp; + uint64_t last_sequence_number; + struct sockaddr remote_addr; + int active:1; + LIST_ENTRY(udp_client) entries; + }; + LIST_INIT(&udp_client_head); + + LIST_HEAD(text_queue_head_t, text_queue_entry) text_queue_head; + struct text_queue_entry { + char *text; + int current_position; + unsigned int loop_count; + LIST_ENTRY(text_queue_entry) entries; + }; + LIST_INIT(&text_queue_head); for(;;){ /* Never gonna give you up, never gonna let you down! */ - if(!fb){ - fprintf(stderr, "Error rendering.\n"); + struct timeb time = {0}; + ftime(&time); + uint64_t now = time.time*1000 + time.millitm; + + int made_active = 0; + /* Choose next active client */ + for(struct udp_client *p = udp_client_head.lh_first; p != NULL; p = p->entries.le_next){ + if(now - p->last_frame_received > FRAME_TIMEOUT){ /* Connection timeout */ + LIST_REMOVE(p, entries); + continue; + } + if(!made_active){ + p->active = 1; + made_active = 1; + }else{ + p->active = 0; + } + } + + if(!made_active){ /* No active streams, render marquee */ + text_queue_entry *entry = text_queue_head.lh_first; + framebuffer_t *text = framebuffer_render_text(entry, glyph_table, BLP_SIZE); + /* COPY TEXT DATA */ + for(size_t line = 0; line < DISPLAY_HEIGHT; line++){ + color_t *datarow = fb->data+(line*DISPLAY_WIDTH); + color_t *textrow = text->data+(line*text->w); + if(entry->current_position > 0) + memset(datarow, 0, sizeof(color_t)*entry->current_position); + if(entry->current_position+text->w < DISPLAY_WIDTH) + memset(datarow+(entry->curent_position+text->w), 0, DISPLAY_WIDTH-(entry->curent_position+text->w)); + memcpy(datarow+(entry->current_position>0 ? entry->current_position : 0), + textrow+(entry->current_position>0 ? 0 : -entry->current_position), + //DISPLAY_WIDTH, text->w-current_position FIXME + } + entry->current_position++; + if(entry->current_position <= -text->w){ + entry->loop_count++; + if(entry->loop_count > TEXT_LOOP_COUNT){ + LIST_REMOVE(entry, entries); + } + } + } + + /* Receive pending IPv4 UDP packets */ + ssize_t received = 0; + while((received = recvmsg(udp4fd, &msg, 0)) > 0){ + if(received < UDP_BUF_SIZE) /* Packet too short */ + continue; + ml_packet_t *pkt = (ml_packet_t*)udpbuf; + if(ntohl(pkt->magic) != 0xDEADBEEF) + continue; + if(ntohs(pkt->width) != DISPLAY_WIDTH || ntohs(pkt->height) != DISPLAY_HEIGHT) + continue; + struct sockaddr_in *src = msg.msg_name; + struct udp_client *entry = NULL; + for(struct udp_client *p = udp_client_head.lh_first; p != NULL; p = p->entries.le_next){ + if(!memcmp(&p->remote_addr, src, sizeof(sockaddr))){ /* found */ + entry = p; + break; + } + } + if(!entry){ + entry = malloc(sizeof(struct udp_client)); + if(!entry){ + fprintf(stderr, "Cannot allocate UDP connection entry\n"); + goto error; + } + memset(entry, 0, sizeof(entry)); + LIST_INSERT_HEAD(&udp_client_head, entry, entries); + memcpy(entry->remote_addr, src, sizeof(sockaddr)); + } + if(entry->last_sequence_number > pkt->seq) /* Delayed packet containing old data */ + continue; + entry->last_timestamp = now; + entry->last_sequence_number = pkt->seq; + if(entry->active){ + /* Copy frame, extending RGB UDP packet data to RGBA framebuffer format */ + color_t *p = fb->data; + rgb_t *q = pkt->data; + while(p < fb->data+fb->w*fb->h){ + p->a = 0; + *(rgb_t *)p++ = *q++; + } + } + } + if(received < 0){ + fprintf(stderr, "Error receiving UDP datagram: %s\n", strerror(errno)); goto error; } - printf("\033[2J"); + /* Receive pending IPv6 UDP packets */ + /* FIXME */ + /* Render frame buffer */ + printf("\033[2J"); console_render_buffer(fb); printf("\n"); - usleep(delay*1000); - - if(gifstate){ - fb = framebuffer_render_gif(NULL, 0, &gifstate, &delay); - if(delay == -1) - delay = 20; - }else{ - fb = framebuffer_render_text(argv[1], glyph_table, BLP_SIZE); - } + + usb_send_buffer(fb); + usleep(1000000/FRAMERATE); } return 0; error: + fclose(f); + close(udpfd); + close(udp6fd); + free(udpbuf); + free(fbdata); + free(fb); return 1; } diff --git a/host/net.h b/host/net.h new file mode 100644 index 0000000..d8ec949 --- /dev/null +++ b/host/net.h @@ -0,0 +1,18 @@ +#ifndef __NET_H__ +#define __NET_H__ + +#include "config.h" + +typedef struct { + uint8_t r, g, b; +} rgb_t; + +typedef struct { + uint32_t magic, seq; + uint16_t width, height; + rgb_t data[DISPLAY_WIDTH*DISPLAY_HEIGHT]; +} ml_packet_t; + +#define UDP_BUF_SIZE sizeof(ml_packet_t) + +#endif//__NET_H__ -- cgit