improve readability of new.c
This commit is contained in:
parent
92500e1432
commit
5b8ce3781c
2 changed files with 240 additions and 126 deletions
18
Makefile
Normal file
18
Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
CC ?= cc
|
||||
CFLAGS ?= -std=c11 -O2 -pthread \
|
||||
-Wall -Wextra -Wpedantic -Wshadow -Wformat=2 \
|
||||
-Wcast-align -Wstrict-prototypes -Wmissing-prototypes \
|
||||
-Wpointer-arith -Wwrite-strings
|
||||
LDFLAGS ?= -pthread
|
||||
|
||||
TARGET := pixelflut-render
|
||||
SRC := new.c
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $(SRC) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
310
new.c
310
new.c
|
|
@ -1,143 +1,239 @@
|
|||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
/*
|
||||
* Pixelflut UDP renderer.
|
||||
*
|
||||
* Reads raw RGB24 frames that main.py continuously writes to INPUT_FILE
|
||||
* (via mmap) and floods them at a Pixelflut server using the binary UDP
|
||||
* protocol. Pixels are picked in a random order so the picture "dissolves"
|
||||
* onto the wall instead of being drawn scanline by scanline.
|
||||
*
|
||||
* Binary Pixelflut packet layout (little-endian on the wire):
|
||||
*
|
||||
* +------+---------+ pixelflut_packet
|
||||
* | cmd | subcmd | 2-byte header
|
||||
* +------+---------+---------+----+----+----+ pixelflut_pixel[0]
|
||||
* | x (u16) | y (u16) | r | g | b | 7 bytes
|
||||
* +----------------+---------+----+----+----+
|
||||
* | ... | PIXELS_PER_PACKET pixels
|
||||
* +----------------------------------------+
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* sendmmsg(), struct mmsghdr, SO_BINDTODEVICE */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define PIXELS 120
|
||||
#define MSGSIZE (2 + 7 * PIXELS)
|
||||
#define WIDTH (640)
|
||||
#define HEIGHT (360)
|
||||
#define OFFSET_X 1200
|
||||
#define OFFSET_Y 40
|
||||
//#define DISPLAY_HOST "100.65.0.2"
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* Portable host -> little-endian 16-bit conversion (matches the wire format). */
|
||||
#if defined(__linux__)
|
||||
# include <endian.h>
|
||||
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
# include <sys/endian.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <libkern/OSByteOrder.h>
|
||||
# define htole16(v) OSSwapHostToLittleInt16(v)
|
||||
#endif
|
||||
|
||||
/* ---- Frame geometry (must match the producer, see main.py). ------------- */
|
||||
#define FRAME_WIDTH 640
|
||||
#define FRAME_HEIGHT 360
|
||||
#define FRAME_CHANNELS 3 /* RGB, one byte per channel */
|
||||
#define FRAME_PIXELS (FRAME_WIDTH * FRAME_HEIGHT)
|
||||
#define FRAME_BYTES (FRAME_PIXELS * FRAME_CHANNELS)
|
||||
|
||||
/* Top-left corner where the frame is drawn on the shared wall. */
|
||||
#define CANVAS_OFFSET_X 1200
|
||||
#define CANVAS_OFFSET_Y 40
|
||||
|
||||
/* ---- Pixelflut binary protocol. ---------------------------------------- */
|
||||
#define PIXELFLUT_CMD_SET 0x00 /* "set pixels" command ... */
|
||||
#define PIXELFLUT_SUBCMD_RGB 0x01 /* ... with 3-byte RGB colours */
|
||||
#define PIXELS_PER_PACKET 120 /* pixels batched into one UDP datagram */
|
||||
|
||||
/* Number of datagrams needed to cover a whole frame once. */
|
||||
#define PACKET_COUNT (FRAME_PIXELS / PIXELS_PER_PACKET)
|
||||
|
||||
/* ---- Target / input. --------------------------------------------------- */
|
||||
/* #define DISPLAY_HOST "100.65.0.2" */
|
||||
#define DISPLAY_HOST "fcda:f100::d"
|
||||
#define DISPLAY_PORT 5005
|
||||
#define SIZE WIDTH*HEIGHT*3
|
||||
#define INPUT_FILE "/tmp/video"
|
||||
#define RANDOMNESS_LEN (WIDTH*HEIGHT)
|
||||
#include <pthread.h>
|
||||
struct mmsghdr msg[RANDOMNESS_LEN/PIXELS];
|
||||
struct iovec iovec[RANDOMNESS_LEN/PIXELS];
|
||||
uint8_t buf[RANDOMNESS_LEN/PIXELS][MSGSIZE];
|
||||
uint64_t randomness[RANDOMNESS_LEN];
|
||||
int randomness_o =0;
|
||||
static unsigned long x=123456789, y=362436069, z=521288629;
|
||||
|
||||
unsigned long xorshf96(void) { //period 2^96-1
|
||||
unsigned long t;
|
||||
x ^= x << 16;
|
||||
x ^= x >> 5;
|
||||
x ^= x << 1;
|
||||
/* A single pixel as it appears on the wire. */
|
||||
#pragma pack(push, 1)
|
||||
struct pixelflut_pixel {
|
||||
uint16_t x; /* little-endian */
|
||||
uint16_t y; /* little-endian */
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
t = x;
|
||||
x = y;
|
||||
y = z;
|
||||
z = t ^ x ^ y;
|
||||
/* A full datagram: header followed by a batch of pixels. */
|
||||
struct pixelflut_packet {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
struct pixelflut_pixel pixels[PIXELS_PER_PACKET];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
return z;
|
||||
}
|
||||
void shuffle(uint64_t *array, size_t n)
|
||||
_Static_assert(sizeof(struct pixelflut_pixel) == 7,
|
||||
"pixel must be exactly 7 bytes on the wire");
|
||||
_Static_assert(FRAME_PIXELS % PIXELS_PER_PACKET == 0,
|
||||
"frame pixels must divide evenly into packets");
|
||||
|
||||
/* Pre-built datagrams and the scatter/gather metadata sendmmsg() needs. */
|
||||
static struct pixelflut_packet packets[PACKET_COUNT];
|
||||
static struct mmsghdr messages[PACKET_COUNT];
|
||||
static struct iovec iovecs[PACKET_COUNT];
|
||||
|
||||
/* xorshf96 PRNG state (Marsaglia), period 2^96-1. */
|
||||
static unsigned long rng_x = 123456789;
|
||||
static unsigned long rng_y = 362436069;
|
||||
static unsigned long rng_z = 521288629;
|
||||
|
||||
static unsigned long xorshf96(void)
|
||||
{
|
||||
if (n > 1)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < n - 1; i++)
|
||||
{
|
||||
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
|
||||
unsigned long t = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = t;
|
||||
}
|
||||
}
|
||||
rng_x ^= rng_x << 16;
|
||||
rng_x ^= rng_x >> 5;
|
||||
rng_x ^= rng_x << 1;
|
||||
|
||||
unsigned long t = rng_x;
|
||||
rng_x = rng_y;
|
||||
rng_y = rng_z;
|
||||
rng_z = t ^ rng_x ^ rng_y;
|
||||
|
||||
return rng_z;
|
||||
}
|
||||
void *send_thread(void * ifname){
|
||||
int sockfd;
|
||||
struct sockaddr_in6 addr;
|
||||
sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (sockfd == -1) {
|
||||
perror("socket()");
|
||||
|
||||
/* Continuously blast the pre-built packets at the display. */
|
||||
static void *send_thread(void *arg)
|
||||
{
|
||||
const char *ifname = arg;
|
||||
|
||||
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
#ifdef SO_BINDTODEVICE
|
||||
/* Best effort: pin traffic to the given interface (needs privileges). */
|
||||
if (ifname != NULL && ifname[0] != '\0') {
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name),(char *) ifname);
|
||||
memset(&ifr, 0, sizeof ifr);
|
||||
snprintf(ifr.ifr_name, sizeof ifr.ifr_name, "%s", ifname);
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr,
|
||||
sizeof ifr) == -1) {
|
||||
perror("setsockopt(SO_BINDTODEVICE)");
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)ifname;
|
||||
#endif
|
||||
|
||||
struct sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_port = htons(DISPLAY_PORT);
|
||||
inet_pton(AF_INET6, DISPLAY_HOST, &addr.sin6_addr);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
|
||||
perror("connect()");
|
||||
if (inet_pton(AF_INET6, DISPLAY_HOST, &addr.sin6_addr) != 1) {
|
||||
fprintf(stderr, "invalid display host: %s\n", DISPLAY_HOST);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
int from = 0;
|
||||
sleep(1);
|
||||
while(1){
|
||||
int retval = sendmmsg(sockfd, &msg[from], (RANDOMNESS_LEN/PIXELS)-from, 0);
|
||||
if (retval == -1) {
|
||||
perror("sendmmsg()");
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&addr, sizeof addr) == -1) {
|
||||
perror("connect");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
from += retval;
|
||||
if(from <= (RANDOMNESS_LEN/PIXELS)){
|
||||
from = 0;
|
||||
|
||||
sleep(1); /* give the producer time to fill the first frame */
|
||||
|
||||
int next = 0;
|
||||
for (;;) {
|
||||
int sent = sendmmsg(fd, &messages[next], PACKET_COUNT - next, 0);
|
||||
if (sent == -1) {
|
||||
perror("sendmmsg");
|
||||
continue;
|
||||
}
|
||||
next += sent; /* resume after a partial send, wrap at the end */
|
||||
if (next >= PACKET_COUNT)
|
||||
next = 0;
|
||||
}
|
||||
|
||||
return NULL; /* unreachable */
|
||||
}
|
||||
int main(int argc, char *argv[]) {
|
||||
printf(argv[1]);
|
||||
x=rand();
|
||||
y=rand();
|
||||
z=rand();
|
||||
for(int i = 0; i < RANDOMNESS_LEN; i++) {
|
||||
randomness[i] = i;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: %s <interface>\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
shuffle(randomness,RANDOMNESS_LEN);
|
||||
for(int i = 0; i < RANDOMNESS_LEN/PIXELS; i++){
|
||||
iovec[i].iov_base = buf[i];
|
||||
iovec[i].iov_len = MSGSIZE;
|
||||
msg[i].msg_hdr.msg_iov = &iovec[i];
|
||||
msg[i].msg_hdr.msg_iovlen = 1;
|
||||
buf[i][0] = 0x00;
|
||||
buf[i][1] = 0x01;
|
||||
|
||||
rng_x = (unsigned long)rand();
|
||||
rng_y = (unsigned long)rand();
|
||||
rng_z = (unsigned long)rand();
|
||||
|
||||
/* Pre-fill the constant parts of every datagram once. */
|
||||
for (int i = 0; i < PACKET_COUNT; i++) {
|
||||
packets[i].cmd = PIXELFLUT_CMD_SET;
|
||||
packets[i].subcmd = PIXELFLUT_SUBCMD_RGB;
|
||||
|
||||
iovecs[i].iov_base = &packets[i];
|
||||
iovecs[i].iov_len = sizeof packets[i];
|
||||
messages[i].msg_hdr.msg_iov = &iovecs[i];
|
||||
messages[i].msg_hdr.msg_iovlen = 1;
|
||||
}
|
||||
int fd = open(INPUT_FILE,O_RDONLY);
|
||||
printf("fd: %d\n", fd);
|
||||
uint8_t * data = mmap(NULL, SIZE, PROT_READ , MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
|
||||
int frame_fd = open(INPUT_FILE, O_RDONLY);
|
||||
if (frame_fd == -1) {
|
||||
perror("open " INPUT_FILE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const uint8_t *frame =
|
||||
mmap(NULL, FRAME_BYTES, PROT_READ, MAP_SHARED, frame_fd, 0);
|
||||
if (frame == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pthread_t th1,th2,th3,th4;
|
||||
pthread_create(&th1, NULL, send_thread, argv[1]);
|
||||
while (1) {
|
||||
for(int i = 0; i < (int)(RANDOMNESS_LEN/PIXELS); i++){
|
||||
for(int j=0; j<PIXELS;j++){
|
||||
uint64_t pixel = xorshf96() % RANDOMNESS_LEN;
|
||||
uint16_t x = pixel%WIDTH;
|
||||
uint16_t y = pixel/WIDTH;
|
||||
uint16_t draw_x = OFFSET_X+x;
|
||||
uint16_t draw_y = OFFSET_Y+y;
|
||||
buf[i][2+j*7+0] = draw_x & 0xff;
|
||||
buf[i][2+j*7+1] = (draw_x >> 8) & 0xff;
|
||||
buf[i][2+j*7+2] = draw_y & 0xff;
|
||||
buf[i][2+j*7+3] = (draw_y >> 8) & 0xff;
|
||||
buf[i][2+j*7+4] = *(data+pixel*3+0) & 0xff;
|
||||
buf[i][2+j*7+5] = *(data+pixel*3+1) & 0xff;
|
||||
buf[i][2+j*7+6] = *(data+pixel*3+2) & 0xff;
|
||||
|
||||
pthread_t sender;
|
||||
if (pthread_create(&sender, NULL, send_thread, argv[1]) != 0) {
|
||||
perror("pthread_create");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Repeatedly repaint the frame in a random pixel order. */
|
||||
for (;;) {
|
||||
for (int i = 0; i < PACKET_COUNT; i++) {
|
||||
for (int j = 0; j < PIXELS_PER_PACKET; j++) {
|
||||
uint64_t src = xorshf96() % FRAME_PIXELS;
|
||||
uint16_t fx = (uint16_t)(src % FRAME_WIDTH);
|
||||
uint16_t fy = (uint16_t)(src / FRAME_WIDTH);
|
||||
const uint8_t *rgb = &frame[src * FRAME_CHANNELS];
|
||||
|
||||
struct pixelflut_pixel *px = &packets[i].pixels[j];
|
||||
px->x = htole16((uint16_t)(CANVAS_OFFSET_X + fx));
|
||||
px->y = htole16((uint16_t)(CANVAS_OFFSET_Y + fy));
|
||||
px->r = rgb[0];
|
||||
px->g = rgb[1];
|
||||
px->b = rgb[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue