Protocolo CGI NetWave para cámaras IP
Muchas de las cámaras IP de red que podemos encontrar en el mercado utilizan el protocolo NetWave, basado en comandos CGI, para obtener las imágenes de vídeo a través de un servidor http interno. En este artículo vamos a ver cómo construir una sencilla aplicación para controlar este tipo de cámaras, que se utilizan normalmente para labores de vigilancia y pueden realizar movimientos de barrido además de proporcionar imágenes de vídeo.
Aquí podéis descargar la referencia de comandos CGI NetWave, y en este otro enlace podéis descargar el código fuente del proyecto IPCameraDemo, con el código de ejemplo que utilizaremos, escrito en C# con Visual Studio 2013.
He dividido el proyecto en tres partes, IPCameraDemo es el programa principal, IPCameraInterfaces contiene un interfaz común que utilizaremos para controlar distintos tipos de cámaras IP, y NetWaveCamera es la implementación para cámaras que utilizan el protocolo NetWave.
Básicamente, tenemos tres grupos de comandos:
- Comandos de control: Se utilizan para obtener información sobre la configuración de la cámara, para establecer parámetros como la resolución, el brillo o el contraste y para mover la cámara en el caso de que esté robotizada.
- Comandos de seguridad: Permiten configurar usuarios y sus niveles de acceso.
- Comandos de vídeo: Para obtener imágenes o canales de vídeo desde la cámara.
Las urls para acceder a la cámara se construyen según el siguiente esquema:
<URL de la cámara>/<comando CGI>?<parametro1>=<valor>[;<parametro2>=<valor>]*
Si la cámara utiliza protección de acceso, siempre deberemos proporcionar los parámetros user, con el nombre de usuario, y pwd, con la contraseña, que determinarán si tenemos permiso para ejecutar el comando.
Configuración de la cámara
Para obtener la configuración actual de la cámara, se utiliza el comando get_camera_params.cgi. Este comando devuelve una cadena de texto con los parámetros en formato <nombre>=<valor> separados por un salto de línea. Los parámetros devueltos son los siguientes:
- Resolution: El valor 8 indica una resolución de 320x200 píxels, y 32 una resolución de 640x480.
- Brightness: Devuelve el nivel de brillo de la cámara, es un entero entre 0 y 255.
- Contrast: Es el nivel de contraste, un entero entre 0 y 6.
- Mode: 0 indica 50Hz, 1 es para 60Hz y 2 para modo exteriores.
- Flip: Indica si la imagen se invierte. Se trata de un parámetro formado por bits, donde 0 indica que la imagen no está invertida, 1 indica inversión en horizontal, 2 en vertical y 3 en horizontal y vertical.
El comando opuesto, para modificar los parámetros anteriores, es camera_control.cgi. Debemos pasar los parámetros con el formato param=<código del parámetro>&value=<valor del parámetro>. Los parámetros se codifican del siguiente modo: 0 es para la resolución, 1 para el brillo, 2 para el contraste, 3 para el modo y 5 para invertir la imagen. Por ejemplo, con el siguiente comando modificamos el brillo poniéndolo a su valor máximo: camera_control_cgi?param=1&value=255.
Control del movimiento
Las cámaras robotizadas que permiten el movimiento tienen un aspecto similar al de la imagen:
Estas cámaras permiten el desplazamiento lateral a derecha e izquierda y horizontal de arriba hacia abajo, además de poderse realizar un barrido por toda el área de visión a través de un único comando.
Para realizar un movimiento se utiliza el comando decoder_control.cgi, con el parámetro command que indica la dirección del movimiento codificado como un valor entero, en la forma decoder_control.cgi?command=<valor>. Los valores más comunes son los siguientes:
- 0 y 1: Iniciar o parar el movimiento hacia arriba.
- 2 y 3: Iniciar o parar el movimiento hacia abajo.
- 4 y 5: Iniciar o parar el movimiento hacia la izquierda.
- 6 y 7: Iniciar o parar el movimiento hacia la derecha.
- 25: Llevar la cámara a la posición de reposo en el centro.
- 26 y 27: Iniciar o parar el barrido vertical.
- 28 y 29: Iniciar o parar el barrido horizontal.
Obtención de imágenes desde la cámara
El comando que se utiliza para obtener una fotografía, en formato jpg, es snapshot.cgi, al que podemos pasar como parámetro el nombre del archivo a descargar con el parámetro next_url.
Estos son los comandos que utilizaremos en el proyecto de ejemplo que acompaña a este artículo. Existen otros muchos comandos con los que podéis experimentar siguiendo el manual de referencia.
Aplicación IPcameraDemo
Vamos a hacer un pequeño resumen del funcionamiento del programa de ejemplo que acompaña al artículo, en concreto de la librería NetWaveCamera que es la que se encarga del control de la cámara. En primer lugar, examinaremos el interface IIPCamera, de la librería IPCameraInterfaces, que contiene las propiedades y funciones que llamaremos desde el programa principal:
public interface IIPCamera
{
int Width { get; set; }
int Height { get; set; }
int Fps { get; set; }
bool NeedsAuthentication { get; set; }
void ShowAuthenticationDialog();
void ShowCameraConfiguration();
void Start();
void Close();
event Action<Bitmap> OnNewImage;
void ReleaseEvents();
}
Las propiedades Width y Height permiten cambiar la resolución de las imágenes. En este caso solo se admiten las resoluciones de 320x200 y 640x480. Con Fps podemos controlar la velocidad de muestreo de la cámara.
NeedsAuthentication indicará si debemos proporcionar datos de usuario y contraseña para acceder a la cámara, y ShowAuthenticationDialog mostrará un cuadro de diálogo para introducir dichos datos. Este cuadro de diálogo está implementado en el formulario AuthenticationParameters del proyecto NetWaveCamera.
ShowcameraConfiguration presenta un cuadro de diálogo no modal con los diferentes controles de la cámara, brillo, contraste y movimiento, implementado en el formulario CameraConfiguration del proyecto NetWaveCamera.
Para iniciar y detener la captura, se utilizan los métodos Start y Close, la lectura de las imágenes se realizará en una tarea aparte de manera asíncrona, y las imágenes se pasarán al programa llamador en forma de Bitmap mediante el evento OnNewImage. Podemos liberar el evento utilizando el método ReleaseEvents.
Para enviar un comando de control, construiremos la url del comando y utilizaremos la clase HttpWebRequest para enviarlo a la cámara mediante el método GetResponse:
string url = _strUrl + (_strUrl.EndsWith("/") ? "" : "/") +
"camera_control.cgi?param=0&value=" +
_nResolution.ToString();
HttpWebRequest wrq = (HttpWebRequest)HttpWebRequest.Create(url);
wrq.Method = "GET";
wrq.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(enc.GetBytes(userpwd)));
WebResponse wr = wrq.GetResponse();
Añadimos a la llamada una cabecera Authorization para el control de acceso, de manera que no sea necesario pasar el nombre de usuario y la contraseña en los parámetros de la url.
Para leer una imagen, seguiremos el mismo procedimiento, obteniendo un Stream con los datos de la imagen:
string url = _strUrl + (_strUrl.EndsWith("/") ? "" : "/") +
"snapshot.cgi?user=" + _strName + "&pwd=" + _strPassword;
HttpWebRequest wrq = (HttpWebRequest)HttpWebRequest.Create(url);
wrq.Method = "GET";
wrq.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(enc.GetBytes(userpwd)));
WebResponse wr = wrq.GetResponse();
Stream receiveStream = wr.GetResponseStream();
if (OnNewImage != null)
{
OnNewImage(new Bitmap(receiveStream));
}
En este caso, hemos pasado el nombre de usuario y la contraseña como parámetros de la url, además de añadir el encabezado de autorización.
Como podéis ver, resulta muy sencillo realizar un programa para controlar este tipo de cámaras desde cualquier dispositivo. Ahora os toca a vosotros inventaros maneras de sacarle partido en vuestras aplicaciones.