Home | History | Annotate | Download | only in examples
      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