1 2 /** 3 * @example camera.c 4 * Question: I need to display a live camera image via VNC. Until now I just 5 * grab an image, set the rect to modified and do a 0.1 s sleep to give the 6 * system time to transfer the data. 7 * This is obviously a solution which doesn't scale very well to different 8 * connection speeds/cpu horsepowers, so I wonder if there is a way for the 9 * server application to determine if the updates have been sent. This would 10 * cause the live image update rate to always be the maximum the connection 11 * supports while avoiding excessive loads. 12 * 13 * Thanks in advance, 14 * 15 * 16 * Christian Daschill 17 * 18 * 19 * Answer: Originally, I thought about using seperate threads and using a 20 * mutex to determine when the frame buffer was being accessed by any client 21 * so we could determine a safe time to take a picture. The probem is, we 22 * are lock-stepping everything with framebuffer access. Why not be a 23 * single-thread application and in-between rfbProcessEvents perform a 24 * camera snapshot. And this is what I do here. It guarantees that the 25 * clients have been serviced before taking another picture. 26 * 27 * The downside to this approach is that the more clients you have, there is 28 * less time available for you to service the camera equating to reduced 29 * frame rate. (or, your clients are on really slow links). Increasing your 30 * systems ethernet transmit queues may help improve the overall performance 31 * as the libvncserver should not stall on transmitting to any single 32 * client. 33 * 34 * Another solution would be to provide a seperate framebuffer for each 35 * client and use mutexes to determine if any particular client is ready for 36 * a snapshot. This way, your not updating a framebuffer for a slow client 37 * while it is being transferred. 38 */ 39 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <rfb/rfb.h> 44 45 46 #define WIDTH 640 47 #define HEIGHT 480 48 #define BPP 4 49 50 /* 15 frames per second (if we can) */ 51 #define PICTURE_TIMEOUT (1.0/15.0) 52 53 54 /* 55 * throttle camera updates 56 */ 57 int TimeToTakePicture() { 58 static struct timeval now={0,0}, then={0,0}; 59 double elapsed, dnow, dthen; 60 61 gettimeofday(&now,NULL); 62 63 dnow = now.tv_sec + (now.tv_usec /1000000.0); 64 dthen = then.tv_sec + (then.tv_usec/1000000.0); 65 elapsed = dnow - dthen; 66 67 if (elapsed > PICTURE_TIMEOUT) 68 memcpy((char *)&then, (char *)&now, sizeof(struct timeval)); 69 return elapsed > PICTURE_TIMEOUT; 70 } 71 72 73 74 /* 75 * simulate grabbing a picture from some device 76 */ 77 int TakePicture(unsigned char *buffer) 78 { 79 static int last_line=0, fps=0, fcount=0; 80 int line=0; 81 int i,j; 82 struct timeval now; 83 84 /* 85 * simulate grabbing data from a device by updating the entire framebuffer 86 */ 87 88 for(j=0;j<HEIGHT;++j) { 89 for(i=0;i<WIDTH;++i) { 90 buffer[(j*WIDTH+i)*BPP+0]=(i+j)*128/(WIDTH+HEIGHT); /* red */ 91 buffer[(j*WIDTH+i)*BPP+1]=i*128/WIDTH; /* green */ 92 buffer[(j*WIDTH+i)*BPP+2]=j*256/HEIGHT; /* blue */ 93 } 94 buffer[j*WIDTH*BPP+0]=0xff; 95 buffer[j*WIDTH*BPP+1]=0xff; 96 buffer[j*WIDTH*BPP+2]=0xff; 97 } 98 99 /* 100 * simulate the passage of time 101 * 102 * draw a simple black line that moves down the screen. The faster the 103 * client, the more updates it will get, the smoother it will look! 104 */ 105 gettimeofday(&now,NULL); 106 line = now.tv_usec / (1000000/HEIGHT); 107 if (line>HEIGHT) line=HEIGHT-1; 108 memset(&buffer[(WIDTH * BPP) * line], 0, (WIDTH * BPP)); 109 110 /* frames per second (informational only) */ 111 fcount++; 112 if (last_line > line) { 113 fps = fcount; 114 fcount = 0; 115 } 116 last_line = line; 117 fprintf(stderr,"%03d/%03d Picture (%03d fps)\r", line, HEIGHT, fps); 118 119 /* success! We have a new picture! */ 120 return (1==1); 121 } 122 123 124 125 126 /* 127 * Single-threaded application that interleaves client servicing with taking 128 * pictures from the camera. This way, we do not update the framebuffer 129 * while an encoding is working on it too (banding, and image artifacts). 130 */ 131 int main(int argc,char** argv) 132 { 133 long usec; 134 135 rfbScreenInfoPtr server=rfbGetScreen(&argc,argv,WIDTH,HEIGHT,8,3,BPP); 136 if(!server) 137 return 0; 138 server->desktopName = "Live Video Feed Example"; 139 server->frameBuffer=(char*)malloc(WIDTH*HEIGHT*BPP); 140 server->alwaysShared=(1==1); 141 142 /* Initialize the server */ 143 rfbInitServer(server); 144 145 /* Loop, processing clients and taking pictures */ 146 while (rfbIsActive(server)) { 147 if (TimeToTakePicture()) 148 if (TakePicture((unsigned char *)server->frameBuffer)) 149 rfbMarkRectAsModified(server,0,0,WIDTH,HEIGHT); 150 151 usec = server->deferUpdateTime*1000; 152 rfbProcessEvents(server,usec); 153 } 154 return(0); 155 } 156