blob: 32f61470a91922241f440a29a4eafe21cf780d10 [file] [log] [blame]
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +00001/* Copyright (C) 2004-2007 Constantin Kaplinsky. All Rights Reserved.
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +00002 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18//
19// PollingManager.cxx
20//
21
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000022#include <stdio.h>
23#include <string.h>
24#include <time.h>
25#include <X11/Xlib.h>
26#include <rfb/LogWriter.h>
27#include <rfb/VNCServer.h>
28#include <rfb/Configuration.h>
29#include <rfb/ServerCore.h>
30
31#include <x0vncserver/PollingManager.h>
32
33static LogWriter vlog("PollingMgr");
34
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000035const int PollingManager::m_pollingOrder[32] = {
36 0, 16, 8, 24, 4, 20, 12, 28,
37 10, 26, 18, 2, 22, 6, 30, 14,
38 1, 17, 9, 25, 7, 23, 15, 31,
39 19, 3, 27, 11, 29, 13, 5, 21
40};
41
Constantin Kaplinsky04e910b2007-12-25 18:10:35 +000042// FIXME: Check that the parameter's value is in the allowed range.
43// This applies to all other parameters as well.
Constantin Kaplinsky1d378022007-12-25 17:43:09 +000044IntParameter PollingManager::m_videoPriority("VideoPriority",
45 "Priority of sending updates for video area (0..8)", 2);
46
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000047//
48// Constructor.
49//
50// Note that dpy and image should remain valid during the object
51// lifetime, while factory is used only in the constructor itself.
52//
Constantin Kaplinsky936c3692007-12-27 08:42:53 +000053// FIXME: Pass XPixelBuffer* instead of Image*.
54//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000055
56PollingManager::PollingManager(Display *dpy, Image *image,
57 ImageFactory *factory,
58 int offsetLeft, int offsetTop)
Constantin Kaplinskyec45c482008-01-18 14:13:16 +000059 : m_dpy(dpy),
60 m_server(0),
61 m_image(image),
62 m_bytesPerPixel(image->xim->bits_per_pixel / 8),
63 m_offsetLeft(offsetLeft),
64 m_offsetTop(offsetTop),
65 m_width(image->xim->width),
Constantin Kaplinskyadebffb2008-01-18 14:33:05 +000066 m_height(image->xim->height),
67 m_widthTiles((image->xim->width + 31) / 32),
68 m_heightTiles((image->xim->height + 31) / 32),
69 m_numTiles(((image->xim->width + 31) / 32) *
70 ((image->xim->height + 31) / 32)),
Constantin Kaplinsky1d378022007-12-25 17:43:09 +000071 m_numVideoPasses(0),
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +000072 m_pollingStep(0)
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000073{
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000074 // Get initial screen image.
75 m_image->get(DefaultRootWindow(m_dpy), m_offsetLeft, m_offsetTop);
76
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +000077 // Create additional images used in polling algorithm, warn if
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000078 // underlying class names are different from the class name of the
79 // primary image.
80 m_rowImage = factory->newImage(m_dpy, m_width, 1);
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +000081 m_columnImage = factory->newImage(m_dpy, 1, m_height);
82 const char *primaryImgClass = m_image->className();
83 const char *rowImgClass = m_rowImage->className();
84 const char *columnImgClass = m_columnImage->className();
85 if (strcmp(rowImgClass, primaryImgClass) != 0 ||
86 strcmp(columnImgClass, primaryImgClass) != 0) {
87 vlog.error("Image types do not match (%s, %s, %s)",
88 primaryImgClass, rowImgClass, columnImgClass);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000089 }
90
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000091 m_changeFlags = new bool[m_numTiles];
Constantin Kaplinsky20390a22008-01-17 15:17:36 +000092 m_rateMatrix = new char[m_numTiles];
93 m_videoFlags = new char[m_numTiles];
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +000094 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinsky20390a22008-01-17 15:17:36 +000095 memset(m_rateMatrix, 0, m_numTiles);
96 memset(m_videoFlags, 0, m_numTiles);
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +000097}
98
99PollingManager::~PollingManager()
100{
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000101 delete[] m_videoFlags;
102 delete[] m_rateMatrix;
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000103 delete[] m_changeFlags;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000104
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000105 delete m_rowImage;
Constantin Kaplinskyed3cf5d2007-12-28 17:59:10 +0000106 delete m_columnImage;
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000107}
108
109//
110// Register VNCServer object.
111//
112
113void PollingManager::setVNCServer(VNCServer *s)
114{
115 m_server = s;
116}
117
118//
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000119// DEBUG: Measuring time spent in the poll() function,
120// as well as time intervals between poll() calls.
121//
122
123#ifdef DEBUG
124void PollingManager::debugBeforePoll()
125{
126 TimeMillis timeNow;
127 int diff = timeNow.diffFrom(m_timeSaved);
128 fprintf(stderr, "[wait%4dms]\t[step %2d]\t", diff, m_pollingStep % 32);
129 m_timeSaved = timeNow;
130}
131
132void PollingManager::debugAfterPoll()
133{
134 TimeMillis timeNow;
135 int diff = timeNow.diffFrom(m_timeSaved);
136 fprintf(stderr, "[poll%4dms]\n", diff);
137 m_timeSaved = timeNow;
138}
139
140#endif
141
142//
143// Search for changed rectangles on the screen.
144//
145
146void PollingManager::poll()
147{
148#ifdef DEBUG
149 debugBeforePoll();
150#endif
151
Constantin Kaplinskyd0b15c62007-10-09 08:15:25 +0000152 // Perform polling and try update clients if changes were detected.
153 if (pollScreen())
Constantin Kaplinskyb30ae7f2006-05-25 05:04:46 +0000154 m_server->tryUpdate();
155
156#ifdef DEBUG
157 debugAfterPoll();
158#endif
159}
160
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000161#ifdef DEBUG_REPORT_CHANGED_TILES
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000162#define DBG_REPORT_CHANGES(title) printChanges((title))
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000163#else
164#define DBG_REPORT_CHANGES(title)
165#endif
166
Constantin Kaplinsky9ee8dc62007-10-09 07:46:32 +0000167bool PollingManager::pollScreen()
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000168{
169 if (!m_server)
170 return false;
171
Constantin Kaplinsky1d378022007-12-25 17:43:09 +0000172 // If video data should have higher priority, and video area was
173 // detected, perform special passes to send video data only. Such
174 // "video passes" will be performed between normal polling passes.
175 // No actual polling is performed in a video pass since we know that
176 // video is changing continuously.
Constantin Kaplinsky04e910b2007-12-25 18:10:35 +0000177 //
178 // FIXME: Should we move this block into a separate function?
179 // FIXME: Giving higher priority to video area lengthens video
180 // detection cycles. Should we do something with that?
Constantin Kaplinsky1d378022007-12-25 17:43:09 +0000181 if ((int)m_videoPriority > 1 && !m_videoRect.is_empty()) {
182 if (m_numVideoPasses > 0) {
183 m_numVideoPasses--;
184 getScreenRect(m_videoRect);
185 return true; // we've got changes
186 } else {
187 // Normal pass now, but schedule video passes for next calls.
188 m_numVideoPasses = (int)m_videoPriority - 1;
189 }
190 }
191
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000192 // Clear the m_changeFlags[] array, indicating that no changes have
193 // been detected yet.
194 memset(m_changeFlags, 0, m_numTiles * sizeof(bool));
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000195
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000196 // First pass over the framebuffer. Here we scan 1/32 part of the
197 // framebuffer -- that is, one line in each (32 * m_width) stripe.
198 // We compare the pixels of that line with previous framebuffer
Constantin Kaplinsky85b5eb92008-01-17 17:41:48 +0000199 // contents and raise corresponding elements of m_changeFlags[].
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000200 int scanOffset = m_pollingOrder[m_pollingStep++ % 32];
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000201 int nTilesChanged = 0;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000202 for (int y = scanOffset; y < m_height; y += 32) {
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000203 nTilesChanged += checkRow(0, y, m_width);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000204 }
205
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000206 // Do the work related to video area detection, if enabled.
Constantin Kaplinsky1d378022007-12-25 17:43:09 +0000207 bool haveVideoRect = false;
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000208 if ((int)m_videoPriority != 0) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000209 handleVideo();
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000210 if (!m_videoRect.is_empty()) {
211 getScreenRect(m_videoRect);
212 haveVideoRect = true;
213 }
214 }
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000215
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000216 DBG_REPORT_CHANGES("After 1st pass");
217
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000218 // If some changes have been detected:
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000219 if (nTilesChanged) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000220 // Try to find more changes around. Before doing that, mark the
221 // video area as changed, to skip comparisons of its pixels.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000222 flagVideoArea(true);
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000223 DBG_REPORT_CHANGES("Before checking neighbors");
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000224 checkNeighbors();
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000225 DBG_REPORT_CHANGES("After checking neighbors");
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000226
227 // Inform the server about the changes. This time, we mark the
228 // video area as NOT changed, to prevent reading its pixels again.
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000229 flagVideoArea(false);
Constantin Kaplinskybf11a2d2008-01-10 15:59:03 +0000230 DBG_REPORT_CHANGES("Before sending");
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000231 nTilesChanged = sendChanges();
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000232 }
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000233
Constantin Kaplinsky52f29d32007-12-28 18:30:54 +0000234#ifdef DEBUG_PRINT_NUM_CHANGED_TILES
235 printf("%3d ", nTilesChanged);
236 if (m_pollingStep % 32 == 0) {
237 printf("\n");
238 }
239#endif
240
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000241#ifdef DEBUG
242 if (nTilesChanged != 0) {
243 fprintf(stderr, "#%d# ", nTilesChanged);
244 }
245#endif
246
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000247 return (nTilesChanged != 0 || haveVideoRect);
Constantin Kaplinskya119b482007-10-04 06:02:02 +0000248}
249
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000250int PollingManager::checkRow(int x, int y, int w)
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000251{
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000252 // If necessary, expand the row to the left, to the tile border.
253 // In other words, x must be a multiple of 32.
254 if (x % 32 != 0) {
255 int correction = x % 32;
256 x -= correction;
257 w += correction;
258 }
259
Constantin Kaplinsky41de2422008-01-19 09:03:47 +0000260 // Read a row from the screen into m_rowImage.
261 getRow(x, y, w);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000262
Constantin Kaplinsky04aa5202008-01-18 15:37:15 +0000263 // Compute a pointer to the initial element of m_changeFlags.
264 bool *pChangeFlags = &m_changeFlags[getTileIndex(x, y)];
265
266 // Compute pointers to image data to be compared.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000267 char *ptr_old = m_image->locatePixel(x, y);
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000268 char *ptr_new = m_rowImage->xim->data;
269
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000270 // Compare pixels, raise corresponding elements of m_changeFlags[].
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000271 // First, handle full-size (32 pixels wide) tiles.
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000272 int nTilesChanged = 0;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000273 int nBytesPerTile = 32 * m_bytesPerPixel;
274 for (int i = 0; i < w / 32; i++) {
275 if (memcmp(ptr_old, ptr_new, nBytesPerTile)) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000276 *pChangeFlags = true;
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000277 nTilesChanged++;
278 }
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000279 pChangeFlags++;
Constantin Kaplinsky86506882008-01-21 10:24:25 +0000280 ptr_old += nBytesPerTile;
281 ptr_new += nBytesPerTile;
282 }
283
284 // Handle the rightmost pixels, if the width is not a multiple of 32.
285 int nBytesLeft = (w % 32) * m_bytesPerPixel;
286 if (nBytesLeft != 0) {
287 if (memcmp(ptr_old, ptr_new, nBytesLeft)) {
288 *pChangeFlags = true;
289 nTilesChanged++;
290 }
Constantin Kaplinskybc6b9e22007-10-04 11:43:41 +0000291 }
292
293 return nTilesChanged;
294}
295
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000296int PollingManager::checkColumn(int x, int y, int h, bool *pChangeFlags)
297{
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000298 getColumn(x, y, h);
299
300 int nTilesChanged = 0;
301 for (int nTile = 0; nTile < (h + 31) / 32; nTile++) {
302 if (!*pChangeFlags) {
303 int tile_h = (h - nTile * 32 >= 32) ? 32 : h - nTile * 32;
304 for (int i = 0; i < tile_h; i++) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000305 // FIXME: Do not compute these pointers in the inner cycle.
Constantin Kaplinsky801123d2008-01-18 15:23:11 +0000306 char *ptr_old = m_image->locatePixel(x, y + nTile * 32 + i);
307 char *ptr_new = m_columnImage->locatePixel(0, nTile * 32 + i);
Constantin Kaplinskyec45c482008-01-18 14:13:16 +0000308 if (memcmp(ptr_old, ptr_new, m_bytesPerPixel)) {
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000309 *pChangeFlags = true;
310 nTilesChanged++;
311 break;
312 }
313 }
314 }
315 pChangeFlags += m_widthTiles;
316 }
317
318 return nTilesChanged;
319}
320
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000321int PollingManager::sendChanges()
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000322{
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000323 const bool *pChangeFlags = m_changeFlags;
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000324 int nTilesChanged = 0;
325
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000326 Rect rect;
327 for (int y = 0; y < m_heightTiles; y++) {
328 for (int x = 0; x < m_widthTiles; x++) {
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000329 if (*pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000330 // Count successive tiles marked as changed.
331 int count = 1;
Constantin Kaplinsky808db552007-10-09 12:48:26 +0000332 while (x + count < m_widthTiles && *pChangeFlags++) {
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000333 count++;
334 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000335 nTilesChanged += count;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000336 // Compute the coordinates and the size of this band.
337 rect.setXYWH(x * 32, y * 32, count * 32, 32);
338 if (rect.br.x > m_width)
339 rect.br.x = m_width;
340 if (rect.br.y > m_height)
341 rect.br.y = m_height;
342 // Add to the changed region maintained by the server.
343 getScreenRect(rect);
344 m_server->add_changed(rect);
345 // Skip processed tiles.
346 x += count;
347 }
348 }
349 }
Constantin Kaplinsky1a032112008-01-09 10:22:42 +0000350 return nTilesChanged;
Constantin Kaplinskya79255b2007-10-07 13:03:55 +0000351}
352
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000353void PollingManager::handleVideo()
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000354{
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000355 // Update counters in m_rateMatrix.
Constantin Kaplinsky20390a22008-01-17 15:17:36 +0000356 for (int i = 0; i < m_numTiles; i++)
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000357 m_rateMatrix[i] += (m_changeFlags[i] != false);
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000358
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000359 // Once per eight calls: detect video rectangle by examining
360 // m_rateMatrix[], then reset counters in m_rateMatrix[].
361 if (m_pollingStep % 8 == 0) {
362 detectVideo();
Constantin Kaplinsky20390a22008-01-17 15:17:36 +0000363 memset(m_rateMatrix, 0, m_numTiles);
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000364 }
Constantin Kaplinskyc1984e02007-10-08 14:54:18 +0000365}
366
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000367void PollingManager::flagVideoArea(bool value)
Constantin Kaplinskybd390352007-12-28 08:44:59 +0000368{
Constantin Kaplinsky48792632007-12-28 18:21:42 +0000369 if (m_videoRect.is_empty())
370 return;
371
Constantin Kaplinskybd390352007-12-28 08:44:59 +0000372 Rect r(m_videoRect.tl.x / 32, m_videoRect.tl.y / 32,
373 m_videoRect.br.x / 32, m_videoRect.br.y / 32);
374
375 for (int y = r.tl.y; y < r.br.y; y++)
376 for (int x = r.tl.x; x < r.br.x; x++)
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000377 m_changeFlags[y * m_widthTiles + x] = value;
Constantin Kaplinskybd390352007-12-28 08:44:59 +0000378}
379
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000380void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000381PollingManager::checkNeighbors()
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000382{
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000383 int x, y;
384
385 // Check neighboring pixels above and below changed tiles.
386 // FIXME: Fast skip to the first changed tile (and to the last, too).
387 // FIXME: Check the full-width line above the first changed tile?
388 for (y = 0; y < m_heightTiles; y++) {
389 bool doneAbove = false;
390 bool doneBelow = false;
391 for (x = 0; x < m_widthTiles; x++) {
392 if (!doneAbove && y > 0 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000393 m_changeFlags[y * m_widthTiles + x] &&
394 !m_changeFlags[(y - 1) * m_widthTiles + x]) {
395 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000396 checkRow(x * 32, y * 32 - 1, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000397 doneAbove = true;
398 }
399 if (!doneBelow && y < m_heightTiles - 1 &&
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000400 m_changeFlags[y * m_widthTiles + x] &&
401 !m_changeFlags[(y + 1) * m_widthTiles + x]) {
402 // FIXME: Check m_changeFlags[] to decrease height of the row.
Constantin Kaplinsky9d37e5c2008-01-18 11:17:26 +0000403 checkRow(x * 32, (y + 1) * 32, m_width - x * 32);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000404 doneBelow = true;
405 }
406 if (doneBelow && doneAbove)
407 break;
408 }
409 }
410
411 // Check neighboring pixels at the right side of changed tiles.
412 for (x = 0; x < m_widthTiles - 1; x++) {
413 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000414 if (m_changeFlags[y * m_widthTiles + x] &&
415 !m_changeFlags[y * m_widthTiles + x + 1]) {
416 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000417 checkColumn((x + 1) * 32, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000418 &m_changeFlags[y * m_widthTiles + x + 1]);
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000419 break;
420 }
421 }
422 }
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000423
424 // Check neighboring pixels at the left side of changed tiles.
425 for (x = m_widthTiles - 1; x > 0; x--) {
426 for (y = 0; y < m_heightTiles; y++) {
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000427 if (m_changeFlags[y * m_widthTiles + x] &&
428 !m_changeFlags[y * m_widthTiles + x - 1]) {
429 // FIXME: Check m_changeFlags[] to decrease height of the column.
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000430 checkColumn(x * 32 - 1, y * 32, m_height - y * 32,
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000431 &m_changeFlags[y * m_widthTiles + x - 1]);
Constantin Kaplinsky474b12f2008-01-09 10:25:34 +0000432 break;
433 }
434 }
435 }
Constantin Kaplinsky553340c2007-12-28 18:37:04 +0000436}
437
438void
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000439PollingManager::printChanges(const char *header) const
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000440{
441 fprintf(stderr, "%s:", header);
442
Constantin Kaplinsky850de2b2008-01-17 19:14:37 +0000443 const bool *pChangeFlags = m_changeFlags;
444
Constantin Kaplinskyf50bd7f2008-01-10 15:27:42 +0000445 for (int y = 0; y < m_heightTiles; y++) {
446 for (int x = 0; x < m_widthTiles; x++) {
447 if (*pChangeFlags++) {
448 // Count successive tiles marked as changed.
449 int count = 1;
450 while (x + count < m_widthTiles && *pChangeFlags++) {
451 count++;
452 }
453 // Print.
454 fprintf(stderr, " (%d,%d)*%d", x, y, count);
455 // Skip processed tiles.
456 x += count;
457 }
458 }
459 }
460
461 fprintf(stderr, "\n");
462}
463
464void
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000465PollingManager::detectVideo()
466{
467 // Configurable parameters.
468 const int VIDEO_THRESHOLD_0 = 3;
469 const int VIDEO_THRESHOLD_1 = 5;
470
Constantin Kaplinskybb563772007-12-25 11:25:07 +0000471 // In m_rateMatrix, clear counters corresponding to non-32x32 tiles.
472 // This will guarantee that the size of the video area is always a
473 // multiple of 32 pixels. This is important for hardware JPEG encoders.
Constantin Kaplinskybb563772007-12-25 11:25:07 +0000474 if (m_width % 32 != 0) {
Constantin Kaplinsky20390a22008-01-17 15:17:36 +0000475 for (int n = m_widthTiles - 1; n < m_numTiles; n += m_widthTiles)
Constantin Kaplinskybb563772007-12-25 11:25:07 +0000476 m_rateMatrix[n] = 0;
477 }
478 if (m_height % 32 != 0) {
Constantin Kaplinsky20390a22008-01-17 15:17:36 +0000479 for (int n = m_numTiles - m_widthTiles; n < m_numTiles; n++)
Constantin Kaplinskybb563772007-12-25 11:25:07 +0000480 m_rateMatrix[n] = 0;
481 }
482
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000483 // First, detect candidate region that looks like video. In other
484 // words, find a region that consists of continuously changing
485 // pixels. Save the result in m_videoFlags[].
Constantin Kaplinsky20390a22008-01-17 15:17:36 +0000486 for (int i = 0; i < m_numTiles; i++) {
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000487 if (m_rateMatrix[i] <= VIDEO_THRESHOLD_0) {
488 m_videoFlags[i] = 0;
489 } else if (m_rateMatrix[i] >= VIDEO_THRESHOLD_1) {
490 m_videoFlags[i] = 1;
491 }
492 }
493
494 // Now, choose the biggest rectangle from that candidate region.
495 Rect newRect;
496 getVideoAreaRect(&newRect);
497
498 // Does new rectangle differ from the previously detected one?
499 // If it does, save new rectangle and inform the server.
500 if (!newRect.equals(m_videoRect)) {
501 if (newRect.is_empty()) {
Constantin Kaplinskydab9a562007-10-10 01:33:39 +0000502 vlog.debug("No video detected");
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000503 } else {
Constantin Kaplinskydab9a562007-10-10 01:33:39 +0000504 vlog.debug("Detected video %dx%d at (%d,%d)",
505 newRect.width(), newRect.height(),
506 newRect.tl.x, newRect.tl.y);
Constantin Kaplinsky6bb4bf12007-10-09 12:03:15 +0000507 }
508 m_videoRect = newRect;
509 m_server->set_video_area(newRect);
510 }
511}
512
513void
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000514PollingManager::getVideoAreaRect(Rect *result)
515{
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000516 int *mx_hlen, *mx_vlen;
517 constructLengthMatrices(&mx_hlen, &mx_vlen);
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000518
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000519 int full_h = m_heightTiles;
520 int full_w = m_widthTiles;
521 int x, y;
522 Rect max_rect(0, 0, 0, 0);
523 Rect local_rect;
524
525 for (y = 0; y < full_h; y++) {
526 for (x = 0; x < full_w; x++) {
527 int max_w = mx_hlen[y * full_w + x];
528 int max_h = mx_vlen[y * full_w + x];
529 if (max_w > 2 && max_h > 1 && max_h * max_w > (int)max_rect.area()) {
530 local_rect.tl.x = x;
531 local_rect.tl.y = y;
532 findMaxLocalRect(&local_rect, mx_hlen, mx_vlen);
533 if (local_rect.area() > max_rect.area()) {
534 max_rect = local_rect;
535 }
536 }
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000537 }
538 }
539
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000540 destroyLengthMatrices(mx_hlen, mx_vlen);
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000541
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000542 max_rect.tl.x *= 32;
543 max_rect.tl.y *= 32;
544 max_rect.br.x *= 32;
545 max_rect.br.y *= 32;
546 if (max_rect.br.x > m_width)
547 max_rect.br.x = m_width;
548 if (max_rect.br.y > m_height)
549 max_rect.br.y = m_height;
550 *result = max_rect;
Constantin Kaplinsky56649982007-09-04 09:15:35 +0000551}
Constantin Kaplinsky0fc9f172007-09-29 04:00:02 +0000552
553void
554PollingManager::constructLengthMatrices(int **pmx_h, int **pmx_v)
555{
556 // Handy shortcuts.
557 int h = m_heightTiles;
558 int w = m_widthTiles;
559
560 // Allocate memory.
561 int *mx_h = new int[h * w];
562 memset(mx_h, 0, h * w * sizeof(int));
563 int *mx_v = new int[h * w];
564 memset(mx_v, 0, h * w * sizeof(int));
565
566 int x, y, len, i;
567
568 // Fill in horizontal length matrix.
569 for (y = 0; y < h; y++) {
570 for (x = 0; x < w; x++) {
571 len = 0;
572 while (x + len < w && m_videoFlags[y * w + x + len]) {
573 len++;
574 }
575 for (i = 0; i < len; i++) {
576 mx_h[y * w + x + i] = len - i;
577 }
578 x += len;
579 }
580 }
581
582 // Fill in vertical length matrix.
583 for (x = 0; x < w; x++) {
584 for (y = 0; y < h; y++) {
585 len = 0;
586 while (y + len < h && m_videoFlags[(y + len) * w + x]) {
587 len++;
588 }
589 for (i = 0; i < len; i++) {
590 mx_v[(y + i) * w + x] = len - i;
591 }
592 y += len;
593 }
594 }
595
596 *pmx_h = mx_h;
597 *pmx_v = mx_v;
598}
599
600void
601PollingManager::destroyLengthMatrices(int *mx_h, int *mx_v)
602{
603 delete[] mx_h;
604 delete[] mx_v;
605}
606
607// NOTE: This function assumes that current tile has non-zero in mx_h[],
608// otherwise we get division by zero.
609void
610PollingManager::findMaxLocalRect(Rect *r, int mx_h[], int mx_v[])
611{
612 int idx = r->tl.y * m_widthTiles + r->tl.x;
613
614 // NOTE: Rectangle's maximum width and height are 25 and 18
615 // (in tiles, where each tile is usually 32x32 pixels).
616 int max_w = mx_h[idx];
617 if (max_w > 25)
618 max_w = 25;
619 int cur_h = 18;
620
621 int best_w = max_w;
622 int best_area = 1 * best_w;
623
624 for (int i = 0; i < max_w; i++) {
625 int h = mx_v[idx + i];
626 if (h < cur_h) {
627 cur_h = h;
628 if (cur_h * max_w <= best_area)
629 break;
630 }
631 if (cur_h * (i + 1) > best_area) {
632 best_w = i + 1;
633 best_area = cur_h * best_w;
634 }
635 }
636
637 r->br.x = r->tl.x + best_w;
638 r->br.y = r->tl.y + best_area / best_w;
639}
640