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*/
57int 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 */
77int 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 */
131int 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