├── start.sh ├── test_usbcam.cpp ├── mjpg_to_jpg.cpp ├── test_capture.cpp ├── usbcam.h └── vdb_release.h /start.sh: -------------------------------------------------------------------------------- 1 | num_frames=0 2 | decompress_jpg=0 3 | stream_video=0 4 | print_timestamps=1 5 | write_to_file=0 6 | camera_name=/dev/video0 7 | camera_width=800 8 | camera_height=600 9 | camera_buffers=3 10 | 11 | # ELP FISHEYE CAMERA CONTROLS 12 | # (v4l2-ctl -d/dev/video0 -L) 13 | powerline=0 # (0=off, 1 = 50Hz, 2 = 60Hz) 14 | whitebalance=1 # (0=off, 1=on) 15 | sharpness=2 # (min=0 max=6 step=1 default=2) 16 | brightness=0 # (min=-64 max=64 step=1 default=0) 17 | contrast=32 # (min=0 max=64 step=1 default=32) 18 | saturation=60 # (min=0 max=128 step=1 default=60) 19 | hue=0 # (min=-40 max=40 step=1 default=0) 20 | gamma=72 # (min=72 max=500 step=1 default=100) 21 | gain=0 # (min=0 max=100 step=1 default=0) 22 | exposure=45 # (min=1 max=5000 step=1 default=157) 23 | 24 | # 25 | # 26 | # 27 | 28 | # echo "Turning off exposure auto priority" 29 | # v4l2-ctl -d $camera_name -c exposure_auto_priority=0 30 | echo "Setting exposure to manual" 31 | v4l2-ctl -d $camera_name -c exposure_auto=1 32 | echo "Setting exposure time to $exposure" 33 | v4l2-ctl -d $camera_name -c exposure_absolute=$exposure 34 | # echo "Setting sharpness to $sharpness" 35 | # v4l2-ctl -d $camera_name -c sharpness=$sharpness 36 | # echo "Setting brightness to $brightness" 37 | # v4l2-ctl -d $camera_name -c brightness=$brightness 38 | # echo "Setting contrast to $contrast" 39 | # v4l2-ctl -d $camera_name -c contrast=$contrast 40 | # echo "Setting saturation to $saturation" 41 | # v4l2-ctl -d $camera_name -c saturation=$saturation 42 | # echo "Setting hue to $hue" 43 | # v4l2-ctl -d $camera_name -c hue=$hue 44 | # echo "Setting gamma to $gamma" 45 | # v4l2-ctl -d $camera_name -c gamma=$gamma 46 | # echo "Setting gain to $gain" 47 | # v4l2-ctl -d $camera_name -c gain=$gain 48 | # echo "Setting power line frequency to $powerline" 49 | # v4l2-ctl -d $camera_name -c power_line_frequency=$powerline 50 | # echo "Setting automatic white balance to $whitebalance" 51 | # v4l2-ctl -d $camera_name -c white_balance_temperature_auto=$whitebalance 52 | 53 | # 54 | # compile and run 55 | # 56 | DEFINES="-DCAMERA_NAME=\"$camera_name\" 57 | -DCAMERA_WIDTH=$camera_width 58 | -DCAMERA_HEIGHT=$camera_height 59 | -DCAMERA_BUFFERS=$camera_buffers 60 | -DNUM_FRAMES=$num_frames 61 | -DDECOMPRESS_JPG=$decompress_jpg 62 | -DSTREAM_VIDEO=$stream_video 63 | -DPRINT_TIMESTAMPS=$print_timestamps 64 | -DWRITE_TO_FILE=$write_to_file" 65 | g++ $DEFINES test_usbcam.cpp -o app -lv4l2 -lturbojpeg && ./app 66 | -------------------------------------------------------------------------------- /test_usbcam.cpp: -------------------------------------------------------------------------------- 1 | // compiling 2 | // g++ test_usbcam.cpp -o app -lv4l2 -lturbojpeg && ./app 3 | 4 | #ifndef NUM_FRAMES 5 | #define NUM_FRAMES 0 6 | #endif 7 | #ifndef DECOMPRESS_JPG 8 | #define DECOMPRESS_JPG 1 9 | #endif 10 | #ifndef STREAM_VIDEO 11 | #define STREAM_VIDEO 0 12 | #endif 13 | #ifndef PRINT_TIMESTAMPS 14 | #define PRINT_TIMESTAMPS 1 15 | #endif 16 | #ifndef WRITE_TO_FILE 17 | #define WRITE_TO_FILE 0 18 | #endif 19 | #ifndef CAMERA_NAME 20 | #define CAMERA_NAME "/dev/video0" 21 | #endif 22 | #ifndef CAMERA_WIDTH 23 | #define CAMERA_WIDTH 800 24 | #endif 25 | #ifndef CAMERA_HEIGHT 26 | #define CAMERA_HEIGHT 600 27 | #endif 28 | #ifndef CAMERA_BUFFERS 29 | #define CAMERA_BUFFERS 3 30 | #endif 31 | 32 | #include "vdb_release.h" 33 | #include "usbcam.h" 34 | #include 35 | #include 36 | #include 37 | 38 | uint64_t get_nanoseconds() 39 | { 40 | struct timespec ts = {}; 41 | clock_gettime(CLOCK_REALTIME, &ts); 42 | uint64_t result = ((uint64_t)ts.tv_sec)*1000000000 + 43 | ((uint64_t)ts.tv_nsec); 44 | return result; 45 | } 46 | 47 | void ctrlc(int) 48 | { 49 | exit(0); 50 | } 51 | 52 | int main(int argc, char **argv) 53 | { 54 | signal(SIGINT, ctrlc); 55 | 56 | usbcam_opt_t opt = {0}; 57 | opt.device_name = CAMERA_NAME; 58 | opt.pixel_format = V4L2_PIX_FMT_MJPEG; 59 | opt.width = CAMERA_WIDTH; 60 | opt.height = CAMERA_HEIGHT; 61 | opt.buffers = CAMERA_BUFFERS; 62 | 63 | usbcam_init(opt); 64 | 65 | #if NUM_FRAMES==0 66 | for (int i = 0; ; i++) 67 | #else 68 | for (int i = 0; i < NUM_FRAMES; i++) 69 | #endif 70 | { 71 | const int Ix = CAMERA_WIDTH; 72 | const int Iy = CAMERA_HEIGHT; 73 | static unsigned char rgb[Ix*Iy*3]; 74 | { 75 | unsigned char *jpg_data; 76 | unsigned int jpg_size; 77 | timeval timestamp; 78 | usbcam_lock(&jpg_data, &jpg_size, ×tamp); 79 | 80 | printf("%5d. ", i); 81 | 82 | #if DECOMPRESS_JPG==1 83 | { 84 | uint64_t t1 = get_nanoseconds(); 85 | if (!usbcam_jpeg_to_rgb(Ix, Iy, rgb, jpg_data, jpg_size)) 86 | { 87 | usbcam_unlock(); 88 | continue; 89 | } 90 | uint64_t t2 = get_nanoseconds(); 91 | printf("Decompressed in %6.2f ms\t", (t2-t1)/1e6); 92 | } 93 | #endif 94 | 95 | #if STREAM_VIDEO==1 96 | { 97 | static uint64_t last_t = get_nanoseconds(); 98 | uint64_t t = get_nanoseconds(); 99 | float dt = (t-last_t)/1e9; 100 | if (dt > 1.0f && vdb_begin()) 101 | { 102 | vdb_imageRGB8(rgb, Ix, Iy); 103 | vdb_end(); 104 | last_t = t; 105 | } 106 | } 107 | #endif 108 | 109 | #if WRITE_TO_FILE==1 110 | { 111 | char filename[256]; 112 | sprintf(filename, "video%04d.jpg", i); 113 | FILE *f = fopen(filename, "w+"); 114 | fwrite(jpg_data, jpg_size, 1, f); 115 | fclose(f); 116 | } 117 | #endif 118 | 119 | usbcam_unlock(); 120 | 121 | #if PRINT_TIMESTAMPS==1 122 | { 123 | // compute frame interval from internal timestamp 124 | { 125 | uint64_t sec = (uint64_t)timestamp.tv_sec; 126 | uint64_t usec = (uint64_t)timestamp.tv_usec; 127 | uint64_t t = sec*1000*1000 + usec; 128 | static uint64_t last_t = t; 129 | printf("%6.2f ms\t", (t-last_t)/1e3); 130 | last_t = t; 131 | } 132 | 133 | // compute frame interval from system clock 134 | { 135 | static uint64_t last_t = get_nanoseconds(); 136 | uint64_t t = get_nanoseconds(); 137 | printf("%6.2f ms\t", (t-last_t)/1e6); 138 | last_t = t; 139 | } 140 | } 141 | #endif 142 | 143 | printf("\n"); 144 | } 145 | } 146 | 147 | usbcam_cleanup(); 148 | 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /mjpg_to_jpg.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Create a valid JPG frame from a MJPG (motion JPG) frame by 6 | // appending a default Huffman table to its header. 7 | // USAGE 8 | // You allocate jpg with atleast mjpg_size + sizeof(huffman). 9 | // You can get just this value by passing NULL for 'jpg'. 10 | // The MJPG must have a Start of Frame marker (0xffc0). 11 | // NOTES 12 | // Some cameras only support the MJPG format (V4L2_PIX_FMT_MJPEG) 13 | // and not JPG format (V4L2_PIX_FMT_JPEG). Some cameras decide to 14 | // output valid JPG frames anyway, while others, such as my webcam, 15 | // decide to output nearly valid JPGs. Apparently, these would be 16 | // valid JPGs if it were not for a missing Huffman table. Luckily, 17 | // it is possible to just squeeze in some default table and get a 18 | // perfectly fine JPG that way. 19 | unsigned int mjpg_to_jpg(unsigned char *mjpg, unsigned int mjpg_size, unsigned char *jpg) 20 | { 21 | // To understand how this works I suggest you run 22 | // $ xxd | less 23 | // on one of your MJPG images, and try to relate the binary data with 24 | // the JPEG specification on wikipedia (en.wikipedia.org/wiki/JPEG#JPEG_files). 25 | // Specifically, look for the ff** markers, starting with ffd8. 26 | // Then compare that with a JPG image. 27 | 28 | static unsigned char huffman[] = 29 | { 30 | // JPEG magic 31 | // 0xff, 0xd8, 32 | 33 | // Text comment 34 | // 0xff, 0xfe, 0x00, 0x10 0x4c, 0x61 0x76, 0x63 0x35, 0x36 0x2e, 0x36 0x30, 0x2e 0x31, 0x30 0x30, 0x00, 35 | 36 | // Huffman table 37 | 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 38 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 39 | 0x0b, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 40 | 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 41 | 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 42 | 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 43 | 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 44 | 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 45 | 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 46 | 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 47 | 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 48 | 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 49 | 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 50 | 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 51 | 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 52 | 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 53 | 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 54 | 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 55 | 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 56 | 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 57 | 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 58 | 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 59 | 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 60 | 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 61 | 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 62 | 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 63 | 0xf7, 0xf8, 0xf9, 0xfa 64 | }; 65 | 66 | unsigned int jpg_size = mjpg_size+sizeof(huffman); 67 | if (!jpg) 68 | return jpg_size; 69 | 70 | // search for Start of Frame (SOF0) marker 71 | unsigned int i = 0; 72 | while ((i+1) < mjpg_size && !(mjpg[i] == 0xff && mjpg[i+1] == 0xc0)) 73 | i++; 74 | 75 | // and squeeze huffman table inbetween 76 | memcpy(jpg, mjpg, i); 77 | memcpy(jpg+i, huffman, sizeof(huffman)); 78 | memcpy(jpg+i+sizeof(huffman), mjpg+i, mjpg_size-i); 79 | return jpg_size; 80 | } 81 | 82 | unsigned char *read_file(const char *filename, unsigned int *length) 83 | { 84 | FILE *f = fopen(filename, "rb"); 85 | if (!f) 86 | return NULL; 87 | fseek(f, 0, SEEK_END); 88 | long s = ftell(f); 89 | fseek(f, 0, SEEK_SET); 90 | unsigned char *result = (unsigned char*)malloc(s); 91 | if (fread(result, s, 1, f) != 1) 92 | { 93 | fclose(f); 94 | return NULL; 95 | } 96 | fclose(f); 97 | *length = (unsigned int)s; 98 | return result; 99 | } 100 | 101 | int main() 102 | { 103 | unsigned int mjpg_size; 104 | unsigned char *mjpg = read_file("video0000.jpg", &mjpg_size); 105 | unsigned int jpg_size = mjpg_to_jpg(mjpg, mjpg_size, NULL); 106 | unsigned char *jpg = (unsigned char*)malloc(jpg_size); 107 | mjpg_to_jpg(mjpg, mjpg_size, jpg); 108 | 109 | FILE *f = fopen("video0000b.jpg", "w+"); 110 | fwrite(jpg, jpg_size, 1, f); 111 | fclose(f); 112 | } 113 | -------------------------------------------------------------------------------- /test_capture.cpp: -------------------------------------------------------------------------------- 1 | // v4l2 magic 2 | // Get the value of setting 'exposure_auto' 3 | // v4l2-ctl -d /dev/video2 -C exposure_auto 4 | // See all controls 5 | // v4l2-ctl -d /dev/video2 --list-ctrls-menus 6 | // Set exposure 7 | // v4l2-ctl -d /dev/video2 -c exposure_auto=3 8 | // See formats 9 | // v4l2-ctl --device=/dev/video1 --list-formats-ext 10 | 11 | // TEGRA X1/TEGRA LINUX DRIVER PACKAGE MULTIMEDIA USER GUIDE 12 | // http://developer2.download.nvidia.com/embedded/L4T/r24_Release_v2.0/Docs/L4T_Tegra_X1_Multimedia_User_Guide_Release_24.2.pdf?nas16vBtpEYXN9Q3_dgD9dZ8msoaqJ3ncR5CVNdqlEnlYt3bPqlKOsRcifhHB02kMNznaxDYRKdtBJg-0xXxHzCbysXTlTMoAwEaFIF3FfHzxlyVQatAbHz-3lkv9FndSDaC8fJUQuKIsAbbFAVRBxYHXbhPXas0BbJga--6wshwIuSTLJK3wFmGmZrgBgpZS9LL9wI 13 | 14 | // g++ test_usbcam.cpp -o app -lv4l2 15 | 16 | // To convert MJPEG frames to JPEG: 17 | // My laptop's webcamera outputs MJPEG frames, and if I try to open them in Linux 18 | // I get Huffman table 0x00 was not defined, or something. Running xxd on the jpeg 19 | // I see the header is ... AVI1, which is a mjpeg alright. 20 | // ffmpeg -i video%04d.jpg -vcodec mjpeg -f image2 video%04d.jpg 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | // Returns number of nanoseconds since the UNIX epoch 38 | uint64_t get_nanoseconds() 39 | { 40 | struct timespec ts = {}; 41 | clock_gettime(CLOCK_REALTIME, &ts); 42 | uint64_t result = ((uint64_t)ts.tv_sec)*1000000000 + 43 | ((uint64_t)ts.tv_nsec); 44 | return result; 45 | } 46 | 47 | void ctrlc(int) 48 | { 49 | exit(EXIT_FAILURE); 50 | } 51 | 52 | void xioctl(int fh, int request, void *arg) 53 | { 54 | int r; 55 | do 56 | { 57 | r = v4l2_ioctl(fh, request, arg); 58 | } while (r == -1 && ((errno == EINTR) || (errno == EAGAIN))); 59 | 60 | if (r == -1) 61 | { 62 | printf("[usbcam.h] USB request failed (%d): %s\n", errno, strerror(errno)); 63 | exit(EXIT_FAILURE); 64 | } 65 | } 66 | 67 | #define usbcam_assert(CONDITION, MESSAGE) { if (!(CONDITION)) { printf("[usbcam.h] Error at line %d: %s\n", __LINE__, MESSAGE); exit(EXIT_FAILURE); } } 68 | 69 | int main(int argc, char **argv) 70 | { 71 | signal(SIGINT, ctrlc); 72 | 73 | const char *device_name = "/dev/video0"; 74 | const int device_buffers = 3; 75 | const int device_width = 800; 76 | const int device_height = 600; 77 | const int device_format = V4L2_PIX_FMT_MJPEG; 78 | 79 | printf("opening %s\n", device_name); 80 | 81 | // Open the device 82 | int fd = v4l2_open(device_name, O_RDWR, 0); 83 | usbcam_assert(fd >= 0, "Failed to open device"); 84 | 85 | // set format 86 | { 87 | v4l2_format fmt = {0}; 88 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 89 | fmt.fmt.pix.pixelformat = device_format; 90 | fmt.fmt.pix.width = device_width; 91 | fmt.fmt.pix.height = device_height; 92 | xioctl(fd, VIDIOC_S_FMT, &fmt); 93 | 94 | usbcam_assert(fmt.fmt.pix.pixelformat == device_format, "Did not get the requested format"); 95 | usbcam_assert(fmt.fmt.pix.width == device_width, "Did not get the requested width"); 96 | usbcam_assert(fmt.fmt.pix.height == device_height, "Did not get the requested height"); 97 | } 98 | 99 | // tell the driver how many buffers we want 100 | { 101 | v4l2_requestbuffers request = {0}; 102 | request.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 103 | request.memory = V4L2_MEMORY_MMAP; 104 | request.count = device_buffers; 105 | xioctl(fd, VIDIOC_REQBUFS, &request); 106 | 107 | usbcam_assert(request.count == device_buffers, "Did not get the requested number of buffers"); 108 | } 109 | 110 | // allocate buffer 111 | void *buffer_start[device_buffers] = {0}; 112 | uint32_t buffer_length[device_buffers] = {0}; 113 | for (int i = 0; i < device_buffers; i++) 114 | { 115 | v4l2_buffer info = {0}; 116 | info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 117 | info.memory = V4L2_MEMORY_MMAP; 118 | info.index = i; 119 | xioctl(fd, VIDIOC_QUERYBUF, &info); 120 | 121 | buffer_length[i] = info.length; 122 | buffer_start[i] = mmap( 123 | NULL, 124 | info.length, 125 | PROT_READ | PROT_WRITE, 126 | MAP_SHARED, 127 | fd, 128 | info.m.offset 129 | ); 130 | 131 | usbcam_assert(buffer_start[i] != MAP_FAILED, "Failed to allocate memory for buffers"); 132 | } 133 | 134 | // start streaming 135 | { 136 | int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 137 | xioctl(fd, VIDIOC_STREAMON, &type); 138 | } 139 | 140 | // queue buffers 141 | for (int i = 0; i < device_buffers; i++) 142 | { 143 | v4l2_buffer info = {0}; 144 | info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 145 | info.memory = V4L2_MEMORY_MMAP; 146 | info.index = i; 147 | xioctl(fd, VIDIOC_QBUF, &info); 148 | } 149 | 150 | int num_frames = 60; 151 | for (int frame = 0; frame < num_frames; frame++) 152 | { 153 | #if 1 // dequeue all the buffers and select the one with latest data 154 | v4l2_buffer buf = {0}; 155 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 156 | buf.memory = V4L2_MEMORY_MMAP; 157 | { 158 | // get a buffer 159 | xioctl(fd, VIDIOC_DQBUF, &buf); 160 | 161 | // check if there are more buffers available 162 | int r = 1; 163 | while (r == 1) 164 | { 165 | fd_set fds; 166 | FD_ZERO(&fds); 167 | FD_SET(fd, &fds); 168 | timeval tv; // if both fields = 0, select returns immediately 169 | tv.tv_sec = 0; 170 | tv.tv_usec = 0; 171 | r = select(fd + 1, &fds, NULL, NULL, &tv); // todo: what if r == -1? 172 | if (r == 1) 173 | { 174 | printf("."); 175 | 176 | // queue the previous buffer 177 | xioctl(fd, VIDIOC_QBUF, &buf); 178 | 179 | // get a new buffer 180 | xioctl(fd, VIDIOC_DQBUF, &buf); 181 | } 182 | } 183 | } 184 | #else // deque whatever frame the driver decides to give us 185 | // this will not necessarily hold the latest data! 186 | v4l2_buffer buf = {0}; 187 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 188 | buf.memory = V4L2_MEMORY_MMAP; 189 | // buf.index; // we don't specify the buffer index 190 | // the driver selects one and gives it to us 191 | xioctl(fd, VIDIOC_DQBUF, &buf); 192 | #endif 193 | 194 | unsigned char *jpg_data = (unsigned char*)buffer_start[buf.index]; 195 | unsigned int jpg_size = buf.bytesused; 196 | 197 | { 198 | timeval timestamp = buf.timestamp; 199 | uint64_t sec = (uint64_t)timestamp.tv_sec; 200 | uint64_t usec = (uint64_t)timestamp.tv_usec; 201 | uint64_t t = sec*1000*1000 + usec; 202 | 203 | static uint64_t last_t = t; 204 | double dt = (t-last_t)/1e6; 205 | last_t = t; 206 | printf("FRAME %3.d\t%6.2f MS\t%u BYTES\tBUFFER %d\n", frame, 1000.0f*dt, jpg_size, buf.index); 207 | } 208 | 209 | { 210 | static uint64_t t_begin = get_nanoseconds(); 211 | if (frame == num_frames - 1) 212 | { 213 | double dt = (get_nanoseconds() - t_begin)/1e9; 214 | printf("total: %.2f f/s ~ %.2f ms/f\n", num_frames/dt, 1000.0f*dt/num_frames); 215 | } 216 | } 217 | 218 | { 219 | char filename[256]; 220 | sprintf(filename, "video%04d.jpg", frame); 221 | FILE *f = fopen(filename, "w+"); 222 | fwrite(jpg_data, jpg_size, 1, f); 223 | fclose(f); 224 | // usleep(110*1000); 225 | } 226 | 227 | xioctl(fd, VIDIOC_QBUF, &buf); 228 | } 229 | 230 | // turn off streaming 231 | { 232 | int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 233 | xioctl(fd, VIDIOC_STREAMOFF, &type); 234 | close(fd); 235 | } 236 | 237 | // dequeue buffers 238 | { 239 | for (int i = 0; i < device_buffers; i++) 240 | munmap(buffer_start[i], buffer_length[i]); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /usbcam.h: -------------------------------------------------------------------------------- 1 | // usbcam.h 2 | // github.com/lightbits 3 | // 4 | // Changelog 5 | // (6) Don't preserve decompressor instance in usbcam_jpeg_to_rgb (thread-safety) 6 | // (5) Automatically unlock previous frame on usbcam_lock if user forgot (and warn) 7 | // (4) Buffer count is unsigned 8 | // (3) JPEG-RGB decompressor returns true/false instead of crashing 9 | // (2) Added turbojpeg JPEG->RGB decompression 10 | // (1) Beginning of time 11 | // 12 | // See §BUILDING for build instructions 13 | 14 | #include 15 | struct usbcam_opt_t 16 | { 17 | const char *device_name; 18 | unsigned int buffers; // See §BUFFERS 19 | unsigned int pixel_format; // See §PIXELFORMATS 20 | unsigned int width; 21 | unsigned int height; 22 | }; 23 | 24 | void usbcam_cleanup(); 25 | void usbcam_init(usbcam_opt_t opt); 26 | void usbcam_lock(unsigned char **data, unsigned int *size, timeval *timestamp); 27 | void usbcam_unlock(); 28 | // See §DECOMPRESSION 29 | bool usbcam_jpeg_to_rgb(int desired_width, int desired_height, unsigned char *rgb, unsigned char *jpg_data, unsigned int jpg_size); 30 | 31 | // 32 | // USER MANUAL 33 | // 34 | // §BUFFERS 35 | // The driver does not overwrite buffers with latest data: 36 | // Therefore, you should request as many buffers as you expect 37 | // processing time to take. For example, if you need 100 ms to 38 | // process one frame and the camera gives one frame every 30 ms, 39 | // then it will fill up three buffers while you process. If you 40 | // requested less than three buffers you will not get the latest 41 | // frame when you ask for the next frame! 42 | // 43 | // §PIXELFORMATS 44 | // A common format is V4L2_PIX_FMT_MJPEG. 45 | // Pixel formats are specified as codes of four characters. 46 | // A list of formats can be found in videodev2.h. 47 | // http://lxr.free-electrons.com/source/include/uapi/linux/videodev2.h#L616 48 | // You can find out what formats your camera supports with 49 | // v4l2-ctl -d /dev/video0 --list-formats-ext 50 | // 51 | // §DECOMPRESSION 52 | // You can specify a desired resolution which does not need to 53 | // match the resolution given in usbcam_init. This will make 54 | // turbojpeg use its internal downscaling capabilities while 55 | // also reducing decompression time. If you specfy the same 56 | // resolution no downscaling happens. 57 | // http://www.libjpeg-turbo.org/Documentation/Documentation 58 | // 59 | // §BUILDING 60 | // STEP 1) Get the video 4 linux 2 development libraries (v4l2) 61 | // $ sudo apt-get install libv4l-dev 62 | // $ sudo apt-get install v4l-utils 63 | // STEP 2) Get the turbojpeg library 64 | // (See https://github.com/libjpeg-turbo/libjpeg-turbo/blob/master/BUILDING.md) 65 | // $ git clone https://github.com/libjpeg-turbo/libjpeg-turbo 66 | // $ cd libjpeg-turbo 67 | // $ autoreconf -fiv 68 | // $ mkdir build 69 | // $ cd build 70 | // $ sh ../configure 71 | // $ make 72 | // $ make install prefix=/usr/local libdir=/usr/local/lib64 73 | // STEP 3) Compiler flags 74 | // g++ ... -lv4l2 -lturbojpeg 75 | 76 | // 77 | // Implementation 78 | // 79 | 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | 93 | #define usbcam_max_buffers 128 94 | #define usbcam_assert(CONDITION, ...) { if (!(CONDITION)) { printf("[usbcam.h line %d] ", __LINE__); printf(__VA_ARGS__); printf("\n"); exit(EXIT_FAILURE); } } 95 | #define usbcam_warn(...) { printf("[usbcam.h line %d] ", __LINE__); printf(__VA_ARGS__); printf("\n"); } 96 | #ifdef USBCAM_DEBUG 97 | #define usbcam_debug(...) { printf("[usbcam.h line %d] ", __LINE__); printf(__VA_ARGS__); printf("\n"); } 98 | #else 99 | #define usbcam_debug(...) { } 100 | #endif 101 | 102 | static int usbcam_has_mmap = 0; 103 | static int usbcam_has_lock = 0; 104 | static int usbcam_has_fd = 0; 105 | static int usbcam_has_stream = 0; 106 | static int usbcam_fd = 0; 107 | static int usbcam_buffers = 0; 108 | static void *usbcam_buffer_start[usbcam_max_buffers] = {0}; 109 | static unsigned int usbcam_buffer_length[usbcam_max_buffers] = {0}; 110 | static v4l2_buffer usbcam_lock_buf = {0}; 111 | 112 | void usbcam_ioctl(int request, void *arg) 113 | { 114 | usbcam_assert(usbcam_has_fd, "The camera device has not been opened yet!"); 115 | int r; 116 | do 117 | { 118 | r = v4l2_ioctl(usbcam_fd, request, arg); 119 | } while (r == -1 && ((errno == EINTR) || (errno == EAGAIN))); 120 | if (r == -1) 121 | { 122 | printf("[usbcam.h] USB request failed (%d): %s\n", errno, strerror(errno)); 123 | exit(EXIT_FAILURE); 124 | } 125 | } 126 | 127 | void usbcam_cleanup() 128 | { 129 | // return any buffers we have dequeued (not sure if this is necessary) 130 | if (usbcam_has_lock) 131 | { 132 | usbcam_debug("Requeuing buffer"); 133 | usbcam_ioctl(VIDIOC_QBUF, &usbcam_lock_buf); 134 | usbcam_has_lock = 0; 135 | } 136 | 137 | // free buffers 138 | if (usbcam_has_mmap) 139 | { 140 | usbcam_debug("Deallocating mmap"); 141 | for (int i = 0; i < usbcam_buffers; i++) 142 | munmap(usbcam_buffer_start[i], usbcam_buffer_length[i]); 143 | usbcam_has_mmap = 0; 144 | } 145 | 146 | // turn off streaming 147 | if (usbcam_has_stream) 148 | { 149 | usbcam_debug("Turning off stream (if this freezes send me a message)"); 150 | int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 151 | usbcam_ioctl(VIDIOC_STREAMOFF, &type); 152 | usbcam_has_stream = 0; 153 | } 154 | 155 | if (usbcam_has_fd) 156 | { 157 | usbcam_debug("Closing fd"); 158 | close(usbcam_fd); 159 | usbcam_has_fd = 0; 160 | } 161 | } 162 | 163 | void usbcam_init(usbcam_opt_t opt) 164 | { 165 | usbcam_cleanup(); 166 | usbcam_assert(opt.buffers <= usbcam_max_buffers, "You requested too many buffers"); 167 | usbcam_assert(opt.buffers > 0, "You need atleast one buffer"); 168 | 169 | // Open the device 170 | usbcam_fd = v4l2_open(opt.device_name, O_RDWR, 0); 171 | usbcam_assert(usbcam_fd >= 0, "Failed to open device"); 172 | usbcam_has_fd = 1; 173 | 174 | // set format 175 | { 176 | v4l2_format fmt = {0}; 177 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 178 | fmt.fmt.pix.pixelformat = opt.pixel_format; 179 | fmt.fmt.pix.width = opt.width; 180 | fmt.fmt.pix.height = opt.height; 181 | usbcam_ioctl(VIDIOC_S_FMT, &fmt); 182 | 183 | usbcam_assert(fmt.fmt.pix.pixelformat == opt.pixel_format, "Did not get the requested format"); 184 | usbcam_assert(fmt.fmt.pix.width == opt.width, "Did not get the requested width"); 185 | usbcam_assert(fmt.fmt.pix.height == opt.height, "Did not get the requested height"); 186 | } 187 | 188 | usbcam_debug("Opened device (%s %dx%d)", opt.device_name, opt.width, opt.height); 189 | 190 | // tell the driver how many buffers we want 191 | { 192 | v4l2_requestbuffers request = {0}; 193 | request.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 194 | request.memory = V4L2_MEMORY_MMAP; 195 | request.count = opt.buffers; 196 | usbcam_ioctl(VIDIOC_REQBUFS, &request); 197 | 198 | usbcam_assert(request.count == opt.buffers, "Did not get the requested number of buffers"); 199 | } 200 | 201 | // allocate buffers 202 | for (int i = 0; i < opt.buffers; i++) 203 | { 204 | v4l2_buffer info = {0}; 205 | info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 206 | info.memory = V4L2_MEMORY_MMAP; 207 | info.index = i; 208 | usbcam_ioctl(VIDIOC_QUERYBUF, &info); 209 | 210 | usbcam_buffer_length[i] = info.length; 211 | usbcam_buffer_start[i] = mmap( 212 | NULL, 213 | info.length, 214 | PROT_READ | PROT_WRITE, 215 | MAP_SHARED, 216 | usbcam_fd, 217 | info.m.offset 218 | ); 219 | 220 | usbcam_assert(usbcam_buffer_start[i] != MAP_FAILED, "Failed to allocate memory for buffers"); 221 | } 222 | 223 | usbcam_buffers = opt.buffers; 224 | usbcam_has_mmap = 1; 225 | 226 | // start streaming 227 | { 228 | int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 229 | usbcam_ioctl(VIDIOC_STREAMON, &type); 230 | } 231 | 232 | usbcam_has_stream = 1; 233 | 234 | // queue buffers 235 | for (int i = 0; i < opt.buffers; i++) 236 | { 237 | v4l2_buffer info = {0}; 238 | info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 239 | info.memory = V4L2_MEMORY_MMAP; 240 | info.index = i; 241 | usbcam_ioctl(VIDIOC_QBUF, &info); 242 | } 243 | } 244 | 245 | void usbcam_unlock() 246 | { 247 | if (usbcam_has_lock) 248 | { 249 | usbcam_ioctl(VIDIOC_QBUF, &usbcam_lock_buf); 250 | usbcam_has_lock = 0; 251 | } 252 | else 253 | { 254 | usbcam_warn("You already unlocked the frame"); 255 | } 256 | } 257 | 258 | void usbcam_lock(unsigned char **data, unsigned int *size, timeval *timestamp) 259 | { 260 | usbcam_assert(usbcam_has_fd, "Camera device not open"); 261 | usbcam_assert(usbcam_has_mmap, "Buffers not allocated"); 262 | usbcam_assert(usbcam_has_stream, "Stream not begun"); 263 | 264 | if (usbcam_has_lock) 265 | { 266 | // you should unlock frames as soon as you are done processing them for best performance 267 | usbcam_warn("You did not unlock the previous frame"); 268 | usbcam_unlock(); 269 | } 270 | 271 | // dequeue all the buffers and select the one with latest data 272 | v4l2_buffer buf = {0}; 273 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 274 | buf.memory = V4L2_MEMORY_MMAP; 275 | { 276 | // get a buffer 277 | usbcam_ioctl(VIDIOC_DQBUF, &buf); 278 | 279 | // check if there are more buffers available 280 | int r = 1; 281 | while (r == 1) 282 | { 283 | fd_set fds; 284 | FD_ZERO(&fds); 285 | FD_SET(usbcam_fd, &fds); 286 | timeval tv; // if both fields = 0, select returns immediately 287 | tv.tv_sec = 0; 288 | tv.tv_usec = 0; 289 | r = select(usbcam_fd + 1, &fds, NULL, NULL, &tv); // todo: what if r == -1? 290 | if (r == 1) 291 | { 292 | // queue the previous buffer 293 | usbcam_ioctl(VIDIOC_QBUF, &buf); 294 | 295 | // get a new buffer 296 | usbcam_ioctl(VIDIOC_DQBUF, &buf); 297 | } 298 | } 299 | } 300 | 301 | *timestamp = buf.timestamp; 302 | *data = (unsigned char*)usbcam_buffer_start[buf.index]; 303 | *size = buf.bytesused; 304 | 305 | usbcam_lock_buf = buf; 306 | usbcam_has_lock = 1; 307 | } 308 | 309 | bool usbcam_jpeg_to_rgb(int desired_width, int desired_height, unsigned char *destination, unsigned char *jpg_data, unsigned int jpg_size) 310 | { 311 | tjhandle decompressor = tjInitDecompress(); 312 | int subsamp,width,height,error; 313 | 314 | error = tjDecompressHeader2(decompressor, 315 | jpg_data, 316 | jpg_size, 317 | &width, 318 | &height, 319 | &subsamp); 320 | 321 | if (error) 322 | { 323 | usbcam_warn("Failed to decode JPEG: %s", tjGetErrorStr()); 324 | tjDestroy(decompressor); 325 | return false; 326 | } 327 | 328 | error = tjDecompress2(decompressor, 329 | jpg_data, 330 | jpg_size, 331 | destination, 332 | desired_width, 333 | 0, 334 | desired_height, 335 | TJPF_RGB, 336 | TJFLAG_FASTDCT); 337 | 338 | if (error) 339 | { 340 | usbcam_warn("Failed to decode JPEG: %s", tjGetErrorStr()); 341 | tjDestroy(decompressor); 342 | return false; 343 | } 344 | 345 | tjDestroy(decompressor); 346 | return true; 347 | } 348 | -------------------------------------------------------------------------------- /vdb_release.h: -------------------------------------------------------------------------------- 1 | // vdb - Version 3 2 | // Changelog 3 | // (3) Float and int sliders and checkboxes 4 | // (2) Message passing from browser to vdb 5 | // (1) Works on unix and windows 6 | 7 | #ifndef VDB_HEADER_INCLUDE 8 | #define VDB_HEADER_INCLUDE 9 | 10 | // STREAM MODE - Run a block of code without blocking the caller. 11 | // EXAMPLE - 12 | // if (vdb_begin()) { 13 | // vdb_point() 14 | // vdb_end(); 15 | // } 16 | int vdb_begin(); // Returns true if vdb is not already busy sending data 17 | void vdb_end(); 18 | 19 | // LOOP MODE - Run a block of code at a specified framerate until 20 | // we receive a 'continue' signal from the client. 21 | // EXAMPLE - 22 | // while (vdb_loop(60)) { 23 | // static float t = 0.0f; t += 1.0f/60.0f; 24 | // vdb_point(cosf(t), sinf(t)); 25 | // } 26 | int vdb_loop(int fps); 27 | 28 | // These functions assign a RGB color to all subsequent draw calls 29 | // The ramp functions will map the input to a smooth gradient, while 30 | // the primary functions (red/green/blue/...) will color the element 31 | // a specified shade of the given primary color. 32 | void vdb_color_rampf(float value); 33 | void vdb_color_ramp(int i); 34 | void vdb_color_red(int shade); 35 | void vdb_color_green(int shade); 36 | void vdb_color_blue(int shade); 37 | void vdb_color_black(int shade); 38 | void vdb_color_white(int shade); 39 | 40 | // These functions make the next elements semi- or fully opaque, 41 | // with an opacity that can be adjusted in the browser. 42 | void vdb_translucent(); 43 | void vdb_opaque(); 44 | 45 | // These functions maps your input coordinates from the specified 46 | // range to the corresponding edges of the viewport. 47 | void vdb_xrange(float left, float right); 48 | void vdb_yrange(float bottom, float top); 49 | void vdb_zrange(float z_near, float z_far); 50 | 51 | // These are your basic 2D draw commands 52 | void vdb_point(float x, float y); 53 | void vdb_line(float x1, float y1, float x2, float y2); 54 | void vdb_fillRect(float x, float y, float w, float h); 55 | void vdb_circle(float x, float y, float r); 56 | 57 | // This will send a densely packed array of (w x h x 3) bytes and 58 | // render it as an image of RGB values, each one byte. 59 | void vdb_imageRGB8(const void *data, int w, int h); 60 | 61 | // These functions let you modify variables in a vdb_begin or vdb_loop block. 62 | // You can build a simple graphical user interface with sliders and checkboxes. 63 | void vdb_slider1f(const char *in_label, float *x, float min_value, float max_value); 64 | void vdb_slider1i(const char *in_label, int *x, int min_value, int max_value); 65 | void vdb_checkbox(const char *in_label, int *x); 66 | 67 | // You probably don't want to mess with this, but if you do, 68 | // this pushes data to the buffer that is sent on each vdb_end. 69 | // You can use this in conjunction with your own parser at the 70 | // browser-side to implement custom rendering. See app.js for 71 | // an example parser, and the any of the vdb_point/line/... 72 | // for an example render command. 73 | void *vdb_push_bytes(const void *data, int count); 74 | 75 | #endif // VDB_HEADER_INCLUDE 76 | 77 | #define VDB_RELEASE 78 | 79 | #define vdb_assert(EXPR) { if (!(EXPR)) { printf("[error]\n\tAssert failed at line %d in file %s:\n\t'%s'\n", __LINE__, __FILE__, #EXPR); return 0; } } 80 | #ifdef VDB_LOG_DEBUG 81 | #define vdb_log(...) { printf("[vdb] %s@L%d: ", __FILE__, __LINE__); printf(__VA_ARGS__); } 82 | #else 83 | #define vdb_log(...) { } 84 | #endif 85 | #define vdb_log_once(...) { static int first = 1; if (first) { printf("[vdb] "); printf(__VA_ARGS__); first = 0; } } 86 | #define vdb_err_once(...) { static int first = 1; if (first) { printf("[vdb] Error at line %d in file %s:\n[vdb] ", __LINE__, __FILE__); printf(__VA_ARGS__); first = 0; } } 87 | #define vdb_critical(EXPR) if (!(EXPR)) { printf("[vdb] Something went wrong at line %d in file %s\n", __LINE__, __FILE__); vdb_shared->critical_error = 1; return 0; } 88 | 89 | #if defined(_WIN32) || defined(_WIN64) 90 | #define VDB_WINDOWS 91 | #define _CRT_SECURE_NO_WARNINGS 92 | #define WIN32_LEAN_AND_MEAN 93 | #include 94 | #else 95 | #define VDB_UNIX 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | #endif 103 | #include 104 | #include 105 | #include 106 | 107 | // Draw commands are stored in a work buffer that is allocated 108 | // once on the first vdb_begin call, and stays a fixed size that 109 | // is given below in number-of-bytes. If you are memory constrained, 110 | // or if you need more memory than allocated by default, you can 111 | // define your own work buffer size before #including vdb. 112 | #ifndef VDB_WORK_BUFFER_SIZE 113 | #define VDB_WORK_BUFFER_SIZE (32*1024*1024) 114 | #endif 115 | 116 | // Messages received from the browser are stored in a buffer that 117 | // is allocated once on the first vdb_begin call, and stays a fixed 118 | // size is given below in number-of-bytes. If you are sending large 119 | // messages from the browser back to the application you can define 120 | // your own recv buffer size before #including vdb. 121 | #ifndef VDB_RECV_BUFFER_SIZE 122 | #define VDB_RECV_BUFFER_SIZE (1024*1024) 123 | #endif 124 | 125 | #ifndef VDB_LISTEN_PORT 126 | #define VDB_LISTEN_PORT 8000 127 | #endif 128 | 129 | #if VDB_LISTEN_PORT < 1024 || VDB_LISTEN_PORT > 65535 130 | #error "[vdb] The specified listen port is outside of the valid range (1024-65535)" 131 | #endif 132 | 133 | #define VDB_LITTLE_ENDIAN 134 | #if !defined(VDB_LITTLE_ENDIAN) && !defined(VDB_BIG_ENDIAN) 135 | #error "You must define either VDB_LITTLE_ENDIAN or VDB_BIG_ENDIAN" 136 | #endif 137 | 138 | #define VDB_LABEL_LENGTH 16 139 | typedef struct 140 | { 141 | char chars[VDB_LABEL_LENGTH+1]; 142 | } vdb_label_t; 143 | 144 | #define VDB_MAX_VAR_COUNT 1024 145 | typedef struct 146 | { 147 | vdb_label_t var_label[VDB_MAX_VAR_COUNT]; 148 | float var_value[VDB_MAX_VAR_COUNT]; 149 | int var_count; 150 | 151 | int flag_continue; 152 | 153 | int mouse_click; 154 | float mouse_click_x; 155 | float mouse_click_y; 156 | } vdb_status_t; 157 | 158 | typedef struct 159 | { 160 | #ifdef VDB_WINDOWS 161 | volatile HANDLE send_semaphore; 162 | volatile LONG busy; 163 | volatile int bytes_to_send; 164 | #else 165 | int bytes_to_send; 166 | pid_t recv_pid; 167 | pid_t send_pid; 168 | // These pipes are used for flow control between the main thread and the sending thread 169 | // The sending thread blocks on a read on pipe_ready, until the main thread signals the 170 | // pipe by write on pipe_ready. The main thread checks if sending is complete by polling 171 | // (non-blocking) pipe_done, which is signalled by the sending thread. 172 | int ready[2]; // [0]: read, [1]: send 173 | int done[2];// [0]: read, [1]: send 174 | #endif 175 | 176 | int has_send_thread; 177 | int critical_error; 178 | int has_connection; 179 | int work_buffer_used; 180 | char swapbuffer1[VDB_WORK_BUFFER_SIZE]; 181 | char swapbuffer2[VDB_WORK_BUFFER_SIZE]; 182 | char *work_buffer; 183 | char *send_buffer; 184 | 185 | char recv_buffer[VDB_RECV_BUFFER_SIZE]; 186 | 187 | vdb_status_t status; 188 | } vdb_shared_t; 189 | 190 | static vdb_shared_t *vdb_shared = 0; 191 | 192 | int vdb_cmp_label(vdb_label_t *a, vdb_label_t *b) 193 | { 194 | int i; 195 | for (i = 0; i < VDB_LABEL_LENGTH; i++) 196 | if (a->chars[i] != b->chars[i]) 197 | return 0; 198 | return 1; 199 | } 200 | 201 | void vdb_copy_label(vdb_label_t *dst, const char *src) 202 | { 203 | int i = 0; 204 | while (i < VDB_LABEL_LENGTH && src[i]) 205 | { 206 | dst->chars[i] = src[i]; 207 | i++; 208 | } 209 | while (i < VDB_LABEL_LENGTH) 210 | { 211 | dst->chars[i] = ' '; 212 | i++; 213 | } 214 | dst->chars[VDB_LABEL_LENGTH] = 0; 215 | } 216 | 217 | #ifdef VDB_RELEASE 218 | // This variable will be defined at the bottom of the concatenated header file 219 | // upon running the make_release_lib program. 220 | extern const char *vdb_html_page; 221 | const char *get_vdb_html_page() { return vdb_html_page; } 222 | #else 223 | // If we are not the release version, we will load app.html from disk and serve that 224 | const char *get_vdb_html_page() 225 | { 226 | static char static_buffer[1024*1024]; 227 | FILE *file = fopen("../app.html", "rb"); // @ todo: robust file reading 228 | vdb_assert(file); 229 | fread(static_buffer, 1, 1024*1024, file); // @ todo: robust file reading 230 | fclose(file); 231 | return static_buffer; 232 | } 233 | #endif 234 | 235 | 236 | // Begin auto-include tcp.c 237 | // interface 238 | int tcp_listen(int port); 239 | int tcp_accept(); 240 | int tcp_shutdown(); 241 | int tcp_send(const void *data, int size, int *sent_bytes); 242 | int tcp_recv(void *buffer, int capacity, int *read_bytes); 243 | int tcp_sendall(const void *data, int size); 244 | 245 | // implementation 246 | #if defined(_WIN32) || defined(_WIN64) 247 | 248 | #define TCP_WINDOWS 249 | #define WIN32_LEAN_AND_MEAN 250 | #include 251 | #include 252 | #pragma comment(lib, "ws2_32.lib") 253 | #define TCP_INVALID_SOCKET INVALID_SOCKET 254 | #define TCP_SOCKET_ERROR SOCKET_ERROR 255 | #define tcp_cleanup() WSACleanup() 256 | #define tcp_close(s) closesocket(s) 257 | #define tcp_socket_t SOCKET 258 | 259 | #else 260 | 261 | #define TCP_UNIX 262 | #include 263 | #include 264 | #include 265 | #include 266 | #include 267 | #include 268 | #include 269 | #define TCP_INVALID_SOCKET -1 270 | #define TCP_SOCKET_ERROR -1 271 | #define tcp_cleanup() 272 | #define tcp_close(s) close(s) 273 | #define tcp_socket_t int 274 | 275 | #endif 276 | 277 | static tcp_socket_t tcp_client_socket = 0; 278 | static tcp_socket_t tcp_listen_socket = 0; 279 | static int tcp_has_client_socket = 0; 280 | static int tcp_has_listen_socket = 0; 281 | 282 | #ifdef TCP_WINDOWS 283 | int tcp_init() 284 | { 285 | struct WSAData wsa; 286 | if (WSAStartup(MAKEWORD(2,2), &wsa) != NO_ERROR) 287 | return 0; 288 | return 1; 289 | } 290 | #else 291 | int tcp_init() { return 1; } 292 | #endif 293 | 294 | int tcp_listen(int listen_port) 295 | { 296 | #if 1 // INADDRY_ANY strategy 297 | 298 | int enable_reuse = 1; 299 | struct sockaddr_in addr = {0}; 300 | tcp_has_listen_socket = 0; 301 | 302 | if (!tcp_init()) 303 | return 0; 304 | 305 | tcp_listen_socket = socket(AF_INET, SOCK_STREAM, 0); 306 | if (tcp_listen_socket == TCP_INVALID_SOCKET) 307 | { 308 | tcp_cleanup(); 309 | return 0; 310 | } 311 | 312 | if (setsockopt(tcp_listen_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable_reuse, sizeof(int)) == TCP_SOCKET_ERROR) 313 | { 314 | tcp_close(tcp_listen_socket); 315 | tcp_cleanup(); 316 | return 0; 317 | } 318 | 319 | addr.sin_family = AF_INET; 320 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 321 | addr.sin_port = htons((unsigned short)listen_port); 322 | if (bind(tcp_listen_socket, (struct sockaddr*)&addr, sizeof(addr)) == TCP_SOCKET_ERROR) 323 | { 324 | tcp_close(tcp_listen_socket); 325 | tcp_cleanup(); 326 | return 0; 327 | } 328 | 329 | if (listen(tcp_listen_socket, 1) == TCP_SOCKET_ERROR) 330 | { 331 | tcp_close(tcp_listen_socket); 332 | tcp_cleanup(); 333 | return 0; 334 | } 335 | tcp_has_listen_socket = 1; 336 | return tcp_has_listen_socket; 337 | 338 | #else // Search for first available address strategy 339 | 340 | struct addrinfo *a = 0; 341 | struct addrinfo *info = 0; 342 | struct addrinfo hints = {0}; 343 | char listen_port_str[64]; 344 | sprintf(listen_port_str, "%d", listen_port); 345 | 346 | tcp_has_listen_socket = 0; 347 | 348 | #ifdef TCP_WINDOWS 349 | struct WSAData wsa; 350 | if (WSAStartup(MAKEWORD(2,2), &wsa) != NO_ERROR) 351 | return 0; 352 | #endif 353 | 354 | // Fetch available addresses 355 | hints.ai_family = AF_UNSPEC; // Don't care if ipv4 or ipv6 356 | hints.ai_socktype = SOCK_STREAM; // Tcp 357 | hints.ai_flags = AI_PASSIVE; // Arbitrary IP? 358 | if (getaddrinfo(0, listen_port_str, &hints, &info) != 0) 359 | return 0; 360 | 361 | // Bind socket to first available 362 | for (a = info; a != 0; a = a->ai_next) 363 | { 364 | #ifdef TCP_WINDOWS 365 | DWORD yes = 1; 366 | #else 367 | int yes = 1; 368 | #endif 369 | int port = 0; 370 | 371 | if (a->ai_addr->sa_family == AF_INET) 372 | port = ntohs((int)((struct sockaddr_in*)a->ai_addr)->sin_port); 373 | else 374 | port = ntohs((int)((struct sockaddr_in6*)a->ai_addr)->sin6_port); 375 | if (port != listen_port) 376 | continue; 377 | 378 | tcp_listen_socket = socket(a->ai_family, a->ai_socktype, a->ai_protocol); 379 | if (tcp_listen_socket == TCP_INVALID_SOCKET) 380 | continue; 381 | 382 | if (setsockopt(tcp_listen_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)) == TCP_SOCKET_ERROR) 383 | { 384 | tcp_close(tcp_listen_socket); 385 | tcp_cleanup(); 386 | continue; 387 | } 388 | 389 | if (bind(tcp_listen_socket, a->ai_addr, (int)a->ai_addrlen) == TCP_SOCKET_ERROR) 390 | { 391 | tcp_close(tcp_listen_socket); 392 | tcp_cleanup(); 393 | continue; 394 | } 395 | if (listen(tcp_listen_socket, 1) == TCP_SOCKET_ERROR) 396 | { 397 | tcp_close(tcp_listen_socket); 398 | tcp_cleanup(); 399 | continue; 400 | } 401 | tcp_has_listen_socket = 1; 402 | break; 403 | } 404 | freeaddrinfo(info); 405 | return tcp_has_listen_socket; 406 | #endif 407 | } 408 | 409 | int tcp_accept() 410 | { 411 | tcp_client_socket = accept(tcp_listen_socket, 0, 0); 412 | if (tcp_client_socket == TCP_INVALID_SOCKET) 413 | { 414 | tcp_close(tcp_listen_socket); 415 | tcp_cleanup(); 416 | return 0; 417 | } 418 | tcp_has_client_socket = 1; 419 | return 1; 420 | } 421 | 422 | int tcp_shutdown() 423 | { 424 | if (tcp_has_client_socket) tcp_close(tcp_client_socket); 425 | if (tcp_has_listen_socket) tcp_close(tcp_listen_socket); 426 | tcp_has_client_socket = 0; 427 | tcp_has_listen_socket = 0; 428 | tcp_cleanup(); 429 | return 1; 430 | } 431 | 432 | int tcp_close_client() 433 | { 434 | tcp_has_client_socket = 0; 435 | tcp_close(tcp_client_socket); 436 | return 1; 437 | } 438 | 439 | int tcp_send(const void *data, int size, int *sent_bytes) 440 | { 441 | *sent_bytes = send(tcp_client_socket, (const char*)data, size, 0); 442 | if (*sent_bytes >= 0) return 1; 443 | else return 0; 444 | } 445 | 446 | int tcp_recv(void *buffer, int capacity, int *read_bytes) 447 | { 448 | *read_bytes = recv(tcp_client_socket, (char*)buffer, capacity, 0); 449 | if (*read_bytes > 0) return 1; 450 | else return 0; 451 | } 452 | 453 | int tcp_sendall(const void *buffer, int bytes_to_send) 454 | { 455 | int sent; 456 | int remaining = bytes_to_send; 457 | const char *ptr = (const char*)buffer; 458 | while (remaining > 0) 459 | { 460 | if (!tcp_send(ptr, remaining, &sent)) 461 | return 0; 462 | remaining -= sent; 463 | ptr += sent; 464 | if (remaining < 0) 465 | return 0; 466 | } 467 | return 1; 468 | } 469 | 470 | // End auto-include tcp.c 471 | 472 | // Begin auto-include websocket.c 473 | // websocket.c - Version 1 - Websocket utilities 474 | 475 | // interface 476 | typedef struct 477 | { 478 | char *payload; 479 | int length; 480 | int fin; 481 | int opcode; 482 | } vdb_msg_t; 483 | int vdb_generate_handshake(const char *request, int request_len, char **out_response, int *out_length); 484 | int vdb_self_test(); 485 | void vdb_form_frame(int length, int opcode, unsigned char **out_frame, int *out_length); 486 | int vdb_parse_message(void *recv_buffer, int received, vdb_msg_t *msg); 487 | 488 | // implementation 489 | #define MBEDTLS_SHA1_C 490 | 491 | // Begin auto-include sha1.c 492 | /* 493 | * FIPS-180-1 compliant SHA-1 implementation 494 | * 495 | * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved 496 | * SPDX-License-Identifier: Apache-2.0 497 | * 498 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 499 | * not use this file except in compliance with the License. 500 | * You may obtain a copy of the License at 501 | * 502 | * http://www.apache.org/licenses/LICENSE-2.0 503 | * 504 | * Unless required by applicable law or agreed to in writing, software 505 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 506 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 507 | * See the License for the specific language governing permissions and 508 | * limitations under the License. 509 | * 510 | * This file is part of mbed TLS (https://tls.mbed.org) 511 | */ 512 | /* 513 | * The SHA-1 standard was published by NIST in 1993. 514 | * 515 | * http://www.itl.nist.gov/fipspubs/fip180-1.htm 516 | */ 517 | 518 | #if defined(MBEDTLS_SHA1_C) 519 | 520 | // Begin auto-include sha1.h 521 | /** 522 | * \file sha1.h 523 | * 524 | * \brief SHA-1 cryptographic hash function 525 | * 526 | * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved 527 | * SPDX-License-Identifier: Apache-2.0 528 | * 529 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 530 | * not use this file except in compliance with the License. 531 | * You may obtain a copy of the License at 532 | * 533 | * http://www.apache.org/licenses/LICENSE-2.0 534 | * 535 | * Unless required by applicable law or agreed to in writing, software 536 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 537 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 538 | * See the License for the specific language governing permissions and 539 | * limitations under the License. 540 | * 541 | * This file is part of mbed TLS (https://tls.mbed.org) 542 | */ 543 | #ifndef MBEDTLS_SHA1_H 544 | #define MBEDTLS_SHA1_H 545 | 546 | #include 547 | #include 548 | 549 | #if !defined(MBEDTLS_SHA1_ALT) 550 | // Regular implementation 551 | // 552 | 553 | #ifdef __cplusplus 554 | extern "C" { 555 | #endif 556 | 557 | /** 558 | * \brief SHA-1 context structure 559 | */ 560 | typedef struct 561 | { 562 | uint32_t total[2]; /*!< number of bytes processed */ 563 | uint32_t state[5]; /*!< intermediate digest state */ 564 | unsigned char buffer[64]; /*!< data block being processed */ 565 | } 566 | mbedtls_sha1_context; 567 | 568 | /** 569 | * \brief Initialize SHA-1 context 570 | * 571 | * \param ctx SHA-1 context to be initialized 572 | */ 573 | void mbedtls_sha1_init( mbedtls_sha1_context *ctx ); 574 | 575 | /** 576 | * \brief Clear SHA-1 context 577 | * 578 | * \param ctx SHA-1 context to be cleared 579 | */ 580 | void mbedtls_sha1_free( mbedtls_sha1_context *ctx ); 581 | 582 | /** 583 | * \brief Clone (the state of) a SHA-1 context 584 | * 585 | * \param dst The destination context 586 | * \param src The context to be cloned 587 | */ 588 | void mbedtls_sha1_clone( mbedtls_sha1_context *dst, 589 | const mbedtls_sha1_context *src ); 590 | 591 | /** 592 | * \brief SHA-1 context setup 593 | * 594 | * \param ctx context to be initialized 595 | */ 596 | void mbedtls_sha1_starts( mbedtls_sha1_context *ctx ); 597 | 598 | /** 599 | * \brief SHA-1 process buffer 600 | * 601 | * \param ctx SHA-1 context 602 | * \param input buffer holding the data 603 | * \param ilen length of the input data 604 | */ 605 | void mbedtls_sha1_update( mbedtls_sha1_context *ctx, const unsigned char *input, size_t ilen ); 606 | 607 | /** 608 | * \brief SHA-1 final digest 609 | * 610 | * \param ctx SHA-1 context 611 | * \param output SHA-1 checksum result 612 | */ 613 | void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, unsigned char output[20] ); 614 | 615 | /* Internal use */ 616 | void mbedtls_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[64] ); 617 | 618 | #ifdef __cplusplus 619 | } 620 | #endif 621 | 622 | #else /* MBEDTLS_SHA1_ALT */ 623 | // #include "sha1_alt.h" 624 | #endif /* MBEDTLS_SHA1_ALT */ 625 | 626 | #ifdef __cplusplus 627 | extern "C" { 628 | #endif 629 | 630 | /** 631 | * \brief Output = SHA-1( input buffer ) 632 | * 633 | * \param input buffer holding the data 634 | * \param ilen length of the input data 635 | * \param output SHA-1 checksum result 636 | */ 637 | void mbedtls_sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ); 638 | 639 | /** 640 | * \brief Checkup routine 641 | * 642 | * \return 0 if successful, or 1 if the test failed 643 | */ 644 | int mbedtls_sha1_self_test( int verbose ); 645 | 646 | #ifdef __cplusplus 647 | } 648 | #endif 649 | 650 | #endif /* mbedtls_sha1.h */ 651 | 652 | // End auto-include sha1.h 653 | #include 654 | 655 | #if !defined(MBEDTLS_SHA1_ALT) 656 | 657 | /* Implementation that should never be optimized out by the compiler */ 658 | static void mbedtls_zeroize( void *v, size_t n ) { 659 | volatile unsigned char *p = (unsigned char*)v; while( n-- ) *p++ = 0; 660 | } 661 | 662 | /* 663 | * 32-bit integer manipulation macros (big endian) 664 | */ 665 | #ifndef GET_UINT32_BE 666 | #define GET_UINT32_BE(n,b,i) \ 667 | { \ 668 | (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ 669 | | ( (uint32_t) (b)[(i) + 1] << 16 ) \ 670 | | ( (uint32_t) (b)[(i) + 2] << 8 ) \ 671 | | ( (uint32_t) (b)[(i) + 3] ); \ 672 | } 673 | #endif 674 | 675 | #ifndef PUT_UINT32_BE 676 | #define PUT_UINT32_BE(n,b,i) \ 677 | { \ 678 | (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ 679 | (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ 680 | (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ 681 | (b)[(i) + 3] = (unsigned char) ( (n) ); \ 682 | } 683 | #endif 684 | 685 | void mbedtls_sha1_init( mbedtls_sha1_context *ctx ) 686 | { 687 | memset( ctx, 0, sizeof( mbedtls_sha1_context ) ); 688 | } 689 | 690 | void mbedtls_sha1_free( mbedtls_sha1_context *ctx ) 691 | { 692 | if( ctx == NULL ) 693 | return; 694 | 695 | mbedtls_zeroize( ctx, sizeof( mbedtls_sha1_context ) ); 696 | } 697 | 698 | void mbedtls_sha1_clone( mbedtls_sha1_context *dst, 699 | const mbedtls_sha1_context *src ) 700 | { 701 | *dst = *src; 702 | } 703 | 704 | /* 705 | * SHA-1 context setup 706 | */ 707 | void mbedtls_sha1_starts( mbedtls_sha1_context *ctx ) 708 | { 709 | ctx->total[0] = 0; 710 | ctx->total[1] = 0; 711 | 712 | ctx->state[0] = 0x67452301; 713 | ctx->state[1] = 0xEFCDAB89; 714 | ctx->state[2] = 0x98BADCFE; 715 | ctx->state[3] = 0x10325476; 716 | ctx->state[4] = 0xC3D2E1F0; 717 | } 718 | 719 | #if !defined(MBEDTLS_SHA1_PROCESS_ALT) 720 | void mbedtls_sha1_process( mbedtls_sha1_context *ctx, const unsigned char data[64] ) 721 | { 722 | uint32_t temp, W[16], A, B, C, D, E; 723 | 724 | GET_UINT32_BE( W[ 0], data, 0 ); 725 | GET_UINT32_BE( W[ 1], data, 4 ); 726 | GET_UINT32_BE( W[ 2], data, 8 ); 727 | GET_UINT32_BE( W[ 3], data, 12 ); 728 | GET_UINT32_BE( W[ 4], data, 16 ); 729 | GET_UINT32_BE( W[ 5], data, 20 ); 730 | GET_UINT32_BE( W[ 6], data, 24 ); 731 | GET_UINT32_BE( W[ 7], data, 28 ); 732 | GET_UINT32_BE( W[ 8], data, 32 ); 733 | GET_UINT32_BE( W[ 9], data, 36 ); 734 | GET_UINT32_BE( W[10], data, 40 ); 735 | GET_UINT32_BE( W[11], data, 44 ); 736 | GET_UINT32_BE( W[12], data, 48 ); 737 | GET_UINT32_BE( W[13], data, 52 ); 738 | GET_UINT32_BE( W[14], data, 56 ); 739 | GET_UINT32_BE( W[15], data, 60 ); 740 | 741 | #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) 742 | 743 | #define R(t) \ 744 | ( \ 745 | temp = W[( t - 3 ) & 0x0F] ^ W[( t - 8 ) & 0x0F] ^ \ 746 | W[( t - 14 ) & 0x0F] ^ W[ t & 0x0F], \ 747 | ( W[t & 0x0F] = S(temp,1) ) \ 748 | ) 749 | 750 | #define P(a,b,c,d,e,x) \ 751 | { \ 752 | e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ 753 | } 754 | 755 | A = ctx->state[0]; 756 | B = ctx->state[1]; 757 | C = ctx->state[2]; 758 | D = ctx->state[3]; 759 | E = ctx->state[4]; 760 | 761 | #define F(x,y,z) (z ^ (x & (y ^ z))) 762 | #define K 0x5A827999 763 | 764 | P( A, B, C, D, E, W[0] ); 765 | P( E, A, B, C, D, W[1] ); 766 | P( D, E, A, B, C, W[2] ); 767 | P( C, D, E, A, B, W[3] ); 768 | P( B, C, D, E, A, W[4] ); 769 | P( A, B, C, D, E, W[5] ); 770 | P( E, A, B, C, D, W[6] ); 771 | P( D, E, A, B, C, W[7] ); 772 | P( C, D, E, A, B, W[8] ); 773 | P( B, C, D, E, A, W[9] ); 774 | P( A, B, C, D, E, W[10] ); 775 | P( E, A, B, C, D, W[11] ); 776 | P( D, E, A, B, C, W[12] ); 777 | P( C, D, E, A, B, W[13] ); 778 | P( B, C, D, E, A, W[14] ); 779 | P( A, B, C, D, E, W[15] ); 780 | P( E, A, B, C, D, R(16) ); 781 | P( D, E, A, B, C, R(17) ); 782 | P( C, D, E, A, B, R(18) ); 783 | P( B, C, D, E, A, R(19) ); 784 | 785 | #undef K 786 | #undef F 787 | 788 | #define F(x,y,z) (x ^ y ^ z) 789 | #define K 0x6ED9EBA1 790 | 791 | P( A, B, C, D, E, R(20) ); 792 | P( E, A, B, C, D, R(21) ); 793 | P( D, E, A, B, C, R(22) ); 794 | P( C, D, E, A, B, R(23) ); 795 | P( B, C, D, E, A, R(24) ); 796 | P( A, B, C, D, E, R(25) ); 797 | P( E, A, B, C, D, R(26) ); 798 | P( D, E, A, B, C, R(27) ); 799 | P( C, D, E, A, B, R(28) ); 800 | P( B, C, D, E, A, R(29) ); 801 | P( A, B, C, D, E, R(30) ); 802 | P( E, A, B, C, D, R(31) ); 803 | P( D, E, A, B, C, R(32) ); 804 | P( C, D, E, A, B, R(33) ); 805 | P( B, C, D, E, A, R(34) ); 806 | P( A, B, C, D, E, R(35) ); 807 | P( E, A, B, C, D, R(36) ); 808 | P( D, E, A, B, C, R(37) ); 809 | P( C, D, E, A, B, R(38) ); 810 | P( B, C, D, E, A, R(39) ); 811 | 812 | #undef K 813 | #undef F 814 | 815 | #define F(x,y,z) ((x & y) | (z & (x | y))) 816 | #define K 0x8F1BBCDC 817 | 818 | P( A, B, C, D, E, R(40) ); 819 | P( E, A, B, C, D, R(41) ); 820 | P( D, E, A, B, C, R(42) ); 821 | P( C, D, E, A, B, R(43) ); 822 | P( B, C, D, E, A, R(44) ); 823 | P( A, B, C, D, E, R(45) ); 824 | P( E, A, B, C, D, R(46) ); 825 | P( D, E, A, B, C, R(47) ); 826 | P( C, D, E, A, B, R(48) ); 827 | P( B, C, D, E, A, R(49) ); 828 | P( A, B, C, D, E, R(50) ); 829 | P( E, A, B, C, D, R(51) ); 830 | P( D, E, A, B, C, R(52) ); 831 | P( C, D, E, A, B, R(53) ); 832 | P( B, C, D, E, A, R(54) ); 833 | P( A, B, C, D, E, R(55) ); 834 | P( E, A, B, C, D, R(56) ); 835 | P( D, E, A, B, C, R(57) ); 836 | P( C, D, E, A, B, R(58) ); 837 | P( B, C, D, E, A, R(59) ); 838 | 839 | #undef K 840 | #undef F 841 | 842 | #define F(x,y,z) (x ^ y ^ z) 843 | #define K 0xCA62C1D6 844 | 845 | P( A, B, C, D, E, R(60) ); 846 | P( E, A, B, C, D, R(61) ); 847 | P( D, E, A, B, C, R(62) ); 848 | P( C, D, E, A, B, R(63) ); 849 | P( B, C, D, E, A, R(64) ); 850 | P( A, B, C, D, E, R(65) ); 851 | P( E, A, B, C, D, R(66) ); 852 | P( D, E, A, B, C, R(67) ); 853 | P( C, D, E, A, B, R(68) ); 854 | P( B, C, D, E, A, R(69) ); 855 | P( A, B, C, D, E, R(70) ); 856 | P( E, A, B, C, D, R(71) ); 857 | P( D, E, A, B, C, R(72) ); 858 | P( C, D, E, A, B, R(73) ); 859 | P( B, C, D, E, A, R(74) ); 860 | P( A, B, C, D, E, R(75) ); 861 | P( E, A, B, C, D, R(76) ); 862 | P( D, E, A, B, C, R(77) ); 863 | P( C, D, E, A, B, R(78) ); 864 | P( B, C, D, E, A, R(79) ); 865 | 866 | #undef K 867 | #undef F 868 | 869 | ctx->state[0] += A; 870 | ctx->state[1] += B; 871 | ctx->state[2] += C; 872 | ctx->state[3] += D; 873 | ctx->state[4] += E; 874 | } 875 | #endif /* !MBEDTLS_SHA1_PROCESS_ALT */ 876 | 877 | /* 878 | * SHA-1 process buffer 879 | */ 880 | void mbedtls_sha1_update( mbedtls_sha1_context *ctx, const unsigned char *input, size_t ilen ) 881 | { 882 | size_t fill; 883 | uint32_t left; 884 | 885 | if( ilen == 0 ) 886 | return; 887 | 888 | left = ctx->total[0] & 0x3F; 889 | fill = 64 - left; 890 | 891 | ctx->total[0] += (uint32_t) ilen; 892 | ctx->total[0] &= 0xFFFFFFFF; 893 | 894 | if( ctx->total[0] < (uint32_t) ilen ) 895 | ctx->total[1]++; 896 | 897 | if( left && ilen >= fill ) 898 | { 899 | memcpy( (void *) (ctx->buffer + left), input, fill ); 900 | mbedtls_sha1_process( ctx, ctx->buffer ); 901 | input += fill; 902 | ilen -= fill; 903 | left = 0; 904 | } 905 | 906 | while( ilen >= 64 ) 907 | { 908 | mbedtls_sha1_process( ctx, input ); 909 | input += 64; 910 | ilen -= 64; 911 | } 912 | 913 | if( ilen > 0 ) 914 | memcpy( (void *) (ctx->buffer + left), input, ilen ); 915 | } 916 | 917 | static const unsigned char sha1_padding[64] = 918 | { 919 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 920 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 921 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 922 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 923 | }; 924 | 925 | /* 926 | * SHA-1 final digest 927 | */ 928 | void mbedtls_sha1_finish( mbedtls_sha1_context *ctx, unsigned char output[20] ) 929 | { 930 | uint32_t last, padn; 931 | uint32_t high, low; 932 | unsigned char msglen[8]; 933 | 934 | high = ( ctx->total[0] >> 29 ) 935 | | ( ctx->total[1] << 3 ); 936 | low = ( ctx->total[0] << 3 ); 937 | 938 | PUT_UINT32_BE( high, msglen, 0 ); 939 | PUT_UINT32_BE( low, msglen, 4 ); 940 | 941 | last = ctx->total[0] & 0x3F; 942 | padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); 943 | 944 | mbedtls_sha1_update( ctx, sha1_padding, padn ); 945 | mbedtls_sha1_update( ctx, msglen, 8 ); 946 | 947 | PUT_UINT32_BE( ctx->state[0], output, 0 ); 948 | PUT_UINT32_BE( ctx->state[1], output, 4 ); 949 | PUT_UINT32_BE( ctx->state[2], output, 8 ); 950 | PUT_UINT32_BE( ctx->state[3], output, 12 ); 951 | PUT_UINT32_BE( ctx->state[4], output, 16 ); 952 | } 953 | 954 | #endif /* !MBEDTLS_SHA1_ALT */ 955 | 956 | /* 957 | * output = SHA-1( input buffer ) 958 | */ 959 | void mbedtls_sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ) 960 | { 961 | mbedtls_sha1_context ctx; 962 | 963 | mbedtls_sha1_init( &ctx ); 964 | mbedtls_sha1_starts( &ctx ); 965 | mbedtls_sha1_update( &ctx, input, ilen ); 966 | mbedtls_sha1_finish( &ctx, output ); 967 | mbedtls_sha1_free( &ctx ); 968 | } 969 | 970 | #if defined(MBEDTLS_SELF_TEST) 971 | /* 972 | * FIPS-180-1 test vectors 973 | */ 974 | static const unsigned char sha1_test_buf[3][57] = 975 | { 976 | { "abc" }, 977 | { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, 978 | { "" } 979 | }; 980 | 981 | static const int sha1_test_buflen[3] = 982 | { 983 | 3, 56, 1000 984 | }; 985 | 986 | static const unsigned char sha1_test_sum[3][20] = 987 | { 988 | { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 989 | 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, 990 | { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 991 | 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, 992 | { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 993 | 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } 994 | }; 995 | 996 | /* 997 | * Checkup routine 998 | */ 999 | int mbedtls_sha1_self_test( int verbose ) 1000 | { 1001 | int i, j, buflen, ret = 0; 1002 | unsigned char buf[1024]; 1003 | unsigned char sha1sum[20]; 1004 | mbedtls_sha1_context ctx; 1005 | 1006 | mbedtls_sha1_init( &ctx ); 1007 | 1008 | /* 1009 | * SHA-1 1010 | */ 1011 | for( i = 0; i < 3; i++ ) 1012 | { 1013 | if( verbose != 0 ) 1014 | mbedtls_printf( " SHA-1 test #%d: ", i + 1 ); 1015 | 1016 | mbedtls_sha1_starts( &ctx ); 1017 | 1018 | if( i == 2 ) 1019 | { 1020 | memset( buf, 'a', buflen = 1000 ); 1021 | 1022 | for( j = 0; j < 1000; j++ ) 1023 | mbedtls_sha1_update( &ctx, buf, buflen ); 1024 | } 1025 | else 1026 | mbedtls_sha1_update( &ctx, sha1_test_buf[i], 1027 | sha1_test_buflen[i] ); 1028 | 1029 | mbedtls_sha1_finish( &ctx, sha1sum ); 1030 | 1031 | if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) 1032 | { 1033 | if( verbose != 0 ) 1034 | mbedtls_printf( "failed\n" ); 1035 | 1036 | ret = 1; 1037 | goto exit; 1038 | } 1039 | 1040 | if( verbose != 0 ) 1041 | mbedtls_printf( "passed\n" ); 1042 | } 1043 | 1044 | if( verbose != 0 ) 1045 | mbedtls_printf( "\n" ); 1046 | 1047 | exit: 1048 | mbedtls_sha1_free( &ctx ); 1049 | 1050 | return( ret ); 1051 | } 1052 | 1053 | #endif /* MBEDTLS_SELF_TEST */ 1054 | 1055 | #endif /* MBEDTLS_SHA1_C */ 1056 | 1057 | // End auto-include sha1.h 1058 | 1059 | int vdb_extract_user_key(const char *request, int request_len, char *key) 1060 | { 1061 | // The user request contains this string somewhere in it: 1062 | // "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n", This 1063 | // code extracts the "dGhlIHNhbXBsZSBub25jZQ==" part. 1064 | int i,j; 1065 | int key_len = 0; 1066 | const char *pattern = "Sec-WebSocket-Key:"; 1067 | int pattern_len = (int)strlen(pattern); 1068 | vdb_assert(request_len >= pattern_len); 1069 | for (i = 0; i < request_len-pattern_len; i++) 1070 | { 1071 | int is_equal = 1; 1072 | for (j = 0; j < pattern_len; j++) 1073 | { 1074 | if (request[i+j] != pattern[j]) 1075 | is_equal = 0; 1076 | } 1077 | if (is_equal) 1078 | { 1079 | const char *src = request + i + pattern_len + 1; 1080 | while (*src && *src != '\r') 1081 | { 1082 | key[key_len++] = *src; 1083 | src++; 1084 | } 1085 | } 1086 | } 1087 | return key_len; 1088 | } 1089 | 1090 | int vdb_generate_accept_key(const char *request, int request_len, char *accept_key) 1091 | { 1092 | // The WebSocket standard has defined that the server must respond to a connection 1093 | // request with a hash key that is generated from the user's key. This code implements 1094 | // the stuff in https://tools.ietf.org/html/rfc6455#section-1.3 1095 | char user_key[1024] = {0}; 1096 | unsigned char new_key[1024] = {0}; 1097 | int user_len = 0; 1098 | int new_len = 0; 1099 | 1100 | user_len = vdb_extract_user_key(request, request_len, user_key); 1101 | vdb_assert(user_len > 0); 1102 | 1103 | // Concatenate salt and user keys 1104 | { 1105 | const char *salt = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 1106 | int salt_len = (int)strlen(salt); 1107 | for (new_len = 0; new_len < user_len; new_len++) 1108 | new_key[new_len] = (unsigned char)user_key[new_len]; 1109 | for (; new_len < user_len + salt_len; new_len++) 1110 | new_key[new_len] = (unsigned char)salt[new_len-user_len]; 1111 | } 1112 | 1113 | // Compute the accept-key 1114 | { 1115 | // Compute sha1 hash 1116 | unsigned char sha1[20]; 1117 | mbedtls_sha1(new_key, (size_t)new_len, sha1); 1118 | 1119 | // Convert to base64 null-terminated string 1120 | { 1121 | const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 1122 | unsigned char x1, x2, x3; 1123 | int i; 1124 | for (i = 2; i <= 20; i += 3) 1125 | { 1126 | x3 = (i < 20) ? sha1[i] : 0; 1127 | x2 = sha1[i-1]; 1128 | x1 = sha1[i-2]; 1129 | *accept_key++ = b64[((x1 >> 2) & 63)]; 1130 | *accept_key++ = b64[((x1 & 3) << 4) | ((x2 >> 4) & 15)]; 1131 | *accept_key++ = b64[((x2 & 15) << 2) | ((x3 >> 6) & 3)]; 1132 | *accept_key++ = (i < 20) ? b64[((x3 >> 0) & 63)] : '='; 1133 | } 1134 | *accept_key = 0; 1135 | } 1136 | } 1137 | 1138 | return 1; 1139 | } 1140 | 1141 | int vdb_generate_handshake(const char *request, int request_len, char **out_response, int *out_length) 1142 | { 1143 | const char *header1 = 1144 | "HTTP/1.1 101 Switching Protocols\r\n" 1145 | "Upgrade: websocket\r\n" 1146 | "Connection: Upgrade\r\n" 1147 | "Sec-WebSocket-Accept: "; 1148 | const char *header2 = "\r\n\r\n"; 1149 | char accept_key[1024]; 1150 | static char response[1024]; 1151 | int response_len = 0; 1152 | size_t i = 0; 1153 | 1154 | vdb_assert(vdb_generate_accept_key(request, request_len, accept_key)); 1155 | for (i = 0; i < strlen(header1); i++) response[response_len++] = header1[i]; 1156 | for (i = 0; i < strlen(accept_key); i++) response[response_len++] = accept_key[i]; 1157 | for (i = 0; i < strlen(header2); i++) response[response_len++] = header2[i]; 1158 | 1159 | *out_response = response; 1160 | *out_length = response_len; 1161 | return 1; 1162 | } 1163 | 1164 | int vdb_self_test() 1165 | { 1166 | int request_len; 1167 | char accept_key[1024]; 1168 | const char *request = 1169 | "GET /chat HTTP/1.1\r\n" 1170 | "Host: server.example.com\r\n" 1171 | "Upgrade: websocket\r\n" 1172 | "Connection: Upgrade\r\n" 1173 | "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" 1174 | "Sec-WebSocket-Protocol: chat, superchat\r\n" 1175 | "Sec-WebSocket-Version: 13\r\n" 1176 | "Origin: http://example.com\r\n\r\n"; 1177 | request_len = (int)strlen(request); 1178 | vdb_generate_accept_key(request, request_len, accept_key); 1179 | vdb_assert(strcmp(accept_key, "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") == 0); 1180 | return 1; 1181 | } 1182 | 1183 | void vdb_print_bytes(void *recv_buffer, int n) 1184 | { 1185 | int index; 1186 | for (index = 0; index < n; index++) 1187 | { 1188 | unsigned char c = ((unsigned char*)recv_buffer)[index]; 1189 | int i; 1190 | printf("%d:\t", index); 1191 | for (i = 7; i >= 4; i--) printf("%d", (c >> i) & 1); 1192 | printf(" "); 1193 | for (i = 3; i >= 0; i--) printf("%d", (c >> i) & 1); 1194 | printf("\n"); 1195 | } 1196 | } 1197 | 1198 | // opcode = 0x2 for binary data 1199 | // opcode = 0x8 for closing handshakes 1200 | void vdb_form_frame(int length, int opcode, unsigned char **out_frame, int *out_length) 1201 | { 1202 | static unsigned char frame[16] = {0}; 1203 | int frame_length = 0; 1204 | { 1205 | // fin rsv1 rsv2 rsv3 opcode 1206 | // 1 0 0 0 xxxx 1207 | frame[0] = (1 << 7) | (opcode & 0xF); 1208 | if (length <= 125) 1209 | { 1210 | frame[1] = (unsigned char)(length & 0xFF); 1211 | frame_length = 2; 1212 | } 1213 | else if (length <= 65535) 1214 | { 1215 | frame[1] = 126; 1216 | frame[2] = (unsigned char)((length >> 8) & 0xFF); 1217 | frame[3] = (unsigned char)((length >> 0) & 0xFF); 1218 | frame_length = 4; 1219 | } 1220 | else 1221 | { 1222 | frame[1] = 127; 1223 | frame[2] = 0; // @ assuming length is max 32 bit 1224 | frame[3] = 0; // @ assuming length is max 32 bit 1225 | frame[4] = 0; // @ assuming length is max 32 bit 1226 | frame[5] = 0; // @ assuming length is max 32 bit 1227 | frame[6] = (length >> 24) & 0xFF; 1228 | frame[7] = (length >> 16) & 0xFF; 1229 | frame[8] = (length >> 8) & 0xFF; 1230 | frame[9] = (length >> 0) & 0xFF; 1231 | frame_length = 10; 1232 | } 1233 | } 1234 | *out_frame = frame; 1235 | *out_length = frame_length; 1236 | } 1237 | 1238 | int vdb_parse_message(void *recv_buffer, int received, vdb_msg_t *msg) 1239 | { 1240 | // https://tools.ietf.org/html/rfc6455#section-5.4 1241 | // Note: WebSocket does not send fields unless 1242 | // they are needed. For example, extended len 1243 | // is not sent if the len fits inside payload 1244 | // len. 1245 | uint32_t opcode; 1246 | uint32_t fin; 1247 | uint64_t len; 1248 | uint32_t mask; 1249 | unsigned char key[4] = {0}; 1250 | unsigned char *frame = (unsigned char*)recv_buffer; 1251 | int i = 0; 1252 | 1253 | // extract header 1254 | vdb_assert(i + 2 <= received); 1255 | opcode = ((frame[i ] >> 0) & 0xF); 1256 | fin = ((frame[i++] >> 7) & 0x1); 1257 | len = ((frame[i ] >> 0) & 0x7F); 1258 | mask = ((frame[i++] >> 7) & 0x1); 1259 | 1260 | // client messages must be masked according to spec 1261 | vdb_assert(mask == 1); 1262 | 1263 | // extract payload length in number of bytes 1264 | if (len == 126) 1265 | { 1266 | vdb_assert(i + 2 <= received); 1267 | len = 0; 1268 | len |= frame[i++]; len <<= 8; 1269 | len |= frame[i++]; 1270 | } 1271 | else if (len == 127) 1272 | { 1273 | vdb_assert(i + 8 <= received); 1274 | len = 0; 1275 | len |= frame[i++]; len <<= 8; 1276 | len |= frame[i++]; len <<= 8; 1277 | len |= frame[i++]; len <<= 8; 1278 | len |= frame[i++]; len <<= 8; 1279 | len |= frame[i++]; len <<= 8; 1280 | len |= frame[i++]; len <<= 8; 1281 | len |= frame[i++]; len <<= 8; 1282 | len |= frame[i++]; 1283 | } 1284 | 1285 | // verify that we read the length correctly 1286 | vdb_assert(len <= (uint64_t)received); 1287 | 1288 | // extract key used to decode payload 1289 | { 1290 | vdb_assert(i + 4 <= received); 1291 | key[0] = frame[i++]; 1292 | key[1] = frame[i++]; 1293 | key[2] = frame[i++]; 1294 | key[3] = frame[i++]; 1295 | } 1296 | 1297 | // decode payload 1298 | { 1299 | int j = 0; 1300 | vdb_assert(i + (int)len <= received); 1301 | for (j = 0; j < (int)len; j++) 1302 | frame[i+j] = frame[i+j] ^ key[j % 4]; 1303 | frame[i+len] = 0; 1304 | } 1305 | 1306 | msg->payload = (char*)(frame + i); 1307 | msg->length = (int)len; 1308 | msg->opcode = (int)opcode; 1309 | msg->fin = (int)fin; 1310 | return 1; 1311 | } 1312 | 1313 | // End auto-include sha1.h 1314 | 1315 | // Begin auto-include vdb_handle_message.c 1316 | 1317 | // This is used by vdb_recv_thread (vdb_network_threads.c) whenever 1318 | // we get a valid message from the client. The client periodically 1319 | // sends status updates at a fixed rate, and some asynchronous events 1320 | // (like mouse clicks and button presses). 1321 | 1322 | // Returns true (1) if we successfully parsed the message 1323 | // or if we did not recognize it. Returns false (0) if something 1324 | // unexpected happened while parsing the message. 1325 | int vdb_handle_message(vdb_msg_t msg, vdb_status_t *status) 1326 | { 1327 | vdb_status_t new_status = *status; 1328 | 1329 | // vdb_log("Got a message (%d): '%s'\n", msg.length, msg.payload); 1330 | // This means the user pressed the 'continue' button 1331 | if (msg.length == 1 && msg.payload[0] == 'c') 1332 | { 1333 | new_status.flag_continue = 1; 1334 | } 1335 | 1336 | // Mouse click event 1337 | if (msg.length > 1 && msg.payload[0] == 'm') 1338 | { 1339 | float x, y; 1340 | vdb_assert(sscanf(msg.payload+1, "%f%f", &x, &y) == 2); 1341 | new_status.mouse_click = 1; 1342 | new_status.mouse_click_x = x; 1343 | new_status.mouse_click_y = y; 1344 | } 1345 | 1346 | // This is a status update that is sent at regular intervals 1347 | if (msg.length > 1 && msg.payload[0] == 's') 1348 | { 1349 | char *str = msg.payload; 1350 | int pos = 0 + 2; 1351 | int got = 0; 1352 | int i; 1353 | int n; 1354 | 1355 | // read 'number of variables' 1356 | vdb_assert(sscanf(str+pos, "%d%n", &n, &got) == 1); 1357 | vdb_assert(n >= 0 && n < VDB_MAX_VAR_COUNT); 1358 | 1359 | // if there are no variables we are done! 1360 | if (n == 0) 1361 | return 1; 1362 | 1363 | pos += got+1; // read past int and space 1364 | vdb_assert(pos < msg.length); 1365 | 1366 | for (i = 0; i < n; i++) 1367 | { 1368 | vdb_label_t label; 1369 | float value; 1370 | 1371 | // read label 1372 | vdb_assert(pos + VDB_LABEL_LENGTH < msg.length); 1373 | vdb_copy_label(&label, str+pos); 1374 | pos += VDB_LABEL_LENGTH; 1375 | 1376 | // read value 1377 | vdb_assert(sscanf(str+pos, "%f%n", &value, &got) == 1); 1378 | pos += got+1; // read past float and space 1379 | 1380 | // update variable @ ROBUSTNESS @ RACE CONDITION 1381 | new_status.var_label[i] = label; 1382 | new_status.var_value[i] = value; 1383 | } 1384 | new_status.var_count = n; 1385 | } 1386 | 1387 | *status = new_status; 1388 | return 1; 1389 | } 1390 | 1391 | // End auto-include vdb_handle_message.c 1392 | 1393 | // Begin auto-include vdb_network_threads.c 1394 | #ifdef VDB_WINDOWS 1395 | int vdb_wait_data_ready() 1396 | { 1397 | WaitForSingleObject(vdb_shared->send_semaphore, INFINITE); 1398 | while (InterlockedCompareExchange(&vdb_shared->busy, 1, 0) == 1) 1399 | { 1400 | vdb_log("CompareExchange blocked\n"); 1401 | } 1402 | return 1; 1403 | } 1404 | int vdb_signal_data_sent() { vdb_shared->busy = 0; return 1; } 1405 | int vdb_poll_data_sent() { return (InterlockedCompareExchange(&vdb_shared->busy, 1, 0) == 0); } 1406 | int vdb_signal_data_ready() { vdb_shared->busy = 0; ReleaseSemaphore(vdb_shared->send_semaphore, 1, 0); return 1; } // @ mfence, writefence 1407 | void vdb_sleep(int ms) { Sleep(ms); } 1408 | #else 1409 | int vdb_wait_data_ready() { int val = 0; return read(vdb_shared->ready[0], &val, sizeof(val)) == sizeof(val); } 1410 | int vdb_poll_data_sent() { int val = 0; return read(vdb_shared->done[0], &val, sizeof(val)) == sizeof(val); } 1411 | int vdb_signal_data_ready() { int one = 1; return write(vdb_shared->ready[1], &one, sizeof(one)) == sizeof(one); } 1412 | int vdb_signal_data_sent() { int one = 1; return write(vdb_shared->done[1], &one, sizeof(one)) == sizeof(one); } 1413 | void vdb_sleep(int ms) { usleep(ms*1000); } 1414 | #endif 1415 | 1416 | int vdb_send_thread() 1417 | { 1418 | vdb_shared_t *vs = vdb_shared; 1419 | unsigned char *frame; // @ UGLY: form_frame should modify a char *? 1420 | int frame_len; 1421 | vdb_log("Created send thread\n"); 1422 | vdb_sleep(100); // @ RACECOND: Let the parent thread set has_send_thread to 1 1423 | while (!vs->critical_error) 1424 | { 1425 | // blocking until data is signalled ready from main thread 1426 | vdb_critical(vdb_wait_data_ready()); 1427 | 1428 | // send frame header (0x2 indicating binary data) 1429 | vdb_form_frame(vs->bytes_to_send, 0x2, &frame, &frame_len); 1430 | if (!tcp_sendall(frame, frame_len)) 1431 | { 1432 | vdb_log("Failed to send frame\n"); 1433 | vdb_critical(vdb_signal_data_sent()); 1434 | break; 1435 | } 1436 | 1437 | // send the payload 1438 | if (!tcp_sendall(vs->send_buffer, vs->bytes_to_send)) 1439 | { 1440 | vdb_log("Failed to send payload\n"); 1441 | vdb_critical(vdb_signal_data_sent()); 1442 | break; 1443 | } 1444 | 1445 | // signal to main thread that data has been sent 1446 | vdb_critical(vdb_signal_data_sent()); 1447 | } 1448 | vs->has_send_thread = 0; 1449 | return 0; 1450 | } 1451 | 1452 | #ifdef VDB_WINDOWS 1453 | DWORD WINAPI vdb_win_send_thread(void *vdata) { (void)(vdata); return vdb_send_thread(); } 1454 | #endif 1455 | 1456 | int vdb_strcmpn(const char *a, const char *b, int len) 1457 | { 1458 | int i; 1459 | for (i = 0; i < len; i++) 1460 | if (a[i] != b[i]) 1461 | return 0; 1462 | return 1; 1463 | } 1464 | 1465 | int vdb_is_http_request(const char *str, int len) 1466 | { 1467 | // First three characters must be "GET" 1468 | const char *s = "GET"; 1469 | int n = (int)strlen(s); 1470 | if (len >= n && vdb_strcmpn(str, s, n)) 1471 | return 1; 1472 | return 0; 1473 | } 1474 | 1475 | int vdb_is_websockets_request(const char *str, int len) 1476 | { 1477 | // "Upgrade: websocket" must occur somewhere 1478 | const char *s = "websocket"; 1479 | int n = (int)strlen(s); 1480 | int i = 0; 1481 | for (i = 0; i < len - n; i++) 1482 | if (vdb_strcmpn(str + i, s, n)) 1483 | return 1; 1484 | return 0; 1485 | } 1486 | 1487 | int vdb_recv_thread() 1488 | { 1489 | vdb_shared_t *vs = vdb_shared; 1490 | int read_bytes; 1491 | vdb_msg_t msg; 1492 | vdb_log("Created read thread\n"); 1493 | while (!vs->critical_error) 1494 | { 1495 | if (vs->critical_error) 1496 | { 1497 | return 0; 1498 | } 1499 | if (!tcp_has_listen_socket) 1500 | { 1501 | vdb_log("Creating listen socket\n"); 1502 | if (!tcp_listen(VDB_LISTEN_PORT)) 1503 | { 1504 | vdb_log("Failed to create socket on port %d\n", VDB_LISTEN_PORT); 1505 | vdb_sleep(1000); 1506 | continue; 1507 | } 1508 | vdb_log_once("Visualization is live at host:%d\n", VDB_LISTEN_PORT); 1509 | } 1510 | if (!tcp_has_client_socket) 1511 | { 1512 | vdb_log("Waiting for client\n"); 1513 | if (!tcp_accept()) 1514 | { 1515 | vdb_sleep(1000); 1516 | continue; 1517 | } 1518 | } 1519 | if (!vs->has_connection) 1520 | { 1521 | int is_http_request; 1522 | int is_websocket_request; 1523 | vdb_log("Waiting for handshake\n"); 1524 | if (!tcp_recv(vs->recv_buffer, VDB_RECV_BUFFER_SIZE, &read_bytes)) 1525 | { 1526 | vdb_log("Lost connection during handshake\n"); 1527 | tcp_shutdown(); 1528 | vdb_sleep(1000); 1529 | continue; 1530 | } 1531 | 1532 | is_http_request = vdb_is_http_request(vs->recv_buffer, read_bytes); 1533 | is_websocket_request = vdb_is_websockets_request(vs->recv_buffer, read_bytes); 1534 | 1535 | if (!is_http_request) 1536 | { 1537 | vdb_log("Got an invalid HTTP request while waiting for handshake\n"); 1538 | tcp_shutdown(); 1539 | vdb_sleep(1000); 1540 | continue; 1541 | } 1542 | 1543 | // If it was not a websocket HTTP request we will send the HTML page 1544 | if (!is_websocket_request) 1545 | { 1546 | const char *content = get_vdb_html_page(); 1547 | static char http_response[1024*1024]; 1548 | int len = sprintf(http_response, 1549 | "HTTP/1.1 200 OK\r\n" 1550 | "Content-Length: %d\r\n" 1551 | "Content-Type: text/html\r\n" 1552 | "Connection: Closed\r\n\r\n%s", 1553 | (int)strlen(content), content); 1554 | 1555 | vdb_log("Sending HTML page.\n"); 1556 | if (!tcp_sendall(http_response, len)) 1557 | { 1558 | vdb_log("Lost connection while sending HTML page\n"); 1559 | tcp_shutdown(); 1560 | vdb_sleep(1000); 1561 | continue; 1562 | } 1563 | 1564 | // Sending 'Connection: Closed' allows us to close the socket immediately 1565 | tcp_close_client(); 1566 | 1567 | vdb_sleep(1000); 1568 | continue; 1569 | } 1570 | 1571 | // Otherwise we will set up the Websockets connection 1572 | { 1573 | char *response; 1574 | int response_len; 1575 | 1576 | vdb_log("Generating WebSockets key\n"); 1577 | if (!vdb_generate_handshake(vs->recv_buffer, read_bytes, &response, &response_len)) 1578 | { 1579 | vdb_log("Failed to generate WebSockets handshake key. Retrying.\n"); 1580 | tcp_shutdown(); 1581 | vdb_sleep(1000); 1582 | continue; 1583 | } 1584 | 1585 | vdb_log("Sending WebSockets handshake\n"); 1586 | if (!tcp_sendall(response, response_len)) 1587 | { 1588 | vdb_log("Connection went down while setting up WebSockets connection. Retrying.\n"); 1589 | tcp_shutdown(); 1590 | vdb_sleep(1000); 1591 | continue; 1592 | } 1593 | 1594 | vs->has_connection = 1; 1595 | } 1596 | } 1597 | #ifdef VDB_UNIX 1598 | // The send thread is allowed to return on unix, because if the connection 1599 | // goes down, the recv thread needs to respawn the process after a new 1600 | // client connection has been acquired (to share the file descriptor). 1601 | // fork() shares the open file descriptors with the child process, but 1602 | // if a file descriptor is _then_ opened in the parent, it will _not_ 1603 | // be shared with the child. 1604 | if (!vs->has_send_thread) 1605 | { 1606 | vs->send_pid = fork(); 1607 | vdb_critical(vs->send_pid != -1); 1608 | if (vs->send_pid == 0) 1609 | { 1610 | signal(SIGTERM, SIG_DFL); // Clear inherited signal handlers 1611 | vdb_send_thread(); // vdb_send_thread sets has_send_thread to 0 upon returning 1612 | _exit(0); 1613 | } 1614 | vs->has_send_thread = 1; 1615 | } 1616 | #else 1617 | // Because we allow it to return on unix, we allow it to return on windows 1618 | // as well, even though file descriptors are shared anyway. 1619 | if (!vs->has_send_thread) 1620 | { 1621 | CreateThread(0, 0, vdb_win_send_thread, NULL, 0, 0); // vdb_send_thread sets has_send_thread to 0 upon returning 1622 | vs->has_send_thread = 1; 1623 | } 1624 | #endif 1625 | 1626 | if (!tcp_recv(vs->recv_buffer, VDB_RECV_BUFFER_SIZE, &read_bytes)) // @ INCOMPLETE: Assemble frames 1627 | { 1628 | vdb_log("Connection went down\n"); 1629 | vs->has_connection = 0; 1630 | tcp_shutdown(); 1631 | #ifdef VDB_UNIX 1632 | if (vs->has_send_thread) 1633 | { 1634 | kill(vs->send_pid, SIGUSR1); 1635 | vs->has_send_thread = 0; 1636 | } 1637 | #endif 1638 | vdb_sleep(100); 1639 | continue; 1640 | } 1641 | if (!vdb_parse_message(vs->recv_buffer, read_bytes, &msg)) 1642 | { 1643 | vdb_log("Got a bad message\n"); 1644 | continue; 1645 | } 1646 | if (!msg.fin) 1647 | { 1648 | vdb_log("Got an incomplete message (%d): '%s'\n", msg.length, msg.payload); 1649 | continue; 1650 | } 1651 | if (msg.opcode == 0x8) // closing handshake 1652 | { 1653 | unsigned char *frame = 0; 1654 | int frame_len = 0; 1655 | vdb_log("Client voluntarily disconnected\n"); 1656 | vdb_form_frame(0, 0x8, &frame, &frame_len); 1657 | if (!tcp_sendall(frame, frame_len)) 1658 | vdb_log("Failed to send closing handshake\n"); 1659 | continue; 1660 | } 1661 | if (!vdb_handle_message(msg, &vs->status)) 1662 | { 1663 | vdb_log("Handled a bad message\n"); 1664 | continue; 1665 | } 1666 | } 1667 | return 0; 1668 | } 1669 | 1670 | #ifdef VDB_WINDOWS 1671 | DWORD WINAPI vdb_win_recv_thread(void *vdata) { (void)(vdata); return vdb_recv_thread(); } 1672 | #endif 1673 | 1674 | // End auto-include vdb_network_threads.c 1675 | 1676 | // Begin auto-include vdb_push_buffer.c 1677 | // Reserve 'count' number of bytes in the work buffer and optionally 1678 | // initialize their values to 'data', if 'data' is not NULL. Returns 1679 | // a pointer to the beginning of the reserved memory if there was 1680 | // space left, NULL otherwise. 1681 | void *vdb_push_bytes(const void *data, int count) 1682 | { 1683 | if (vdb_shared->work_buffer_used + count <= VDB_WORK_BUFFER_SIZE) 1684 | { 1685 | const char *src = (const char*)data; 1686 | char *dst = vdb_shared->work_buffer + vdb_shared->work_buffer_used; 1687 | if (src) memcpy(dst, src, count); 1688 | else memset(dst, 0, count); 1689 | vdb_shared->work_buffer_used += count; 1690 | return (void*)dst; 1691 | } 1692 | return NULL; 1693 | } 1694 | 1695 | // Reserve just enough memory for a single variable type. Might be 1696 | // more efficient than calling push_bytes if you have a lot of small 1697 | // types. I'm sorry that this is a macro; specialized functions for 1698 | // common types are given below. 1699 | #define _vdb_push_type(VALUE, TYPE) \ 1700 | if (vdb_shared->work_buffer_used + sizeof(TYPE) <= VDB_WORK_BUFFER_SIZE) \ 1701 | { \ 1702 | TYPE *ptr = (TYPE*)(vdb_shared->work_buffer + vdb_shared->work_buffer_used); \ 1703 | vdb_shared->work_buffer_used += sizeof(TYPE); \ 1704 | *ptr = VALUE; \ 1705 | return ptr; \ 1706 | } \ 1707 | return NULL; 1708 | 1709 | uint8_t *vdb_push_u08(uint8_t x) { _vdb_push_type(x, uint8_t); } 1710 | uint32_t *vdb_push_u32(uint32_t x) { _vdb_push_type(x, uint32_t); } 1711 | float *vdb_push_r32(float x) { _vdb_push_type(x, float); } 1712 | 1713 | // End auto-include vdb_push_buffer.c 1714 | 1715 | // Begin auto-include vdb_draw_commands.c 1716 | #define vdb_color_mode_primary 0 1717 | #define vdb_color_mode_ramp 1 1718 | 1719 | #define vdb_mode_point2 1 1720 | #define vdb_mode_point3 2 1721 | #define vdb_mode_line2 3 1722 | #define vdb_mode_line3 4 1723 | #define vdb_mode_fill_rect 5 1724 | #define vdb_mode_circle 6 1725 | #define vdb_mode_image_rgb8 7 1726 | #define vdb_mode_slider 254 1727 | 1728 | static unsigned char vdb_current_color_mode = 0; 1729 | static unsigned char vdb_current_color = 0; 1730 | static unsigned char vdb_current_alpha = 0; 1731 | 1732 | static float *vdb_point_size = 0; 1733 | static float *vdb_line_size = 0; 1734 | static float *vdb_alpha_value = 0; 1735 | static unsigned char *vdb_nice_points = 0; 1736 | 1737 | static float vdb_xrange_left = -1.0f; 1738 | static float vdb_xrange_right = +1.0f; 1739 | static float vdb_yrange_bottom = -1.0f; 1740 | static float vdb_yrange_top = +1.0f; 1741 | static float vdb_zrange_far = -1.0f; 1742 | static float vdb_zrange_near = +1.0f; 1743 | 1744 | // This function is automatically called on a successful vdb_begin call 1745 | // to let you set up whatever state before beginning to submit commands. 1746 | void vdb_begin_submission() 1747 | { 1748 | vdb_current_color_mode = vdb_color_mode_primary; 1749 | vdb_current_alpha = 0; 1750 | vdb_xrange_left = -1.0f; 1751 | vdb_xrange_right = +1.0f; 1752 | vdb_yrange_bottom = -1.0f; 1753 | vdb_yrange_top = +1.0f; 1754 | vdb_zrange_far = -1.0f; 1755 | vdb_zrange_near = +1.0f; 1756 | 1757 | // Reserve the immediately first portion of the work buffer 1758 | // for geometry-global variables 1759 | vdb_point_size = vdb_push_r32(4.0f); 1760 | vdb_line_size = vdb_push_r32(2.0f); 1761 | vdb_alpha_value = vdb_push_r32(0.5f); 1762 | vdb_nice_points = vdb_push_u08(0); 1763 | } 1764 | 1765 | // This function is automatically called on vdb_end, right before 1766 | // the workload is sent off the the network thread. 1767 | void vdb_end_submission() 1768 | { 1769 | // Mark events as handled 1770 | vdb_shared->status.mouse_click = 0; 1771 | } 1772 | 1773 | void vdb_color_primary(int primary, int shade) 1774 | { 1775 | if (primary < 0) primary = 0; 1776 | if (primary > 4) primary = 4; 1777 | if (shade < 0) shade = 0; 1778 | if (shade > 2) shade = 2; 1779 | vdb_current_color_mode = vdb_color_mode_primary; 1780 | vdb_current_color = (unsigned char)(3*primary + shade); 1781 | } 1782 | 1783 | void vdb_color_rampf(float value) 1784 | { 1785 | int i = (int)(value*63.0f); 1786 | if (i < 0) i = 0; 1787 | if (i > 63) i = 63; 1788 | vdb_current_color_mode = vdb_color_mode_ramp; 1789 | vdb_current_color = (unsigned char)i; 1790 | } 1791 | 1792 | void vdb_color_ramp(int i) 1793 | { 1794 | vdb_current_color_mode = vdb_color_mode_ramp; 1795 | vdb_current_color = (unsigned char)(i % 63); 1796 | } 1797 | 1798 | void vdb_color_red(int shade) { vdb_color_primary(0, shade); } 1799 | void vdb_color_green(int shade) { vdb_color_primary(1, shade); } 1800 | void vdb_color_blue(int shade) { vdb_color_primary(2, shade); } 1801 | void vdb_color_black(int shade) { vdb_color_primary(3, shade); } 1802 | void vdb_color_white(int shade) { vdb_color_primary(4, shade); } 1803 | void vdb_translucent() { vdb_current_alpha = 1; } 1804 | void vdb_opaque() { vdb_current_alpha = 0; } 1805 | 1806 | void vdb_setPointSize(float radius) { if (vdb_point_size) *vdb_point_size = radius; } 1807 | void vdb_setLineSize(float radius) { if (vdb_line_size) *vdb_line_size = radius; } 1808 | void vdb_setNicePoints(int enabled) { if (vdb_nice_points) *vdb_nice_points = (unsigned char)enabled; } 1809 | void vdb_setTranslucency(float alpha) { if (vdb_alpha_value) *vdb_alpha_value = alpha; } 1810 | 1811 | void vdb_xrange(float left, float right) 1812 | { 1813 | vdb_xrange_left = left; 1814 | vdb_xrange_right = right; 1815 | } 1816 | 1817 | void vdb_yrange(float bottom, float top) 1818 | { 1819 | vdb_yrange_bottom = bottom; 1820 | vdb_yrange_top = top; 1821 | } 1822 | 1823 | void vdb_zrange(float z_near, float z_far) 1824 | { 1825 | vdb_zrange_near = z_near; 1826 | vdb_zrange_far = z_far; 1827 | } 1828 | 1829 | float vdb__unmap_x(float x) { return vdb_xrange_left + (0.5f+0.5f*x)*(vdb_xrange_right-vdb_xrange_left); } 1830 | float vdb__unmap_y(float y) { return vdb_yrange_bottom + (0.5f+0.5f*y)*(vdb_yrange_top-vdb_yrange_bottom); } 1831 | float vdb__map_x(float x) { return -1.0f + 2.0f*(x-vdb_xrange_left)/(vdb_xrange_right-vdb_xrange_left); } 1832 | float vdb__map_y(float y) { return -1.0f + 2.0f*(y-vdb_yrange_bottom)/(vdb_yrange_top-vdb_yrange_bottom); } 1833 | float vdb__map_z(float z) { return +1.0f - 2.0f*(z-vdb_zrange_near)/(vdb_zrange_far-vdb_zrange_near); } 1834 | 1835 | void vdb_push_style() 1836 | { 1837 | unsigned char opacity = ((vdb_current_alpha & 0x01) << 7); 1838 | unsigned char mode = ((vdb_current_color_mode & 0x01) << 6); 1839 | unsigned char value = ((vdb_current_color & 0x3F) << 0); 1840 | unsigned char style = opacity | mode | value; 1841 | vdb_push_u08(style); 1842 | } 1843 | 1844 | void vdb_point(float x, float y) 1845 | { 1846 | vdb_push_u08(vdb_mode_point2); 1847 | vdb_push_style(); 1848 | vdb_push_r32(vdb__map_x(x)); 1849 | vdb_push_r32(vdb__map_y(y)); 1850 | } 1851 | 1852 | void vdb_point3d(float x, float y, float z) 1853 | { 1854 | vdb_push_u08(vdb_mode_point3); 1855 | vdb_push_style(); 1856 | vdb_push_r32(vdb__map_x(x)); 1857 | vdb_push_r32(vdb__map_y(y)); 1858 | vdb_push_r32(vdb__map_z(z)); 1859 | } 1860 | 1861 | void vdb_line(float x1, float y1, float x2, float y2) 1862 | { 1863 | vdb_push_u08(vdb_mode_line2); 1864 | vdb_push_style(); 1865 | vdb_push_r32(vdb__map_x(x1)); 1866 | vdb_push_r32(vdb__map_y(y1)); 1867 | vdb_push_r32(vdb__map_x(x2)); 1868 | vdb_push_r32(vdb__map_y(y2)); 1869 | } 1870 | 1871 | void vdb_line3d(float x1, float y1, float z1, float x2, float y2, float z2) 1872 | { 1873 | vdb_push_u08(vdb_mode_line3); 1874 | vdb_push_style(); 1875 | vdb_push_r32(vdb__map_x(x1)); 1876 | vdb_push_r32(vdb__map_y(y1)); 1877 | vdb_push_r32(vdb__map_z(z1)); 1878 | vdb_push_r32(vdb__map_x(x2)); 1879 | vdb_push_r32(vdb__map_y(y2)); 1880 | vdb_push_r32(vdb__map_z(z2)); 1881 | } 1882 | 1883 | void vdb_fillRect(float x, float y, float w, float h) 1884 | { 1885 | vdb_push_u08(vdb_mode_fill_rect); 1886 | vdb_push_style(); 1887 | vdb_push_r32(vdb__map_x(x)); 1888 | vdb_push_r32(vdb__map_y(y)); 1889 | vdb_push_r32(vdb__map_x(x+w)); 1890 | vdb_push_r32(vdb__map_y(y+h)); 1891 | } 1892 | 1893 | void vdb_circle(float x, float y, float r) 1894 | { 1895 | vdb_push_u08(vdb_mode_circle); 1896 | vdb_push_style(); 1897 | vdb_push_r32(vdb__map_x(x)); 1898 | vdb_push_r32(vdb__map_y(y)); 1899 | vdb_push_r32(vdb__map_x(r) - vdb__map_x(0.0f)); 1900 | vdb_push_r32(vdb__map_y(r) - vdb__map_y(0.0f)); 1901 | } 1902 | 1903 | void vdb_imageRGB8(const void *data, int w, int h) 1904 | { 1905 | vdb_push_u08(vdb_mode_image_rgb8); 1906 | vdb_push_style(); 1907 | vdb_push_u32(w); 1908 | vdb_push_u32(h); 1909 | vdb_push_bytes(data, w*h*3); 1910 | } 1911 | 1912 | void vdb_slider1f(const char *in_label, float *x, float min_value, float max_value) 1913 | { 1914 | int i = 0; 1915 | vdb_label_t label = {0}; 1916 | vdb_copy_label(&label, in_label); 1917 | vdb_push_u08(vdb_mode_slider); 1918 | vdb_push_style(); 1919 | vdb_push_bytes(label.chars, VDB_LABEL_LENGTH); 1920 | vdb_push_r32(*x); 1921 | vdb_push_r32(min_value); 1922 | vdb_push_r32(max_value); 1923 | vdb_push_r32(0.01f); 1924 | 1925 | // Update variable 1926 | // @ ROBUSTNESS @ RACE CONDITION: Mutex on latest message 1927 | for (i = 0; i < vdb_shared->status.var_count; i++) 1928 | { 1929 | if (vdb_cmp_label(&vdb_shared->status.var_label[i], &label)) 1930 | { 1931 | float v = vdb_shared->status.var_value[i]; 1932 | if (v < min_value) v = min_value; 1933 | if (v > max_value) v = max_value; 1934 | *x = v; 1935 | } 1936 | } 1937 | } 1938 | 1939 | void vdb_slider1i(const char *in_label, int *x, int min_value, int max_value) 1940 | { 1941 | int i = 0; 1942 | vdb_label_t label = {0}; 1943 | vdb_copy_label(&label, in_label); 1944 | vdb_push_u08(vdb_mode_slider); 1945 | vdb_push_style(); 1946 | vdb_push_bytes(label.chars, VDB_LABEL_LENGTH); 1947 | vdb_push_r32((float)*x); 1948 | vdb_push_r32((float)min_value); 1949 | vdb_push_r32((float)max_value); 1950 | vdb_push_r32(1.0f); 1951 | 1952 | // Update variable 1953 | // @ ROBUSTNESS @ RACE CONDITION: Mutex on latest message 1954 | for (i = 0; i < vdb_shared->status.var_count; i++) 1955 | { 1956 | if (vdb_cmp_label(&vdb_shared->status.var_label[i], &label)) 1957 | { 1958 | int v = (int)vdb_shared->status.var_value[i]; 1959 | if (v < min_value) v = min_value; 1960 | if (v > max_value) v = max_value; 1961 | *x = v; 1962 | } 1963 | } 1964 | } 1965 | 1966 | void vdb_checkbox(const char *in_label, int *x) 1967 | { 1968 | vdb_slider1i(in_label, x, 0, 1); 1969 | } 1970 | 1971 | int vdb_mouse_click(float *x, float *y) 1972 | { 1973 | if (vdb_shared->status.mouse_click) 1974 | { 1975 | *x = vdb__unmap_x(vdb_shared->status.mouse_click_x); 1976 | *y = vdb__unmap_y(vdb_shared->status.mouse_click_y); 1977 | return 1; 1978 | } 1979 | return 0; 1980 | } 1981 | 1982 | // End auto-include vdb_draw_commands.c 1983 | 1984 | // Begin auto-include vdb_begin_end.c 1985 | #ifdef VDB_UNIX 1986 | void vdb_unix_atexit() 1987 | { 1988 | if (vdb_shared) 1989 | { 1990 | kill(vdb_shared->recv_pid, SIGTERM); 1991 | kill(vdb_shared->send_pid, SIGTERM); 1992 | } 1993 | } 1994 | #endif 1995 | 1996 | int vdb_begin() 1997 | { 1998 | if (!vdb_shared) 1999 | { 2000 | #ifdef VDB_UNIX 2001 | vdb_shared = (vdb_shared_t*)mmap(NULL, sizeof(vdb_shared_t), 2002 | PROT_READ|PROT_WRITE, 2003 | MAP_SHARED|MAP_ANONYMOUS, -1, 0); 2004 | if (vdb_shared == MAP_FAILED) 2005 | vdb_shared = 0; 2006 | #else 2007 | vdb_shared = (vdb_shared_t*)calloc(sizeof(vdb_shared_t),1); 2008 | #endif 2009 | 2010 | if (!vdb_shared) 2011 | { 2012 | vdb_err_once("Tried to allocate too much memory, try lowering VDB_RECV_BUFFER_SIZE and VDB_SEND_BUFFER_SIZE\n"); 2013 | return 0; 2014 | } 2015 | 2016 | #ifdef VDB_UNIX 2017 | atexit(vdb_unix_atexit); // We want to terminate any child processes when we terminate 2018 | 2019 | vdb_critical(pipe(vdb_shared->ready) != -1); 2020 | vdb_critical(pipe2(vdb_shared->done, O_NONBLOCK) != -1); 2021 | 2022 | // Create recv process 2023 | vdb_shared->recv_pid = fork(); 2024 | vdb_critical(vdb_shared->recv_pid != -1); 2025 | if (vdb_shared->recv_pid == 0) 2026 | { 2027 | signal(SIGTERM, SIG_DFL); // Clear inherited signal handlers 2028 | vdb_recv_thread(); 2029 | _exit(0); 2030 | } 2031 | vdb_signal_data_sent(); // Needed for first vdb_end call 2032 | 2033 | #else 2034 | vdb_shared->send_semaphore = CreateSemaphore(0, 0, 1, 0); 2035 | CreateThread(0, 0, vdb_win_recv_thread, NULL, 0, 0); 2036 | #endif 2037 | 2038 | vdb_shared->work_buffer = vdb_shared->swapbuffer1; 2039 | vdb_shared->send_buffer = vdb_shared->swapbuffer2; 2040 | // Remaining parameters should be initialized to zero by calloc or mmap 2041 | } 2042 | if (vdb_shared->critical_error) 2043 | { 2044 | vdb_err_once("You must restart your program to use vdb.\n"); 2045 | return 0; 2046 | } 2047 | if (!vdb_shared->has_connection) 2048 | { 2049 | return 0; 2050 | } 2051 | vdb_shared->work_buffer_used = 0; 2052 | vdb_begin_submission(); 2053 | return 1; 2054 | } 2055 | 2056 | void vdb_end() 2057 | { 2058 | vdb_shared_t *vs = vdb_shared; 2059 | if (vdb_poll_data_sent()) // Check if send_thread has finished sending data 2060 | { 2061 | vdb_end_submission(); 2062 | 2063 | char *new_work_buffer = vs->send_buffer; 2064 | vs->send_buffer = vs->work_buffer; 2065 | vs->bytes_to_send = vs->work_buffer_used; 2066 | vs->work_buffer = new_work_buffer; 2067 | vs->work_buffer_used = 0; 2068 | 2069 | // Notify sending thread that data is available 2070 | vdb_signal_data_ready(); 2071 | } 2072 | } 2073 | 2074 | int vdb_loop(int fps) 2075 | { 2076 | static int entry = 1; 2077 | if (entry) 2078 | { 2079 | while (!vdb_begin()) 2080 | { 2081 | } 2082 | entry = 0; 2083 | } 2084 | else 2085 | { 2086 | vdb_end(); 2087 | vdb_sleep(1000/fps); 2088 | if (vdb_shared->status.flag_continue) 2089 | { 2090 | vdb_shared->status.flag_continue = 0; 2091 | entry = 1; 2092 | return 0; 2093 | } 2094 | while (!vdb_begin()) 2095 | { 2096 | } 2097 | } 2098 | return 1; 2099 | } 2100 | 2101 | // End auto-include vdb_begin_end.c 2102 | 2103 | // Begin embedded app.html 2104 | const char *vdb_html_page = 2105 | "\n" 2106 | "\n" 2107 | "vdebug\n" 2108 | "\n" 2109 | "\n" 2110 | "\n" 2158 | "\n" 2159 | "\n" 2160 | "\n" 2161 | "\n" 2174 | "\n" 2184 | "\n" 2185 | "\n" 2186 | "\n" 2187 | "\n" 2935 | "\n" 2936 | "\n" 2937 | "\n" 2938 | "\n" 2939 | "\n" 2940 | "
\n" 2941 | " \n" 2942 | " \n" 2943 | "\n" 2944 | " Continue\n" 2945 | "\n" 2946 | "
\n" 2947 | " \n" 2948 | " Connect\n" 2949 | "

\n" 2950 | "
\n" 2951 | "\n" 2952 | "
\n" 2953 | "
\n" 2954 | "
\n" 2955 | "\n" 2956 | "\n" 2957 | "\n" 2958 | ; 2959 | // End embedded app.html 2960 | --------------------------------------------------------------------------------