6.- APLICACIÓN DESARROLLADA A lo largo de este capítulo se procederá a realizar la descripción de la aplicación desarrollada. El objetivo principal es desarrollar un programa que se ejecute en el cliente, intentando adaptarlo lo máximo posible al servidor de vídeo ya existente, para facilitar el trabajo. La explicación se ha estructurado en varias partes: una primera parte en la que se va a explicar la estructura con la que se diseñó el servidor de video (necesario para entender el funcionamiento del conjunto), una segunda parte donde se va a comentar la programación de la interfaz de usuario del servidor, una tercera parte correspondiente a la realización de la captura de imágenes y finalmente la parte correspondiente al cliente del servidor de video, que establecerá conexiones y mostrará las imágenes por pantalla. 6.1.- Estructura del servidor La programación del servidor de video se ha realizado utilizando el lenguaje de programación Java, y con una estructura orientada a objetos. En el diseño del servidor, se distinguieron cuatro partes fundamentales y claramente diferenciadas entre ellas: • La programación de la interfaz gráfica de usuario (GUI), que se ha realizado utilizando AWT y Swing. • La programación de un servidor web que esté a la escucha de peticiones de conexión, sirva una pagina web desde la que el cliente decida que es lo que quiere hacer, y le proporcione al servidor los datos necesarios para la transmisión. • La programación de la parte correspondiente a la captura, preparación y transmisión de las imágenes video hasta el cliente. • La parte correspondiente a la captura de imágenes en diferentes formatos utilizando la API JIMI, proporcionada por Sun Microsystems. Para nuestro proyecto sólo utilizaremos tres de ellas; la interfaz gráfica, necesaria para poder configurar y poner en marcha el servidor de vídeo; el servidor web, al que haremos las peticiones mediante conexiones HTTP (no mediante un navegador específico, sino realizando conexiones independientes) y la parte correspondiente a la captura de imágenes usando JIMI. Esto es así porque sólo llevaremos a cabo la transmisión de imágenes estáticas (y no de vídeo en tiempo real). Además, será necesario un programa que se ejecutará en el cliente, que establecerá dichas conexiones compatibles con este servidor y que mostrará por pantalla las imágenes capturadas y recibidas. A través de la interfaz gráfica de usuario (GUI) se tiene el control del servidor y se muestra una interfaz más amigable para el usuario del servidor que una interfaz de texto. Desde la misma, tendremos que configurar algunos aspectos, como el dispositivo de captura, el directorio de trabajo, etc. Además, podremos poner en marcha o detener el servidor desde estos menús que comentaremos más adelante. Como se ha comentado, el servidor va a estar esperando conexiones desde un navegado Web, para lo se ha tenido que implementar un servidor Web para que esté a la escucha de las distintas peticiones y las atienda sirviéndoles un página Web desde la que el cliente podrá seleccionar la operación que desea realizar (visualización del video o captura de imágenes en distintos formatos). Figura 6-1: Conexiones HTTP cliente / servidor Esta es la idea inicial de este servidor, ya que estaba pensado para funcionar con PC's como clientes; de hecho, podríamos haberlo usado así también y acceder al servidor a través de un navegador web desde nuestro dispositivo portátil compatible con CDC. Sin embargo, hemos optado por variar el planteamiento y establecer conexiones HTTP independientes desde el programa que se ejecutará en el cliente. Cuando el cliente solicita una captura de imagen, en el servidor entra en funcionamiento la parte correspondiente a Jimi, realizando una captura del la imagen de video y procesándola; el cliente establece una conexión HTTP con la URL correspondiente a dicha captura, lee la imagen del flujo HTTP y la almacena en un fichero local que posteriormente se usará por el applet para mostrar la imagen correspondiente por pantalla en nuestro dispositivo móvil. Además, también es posible solicitar la transmisión de una imagen almacenada en el sistema de ficheros del servidor, en cualquier formato de los admitidos. 6.2.- La interfaz gráfica de Usuario (GUI) La interfaz gráfica de usuario ha sido programada utilizando Swing y AWT, y consta de una ventana principal que nos muestra tres menús desplegables desde los que se controla todo el servidor de video. Cuando el servidor está sin iniciar su funcionamiento, se verá esto: Figura 6-2: Servidor antes de su funcionamiento Cuando se inicia el funcionamiento del servidor, aparece una ventana en la que se muestra la imagen de video que se obtiene del dispositivo (en nuestro caso una cámara web) y desde la cual se van a realizar las capturas de las imágenes solicitadas por el cliente. Figura 6-3: Servidor en funcionamiento Los menús desplegables que se encuentran son: • Menú Configuración, en el que se muestra una única opción, que es la de configurar el servidor de video. Se puede seleccionar esta opción con la combinación de teclas Ctrl-C. Figura 6-4: Menú configuración Los parámetros que debemos configurar en el servidor de video, para su correcto funcionamiento, son cinco: o Dispositivo de Video, donde se debe seleccionar el dispositivo de video que vamos a utilizar para capturar el video que queremos transmitir. o Puerto, donde se debe indicar el número de puerto por el que va a estar escuchando el servidor Web las peticiones de conexión. o Formato de Video, donde se debe indicar el formato en el que queremos que esté el video que vamos a transmitir. Los formatos permitidos, así como sus características se encuentran en la tabla x. o Directorio de trabajo, donde se debe indicar el directorio de trabajo del servidor. En este directorio deberá estar la página Web que se va a servir a los clientes, así como el applet que se ejecutará en el cliente cuando se solicite la transmisión de video. o Nombre del archivo que contiene la página Web que se va a mostrar a los clientes. Formato Tipo de Contenido Calidad de Imagen Requisitos de CPU Ancho de Banda Necesario RGB AVI Alta Medio Alto Medio Medio Bajo Alto Alto Alto QuickTime RTP AVI H.263 QuickTime RTP AVI JPEG QuickTime RTP Tabla 6-1: Formatos de imágenes La figura siguiente nos muestra una captura del diálogo de configuración del servidor. Figura 6-5: Configuración del servidor • Menú Acciones, en el que se muestran tres posibles acciones, Iniciar, Detener y Salir. Figura 6-6: Menú acciones o Iniciar, que si se pincha sobre ella, el servidor comenzará a escuchar las peticiones de conexión, así como a mostrar la señal de video que se transmitirá. Si el servidor de video no fue configurado con anterioridad, al seleccionar esta acción, se mostrará un mensaje de error, indicando que primero se debe configurar el servidor. Se puede seleccionar esta opción con la combinación de teclas Ctrl-I. Figura 6-7: Error por no configuración o Detener, con la que se detiene el funcionamiento del servidor. Se puede seleccionar esta opción con la combinación de teclas Ctrl-D. o Salir, con la que se cierra la aplicación. Se puede seleccionar esta opción con la combinación de teclas Ctrl-S. • Menú Ayuda, en el que se encuentra la información del servidor en dos posibles acciones. Figura 6-8: Menú ayuda o Ayuda Servidor, con la que se muestra un pequeño diálogo con información básica del funcionamiento del servidor de video. Se puede seleccionar esta opción con la combinación de teclas Ctrl-A. Figura 6-9: Ayuda del servidor de video o Sobre..., con la que se muestra un diálogo con los créditos del servidor de video (desarrollado por un compañero en un PFC anterior): Figura 6-10: Créditos 6.3.- Descripción del servido Web utilizado El servidor para captura de imágenes y video, va a constar de un servidor Web estándar que acepte peticiones de los clientes desde un navegador Web. El servidor Web se ha implementado de forma que cumpla el protocolo HTTP, aunque se le ha añadido una funcionalidad más para aceptar las peticiones de captura de imágenes por parte de los clientes. El servidor Web implementado, estará ejecutando en un hilo distinto al principal e irá atendiendo las peticiones de los distintos clientes. No se considera importante ahondar en la implementación del mismo; simplemente comentar que el programa cliente realizará peticiones con el formato adecuado (que se explica en el siguiente apartado) que establecerán conexiones con el servidor Web para "leer" las imágenes del flujo HTTP. Figura 6-11: Servidor Web utilizado 6.4.- Captura y tratamiento de imágenes 6.4.1.- El formato de las peticiones Como ya se ha comentado en puntos anteriores, las peticiones de captura de imágenes se realizan mediante peticiones que atiende el servidor Web que se ha implementado. En concreto estableceremos conexiones HTTP con el mismo, por lo que es necesario establecer un formato específico para dichas peticiones y hacer posible la comunicación cliente / servidor. Dentro de la petición se pueden indicar los parámetros de la imágen con los que se quiere que se realice la captura. Los parámetros que se pueden modificar son: • Formato de la imagen. Se le puede indicar el formato en el que se desea que se desea que se reciba la imagen. Según el formato pedido, se tendrán parámetros adicionales que se permiten modificar. Los formatos que se permiten son: o JPEG, en el que se puede configurar la calidad que se desea que tenga la imagen. o PNG, en el que se puede configurar el nivel de compresión que se desea que tenga la imagen. o BMP, que no tiene ningún otro parámetro configurable. • Escala de grises. Se le puede indicar al servidor si se desea una captura de imagen en escala de grises. • Tamaño de la imagen. Para que el servidor Web implementado acepte estas peticiones, deben tener un formato determinado. La petición de la captura de la imagen debe comenzar por un signo de interrogación cerrado y a continuación se deben indicar los parámetros que se quieren modificar en la captura de la imagen separados por signos de interrogación cerrados. http://direcciónIP:puerto/?parámetro1?parámetro2?...? Los parámetros que se quieren modificar tienen que indicarse con el siguiente formato: • Formato de la imagen. Para indicar el formato de la captura de la imagen se debe utilizar el comando “format=” seguido del valor que se desee, siendo los valores posibles: o “jpg” o “jpeg” para indicar que se desea una imagen en formato JPEG. o “png” para indicar que se desea una imagen en formato PNG. o “bmp”para indicar que se desea una imagen en formato BMP. • Escala de grises. Para indicar que se desea una imagen en escala de grises se debe utilizar el comando “gray”. • Tamaño de la imagen. Para indicar que se desea cambiar el tamaño de la imagen pedida, se debe utilizar el comando “resize=” seguido de los valores nuevos valores para el tamaño de la imagen separados por un asterisco. • Calidad de la imagen en formato JPEG. Para indicar la calidad se debe utilizar el comando “quality=” mas un valor entre cero y cien. Si no se especifica, tendrá un valor por defecto de cincuenta. • Nivel de compresión de la imagen en formato PNG. Para indicar el nivel de compresión se debe utilizar el comando “comp=” seguido del valor de compresión. Tendremos tres valores posibles: o “none” que indica que no se desea compresión. o “max” que indica que se desea el nivel de compresión máximo. o “def” que indicar que se use el nivel de compresión por defecto. Si no se indica este parámetro no se comprimirá la imagen. Unos ejemplos de peticiones pueden ser: • ?format=jpeg?, que proporciona una imagen en formato JPEG y con los valores con los que se realizó la captura. • ?format=png?resize=800*600?comp=none?, que proporciona una imagen en formato PNG con un tamaño de 800x600 y sin compresión. • ?format=bmp?resize=220*260?gray?, que proporciona una imagen en formato BMP con un tamaño de 220x260 y en escala de grises. 6.5.- El cliente de vídeo Se trata de la aplicación propiamente dicha de este Proyecto Fin de Carrera. Está programado usando el Personal Profile de la configuración CDC de la plataforma de Java J2ME. Consta de dos partes principales: el programa que establece la conexión con el servidor y recibe la imagen solicitada, y la applet que muestra la imagen capturada por pantalla. A continuación comentaremos ambos subprogramas por separado. 6.5.1.- La Clase Client Es la clase que incluye el método principal main(String argc[]), que comprueba que la petición proporcionada por la línea de comandos es correcta, y si es así llama primero al método que establecen la URL adecuada a la que pedir la imagen, y luego al que establece la conexión con dicha dirección y lee la imagen del flujo HTTP, almacenándola en un fichero local. Clase Client package pfc; import java.io.*; public class Client{ public static void main(String args[]) { Connection con = new Connection(); Petition pet = new Petition(); String url = null; String param[] = null; String format = null; // Comprobamos que la ejecución por línea de comandos es correcta; // si no es así, se indica la forma correcta. if(args.length!=5){ System.out.println("Forma de uso: ./cvm pfc/Client 1_IP 2_fichero(o_'cam') 3_formato_img('jpeg','png','bmp') 4_gris(si/no) 5_resolucion(x*y/no)"); //Si es correcta, creamos la URL y establecemos la conexión }else{ format = args[2]; param = args; url=pet.createURL(param); System.out.println("Estableciendo conexion con el servidor para obtener imagen de formato ."+format); System.out.println(url); con.stablishConnection(url); } } } Los atributos de esta clase son los siguientes: • con, que es un objeto de la clase Connection, para llamar al método que establecerá la conexión HTTP. • pet, que es un objeto de la clase Petition, para llamar al método que creará la URL adecuada según la petición obtenida por la línea de comandos. • url, que es un objeto de la clase String para almacenar la URL devuelta por el método anterior. • param, que es un array de objetos de la clase String, que será una copia de los parámetros obtenidos por la línea de comandos para poder manejarlos para crear la URL. Atributos de la clase Client Connection con = new Connection(); Petition pet = new Petition(); String url = null; String param[] = null; El formato de la ejecución por la línea de comandos, por tanto, es el siguiente: ./cvm pfc/Client 1_IP 2_fichero(o_'cam') 3_formato_img('jpeg','png','bmp') 4_gris(si/no) 5_resolucion(x*y/no) donde cada uno de los parámetros representa lo siguiente: • ./cvm pfc/Client -> es la ejecución bajo la C virtual machine de la clase Client dentro del paquete pfc. • 1_IP -> dirección IP del servidor Web al que vamos a solicitar las imágenes. • 2_fichero(o_'cam') -> nombre del archivo del sistema de ficheros del servidor que queremos obtener, o bien la palabra cam si lo que deseamos es una captura del dispositivo de vídeo conectado al mismo (en nuestro caso, una web cam). • 3_formato_img('jpeg','png','bmp') -> formato deseado de la imagen que estamos solicitando, a elegir entre los 3 que se indican. • 4_gris(si/no) -> para decir si queremos que la imagen obtenida sea en escala de grises o no. • 5_resolución(x*y/no) -> indicar una resolución determinada (800*600, por ejemplo) si queremos un redimensionado de la imagen, o la palabra no si queremos el tamaño predeterminado. Una vez comprobado que el número de parámetros es el correcto, se pasa al procesado de la URL correspondiente en función de dichos elementos y a establecer la conexión. 6.5.2.- La Clase Petition Se trata de la clase que, en función de los parámetros proporcionados por el usuario por la línea de comandos, crea y devuelve una URL con el formato adecuado para que sea interpretada por el servido Web que usaremos. Los parámetros de esta clase son los siguientes: • IP, una cadena de caracteres donde se almacena la IP del servidor y que es proporcionada por la línea de comandos. • name, otra cadena que guarda el nombre del archivo, en caso de que no queramos una captura de la web cam. • format, un tercer String que almacenará el formato de la imagen solicitada. • http, una cadena de caracteres auxiliar para crear la URL final. • gray, un objeto de la clase String que almacenará la petición del usuario respecto a la obtención de la imagen en escala de grises o no. • size, una cadena para almacenar el valor de las dimensiones de la imagen solicitada por el usuario. • size_aux, un nuevo String auxiliar para conformar la URL. • url, cadena de caracteres que almacenará la dirección completa a la que debemos conectarnos. Atributos de la clase petition String String String String String String String String IP = null; name = null; format = null; http = "http://"; gray = null; size = null; size_aux = "resize="; url = null; 6.5.2.1.- El método createURL(String param[]) Es un método que recibe un array de cadenas de caracteres que no es más que una copia del array que obtiene el método main por la línea de caracteres. Lee cada uno de dichas cadenas y en función de sus valores establece la URL con el formato adecuado a las peticiones que admite nuestro servidor Web. Método createURL(String param[]) public String createURL(String param[]){ IP = param[0]; name = param[1]; format = param[2]; gray = param[3]; size = param[4]; if (name.compareTo("cam")==0){ name="?format="; format=format.concat("?"); if (gray.compareTo("si")==0){ gray="gray?"; }else{ gray=""; } if (size.compareTo("no")!=0){ size=size_aux.concat(size.concat("?")); }else{ size=""; } } else{ name=name.concat("."); gray=""; size=""; } url = http.concat(IP).concat("/").concat(name).concat(format) / .concat(gray).concat(size); return(url); } 6.5.3.- La Clase Connection Es la clase que va a establecer la conexión con el servidor Web, va a leer la imagen solicitada del flujo HTTP y la almacenará en un fichero para su posterior visionado mediante una applet. Los atributos de esta clase son los siguientes: • hc, un objeto de la clase HttpConnection, que será el encargado de abrir la conexión con la URL determinada. • in, objeto de la clase DataInputStream, para leer los bytes que contienen la información de la imagen del flujo HTTP. • fos, objeto de la clase FileOutputStream, para almacenar dichos bytes en un fichero. • file, una cadena de caracteres que almacena el nombre del archivo en el que se guardará la imagen. Atributos de la clase Connection HttpConnection hc = null; DataInputStream in = null; FileOutputStream fos=null; String file = new String("prueba"); 6.5.3.1.- El método stablishConnection(String url) Método que recibe como parámetro el String que almacena una determinada url, establece la conexión http con la misma, lee la imagen y la almacena en un fichero. Lo primero que hace este método es abrir la conexión, definir un array de bytes y se obtiene la longitud en bytes de la imagen: Establecimiento de la conexión public void stablishConnection(String url){ try { hc = (HttpConnection)Connector.open(url); int length = (int)hc.getLength(); byte[] data = null; A continuación, lee la imagen byte a byte mediante el objeto de la clase DataInputStream, y almacena dichos bytes en el array data: Lectura de la imagen en bytes if (length != -1) { data = new byte[length]; in = new DataInputStream(hc.openInputStream()); for (int i = 0, n = length; i<n; i++) { data[i] = (byte)in.read(); } } else { // Si no es dada la longitud, leemos los datos a trozos. int chunkSize = 512; int index = 0; int readLength = 0; in = new DataInputStream(hc.openInputStream()); data = new byte[chunkSize]; do { if (data.length < index + chunkSize) { byte[] newData = new byte[index + chunkSize]; System.arraycopy(data, 0, newData, 0, data.length); data = newData; } readLength = in.read(data, index, chunkSize); index += readLength; } while (readLength == chunkSize); length = index; } Se informa por pantalla del éxito de la transmisión y se almacena la imagen en un fichero: Almacenamiento de los datos en un archivo System.out.println("Fin de la transmision"); fos = new FileOutputStream(file); fos.write(data); Finalmente, se cierran los flujos abiertos una vez los dejamos de usar: Cierre de flujos finally { try { if (in != null) in.close(); if (hc != null) hc.close(); if (fos!= null) fos.close(); }catch (IOException ioe) {} } 6.5.4.- La Clase ImageLoader Por ultimo, tenemos la clase que muestra la imagen por pantalla, que no es más que una simple applet que crea la imagen desde el archivo almacenado anteriormente: Los atributos de esta clase: • toolkit, objeto de la clase Toolkit para crear la imagen a partir del archivo correspondiente. • image, objeto de la clase Image que será la imagen creada a partir de los datos obtenidos en la conexión anterior. • tracker, objeto de la clase MediaTracker, para controlar el procesado de cada imagen. Atributos de la clase ImageLoader Toolkit toolkit; Image image; MediaTracker tracker = new MediaTracker(this); 6.5.4.1.- El método start() Método que genera la imagen a mostrar por pantalla, comprueba que su procesado es correcto con ayuda del objeto tracker, y llama a repaint() para poder visionarla. El método start() del applet public void start() { //crea la imagen desde el fichero "prueba" toolkit = Toolkit.getDefaultToolkit(); image = toolkit.getImage("prueba"); // controla la correcta creación de la imagen y manda "pintar" tracker.addImage(image,1); try { tracker.waitForID(1); }catch (InterruptedException ie) { System.out.println(ie); } repaint(); } 6.5.4.2.- El método paint(Graphics g) Método que muestra la imagen creada por pantalla, además de pintar el fondo de la misma de líneas rojas horizontales. El método paint(Graphics g) del applet public void paint(Graphics g) { Dimension d = getSize(); // Dimensiones de la imagen a dibujar int w = image.getWidth(this); int h = image.getHeight(this); //Fondo de líneas rojas horizontales g.setColor(Color.red); for (int i = 2; i < d.height; i += 4) { g.drawLine(0,i,d.width,i); } // Dibuja la imagen g.drawImage(image,0,0,w,h,0,0,w,h,this); } 6.5.4.3.- Archivo HTML necesario para visionar el Applet El applet anterior tiene que estar contenido en un archivo HTML para poder ser visionado, bien mediante un navegador web, bien mediante un AppletViewer. El código HTML de dicho archivo es el siguiente: Archivo HTML que contiene el Applet <html> <head> <meta name="Author" content = "David Alvarez Suarez"> <title>Applet que muestra la captura de la imagen</title> </head> <body> <applet code="ImageLoader.class" width="800" height="300"> </applet> </body> </html>