diff options
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | examples/Makefile.am | 2 | ||||
-rw-r--r-- | examples/camera.c | 153 |
3 files changed, 158 insertions, 1 deletions
@@ -1,3 +1,7 @@ +2006-04-17 Steven Carr <scarr@jsa-usa.com> + * Added an example camera application to demonstrate another + way to write a server application. + 2006-04-05 Karl Runge <runge@karlrunge.com> * classes/ssl: SSL Java viewer workarounds for firewall proxies (signed applet as last resort, proxy.vnc). diff --git a/examples/Makefile.am b/examples/Makefile.am index 644f017..c2ef6fa 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -18,5 +18,5 @@ noinst_HEADERS=radon.h noinst_PROGRAMS=example pnmshow regiontest pnmshow24 fontsel \ vncev storepasswd colourmaptest simple simple15 $(MAC) \ - $(FILETRANSFER) backchannel $(BLOOPTEST) + $(FILETRANSFER) backchannel $(BLOOPTEST) camera diff --git a/examples/camera.c b/examples/camera.c new file mode 100644 index 0000000..f46f5c7 --- /dev/null +++ b/examples/camera.c @@ -0,0 +1,153 @@ + +/* + * Question: I need to display a live camera image via VNC. Until now I just + * grab an image, set the rect to modified and do a 0.1 s sleep to give the + * system time to transfer the data. + * This is obviously a solution which doesn't scale very well to different + * connection speeds/cpu horsepowers, so I wonder if there is a way for the + * server application to determine if the updates have been sent. This would + * cause the live image update rate to always be the maximum the connection + * supports while avoiding excessive loads. + * + * Thanks in advance, + * + * + * Christian Daschill + * + * + * Answer: Originally, I thought about using seperate threads and using a + * mutex to determine when the frame buffer was being accessed by any client + * so we could determine a safe time to take a picture. The probem is, we + * are lock-stepping everything with framebuffer access. Why not be a + * single-thread application and in-between rfbProcessEvents perform a + * camera snapshot. And this is what I do here. It guarantees that the + * clients have been serviced before taking another picture. + * + * The downside to this approach is that the more clients you have, there is + * less time available for you to service the camera equating to reduced + * frame rate. (or, your clients are on really slow links). Increasing your + * systems ethernet transmit queues may help improve the overall performance + * as the libvncserver should not stall on transmitting to any single + * client. + * + * Another solution would be to provide a seperate framebuffer for each + * client and use mutexes to determine if any particular client is ready for + * a snapshot. This way, your not updating a framebuffer for a slow client + * while it is being transferred. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <malloc.h> +#include <rfb/rfb.h> + + +#define WIDTH 640 +#define HEIGHT 480 +#define BPP 4 + +/* 15 frames per second (if we can) */ +#define PICTURE_TIMEOUT (1.0/15.0) + + +/* + * throttle camera updates +*/ +int TimeToTakePicture() { + static struct timeval now={0,0}, then={0,0}; + double elapsed, dnow, dthen; + + gettimeofday(&now,NULL); + + dnow = now.tv_sec + (now.tv_usec /1000000.0); + dthen = then.tv_sec + (then.tv_usec/1000000.0); + elapsed = dnow - dthen; + + if (elapsed > PICTURE_TIMEOUT) + memcpy((char *)&then, (char *)&now, sizeof(struct timeval)); + return elapsed > PICTURE_TIMEOUT; +} + + + +/* + * simulate grabbing a picture from some device + */ +int TakePicture(unsigned char *buffer) +{ + static int last_line=0, fps=0, fcount=0; + int line=0; + int i,j; + struct timeval now; + + /* + * simulate grabbing data from a device by updating the entire framebuffer + */ + + for(j=0;j<HEIGHT;++j) { + for(i=0;i<WIDTH;++i) { + buffer[(j*WIDTH+i)*BPP+0]=(i+j)*128/(WIDTH+HEIGHT); /* red */ + buffer[(j*WIDTH+i)*BPP+1]=i*128/WIDTH; /* green */ + buffer[(j*WIDTH+i)*BPP+2]=j*256/HEIGHT; /* blue */ + } + buffer[j*WIDTH*BPP+0]=0xff; + buffer[j*WIDTH*BPP+1]=0xff; + buffer[j*WIDTH*BPP+2]=0xff; + } + + /* + * simulate the passage of time + * + * draw a simple black line that moves down the screen. The faster the + * client, the more updates it will get, the smoother it will look! + */ + gettimeofday(&now,NULL); + line = now.tv_usec / (1000000/HEIGHT); + if (line>HEIGHT) line=HEIGHT-1; + memset(&buffer[(WIDTH * BPP) * line], 0, (WIDTH * BPP)); + + /* frames per second (informational only) */ + fcount++; + if (last_line > line) { + fps = fcount; + fcount = 0; + } + last_line = line; + fprintf(stderr,"%03d/%03d Picture (%03d fps)\r", line, HEIGHT, fps); + + /* success! We have a new picture! */ + return (1==1); +} + + + + +/* + * Single-threaded application that interleaves client servicing with taking + * pictures from the camera. This way, we do not update the framebuffer + * while an encoding is working on it too (banding, and image artifacts). + */ +int main(int argc,char** argv) +{ + long usec; + + rfbScreenInfoPtr server=rfbGetScreen(&argc,argv,WIDTH,HEIGHT,8,3,BPP); + server->desktopName = "Live Video Feed Example"; + server->frameBuffer=(char*)malloc(WIDTH*HEIGHT*BPP); + server->alwaysShared=(1==1); + + /* Initialize the server */ + rfbInitServer(server); + + /* Loop, processing clients and taking pictures */ + while (rfbIsActive(server)) { + if (TimeToTakePicture()) + if (TakePicture((unsigned char *)server->frameBuffer)) + rfbMarkRectAsModified(server,0,0,WIDTH,HEIGHT); + + usec = server->deferUpdateTime*1000; + rfbProcessEvents(server,usec); + } + return(0); +} |