1a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
2a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat/**
3a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * @example camera.c
4a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * Question: I need to display a live camera image via VNC. Until now I just
5a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * grab an image, set the rect to modified and do a 0.1 s sleep to give the
6a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * system time to transfer the data.
7a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * This is obviously a solution which doesn't scale very well to different
8a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * connection speeds/cpu horsepowers, so I wonder if there is a way for the
9a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * server application to determine if the updates have been sent. This would
10a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * cause the live image update rate to always be the maximum the connection
11a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * supports while avoiding excessive loads.
12a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
13a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * Thanks in advance,
14a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
15a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
16a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * Christian Daschill
17a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
18a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
19a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * Answer: Originally, I thought about using seperate threads and using a
20a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * mutex to determine when the frame buffer was being accessed by any client
21a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * so we could determine a safe time to take a picture.  The probem is, we
22a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * are lock-stepping everything with framebuffer access.  Why not be a
23a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * single-thread application and in-between rfbProcessEvents perform a
24a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * camera snapshot.  And this is what I do here.  It guarantees that the
25a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * clients have been serviced before taking another picture.
26a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
27a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * The downside to this approach is that the more clients you have, there is
28a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * less time available for you to service the camera equating to reduced
29a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * frame rate.  (or, your clients are on really slow links). Increasing your
30a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * systems ethernet transmit queues may help improve the overall performance
31a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * as the libvncserver should not stall on transmitting to any single
32a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * client.
33a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat *
34a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * Another solution would be to provide a seperate framebuffer for each
35a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * client and use mutexes to determine if any particular client is ready for
36a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * a snapshot.  This way, your not updating a framebuffer for a slow client
37a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * while it is being transferred.
38a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat */
39a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
40a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#include <stdio.h>
41a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#include <stdlib.h>
42a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#include <string.h>
43a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#include <rfb/rfb.h>
44a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
45a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
46a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#define WIDTH  640
47a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#define HEIGHT 480
48a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#define BPP      4
49a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
50a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat/* 15 frames per second (if we can) */
51a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat#define PICTURE_TIMEOUT (1.0/15.0)
52a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
53a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
54a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat/*
55a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * throttle camera updates
56a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat*/
57a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehatint TimeToTakePicture() {
58a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    static struct timeval now={0,0}, then={0,0};
59a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    double elapsed, dnow, dthen;
60a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
61a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    gettimeofday(&now,NULL);
62a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
63a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    dnow  = now.tv_sec  + (now.tv_usec /1000000.0);
64a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    dthen = then.tv_sec + (then.tv_usec/1000000.0);
65a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    elapsed = dnow - dthen;
66a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
67a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    if (elapsed > PICTURE_TIMEOUT)
68a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat      memcpy((char *)&then, (char *)&now, sizeof(struct timeval));
69a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    return elapsed > PICTURE_TIMEOUT;
70a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat}
71a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
72a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
73a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
74a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat/*
75a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * simulate grabbing a picture from some device
76a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat */
77a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehatint TakePicture(unsigned char *buffer)
78a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat{
79a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  static int last_line=0, fps=0, fcount=0;
80a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  int line=0;
81a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  int i,j;
82a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  struct timeval now;
83a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
84a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  /*
85a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   * simulate grabbing data from a device by updating the entire framebuffer
86a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   */
87a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
88a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  for(j=0;j<HEIGHT;++j) {
89a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    for(i=0;i<WIDTH;++i) {
90a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat      buffer[(j*WIDTH+i)*BPP+0]=(i+j)*128/(WIDTH+HEIGHT); /* red */
91a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat      buffer[(j*WIDTH+i)*BPP+1]=i*128/WIDTH; /* green */
92a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat      buffer[(j*WIDTH+i)*BPP+2]=j*256/HEIGHT; /* blue */
93a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    }
94a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    buffer[j*WIDTH*BPP+0]=0xff;
95a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    buffer[j*WIDTH*BPP+1]=0xff;
96a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    buffer[j*WIDTH*BPP+2]=0xff;
97a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  }
98a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
99a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  /*
100a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   * simulate the passage of time
101a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   *
102a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   * draw a simple black line that moves down the screen. The faster the
103a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   * client, the more updates it will get, the smoother it will look!
104a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat   */
105a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  gettimeofday(&now,NULL);
106a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  line = now.tv_usec / (1000000/HEIGHT);
107a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  if (line>HEIGHT) line=HEIGHT-1;
108a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  memset(&buffer[(WIDTH * BPP) * line], 0, (WIDTH * BPP));
109a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
110a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  /* frames per second (informational only) */
111a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  fcount++;
112a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  if (last_line > line) {
113a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    fps = fcount;
114a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    fcount = 0;
115a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  }
116a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  last_line = line;
117a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  fprintf(stderr,"%03d/%03d Picture (%03d fps)\r", line, HEIGHT, fps);
118a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
119a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  /* success!   We have a new picture! */
120a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  return (1==1);
121a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat}
122a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
123a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
124a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
125a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
126a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat/*
127a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * Single-threaded application that interleaves client servicing with taking
128a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * pictures from the camera.  This way, we do not update the framebuffer
129a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat * while an encoding is working on it too (banding, and image artifacts).
130a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat */
131a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehatint main(int argc,char** argv)
132a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat{
133a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  long usec;
134a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
135a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  rfbScreenInfoPtr server=rfbGetScreen(&argc,argv,WIDTH,HEIGHT,8,3,BPP);
136a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  if(!server)
137a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    return 0;
138a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  server->desktopName = "Live Video Feed Example";
139a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  server->frameBuffer=(char*)malloc(WIDTH*HEIGHT*BPP);
140a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  server->alwaysShared=(1==1);
141a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
142a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  /* Initialize the server */
143a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  rfbInitServer(server);
144a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
145a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  /* Loop, processing clients and taking pictures */
146a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  while (rfbIsActive(server)) {
147a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    if (TimeToTakePicture())
148a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat      if (TakePicture((unsigned char *)server->frameBuffer))
149a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat        rfbMarkRectAsModified(server,0,0,WIDTH,HEIGHT);
150a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat
151a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    usec = server->deferUpdateTime*1000;
152a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat    rfbProcessEvents(server,usec);
153a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  }
154a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat  return(0);
155a430b2b5ca4f0967836f5820e8f03adc17fc0a24San Mehat}
156