2009-12-01 25 views
22

Estoy aprendiendo WCF, LINQ y algunas otras tecnologías al escribir, desde cero, una aplicación de control remoto personalizada como VNC. Lo estoy creando con tres objetivos principales en mente:¿Manera eficiente de enviar imágenes a través de WCF?

  1. El servidor proporcionará 'control remoto' en un nivel de aplicación (es decir, ventanas integradas) en lugar de acceso completo al escritorio.
  2. El cliente puede seleccionar cualquier cantidad de aplicaciones que se ejecutan en el servidor y recibir una secuencia de imágenes de cada una de ellas.
  3. Un cliente puede conectarse a más de un servidor simultáneamente.

En este momento estoy usando WCF para enviar una matriz de bytes que representa la ventana que se envía: aplicación

using (var ms = new MemoryStream()) { 
    window.GetBitmap().Save(ms, ImageFormat.Jpeg); 
    frame.Snapshot = ms.ToArray(); 
} 

GetBitmap:

var wRectangle = GetRectangle(); 
var image = new Bitmap(wRectangle.Width, wRectangle.Height); 
var gfx = Graphics.FromImage(image); 

gfx.CopyFromScreen(wRectangle.Left, wRectangle.Top, 0, 0, wRectangle.Size, CopyPixelOperation.SourceCopy); 

return image; 

Se envía a través de WCF (TCPBinding y siempre estará en LAN) al cliente y reconstruido en un formulario de Windows en blanco sin ningún borde como este:

using (var ms = new MemoryStream(_currentFrame.Snapshot)) 
{ 
    BackgroundImage = Image.FromStream(ms); 
} 

Me gustaría hacer que este proceso sea lo más eficiente posible tanto en el uso de la CPU como de la memoria con el ancho de banda en tercer lugar. Mi objetivo es que el cliente se conecte a más de 5 servidores con más de 10 aplicaciones por servidor.

¿Es mi método actual el mejor enfoque (mientras sigo usando estas tecnologías) y hay algo que pueda hacer para mejorarlo?

ideas que busco en (pero no tengo ninguna experiencia con):

  • utilización de biblioteca de gráficos de código abierto para capturar y guardar las imágenes en lugar de la solución .NET.
  • Guardando como PNG u otro tipo de imagen en lugar de JPG.
  • Enviar deltas de imagen en vez de una imagen completa todo el tiempo.
  • Pruebe y "grabe" las ventanas y cree una secuencia de video comprimido en lugar de instantáneas de imágenes (¿mpeg?).

Respuesta

20

Usted debe ser consciente de estos puntos:

  • de transporte: TCP/binario de codificación de mensajes va a ser la forma más rápida de transferir los datos de imagen
  • captura
  • Image: usted puede confiar en P/Invoke para acceder a sus datos de pantalla, ya que esto puede ser más rápido y consumir más memoria. Algunos ejemplos: Capturing the Screen Image in C# [P/Invoke], How to take a screen shot using .NET [Managed] y Capturing screenshots using C# (Managed)
  • Debe reducir los datos de su imagen antes de enviarla;
    • elegir el formato de imagen con prudencia, ya que algunos formatos tienen compresión nativa (en formato JPG)
    • un ejemplo debería ser Find differences between images C#
    • enviando única diferencia de imágenes, puede recortar y simplemente enviar áreas que no estén vacíos
  • Intente inspeccionar sus mensajes WCF. Esto te ayudará a comprender cómo se formatean los mensajes y te ayudará a identificar cómo hacer que esos mensajes sean más pequeños.

Justo después pasando por todos estos pasos y estar satisfecho con su código final, se puede descargar VncSharp source code. Implementa the RFB Protocol(Wikipedia entry), "a simple protocol for remote access to graphical user interfaces. Because it works at the framebuffer level it is applicable to all windowing systems and applications, including X11, Windows and Macintosh. RFB is the protocol used in VNC (Virtual Network Computing)."

0

En lugar de capturar la imagen completa acaba de enviar las subsecciones más pequeñas de la imagen. Significado: comenzando en la esquina superior izquierda, envíe una imagen de 10x10 píxeles, luego "mueva" diez píxeles y envíe el siguiente cuadro de 10 píxeles, y así sucesivamente. A continuación, puede enviar docenas de imágenes pequeñas y luego actualizar la imagen completa pintada en el cliente. Si ha usado RDC para ver imágenes en una máquina remota, probablemente lo haya visto hacer este tipo de pintura de pantalla.

Al utilizar las secciones de imagen más pequeñas, puede dividir también los deltas, de modo que si nada ha cambiado en la sección actual, puede omitirlo con seguridad, informar al cliente que se está salteando y pasar al siguiente sección.

Definitivamente querrá usar la compresión para enviar las imágenes. Sin embargo, debe verificar si obtiene tamaños de archivo más pequeños utilizando compresión similar a gZip, o si usa un códec de imagen que le brinda mejores resultados. Nunca he hecho una comparación, así que no puedo decirlo de una manera u otra.

+0

Sí, siento que dividir la imagen en mosaicos y detectar si un mosaico ha cambiado desde el último cuadro definitivamente es una optimización que debo implementar. Creo que el camino más efectivo es hacer que todo funcione lo mejor posible en la imagen completa primero (obtener la compresión correcta, detectar si la imagen ha cambiado desde el último cuadro, usar el método más rápido para serializar la imagen, etc.) y luego dividir la imagen y aplicar todas las mejoras a cada azulejo. – InvertedAcceleration

+0

Comprobaré la diferencia de tamaño de los diversos formatos de imagen VS de compresión genérica, pero estoy bastante seguro de que para que la compresión sea realmente efectiva debe ser específica del contexto. Creo que jpg y png producirán tamaños de imagen mucho más pequeños que un bmp comprimido usando bzip o gzip. – InvertedAcceleration

1

La forma más rápida para enviar datos entre el cliente/servidor es enviar una matriz de bytes, o varias matrices de bytes. De esta forma, WCF no tiene que hacer ninguna serialización personalizada en sus datos.

Dicho esto. Debe usar la nueva biblioteca WPF/.Net 3.5 para comprimir sus imágenes en lugar de las de System.Drawing. Las funciones en el espacio de nombres System.Windows.Media.Imaging son más rápidas que las anteriores y aún se pueden usar en winforms.

Para saber si la compresión es el camino a seguir, tendrá que comparar su escenario para saber cómo se compara el tiempo de compresión/descompresión con la transferencia de todos los bytes descomprimidos.

Si transfiere los datos a través de Internet, entonces la compresión ayudará a ciencia cierta. Entre los componentes en la misma máquina o en una LAN, el beneficio puede no ser tan obvio.

También puede probar la compresión de la imagen, a continuación, fragmentar los datos y enviar de forma asíncrona con un trozo Identificación del cual rompecabezas en el cliente. conexiones TCP comienzo lento y el aumento de ancho de banda con el tiempo, por lo que a partir de dos o cuatro al mismo tiempo se debe cortar el tiempo total de transferencia (todo dependiendo de la cantidad de datos que va a enviar). La fragmentación de los bytes de imágenes comprimidas también es más fácil en términos de lógica en comparación con hacer mosaicos en las imágenes reales.

Resumido: System.Windows.Media.Imaging debería ayudar tanto a la CPU como al ancho de banda en comparación con su código actual. En cuanto a la memoria, yo diría que es lo mismo.

0
  1. Su solución se ve bien para mí, pero sugiero (como otros lo hicieron) utilizar azulejos y comprimir el tráfico cuando sea posible. Además, creo que debería enviar toda la imagen de vez en cuando, solo para asegurarse de que los deltas del cliente tengan una "base" común.

  2. Tal vez se puede utilizar una solución existente para la transmisión, tales como RTP-H263 para la transmisión de vídeo. Funciona de maravilla, usa compresión y está bien documentado y ampliamente utilizado. A continuación, puede omitir la parte WCF e ir directamente a la parte de transmisión (ya sea a través de TCP o sobre UDP). Si su solución debe pasar a producción, tal vez el enfoque de transmisión H263 sería mejor en términos de capacidad de respuesta y uso de la red.

4

Trabajé en un proyecto similar hace un tiempo. Esta fue mi enfoque general:

  • Rasterized el mapa de bits capturado a los azulejos de 32x32
  • Para determinar qué azulejos había cambiado entre bastidores que utilicé código no seguro para compararlas 64-bits a la vez
  • En el set de azulejos Delta I aplicada a uno de los filtros PNG para mejorar la compresibilidad y tuvo los mejores resultados con el Paeth filter
  • Usado DeflateStream para comprimir los deltas filtrados
  • Usado BinaryMessageEncoding enlace personalizado al servicio para transmitir la datos en Binario en lugar de la versión codificada en Base64 predeterminada

Algunas consideraciones del lado del cliente. Cuando se trata de grandes cantidades de datos que se transfieren a través de un servicio WCF, encontré que algunos parámetros del HttpTransportBinding y el XmlDictionaryRenderQuotas se configuraron en valores bastante conservadores. Entonces querrás aumentarlos.

+0

"En el conjunto de mosaicos delta apliqué uno de los filtros PNG para mejorar la capacidad de compresión y obtuve los mejores resultados con el filtro Paeth" Me preguntaba cómo se lograría esto en C#, ya que no veo dónde aplicar filtro, y EncoderParameters no acepta nada del tipo ¿Algún consejo? –

0
Bitmap scrImg = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height); 
Graphics scr; 
scr.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.PrimaryScreen.Bounds.Size); 
testPictureBox.Image = (Image)scrImg; 

Utilizo este código para capturar mi pantalla.

Cuestiones relacionadas