\chapter{El sistema desarrollado}

Este capítulo explica los pasos a seguir para conseguir obtener un
software cliente-servidor de vídeo progresivo. Los requerimientos
exigidos a la hora de implementarlo han sido:

\begin{enumerate}
\item Que se adapte al ancho de banda de la red.
\item Que el sistema se adapte a la carga de trabajo de la CPU de la máquina cliente donde se ejecuta la aplicación.
\item Que proporcione escalabilidad en calidad.
\end{enumerate}

Las características del algoritmo SPIHT hacen que los anteriores
requisitos puedan llevarse a cabo. Esto es debido a que el sistema no
es dependiente de la cantidad de información recibida en un instante
dado (que depende del ancho de banda disponible). Además, el proceso
de descodificación del algoritmo puede ser abandonado en cualquier
instante lo que hace que el software se adapte a máquinas con distinta
potencia de cálculo.

El sistema por tanto está formado por un software servidor que se
ejecuta en una máquina remota, donde están almacenados los vídeos
comprimidos y un software cliente, que se comunica con el software
servidor, procesa la información recibida y la visualiza en la
pantalla.

La idea general del software cliente se basa en un pipeline de tres
etapas donde el flujo del programa avanza en el tiempo. Las tres
etapas generales son:


\begin{itemize}

\item Etapa 1: Lectura (L) de los datos y descodificación (D). 
\item Etapa 2: Transformación inversa del volumen (T). 
\item Etapa 3: Visualización de las imágenes (V). 

\end{itemize}
siendo su representación gráfica la que se muestra en la Figura \ref{fig:EstructuraCauce}.


\begin{figure}
\begin{center}
\begin{tabular}{|c|c|c|c|c|c|}
\hline
& Tiempo 1 & Tiempo 2 & Tiempo 3 & Tiempo 4 & \\
\hline
Etapa 1 & L/D GOP1 & L/D GOP2 & L/D GOP3 & L/D GOP4 & \ldots\\
\hline
Etapa 2 &  & T GOP1 & T GOP2 & T GOP3 & T GOP4\\
\hline
Etapa 3 &  &  & V GOP1 & V GOP2 & V GOP3\\
\hline
\end{tabular}
\end{center}
\caption[Estructura de las etapas del cauce]{Estructura de las etapas del cauce.}
\label{fig:EstructuraCauce}

\end{figure}

Por lo tanto cuando todas las etapas del pipeline tienen datos se
trabaja con información de tres slots de tiempo distintos, es decir,
se tiene una latencia de dos slots. Al tercer slot de tiempo comienza
la visualización del vídeo en la pantalla.
 
\section{Formato de la información a transmitir}

El formato del vídeo comprimido tiene una serie de características que
vamos a describir en este apartado.
 
Para generar un vídeo progresivo, que el software desarrollado sea
capaz de visualizar, es necesario tener un vídeo en formato RAW (vídeo
que no tiene ningún tipo de compresión, es decir, que todos los
fotogramas del vídeo están representados mediante matrices
bidimensionales de puntos). Con una WebCam o videocámara digital se
puede obtener una secuencia de vídeo con estas características.
 
Una vez que se tiene el vídeo en formato RAW, se comprime con un
PVC\cite{SPIHT:96} que nos genera un fichero con un formato
especial. Este tiene una propiedad muy importante y es que si
descomprimimos solo una parte del code-stream y lo volvemos a
comprimir, generamos exactamente el mismo code-stream. Esto implica
necesariamente que el algoritmo es sin pérdida y que es progresivo.
 
La representación gráfica del formato del fichero comprimido se
muestra en la Figura
\ref{fig:FormatoFichero}.



\begin{figure}
\epsfig{file=figuras/eps/estructura_fichero,width=12.5cm}
\caption[Formato del fichero comprimido con SPIHT-3D]{Formato del fichero comprimido con SPIHT-3D.}
\label{fig:FormatoFichero}
\end{figure}

Como se puede observar en la Figura \ref{fig:FormatoFichero} al
comienzo del fichero tenemos la información relativa al número de
fotogramas o frames, la altura y la anchura de que se compone cada GOP
que se va a ir descomprimiendo en cada intervalo de tiempo.
 
La información que se envía de cada GOP es un conjunto de coeficientes
wavelet que una vez descodificados se utilizarán para reconstruir el
GOP original.

\section{El servidor de vídeo progresivo}

En este apartado nos vamos a centrar en la parte servidora, la que
envía el vídeo por el canal a los clientes que lo soliciten.
 
La comunicación entre el cliente y el servidor se realiza utilizando
sockets de tipo TCP. Este tipo de socket asegura la transmisión sin
errores ya que realiza el control de flujo y errores mediante
retransmisión.
 
El comportamiento es bastante simple. Una vez que el servidor se
lanza, se queda a la escucha de peticiones. Cuando el cliente realiza
la petición de conexión, el servidor da conformidad y recibe entonces
el nombre del fichero de vídeo que tiene que comenzar a transmitir, lo
busca en su árbol de directorios y envía conformidad en caso de
existencia o fallo en caso negativo. Si existe el fichero solicitado y
una vez enviada conformidad, se queda a la escucha de peticiones de
envío de información.

El servidor es por tanto, un programa que hace lo que el cliente le
ordena, es decir, es el cliente quien realmente decide qué es lo que
el servidor le tiene que enviar y cuándo se lo tiene que enviar. Un
ejemplo de interacción entre el servidor y el cliente sería el
mostrado en la Figura
\ref{fig:interaccion}.

\begin{figure}
\begin{center}
\epsfig{file=figuras/eps/interacion, width=12cm}
\end{center}
\caption[Ejemplo de interacción entre el servidor y el cliente]{Un ejemplo de interacción entre el servidor y el cliente.}
\label{fig:interaccion}
\end{figure}

Aunque el vídeo esté comprimido, este tiene un tamaño bastante grande
(la compresión es sin pérdida), por lo que es muy difícil que sea
necesario enviar completamente cada GOP. Ésto sólo ocurre si es
preciso una representación exacta del vídeo original.

Es el cliente el que determina la cantidad de información del GOP que
precisa, dependiendo de los requerimientos a los que esté
sometido. Por lo tanto, el servidor tiene que posicionarse al comienzo
de cada GOP en cada petición. La forma en que se posiciona es bastante
rápida ya una de las partes definidas en el formato del vídeo es el
tamaño de cada GOP. Así, si el cliente nos pide $X$ bits de
información y el tamaño completo es $N$ bits, el servidor tendrá que
avanzar $ N - X$ bits.

\section{El cliente de vídeo progresivo}

Este apartado va a describir el software cliente que se encarga de
recibir la información del canal, procesarla y visualizarla por la
pantalla.

El cliente realiza una serie de operaciones que son necesarias para
poder visualizar el contenido de cada GOP. Estas operaciones están
divididas en cuatro tareas fundamentales, que a grandes rasgos son:

\begin{enumerate} 

\item Lectura del socket: Este proceso lee del canal un code-stream de bytes (GOP codificado en la parte servidora) y lo irá almacenando en memoria para su posterior procesado. 
 
\item Descodificación del code-stream: La descodificación tiene como entrada la información leída del socket que está comprimida y genera como salida un volumen de coeficientes wavelet.
 
\item Transformada inversa (DWT$^{-1}$): Este proceso tiene como entrada un volumen de coeficientes wavelet y genera como salida un volumen de puntos. 
 
\item Visualización del GOP: Se encarga de representar la secuencia de imágenes. 

\end{enumerate}

\subsection{Implementación del cliente de vídeo progresivo}

Este apartado presenta la forma en que se han organizado las tareas,
las estructuras de datos y técnicas de programación utilizadas en la
implementación del cliente de vídeo progresivo. Una vez mostradas se
describe el funcionamiento del software.

%Exponer las estructuras como si fueran begin{itemize} y después de mostrar el esquema 
%gráfico suelto la parrafada

Las estructuras y técnicas más relevantes del cliente son:

\begin{itemize}

\item Organización de las tareas: Las tareas de descodificación y transformación inversa están englobadas en una misma función colocadas una detrás de otra de forma secuencial. El motivo es que son funciones intensivas en cálculo y se
mejora el rendimiento si se mantienen juntas.  Las funciones de
lectura del socket y visualización de la secuencia de imágenes se
realizan en procedimientos independientes. Los procesos de lectura del
socket y visualización no necesitan potencia de cálculo, por lo que la
mayoría del tiempo la CPU estará ocupada realizando los procesos de
descodificación y de transformación inversa.

\item Señales del sistema operativo: Como debemos realizar las tareas anteriormente mencionadas para cada GOP que se recibe, se necesita un mecanismo que nos permita realizar procesos de forma simultánea para información relativa a GOPs distintos, según se vio en la Figura
\ref{fig:EstructuraCauce}. Para ejecutar estos procesos de forma simultánea se utilizan las señales del sistema operativo. En nuestro sistema se utiliza una señal que ejecuta una interrupción un número determinado de veces. La cantidad de interrupciones es función del número de fotogramas del GOP. Es decir, que si nuestro GOP está formado por 16 fotogramas, la interrupción se ejecuta 16 veces durante el procesamiento completo del GOP. En cada ejecución de la interrupción se realiza la visualización de un fotograma del GOP y se lee del
socket la información que hubiera.

\item Tipo de socket: El sistema utiliza para las lecturas del canal socket de tipo ``no bloqueantes''. Su principal característica es que si al hacer una
lectura sobre ellos no existen datos se retorna al punto de llamada
sin ningún tipo de demora.

\item Buffer de lectura: Como la tasa de bits disponible en la red no es constante a lo largo del tiempo sino que puede sufrir ciertos cambios, es posible que en un momento dado no se reciba información para procesar, o que la
información llegue demasiado tarde para que sea útil en la
ejecución. Para evitar estos problemas, la información leída del
socket se almacena en un buffer de lectura de $M$ posiciones. Cada
posición del buffer alberga información de GOP de tiempos
distintos. De esta forma se protege el sistema de los posibles
altibajos de la red. Se aprovecha mejor el canal de comunicación
debido a que no es necesario descartar información que pudiese llegar
fuera de tiempo. Esta mejor utilización del canal, repercute
positivamente en la calidad de visualización. Además de esta forma se
consigue que la lectura del socket sea un proceso totalmente
independiente del proceso de cálculo (descodificación y transformación
inversa).

\item Vectores de control de información: Para saber en todo momento la cantidad de información que vamos a obtener de la red y a qué posición del buffer corresponde se tienen unos vectores que se utilizan para esta función. De esta forma cuando se realiza una lectura del socket se conoce la posición del buffer de lectura a la que pertenece la información leída.

\item Buffer de proceso: A la hora de procesar la información es necesario que la salida de un proceso sea la entrada del siguiente y así sucesivamente. Por lo tanto, para poder trabajar con slots de tiempo distinto en las
funciones de descodificación, transformación inversa y visualización,
evitando escrituras sobre información útil, se utilizan buffers con
un determinado tamaño que nos permitan leer y escribir en los distintos
procesos sin necesidad de hacer copia de la información procesada.

\end{itemize}


\begin{figure}
\begin{center}
\epsfig{file=figuras/eps/tercera_imple, width=12cm}
\end{center}
\caption[Representación gráfica del cliente de vídeo progresivo]{Representación gráfica del cliente de vídeo progresivo.}
\label{fig:ImplementacionNatural}
\end{figure}


%Aquí empieza a explicar la figura del cliente.
La Figura \ref{fig:ImplementacionNatural} muestra la representación
gráfica de la implementación del cliente de vídeo progresivo. La
implementación se comporta de la siguiente forma: Una vez que el
servidor está en ejecución, los clientes pueden conectarse al
mismo. Para ello se negocia la aceptación de la conexión y la
existencia del fichero de vídeo solicitado. Una vez comprobado que el
fichero existe, el servidor envía al cliente los parámetros del
fichero. Estos parámetros se utilizan para crear las estructuras de
memoria necesarias para el buffer de lectura, los buffers de proceso,
etc.

Una vez reservado espacio en memoria, se llena el buffer de lectura
con $M$ GOPs de información. Al disponer del buffer de lectura, el
sistema puede estar $M$ slots de tiempo sin recibir información del
socket que no se presenta ningún problema en la visualización. 

Se inicializa la interrupción y a partir de este momento cada cierto
tiempo se ejecuta un procedimiento en el cual se visualiza un
fotograma del GOP que se está representando, y se sondea el socket
para comprobar si existen datos que almacenar en el buffer de
lectura. Este proceso es inmediato y no consume tiempo de CPU. La
mayor parte del tiempo de CPU se están realizando los procesos de
descodificación y de transformación inversa.

Cuando se alcanza la última interrupción se realiza la petición de
información de un nuevo GOP al servidor y se desplazan las estructuras
de memoria para repetir el proceso completo.

Este proceso se repite hasta alcanzar el último GOP de la secuencia de
vídeo pedida. Una vez terminada la visualización, se liberan todos los
recursos reservados y se cierra la conexión con el servidor.

El sistema presenta un problema y es que, la función de
descodificación y transformación inversa se realiza sobre el total de
información recibida de la red, es decir, que si en un instante dado
se tienen $Y$ bytes de información sobre un GOP, se procesan $Y$
bytes. Hasta que no se termina de procesar completamente dicha
cantidad de datos no se pasa el resultado a la fase de
visualización. En un caso normal, este tiempo de procesamiento es
inferior a lo que se tarda en representar un volumen, pero existen
casos donde esto no se cumple.

Por ejemplo, si nuestra CPU no es muy potente y se procesa mucha
información en un cierto tiempo, es posible que no termine dicho
proceso antes de que la fase de visualización represente el GOP
completo. Otro caso ocurre cuando la CPU está procesando una cantidad
de información de forma estable y un programa externo comienza a
ejecutarse. La CPU tiene que atender a varios programas que se están
ejecutando en paralelo y entonces su capacidad de procesamiento en
nuestra aplicación deja de ser suficiente para procesar el volumen de
información.
 
Estudiando el comportamiento de los algoritmos de descodificación y
transformación inversa deducimos que:

\begin{itemize}
 
\item El algoritmo que realiza la transforma inversa wavelet  es un proceso que no se puede abandonar\footnote{Abandonar una proceso significa que termina y retorna de forma inmediata al punto de llamada.} sin que se haya procesado completamente todo el volumen.  
 
\item El algoritmo que realiza la descodificación usando SPIHT-3D es un proceso que sí se puede abandonar en cualquier momento. Esto afecta a la cantidad de información que le haya dado tiempo a descodificar. En cualquier caso, el volumen obtenido es válido para que se le realice la transformada inversa. 
 
\end{itemize}

Partiendo de estas premisas y para solucionar el problema
anteriormente mencionado, modificamos la fase de procesamiento
invirtiendo el orden de las funciones de descodificación y
transformación inversa. Sabiendo que la función de transformación
inversa tiene un tiempo de proceso constante y la función de
descodificación tiene un tiempo de proceso que varía en función de la
cantidad de información que se tenga que descodificar, haremos que
primero se procese la transformación inversa de forma completa y
posteriormente la descodificación con el tiempo restante. Cuando el
tiempo disponible para realizar ambas funciones esté a punto de
finalizar, abandonaremos la función de descodificación.

La Figura \ref{fig:sistema_actual} muestra la secuencia de operaciones
con las funciones de descodificación y transformada sin invertir,
mientras que la Figura \ref{fig:sistema_invertido} muestra la
secuencia de operaciones con las funciones de descodificación y
transformada invertidas.

\begin{figure}
\begin{center}
\epsfig{file=figuras/eps/sistema_actual, width=9cm}
\end{center}
\caption[Secuencia de operaciones con las funciones sin invertir]{Secuencia de operaciones con las funciones sin invertir.}
\label{fig:sistema_actual}
\end{figure}

\begin{figure}
\begin{center}
\epsfig{file=figuras/eps/sistema_invertido, width=9cm}
\end{center}
\caption[Secuencia de operaciones con las funciones invertidas]{Secuencia de operaciones con las funciones invertidas.}
\label{fig:sistema_invertido}
\end{figure}

Podemos observar que, con esta implementación se tiene una latencia de
tres slots de tiempo desde que se comienza a recibir información
del primer GOP hasta que se visualiza. Este es el único efecto
negativo de este diseño.
 
A la función de descodificación tenemos que indicarle que en un
momento dado abandone lo que está procesando y termine. El tiempo
necesario para abandonar la función es corto, pero no es
inmediato. Esto es debido a la estructura del algoritmo y por lo tanto
debemos tenerlo en cuenta.
 
El lugar idóneo para indicar a la función de descodificación que debe
abandonar el proceso es dentro de la función de visualización ya que
sabemos en qué fotograma del GOP nos encontramos y podemos informar en
el momento adecuado. Para informar a la función de descodificación que
termine, se utiliza un flag booleano. Este flag se activa a un
determinado número de interrupciones antes del final del procesamiento
del GOP. Dicho valor debe ser cuidadosamente seleccionado. Si es
grande le indicamos a la función que abandone muy pronto, con lo que
se descodifica poca información perdiendo calidad en la imagen. Si es
pequeño entonces es posible que la función no abandone el proceso a
tiempo. Debemos encontrar un punto de equilibrio. Un valor idóneo para
la CPU donde se han realizado las simulaciones es 2. Un ejemplo de
cómo se comporta el nuevo modelo se puede ver en la Figura
\ref{fig:CauceEtapasInvertidas}. Se puede observar que en los GOPs número 1, 3 y 4 la fase de descodificación termina a tiempo mientras que en el GOP número 2 la fase de descodificación debe abandonar de forma forzada su proceso. Cada vez que se ejecuta una interrupción se visualiza un fotograma y se lee del socket.

\begin{figure}
\begin{center}
\epsfig{file=figuras/eps/cauce_etapas_invertidas, width=12cm}
\end{center}
\caption[Representación del cauce de ejecución del cliente de vídeo progresivo con las funciones invertidas]{Representación del cauce de ejecución del cliente de vídeo progresivo con las funciones invertidas. La potencia de la CPU se considera constante.}
\label{fig:CauceEtapasInvertidas}
\end{figure}

\begin{figure}
\begin{center}
\epsfig{file=figuras/eps/quinta_imple, width=12cm}
\end{center}
\caption[Representación gráfica del cliente de vídeo progresivo con las funciones invertidas]{Representación gráfica del cliente de vídeo progresivo con las funciones invertidas.}
\label{fig:quinta_imple}
\end{figure}
 
Con este cambio en la ejecución, el cliente ha mejorado su
comportamiento ante factores internos o externos que afecten a la
capacidad de procesamiento de la CPU. La representación gráfica de la
implementación del cliente de vídeo progresivo con las funciones
invertidas es la mostrada en la Figura
\ref{fig:quinta_imple}.

 
\subsection{Mejora a la implementación} 
 
Para conseguir que el software se adapte a la tasa de transferencia de
la red y a la capacidad de procesamiento de la CPU es necesario
conocer cuáles son las cotas superiores de dichas medidas. Una vez
obtenidas, se realiza una petición al servidor tomando como referencia
esa información.

Para saber en cada momento la tasa de transmisión existente se realiza un
control sobre la cantidad de datos solicitada al servidor y el tiempo
que se tarda en recibirse. Cuando el tiempo que se tarda en recibir la
información solicitada es inferior al tiempo disponible para procesar
y visualizar el GOP, nos indica que la tasa de transmisión ha aumentado. En
caso contrario, es decir, si aun nos queda tiempo de procesamiento,
significa que la tasa de transmisión ha disminuido.


Para calcular la tasa de procesamiento de la CPU nos basamos en la
cantidad de información que la función de descodificación es capaz de
procesar obteniendo así una buena aproximación. Se modifica dicha
función para conocer en cada momento el número de bytes que se
procesan. En base a esa información se hace una estimación de lo que
realmente se puede procesar.
 
Al realizar la petición al servidor hay que tener en cuenta las nuevas
tasas de transmisión y de CPU calculadas para cada GOP del vídeo. Ambas tasas
están relacionadas de forma indirecta (el valor que se pide al
servidor es el mínimo de estos dos valores). Esto es una deducción
lógica ya que lo que indican las tasas de transmisión y de CPU es la cota
superior (de lectura y procesamiento) aproximada para el sistema.
 
Como ejemplo demostrativo, supondremos que la transmisión de vídeo se
realiza sobre una red local. En este escenario la tasa de transmisión que se
puede obtener es muy superior a la tasa de CPU. Así, el límite
superior lo impone la CPU por lo que se lee de la red sólo la cantidad
de información que la CPU pueda procesar.
 
El escenario contrario, es la transmisión de vídeo usando un modem de
56 kbps, donde la tasa de transmisión es pequeña en comparación con la tasa de
CPU. En este caso el valor máximo de información a pedir lo indica la
tasa de transmisión.
 
Por último, incluso controlando la tasa de CPU y de transmisión, nunca estamos
seguros de que toda la fase de procesamiento (transformada inversa y
descodificación) acaben a tiempo para el siguiente GOP. En este caso
se produce una espera en la reproducción lo más pequeña posible para
que la sincronización permanezca. Esto es posible porque es el cliente
quién controla la transmisión de los datos.
 
\section{Descripción de la interfaz de la aplicación}
 
Se ha diseñado la interfaz usando las APIs de X Window que reciben el
nombre de Xlib. Estas bibliotecas ofrecen una libertad total para
diseñar cualquier tipo de interfaz pero tienen el inconveniente, de
que hay que programar a muy bajo nivel. Los entornos X Window de UNIX
están basados en estas bibliotecas. Otra ventaja es que aumenta la
portabilidad de la aplicación.
 
 
\subsection{Introducción a X Window}
 
X Window es un sistema de ventanas desarrollado a mediados de los 80 y
que se ha convertido en el estándar de los sistemas UNIX y, en
particular, de Linux.
 
X Window es un sistema cliente/servidor, donde el servidor reside en
cualquiera de los ordenadores de la red, y el cliente puede ser el
mismo host que el servidor o cualquier otro host(u hosts) de la
red. 

El cliente se dedica a hacer peticiones al servidor, que es quien
maneja el display o los dispositivos de entrada. Estos programas
clientes son:

\begin{itemize}
\item Aplicaciones: Son los programas en sí mismos: procesadores de textos, hojas de cálculo, juegos, etc. Existen múltiples maneras de generar las aplicaciones (Gtk, Xlib, Imlib, etc), pero nosotros utilizaremos las llamadas a Xlib. 
\item Windows Managers: Son los gestores de ventanas, programas cliente con algunos privilegios extra que se desarrollan para que gestionar el interfaz o GUI comunicándose con el servidor X y que es el encargado de la forma en que se presentan las ventanas en pantalla, de dar facilidades con menús de configuración del sistema, barras de tareas, etc.  
\end{itemize} 

En cambio, el servidor X es el programa que proporciona las primitivas
a las aplicaciones, con las que se comunica por medio de sockets. Los
programas son ejecutados en la máquina servidora y se visualizan en la
máquina cliente. El servidor controla el display ejecutando las
peticiones de los clientes, generando los eventos y notificándoselos a
las aplicaciones.
 
 
\subsection{Diseño de la interfaz}
 
Tomando como ``referencia'' una conocida aplicación de Windows
realizamos una interfaz adaptada a nuestro fin. Para ello diseñamos
una serie de imágenes JPEG con las partes de la interfaz. En conjunto
es parecido a las aplicaciones que usan skins (pieles) para cambiar su
aspecto gráfico.
 
Una vez obtenidas las imágenes que forman parte de nuestra interfaz,
se integra de forma independiente en un programa aparte y le damos
funcionalidad a las opciones.

La imagen de la Figura \ref{fig:interfaz} es la base de nuestra
interfaz.

\begin{figure}[ht]
\centering
\epsfig{file=figuras/eps/interfaz, height=13cm}
\caption[Imagen principal de la interfaz]
{Imagen principal de la interfaz.}
\label{fig:interfaz}
\end{figure}
 
Para el diseño de las imágenes se ha usado un programa de diseño
gráfico para la plataforma Windows. El software en cuestión es ``Adobe
Photoshop 5.0''. Este programa nos permite realizar cualquier tipo de
tratamiento sobre una imagen. En nuestro caso nos hizo falta rediseñar
parte de la interfaz, limpiarla, redondear los bordes, etc.

\subsection{Funcionamiento de la interfaz} 
 
En este apartado vamos a explicar el funcionamiento de la interfaz y
la información que nos ofrece como usuario de la misma.
 
La interfaz está dividida en 4 partes diferenciadas: 

\begin{enumerate} 

\item Zona de información: En esta zona se muestra información relativa a la conexión (máquina y puerto a donde estamos conectados), el nombre del vídeo que se está reproduciendo, la máxima cantidad de información a la que podemos reproducir y la cantidad de información del siguiente GOP que vamos a procesar.
 
\item Buffer lectura: Esta situado a la derecha de la interfaz y está formado por unas barras de colores, el color indica el estado de cada posición del buffer.
 
\begin{enumerate}
\item Si el color es verde, indica que esa posición tiene información para ser procesada cuando sea su turno. 
\item Si el color es gris indica que esa posición del buffer está vacía o se está llenando, pero que aún no está completa.  
\item Existen dos colores más (azul y rojo), estos colores sólo se encienden en la posición más alta del buffer. Si la cantidad de información pedida al servidor en el slot de tiempo anterior no ha sido totalmente recibida o procesada, se indica con un color la causa (tasa de transmisión o de CPU). Si el color es rojo indica que el mínimo entre los dos valores es la tasa de transmisión. Si el color es azul indica que el valor mínimo es la tasa de CPU. 

En cada posición del buffer tiene un número que indica la cantidad de
kbits ($10³$ bits) que hay almacenado en dicha posición.
\end{enumerate} 
 
\item Marcador tiempo: Está situado debajo de la pantalla principal cada vez que el reloj avanza nos indica que un GOP ha sido procesado, así que realmente no es un temporizador de horas, minutos y segundos sino que es un ``temporizador de GOPs''. 
 
\item Cuadro de mandos: El cuadro de mandos es la forma que tiene el usuario de comunicarse con la interfaz, existen diferentes opciones a realizar: 

\begin{enumerate} 

\item Play: Este botón se utiliza para empezar a reproducir el vídeo, también se usará cuando el vídeo esté parado y queramos ponerlo en marcha.

\item Stop: Se utiliza para parar la imagen de la reproducción, el programa sigue recibiendo información del socket.
\item Más y menos: Estos botones se utilizan para indicarle al programa cúal es la cota máxima de bytes que podemos recibir, procesar, etc. de un GOP. Se utiliza para comprobar la progresividad de la reproducción. Con estos botones aumentamos o disminuimos esa cota.
\item Salida: Este botón sirve para abandonar la aplicación. Es necesario pulsar esta opción ya que así la aplicación se abandona de forma adecuada, liberando todos los recursos reservados.
\item Barra de desplazamiento: Esta barra se utiliza para forzar que se pida un porcentaje de la información calculada para el siguiente slot de tiempo. Se utiliza para ver la progresividad de la reproducción. Cada posición de la barra indica un porcentaje de dicha cantidad. Estos porcentajes son (20, 40, 60, 80, 100, 120, 140, 160, 180, 200) siempre que no sobrepasen la tasa calculada se pedirá el porcentaje indicado.

\end{enumerate}
\end{enumerate}


\subsection{Programación de la interfaz}
 
Después de estudiar la forma de programar usando las APIs de X Window
y habiendo comprendido su funcionamiento, se realizó un programa para
diseñar nuestra interfaz y le añadimos la funcionalidad.
 
La estructura básica de cualquier programa en X Window es la
siguiente:

\begin{verbatim}

Programa: 
{ 
        Inicialización(); 
        
        SeleccionarEventos(); 
 
        Repetir Espera_Evento 
        { 
                Si llega Pulsación_de_teclado: 
                { 
                        Código que gestiona teclado; 
                } 
                Si llega Pulsción_de_ratón: 
                { 
                        Código que gestiona ratón; 
                } 
                etc... 
                 
        } 
        LiberarRecursos(); 
}

\end{verbatim}

En la inicialización se suelen realizar las siguientes operaciones:

\begin{enumerate} 

\item Apertura del display: Mediante XOpenDisplay() se abrirá el display deseado, normalmente el display 0. 
\item Creación de una (o más) ventanas de trabajo: Utilizando funciones específicas para ello (XCreateSimpleWindow() o XCreateWindow()), creamos la ventana principal de nuestra aplicación. 
\item Mapeado de la ventana: Con el fin de que la ventana sea visible, la mapeamos.

\end{enumerate}

En la selección de eventos se suele realizar las siguientes
operaciones:
 
\begin{itemize}

\item Indicar al servidor que tipo de eventos tiene que detectar, clasificar e identificar para que la aplicación defina su comportamiento ante cada evento. Para ello hay que estudiar bien los eventos y seleccionar sólo los necesarios, esto evita la sobrecarga de operaciones en el servidor. 

\end{itemize}
  
Seleccionamos los eventos para los que nuestra aplicación debe
responder. Los eventos configurados son:

\begin{itemize}

\item ExposureMask: Al seleccionar esta máscara se reciben los eventos de tipo Expose, que indican que una parte rectangular de la ventana se ha hecho visible. Un ejemplo ocurre en el caso de que una ventana de otra aplicación solapara en parte a nuestra ventana y de repente fuera cerrada o movida, con lo que el servidor se ve en la obligación de avisarnos de esta circunstancia para que el programa sepa que puede dibujar en dicha área. Si más de una zona se liberara simultáneamente, se generará un evento por cada zona rectangular que se haga visible, lo que nos permitirá redibujar cada una de estas zonas con los datos gráficos que deban contener y que antes no eran necesarios al estar tapados por otra ventana. 
 
\item ButtonPressMask: Nos avisa de pulsaciones del botón del mouse mediante un evento del tipo ButtonPress. Es decir, si seleccionamos el evento ButtonPressMask el servidor nos envía los mensajes ButtonPress pertinentes a la ventana especificada. 
 
\item PointerMotionMask: Esta máscara hace que se reciba un evento MotionNotify cuando se produzca cualquier movimiento del puntero del ratón (sin necesitar que el botón sea pulsado). 
 

\item Existen una serie de eventos que siempre están seleccionados, que son los siguientes: MappingNotify, ClientMessage, SelectionRequest, SelectionNotify y SelectionClear.  
 
\end{itemize}

Dependiendo del tipo de evento que se desencadene realizamos unas
operaciones u otras. Dichas operaciones son:

\begin{itemize}
 
\item Evento Expose: En este evento realizamos las operaciones de repintado de la pantalla con los datos almacenados durante la ejecución. Por ejemplo, imaginemos que estamos reproduciendo un vídeo y una ventana externa solapa nuestra ventana. Al moverla y volver a dejarla visible se genera este evento y por lo tanto debemos repintar la pantalla con los datos actuales. 
 
\item Evento ButtonPress: En este evento comprobamos si hemos pulsado con el ratón sobre ciertas partes de la interfaz, para controlar pulsaciones de botones, etc. Es el evento que genera las acciones en nuestra aplicación. 
 
\item Evento MotionNotify: En este evento controlamos si el puntero del ratón ha pasado sobre alguna parte de la ventana. Lo utilizamos para iluminar los botones de la aplicación cuando pasamos el puntero del ratón sobre ellos. 

\end{itemize} 

Otra de las tareas a realizar es la carga de las imágenes sobre una
estructura adecuada para nuestro programa, dicha estructura esta
formada por:
 
\begin{verbatim} 
struct est_imagen 
{
   int x,y,anchura,altura,xmin,xmax,ymin,ymax; 
   Pixmap bitmap;
   Pixmap bitmap2;
   int origenx; 
   int origeny; 
   int desplazamiento; 
}; 
 
\end{verbatim}

donde: 
 
\begin{itemize}

\item X,Y : Posición inicial de la imagen en la ventana. 
\item Anchura, Altura: Indican la altura y la anchura de la imagen. 
\item Xmin, Ymin, Xmax, Ymax: Indican el contorno de la imagen y que se utilizará para indicar si ha sido pulsado, seleccionado, etc. 
\item Bitmap, Bitmap2: En estas variables se cargarán los mapas de bits en si, es decir las imágenes JPEG, tenemos dos, porque normalmente cargamos la imagen activada y la desactivada. 
\item Origenx, Origeny: Se utiliza para cargar varias imágenes de forma paralela, por ejemplo el buffer, o la barra de desplazamiento. 
\item Desplazamiento: Indica el desplazamiento entre imagen e imagen, se usa para el buffer y la barra de desplazamiento. 

\end{itemize}
 
Esta estructura es la misma para todas las imágenes aunque existan
algunas que no utilizan todos los campos.
 

\subsection{Integración de la interfaz y el cliente}
 
 
Para poder integrar la interfaz y el cliente de vídeo progresivo se
utilizaron funciones de programación de sistemas: creación de procesos
mediante fork(), memoria compartida para la comunicación entre
procesos, etc.
 
El grueso de la aplicación está dividido en dos procesos, uno que
controla la interfaz, generación de eventos,
etc., y otro proceso donde se hace la lectura de información sobre el
socket, la descodificación, la transformación inversa y la
visualización de GOP. Ambos procesos se comunicaban mediante una
estructura almacenada en un área de memoria compartida. 

La estructura compartida por ambos procesos y por tanto alojada en
memoria compartida tiene la siguiente definición:

\begin{verbatim} 
struct estructura { 
   int bpgmemoria;
   int reloj;
   int bpgmax;
   int bpgreal;
   int stop;
   int salir;
   int cerrojo;
   float alfa;
   float porcentaje;
   int pics;
   int picscpu;
   int picscpuleft;
   int bpgred;
   int bpgdec;
   int causante;
   int swcpu; 
}; 
 
\end{verbatim}

donde : 

\begin{itemize}
 
\item bpgmemoria: Esta variable se usa para almacenar los BPG\footnote{BPG (bits per GOP): Indica la cantidad de bits que forman un GOP. En nuestro caso la cantidad de bits que leemos de cada GOP.} máximos permitidos durante la ejecución de la aplicación. Esto es debido a que la reserva de memoria está en función de esta variable. 
\item reloj: Variable usada para indicar en qué GOP de ejecución nos encontramos, además de alterar el tiempo en la interfaz, similar a un reloj digital. 
\item bpgmax: En esta variable almacenamos los BPG máximos actuales para un instante en concreto. Nunca supera a la variable bpgmemoria. 
\item bpgreal: Indican los bits que se pasan a la fase de procesamiento y que por tanto repercute en la calidad de la imagen. 
\item stop: Indica si se ha pulsado o no el botón de parada. 
\item salir: Indica si se ha pulsado el botón de salida. Esta variable activada hace que la aplicación termine. 
\item cerrojo: Variable de condición utilizada para los cerrojos. 
\item alfa: Variable utilizada para almacenar el valor de ( necesaria para el cálculo de la tasa de transmisión. 
\item porcentaje: Indica el porcentaje seleccionado en la barra de desplazamiento. 
\item pics: indica en qué interrupción se ha terminado de leer la cantidad de información pedida al servidor en el slot de tiempo anterior.  
\item picscpu: Indica en qué interrupción se ha terminado el proceso de descodificación. Se utiliza para calcular la tasa de CPU. 
\item picscpuleft: Indica cuantas interrupciones quedan por ejecutarse una vez que se termina el proceso de descodificación. 
\item bpgred: Indica la cantidad de bytes que se han leído durante $pics$ interrupciones. Se utiliza para calcular la tasa de transmisión. 
\item bpgdec: En esta variable es donde se almacenan los bytes reales que se procesan en la fase de descodificación. Se utiliza para calcular la tasa de CPU. 
\item causante: Almacena un número que indica quién es el responsable de la cantidad de información pedida al servidor ya que esta tasa se calcula como el valor mínimo de la tasa de transmisión y de CPU. 
\item swcpu: Se utiliza como variable de condición para saber en qué instante ha terminado de procesar la función de descodificación. 
 
\end{itemize}

