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