add libvncserver
[presencevnc] / libvnc / examples / camera.c
1
2 /*
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.
11  *
12  * Thanks in advance,
13  *
14  *
15  * Christian Daschill 
16  *
17  *
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.
25  *
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
31  * client.
32  *
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.
37  */
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <rfb/rfb.h>
43
44
45 #define WIDTH  640
46 #define HEIGHT 480
47 #define BPP      4
48
49 /* 15 frames per second (if we can) */
50 #define PICTURE_TIMEOUT (1.0/15.0)
51
52
53 /*
54  * throttle camera updates
55 */
56 int TimeToTakePicture() {
57     static struct timeval now={0,0}, then={0,0};
58     double elapsed, dnow, dthen;
59
60     gettimeofday(&now,NULL);
61
62     dnow  = now.tv_sec  + (now.tv_usec /1000000.0);
63     dthen = then.tv_sec + (then.tv_usec/1000000.0);
64     elapsed = dnow - dthen;
65
66     if (elapsed > PICTURE_TIMEOUT)
67       memcpy((char *)&then, (char *)&now, sizeof(struct timeval));
68     return elapsed > PICTURE_TIMEOUT;
69 }
70
71
72
73 /*
74  * simulate grabbing a picture from some device
75  */
76 int TakePicture(unsigned char *buffer)
77 {
78   static int last_line=0, fps=0, fcount=0;
79   int line=0;
80   int i,j;
81   struct timeval now;
82
83   /*
84    * simulate grabbing data from a device by updating the entire framebuffer
85    */
86   
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 */
92     }
93     buffer[j*WIDTH*BPP+0]=0xff;
94     buffer[j*WIDTH*BPP+1]=0xff;
95     buffer[j*WIDTH*BPP+2]=0xff;
96   }
97
98   /*
99    * simulate the passage of time
100    *
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!
103    */
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));
108
109   /* frames per second (informational only) */
110   fcount++;
111   if (last_line > line) {
112     fps = fcount;
113     fcount = 0;
114   }
115   last_line = line;
116   fprintf(stderr,"%03d/%03d Picture (%03d fps)\r", line, HEIGHT, fps);
117
118   /* success!   We have a new picture! */
119   return (1==1);
120 }
121
122
123
124
125 /* 
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).
129  */
130 int main(int argc,char** argv)
131 {                                       
132   long usec;
133   
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);
138
139   /* Initialize the server */
140   rfbInitServer(server);           
141
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);
147           
148     usec = server->deferUpdateTime*1000;
149     rfbProcessEvents(server,usec);
150   }
151   return(0);
152 }