Implementando um VNC

Pessoal,

Gostaria de tentar implementar um VNC, para permitir que clientes vejam um videozinho do que eu estou fazendo no meu desktop, por exemplo enquanto eu mexo no Eclipse. Não preciso do controle remoto, apenas do video.

Estou meio perdido:

  1. Existe um protocolo padrão para VNC com baixo consumo de banda?

  2. Pensando um pouco pensei que eu poderia implementar o meu próprio protocolo, enviando apenas os pixels que estão mudando na tela. Daí se o cara fica olhando admirado para o eclipse sem fazer nada ele não manda nada. Se o cara digita um código ele só manda os pixelzinhos do texto que está aparecendo ali. É assim que o VNC funciona ou é algo bem mais sofisticado?

  3. Existe alguma implementação em Java open-source?

Fala Sergio!

  1. Yeap: http://www.tightvnc.com

  2. Bem mais sofisticado, mas no geral eh isso mesmo.

  3. Yeap, mas so pro viewer, nao pro servidor. http://www.tightvnc.com/download.html

De qualquer maneira, pegar os pixels e ver o que foi alterado é uma operação terrivelmente onerosa.

Provavelmente você deve ter já usado o VNC (tanto faz se é o TightVNC ou o RealVNC) e visto que ele puxa uma CPU violenta da máquina que está sendo controlada (eu estava controlando uma máquina Windows 98, Celeron 333 para poder fazer uns testes. O RealVNC estava puxando 30% da CPU :wink: )

O que o RealVNC faz agora é algo um pouco mais esperto. Ele instala um “hook” no Windows que vigia todas as operações de mouse, teclado e janelas. Quando alguma coisa é alterada na máquina que está sendo controlada, ele usa o “hook” para enviar para o client VNC os parâmetros da API que está sendo chamada pelo Windows para mover o mouse, ou tratar o teclado, ou pintar as janelas. Só que isso é tudo código em C mesmo. Ele só não usa o hook quando você está usando uma aplicação console. Nesse caso ele tem de usar o método de pegar os pixels e ver o que foi alterado - aí o bicho pega.

Mas aí perde a independência de plataforma, né?

Acho que no meu caso, onde eu quero apenas um videozinho que vai passar dentro de uma janela com scrollbar, o primeiro método é mais indicado né?

Agora vc tem razão quanto a fome disso: 1024 x 768 = 786432 pixels

Se eu tenho um frame rate de 1 por segundo, vou ter que percorrer esse array a cada um segundo pra ver se algo mudou. O CPU não vai ficar idle nunca assim… É assim mesmo que eu vou ter que fazer isso né ??? Depois é legal meter um Zip encima dos pixels que mudaram né… Se o cara abriu uma tela branca encima, eu não vou querer mandar 786432 pixels brancos.

Quanto à “independência de plataforma” acho que você vai ter de se restringir à parte client. Tanto o TightVNC quanto o RealVNC têm clients Java.

E o esquema de compressão de ambos é bastante complicado, porque para fazer uma compressão eficiente, é necessário fazê-la em duas dimensões, não somente em uma, se você fosse usar um esquema ingênuo de .zip (e até 3 se você considerar a cor como sendo uma dimensão; o VNC tem a opção de diminuir a profundidade das cores - de 16M cores para somente 256 cores, por exemplo - para diminuir a quantidade de dados a serem transmitidos) . Além disso, usam alguns truques, por exemplo, é interessante atualizar mais freqüentemente a área em volta do “caret” e do ponteiro do mouse, porque é provavelmente lá onde você está focalizando sua atenção, e é provavelmente lá que a tela está sendo modificada.

Usa mpeg1, ou pelo menos jpeg, pra comprimir os frames. Codificar jpeg com java é brincadeira de criança (já tem API pra isso). Já mpeg exige um pouquinho mais de trabalho. Se usar jpeg, use tabelas fixas de compressão e quantização, isso te salva uns tantos bytes.

A parte ruim de usar técnicas lossy é que texto fica dificil de ler com compressão muito alta.

Mas se você estiver realmente se sentindo aventuroso, use lzw mesmo e use motion vectors, damage zones e toda esses truques para acelerar a transmissão.

E o mais importante, fique o mais longe possivel do TCP se você quer algo que funcione em real-time.

Fico imaginando um VNC com “lossy compression” (não só a redução de profundidade nas cores)…
Ia ser meio complicado digitar texto no site do GUJ, que usa umas letras muito pequenas. Ia aparecer só “sujeira”.

Depende, se você tiver acesso ao toolkit, pode tratar texto de forma especial.

Teoricamente isso é poderia ser feito com AWT/Swing usando algo semelhante ao xprotocol.

tenho um colega que implementou uma especie VNC, mas só para mouse…
nada muito sofisticado…

se for open source o programa ele com certeza libera o fonte…

e aí???

Nossa, essa thread mal comecou e ja ta com aquele cheirinho de reinvencao de roda… hmmmmmmm… delicia :mrgreen:

Nao sei nem se a AWT te da esse controle todo a pegar os pixels da tela. Rola fazer isso com o java.awt.Robot e amigos?

Pois é, já tem tudo pronto, e o client do RealVNC é minúsculo (tanto o applet quanto o executável), e foi portado para N arquiteturas, que custa rodar o client e o server? Eu lembro que existe um modo do VNC que lhe permite conectar N clients a um único server, para que você possa mostrar o tal “videozinho”.

Eu acho que o que o Sérgio quer é um VNC muito mais simples e jmais flexível apra embutir em algo (lohis?).

Por que tu não tenta o método mais simples e vê se dá galho? Eu acho que se a área monitorada for pequena não deve ter muitos problemas.

Shoes

O programinha abaixo já te permite brincar de fazer um codec VNC.

Ele te fala quantos pixels estão mudando conforme vc brinca com a tela.

Se alguem fizer um codec de brincadeira, tipo um MPEG da vida e quiser me doar eu aceito. :stuck_out_tongue:

O objetivo e compremir esses bytes que estão mudando para eu poder jogar isso na rede.


import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;

public class ScreenVideo extends JPanel implements Runnable {
	
	private int x, y, w, h;
	private float fps;
	private Robot robot;	
	private Rectangle rect;
	private Thread thread;
	private volatile boolean bThread;
	private int sleeptime;
	private Dimension size;
	private int [] imageBytes;
	private int [] lastFrame;
	
	public ScreenVideo(int x, int y, int w, int h, float fps) throws AWTException {
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
		this.fps = fps;
		this.rect = new Rectangle(x, y, w, h);
		this.size = new Dimension(w, h);
		this.robot = new Robot();
		this.sleeptime = (int) ((1 / fps) * 1000);
		this.imageBytes = new int[w * h];
		this.lastFrame = new int[w * h];
	}
	
	public void start() {
		startThread();
	}
	
	public void stop() {
		stopThread();
	}
	
	public Dimension getPreferredSize() {
		return size;
	}
	
	private void startThread() {
		bThread = true;
		thread = new Thread(this);
		thread.start();
	}
	
	private void stopThread() {
		bThread = false;
		thread.interrupt();
		thread = null;
	}
	
	public void run() {
		try {
			while(bThread) {
				BufferedImage bi = robot.createScreenCapture(rect);
				Graphics g = getGraphics();
				g.drawImage(bi, 0, 0, this);
				
				// veja o que mudou...
				bi.getRGB(0, 0, w, h, imageBytes, 0, w);
				int changed = 0;
				for(int i=0;i<imageBytes.length;i++) {
					if (imageBytes[i] != lastFrame[i]) {
						changed++;
						lastFrame[i] = imageBytes[i];
					}
				}
				System.out.println("Mudou: " + changed);
				
				Thread.sleep(sleeptime);
			}
		} catch(InterruptedException e) {
			// bye
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String [] args) throws Exception {
		JFrame frame = new JFrame("Minha Tela");
		final ScreenVideo sv = new ScreenVideo(0, 0, 1024, 768, 1);
		frame.setContentPane(sv);
		
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				sv.stop();
				System.exit(0);
			}
		});
		
		frame.pack();
		sv.start();
		frame.setVisible(true);
	}
}
				
			
	
	

>

Sergio, qual a performance disso?

Uma merda como o thingol falou, mas o P4 aqui tá aguentando tranquilo… :slight_smile:

Não há nada que um pentium não resolva hoje em dia, né? :stuck_out_tongue:

PS: Gostou do volatile no bThread ??? :thumbup:

Nao rodei o codigo, mas se eu entendi ele desenha um JFrame de 1024x768 na tela, e poe ele como visivel em cima de tudo, capturando o que estiver ali. Se for pra capturar soh coisas que acontecem dentro de uma janela AWT que vc tem controle, eh mais facil escutar a EventQueue - e tirar o screenshot sempre que um evento passar por ali :wink:

Acho que o legal seria vc minimizar esse JFrame e começar a mexer no Eclipse por exemplo. Daí o frame vai pegando tudo.

Mas concordo contigo, esse JFrame ficou sem utilidade. Não faz sentido para a pessoa que está capturando ver o que ela está capturando. Se não acaba virando espelho infinito.

Pô, tentei comprimir com ZIP e ficou ruim. Picos de 10 k por segundo. Fica muito pesado em termos de banda.

Vou dar uma olhada no VNC. Reinventar a roda dessa vez vai ser realmente loucura. :cry:

Acabei de ver tb que para cada pixel vc tem que enviar a posição e o valor, ou seja, o dobro de informações. :cry:

Que pena que o Swing não tem um esquema de permitir você amarrar um “hook” nas rotinas que você está chamando - ou teria se você mexesse em javax.swing.DebugGraphics?
(O fonte do Swing está disponível, mas você tem coragem de mexer nele?).
Senão dava para fazer uma coisa parecida com o RealVNC (ou o TightVNC usando um driver especial). Ou seja, em vez de ler a tela (uma operação lenta), a cada vez que o server executasse um comando do tipo drawString ou drawOval, por exemplo, enviasse para seus clientes (talvez usando UDP e multicast :wink: ) o comando drawString codificado, com os parâmetros…

Seria que não rola usar uma Toolkit wrapper?

Se o interesse é em fazer algo só para o Eclipse, já tentou ver se a API dele não oferece algo do gênero?