Set Top Box - MADS Group

Transcripción

Set Top Box - MADS Group
Facultad de Informática
Departamento de Computación
PROYECTO FIN DE CARRERA
INGENIERÍA INFORMÁTICA
Desarrollo de un Set Top Box basado en
Linux para un servicio de vı́deo bajo
demanda
Autor: Samuel Rivas González
Tutor: Vı́ctor M. Gulı́as Fernández
A Coruña, 30 de Junio de 2003
Especificación
Tı́tulo del proyecto:
Desarrollo de un Set Top Box basado en Linux para un servicio de
vı́deo bajo demanda
Clase:
Proyecto clásico de ingenierı́a.
Nombre del alumno:
Samuel Rivas González.
Nombre director :
Vı́ctor M. Gulı́as Fernández.
Nombre del tutor :
Vı́ctor M. Gulı́as Fernández.
Miembros del tribunal :
Miembros suplentes:
Fecha de lectura:
Calificación:
A mi abuelo Luı́s
Agradecimientos
Supongo que será innecesario decir que no he sido yo el único artı́fice de que
llegara el momento de escribir estas lı́neas; son tantas las personas a las que
debo mucho por el apoyo y ayuda que he recibido durante todos estos años de
carrera que serı́a imposible nombrarlas a todas sin adjuntar un nuevo tomo a este
documento. Me gustarı́a, sin embargo, mencionar a algunas personas que han
tenido una especial influencia en el hecho que que ahora mismo esté a punto de
terminar este proyecto:
A mis padres, por darme la oportunidad de llegar hasta el final de
este carrera y el apoyo necesario para conseguirlo.
A mis hermanos, mis abuelos y mis tı́as que me llevan aguantando
tantos años.
A Rubén y Julio, por las prácticas y los exámenes que preparamos
juntos.
A Pablo, por las horas de estudio que compartimos.
A todos los que soportaron la convivencia conmigo: Iván durante 5
años y Xavi, Fernando y Julio (otra vez) durante este último año de
carrera.
A Vı́ctor, por llevarme el proyecto.
A la gente del laboratorio LFCIA por toda la ayuda que me ofrecieron
durante este trabajo.
Y a todos mis amigos que en algún momento de mi vida me han
ayudado o sencillamente han compartido parte de su tiempo conmigo.
Sin eso serı́a imposible haber llegado hasta aquı́.
Muchas gracias.
Resumen
El objetivo de este proyecto es desarrollar un Set Top Box que permita al
usuario final acceder a un servicio de vı́deo bajo demanda a través de una interfaz definida en un servidor remoto.
Las limitaciones de espacio a las que se debe atener el software desarrollado
para realizar las funcionalidades requeridas en el Set Top Box obligan a la utilización de bibliotecas de bajo nivel para el control del hardware gráfico. Para ello se
ha elegido la biblioteca DirectFB, que permite controlar directamente el dispositivo framebuffer de Linux a través de un API escrito en C, que será el lenguaje
utilizado para implementar la aplicación encargada de comunicarse con el usuario.
La interfaz presentada por el Set Top Box al usuario será definida utilizando el
estándar XML. Para ello se ha desarrollado una aplicación que proporcionará los
documentos XML necesarios a la aplicación cliente residente en el Set Top Box.
Para el desarrollo de esta aplicación se ha elegido el lenguaje funcional Erlang.
El software desarrollado para el Set Top Box deberá ser capaz de realizar las
siguientes funciones:
Comunicarse con el servidor de documentos XML utilizando el protocolo
HTTP.
Comprender los documentos XML recibidos y mostrar al usuario una interfaz de acuerdo con dichos documentos.
Comprender eventos generados por el usuario a través de los periféricos de
entrada pertinentes (por ejemplo, el mando a distancia).
Realizar las acciones necesarias ante los eventos de entrada generados por
el usuario; de acuerdo con las especificaciones de los documentos XML.
Dichas acciones comprenden, entre otras, solicitud de nuevos documentos
XML, reproducción de vı́deo, cambios en las preferencias del usuario ...
La aplicación encargada de proporcionar los documentos XML se desarrollará de forma que realice las funciones mı́nimas necesarias para poder validar
el correcto funcionamiento del software implementado para el Set Top Box. Esta
aplicación recibirá peticiones HTTP y responderá enviando documentos XML utilizando el mismo protocolo. La información necesaria sobre los distintos usuarios
y los medios disponibles se mantendrá en una base de datos.
Palabras clave
DirectFB, framebuffer, Set Top Box, C, vı́deo bajo demanda, XML, HTTP,
Erlang, vı́deo, interfaz.
Índice general
1. Introducción
1.1. Introducción . . . . . . . .
1.2. Objetivos . . . . . . . . .
1.3. Etapas del proyecto . . . .
1.4. Estructura de la memoria
.
.
.
.
1
1
2
3
4
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
6
6
7
8
9
9
12
14
15
17
17
19
20
21
23
23
24
26
27
3. Análisis
3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2. Protocolo de comunicación . . . . . . . . . . . . . . . . . . . . . .
3.3. Set Top Box . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
29
30
31
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2. Contextualización
2.1. Vı́deo bajo demanda . . . . . . . . . .
2.1.1. Introducción . . . . . . . . . . .
2.1.2. Servidores de vı́deo comerciales
2.1.3. El servidor VoDKA . . . . . . .
2.2. Set Top Boxes . . . . . . . . . . . . . .
2.2.1. Introducción . . . . . . . . . . .
2.2.2. Middleware . . . . . . . . . . .
2.2.3. Funcionalidades tı́picas . . . . .
2.2.4. Ejemplos comerciales . . . . . .
2.3. Programación gráfica en Linux . . . . .
2.3.1. Introducción . . . . . . . . . . .
2.3.2. Framebuffer . . . . . . . . . . .
2.3.3. Microwindows . . . . . . . . . .
2.3.4. Qt . . . . . . . . . . . . . . . .
2.4. DirectFB . . . . . . . . . . . . . . .
2.4.1. Introducción . . . . . . . . . . .
2.4.2. Arquitectura . . . . . . . . . . .
2.4.3. Términos importantes . . . . .
2.4.4. API . . . . . . . . . . . . . . .
i
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
ii
Índice general
3.4. Aplicación DFBCIn . . . . . . . . .
3.4.1. Introducción . . . . . . . . .
3.4.2. Módulo gráfico . . . . . . .
3.4.3. Parser . . . . . . . . . . . .
3.4.4. Conclusiones . . . . . . . .
3.5. Servidor XML (VXMLS) . . . . . .
3.6. Ciclo de Desarrollo . . . . . . . . .
3.6.1. Introducción . . . . . . . . .
3.6.2. Prototipo . . . . . . . . . .
3.6.3. DFBCIn . . . . . . . . . . .
3.6.4. VXMLS . . . . . . . . . . .
3.6.5. Prueba del sistema conjunto
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
32
32
32
34
35
36
37
37
37
39
40
40
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
42
42
42
43
45
48
48
48
54
55
55
56
58
59
65
71
75
75
76
78
78
80
5. Implementación de DFBCIn
5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2. Estándares de codificación . . . . . . . . . . . . . . . . . . . . . .
5.2.1. Nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . .
85
86
87
87
4. Diseño
4.1. Introducción . . . . . . . . . . . . . . . . . . .
4.2. Definición de la interfaz y objetos del dominio
4.2.1. Introducción . . . . . . . . . . . . . . .
4.2.2. La clase Menu . . . . . . . . . . . . . .
4.2.3. La interfaz Action . . . . . . . . . . .
4.3. Documentos XML . . . . . . . . . . . . . . . .
4.3.1. Introducción . . . . . . . . . . . . . . .
4.3.2. Descripción de los menús . . . . . . . .
4.4. Protocolo de comunicación . . . . . . . . . . .
4.5. Diseño de DFBCIn . . . . . . . . . . . . . . .
4.5.1. División en subsistemas . . . . . . . .
4.5.2. Interacción entre módulos . . . . . . .
4.5.3. Objetos comunes . . . . . . . . . . . .
4.5.4. Diseño del motor de la aplicación . . .
4.5.5. Diseño del módulo VXMLS . . . . . .
4.5.6. Diseño del módulo gráfico . . . . . . .
4.6. Diseño de VXMLS . . . . . . . . . . . . . . .
4.6.1. Introducción . . . . . . . . . . . . . . .
4.6.2. Objetos del dominio . . . . . . . . . .
4.6.3. Modelo entidad-relación . . . . . . . .
4.6.4. Diseño de la capa modelo . . . . . . .
4.6.5. Diseño de la vista y el controlador . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Índice general
5.2.2. Objetos . . . . . . . . . . . . . . . . .
5.3. Control de errores . . . . . . . . . . . . . . . .
5.4. Control de concurrencia . . . . . . . . . . . .
5.5. Tipos de datos de uso general . . . . . . . . .
5.5.1. Pilas . . . . . . . . . . . . . . . . . . .
5.5.2. Arrays . . . . . . . . . . . . . . . . . .
5.6. La clase Menu . . . . . . . . . . . . . . . . . .
5.7. La clase Option . . . . . . . . . . . . . . . . .
5.8. La clase Key . . . . . . . . . . . . . . . . . . .
5.9. La interfaz Action . . . . . . . . . . . . . . . .
5.9.1. Introducción . . . . . . . . . . . . . . .
5.9.2. Tipos de datos . . . . . . . . . . . . .
5.9.3. Funciones . . . . . . . . . . . . . . . .
5.9.4. ¿Cómo instanciar una acción? . . . . .
5.9.5. ¿Cómo añadir una nueva acción? . . .
5.10. Implementación del motor de la aplicación . .
5.10.1. Main . . . . . . . . . . . . . . . . . . .
5.10.2. Interface . . . . . . . . . . . . . . . . .
5.10.3. InterfaceInput . . . . . . . . . . . . . .
5.10.4. Mp3Player . . . . . . . . . . . . . . .
5.11. Implementación del módulo VXMLS . . . . .
5.11.1. Módulo HTTP . . . . . . . . . . . . .
5.11.2. Parser VXMLS . . . . . . . . . . . . .
5.11.3. Parser para los documentos menu . . .
5.11.4. Implementación de la fachada VXMLS
5.12. Implementación del Módulo gráfico . . . . . .
5.12.1. Introducción . . . . . . . . . . . . . . .
5.12.2. Estructuración del código . . . . . . .
5.12.3. La fachada graphics.c . . . . . . . .
5.12.4. La inicialización de DirectFB . . . .
5.12.5. Control de eventos de entrada . . . . .
5.12.6. Impresión de menús y texto . . . . . .
5.12.7. Reproducción de vı́deo . . . . . . . . .
5.12.8. Módulo de pruebas . . . . . . . . . . .
5.13. Sistema de propiedades . . . . . . . . . . . . .
5.14. Etapas de codificación . . . . . . . . . . . . .
5.14.1. Desarrollo del núcleo de la aplicación .
5.14.2. Reproducción básica de vı́deo . . . . .
5.14.3. Pruebas en el Set Top Box . . . . . . .
5.14.4. Varias mejoras . . . . . . . . . . . . .
5.14.5. Comunicación HTTP . . . . . . . . . .
iii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
89
90
92
94
94
95
96
98
99
100
100
100
104
107
108
108
108
110
114
116
118
118
120
121
133
136
136
137
137
138
138
140
141
144
145
145
145
146
147
147
148
iv
Índice general
5.14.6. Integración con VXMLS . . . . . . . . . . . . . . . . . . . 148
5.14.7. Integración con VoDKA . . . . . . . . . . . . . . . . . . . 149
5.14.8. Reproductor Mp3 y acciones de texto . . . . . . . . . . . . 149
6. Implementación de VXMLS
6.1. Introducción . . . . . . . . . . . . . . . . .
6.1.1. Generalidades sobre Erlang/OTP .
6.1.2. ¿Cómo funciona Inets? . . . . . . .
6.1.3. ¿Cómo funciona Mnesia? . . . . . .
6.1.4. Servidores genéricos . . . . . . . . .
6.1.5. Estructura de la aplicación VXMLS
6.1.6. Tipos de datos . . . . . . . . . . .
6.1.7. Control de errores . . . . . . . . . .
6.2. Capa Modelo . . . . . . . . . . . . . . . .
6.2.1. Tipos de datos . . . . . . . . . . .
6.2.2. Administración de la base de datos
6.2.3. Funciones de la fachada . . . . . .
6.3. Capa Vista . . . . . . . . . . . . . . . . .
6.4. Capa Controlador . . . . . . . . . . . . . .
6.4.1. Generación de la respuesta HTTP .
6.4.2. Traducción de mensajes . . . . . .
6.4.3. Fachada . . . . . . . . . . . . . . .
6.4.4. Creación y destrucción de sesiones .
6.4.5. Propiedades de personalización . .
6.4.6. Generación de menús . . . . . . . .
6.4.7. Composición de objetos Menu . . .
6.4.8. Menús definidos . . . . . . . . . . .
6.4.9. Ejemplos de vxmls get menu . . .
6.5. Extensibilidad . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
151
152
152
152
153
154
154
155
156
157
157
159
159
165
167
167
167
168
168
169
169
171
172
173
175
7. Validación
7.1. Despliegue del sistema . . . . . . . . . .
7.2. Tamaño del software para el Set Top Box
7.3. DFBCIn . . . . . . . . . . . . . . . . . .
7.3.1. Problemas de rendimiento . . . .
7.3.2. Problemas con avifile . . . . .
7.4. VXMLS . . . . . . . . . . . . . . . . . .
7.4.1. Comportamiento general . . . . .
7.4.2. Problemas con Inets . . . . . . .
7.5. Streaming . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
179
179
180
181
181
182
184
184
184
184
.
.
.
.
.
.
.
.
.
Índice general
v
8. Conclusiones y lı́neas futuras
8.1. Conclusiones . . . . . . . . . .
8.2. Continuación de este proyecto
8.2.1. DFBCIn . . . . . . . .
8.2.2. VXMLS . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
187
187
189
189
190
A. DTD utilizados
193
A.1. DTD utilizado para la definición de menús . . . . . . . . . . . . . 193
A.2. DTD utilizado para la especificación de las respuestas de VXMLS 195
B. API del módulo gráfico de DFBCIn
197
C. Definición de IDirectFBVideoProvider
199
D. Tipos de datos en Erlang
203
E. Instalación y ejecución
E.1. Estructura de la distribución
E.2. Instalación de VXMLS . . .
E.2.1. Compilación . . . . .
E.2.2. Instalación . . . . . .
E.3. Instalación de DFBCIn . . .
E.3.1. Requisitos previos . .
E.3.2. Compilación . . . . .
E.3.3. Ejecución . . . . . .
205
205
205
205
206
207
207
207
208
F. Glosario
Bibliografı́a
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
209
213
Índice de figuras
1.1. Relación entre el usuario y el servidor de VoD . . . . . . . . . . .
2
2.1.
2.2.
2.3.
2.4.
2.5.
2.6.
2.7.
Configuración interna de un Set Top Box . . . . . . . . .
Capas de un Set Top Box . . . . . . . . . . . . . . . . .
Prismiq MediaPlayer . . . . . . . . . . . . . . . . . . . .
Set Top Box de HMMI . . . . . . . . . . . . . . . . . . .
XPC SS50 . . . . . . . . . . . . . . . . . . . . . . . . . .
Acceso de una aplicación DirectFB al hardware gráfico
Relación entre las interfaces de DirectFB . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
10
11
16
16
17
25
27
3.1.
3.2.
3.3.
3.4.
Prototipo de interfaz con el usuario
Creación de parsers con expat . . .
Aplicación DFBSee . . . . . . . . .
Aplicación DFBPoint . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
35
38
39
4.1. Primera aproximación a la clase Menu . . . . . . . . .
4.2. Segunda aproximación a la clase Menu . . . . . . . . .
4.3. Diseño definitivo de la clase Menu . . . . . . . . . . . .
4.4. Acciones para el manejo de menús . . . . . . . . . . . .
4.5. Acciones para el control de vı́deos . . . . . . . . . . . .
4.6. Resto de acciones del sistema . . . . . . . . . . . . . .
4.7. Módulos de DFBCIn . . . . . . . . . . . . . . . . . . .
4.8. Interacción entre los módulos de DFBCIn . . . . . . . .
4.9. Clase Menu en DFBCIn . . . . . . . . . . . . . . . . .
4.10. Clases para el almacenamiento de datos . . . . . . . . .
4.11. Clases del motor . . . . . . . . . . . . . . . . . . . . .
4.12. Detección de eventos de entrada . . . . . . . . . . . . .
4.13. Ciclo de ejecución . . . . . . . . . . . . . . . . . . . . .
4.14. Estados de InterfaceInput e Interface . . . . . . . . . .
4.15. Máquina de estados de Interface . . . . . . . . . . . . .
4.16. Interacción entre los componentes del módulo VXMLS
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
44
46
47
47
56
57
59
60
61
62
62
63
64
66
vii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
viii
Índice de figuras
4.17. Clases del módulo VXMLS . . . . . . . . . . . . . . . . . . . . . .
4.18. Clases utilizadas para la construcción del parser para descripciones
de menús . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.19. Clases utilizadas para la construcción módulo gráfico . . . . . . .
4.20. Máquina de estados de VideoPlayer . . . . . . . . . . . . . . . . .
4.21. Aspecto visual de las dos implementaciones del módulo gráfico . .
4.22. Patrón Model/View/Controller en el diseño de VXMLS . . . . . .
4.23. Objetos del dominio . . . . . . . . . . . . . . . . . . . . . . . . .
4.24. Objetos valor utilizados para definir objetos Menu . . . . . . . . .
4.25. Objetos valor utilizados por VXMLS . . . . . . . . . . . . . . . .
4.26. Modelo entidad-relación . . . . . . . . . . . . . . . . . . . . . . .
4.27. Clases de la vista y el controlador de VXMLS . . . . . . . . . . .
7.1.
7.2.
7.3.
7.4.
Despliegue del sistema final . . . . . . .
DFBCIn funcionando en el Set Top Box
Capas de acceso al servicio de streaming
Cluster servidor de vı́deo utilizado . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
67
68
73
74
75
76
77
77
78
79
81
180
181
185
186
Capı́tulo 1
Introducción
Índice General
1.1.
1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . .
1
1.2. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . .
2
1.3. Etapas del proyecto . . . . . . . . . . . . . . . . . . . .
3
1.4. Estructura de la memoria . . . . . . . . . . . . . . . .
4
Introducción
Un servicio de vı́deo bajo demanda (VoD de aquı́ en adelante para simplificar)
permite a los usuarios finales el acceso a medios de vı́deo de forma interactiva sin
ningún tipo de programación preestablecida. Para proporcionar este tipo de servicio es necesario un servidor de streaming que sea capaz de distribuir los medios
en un flujo continuo de datos de forma que puedan ser reproducidos mientras se
reciben.
Para el usuario final, el servidor de streaming puede ser visto como un gran
conjunto de medios sin estructura a los que puede acceder. Permitir el uso de esos
recursos pasa por la obligación de desarrollar un sistema que posibilite el acceso
de forma controlada a dichos medios y que los presente de forma comprensible
y ordenada. Este es el objetivo que persigue este proyecto: desarrollar un Set
Top Box que proporcione acceso desde el hogar a los medios distribuidos por un
servidor VoD.
1
2
Capı́tulo 1. Introducción
Para mantener centralizado el control de las interfaces presentadas a los usuarios, el software desarrollado seguirá el paradigma cliente-servidor, de forma que
el software que controla el funcionamiento del Set Top Box obtendrá la información necesaria de un servidor, con el que se comunicará utilizando el estándar
XML sobre el protocolo HTTP.
Flujo
Servidor VoD
Actor
Set Top Box
n
1
Servidor XML
Figura 1.1: Relación entre el usuario y el servidor de VoD
El desarrollo de la aplicación cliente se ha realizado utilizando el lenguaje C y
las bibliotecas DirectFB [1], para el control del hardware gráfico, y expat [2],
para la implementación del parser XML. Esta aplicación recibe el nombre de
DFBCIn (acrónimo de DirectFB Client Interface).
El servidor XML se ha implementado utilizando el lenguaje funcional Erlang [3] y diversas herramientas proporcionadas por la plataforma de desarrollo
Erlang/OTP [4].
Como servidor de VoD se utilizará VoDKA [5]. Por ello, a la aplicación encargada de servir los documentos XML se le ha bautizado como VXMLS (VoDKA
XML Server ).
1.2.
Objetivos
El principal objetivo del proyecto es el diseño y desarrollo de un Set Top Box
basado en Linux que permita el acceso de forma controlada al servidor de VoD
VoDKA. El software desarrollado deberá ajustarse a las restricciones de espacio
Etapas del proyecto
3
presentadas por este tipo de sistemas.
Como objetivo secundario también se desarrollará una aplicación que actúe
como servidora de los documentos XML que necesitará el Set Top Box para generar la interfaz que presentará al usuario.
Tanto el desarrollo del Set Top Box como de la aplicación encargada de proporcionar la descripción de la interfaz deberán cumplir los siguientes objetivos:
Permitir al usuario acceder a los servicios de VoDKA a través de un televisor
convencional.
Controlar el acceso a los medios, manteniendo información sobre los usuarios
que disfrutan de los mismos.
Permitir a los usuarios elegir ciertas configuraciones para adaptar los servicios a sus necesidades. Por ejemplo, cada usuario deberı́a poder elegir el
idioma en el que se le proporcione la información que precise.
Mantener la interfaz presentada a los usuarios siempre actualizada, pudiendo variar según, por ejemplo, la hora del dı́a, las preferencias del usuario,
etc.
1.3.
Etapas del proyecto
Durante el desarrollo del proyecto se ha pasado por las siguientes etapas:
1. Estudio de las diferentes alternativas existentes para programar accediendo
directamente al dispositivo framebuffer de Linux.
2. Elección de una de las alternativas (en este caso DirectFB) e implementación de una aplicación de ejemplo para comprobar su idoneidad.
3. Elección de la estructura de los documentos XML que deberá interpretar la
aplicación cliente.
4. Diseño e implementación de la aplicación cliente DFBCIn siguiendo el paradigma de desarrollo incremental. Como no se dispone todavı́a del servidor
de documentos los incrementos iniciales trabajarán con documentos estáticos: en las primeras fases con ficheros y, una vez avanzado el desarrollo,
solicitando los documentos de un directorio remoto utilizando el protocolo
HTTP.
4
Capı́tulo 1. Introducción
5. Estudio de las herramientas proporcionadas por Erlang/OTP para el desarrollo de aplicaciones HTTP.
6. Diseño e implementación del servidor de documentos XML VXMLS.
7. Último incremento de la aplicación cliente, en el que se añadirá la funcionalidad necesaria para permitir la comunicación con el servidor implementado
en el paso anterior.
8. Integración del sistema resultante con VoDKA.
9. Validación del sistema.
1.4.
Estructura de la memoria
En el capı́tulo 2 se expone una introducción a la tecnologı́a de vı́deo bajo demanda, en la que se hace una breve descripción del servidor VoDKA. También se
exponen las caracterı́sticas generales de los Set Top Boxes y de la programación
gráfica bajo Linux, finalizando con un apartado dedicado a los aspectos generales
de la biblioteca DirectFB.
Los capı́tulos 3 y 4 describen las fases de análisis y diseño de este proyecto.
La fase de implementación se divide dos capı́tulos: en el capı́tulo 5 se documenta
la implementación del software diseñado para el Set Top Box y en el capı́tulo 6
se documenta la implementación del servidor de documentos XML.
En el capı́tulo 7 se documenta la fase de validación del sistema.
Por último, en el capı́tulo 8 se exponen las conclusiones obtenidas tras la finalización del proyecto y se exponen una serie de lı́neas de trabajo que quedan
abiertas.
Se incluyen varios apéndices con diversa información que puede ser de utilidad
para comprender alguna de las secciones de esta memoria, información sobre la
instalación y ejecución del software desarrollado y un glosario en el que se explican
los acrónimos utilizados a lo largo de este documento.
Capı́tulo 2
Contextualización
Índice General
2.1. Vı́deo bajo demanda
. . . . . . . . . . . . . . . . . . .
6
2.1.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
6
2.1.2. Servidores de vı́deo comerciales . . . . . . . . . . . . .
7
2.1.3. El servidor VoDKA
. . . . . . . . . . . . . . . . . . .
8
2.2. Set Top Boxes . . . . . . . . . . . . . . . . . . . . . . .
9
2.2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
9
2.2.2. Middleware . . . . . . . . . . . . . . . . . . . . . . . .
12
2.2.3. Funcionalidades tı́picas
. . . . . . . . . . . . . . . . .
14
2.2.4. Ejemplos comerciales . . . . . . . . . . . . . . . . . . .
15
2.3. Programación gráfica en Linux . . . . . . . . . . . . .
17
2.3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
17
2.3.2. Framebuffer . . . . . . . . . . . . . . . . . . . . . . . .
19
2.3.3. Microwindows . . . . . . . . . . . . . . . . . . . . . . .
20
2.3.4. Qt . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
2.4. DirectFB
. . . . . . . . . . . . . . . . . . . . . . . . . .
23
2.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
23
2.4.2. Arquitectura . . . . . . . . . . . . . . . . . . . . . . .
24
2.4.3. Términos importantes . . . . . . . . . . . . . . . . . .
26
2.4.4. API . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
5
6
Capı́tulo 2. Contextualización
2.1.
Vı́deo bajo demanda
2.1.1.
Introducción
La intención de un servicio de VoD es proporcionar un servicio de vı́deo en el
que múltiples usuarios pueden solicitar un objeto de vı́deo en cualquier momento.
Las posibles aplicaciones de un sistema de este tipo pueden ser: pelı́culas bajo
demanda, noticias interactivas, aprendizaje a distancia, etc.
Un sistema de este tipo debe incluir los siguientes elementos:
El servidor de vı́deo: Es similar a un servidor de ficheros normal, pero a
diferencia de estos, debe priorizar el mantenimiento del flujo de datos constante. Un servicio de vı́deo no puede proporcionar el vı́deo completo ante
una petición, puesto que el tamaño de los datos es demasiado grande. En
lugar de esperar a recibir el vı́deo completo, el terminal cliente recibe un
flujo constante de datos, que irá reproduciendo a medida que le van llegando. Por lo tanto, el servidor de vı́deo tiene que mantener el flujo de
datos de manera ininterrumpida y con la cadencia adecuada para que la
reproducción del vı́deo en el terminal sea continua. Esta técnica se conoce
como streaming. Para este proyecto se ha utilizado VoDKA como servidor
de vı́deo.
El servidor de aplicaciones: Debe incluir dos módulos básicos:
• La aplicación de usuario: Garantiza la conectividad de los usuarios
desde sus puestos remotos y le permite explorar entre los contenidos
a los que está autorizado. Asimismo, esta aplicación también controla
los datos y estadı́sticas de los distintos usuarios registrados. En el caso
de este proyecto, se desarrollará la aplicación de usuario VXMLS.
• La aplicación de gestión: La aplicación de gestión de un sistema de
vı́deo bajo demanda incluye: alta, clasificación, modificación y borrado
de los contenidos y los meta-datos; gestión y asignación de los perfiles
de usuario; asignación de claves de acceso para los distintos niveles de
contenido y monitorizado en tiempo real del estado del sistema. Los
datos manejados por esta aplicación estarán en una base de datos a la
que también accederá la aplicación de usuario. En [6] se ha desarrollado una aplicación de gestión para VoDKA que permite gestionar y
administrar los usuarios y medios asociados al servidor VoDKA.
Los codificadores en lı́nea: Permiten ofrecer material no pregrabado. Deben
ser capaces de obtener el vı́deo y comprimirlo en tiempo real para proporcional material visual en directo.
Vı́deo bajo demanda
7
Los terminales: En ellos es donde el usuario puede ver el vı́deo que ha solicitado. Estos terminales pueden ser de dos tipos: ordenadores personales o Set
Top Boxes (En este proyecto se desarrolla un terminal del segundo tipo).
Los terminales reciben el vı́deo, lo decodifican y lo presentan por pantalla
al usuario. Normalmente, el software que controla el terminal también debe
proporcionar la posibilidad de comunicarse con la aplicación de usuario. En
este proyecto se desarrolla la aplicación DFBCIn para este propósito.
La red : Deben tenerse en cuenta los factores de QoS, protocolos unicast y
multicast y otros factores especı́ficos para la transmisión de vı́deo.
2.1.2.
Servidores de vı́deo comerciales
En los últimos años las empresas más importantes que comercializan productos multimedia han desarrollado sistemas relacionados con el vı́deo bajo demanda.
Algunas de las soluciones están más adaptadas a redes de bajo ancho de banda, como es el caso de RealVideo Server, de Real Networks, que hacen uso de
tecnologı́as de caché de los streams para optimizar el uso de una red con las
caracterı́sticas de Internet, o Microsoft Windows Media Server, que utiliza protocolos propietarios y puede ser utilizado en entornos de un mayor ancho de banda
con un rendimiento menor.
Otras soluciones se enfocan más a entornos LAN o WAN, con mayor disponibilidad de ancho de banda. Las más representativas son Darwing Streaming
Server [7], de Apple, que es la versión de código abierto de Quicktime Streaming Server, y sólo es un servidor de streaming RTP, sin incluir la gestión del
almacenamiento; DB2 Digital Library Video Charger [8], de IBM; Oracle Video
Server [9, 10], quizá el más utilizado de todos, con arquitectura cliente-servidor,
pero con limitaciones de escalabilidad; Kassenna MediaBase, una evolución de
WebForce MediaBase, de SGI, con caracterı́sticas comunes al diseño de VoDKA
(modular, separación de funciones de adquisición, distribución y streaming, basado en sistemas UNIX), pero una menor flexibilidad y capacidad de adaptación;
Philips WebCine Server [11], servidor de streaming MPEG4 basado en Linux;
Cisco IP/TV [12], solución cerrada con herramientas orientadas al mercado de
formación; y el sistema de SUN: StorEdge Media Central [13]. También existen
soluciones de caja negra, que consisten en la combinación de un hardware especializado con alguno de los productos anteriores.
En el contexto académico la mayorı́a de los proyectos poseen un carácter altamente experimental. En [14] se analiza una solución jerárquica para la construc-
8
Capı́tulo 2. Contextualización
ción de servidores multimedia. En Stony Brook Video Server Project [15, 16, 17]
se intenta crear un servidor distribuido que proporcione al usuario caracterı́sticas de indexación, búsqueda y streaming de vı́deos, y está desarrollada con una
interesante arquitectura cliente servidor, adaptada para redes locales. En [18] se
presenta una alternativa totalmente distinta a la expuesta en este artı́culo, utilizando una arquitectura de memoria compartida.
Un estudio más detallado de algunas de estas soluciones se puede encontrar
en [19].
2.1.3.
El servidor VoDKA
2.1.3.1.
Caracterı́sticas generales de VoDKA
El sistema VoDKA es un servidor de streaming multimedia bajo demanda
extremadamente flexible, pero optimizado para el entorno de una operadora de
telecomunicaciones.
Caracterı́sticas principales:
Multiprotocolo: soporte para streaming de medios arbitrarios CBR por HTTP,
MPEG-4 y Quicktime sobre RTSP/RTP, MPEG Layer3 (MP3) sobre RTP,
MP3 sobre Icecast y ASF sobre HTTP con control del medio.
Multiproveedor: previsión de múltiples proveedores de medios integrados en
una única red de distribución de contenidos, cada uno en su dominio administrativo.
Flexibilidad en el almacenamiento: posibilidad de utilizar almacenamiento
en disco o cinta propio, servidores webDAV [20] u otros servidores VoDKA
encadenados como fuentes de medios.
Modularidad: el sistema está compuesto de una serie de módulos que se interconectan de forma flexible para adaptarse a configuraciones muy diferentes.
Distribuido: los distintos componentes del sistema se comunican entre sı́ de forma transparente a su localización. El servidor puede configurarse de forma
adaptada a la topologı́a de red, reduciendo costes de despliegue. Además,
se modelan mediante funciones de coste las caracterı́sticas de red y equipos
para optimizar la utilización de enlaces y equilibrar la carga, asegurando
calidad de servicio.
Set Top Boxes
9
Escalabilidad: gracias a su arquitectura, es posible configurar tanto un microservidor contenido en un equipo embebido como un cluster de cientos de
nodos, e incluso metaclusters o agrupaciones de clusters.
2.1.3.2.
Plataformas soportadas
La mayor parte de VoDKA está desarrollada en Erlang/OTP (Open Telecom
Platform). El código Erlang se ejecuta dentro de un runtime (ERTS), que incluye
una máquina virtual (en la que se ejecuta o bien bytecode o bien código nativo),
bibliotecas y una interfaz al sistema operativo y al hardware común a todas las
plataformas. Todos los módulos desarrollados en Erlang, por tanto, pueden, en
principio, instalarse sobre cualquier plataforma soportada por el ERTS: Solaris,
AIX, BSD, VxWorks, Win32 y Linux sobre múltiples arquitecturas (x86, SPARC,
ARM, PowerPC, S/390).
Algunas partes de VoDKA están desarrolladas en C y C++ , y puede que no
estén disponibles para todas las arquitecturas.
2.1.3.3.
Red de interconexión
Cuando una instalación de VoDKA abarca más de un nodo fı́sico, se supone
que existe una red IP que interconecta todos los nodos entre sı́. Es posible, sin
embargo, dividir la red de nodos VoDKA en clusters independientes, pero complica el mantenimiento.
En casos excepcionales es factible utilizar una red no IP, pero no es aconsejable en absoluto.
VoDKA utiliza toda la infraestructura del sistema operativo para acceder a
la red, con lo que podrá emplear cualquier interfaz soportado por éste. Habitualmente se desarrolla y prueba sobre interfaces Ethernet, Fast Ethernet y Gigabit
Ethernet en Linux, y Fast Ethernet y ATM con LANE en Solaris.
2.2.
Set Top Boxes
2.2.1.
Introducción
Un Set Top Box puede ser visto como un dispositivo que permite realizar
ciertas operaciones como el acceso a Internet o la recepción de vı́deo digital desde
el hogar, utilizando un televisor convencional como interfaz de salida y (posiblemente) un mando a distancia como dispositivo de entrada. En algunas ocasiones
10
Capı́tulo 2. Contextualización
se denomina a los Set Top Boxes como decodificadores, o IRDs.
Como ejemplos conocidos de Set Top Boxes se pueden citar los decodificadores de Canal+, Canal Satélite, Vı́a Digital, Quiero TV, etc.
Internamente, un Set Top Box es similar a un ordenador personal convencional, pero con algunas modificaciones para adaptarlo especı́ficamente a la ejecución
de aplicaciones multimedia, como pueden ser decodificadores MPEG hardware,
salidas de televisión digital, salida de audio estéreo, puerto de infrarrojos para
el mando a distancia, etc. Como medios de almacenamiento secundario pueden
disponer de un disco duro y/o memoria flash. También deben disponer de algún
tipo de dispositivo de red para recibir la información multimedia, receptores de
señal de televisión y un dispositivo de acceso condicional para controlar la utilización de los servicios de pago tales como el pago por visión.
Codificador NTSC/PAL
Puerto de infrafojos
Control de pantalla
DACs
y generacion de graficos
de audio
Decodificador de
video via satelite
Logica de
Control
DACs
de video
video por cable
Decodificador de
Control de E/S
Decodificador de
Memoria
CPU
Modem
video via terrestre
Distribucion de reloj
Decodificador
MPEG
RS232
xDSL
Logica de
Control
Unidad de Acceso Condicional
Interfaz HDD
Disco Duro/Flash
IEEE1394
USB
Figura 2.1: Configuración interna de un Set Top Box
Sobre la capa hardware se apilan las capas software necesarias para el correcto
funcionamiento del Set Top Box, de forma similar a como se harı́a en un PC, como
se puede ver en la figura 2.2.
1. Receptor de televisión digital : Dispositivos hardware necesarios para la recepción de la información necesaria. En la capa hardware también se con-
Set Top Boxes
11
Aplicaciones
Capa de aplicacion
API
Plataforma Software
Middleware
Sistema Operativo de
Tiempo Real
Capa RTOS
Drivers
Receptor de television digital
Capa Hardware
Figura 2.2: Capas de un Set Top Box
templan los dispositivos básicos para el funcionamiento del sistema (memoria, CPU, etc), pero se denomina receptor de televisión digital para señalar
que no es una plataforma hardware genérica, sino que está orientada a esa
tarea en particular.
2. Sistema Operativo: Se utilizan sistemas operativos de tiempo real (RTOS)
como pSOS, WindowsCE, Linux, VxWorks, OS20 o Nucleus.
3. Plataforma software: También conocido como middleware. Conjunto de
módulos que facilitan un desarrollo más eficiente. Proporciona un API para
cada lenguaje de programación soportado. Para la implementación del API,
el middleware puede incluir un gestor de aplicaciones, una máquina virtual,
las bibliotecas, el motor interactivo y las bases de datos.
4. Aplicaciones: Esta capa no necesita estar residente permanentemente, sino
que puede ser descargada bajo demanda.
Un Set Top Box está pensado para ser una máquina asequible, que el usuario
colocará en su hogar cerca del televisor para que realice las tareas necesarias que
permitan el disfrute de un servicio de vı́deo digital. Por lo tanto, es necesario
aplicar ciertas restricciones sobre el hardware a utilizar.
12
Capı́tulo 2. Contextualización
Debe ser razonablemente económico, esto implica que la CPU no tendrá toda la potencia de las últimas generaciones además de constreñir la cantidad
de memoria RAM y la capacidad del dispositivo de almacenamiento secundario utilizado (disco duro o memoria flash).
No debe ser voluminoso ni ruidoso. Esta restricción afecta a la utilización de
discos duros y de chips o fuentes de alimentación que requieran dispositivos
de ventilación para su correcta refrigeración.
Para compensar las restricciones de potencia de la CPU, se suelen incluir decodificadores MPEG hardware para liberar a la CPU de la carga de esta tarea.
Para disminuir los costes, ruidos y consumo, los Set Top Boxes no suelen
incluir disco duro, sino que utilizan memoria flash como dispositivo de almacenamiento secundario. Además, es deseable que el bloque de memoria utilizado sea
de la menor capacidad posible para no encarecer el sistema final.
2.2.2.
Middleware
En la actualidad existen varias organizaciones desarrolladoras de estándares
para el desarrollo de plataformas middleware.
DVB : El proyecto DVB [21] es un consorcio compuesto por alrededor de
300 operadores de vı́deo, operadores de red, desarrolladores de software,
fabricantes, etc. creado para el desarrollo de estándares para la transmisión
de televisión digital y servicios de datos. De este proyecto han salido cuatro
estándares mundialmente aceptados:
• DVB-S Estándar para la transmisión de vı́deo digital vı́a satélite.
• DVB-T Estándar para la transmisión de vı́deo digital por vı́a terrestre.
Actualmente no está instaurado en América, en donde se utiliza ATSC,
ni en Japón, en dónde se sigue el estándar ISDB-T.
• DVB-C Estándar para la transmisión de vı́deo digital por cable.
• MHP Estándar de televisión digital interactiva. Define un interfaz
genérico entre las aplicaciones interactivas y el terminal que las debe
ejecutar. Un middleware que proporcione el interfaz MHP podrá ejecutar las aplicaciones desarrolladas de acuerdo con este estándar independientemente de la plataforma hardware que utilice.
ATSC : Es una organización internacional sin ánimo de lucro para desarrollar estándares voluntarios para televisión digital. ATSC ha desarrollado
Set Top Boxes
13
numerosos estándares para transmisión de vı́deo, codificación, compresión
de audio digital, acceso condicional, etc. En cuanto a estándar de middleware ATSC ha propuesto el estándar DASE [22].
OpenCable: Es una iniciativa para dotar a la industria de televisión por
cable de los estándares necesarios para desarrollar servicios interactivos.
Como estándar de middleware propone OCAP [23].
JavaTV [24]: Especifica un API que proporciona una plataforma ideal de
desarrollo y utilización de aplicaciones sobre la plataforma Java. Dicho API
proporciona operaciones para realizar streaming tanto de audio como de
vı́deo, acceso condicional, control de canales y control de gráficos en pantalla.
Algunas de las plataformas más utilizadas son:
OpenTV [25]: Es la plataforma más utilizada en la actualidad para el desarrollo de sistemas de televisión digital sobre Set Top Boxes. Es una solución
comercial ampliamente utilizada por compañı́as de televisión interactiva.
Proporciona la plataforma middleware, aplicaciones, herramientas de desarrollo y soporte técnico. Las aplicaciones proporcionadas por OpenTV
abarcan campos como el t-comercio, el streaming, telebanca, juegos, navegadores, etc. Es la tecnologı́a utilizada por el Set Top Box de Vı́a Digital y
QueroTV.
Media Highway: Es un middleware propietario desarrollado por Canal+
Technologies [26]. Soporta los estándares DVB, OpenCable, y ATSC. Puede
ejecutar aplicaciones interactivas escritas en lenguajes soportados por dichos
estándares como Java, HTML o XDML. Es el middleware que utiliza el Set
Top Box de Canal Satélite.
Alticast [27]: Construye aplicaciones de televisión digital para una cantidad
importante de proveedores en Asia. Su middleware soporta los estándares
de aplicación MHP y DASE. Como máquina virtual en la que ejecutar las
aplicaciones utiliza AltiJVM, que es una implementación de la máquina
virtual de Java.
TV Navigator : Desarrollado por Liberate [28], es un middleware con una
representación importante en los Set Top Boxes de Estados Unidos. Soporta
Java y HTML.
14
Capı́tulo 2. Contextualización
MSTV [29]: Es el middleware desarrollado por Microsoft. Engloba varios
productos como WebTV, PersonalTV y UltimateTV. Estos productos permitieron obtener a Microsoft el liderazgo en el mundo de la televisión interactiva, pero con el tiempo han ido perdiendo viabilidad comercial y están
siendo abandonados. Ahora, Microsoft concentra sus esfuerzos en un middleware basado en su sistema operativo WindowsCE para compañı́as de
cable.
NDS Core [30]: Middleware desarrollado por NDS, principalmente instalado
en Set Top Boxes de América Latina y Asia. Pretende ser compatible tanto
con MHP como con OCAP y soporta Java y HTML.
2.2.3.
Funcionalidades tı́picas
Además de la recepción y reproducción de canales de audio y vı́deo digital,
los Set Top Boxes pueden realizar otro tipo de actividades como pueden ser la
utilización de Internet, descarga de juegos, telecompra, telebanca, tienda virtual,
solicitud de información de forma interactiva ...
Para los Set Top Boxes comerciales más conocidos en España, las funcionalidades van desde la más básica del Set Top Box de Canal+ hasta las más
avanzadas ofrecidas por Canal Satélite y Vı́a Digital.
El Set Top Box de Canal+ simplemente decodifica la señal recibida para permitir al abonado visualizar la programación que no se distribuye en abierto.
Los Set Top Boxes de Canal Satélite y Vı́a Digital ofrecen servicios interactivos, además de proporcionar el acceso a los diferentes canales de vı́deo. Algunas
de las operaciones que permiten realizar son:
Compra de estrenos o partidos.
Consulta de información bursátil.
Operaciones bancarias.
Recarga de telefonı́a.
Servicios de mensajes a móviles.
Información deportiva.
Guı́a de programación.
Set Top Boxes
15
Comercio por televisión (t-comercio).
Teniendo en cuenta las posibilidades ofrecidas por un Set Top Box, se puede
establecer la siguiente clasificación:
Set Top Box para TV por broadcast: Es el nivel más básico de Set Top Box.
No dispone de canal de retorno, simplemente recibe una señal de vı́deo que
decodifica para que el usuario pueda disfrutarla (por ejemplo, el decodificador de Canal+).
Set Top Box para TV digital : Dispone de un canal de retorno para permitir
la interacción del usuario con el proveedor del servicio de TV.
Set Top Box avanzado: Tiene caracterı́sticas muy similares a un PC: tienen
una CPU potente, disco duro de gran capacidad, etc. Disponen de acceso de
alta velocidad a Internet, posibilidad de grabación digital de vı́deo, juegos,
capacidad para el envı́o y recepción de correo electrónico, etc.
Sidecar : Proporciona un nuevo canal de recepción de datos para trabajar
conjuntamente con el canal recibido por el cliente en su Set Top Box original.
Set Top Box hı́brido: Set Top Box especializado para televisión por cable y
que incluye otras funciones, por ejemplo, reproducción de DVDs.
2.2.4.
Ejemplos comerciales
2.2.4.1.
Prismiq MediaPlayer
Este Set Top Box obtiene medios audiovisuales de la red y los presenta de
forma ordenada, permitiendo al usuario disfrutarlos en un televisor. Puede reproducir vı́deo en formato MPEG 1 y 2 y DIVX con ayuda de un PC.
La configuración de este Set Top Box es bastante simple, ya que el trabajo
de localizar, adaptar y enviar los medios lo realiza una aplicación servidora que
debe ser instalada en un PC:
CPU : microprocesador NEC uPD61130 32-bit MIPS con un decodificador
MPEG integrado.
Memoria: 64 megabytes de SDRAM
Interfaz de red : 10/100 Fast Ethernet integrada.
16
Capı́tulo 2. Contextualización
Figura 2.3: Prismiq MediaPlayer
Salidas de vı́deo: S-Video y vı́deo compuesto.
Salidas de audio: S/P DIF y RCA (estéreo).
Software: Linux 2.4, navegador y cliente AOL integrados.
2.2.4.2.
Set Top Box de HMMI
En este Set Top Box se hicieron algunas pruebas al principio del proyecto,
pero el soporte de framebuffer para la tarjeta gráfica que tiene dio bastantes
problemas y no se consiguió hacer funcionar correctamente la aplicación DFBCIn
en él.
Figura 2.4: Set Top Box de HMMI
2.2.4.3.
Shuttle Spacewalker XPC SS50
Este es el Set Top Box que se utilizó para la validación del sistema, se le
instaló un receptor de infrarrojos para recibir la señal del mando a distancia y
Programación gráfica en Linux
17
un disco duro para facilitar las pruebas. La configuración original utiliza una
memoria flash como almacenamiento secundario.
Figura 2.5: XPC SS50
Las caracterı́sticas de este Set Top Box son muy próximas a las de un PC:
CPU : Intel Pentium 4 Socket478.
Tarjeta de sonido: C-Media Electronics Inc CM8738 integrada.
Tarjeta de vı́deo: Silicon Integrated Systems SiS650/AGP VGA Display
Adapter integrada.
Memoria: 2 slots DIMM con capacidad de hasta 2 gigabytes de memoria
DDR.
Interfaz de red : 10/100 Fast Ethernet integrada.
Salida de televisión/S-Video.
Slots de expansión AGP y PCI.
Puertos USB y Firewire.
2.3.
Programación gráfica en Linux
2.3.1.
Introducción
Hoy en dı́a, la programación de interfaces gráficas para Linux se hace habitualmente sobre el sistema X Windows que proporciona una capa de abstracción
18
Capı́tulo 2. Contextualización
sobre el hardware gráfico y un API para desarrollar interfaces basadas en ventanas y eventos (tanto de teclado como de ratón).
El sistema X Windows se ha transformado en el estándar de facto para la
programación gráfica en Linux y Unix. X Windows permite el desarrollo de aplicaciones gráficas independientes del sistema operativo y la plataforma hardware,
es transparente a la red y soporta numerosos entornos de escritorio muy populares hoy en dı́a.
Sin embargo, La utilización del sistema X Windows supondrı́a una sobrecarga
demasiado pesada para el Set Top Box. Como se dijo en la sección 2.2.1, un sistema de este tipo debe ser asequible y silencioso, lo que implica poco espacio en el
almacenamiento secundario y una CPU de potencia limitada. Para aprovechar el
hardware disponible será necesario utilizar algún framework de orientación menos
genérica que X Windows.
Por lo tanto será necesario buscar otras alternativas para la programación
de la interfaz gráfica. La idea es bajar el nivel de programación y acceder directamente al hardware gráfico. Se utilizará un lenguaje de nivel medio-bajo para
escribir directamente en el framebuffer (ver la sección 2.3.2).
Sin embargo, trabajar directamente sobre el framebuffer es complicado y hace
el código dependiente de la plataforma (si se quieren utilizar las posibilidades de
aceleración gráfica). Por ello, se han estudiado distintos frameworks que proporcionen una mayor abstracción en forma de un API independiente del hardware
gráfico.
Las opciones estudiadas fueron:
Microwindows [31]: Proporciona un API escrito en C que permite realizar
operaciones gráficas básicas (rectángulos, cı́rculos, ...) y operaciones con
ventanas. Está orientado a la implementación de aplicaciones gráficas con
ventanas, pero prescindiendo de sistemas como Microsoft Windows o X
Windows.
Qt [32]: Proporciona un API C++ y una gran colección de objetos que
permiten realizar aplicaciones con un look and feel nativo independiente de la plataforma. Para este proyecto se estudió su versión embebida
(Qt/Embedded), que trabaja directamente sobre el framebuffer para evitar
sobrecargas.
Programación gráfica en Linux
19
DirectFB [33]: Proporciona un API escrito en C, pero con diseño objetual.
Está pensado para realizar operaciones gráficas con aceleración, evitando la
utilización del sistema X Windows, en favor de la utilización del dispositivo
framebuffer directamente [1]. Proporciona una abstracción de proveedor de
imagen, de proveedor de fuentes y de proveedor de vı́deo; Tiene operaciones para manejar la entrada ya sea por teclado, mando a distancia, ratón,
joystick o pantalla táctil y define un sistema de gestor de ventanas.
El framework elegido para la programación gráfica fue DirectFB, ya que
proporciona muchas opciones interesantes para el desarrollo de software para un
Set Top Box. Principalmente, la opción más útil es la abstracción que proporciona para el manejo de vı́deo, que desacopla la aplicación del formato de vı́deo
utilizado.
2.3.2.
Framebuffer
El framebuffer es una parte de la memoria RAM de la computadora en la que
se almacena la información para una imagen o frame. Esta información consiste
tı́picamente en los valores de color para cada pixel de la pantalla.
El kernel de Linux ha incorporado (a partir de su versión 2.1.107) el dispositivo framebuffer [34], con el que permite la programación gráfica en una consola.
Este dispositivo fue desarrollado con el objetivo inicial de permitir al kernel de
Linux emular una consola de texto en sistemas como Apple Macintosh, que no
soportan el modo texto VGA. Finalmente, el dispositivo framebuffer pasó a estar
completamente integrado en el kernel de Linux y se espera que llegue a ser el
modo estándar de acceso al hardware gráfico.
El dispositivo framebuffer presenta una abstracción del hardware gráfico y
permite a las aplicaciones software acceder al hardware gráfico utilizando una
interfaz bien definida. De esta forma, el software no necesita conocer los detalles
de bajo nivel (registros hardware), excepto para la aceleración hardware.
La utilización del dispositivo framebuffer supone las siguientes ventajas:
Proporciona un método unificado para el acceso al hardware gráfico a través
de diferentes plataformas.
Los controladores pueden ser compartidos entre distintas arquitecturas, lo
que disminuye la duplicación de código.
20
Capı́tulo 2. Contextualización
Proporciona control tanto para una como para varias pantallas activadas,
se soportan hasta 8 dispositivos framebuffer (8 pantallas).
El dispositivo framebuffer puede ser utilizado desde el espacio de usuario
accediendo al dispositivo especial /dev/fb[0-7], de la misma forma que se accede
al resto de dispositivos.
E/S normal : Se puede acceder a los contenidos del framebuffer mediante
las operaciones de lectura y escritura read() y write().
nmap: Utilizando la llamada nmap() se puede hacer mapping de los contenidos del framebuffer en la memoria de usuario, de forma que el framebuffer
se vuelve una pieza de memoria accesible. También se puede hacer mapping
de los registros MMIO en el espacio de usuario, pero para ello es necesario desactivar la aceleración hardware para la consola de texto para evitar
conflictos en el acceso a dichos registros.
ioclt: La llamada ioctl() permite realizar otro tipo de operaciones como cambiar el modo gráfico, obtener información sobre el hardware subyacente... Cada dispositivo framebuffer particular puede definir ioctls para
operaciones especificas del dispositivo de ser necesario.
No hay código genérico de aceleración gráfica en el kernel (solo para consola de
texto), todas las operaciones genéricas de aceleración hardware deberán hacerse
desde el espacio de usuario utilizando mapping sobre los registros MMIO.
2.3.3.
Microwindows
Microwindows [31] es un proyecto de Código Abierto que tiene como objetivo dotar las caracterı́sticas de los sistemas de ventanas modernos a dispositivos
y plataformas más pequeños. Permite desarrollar aplicaciones y probarlas en el
escritorio de Linux para luego compilarlas para el dispositivo deseado.
Proporciona dos APIs, uno compatible con el API del sistema de ventanas
de los sistemas Win32/WinCE, llamado Microwindows, y otro similar al de Xlib,
la biblioteca de desarrollo de aplicaciones para el sistema X Windows, llamado
Nano-X. Bajo Linux es más utilizado el API Nano-X ya que soporta la arquitectura cliente-servidor.
Actualmente funciona en sistemas Linux de 32 bits utilizando el soporte framebuffer del kernel (ver sección 2.3.2), bajo el sistema X Windows (útil para el
Programación gráfica en Linux
21
desarrollo) y utilizando la biblioteca SVGAlib 1 . Adicionalmente, Microwindows
ha sido portado para los sistemas operativos ELKS (Linux embebido de 16 bits),
MS-DOS, tanto en modo protegido como en modo real, y RTEMS (sistema operativo de tiempo real).
Las desventajas que presenta este framework con respecto a los objetivos que
se persiguen en este proyecto son:
Está orientado a la creación de interfaces con ventanas. La interfaz que
se pretende desarrollar está basada en un sistema de menús a pantalla
completa.
El API proporcionado no es orientado a objetos, esto complicarı́a la estructuración del código, obligando a la implementación de una biblioteca
propia para ocultar la utilización de este framework al resto del código de
la aplicación.
No proporciona operaciones de alto nivel como la reproducción de vı́deo,
aunque sı́ tiene operaciones para manipular imágenes y fuentes.
2.3.4.
Qt
Qt es un [32] framework para la creación de aplicaciones e interfaces gráficas
multiplataforma. Incluye una biblioteca C++ y herramientas para facilitar el desarrollo rápido de aplicaciones.
La biblioteca C++ proporciona una gran cantidad de clases completamente
funcionales e interfaces consistentes. Está desarrollada totalmente siguiendo el
paradigma de orientación a objetos.
Las aplicaciones hechas con Qt se ejecutarán nativamente en las plataformas
soportadas, las aplicaciones podrán ser portadas a nuevas plataformas simplemente recompilándolas con la biblioteca Qt correspondiente a la nueva plataforma.
Este framework se distribuye para cuatro tipos de sistemas:
Qt/Windows: Permite compilar las aplicaciones Qt para Microsoft Windows
95/98/Me, NT4, 2000 y XP.
Qt/X11 : Para Linux, Solaris, HP-UX, IRIX, AIX y otras variantes de UNIX.
1
biblioteca que facilita el acceso a hardware de vı́deo compatible con los estándares VGA y
SVGA.
22
Capı́tulo 2. Contextualización
Qt/Mac: Aplicaciones que funcionen sobre Apple Mac OS X.
Qt/Embedded : Sistemas Linux embebidos utilizando el framebuffer en lugar
del sistema X Windows como hace Qt/X11.
La solución estudiada para el desarrollo del proyecto fue Qt/Embedded.
Este framework no utiliza capas de emulación ni máquinas virtuales para posibilitar la portabilidad, cada edición es una implementación en código nativo del
API ofrecido.
La utilización de Qt ofrece las siguientes ventajas:
Independencia real de plataforma. Para este proyecto no es especialmente
importante, ya que la aplicación se desarrollará sólo para sistemas embebidos.
Acceso a bases de datos utilizando un API independiente para bases de
datos SQL. Esta caracterı́stica tampoco es necesaria para el software a
desarrollar en este proyecto.
Posibilidad de desarrollo rápido de aplicaciones. La plataforma Qt proporciona herramientas de productividad como QtDesigner, una herramienta
para desarrollar interfaces gráficas.
Soporta internacionalización.
Velocidad de ejecución (se ejecuta en código nativo).
Aspecto visual (look and feel ) configurable.
Incluye módulos de espacio de trabajo (soporte MDI), vista de iconos, redes,
XML, canvas, OpenGL, ...
Sin embargo, Qt fue rechazado por dos razones:
Está orientado al desarrollo de interfaces gráficas basadas en la utilización
de ventanas. Como se comentó en la sección anterior, la interfaz que se
desarrollará no será basada en ventanas, sino que estará formada por varios
menús a pantalla completa.
Es una solución demasiado “grande”. Está pensado para realizar aplicaciones con interfaces visuales complejas y que se puedan ejecutar en diferentes
plataformas. Para este proyecto interesa una solución más ajustada, para
respetar las restricciones de espacio presentadas por los Set Top Boxes.
DirectFB
2.4.
DirectFB
2.4.1.
Introducción
23
DirectFB [1] es una biblioteca ligera desarrollada teniendo en cuenta las necesidades especiales de los sistemas embebidos. Proporciona aceleración gráfica,
control y abstracción de dispositivos de entrada, sistema de ventanas integrado y
soporte de múltiples capas visuales sobre el dispositivo framebuffer de Linux.
Es una capa de abstracción completa, que incluye la utilización de operaciones software para aquellos casos en los que la operación no esté soportada por el
hardware subyacente.
También proporciona un sistema de ventanas integrado, que permite la creación de ventanas translúcidas.
Proporciona las siguientes operaciones gráficas aceleradas por hardware:
Dibujo/relleno de rectángulos.
Dibujo/relleno de triángulos.
Dibujo de lı́neas.
Copia de imágenes (blitting) tanto elástica como directa.
Mezcla con un canal alpha (alpha blending).
Mezcla con un valor alpha constante (alpha modulation).
Nueve funciones de mezcla para el origen y el destino respectivamente.
Modulación por color (colorizing).
Copia basada en color (color keying) tanto para el origen como para el
destino.
DirectFB tiene su propia gestión de recursos de memoria de vı́deo, de forma
que recursos como capas visuales, dispositivos de entrada o superficies pueden ser
bloqueados para acceso exclusivo. Proporciona abstracciones para los diferentes
objetos gráficos, tales como capas, superficies y ventanas. Esto permite la selección entre aplicaciones basadas en ventanas o aplicaciones pensadas para pantalla
completa.
24
Capı́tulo 2. Contextualización
El API de DirectFB está diseñado para facilitar la creación de plugins que
implementen las siguientes partes:
Aceleración gráfica: Las operaciones que no puedan ser realizadas por hardware (según la implementación existente para el hardware que se esté utilizando) serán realizadas por software. Soporta las tarjetas gráficas Matrox
G200/G400/G450/G550, ATI128, Vodoo 3, NeoMagic, nVidia RIVA TNT,
S3 Savage y Cyber Pro, aunque para algunas no está soportada la aceleración para todas las operaciones. Cualquier otra tarjeta puede ser utilizada
sin aceleración hardware, ya sea utilizando un dispositivo framebuffer especı́fico o el framebuffer genérico VESA.
Dispositivos de entrada: Permite abstraer el concepto de dispositivo de entrada, de forma que los programas no tengan que preocuparse del hardware
utilizado, sólo deberán tener en cuenta los eventos generados. Actualmente,
los dispositivos de entrada soportados son teclado, ratones serie y PS/2,
joysticks, pantallas táctiles y dispositivos remotos por infrarrojos.
Proveedor de imagen: Abstrae el concepto de imagen de forma que las aplicaciones no tengan que preocuparse del formato utilizado por la imagen que
desean mostrar. Soporta los formatos JPEG, PNG, GIF y MPEG2 I-Frame.
Proveedor de vı́deo: La misma idea, pero para archivos de vı́deo. Puede
reproducir vı́deos en formato MPEG1/2, AVI, Macromedia Flash y OpenquickTime. También soporta Video4Linux.
Proveedor de fuentes: Permite utilizar distintos tipos de fuente de forma
homogénea. Soporta el formato propio de fuentes de DirectFB y fuentes
TrueType.
2.4.2.
Arquitectura
DirectFB utiliza la interfaz proporcionada por el dispositivo framebuffer de
Linux para acceder al hardware gráfico. Hay controladores especiales para los
framebuffers de ciertos dispositivos integrados en el kernel de Linux, el resto de
dispositivos funcionarán también utilizando el controlador para el framebuffer
VESA, pero con ciertas limitaciones.
Independientemente de que se utilice aceleración gráfica o no, DirectFB
utilizará el dispositivo framebuffer para realizar las siguientes operaciones:
Establecer el modo de vı́deo (resolución, profundidad de color y temporizaciones).
DirectFB
25
Mapping de la memoria del framebuffer de la tarjeta.
Cambios en el área visible (viewport) del framebuffer para soportar doble
buffering. De esta forma se podrán realizar cambios en un área no visible
y hacerla visible de forma sı́ncrona con el barrido del monitor para evitar
efectos visuales desagradables.
Si la tarjeta está soportada por DirectFB y el controlador para el chipset
está presente en el kernel, DirectFB también usará el dispositivo framebuffer
para las siguientes tareas:
Mapping de memoria de los puertos de entrada/salida de las tarjetas.
Desactivación de la aceleración interna del dispositivo framebuffer.
Para ejecutar una operación gráfica especı́fica, el controlador de DirectFB
hará un mapping de los puertos de entrada/salida de la tarjeta en memoria para
transmitir la operación concreta a realizar. Como se dijo en la sección 2.3.2, la
aceleración hardware se programa completamente desde el espacio de usuario. Ver
la figura 2.6.
Aplicacion DirectFB
Espacio del Usuario
DirectFB
Controlador para
el chipset
Controlador para el dispositivo
framebuffer
framebuffer
registros de
temporizacion y
modo de video
Espacio del kernel
motor de
aceleracion
Hardware grafico
Solo se aplica cuando se utiliza la consola del framebuffer
Desactivado mientras se ejecute una aplicacion DirectFB
Figura 2.6: Acceso de una aplicación DirectFB al hardware gráfico
Para acceder a los dispositivos de entrada, DirectFB utiliza las interfaces
estándar que Linux proporciona para los distintos dispositivos. DirectFB no
accede nunca al hardware de entrada directamente.
26
Capı́tulo 2. Contextualización
2.4.3.
Términos importantes
Blitting: Se refiere al proceso de copiar datos de una imagen. El caso más simple
es copiar una imagen de una superficie a otra cuando ambas superficies tiene
el mismo tamaño y el mismo formato de pixel. En este caso solamente tiene
que ser copiada la memoria sin ningún tipo de procesado. Otros casos más
avanzados son la copia elástica (stretch blitting) entre superficies de distinto
tamaño, la copia con canal alpha o copia con cambio de formato de pixels.
La mayorı́a de las tarjetas gráficas incluyen un blitter hardware capaz de
realizar diversas operaciones de copia.
Superficie: Es un zona de memoria reservada en la que se almacena la información sobre los pixels de una imagen en un formato especı́fico. Una superficie
puede residir en la memoria de vı́deo y/o en la memoria del sistema. Es
posible realizar operaciones de dibujo sobre una superficie, o copiar una superficie en otra, como se explicó anteriormente. Adicionalmente cada superficie puede utilizar un doble buffer de gráficos, de forma que las operaciones
realizadas sobre ella sólo se vuelven válidas cuando se llama a una función
para cambiar el buffer visible.
En el modo de pantalla completa, la pantalla visible es representada por
la “Superficie Primaria”. Esta superficie normalmente dispondrá de doble
buffer para evitar efectos desagradables cuando se realicen operaciones sobre
ella.
Subsuperficie: Presenta el mismo interfaz que una superficie normal, pero no
tiene memoria reservada para sı́ misma.
Capa: Dependiendo del hardware gráfico, puede haber una o más capas visibles.
La mayorı́a de las tarjetas gráficas de PC sólo disponen de una capa, pero
la tarjeta de un Set Top Box puede soportar dos o más capas. Las capas
ocupan diferentes posiciones en la memoria de vı́deo y suelen ser combinadas
utilizando mezcla alpha, esto lo hace el hardware automáticamente.
Ventana: Normalmente, los contenidos de una superficie de una capa son controlados por el sistema de ventanas integrado que muestra las ventanas
pertenecientes a la capa sobre un fondo configurable. Cada ventana tiene
su propia superficie que es utilizada por el sistema de ventanas para componer la imagen de ventanas superpuestas. Alternativamente, las aplicaciones
pueden obtener acceso exclusivo a la capa, de esta forma la aplicación tiene
acceso directo a la capa y a su contenido, no se muestran ventanas ni fondos.
Esta será la forma en la que se desarrollará el software de este proyecto.
DirectFB
2.4.4.
27
API
El API de DirectFB está estructurado utilizando interfaces. Una interfaz es
una estructura C que contiene punteros a funciones. Dependiendo de la implementación de dicha interfaz, estos punteros apuntarán a distintas funciones. Por
ejemplo IDirectFBSurface puede representar la pantalla completa, los contenidos de una ventana o una subsuperficie.
IDirectFB es la “Super Interfaz”. Es la única que puede ser creada por una
función global (DirectFBCreate). El resto de interfaces serán creadas llamando
a funciones de IDirectFB o a funciones de interfaces ya creadas. Se puede acceder a las interfaces existentes (por ejemplo los dispositivos de entrada) a través
de IDirectFB. En la figura 2.7 se puede observar la relación entre las distintas
interfaces de DirectFB.
«super interface>>
IDirectFB
Obtener
Crear
Crear
IDirectFBDisplayLayer
IDirectFBSurface
Obtener
IDirectFBInputDevice
Obtener
Crear
IDirectFBWindow
Obtener
Crear
IDirectFBInputBuffer
IDirectFBImageProvider
IDirectFBVideoProvider
IDirectFBFont
Figura 2.7: Relación entre las interfaces de DirectFB
Ciertas interfaces están implementadas como módulos que serán cargados en
tiempo de ejecución en caso de que sea necesario. Las interfaces implementadas de esta forma son IDirectFBImageProvider, IDirectFBVideoProvider e
IDirectFBFont. La aplicación cargará la implementación necesaria en cuando se
construya la interfaz.
Otras interfaces también implementadas como módulos serán cargadas durante la inicialización de la aplicación. Estas interfaces son IDirectFBInputDevice y
GfxCard (interfaz interna inaccesible para las aplicaciones, que contiene el código
encargado de utilizar la aceleración hardware en caso de que sea posible).
De esta forma se podrán añadir nuevas implementaciones de controlador de
aceleración, dispositivos de entrada y proveedores de medios sin necesidad de
recompilar ni DirectFB ni la aplicación.
Capı́tulo 3
Análisis
Índice General
3.1.
3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . .
29
3.2. Protocolo de comunicación . . . . . . . . . . . . . . . .
30
3.3. Set Top Box
. . . . . . . . . . . . . . . . . . . . . . . .
31
3.4. Aplicación DFBCIn . . . . . . . . . . . . . . . . . . . .
32
3.4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
32
3.4.2. Módulo gráfico . . . . . . . . . . . . . . . . . . . . . .
32
3.4.3. Parser . . . . . . . . . . . . . . . . . . . . . . . . . . .
34
3.4.4. Conclusiones . . . . . . . . . . . . . . . . . . . . . . .
35
3.5. Servidor XML (VXMLS) . . . . . . . . . . . . . . . . .
36
3.6. Ciclo de Desarrollo . . . . . . . . . . . . . . . . . . . .
37
3.6.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
37
3.6.2. Prototipo . . . . . . . . . . . . . . . . . . . . . . . . .
37
3.6.3. DFBCIn . . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.6.4. VXMLS . . . . . . . . . . . . . . . . . . . . . . . . . .
40
3.6.5. Prueba del sistema conjunto . . . . . . . . . . . . . . .
40
Introducción
El objetivo principal de este proyecto es el desarrollo de un sistema que permita al usuario final el acceso de forma controlada a los medios disponibles en el
29
30
Capı́tulo 3. Análisis
servidor de VoD VoDKA.
Por lo tanto, se necesita un dispositivo que actúe como mediador entre el usuario y el servidor VoD. Es deseable que dicha interacción pueda ser controlada de
forma remota y centralizada, de forma que pueda adaptarse según las condiciones
del entorno. Esto nos lleva a la necesidad de implementar otro dispositivo adicional que permita definir el protocolo de interacción que se presentará al usuario.
Ası́ pues, este proyecto se analizará dividido en dos subproyectos: el desarrollo
de un Set Top Box y el de un servidor que proporcione a dicho Set Top Box la
información necesaria para controla la interacción con el usuario.
Esta separación supone la necesidad de definir un protocolo de comunicación
entre el Set Top Box y el servidor de “definiciones de interfaz”. Para establecer dicho protocolo, se han utilizado los estándares HTTP [35] y XML [36], lo
que permitirá la utilización de herramientas ya existentes para el desarrollo del
software necesario.
3.2.
Protocolo de comunicación
Para definir el protocolo de comunicación entre el Set Top Box y el servidor
de “definiciones de interfaz” se necesita especificar:
El sistema de comunicación: Se utilizará el protocolo HTTP sobre una conexión TCP convencional. El Set Top Box actuará como cliente, solicitando
la información que necesite mediante peticiones HTTP al servidor.
La estructura de la información: Se utilizará el estándar XML. El servidor
devolverá la información requerida por el cliente enviando un documento
XML en una respuesta HTTP.
El estándar XML permite especificar la estructura que debe tener un documento de forma independiente de su contenido. Hay diferentes formas de expresar
la estructura de un documento XML, en este proyecto se utilizarán DTDs [36]
para definir la forma en la que los documentos deben expresar la información
requerida.
El servidor de “definiciones de interfaz” (servidor XML de aquı́ en adelante)
necesitará expresar dos tipos de respuestas: la definición de la interfaz solicitada
por el Set Top Box y los mensajes de confirmación o de error para el resto de
Aplicación DFBCIn
31
operaciones que el Set Top Box pueda solicitarle (por ejemplo, iniciar la sesión).
La estructura del documento de confirmación simplemente tiene que comprobar dos casos: éxito o fracaso.
Los documentos utilizados para definir la interfaz son bastante más complejos.
Deben poder expresar tanto el aspecto visual como la funcionalidad a realizar por
la interfaz definida. Por lo tanto, debe diseñarse una estructura que sea fácilmente
extensible, para no constreñir el aumento de funcionalidades del Set Top Box.
3.3.
Set Top Box
El desarrollo del Set Top Box debe tener como objetivo un sistema que sea
capaz de realizar las siguientes tareas:
Comunicarse con el servidor VXMLS para obtener la información necesaria
sobre la interfaz a presentar al usuario. Esta interfaz debe lo suficientemente
clara y sencilla, de forma que no suponga ninguna dificultad para cualquier
tipo de usuario.
Presentar dicha interfaz al usuario en un televisor convencional y permitirle
interactuar con ella a través de un mando a distancia.
Reproducir vı́deo recibido del servidor VoD VoDKA.
Debe ser un sistema razonablemente económico.
El software se desarrollará de forma que no sea dependiente de la plataforma,
deberá ser posible ejecutarlo en cualquier Set Top Box que disponga de una salida
para televisión y una tarjeta de red.
Como sistema operativo se utilizará Linux. Para respetar las restricciones de
espacio será necesario evitar el sistema X Windows, por lo que la programación
gráfica se hará accediendo directamente al hardware mediante el dispositivo framebuffer de Linux (ver la sección 2.3).
Para controlar la interacción con el usuario y la comunicación con el servidor
XML se desarrollará una única aplicación llamada DFBCIn (DirectFB C Interface).
La comunicación con el servidor VoD se ocultará al resto del sistema utilizando
el proyecto desarrollado en [37] que permite manejar un vı́deo servido con el
método de streaming como si se tratara de un fichero local.
32
Capı́tulo 3. Análisis
3.4.
Aplicación DFBCIn
3.4.1.
Introducción
El objetivo de esta aplicación es el de controlar la interacción entre el usuario
y los contenidos del servidor de streaming VoDKA.
Se puede dividir el análisis de esta aplicación en tres módulos que deberán
estar bien desacoplados:
El módulo gráfico: Se encargará de controlar el aspecto gráfico de la interfaz
mostrada al usuario. Como se verá en la sección 3.4.2, este módulo también
deberá controlar los dispositivos de entrada.
El módulo VXMLS : Se encargará de enviar las peticiones necesarias a
VXMLS y de transformar los ficheros XML recibidos en una estructura
de datos comprensible para el resto de la aplicación. Para ello deberá implementar un parser para interpretar los documentos recibidos.
El motor de la aplicación: Se encargará de ejecutar las acciones solicitadas
por el usuario. Este módulo accederá a los dos anteriores a través de un
API definido y estable.
3.4.2.
Módulo gráfico
Para la programación gráfica se ha elegido la librerı́a DirectFB [1] (en la
sección 2.4 se hace una pequeña introducción). El desarrollo se hará utilizando
el lenguaje de programación de medio nivel C. De esta forma, se aligera notablemente la carga, tanto de ejecución como de espacio, pero la codificación se
vuelve mucho más complicada que si se utilizara un lenguaje de más alto nivel y
el sistema X Windows.
Durante la fase de análisis se ha desarrollado una pequeña aplicación para
validar la utilidad de la biblioteca DirectFB, de acuerdo con los objetivos del
proyecto. En dicha aplicación se comprobó el comportamiento de DirectFB en
una aplicación multi-hilo en la que se puede reproducir un vı́deo elegido mediante
un menú gráfico. En esta aplicación se resumı́a la funcionalidad que es necesario
implementar en la aplicación DFBCIn con la ayuda de esta biblioteca, obviando
el resto de objetivos que se implementarán de forma independiente a la utilización
de DirectFB. En la figura 3.1 se muestra el ciclo de interacción implementado
en este prototipo.
Aplicación DFBCIn
33
También se han realizado pequeñas aplicaciones de prueba para comprobar
que DirectFB permite el acceso concurrente de varios hilos de programación a
sus recursos, que es capaz de reproducir varios vı́deos al mismo tiempo (se comprobó que sólo puede reproducir varios vı́deos si sólo uno contiene audio) y su
comportamiento ante fallos de la aplicación.
Figura 3.1: Prototipo de interfaz con el usuario
Además, para el desarrollo de la aplicación anterior se tomaron como referencia algunas aplicaciones ya existentes implementadas utilizando DirectFB, lo
que ayudó a afrontar la codificación de DFBCIn con buen conocimiento del API
y del estilo de codificación a seguir en la parte gráfica.
La utilización de DirectFB proporciona algunas ventajas interesantes:
Está diseñada siguiendo el paradigma de orientación a objetos, lo que facilita
la traducción del diseño a la codificación.
Proporciona numerosas abstracciones que desacoplan el código desarrollado
de aspectos tales como la aceleración hardware o la decodificación de vı́deo.
Permite el control de diversos dispositivos de entrada, entre ellos de puerto
de infrarrojos. De esta forma, para el código desarrollado, no hay diferencia
entre el mando a distancia y el teclado.
El API es claro, sencillo y orientado a objetos. La documentación, aunque
no excelente, es suficiente y está actualizada.
34
Capı́tulo 3. Análisis
Pero también trae consigo una serie de contratiempos, la mayorı́a derivados
de la utilización de un lenguaje de bajo nivel y con numerosos efectos colaterales
como es C:
Es necesario programar de forma explı́cita la reserva y liberación de memoria, ya que C no aporta un sistema de “recolección de basura”.
C no es orientado a objetos, la traducción de los objetos del diseño es más
complicada que si se utilizara un lenguaje moderno de más alto nivel.
La combinación de los dos puntos anteriores hace que la compartición del
mismo objeto entre distintos módulos de la aplicación tenga que ser controlada de forma explı́cita (es necesario asegurarse de que el objeto se libera
solamente una vez cuando deje de ser necesario).
El lenguaje C también obliga a hacer explı́cito el control de errores ya que
no aporta ningún sistema de control de excepciones.
La biblioteca DirectFB está actualmente en estado de desarrollo, por lo
que será necesario tener un especial cuidado en el diseño para asegurar que
los cambios sufridos por dicha biblioteca sólo afecten al módulo gráfico de
DFBCIn y no se propaguen por el resto de la implementación.
DirectFB controla tanto el hardware gráfico como los dispositivos de entrada. Esto hace imposible separar fı́sicamente los módulos de entrada de
los de salida en el diseño del sistema. El módulo de entrada queda definitivamente ligado a la utilización o no de DirectFB en el de salida. Como es
necesario independizar el módulo gráfico del resto de la aplicación, el diseño
del sistema de entrada quedará vinculado al módulo gráfico.
Un fallo en el proceso de liberación de recursos puede hacer que DirectFB
deje el sistema inestable, siendo necesaria su recuperación de forma remota.
3.4.3.
Parser
Para interpretar los documentos XML recibidos del servidor VXMLS es necesario implementar un parser, para ello se ha utilizado la biblioteca expat [2]
que proporciona una implementación de un parser XML genérico que se puede
particularizar utilizando la idea del patrón Strategy [38].
Aplicación DFBCIn
35
VXMLS puede devolver dos tipos de documentos, los utilizados para especificar la interfaz con el usuario1 y los que certifican el éxito de operaciones sin
resultado. En la figura 3.2 se puede ver cómo se pueden obtener distintos parsers
utilizando expat.
XML_Parser
+XML_parse(): UserData
#startElement(userData:UserData,tagName:String,attributes:Array)
#EndElement(userData:UserData,tagName:String)
#characterData(userData:UserData,text:String,length:Integer)
MenuParser
+parse(stream:CharacterStream): Menu
-startElement(userData:UserData,tagName:String,attributes:Array)
-EndElement(userData:UserData,tagName:String)
-characterData(userData:UserData,text:String,length:Integer)
VXMLSParser
+parse(stream:CharacterStream): Boolean
-startElement(userData:UserData,tagName:String,attributes:Array)
-EndElement(userData:UserData,tagName:String)
-characterData(userData:UserData,text:String,length:Integer)
Figura 3.2: Creación de parsers con expat
La aproximación a este diseño utilizando el lenguaje C presenta algunas complicaciones. La solución propuesta por expat proporciona una función que devuelve un objeto de tipo XML Parser en el que se deben instalar unos manejadores
para los eventos startElement, endElement y characterData.
Dado que la estructura de los documentos a analizar puede ser arbitrariamente
profunda, será necesario utilizar una pila para ir almacenando la información a
medida que el parser va llamando los manejadores proporcionados. Además, el
número de etiquetas que pueden ser utilizadas en los documentos que definen la
interfaz es bastante elevado (y aumentará a medida que aumente la funcionalidad
del sistema), por lo que se deberá poner especial cuidado en la estructuración del
código del parser para evitar la duplicación del mismo. También se debe tener en
cuenta que el diseño debe quedar abierto de forma que se pueda añadir fácilmente
el código necesario para interpretar las nuevas etiquetas que vayan surgiendo a lo
largo del ciclo de vida del proyecto.
3.4.4.
Conclusiones
El diseño de la aplicación DFBCIn debe perseguir los siguientes objetivos:
Aislar lo más posible la utilización de DirectFB para minimizar el impacto
de los posibles cambios en dicha biblioteca.
1
La especificación de la interfaz se hará definiendo una sucesión de menús, como se verá en
apartados posteriores. Por ello, el parser que se utilizará para este tipo de documentos devuelve
objetos Menu
36
Capı́tulo 3. Análisis
La comunicación con VXMLS también deberá ser desacoplada al máximo
del resto de la aplicación para evitar que los cambios en VXMLS se propaguen por el código de DFBCIn.
Permitir la extensión de la funcionalidad ofrecida con poco esfuerzo.
A pesar de que el desarrollo se haga en C, el diseño será objetual.
3.5.
Servidor XML (VXMLS)
El objetivo de esta aplicación es atender las peticiones de las aplicaciones
DFBCIn residentes en Set Top Boxes remotos, responderles con el correspondiente documento XML y mantener una base de datos con información, tanto de
los medios disponibles en VoDKA como información referente a los usuarios.
La funcionalidad ofrecida por el servidor será muy básica, simplemente la necesaria para comprobar el correcto funcionamiento de la aplicación DFBCIn y
demostrar las posibilidades que ofrece el hecho de mantener la información sobre
la interfaz con el usuario centralizada en un servidor remoto.
El desarrollo del servidor se ha hecho utilizando el lenguaje funcional Erlang [3] y algunas herramientas proporcionadas por la plataforma de desarrollo
Erlang/OTP [4]. Concretamente: el contenedor de aplicaciones HTTP Inets [39],
el sistema gestor de bases de datos distribuido Mnesia [40] y el lenguaje de consulta Mnemosyne [41].
La aplicación VXMLS debe ser capaz de realizar las siguientes operaciones:
Mantener sesiones para los diferentes usuarios conectados.
Mantener información sobre los usuarios en la base de datos.
Mantener información sobre los medios disponibles en VoDKA.
Generar documentos XML de forma dinámica, dependiendo del contexto
actual.
Permitir a los usuarios configurar sus preferencias.
Las posibilidades de personalización implementadas son la selección de idioma
y la selección de perfil, de forma que cada Set Top Box puede mantener información sobre varios usuarios.
Ciclo de Desarrollo
37
La definición de interfaz que debe proporcionar VXMLS permitirá acceder
a las posibilidades de personalización anteriores y a la reproducción de vı́deos,
que se mostraran clasificados como novedades, vistos y antiguos. Dentro de cada
una de estas clasificaciones, los vı́deos estarán clasificados en géneros. El servidor
también deberá encargarse de mover los vı́deos vistos por el usuario a la sección
vistos.
3.6.
Ciclo de Desarrollo
3.6.1.
Introducción
Una vez establecidos los requisitos que debe cumplir el sistema final, se debe
elegir el ciclo de desarrollo que se seguirá para llevar a cabo este proyecto:
1. Desarrollo de un prototipo que demuestre la utilidad de DirectFB. El
prototipo diseñado deberá dibujar menús, controlar la entrada del usuario y
reproducir vı́deo. Estas son las operaciones que serán implementadas con la
ayuda de DirectFB en la aplicación DFBCIn. También se ha codificado un
parser mı́nimo para comprobar la utilidad de la biblioteca expat empleada
en la codificación del parser XML.
2. Desarrollo de una primera versión de la aplicación DFBCIn que trabaje
con ficheros XML estáticos en lugar de obtenerlos del servidor dinámico
VXMLS.
3. Desarrollo de la aplicación VXMLS.
4. Desarrollo de un nuevo incremento de DFBCIn que se comunique con
VXMLS para obtener la información sobre la interfaz con el usuario a utilizar.
5. Refinamiento de DFBCIn y de VXMLS en conjunto.
3.6.2.
Prototipo
Para comprobar que realmente se puede llevar a cabo la implementación gráfica utilizando DirectFB se desarrolló un prototipo en el que solamente se programó la parte gráfica de una posible aplicación para el Set Top Box.
Este prototipo simplemente muestra una serie de menús (que no están especificados por ninguna fuente externa) que permiten la elección de una imagen de
38
Capı́tulo 3. Análisis
fondo o la reproducción de un vı́deo.
Para la programación de este prototipo se ha consultado el código fuente de
algunas aplicaciones de ejemplo implementadas utilizando DirectFB. Una de
ellas es el reproductor de vı́deo DFBSee (figura 3.3) y otra es DFBPoint (figura
3.4), que muestra una presentación de transparencias a partir de una especificación XML.
Figura 3.3: Aplicación DFBSee
En el código de la aplicación DFBSee se puede ver cómo programar un sistema
que reproduzca vı́deo y permita al usuario realizar acciones tales como pararlo,
avanzarlo o ver una barra de progreso.
La aplicación DFBPoint resulta útil para hacerse una idea de cómo programar
la presentación de menús. Implementa un parser especı́fico para los documentos
XML que utiliza, por lo que esta parte no se tendrá en cuenta para la aplicación
DFBCIn, en la que se utilizará la biblioteca expat para la implementación del
parser.
Tras el estudio de estas dos aplicaciones, se ha desarrollado el prototipo, lo
que permite afrontar el diseño de la aplicación DFBCIn con los conocimientos
necesarios para ocultar lo más posible la utilización de DirectFB a los módulos
que no precisan el acceso al hardware gráfico.
Ciclo de Desarrollo
39
Figura 3.4: Aplicación DFBPoint
Para verificar la utilidad de la biblioteca expat se ha codificado un pequeño
programa que muestra por pantalla los distintos eventos que detecta el parser
genérico proporcionado por expat. La implementación del parser para DFBCIn
deberá sustituir estos mensajes por las operaciones a realizar.
3.6.3.
DFBCIn
Para el desarrollo de la aplicación DFBCIn se ha seguido el paradigma de
programación incremental. En primer lugar se desarrollará el núcleo de la aplicación y a partir del momento en que el núcleo haya sido validado con las pruebas
correspondientes, se continuará con el desarrollo de nuevos incrementos que vayan
aumentando la funcionalidad proporcionada por la aplicación.
El paradigma de desarrollo incremental se adapta bien al tipo de software a
desarrollar, ya que uno de los requisitos planteados para la aplicación DFBCIn
es que se pueda extender la funcionalidad ofrecida a medida que vayan surgiendo nuevas ideas o necesidades. Seguir un desarrollo incremental obliga de forma
explı́cita a que el diseño soporte la creación de nuevas versiones con nuevas caracterı́sticas a partir de las versiones anteriores.
Uno de los principales problemas del desarrollo incremental es la definición
40
Capı́tulo 3. Análisis
de lo que será el núcleo de la aplicación. Para el caso de DFBCIn, el primer
incremento tendrá como objetivo un sistema que pueda interpretar documentos
XML estáticos localizados en ficheros locales que definan una interfaz muy sencilla en la que lo único que se pueda hacer sea la navegación por los menús. En
este incremento ya se contemplan varias de las funcionalidades importantes que
se esperan de DFBCIn: la interpretación de documentos XML, la presentación
gráfica de menús y el control de entrada por parte del usuario. Se dejarán para
incrementos posteriores la comunicación con un sistema remoto para la obtención
de los documentos XML y la reproducción de vı́deo.
3.6.4.
VXMLS
Puesto que el desarrollo de VXMLS se empezará cuando el desarrollo de
DFBCIn ya esté suficientemente avanzado, a esas alturas se se tendrá una definición bastante estable de la estructura de los documentos XML a servir y de
las posibilidades esperadas de la interfaz definida. Por lo tanto, el desarrollo del
servidor VXMLS podrá hacerse en un único incremento con sus correspondientes
etapas de análisis, diseño, codificación y prueba.
3.6.5.
Prueba del sistema conjunto
Una vez terminado el desarrollo de DFBCIn y de VXMLS, se realizaron las
pruebas de ambos sistemas en conjunto (una vez hechos los cambios necesarios en
DFBCIn para que se comunique correctamente con VXMLS), tras las cuales se
ha desarrollado el último incremento de ambas aplicaciones hasta tener el sistema
final funcionando correctamente.
Capı́tulo 4
Diseño
Índice General
4.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . .
42
4.2. Definición de la interfaz y objetos del dominio . . . .
42
4.2.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
42
4.2.2. La clase Menu . . . . . . . . . . . . . . . . . . . . . .
43
4.2.3. La interfaz Action . . . . . . . . . . . . . . . . . . . .
45
4.3. Documentos XML . . . . . . . . . . . . . . . . . . . . .
48
4.3.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
48
4.3.2. Descripción de los menús . . . . . . . . . . . . . . . .
48
4.4. Protocolo de comunicación . . . . . . . . . . . . . . . .
54
4.5. Diseño de DFBCIn . . . . . . . . . . . . . . . . . . . .
55
4.5.1. División en subsistemas . . . . . . . . . . . . . . . . .
55
4.5.2. Interacción entre módulos . . . . . . . . . . . . . . . .
56
4.5.3. Objetos comunes . . . . . . . . . . . . . . . . . . . . .
58
4.5.4. Diseño del motor de la aplicación . . . . . . . . . . . .
59
4.5.5. Diseño del módulo VXMLS . . . . . . . . . . . . . . .
65
4.5.6. Diseño del módulo gráfico . . . . . . . . . . . . . . . .
71
4.6. Diseño de VXMLS . . . . . . . . . . . . . . . . . . . . .
75
4.6.1. Introducción . . . . . . . . . . . . . . . . . . . . . . .
75
4.6.2. Objetos del dominio . . . . . . . . . . . . . . . . . . .
76
4.6.3. Modelo entidad-relación . . . . . . . . . . . . . . . . .
78
4.6.4. Diseño de la capa modelo . . . . . . . . . . . . . . . .
78
41
42
Capı́tulo 4. Diseño
4.6.5. Diseño de la vista y el controlador . . . . . . . . . . .
4.1.
80
Introducción
Antes de proceder al diseño de las aplicaciones VXMLS y DFBCIn y del
sistema que utilizarán para comunicarse, es necesario describir qué es lo que se
entenderá por “descripción de interfaz”, es decir, hay que especificar qué es lo
que VXMLS tiene que enviar a DFBCIn.
Una vez establecida la definición de lo que se entenderá cómo “interfaz”, se
debe hacer el diseño de los objetos que conformarán el dominio (los objetos comunes a ambas aplicaciones).
Tras los dos pasos anteriores ya se tendrá hecho el diseño de la parte común
a VXMLS y DFBCIn. Será necesario diseñar el protocolo de comunicación que
ambas deberán cumplir. Una vez hecho esto se podrá comenzar el diseño de ambos
sistemas por separado.
4.2.
Definición de la interfaz y objetos del dominio
4.2.1.
Introducción
Como ya explicó en la sección 3.1, la aplicación VXMLS debe proporcionarle
a DFBCIn una especificación de la interfaz a utilizar para comunicarse con el
usuario. Antes de empezar el diseño de cualquier otra parte del sistema es necesario definir con claridad cómo se va a describir exactamente dicha interfaz con
el usuario.
Se pretende que la interfaz sea sencilla y que el usuario interactúe utilizando
un mando a distancia. También es deseable que el número de botones necesario
para acceder a la funcionalidad básica sea mı́nimo.
Por lo tanto, la idea inicial de interfaz con el usuario será una sucesión de
menús en los cuales el usuario deberá elegir entre las opciones disponibles. La
interfaz será una generalización de la desarrollada para el prototipo implementado
en la fase de análisis (ver la figura 3.1).
Definición de la interfaz y objetos del dominio
4.2.2.
43
La clase Menu
Ası́ pues, se entenderá como “descripción de interfaz” la descripción de la
sucesión de menús que se irán mostrando al usuario. La clase Menu será la que
defina los objetos que utilizará VXMLS para indicarle a DFBCIn la interfaz que
deberá mostrar. Es decir, VXMLS generará objetos Menu y los transmitirá serializados (en forma de documentos XML) a la aplicación cliente DFBCIn. En la
figura 4.1 se muestra una primera aproximación a la clase Menu.
Menu
Option
1..∗ + text: String
− header: String
+ draw()
+ execute()
Figura 4.1: Primera aproximación a la clase Menu
Sin embargo, siguiendo este diseño, la información sobre las acciones a realizar por cada opción estarı́a codificadas en el método execute, lo que obligarı́a a
especializar una nueva clase para cada opción de cada menú.
Aplicando el patrón de diseño Command [38], se definirá una nueva interfaz
Action, que representará una acción genérica a realizar cuando se selecciona una
opción. En la figura 4.2 se puede ver la aproximación utilizada para definir los
objetos Option.
Menu
Option
1..∗ + text: String
− header: String
+ draw()
for all actions do
action.execute
end
+ execute()
1..∗
<<interface>>
Action
+ execute()
NextMenuAction
PreviousMenuAction
− nextMenu: String
− depth: Int
+ execute()
+ execute()
Figura 4.2: Segunda aproximación a la clase Menu
La interfaz Action proporciona la extensibilidad que se exigió en el análisis
44
Capı́tulo 4. Diseño
referente a la funcionalidad ofrecida por el Set Top Box. En los diferentes subsistemas que componen el proyecto se deberá implementar este diseño preservando
la posibilidad de añadir nuevas acciones manteniendo una interfaz común, de forma que estas nuevas acciones no afecten al resto del sistema.
Además, la interfaz Action será utilizada para definir las acciones a realizar
en otros casos, además de la ejecución de opciones seleccionadas por el usuario.
El diseño definitivo de la clase Menu es el especificado en la figura 4.3
Menu
+ majorVersion: Int
+ minorVersion: Int
− header: String
− time: Int
Option
1..∗ text: String
+ execute()
+ draw()
for all actions do
action.execute
end
1..∗
<<interface>>
initActions
timeActions
0..∗
0..*
Action
+ execute()
NextMenuAction
PreviousMenuAction
− nextMenu: String
− depth: Int
+ execute()
+ execute()
Figura 4.3: Diseño definitivo de la clase Menu
Los atributos majorVersion y minorVersion se utilizarán para controlar las
nuevas implementaciones de la clase Menu que VXMLS vaya siendo capaz de
servir. En el caso de que DFBCIn reciba un menú de una versión mayor a la que
sea capaz de soportar deberá actualizar su implementación de forma que pueda
interpretar las nuevas caracterı́sticas de la clase Menu (idealmente, estas nuevas
caracterı́sticas sólo deberı́an ser nuevas implementaciones de la interfaz Action).
Las acciones initActions se ejecutarán cada vez que DFBCIn cargue un nuevo objeto de la clase Menu.
El atributo time indicará (en caso de que sea necesario) el tiempo a esperar
desde que se carga el menú hasta que se ejecuten las acciones timeActions. La
espera se reiniciará cada vez que se reciba alguna entrada por parte del usuario.
Definición de la interfaz y objetos del dominio
4.2.3.
La interfaz Action
4.2.3.1.
Acciones implementadas
45
El primer incremento de la aplicación DFBCIn solamente pretende conseguir
una interfaz en la que el usuario pueda navegar por los menús, sin ninguna otra
posibilidad. Para ello, inicialmente sólo se necesitará implementar una acción para pasar a un nuevo menú y otra para volver a menús anteriores (NextMenuAction y PreviousMenuAction respectivamente, inicialmente ChangeOptionAction
estará codificada directamente como respuesta a la pulsación de las teclas arriba
y abajo).
Además de las dos acciones citadas anteriormente, el sistema final contiene
las siguientes implementaciones de Action:
PlayVideoAction: Inicia la reproducción de un vı́deo.
StopVideoAction: Detiene la reproducción en curso.
ResumeVideoAction: Continúa con la reproducción de vı́deo.
SeekVideoAction: Salta a un momento especı́fico del vı́deo que se está reproduciendo.
UnloadVideoAction: Termina la reproducción de vı́deo.
HideMenuAction: Oculta el menú de forma que no sea visible en la pantalla.
ReloadMenuAction: Vuelve a solicitar el menú actual a VXMLS.
ShowMenuAction: Muestra el menú actual en la pantalla.
ChangeOptionAction: Cambia la opción seleccionada en el menú actual.
BindKeyAction: Enlaza una tecla a un grupo de acciones a realizar en caso
de que el usuario la pulse.
SetPropertyAction: Cambia el valor de un parámetro de configuración.
ShowTextAction: Muestra texto en la pantalla.
HideTextAction: Oculta el texto mostrado en la pantalla.
LoadMp3Action: Inicia la reproducción de un fichero de audio mp3.
PauseContinueMp3Action: Detiene o continua la reproducción de audio.
KillMp3Action: Elimina el reproductor de mp3 para que permitir a otros
subsistemas utilizar la tarjeta de sonido.
46
4.2.3.2.
Capı́tulo 4. Diseño
Acciones para el manejo de menús
<<interface>>
Action
+ execute()
ReloadMenuAction
HideMenuAction
ShowMenuAction
NextMenuAction
PreviousMenuAction
+ execute()
+ execute()
+ execute()
− nextMenu: String
− arguments: Array
− returnDepth: Int
− reinit: Bool
+ execute()
+ execute()
Figura 4.4: Acciones para el manejo de menús
En la figura 4.4 está representado el diseño de las acciones a implementar para
controlar la navegación por los menús de la interfaz.
ReloadMenuAction, ShowMenuAction y HideMenuAction solamente deberán
realizar acciones sobre el menú actual y no necesitan ningún tipo de atributos.
NextMenuAction tiene como atributos el nombre del menú a cargar (DFBCIn
solicitará un menú con ese nombre a VXMLS) y una lista de argumentos. La lista
de argumentos se añadió al diseño durante la etapa de diseño de VXMLS, para
permitir generalizar la generación de menús similares, por ejemplo, para generar
un menú en el que las opciones sean una lista de pelı́culas, DFBCIn solicitará un
menú lista de pelı́culas con los atributos género y filtro.
La acción PreviousMenuAction tiene como atributos returnDepth, que indica el número de menús que se debe volver atrás y reinit, para indicar si se
debe reinicializar el menú al que se retorna o se deben mantener sus propiedades
actuales.
4.2.3.3.
Acciones para el control de vı́deos
En la figura 4.5 se puede ver el diseño de las acciones utilizadas para controlar
la reproducción de vı́deo.
Las acciones ResumeVideoAction, UnloadVideoAction y StopVideoAction no
tienen ningún parámetro, simplemente deben actuar sobre la reproducción en
curso.
La acción SeekVideoAction tiene como atributos mode, que indica cómo se
quiere posicionar el vı́deo (puede ser forward para avanzar, backward para retro-
Definición de la interfaz y objetos del dominio
<<interface>>
Action
47
1.. ∗
endActions
+ execute()
SeekVideoAction
ResumeVideoAction
StopVideoAction
UnloadVideoAction
PlayVideoAction
− mode: String
+ execute()
+ execute()
+ execute()
− video: String
− time: Int
+ execute()
+ execute()
Forward, Backward o Absolute
Figura 4.5: Acciones para el control de vı́deos
ceder o absolute para ir a un momento especı́fico de la reproducción), y time, que
indica o bien el tiempo que se debe avanzar o retroceder, o bien el momento al
que se debe saltar.
La acción PlayVideoAction es una aplicación del patrón Composite [38]. Como
atributo tiene el nombre del vı́deo a reproducir (video). Al finalizar la reproducción del vı́deo, se ejecutaran las acciones endActions.
4.2.3.4.
Otras acciones
En la figura 4.6 se describe el diseño del resto de acciones que se han implementado para el sistema final.
<<interface>>
0 .. ∗
Action
keyActions
ACTIONS
NULL
EXECUTE
+ execute()
KillMp3PlayerAction
+ execute()
SetPropertyAction
ChageOptionAction
− name: String
− absolute: Bool
− type: String
− value: Int
− value: Int
− key: String
+ execute()
+ execute()
+ execute()
ShowTextAction
PauseContinueMp3Action
LoadMp3Action
HideTextAction
− lines: Array
+ execute()
− path: String
+ execute()
+ execute()
BindKeyAction
+ execute()
Figura 4.6: Resto de acciones del sistema
SetPropertyAction tiene como atributos el nombre de la propiedad a modificar
(name) y el nuevo valor que debe tomar esa propiedad (value).
48
Capı́tulo 4. Diseño
ChangeOptionAction tiene como atributos un entero y un valor booleano para
indicar si el valor value se debe sumar a la opción actual o si es la acción que
hay que seleccionar.
ShowTextAction tiene como atributos un array de lı́neas de texto.
LoadMp3Action tiene como atributo la ruta completa del fichero mp3 a reproducir.
Las acciones HideTextAction, PauseContinueMp3Action y KillMp3Player no
necesitan atributos para ser ejecutadas.
4.3.
Documentos XML
4.3.1.
Introducción
Se necesitan dos tipos de documentos XML, uno de ellos se utilizará para
confirmar aquellas operaciones para las que VXMLS no devuelve un objeto de la
clase Menu y otro para interpretar las descripciones de los objetos Menu.
La estructura de estos documentos que describen respuestas de VXMLS es
muy simple, consisten en la etiqueta raı́z vxmls con, bien una etiqueta ok anidada para confirmar la operación, bien una etiqueta error con un mensaje de error
anidado para indicar un fallo en la operación.
La estructura de los documentos que describen un objeto de la clase Menu
son bastante más complejos y se explicarán en la siguiente sección.
En el apéndice A se pueden ver los DTDs que especifican la estructura de
ambos documentos.
4.3.2.
Descripción de los menús
4.3.2.1.
Introducción
La estructura de los documentos XML utilizada para la descripción de los
menús debe atenerse al hecho de que el número de acciones irá aumentando con
el paso del tiempo.
Documentos XML
49
La interfaz Action será representada por la etiqueta action que no tiene
argumentos. Anidadas dentro de esta etiqueta irán las etiquetas especı́ficas de
cada acción a realizar. De esta forma se mantiene la idea del diseño de mostrar
todas las acciones bajo una interfaz común.
4.3.2.2.
Descripción del menú
Teniendo en cuenta la clase Menu diseñada en el apartado 4.2, los documentos
XML utilizados para definir un objeto perteneciente a dicha clase podrán contener
las siguientes etiquetas (ver apéndice A).
header: Contendrá el texto a mostrar como tı́tulo del menú.
version: Versión de la definición de menú utilizada, DFBCIn deberá actualizarse cuando reciba un menú con una versión mayor a la que soporte.
timer: Tiene un atributo time que indica el tiempo a esperar sin recibir
eventos del usuario antes de ejecutar las acciones contenidas por esta etiqueta.
initActions: Anidadas en esta etiqueta están las acciones a realizar en el
momento en que se cargue el menú.
option: Representa a la clase option, tendrá anidadas las etiquetas:
• text Contiene el texto a mostrar al usuario para identificar la opción.
• action Cada opción tendrá anidado un número de etiquetas de este
tipo para definir las acciones a realizar cuando se seleccione dicha
opción.
Las etiquetas que describen cada implementación de la interfaz Action.
Por ejemplo, las siguientes etiquetas XML describen un menú con un bloque
de acciones a realizar en el momento en el que sea cargado, otro bloque de acciones
que se ejecutarán en caso de que pasen diez segundos sin que se detecte ningún
evento y dos opciones (el contenido de las etiquetas action se explica en la
siguiente sección):
<menu>
<header>Menu de ejemplo</header>
<version>0.0</version>
<initActions>
50
Capı́tulo 4. Diseño
<action>...</action>
<action>...</action>
</initActions>
<timer seconds="10">
<action>...</action>
</timer>
<option>
<text>Primera opción</text>
<action>...</action>
<action>...</action>
</option>
<option>
<text>Segunda opción</text>
<action>...</action>
</option>
</menu>
4.3.2.3.
Descripción de las acciones
Anidadas dentro de la etiqueta action se encontrarán las etiquetas que describen cada acción especı́fica. Cada etiqueta que represente a una acción deberá describir, ya sea mediante atributos, texto o nuevas etiquetas anidadas, un objeto
perteneciente a la clase implementación de Action correspondiente (ver la sección
4.2.3).
Las etiquetas que describen las distintas acciones son las siguientes:
nextMenu: Representa una acción de tipo NextMenuAction, nextMenu contiene el valor del atributo con el mismo nombre del objeto que se está definiendo. Para especificar los argumentos que se deben pasar a VXMLS
cuando se solicita el menú se utilizarán etiquetas menuArgument anidadas
en el interior de la etiqueta nextMenu. Estas etiquetas nextArgument deben
tener los siguientes atributos:
• name: Nombre del argumento.
• value: Valor del argumento.
Por ejemplo, una acción para solicitar el menú movie list menu con los
atributos filter=shown y genre=adventure se utilizará una etiqueta como
esta:
Documentos XML
51
<action>
<nextMenu name="movie_list_menu">
<menuArgument name="genre" value="adventure"/>
<menuArgument name="filter" value="shown"/>
</nextMenu>
</action>
previousMenu: Describe una acción de tipo PreviousMenuAction. Contiene
los atributos depth, que contiene el valor de atributo returnDepth del
objeto definido, y reinit, que representa al atributo con el mismo nombre
de la clase PreviousMenuAction.
hideMenu: Representa una acción de tipo HideMenuAction.
showMenu: Representa una acción de tipo ShowMenuAction.
reloadMenu: Representa una acción de tipo ReloadMenuAction.
changeOption: Describe una acción de tipo ChangeOptionAction. Para definir los atributos del objeto a crear, esta etiqueta contiene dos atributos:
value, que representa al atributo value del objeto, y mode. El atributo
mode puede ser relative con lo que el atributo absolute del objeto creado
serı́a falso, o relative, con lo que el atributo absolute del objeto serı́a
verdadero.
playVideo: Se utiliza para describir acciones del tipo PlayVideoAction.
Contiene un único atributo file que contiene la referencia al vı́deo a reproducir (atributo video de la clase PlayVideoAction). Anidadas dentro de
esta etiqueta habrá otras etiquetas action, que representan las acciones a
ejecutar cuando la reproducción de vı́deo alcance su fin. Por ejemplo, las
siguientes etiquetas definen una acción que inicia la reproducción de un
vı́deo de forma que cuando finalice se carga un nuevo menú se muestra por
encima del vı́deo ya parado.
<action>
<playVideo file="video.avi">
<action>
<stopVideo/>
</action>
<action>
<nextMenu name="video_end_menu">
</nextMenu>
52
Capı́tulo 4. Diseño
</action>
<action>
<showMenu/>
</action>
</playVideo>
</action>
stopVideo: Representa una acción de tipo StopVideoAction.
resumeVideo: Representa una acción de tipo ResumeVideoAction.
unloadVideo: Representa una acción de tipo UnloadVideoAction.
seekVideo: Representa una acción de tipo SeekVideoAction. Para especificar los atributos del objeto a crear, esta etiqueta contiene dos atributos:
mode, que define el valor de atributo con el mismo nombre del objeto a crear,
y time, que define el valor del atributo time de dicho objeto. El atributo
mode podrá tomar los valores forward, backward o absolute.
bindKey: Describe una acción de tipo BindKeyAction. Como atributos únicamente tiene key que especifica la tecla que se quiere enlazar. Para definir
a qué se quiere enlazar dicha tecla, una etiqueta bindKey puede contener
una de estas dos etiquetas:
• keyActions: Anidadas dentro de esta etiqueta irán un cierto número
de etiquetas action que representan las acciones a ejecutar cuando el
usuario pulse la tecla en cuestión. Por ejemplo, para hacer que una
pulsación de la tecla UP cambie la opción seleccionada por la opción
anterior:
<action>
<bindKey key="UP">
<keyActions>
<action>
<changeOption value="-1" mode="relative"/>
</action>
<action>
<showMenu/>
</action>
</keyActions>
</bindKey>
</action>
Protocolo de comunicación
53
• keyEvent: Tiene un atributo type que define el comportamiento que
debe seguir DFBCIn cuando un usuario pulse la tecla vinculada. Los
valores que puede tomar type son:
◦ EXECUTE: Indica que se de deben ejecutar las acciones vinculadas
a la opción actualmente seleccionada.
◦ NULL: La pulsación de una tecla vinculada a un evento NULL no
provoca ningún tipo de reacción en DFBCIn.
Por ejemplo, para hacer que el botón SEL provoque la ejecución de las
acciones de la opción seleccionada:
<action>
<bindKey key="SEL">
<keyEvent type="EXECUTE"/>
</bindKey>
</action>
setProperty: Representa una acción de tipo SetPropertyAction. Para describir el objeto a crear, esta etiqueta utiliza los atributos name y value,
cuyos nombres son idénticos a los de los atributos del objeto que se está definiendo.
showText: Representa una acción de tipo ShowTextAction. Contiene un
grupo de etiquetas textLine que describen las lı́neas de texto a mostrar.
Por ejemplo, para describir una acción que muestra el resumen de una
pelı́cula:
<action>
<showText>
<textLine>Habiendo marchado en busca de</textLine>
<textLine>fortuna y después de unos</textLine>
<textLine>a~
nos ...</textLine>
</showText>
</action>
hideText: Representa una acción de tipo HideTextAction.
loadMp3: Representa una acción de tipo LoadMp3Action. Contiene el atributo path para describir el atributo con el mismo nombre del objeto definido.
pauseContinueMp3: Representa una acción de tipo PauseContinueMp3Action.
killMp3Player: Representa una acción de tipo KillMp3PlayerAction.
54
Capı́tulo 4. Diseño
4.4.
Protocolo de comunicación
La comunicación entre DFBCIn y VXMLS se hará siguiendo el estándar
HTTP [35]. DFBCIn hará peticiones GET al servidor VXMLS. Este devolverá un
documento XML encapsulado en una respuesta HTTP.
Un mensaje GET permite solicitar un documento dada una URL, en esta
petición se puede incluir el nombre del documento a solicitar y un número indeterminado de argumentos. Por lo tanto, para permitir la comunicación entre
DFBCIn y VXMLS será necesario definir las URL a las que DFBCIn deberá referirse cuando necesite solicitar información de VXMLS, o solicitar que VXMLS
realice alguna operación.
Las operaciones que VXMLS debe permitir realizar son:
Creación de una nueva sesión de usuario.
Destrucción de la sesión de usuario.
Petición de la descripción de un determinado menú.
Cambio de un parámetro de personalización.
Cada una de estas acciones será una URL a la que se le pasarán los argumentos necesarios. VXMLS responderá con un documento XML que DFBCIn
podrá interpretar para obtener el resultado esperado.
Suponiendo que la URL que apunta al servidor VXMLS sea http://vxmls,
las peticiones HTTP para realizar las operaciones anteriores serán:
Para solicitar una nueva sesión para el cliente settopbox que tiene como
clave palabrasecreta, DFBCIn deberá hacer una petición GET a la URL:
http://vxmls/login?client=settopbox&passwd=palabrasecreta
Para destruir la sesión:
http://vxmls/logout
Para solicitar el menú main menu, suponiendo que no se necesiten argumentos adicionales para dicho menú, la URL a solicitar será la siguiente:
http://vxmls/get_menu?menu=main_menu
Diseño de DFBCIn
55
En caso de que la petición de menú necesite argumentos, por ejemplo el
menú pelı́culas con el argumento filtro con valor nuevas, la URL será:
http://vxmls/get_menu?menu=pelı́culas&filtro=nuevas
Para cambiar el la propiedad language a es, VXMLS deberá proporcionar
la URL:
http://vxmls/set_property?name=language&value=es
4.5.
Diseño de DFBCIn
4.5.1.
División en subsistemas
Durante el análisis, se establecieron unos aspectos que serı́a deseable que cumpliera el diseño de la aplicación DFBCIn (sección 3.4.4), para aislar los módulos
conflictivos de los módulos estables, evitando que los problemas y riesgos derivados de las partes más complicadas del sistema se propaguen por toda la implementación.
La aplicación DFBCIn se compone de tres módulos básicos:
Módulo gráfico: Será el encargado de realizar las operaciones gráficas necesarias para comunicarse con el usuario. Como efecto colateral, debido a
la utilización de DirectFB, también se encargará del control de la entrada. Esto es ası́ ya que es deseable que se puedan utilizar distintos módulos
gráficos (en forma de plugins) sin necesidad de recompilar el resto de la aplicación, incluso módulos gráficos que no utilicen DirectFB. Puesto que la
utilización de DirectFB condiciona la forma en la que se realiza el control
de la entrada, la implementación de la entrada deberá estar en este módulo, aunque en el diseño lógico se considerará un submódulo. Un diseño en
el que el módulo de entrada fuera independiente del módulo gráfico serı́a
más correcto teóricamente, pero en la práctica siempre estarı́a fuertemente
acoplado al módulo gráfico.
Módulo de comunicación con VXMLS 1 : Se compondrá de un parser y una
implementación del protocolo HTTP. Para el resto del sistema será una caja
negra a la que se le pueden solicitar los menús (y el resto de operaciones
que puede realizar VXMLS) indicados por las acciones que lo requieran.
1
de aquı́ en adelante módulo VXMLS, para abreviar
56
Capı́tulo 4. Diseño
Motor de la aplicación: Será el encargado de coordinar la actuación de los
dos módulos anteriores y de ejecutar los objetos de la clase Action.
VXMLS
Libreria grafica
graphics.h
Motor
Modulo VXMLS
Figura 4.7: Módulos de DFBCIn
El módulo gráfico se implementará como una biblioteca dinámica, de forma
que se puedan intercambiar distintos módulos que cumplan la interfaz esperada
sin necesidad de recompilar el sistema. Los otros dos módulos formarán el programa que se enlazará dinámicamente con el módulo gráfico seleccionado. La figura
4.7 representa al sistema final, funcionando junto con el sistema VXMLS.
4.5.2.
Interacción entre módulos
En la figura 4.8 se describe un ejemplo de interacción entre los distintos módulos del sistema2 . Se observa como el módulo gráfico actúa como fachada (patrón
Facade [38]), aislando la entrada y la salida del resto del sistema, y como el módulo VXMLS actúa como adaptador de VXMLS (patrón Adapter [38]), traduciendo
las peticiones del motor para que sean comprensibles para VXMLS y las repuestas
de VXMLS para que sean comprensibles para el resto de la aplicación. El motor
controla la ejecución de ambos módulos.
El diagrama muestra la secuencia que sigue el sistema en su inicio y un par
de ejemplos de reacción ante distintos eventos. Los sucesos relevantes son los
siguientes:
1. El motor inicia su ejecución y solicita al módulo VXMLS que se inicialice.
2. El módulo VXMLS pide una nueva sesión al servidor VXMLS, cuando éste
le confirma la operación devuelve el control al motor.
2
Las funciones y mensajes son una simplificación de las interfaces entre los módulos.
Diseño de DFBCIn
57
Modulo grafico
Motor
Modulo VXMLS
VXMLS
init()
login
ok
init()
getMenu(mainMenu)
getMainMenu
documento XML
menu
showMenu(menu)
menu visual
evento de usuario
evento de entrada
changeOption(newOption)
menu visual
evento de temporizacion
setProperty(language, es)
setLanguage(es)
Figura 4.8: Interacción entre los módulos de DFBCIn
3. El motor inicializa el módulo gráfico.
4. El motor solicita al módulo VXMLS el objeto menu que representa el primer
menú a mostrar al usuario.
5. El módulo VXMLS hace una petición HTTP a VXMLS solicitando la descripción del menú principal, recibe la respuesta de VXMLS y la transforma
en el objeto Menu solicitado.
6. El motor le solicita al módulo gráfico que muestre el menú en la pantalla.
7. El usuario reacciona ante el menú y genera algún tipo de evento, por ejemplo
pulsa una tecla que deberı́a cambiar la opción seleccionada.
8. El módulo gráfico detecta este evento y se lo comunica al motor.
9. El motor obtiene el conjunto de acciones relacionadas con el evento notificado por el módulo gráfico y las ejecuta.
10. La acción que deberı́a cambiar la opción provoca que el motor mande un
mensaje al módulo gráfico para que muestre el cambio por pantalla.
11. El motor detecta un evento de temporización y ejecuta las acciones definidas
para dicho evento.
12. Esta vez la acción ejecutada debe cambiar el lenguaje. Para ello, el motor
solicita al módulo VXMLS que realice este cambio.
58
Capı́tulo 4. Diseño
13. El módulo VXMLS envı́a un mensaje HTTP a VXMLS solicitando el cambio
de lenguaje a VXMLS, espera a la confirmación y comunica al motor el éxito
o fracaso de la operación.
Hay varios eventos que pueden provocar la ejecución de acciones por parte
del motor, algunos de estos eventos son detectados por la biblioteca gráfica (los
eventos provocados por la entrada del usuario o durante la reproducción de un
vı́deo) y otros son controlados por el motor (los eventos de temporización y la
inicialización de menús). Durante la ejecución de las acciones, el motor utilizará el
módulo gráfico y el módulo VXMLS para comunicarse con los agentes externos
al sistema (el usuario y VXMLS).
En resumen, el flujo de información entre los tres módulos de DFBCIn y los
dos agentes externos (usuario y VXMLS) es como sigue:
El usuario se comunica con el módulo gráfico mediante los dispositivos de
entrada necesarios (por ejemplo el mando a distancia) y recibe información
del mismo a través del televisor. El módulo gráfico controla estos dispositivos.
El módulo gráfico envı́a eventos al motor para que éste ejecute las acciones
pertinentes. El motor se comunica con el usuario provocando cambios en la
interfaz utilizando el API ofrecido por el módulo gráfico.
El motor utilizará otro API para solicitar al módulo VXMLS que realice las
operaciones que requieran comunicación con el servidor VXMLS. El módulo
VXMLS enviará las peticiones HTTP necesarias y traducirá la respuesta
transformándola en objetos comprensibles por el motor de la aplicación
(normalmente instancias de la clase Menu).
4.5.3.
Objetos comunes
La clase Menu explicada en la sección 4.2.2 deberá ser implementada en
DFBCIn para permitir la comunicación entre los tres módulos que la componen.
Para almacenar información sobre el estado de los objetos Menu almacenados
por DFBCIn, el diseño general de clase Menu deberá ser ligeramente extendido.
La aplicación necesita saber para cada menú, cuáles son las teclas que están
vinculadas a qué acciones. Para ello se añade una nueva clase Key que será referenciada por la clase Menu.
Diseño de DFBCIn
59
Menu
+ majorVersion: Int
+ minorVersion: Int
− header: String
− time: Int
Option
1..∗ + text: String
for all actions do
action.execute
end
+ execute()
+ draw()
1..∗
initActions
timeActions
∗
Key
+ key: String
− event: String
+ execute()
0..∗
0..*
actions
<<interface>>
Action
+ execute()
0..*
ACTIONS
EXECUTE
NULL
Figura 4.9: Clase Menu en DFBCIn
Para manejar información persistente se ha diseñado un pequeño sistema de
propiedades que leerá y guardará la información de un fichero de texto. Este
sistema permitirá a los distintos hilos de ejecución de la aplicación acceder concurrentemente al contenido del fichero y modificarlo en caso de que sea necesario.
Los métodos exportados para el resto de la aplicación son tres:
getPropertyValue: Devuelve el valor de una propiedad con un nombre dado.
setPropertyValue: Cambia el valor de una determinada propiedad.
saveProperties: Salva los datos almacenados en memoria en el disco.
En los últimos incrementos, la mayorı́a de los parámetros de personalización
pasan a ser almacenados por VXMLS, por lo que , finalmente, el sistema de propiedades solamente se utilizará para almacenar valores estáticos que no necesitan
ser cambiados (el nombre y clave del Set Top Box, la dirección del servidor, etc).
También se han diseñado dos clases para almacenar distintos tipos de información, la clase Stack (pila) y la clase Array. Ambas clases se pueden utilizar
para guardar cualquier tipo de objeto3 .
4.5.4.
Diseño del motor de la aplicación
4.5.4.1.
Objetivos
El objetivo de este módulo es el de controlar el funcionamiento del resto de
módulos del sistema y el de realizar las acciones especificadas por los objetos
3
La implementación de estas clases en C presenta ciertos problemas a la hora de compartir
objetos y de liberar memoria como se verá en el capı́tulo dedicado a la implementación de
DFBCIn
60
Capı́tulo 4. Diseño
Stack
Array
+ empty(): Boolean
+ push(data: Object)
+ pop(): Object
+ seeTop(): Object
+ add(data: Object)
+ getData(position: Int): Object
+ elements(): Integer
+ flush()
Figura 4.10: Clases para el almacenamiento de datos
Action que deban ser ejecutados.
Este módulo contiene la implementación de la parte más estable del sistema,
ya que no está en contacto con ningún agente exterior. Básicamente, deberá implementar el ciclo de ejecución y las estructuras de datos para mantener la información necesaria para la ejecución de las acciones recibidas.
Con respecto a los objetos del dominio, a este módulo le corresponde la implementación de los objetos pertenecientes a la clase Action. Será el módulo encargado de realizar las operaciones convenientes para ejecutar las acciones.
4.5.4.2.
Estructura interna
En la figura 4.11 se puede ver el diseño interno de este módulo. Básicamente,
tiene cinco componentes: el objeto principal, un objeto encargado de controlar
la interfaz con el usuario, un objeto que ayudará a éste controlando los eventos
de entrada, un objeto que se encargará de reproducir archivos de audio y las
implementaciones de la interfaz Action.
Main: El método main será el método a invocar para iniciar la ejecución de
la aplicación.
Interface: Un objeto de esta clase se encargará de controlar la comunicación
con la biblioteca gráfica, realizando las operaciones necesarias para mostrar
la interfaz del usuario correctamente.
InterfaceInput: Un objeto de esta clase se encargará de comunicarle a Interface los eventos de entrada de usuario que detecte con ayuda de la biblioteca
gráfica. Interface e InterfaceInput son una aplicación simplificada del patrón
Observer [38]. InterfaceInput avisará a Interface cuando detecte un evento
de entrada y lo almacenará hasta que Interface pueda atenderlo.
Mp3Player : Un objeto de esta clase se encargará de reproducir ficheros de
audio.
Diseño de DFBCIn
61
<<interface>>
Action
+ execute()
Mp3Player
− path: String
+ load(path: String)
+ pauseContinue()
Interface
Modulo VXMLS
− actualMenu: Menu
Main
+ main()
Recibe gupos de acciones de
interface y para cada una
de ellas ejecuta Action.execute
+ loadMenu(menu: Menu)
+ showMenu()
+ hideMenu()
+ unloadMenu()
+ loadVideo(video: String, endActions: array)
+ playVideo()
+ stopVideo()
+ resumeVideo()
+ unloadVideo()
+ waitForActions(): Array
+ notifyInputEvent()
+ notifyVideoEnd()
+ notifyNewFrame()
Modulo Grafico
interfaceInput.getInputKey()
InterfaceInput
− inputKey: Key
+ getInputKey(): Key
− notify()
interface.notifyInputEvent()
Figura 4.11: Clases del motor
Action: Las diferentes implementaciones de esta interfaz podrán utilizar
tanto el módulo VXMLS como el módulo gráfico para realizar las operaciones necesarias para la ejecución del método execute. La mayorı́a de las
operaciones gráficas deberán hacerse utilizando el objeto Interface.
Interface, InterfaceInput y Mp3Player son clases Singleton [38], la aplicación
instanciará un único objeto de cada una de estas tres clases.
La interacción entre los distintos objetos que componen este módulo se ha
ejemplificado en la figura 4.12. En ella se puede ver como Main notifica a Interface que está a la espera de nuevas acciones a ejecutar. Cuando InterfaceInput
detecta un nuevo evento de entrada se lo hace saber a Interface y espera a que
Interface puede consultar ese evento. Interface comprobará si hay un grupo de
acciones relacionadas con el evento detectado y, de ser ası́, se las envı́a a Main
para que las ejecute.
Los métodos notifyNewFrame y notifyVideoEnd serán utilizados por el módulo gráfico para avisar a Interface de que hay un nuevo frame de vı́deo listo para
ser mostrado o de que el final del vı́deo ha sido alcanzado.
En este diagrama se han obviado las acciones de inicialización. También se
debe tener en cuanta que no sólo los eventos de entrada pueden provocar la
62
Capı́tulo 4. Diseño
Main
Graphics
create()
create()
InterfaceInput
Interface
waitForActions()
notifyInputEvent()
evento de entrada
getInputEvent()
Array
InputKey
Figura 4.12: Detección de eventos de entrada
ejecución de acciones. Interface puede detectar otro tipo de eventos y devolver el
grupo de acciones correspondiente al evento detectado.
4.5.4.3.
Ciclo de ejecución de Main
El ciclo de ejecución la aplicación (método main de la clase Main) se puede
ver en la figura 4.13.
Inicializacion
Menu Principal
Esperando acciones
Ejecutando acciones
Figura 4.13: Ciclo de ejecución
1. En primer lugar deberá inicializar todos los subsistemas necesarios, es decir,
el módulo gráfico y el módulo VXMLS.
2. El segundo paso consiste en obtener el objeto que representa al menú principal y pasárselo al módulo gráfico para que lo muestre por pantalla.
Diseño de DFBCIn
63
3. Tras estos dos pasos iniciales el motor deberá esperar a que el objeto Interface entregue un bloque de acciones a ejecutar.
4. Una vez obtenidas las acciones, el motor pasará a ejecutarlas (para cada
objeto de la clase Action recibido, se invocará su método execute). Cuando
termine la ejecución del bloque completo volverá al estado de espera por un
nuevo bloque de acciones.
Es importante señalar que no se contempla la finalización del programa como
un comportamiento normal. La aplicación sólo terminará en caso de que se produzca una situación anormal. Para el desarrollo, se ha definido una tecla EXIT
que provoca la finalización de la aplicación generando una notificación de error
“artificialmente”. La aplicación definitiva sólo deberı́a terminar en caso de que se
apague el Set Top Box, esto se considerará una situación excepcional y su control
será idéntico al que se hace con los errores graves, que provocan la finalización de
la aplicación.
4.5.4.4.
Máquinas de estados de Interface e InterfaceInput
En el diagrama de estados de la figura 4.14 se puede observar el proceso
mediante el cual InterfaceInput notifica que ha detectado un nuevo evento de entrada. InterfaceInput bloquea la detección de eventos hasta que Interface recibe
el evento detectado. Esto se podrı́a evitar con una cola de eventos, pero eso se
supone que lo hará la biblioteca gráfica en caso de que sea necesario.
waitForActions()
notifyInputEvent()/
getInputKey()
Reposo
Sin Eventos
nuevo evento/notify()
getInputKey()
notifyInputEvent()/
getInputKey()
waitForActions()
EventoAlmacenado
Acciones listas
Estados de InterfaceInput
Esperando acciones
Estados de Interface
Figura 4.14: Estados de InterfaceInput e Interface
En el caso de los estados de Interface, todas las transiciones provocadas por
notifyInputEvent también podrán ser provocadas por notifyVideoEnd, pero
para este caso, el módulo gráfico no cambia de estado tras la notificación de final
de vı́deo (no se puede producir un final de vı́deo imprevisto antes de que Interface
ejecute las acciones necesarias debidas al final notificado).
64
Capı́tulo 4. Diseño
La máquina de estados de Interface es mucho más compleja que la mostrada en la figura 4.14, además los de estados referentes a las acciones a ejecutar,
también debe mantener control sobre el estado del menú actual y sobre la reproducción de vı́deo. Además los estados mostrados en esta figura son una pequeña
simplificación: puede ser que la tecla detectada por InterfaceInput no tenga asociada ninguna acción.
Por todo ello, es más sencillo representar sus transiciones como tres máquinas
de estados concurrentes, aunque el estado real sea una combinación de los tres
estados en los que se halle cada una de las submáquinas de estados. En la figura
4.15 se puede ver con más detalle la evolución de Interface durante su ciclo de
ejecución.
notifyVideoEnd()/getVideoActions()
inicializacion de menu
temporizacion
waitForActions()
tecla vinculada
tecla EXEC
Reposo
notifyVideoEnd()/getVideoActions()
inicializacion de menu
temporizacion
notifyInputEvent()/
getInputKey()
Sin Menu
loadMenu(menu)
unloadMenu()
Menu Cargado
tecla no vinculada
Sin Video
unload()
load(video)
Video Cargado
waitForActions()
play()
Reposo−Comprobacion
de tecla
tecla vinculada
tecla EXEC
Acciones listas
Esperando acciones
notifyInputEvent()/
getInputKey()
resumeVideo()
stopVideo()
gotoVideoPosition()
Reproduciendo
tecla no vinculada
Esperando−Comprobacion
de tecla
Figura 4.15: Máquina de estados de Interface
Como se puede ver, los estados referentes al vı́deo y a los menús son más simples de lo que cabı́a suponer. El control real lo llevará el módulo gráfico. Interface
simplemente necesita saber cuándo se está reproduciendo vı́deo y cuándo no y
cuándo tiene un menú cargado. El estado referente a si el menú es visible, si el
vı́deo esta parado, etc. será controlado por la biblioteca gráfica.
4.5.4.5.
Ejecución de acciones
La ejecución de ciertas acciones obliga a mantener algún tipo de estado durante el ciclo de ejecución del motor de la aplicación.
Diseño de DFBCIn
65
Las acciones NextMenuAction y PreviousMenuAction obligan a que se lleve
un control de los menús anteriores al actual, esto se hará con una pila en la
que se van almacenando los menús requeridos por NextMenuAction y de la
que se van eliminando los menús descartados por PreviousMenuAction.
La acción BindKeyAction necesita un mecanismo para almacenar las acciones vinculadas a las teclas para cada menú que se encuentre en la pila
de menús. Para solucionar esto se ha ampliado la clase Menu como se explicó en la sección 4.5.3.
El resto de variables de estado (en la implementación actual el estado de un
usuario sólo contempla las pelı́culas vistas y el lenguaje) serán mantenidas
por el servidor VXMLS.
4.5.4.6.
Reproducción de audio
La reproducción de audio no influye en el resto del sistema, el reproductor
comenzará a reproducir un fichero cuando sea cargado utilizando el método load
y detendrá la reproducción cuando se invoque su método pauseContinue. La siguiente llamada a pauseContinue provocará que se reanude la reproducción.
Para comenzar la reproducción de vı́deo será necesario eliminar este objeto
para que libere el dispositivo de audio, por lo tanto, load será un método de
clase, que se encargará de instanciar el objeto correspondiente en caso de que aún
no exista.
4.5.5.
Diseño del módulo VXMLS
4.5.5.1.
Objetivos
Este módulo deberá actuar como adaptador entre el motor y el servidor
VXMLS, ocultando al resto del sistema la comunicación con VXMLS. En los
primeros incrementos del sistema, este módulo no obtendrá las descripciones de
los menús de VXMLS sino que utilizará medios de almacenamiento estáticos (en
las primeras fases ficheros locales y en fases más avanzadas se comunicará con un
servidor HTTP estático).
En el diagrama de secuencia de la figura 4.8 muestra cómo interactúa este
módulo con VXMLS para todas las operaciones enumeradas excepto para la liberación de recursos. Esta última operación consiste simplemente en el envı́o de un
mensaje logout a VXMLS para que destruya la sesión creada en la inicialización
del sistema.
66
Capı́tulo 4. Diseño
4.5.5.2.
Componentes del sistema
La interfaz ofrecida por el módulo VXMLS al resto del sistema proporciona
las siguientes operaciones.
Inicialización: Esta operación deberá ser ejecutada una sola vez antes de
realizar cualquier otra operación sobre este módulo. El módulo VXMLS
solicitará una nueva sesión VXMLS y creará las estructuras de datos necesarias.
Liberación de recursos: Provocará la liberación de la memoria ocupada por
los datos internos de este módulo y la destrucción de la sesión creada para
el usuario en VXMLS.
Cambio de un valor de personalización: Los parámetros de personalización
tienen un nombre y un valor, esta función permitirá solicitar a VXMLS que
modifique el valor de un parámetro dado. Los únicos parámetros soportados
en la implementación final son el usuario activo (cada Set Top Box tiene su
cuenta en VXMLS y dentro de la misma puede mantener datos de varios
usuarios) y el lenguaje a utilizar para el usuario activo. También se incluye
dentro de este tipo de operación el hecho de marcar una pelı́cula como vista.
Solicitud de una descripción de un menú. Devuelve un objeto de la clase
Menu describiendo el menú solicitado.
Este módulo contiene dos componentes principales: el parser encargado de
interpretar los documentos XML que VXMLS envı́a como respuesta a las peticiones de menús y una implementación parcial del protocolo HTTP que permita
la comunicación con VXMLS. La interacción entre estos componentes se puede
ver en la figura 4.16.
Motor
Parser
Modulo HTTP
VXMLS
getMenu()
peticion de menu en un mensaje HTTP
documento XML en un mensaje HTTP
documento XML
objeto menu
setProperty()
mensaje HTTP
confimacion XML en un mensaje HTTP
documento XML
confirmacion
Figura 4.16: Interacción entre los componentes del módulo VXMLS
Diseño de DFBCIn
67
Las operaciones login y logout son similares a la operación setProperty.
Internamente, el módulo VXMLS se organizará como se puede ver en la figura
4.17. Se compone de tres clases utilidad.
VXMLSFacade: Fachada que proporcionará las operaciones especificadas
por la interfaz al resto del sistema (patrón Facade [38]). Se encargará de
componer la petición HTTP correcta, enviarla con ayuda de la clase HTTP
e interpretar la respuesta de VXMLS utilizando los métodos de la clase
Parser.
HTTP : Controla la comunicación con el servidor HTTP. Se encargará de
transmitir el mensaje GET para una URL de un servidor que esté escuchando HTTP en un determinado puerto y de devolver la respuesta del servidor
como un flujo de datos.
Parser : Interpreta los documentos XML obtenidos de VXMLS.
<<utility class>>
VXMLS facade
+ init
+ deinit()
+ getMenu(name:String, arguments:Array)
+ setProperty(name:String, value:String)
<<utility class>>
<<utility class>>
HTTP
Parser
+ httpGet(server:String, port:Int, url:String): Stream
+ escapedEnconding(string:String): String
+ parseMenu(menuDocument: Document):Menu
+ parseVXMLSResponse(response: Document): Boolean
Figura 4.17: Clases del módulo VXMLS
4.5.5.3.
Diseño del parser
Es importante tener especial cuidado en el desarrollo de la clase Parser, deberá diseñarse de forma que sea fácil añadir código para interpretar las nuevas
acciones que se vayan añadiendo al sistema. Como se vio en la sección 3.4.3, la
biblioteca expat implementa un parser genérico en el que hay que definir ciertas
funciones. Para los objetivos de este proyecto, las funciones que se deben implementar son las de apertura y cierre de etiquetas y la de sección de caracteres.
Dichas funciones deben compartir algún objeto para almacenar información durante el proceso de interpretación del documento XML.
68
Capı́tulo 4. Diseño
Para almacenar la información necesaria a lo largo del proceso de parsing se
utilizará un objeto de la clase ParserData, que contiene una pila (ver la sección
4.5.3) y un objeto Menu que se irá construyendo a medida de que se disponga
de la información necesaria. En la pila se almacenará objetos de la clase Context
con la información necesaria para el tratamiento de las etiquetas abiertas hasta
el momento. Normalmente, en caso de que sea necesario, se apilará un nuevo
contexto durante la apertura de la etiqueta y se desapilará durante el cierre de
la etiqueta. De esta forma, una etiqueta puede acceder a la información de todas
sus etiquetas padre. El diseño de clases del parser puede verse en la figura 4.18.
Expat
ParserData
MenuParser
− parserData: ParserData
+ parse(document: Stream): Menu
unconpleteMenu
Menu
contextStack
Stack
+ pushContext(context: Context)
+ popContext(): Context
+ seeTopContext(): Context
TagManager
+ startTag(tag: Tag, arguments: Array, parserData: ParserData)
+ endTag(tag: Tag, parserData: ParserData)
+ characterData(tag: Tag, data:String, paserData: ParserData)
Context
0..∗
OptionContext
+ option: Option
CharacterContext
+ text: String
ActionContext
+ action: Action
HeaderContext
TextContext
VersionContext
BindKeyContext
+ key: Key
+ majorVersion: Int
+ minorVersion: Int
Figura 4.18: Clases utilizadas para la construcción del parser para descripciones
de menús
La clase Context es abstracta para permitir la ampliación de los tipos de información almacenables en caso de que sea necesario. Por ejemplo, cuando se
añadió la acción BindKey (ver la sección 4.2.3) fue necesario añadir una nueva
estructura para almacenar información durante el proceso de parsing de las etiquetas que definen dicha acción. Con este diseño, simplemente hizo falta extender
la clase Context y crear una nueva clase para la etiqueta especı́fica.
Las funciones de apertura y cierre de la etiqueta action deberán ser capaces
de manejar las acciones como clases abstractas que son. Para ello, en la apertura
de una etiqueta action se apilará un nuevo objeto de la clase ActionContext cuyo
atributo action será una acción vacı́a. Las funciones que controlan las etiquetas
Diseño de DFBCIn
69
que pueden aparecer anidadas dentro de una etiqueta action deberán crear el
objeto de una subclase Action concreta y colocarlo en el contexto apilado para
la etiqueta action.
El control de las acciones a realizar para la apertura y cierre de cada etiqueta
lo hará la clase utilidad TagManager. La implementación de esta clase también
deberá ser adaptada a medida que vayan apareciendo nuevas etiquetas a tratar.
Aún ası́, la utilización de la pila de contextos permite que el código necesario para
el tratamiento de las nuevas etiquetas que aparezcan para describir acciones no
afecte al código ya existente:
El código para manejar las etiquetas anidadas dentro de action solamente
necesita tener en cuenta los datos que hay en la pila por encima del contexto
colocado en la apertura de la etiqueta action.
El código que maneja la etiqueta action espera que cuando se cierre esta
etiqueta, el atributo action del contexto de la cima de la pila sea la acción
completa, sin importar que implementación concreta de la interfaz Action
sea.
El código para manejar el resto de etiquetas no se ve afectado, ya que no
hay datos referentes a las acciones en la pila.
Normalmente, una etiqueta que pueda tener texto anidado colocará un contexto CharacterContext en la cima de la pila durante su apertura, La función
characterData escribirá el texto en dicho contexto y durante el cierre de la etiqueta se procesará dicho texto. Esto se hace ası́ porque expat no asegura que el
texto contenido por las etiquetas sea leı́do en un solo bloque, sino que podrá ser
leı́do en varios bloques provocando varias llamadas a la función characterData.
El parser utilizado para las repuestas de VXMLS es mucho más sencillo y
puede ser desarrollado directamente, sin necesidad de una pila.
4.5.5.4.
Diseño del módulo HTTP
Este módulo será el encargado de enviar peticiones GET HTTP [35] a un
servidor y devolver su respuesta a través de un descriptor de fichero, de forma que
pueda ser interpretada por el parser. El diseño de este módulo no presenta ninguna
dificultad digna de mención, el proceso a seguir ante una llamada a la función
httpGet4 para enviar una petición a un puerto de un servidor determinado será:
4
La sintaxis exacta para esta función en C es int httpGet(const char *server, int
port, const char *request, int fileDes).
70
Capı́tulo 4. Diseño
1. Establecer una conexión TCP con el servidor en el puerto especificado.
2. Componer la cabecera HTTP a enviar al servidor.
3. Enviar dicha cabecera y esperar por la respuesta.
4. Interpretar la cabecera HTTP de la respuesta, tomar medidas en caso de
que sea necesario (por ejemplo, almacenar las cookies) y escribir el contenido
de la respuesta en el descriptor de fichero.
5. Cerrar el descriptor de fichero en cuanto se detecte el final del mensaje (para
poder utilizar un pipe con el parser ).
La función escapedEncoding se utilizará para codificar las cadenas de texto
que forman la URL solicitada de acuerdo con las especificaciones de [42].
4.5.5.5.
Funciones de la fachada VXMLSFacade
VXMLSFacade oculta la estructura interna de este módulo al resto del sistema. Para implementar las funciones que ofrece su API utilizará los dos submódulos comentados anteriormente y el subsistema de propiedades (ver 4.5.3).
init: Durante la inicialización, VXMLSFacade obtendrá del sistema de
propiedades el nombre y clave del Set Top Box, ası́ como el servidor y puerto
en el que está escuchando VXMLS. Con estos datos utilizará el módulo
HTTP para enviar a VXMLS una petición de nueva sesión de usuario.
Finalmente, utilizará el parser VXMLS para asegurarse de que VXMLS
confirma la operación o para obtener el mensaje de error en caso contrario.
deinit: Durante la destrucción del módulo VXMLS la fachada enviará un
mensaje a VXMLS para que destruya la sesión VXMLS (obteniendo el
servidor y puerto del sistema de propiedades y enviando el mensaje con
ayuda del módulo HTTP como en el caso anterior). Interpretará la respuesta
de VXMLS con el parser VXMLS.
setProperty: Enviará un mensaje a VXMLS notificando el nuevo valor
para la propiedad5 de forma similar a como se explicó en los dos casos
anteriores.
getMenu: De nuevo enviará un mensaje solicitando un menú con ayuda del
sistema de propiedades y el módulo HTTP. Interpretará la respuesta del
módulo HTTP con ayuda del parser de menús y devolverá el objeto Menu
correspondiente al menú solicitado.
5
estas propiedades las almacena el servidor, las propiedades locales las maneja el sistema de
propiedades local.
Diseño de DFBCIn
4.5.6.
Diseño del módulo gráfico
4.5.6.1.
Objetivos
71
La función de este módulo será la de controlar tanto los interfaces de salida como los de entrada. La inclusión del control de la entrada en este módulo
es un efecto colateral de la utilización de DirectFB, como se explicó en la
sección 3.4.2. Asimismo como interfaz de salida no sólo se utilizará la pantalla,
sino que también se controlará el dispositivo de audio durante la reproducción
de vı́deo. Por estos dos motivos, este módulo podrı́a haberse llamado módulo de
entrada/salida, pero el API que ofrece al resto del sistema solamente contiene
operaciones para modificar los gráficos mostrados en pantalla y una función para
obtener un evento de entrada.
Este módulo será un componente intercambiable del sistema final, se podrán
elegir diferentes implementaciones del mismo API sin necesidad de recompilar la
aplicación.
Se han desarrollado dos módulos gráficos distintos, uno de pruebas que utilizará la consola de texto para mostrar los menús y que mostrará mensajes en
lugar de realizar operaciones tales como reproducir vı́deo y el módulo gráfico “de
verdad” con todas las operaciones necesarias para permitir la ejecución correcta
de las acciones implementadas en el sistema. En las siguientes secciones se describirá el diseño que se ha seguido para la implementación completa utilizando
DirectFB, en la sección 4.5.6.4 se explicará brevemente qué objetos deberán
cambiar para la implementación del módulo simplificado para las pruebas.
El diseño de ambos módulos es idéntico, lo único que cambia es la implementación de los métodos.
4.5.6.2.
Funciones del API
El API que deberá implementar un componente que pretenda ser utilizado
como módulo gráfico para DFBCIn deberá ofrecer las siguientes operaciones (el
API formal se puede ver en el apéndice B):
init y deinit: Se asegura que DFBCIn llamará una sola vez a la función init
antes de llamar a cualquier otra función del API. También asegura que se
llamará a la función deinit antes de terminar la ejecución y que no se
llamará a ninguna otra función del API después de la llamada a deinit.
loadMenu: Carga un objeto Menu en el módulo gráfico.
72
Capı́tulo 4. Diseño
unloadMenu: Elimina el objeto Menu actual del módulo gráfico.
setBox: Especifica si se debe dibujar una caja semitransparente rodeando al
menú.
loadVideo: De forma similar a loadMenu, establece el “vı́deo actual”.
playVideo: Inicia la reproducción del vı́deo actual.
stopVideo: Congela la reproducción de vı́deo.
resumeVideo: Continúa con la reproducción de vı́deo.
getVideoPosition: Devuelve la posición de vı́deo actual.
gotoVideoPosition: Posiciona el vı́deo.
blitVideo: Copia el frame actual en la pantalla.
unloadVideo: Libera los recursos consumidos para la reproducción de vı́deo.
setNewFrameCallback: Establece un método que será invocado por el módulo
gráfico cada vez que haya un nuevo frame disponible.
setVideoEndCallback: Establece el método a invocar por el módulo gráfico
cuando se alcance el final del vı́deo.
flipScreen: Este será el método que deberá ser llamado por el motor de la
aplicación para hacer visibles los cambios de estado hechos utilizando el
resto de métodos del API. Cuando se invoque esta acción, el módulo gráfico
deberá dibujar bien el fondo de pantalla, bien el último frame de vı́deo
disponible, y por encima el menú cargado en caso de que lo haya.
testInput: Bloquea la ejecución hasta que haya un nuevo evento de entrada
disponible. En caso de que ya se hubiera detectado un evento de entrada
anterior a esta llamada, será devuelto ese evento sin bloquear la ejecución.
Hay que tener en cuenta ciertas consideraciones adicionales a la hora de implementar un nuevo componente que respete este API:
El objeto menú cargado con loadMenu pasa a estar compartido con el resto
de la aplicación, por lo tanto, será necesario bloquearlo cuando se quiera
acceder a su información.
La operación loadMenu no hace el menú visible, es necesario llamar a
flipScreen para ello; lo mismo ocurre con hideMenu.
Diseño de DFBCIn
73
Entre las llamadas a loadVideo y unloadVideo, las llamadas a flipScreen
provocarán que se dibuje el frame actual como fondo.
4.5.6.3.
Subsistemas del módulo
En el diseño de este módulo se debe hacer especial hincapié en aislar el código
conflictivo lo más posible, ya que este módulo implementa dos partes especialmente problemáticas del sistema: el control de la entrada de usuario y la utilización
del hardware gráfico.
El diagrama de clases de la figura 4.19 muestra la división que se ha hecho
para aislar el código inestable en diferentes objetos.
Todos los metodos
delegan en el objeto
correspondiente
<<utility class>>
GraphicsFacade
Hardware E/S
VideoPlayer
InterfaceDraw
− video: VideoObject
− menu: Menu
− showBox: Bool
+ gotoPosition(pos: Int)
+ load(video: String)
+ play()
+ stop()
+ unload()
+ videoBlit()
+ getPosition(): Int
+ setNewFrameCallback(callback: function)
+ setEndCallback(callback: function)
− newFrame()
− end()
+ loadMenu(menu: Menu)
+ unloadMenu()
+ setBox(showBox: Bool)
+ flipBuffer()
DirectFB
EventCatcher
+ testInput(): Key
Figura 4.19: Clases utilizadas para la construcción módulo gráfico
Como siempre, la estructura interna del módulo se oculta tras una aplicación
del patrón Facade [38].
InterfaceDraw contiene el código encargado de dibujar los menús y el fondo
en la pantalla.
VideoPlayer contiene el código encargado de manejar los objetos de vı́deo.
También deberá realizar operaciones de dibujo en la pantalla.
EventCatcher que transformará los eventos detectados en objetos del tipo
Key. EventCatcher contiene el código dependiente del dispositivo de entrada, de
esta forma InterfaceInput se mantiene independiente con respecto al sistema de
control de entrada utilizado (ver la sección 4.5.4.2).
74
Capı́tulo 4. Diseño
El diseño de Interface, (ver la sección 4.5.4.2) y VideoPlayer es una simplificación del patrón Observer [38], VideoPlayer deberá avisar a Interface cada vez que
decodifique un nuevo frame de video o cuando el vı́deo que se está reproduciendo
finalice.
La máquina de estados de VideoPlayer se puede ver en la figura 4.20. Lo único destacable de este diagrama es que las operaciones de posicionamiento pueden
provocar la finalización del vı́deo, ası́ como retroceder un vı́deo ya finalizado.
Sin Video
unload()
unload()
load(video)
Video Cargado
play()
stop()
Reproduciendo
Parado
resume()
fin del video
setPosition()
setPosition()
setPosition()
Finalizado
Figura 4.20: Máquina de estados de VideoPlayer
La máquina de estados de InterfaceDraw debe contemplar los casos de tener
o no un menú cargado y tener o no texto cargado.
4.5.6.4.
Módulo gráfico basado en la consola de texto
Para la ejecución de ciertas pruebas se ha desarrollado un módulo gráfico que
utiliza la consola de texto para representar los menús. El resto de operaciones
como la reproducción de vı́deo o el cambio de buffer visible se representarán mediante mensajes de texto.
Asimismo, este otro módulo gráfico sirve para validar el hecho de que se puedan intercambiar distintas implementaciones de este componente sin necesidad
de recompilar el motor y el módulo VXMLS.
El diagrama de clases para este sistema es el mismo que el mostrado en la
figura 4.19, con la diferencia de que ninguna clase utiliza DirectFB. Las modificaciones que se introducen en este módulo son:
Diseño de VXMLS
75
InterfaceDraw : Mostrará el menú actual en la consola de texto.
VideoPlayer : Todas los métodos (excepto los de notificación, que se dejan con implementación vacı́a) muestran un mensaje informando que se ha
llamado a ese método, sin realizar ninguna otra operación.
InterfaceInput: Los eventos de entrada se detectarán de forma rudimentaria
utilizando funciones de la biblioteca stdio de C.
En la figura 4.21 se puede ver el aspecto visual que presentan ambas implementaciones.
Figura 4.21: Aspecto visual de las dos implementaciones del módulo gráfico
4.6.
Diseño de VXMLS
4.6.1.
Introducción
El diseño de la aplicación VXMLS se ha hecho siguiendo el patrón Model/View/Controller [43]. Este patrón permite separar el aspecto visual (vista)
de los datos que refleja (modelo). La capa controlador se encarga de interpretar
los eventos generados por el usuario y actuar en consecuencia, normalmente provocando cambios en el modelo (que serán reflejados por la vista).
Para el caso de esta aplicación, el usuario será una instancia de la aplicación
DFBCIn, por lo que la entrada será más controlable.
La vista se encargará de generar documentos XML de acuerdo con el modelo
y las peticiones del usuario.
El modelo deberá ofrecer una interfaz para acceder y modificar la información
persistente. En este proyecto se ha utilizado la base de datos Mnesia [40] perteneciente a la plataforma Erlang/OTP. El hecho de utilizar una base de datos
76
Capı́tulo 4. Diseño
interna a la plataforma de programación utilizada simplifica la implementación
de la capa modelo, ya que las funciones de la interfaz del modelo se podrán implementar directamente sin necesidad de incluir una subcapa para el acceso a
una base de datos externa. Aún ası́, el modelo debe ser diseñado de forma que
oculte la utilización de Mnesia, de forma que se pueda sustituir por otra base
datos utilizando una subcapa de adaptación y un controlador para la nueva base
de datos.
Vista
Controlador
Modelo
DFBCIn
Modelo VXMLS
Mnesia
Datos
persistentes
Figura 4.22: Patrón Model/View/Controller en el diseño de VXMLS
La aplicación VXMLS desarrollada tiene una funcionalidad muy básica, pero
este diseño es igualmente válido para una aplicación más compleja.
4.6.2.
Objetos del dominio
La información que necesitará VXMLS para su funcionamiento se estructura
de la forma descrita en la figura 4.23. Para cada Set Top Box registrado se permite un cierto número de usuarios. Cada usuario tiene un lenguaje asociado y un
conjunto de pelı́culas vistas. Las pelı́culas están asociadas a un único género y
tienen cierta información que está almacenada en distintos idiomas. Los géneros
tienen un nombre que también se almacena para los distintos idiomas.
Para manejar las sesiones creadas para los diferentes Set Top Boxes, se utilizará un sistema de cookies, cada sesión se identificará con una única cookie que,
a su vez, estará relacionada con un único Set Top Box.
VXMLS también deberá manejar objetos de la clase Menu (ver figura 4.3).
No será necesario implementar los métodos de la clase Menu ya que los objetos
creados serán enviados a DFBCIn, que será el encargado de ejecutar los métodos
necesarios. VXMLS simplemente se encargará de dar los valores correctos a los
atributos de los objetos solicitados por DFBCIn, transformarlos en documentos
Diseño de VXMLS
77
Set Top Box
1
n
Usuario
Lenguaje
n
n
Cookie
1
Genero
n
Info Genero
1
n
n
1
Pelicula
n
n
Info Pelicula
Figura 4.23: Objetos del dominio
XML y enviarlos utilizando el protocolo HTTP.
Para manejar la información persistente se utilizará una aplicación del patrón
Value Object [44]. Este patrón permite encapsular varios datos relacionados en un
único objeto, facilitando el intercambio de información entre las diferentes capas
de la aplicación.
Los objetos pertenecientes a la clase Menu explicada en la figura 4.3 serán
definidos siguiendo el diseño mostrado en la figura 4.24. Con este diseño, VXMLS
mantendrá encapsulada la información necesaria para enviar la información sobre
los menús a DFBCIn.
Timer
Menu
− majorVersion: Int
− minorVersion: Into
− header: String
+ getMinorVersion(): Int
+ getMajorVersion(): Int
+ getHeader(): String
+ getTimer(): Timer
+ getInitActions(): Array
+ getOptions(): Array
+ getTimer(): Timer
+ getTimer(): Timer
HideMenu
ReloadMenu
HideText
0..1 − time: Int
+ getTime(): Int
+ getActions(): Array
initActions
Option
0..* − text: String
0..*
0..*
0..*
0..*
<<interface>>
Action
+ getText(): String
+ getActions(): Array
0..*
endActions
ChangeOption
NextMenu
PreviousMenu
SeekVideo
PlayVideo
− value: Int
− mode: String
− name: String
− argument: Array
− depth: Int
− reinit: Bool
− time: Int
− mode: String
− file: String
+ getValue(): Int
+ getMode(): String
+ getName(): String
+ getString(): Array
+ getDepth(): Int
+ getReinit(): Bool
+ getTime(): Int
+ getMode(): String
UnloadVideo
ShowMenu
SetProperty
ShowText
− name: String
− value: String
− lines: Array
StopVideo
+ getFile(): String
+ getEndActions(): Array
ResumeVideo
+ getLines(): Array
+ getName(): String
+ getValue(): String
BindKey
− key: String
− event: String
+ getKey(): String
+ getEvent(): String
+ getActions(): Array
Figura 4.24: Objetos valor utilizados para definir objetos Menu
Los objetos valor necesarios para almacenar la información sobre los usuarios,
78
Capı́tulo 4. Diseño
pelı́culas y lenguajes se puede ver en la figura 4.25.
SetTopBox
− id: String
− password: String
− currentUserId: String
− cookieId: String
+ getPassword(): String
+ getId(): String
+ getCookie(): Cookie
+ setCookie(cookie: Cookie)
+ getActiveUserId(): String
+ setActiveUser(userId: String)
Cookie
− id: String
− stpbxId: String
+ getId(): String
+ getStpbx(): Set Top Box
1..*
User
Language
− name: String
− languageId: String
− id: String
− name: String
+ getName(): String
+ getLanguageId(): String
+ setLanguage(languageId: String)
+ getId(): String
+ getName(): String
Shown Movies
Genre
0..*
− id: String
− name: String
− languageId: String
Movie
− id: String
− file: String
− year: Int
− genreId: String
+ getId(): String
+ getName(): String
+ getId(): String
+ getFile(): String
+ getInfo(lang: String): MovieInfo
+ getGenreId: String
+ getYear(): Int
1..*
MovieInfo
− title: String
− synopsys: String
+ getTitle(): String
+ getSynopsys(): String
TranslatedMovie
+ getMovie(): Movie
+ getMovieInfo(): MovieInfo
Figura 4.25: Objetos valor utilizados por VXMLS
La clase TranslatedMovie no representa a ningún objeto persistente, pero es
útil para almacenar toda la información relativa a una pelı́cula en un determinado
idioma.
La clase Genre representa la información referente a un determinado género
traducida a un idioma concreto. Esta clase representa a los objetos del dominio
Genero y Info Genero definidos en la figura 4.23. Los objetos Genero no necesitan
ser definidos ya que el único atributo que tienen es su propio identificador.
4.6.3.
Modelo entidad-relación
Para mantener los objetos persistentes en la base de datos se ha diseñado el
modelo entidad-relación de la figura 4.26.
Las tablas necesarias para implementar este modelo serán ocultadas al resto
de la aplicación utilizando los objetos valor explicados en la sección anterior.
4.6.4.
Diseño de la capa modelo
La capa modelo presentará una fachada (patrón Session Facade [44]) al resto
del sistema. Esta fachada proporciona una interfaz simple a los clientes, ocultando la interacciones complejas ente los objetos del dominio. De esta forma se evita
Diseño de VXMLS
79
stpbx id
cookie id
Cookie
1:1
cookie id
user name
0:1
user name
1:1
Set Top Box
1:n
1:1
password
User
1:1
stpbx id
stpbx id
1:1
0:n
language id
Shown
Movie
Language
0:n
title
movie id
0:n
0:n
lang name
movie id
1:1
user name
genre id
0:n
Movie Info
1:1
1:n
Movie
file
1:1
movie id
language id
synopsys
new
year
language id
1:1
0:n
Genre Info
genre name
1:1
genre id
1:n
Genre
genre id
Figura 4.26: Modelo entidad-relación
exponer la estructura interna de la capa modelo al resto del sistema.
El hecho de utilizar Mnesia [40] como base de datos elimina la necesidad de
diseñar una capa de DAOs [44] para desacoplar los métodos de la fachada de la
base de datos utilizada.
Por lo tanto, el modelo de VXMLS se compondrá únicamente de una clase
fachada que utilizará el API de Mnesia para acceder a los datos persistentes.
Los métodos públicos que deberá ofrecer la fachada son:
Obtener la contraseña de un determinado Set Top Box.
Crear una nueva cookie y enlazarla a un Set Top Box.
Eliminar una cookie (y desenlazarla del Set Top Box correspondiente).
Obtener el identificador del Set Top Box enlazado a una cookie.
Buscar todos los usuarios de un Set Top Box. Devolverá una lista de objetos
User.
Establecer el valor de una propiedad de personalización.
80
Capı́tulo 4. Diseño
Obtener el lenguaje seleccionado por un determinado usuario. No devuelve
el objeto Language en sı́, sino su identificador (que es el identificador ISO
del lenguaje).
Buscar todos los lenguajes soportados por el sistema. Devuelve una lista de
objetos Language.
Buscar todos los géneros. Devuelve una lista de objetos Genre.
Buscar todas las pelı́culas de un cierto género que ya han sido vistas por
un determinado usuario. Devuelve una lista de objetos TranslatedMovie.
Buscar las pelı́culas de un género que no han sido vistas por un usuario
dado. Podrá devolver una lista con las pelı́culas nuevas (atributo new de
la clase Movie) o con las viejas según se desee. Como para la operación
anterior, la lista devuelta contendrá objetos TranslatedMovie
Obtener la información sobre una pelı́cula traducida a un cierto idioma.
Devuelve un objeto TranslatedMovie.
Marcar una pelı́cula como vista.
Una alternativa a este diseño serı́a utilizar una fachada con estado (Stateful
Session Facade), pero la implementación de este patrón en Erlang es más complicado que el de una fachada sin estado (Stateless Session Facade).
No se ha desarrollado ninguna aplicación de administración para manipular
los contenidos de la base de datos. En caso de hacerse, dicha aplicación deberı́a
acceder a la misma base de datos que VXMLS pero a través de otra fachada que
ofreciera operaciones de creación y eliminación de tablas, ası́ como operaciones
para añadir información a las tablas existentes.
Para hacer las pruebas del sistema, estas operaciones se han codificado en
capa modelo de VXMLS, pero no estarán presentes en el API para el resto de la
aplicación. La única operación disponible para realizar labores de administración
será initDB, que creará las tablas necesarias e insertará los datos especificados
en un fichero de configuración. Esta operación no podrá ejecutarse con el sistema
en funcionamiento.
4.6.5.
Diseño de la vista y el controlador
La capa vista se compone de una única clase encargada de componer los documentos XML a enviar como respuesta ante las peticiones de los clientes DFBCIn.
Diseño de VXMLS
81
El controlador deberá atender las peticiones del usuario, actuar sobre el modelo para obtener la información necesaria y realizar las modificaciones oportunas
y, finalmente, utilizar las operaciones proporcionadas por la vista para enviar la
respuesta al usuario.
En la figura 4.27 se pueden ver las relaciones entre las distintas clases que
conforman la vista y el controlador (todas las clases pertenecen al controlador
excepto XMLUtil ).
VXMLS Facade
VXMLS Model
+ login(env: Array, args: Array): String
+ logout(env: Array, args: Array): String
+ getMenu(env: Array, args: Array): String
+ setProperty(env: Array, args: Array): String
Logger
PropertiesManager
Menu Dispatcher
+ login(env: Array, args: Array): String
+ logout(env: Array, args: Array): String
+ setProperty(env: Array, args: Array): String
+ getMenu(env: Array, args: Array): String
<<create>>
<<interface>>
MenuDocument
Lib
+ validateUser(args: Array): Cookie
+ validateCookie(env: Array): String
+ httpDocument(header: Array, content:String): String
+ httpDocument(content: String): String
+ XMLDocument(clientId: String, user: User, languageId: String, arguments: Array): String
XMLUtil
+ menu2xml(menu: Menu): String
+ errorReport(message: String): String
+ okMessage(): String
...
MainMenu
+ XMLDocument(clientId: String, user: User, languageId: String, arguments: Array): String
Map
1..*
+ getValue(key: String): String
Language Server
+ getMessage(langId: String, messageId: String): String
MenuComposer
+ compose(elements: Array): Menu
+ option(text: String, actions: Array): Option
+ timer(time: Int, data: Object): Timer
+ action(type: String, data: Object): Action
elements contiene los distintos
subobjetos que forman el menu
tales como los objetos Option,
un objeto Timer y los objetos
Action con las acciones de
inicializacion
Figura 4.27: Clases de la vista y el controlador de VXMLS
La clase VXMLSFacade es una clase utilidad que resulta de la aplicación
del patrón Facade [38]. Será la interfaz de entrada al controlador que utilizará Inets [39] para solicitar que se realicen las operaciones correspondientes a
una determinada URL (ver la sección 4.4).
El resto de clases realizarán las siguientes funciones:
Logger : Clase utilidad que se encargará de atender las URLs de petición
de login/logout. Los argumentos que recibe son env con los campos de la
cabecera HTTP de la petición hecha por el cliente y args con los argumentos pasados en la URL. En el caso de petición de login, deberá obtener
los argumentos client y passwd de la URL y utilizar la clase Lib para
comprobar que son correctos, en caso de que ası́ sea, utilizará el método
httpDocument para enviar un mensaje confirmando la operación y establecer la cookie devuelta por validateUser. Para realizar la operación de
82
Capı́tulo 4. Diseño
logout, utilizará la clase Lib para comprobar la validez de la sesión, el modelo para eliminar la cookie almacenada en la base de datos para identificar
la sesión que se estaba manteniendo para el usuario y, finalmente, de nuevo
la clase Lib para crear el mensaje HTTP de respuesta con un campo que
anula la cookie almacenada por el cliente. Para obtener el contenido de los
mensajes a devolver utilizará la clase XMLUtil.
PropertiesManager : Al igual que la clase anterior, atenderá las peticiones a
la URL utilizada para cambiar los valores de personalización. Extraerá los
argumentos name y value y utilizará PropertiesManager para realizar los
cambios necesarios. Para identificar el usuario utilizará Lib para validar
la cookie que deberá encontrarse entre los campos de la cabecera HTTP
descritos por env.
MenuDispatcher : Como las clases anteriores, atenderá las peticiones a la
URL utilizada para solicitar un menú, comprobando la sesión del usuario
utilizando la cookie. Obtendrá el menú a generar del argumento menu de la
URL, después instanciará un objeto perteneciente a la subclase de MenuDocument correspondiente e invocará el método xmlDocument para conseguir
la descripción XML del menú solicitado. Esta descripción será transformada
en un mensaje HTTP utilizando Lib.
XMLDocument: Esta interfaz define los objetos que serán los encargados
de crear los objetos Menu correspondientes a cada uno de los menús que
pueden ser solicitados por DFBCIn. En la figura 4.27 sólo se muestra MainMenu, que será la clase que describe el objeto que genera el menú principal,
pero el resto de implementaciones se relacionan de forma idéntica con el
resto de clases del sistema. Básicamente, lo que harán estos objetos cuando
se invoque el método xmlDocument será:
1. Solicitar a LanguageServer los mensajes necesarios, traducidos al lenguaje especificado por el argumento languageId.
2. Obtener la información necesaria utilizando el API expuesto por la
capa modelo.
3. Crear un nuevo objeto Menu utilizando la clase MenuComposer.
4. Transformar el objeto Menu creado en un documento XML utilizando
la clase XMLUtil.
Lib: Clase utilidad que realizará ciertas operaciones misceláneas para la
comprobación y creación de sesiones (validateUser y validateCookie) y
para la creación de documentos HTTP (las dos variantes de httpDocument).
Diseño de VXMLS
83
También tendrá operaciones para realizar otras operaciones poco importantes, como pueden ser las transformaciones entre distintos tipos utilizados
por la aplicación que, han sido omitidas para simplificar el diagrama.
XMLUtil : Clase utilidad para generar documentos XML. Como se vio en
apartados anteriores, algunas implementaciones de la interfaz Action son
una aplicación del patrón Composite [38]. Dadas las caracterı́sticas de los
documentos XML es fácil transformar un compuesto en un documento utilizando una aproximación al patrón Visitor [38]. Por ejemplo, el pseudocódigo para transformar una acción playVideoAction en una etiqueta XML
action con el contenido correspondiente serı́a:
render(action: PlayVideoAction) {
print("<action>")
print(" <playVideo file=", action.file>)
foreach(aux = action.endActions) {
render(aux)
}
print(" </playVideo>")
print("</action>")
}
XMLUtil “visita” cada componente del objeto Menu y lo transforma en
una parte del documento XML que lo describe. No es una aplicación pura
del patrón Visitor ya que la estructura interna de los objetos de la clase
Menu debe ser conocida por el el visitante, pero es más fácil trasladar este
diseño a una implementación en Erlang que el diseño propuesto en [38], en
el que la clase visitada debe proporcionar un método que acepte la visita. La
generación de los mensajes de error o de confirmación no presenta ningún
problema ya que son documentos XML muy sencillos y con definiciones no
recursivas.
MenuComposer : Clase utilidad que proporciona las operaciones necesarias
para componer objetos Menu. Es una aplicación del patrón Builder [38], las
implementaciones de MenuDocument actúan como directores y MenuComposer como constructor.
LanguageServer : Es una aplicación del patrón Singleton [38]. Controlará un
conjunto de mapas en los que estarán almacenadas las traducciones de los
diferentes mensajes visuales a los idiomas soportados por la aplicación. Las
diferentes traducciones se cargarán de ficheros planos en los que se asociarán
las claves identificadoras para cada mensaje con la cadena a mostrar para
ese identificador.
Capı́tulo 5
Implementación de DFBCIn
Índice General
5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . .
86
5.2. Estándares de codificación . . . . . . . . . . . . . . . .
87
5.2.1. Nombres . . . . . . . . . . . . . . . . . . . . . . . . . .
87
5.2.2. Objetos . . . . . . . . . . . . . . . . . . . . . . . . . .
89
5.3. Control de errores . . . . . . . . . . . . . . . . . . . . .
90
5.4. Control de concurrencia . . . . . . . . . . . . . . . . .
92
5.5. Tipos de datos de uso general . . . . . . . . . . . . . .
94
5.5.1. Pilas . . . . . . . . . . . . . . . . . . . . . . . . . . . .
94
5.5.2. Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . .
95
5.6. La clase Menu . . . . . . . . . . . . . . . . . . . . . . .
96
5.7. La clase Option . . . . . . . . . . . . . . . . . . . . . . .
98
5.8. La clase Key . . . . . . . . . . . . . . . . . . . . . . . .
99
5.9. La interfaz Action . . . . . . . . . . . . . . . . . . . . . 100
5.9.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . 100
5.9.2. Tipos de datos . . . . . . . . . . . . . . . . . . . . . . 100
5.9.3. Funciones . . . . . . . . . . . . . . . . . . . . . . . . . 104
5.9.4. ¿Cómo instanciar una acción? . . . . . . . . . . . . . . 107
5.9.5. ¿Cómo añadir una nueva acción? . . . . . . . . . . . . 108
5.10. Implementación del motor de la aplicación . . . . . . 108
5.10.1. Main
. . . . . . . . . . . . . . . . . . . . . . . . . . . 108
5.10.2. Interface
. . . . . . . . . . . . . . . . . . . . . . . . . 110
85
86
Capı́tulo 5. Implementación de DFBCIn
5.10.3. InterfaceInput
. . . . . . . . . . . . . . . . . . . . . . 114
5.10.4. Mp3Player . . . . . . . . . . . . . . . . . . . . . . . . 116
5.11. Implementación del módulo VXMLS . . . . . . . . . . 118
5.11.1. Módulo HTTP . . . . . . . . . . . . . . . . . . . . . . 118
5.11.2. Parser VXMLS . . . . . . . . . . . . . . . . . . . . . . 120
5.11.3. Parser para los documentos menu . . . . . . . . . . . . 121
5.11.4. Implementación de la fachada VXMLS . . . . . . . . . 133
5.12. Implementación del Módulo gráfico . . . . . . . . . . 136
5.12.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . 136
5.12.2. Estructuración del código . . . . . . . . . . . . . . . . 137
5.12.3. La fachada graphics.c . . . . . . . . . . . . . . . . . 137
5.12.4. La inicialización de DirectFB . . . . . . . . . . . . . 138
5.12.5. Control de eventos de entrada . . . . . . . . . . . . . . 138
5.12.6. Impresión de menús y texto . . . . . . . . . . . . . . . 140
5.12.7. Reproducción de vı́deo . . . . . . . . . . . . . . . . . . 141
5.12.8. Módulo de pruebas . . . . . . . . . . . . . . . . . . . . 144
5.13. Sistema de propiedades . . . . . . . . . . . . . . . . . . 145
5.14. Etapas de codificación . . . . . . . . . . . . . . . . . . . 145
5.14.1. Desarrollo del núcleo de la aplicación . . . . . . . . . . 145
5.14.2. Reproducción básica de vı́deo . . . . . . . . . . . . . . 146
5.14.3. Pruebas en el Set Top Box
. . . . . . . . . . . . . . . 147
5.14.4. Varias mejoras . . . . . . . . . . . . . . . . . . . . . . 147
5.14.5. Comunicación HTTP
. . . . . . . . . . . . . . . . . . 148
5.14.6. Integración con VXMLS . . . . . . . . . . . . . . . . . 148
5.14.7. Integración con VoDKA . . . . . . . . . . . . . . . . . 149
5.14.8. Reproductor Mp3 y acciones de texto . . . . . . . . . 149
5.1.
Introducción
La implementación en C de un diseño orientado a objetos supone ciertas dificultades. C es un lenguaje imperativo de bajo nivel, la gestión de memoria, de
errores y de concurrencia se debe hacer de forma explı́cita. Además, el concepto
Estándares de codificación
87
de objeto debe ser simulado de alguna forma, combinando estructuras de datos
con funciones.
Como norma general, la implementación de un objeto consistirá en un módulo en el que se definirán las estructuras de datos necesarias para almacenar la
información relevante para cada objeto instanciado y las funciones que operarán
sobre dichas estructuras de datos. En la sección 5.2 se explica con más detalle la
estructura de estos módulos.
Como se ha dicho en la fase de análisis, el ciclo de vida de DFBCIn seguirá el
paradigma incremental. En la primera iteración se desarrollará un sistema que
aporte la funcionalidad más básica. Una vez hecho esto, se irán añadiendo posibilidades hasta obtener el sistema final.
A lo largo de este capı́tulo se explica la implementación del sistema final, en
la sección 5.14 se explican los pasos que se dieron hasta alcanzar esta implementación.
5.2.
Estándares de codificación
5.2.1.
Nombres
Tanto los nombres como el contenido de los comentarios están escritos en inglés
para facilitar la lectura del código a cualquier persona. Para conseguir un espacio
de nombres suficientemente homogéneo y facilitar la compresión del código, se
seguirán una serie de reglas a la hora de elegir los nombres de las funciones, tipos
de datos y variables utilizadas.
Los nombres compuestos estarán formados por las palabras concatenadas,
marcando el principio de la cada palabra con una letra mayúscula: nombreCompuesto.
Los nombres de los Tipos de datos comenzarán con mayúsculas: NombreDeTipoDeDato.
Las variables y funciones comenzarán con minúsculas: nombreDeVariable y
nombreDeFunción().
Las funciones de creación (reserva de memoria) para un determinado tipo
de dato TipoDeDato se llamarán newTipoDeDato.
88
Capı́tulo 5. Implementación de DFBCIn
Las funciones de liberación de memoria para el tipo de dato TipoDeDato
se llamarán freeTipoDeDato.
En caso que se lleve un control de referencias para una clase NombreDeClase
cuyos objetos puedan ser compartidos y liberados por diferentes módulos
de la aplicación, la memoria necesaria para el tipo de datos que representa
los argumentos del objeto (ver sección 5.2.2) se manejará con las funciones
newNombreDeClase, addNombreDeClaseReference y releaseNombreDeClase. addNombreDeClaseReference se utilizará para señalar que hay una nueva
referencia al objeto y releaseNombreDeClase para indicar que una referencia ya no va a seguir siendo utilizada. Cuando la última referencia deje de
ser utilizada se liberará la memoria reservada para los datos del objeto en
cuestión.
En algunos casos es necesario exportar funciones que van a ser utilizadas por
macros que oculten ciertos aspectos, o que van a ser utilizadas solamente
como callbacks, existiendo otra función o macro que será utilizada en la
mayorı́a de los casos para cumplimentar el objetivo al que está orientado
dicha función. En estos casos la función se llamará nombreDeFunción, y
la macro o función de uso general que utilice dicha función será llamada
nombreDeFunción.
Los tipos de datos suelen ser punteros a estructuras. El nombre de la estructura es irrelevante para la aplicación, excepto en el momento en el
que se necesita reservar la memoria para dicha estructura. El nombre de la
estructura de datos a la que apunta un tipo PunteroAEstructura será PunteroAEstructura.
las funciones exportadas por un módulo que implemente una clase utilidad
(ver sección 5.2.2) Módulo se llamarán móduloFunción1, móduloFunción2,
...
En caso de que un módulo Módulo necesite ser inicializado antes de ser
utilizado, deberá definir las funciones móduloInit y móduloDeinit para las
labores de inicialización y liberación del módulo.
Las funciones que operan sobre un tipo de datos TipoDeDatos tendrán el
nombre del tipo de datos como parte de su nombre compuesto, por ejemplo
operaciónSobreTipoDeDatos.
Los nombres de las macros se escribirán en mayúsculas, con barras bajas
como separadoras entre las palabras: NOMBRE DE MACRO.
Estándares de codificación
89
los nombres de los ficheros fuente estarán en minúsculas y utilizarán un
guión como separador de palabras en caso de que sea necesario: nombre-defichero.c, nombre-de-fichero.h.
5.2.2.
Objetos
La implementación de una determinada clase del diseño se hará dependiendo
de si la clase es una clase utilidad, una clase que define un objeto Singleton [38]
o una clase que define objetos que van a ser compartidos por diferentes módulos
del programa.
Una clase utilidad, por ejemplo ClaseUtilidad, será implementada creando un
módulo ClaseUtilidad formado por el fichero clase-utilidad.c y la cabecera
clase-utilidad.h. Las funciones exportadas en clase-utilidad.h deberán tener nombres que comiencen por claseUtilidad, para facilitar la lectura del código
(claseUtilidadNombre, serı́a equivalente a una llamada al método nombre de la
clase ClaseUtilidad en un lenguaje orientado a objetos).
Los objetos Singleton serán implementados de forma similar a una clase utilidad, pero normalmente será necesario que el código que se encargue de simular
su comportamiento esté en un nuevo hilo de ejecución. En la sección 5.4 se explica como se hará el control de los módulos que generan nuevos hilos de ejecución.
La implementación de los objetos que vayan a ser compartidos entre diferentes módulos de la aplicación dependerá de si es necesario llevar las referencias del
objeto para poder liberar la memoria o no. En ambos casos, el objeto a compartir
se codificará como un tipo de dato que será un puntero a una estructura en la
que se almacenará la información necesaria sobre el objeto en cuestión (puede ser
necesario que dicha estructura también contenga algún semáforo o similar para
controlar el acceso concurrente a sus contenidos) y una serie de funciones que simulen los métodos definidos por la clase a la que pertenece el objeto que estamos
tratando. A estas funciones será necesario pasarles la estructura que contiene los
datos del objeto.
Por ejemplo si se quiere ejecutar el método objetoCompartido.método, la lı́nea
de código será métodoObjetoCompartido(objetoCompartido, ...).
El constructor del objeto será la función newNombreDeClase explicada en la
sección 5.2.1.
En caso de que sea necesario llevar un control de las referencias existentes al
90
Capı́tulo 5. Implementación de DFBCIn
objeto, para poder liberar su memoria, será necesario añadir un campo a la estructura de datos que contiene los atributos del objeto para contar dichas referencias. Para ello se definirá la función addNombreDeClaseReference que deberá ser
llamada por cada nuevo módulo que vaya a mantener una referencia al objeto.
La función releaseNombreDeClase liberará una referencia. La memoria reservada
para mantener la información necesaria sobre el objeto será liberada cuando el
contador de referencias llegue a cero.
Las llamadas a releaseNombreDeClase deben ser sincronizadas para evitar
problemas de concurrencia en caso de que varios hilos intenten ejecutar esta función simultáneamente, lo que podrı́a dejar la memoria sin liberar. No es necesario
sincronizar addNombreDeClaseReference porque se supone que se está accediendo de forma segura al objeto que se quiere referenciar en el momento en el que
se añade dicha referencia, es decir, no hay peligro de que se haga una llamada
releaseNombreDeClase que vaya a liberar la memoria ocupada por el objeto durante la ejecución de addNombreDeClaseReference, puesto que la referencia debe
ser añadida en un hilo que ya mantenga su propia referencia.
5.3.
Control de errores
El estado de error de la aplicación se controla con la función setAppError,
con la que se puede establecer el error que se ha detectado y el fichero y la lı́nea
en la que se ha producido. Como se explicó en la sección 5.2.1, un nombre que
empieza por el caracter no debe ser utilizado directamente, esta función se exporta para que pueda ser utilizada por las macros que se definen para realizar el
control de errores a lo largo de la aplicación.
Los errores detectados se clasificarán según un tipo que está definido por la
enumeración AppError. Se define una variable global de este tipo que será manipulada por las funciones exportadas por el módulo errors (estas funciones están
declaradas en el fichero errors.h). Esta variable puede tomar uno de los siguientes valores :
NO ERROR: No se ha detectado ningún error.
MULTIPLE ERROR: Se ha llamado más de una vez a setAppError.
NO MEMORY : No hay suficiente memoria.
INVALID OPERATION : Se ha intentado realizar una operación no permitida.
Control de errores
91
PARSER ERROR: Error durante el proceso de interpretación de un documento de texto.
IMPLEMENTATION ERROR: Se ha alcanzado una sección del código que
no deberı́a ser alcanzada. Se trata de un error en la implementación.
VERSION ERROR: Se ha recibido un documento con una versión mayor
a la que se puede manejar.
EXTERNAL ERROR: Fallo en algo ajeno a la implementación (por ejemplo, fallo en una llamada al sistema).
BAD FILE FORMAT : Un fichero no cumple la estructura que se esperaba.
LOST PROPERTY : No se puede encontrar el valor de una propiedad obligatoria.
DOWNLOAD ERROR: Fallo en la descarga de un fichero.
VXMLS ERROR: Fallo en el servidor VXMLS.
MPG123 ERROR: Fallo en el reproductor de mp3.
Las funciones exportadas para permitir el control de errores durante la ejecución de la aplicación son:
void setErrorMessage(const char *message): Se utiliza para establecer un mensaje explicativo que será escrito por printAppError junto con
la descripción genérica para el estado de error en el que se haya la aplicación.
AppError getAppError(void): Devuelve el estado de error actual.
void unsetAppError(void): Establece NO ERROR como estado de error.
void printAppError(void): Escribe un mensaje explicativo en el descriptor de error estándar.
short existsError(void): Devuelve 0 en caso de que el estado de error
sea NO ERROR y un número distinto de 0 en otro caso.
Para la aplicación desarrollada, el comportamiento que se seguirá en caso de
se produzca una situación excepcional será establecer el error correspondiente y
finalizar la ejecución lo antes posible, haciendo una llamada a printAppError
antes de terminar para notificar las causas del fallo. Para ello, en errors.h se
define la macro DFBCIN FATAL, que provoca la finalización inminente de la aplicación en un estado de error dado. Es posible llamar a setErrorMessage antes
para añadir información al mensaje mostrado en la finalización de la aplicación.
92
Capı́tulo 5. Implementación de DFBCIn
#define DFBCIN_FATAL(error)
{
_setAppError(error, __LINE__, __FILE__);
printAppError();
unsetAppError();
exit(EXIT_FAILURE);
}
\
\
\
\
\
\
También hay algunas funciones que pueden detectar errores durante su ejecución, pero que no pueden decidir si dicho error es lo suficientemente grave como
para finalizar la ejecución o puede ser solventado de alguna otra forma. Estas
funciones deberán devolver una variable de tipo AppError. En caso de que una
llamada a una de estas funciones deba devolver un valor siempre correcto, la
función que realiza la llamada puede utilizar la macro DFBCIN CHECK que terminará la ejecución en caso de que la función llamada devuelva un valor indicando
un error.
#define DFBCIN_CHECK(x...)
{
int err;
err = (x);
if (err != NO_ERROR) {
_setAppError(err, __LINE__, __FILE__);
printAppError();
unsetAppError();
exit(EXIT_FAILURE);
}
\
\
\
\
\
\
\
\
\
\
\
}
5.4.
Control de concurrencia
En la sección 5.2.2, se comentó que para codificar los objetos de algunas de
las clases del diseño serı́a necesario añadir nuevos hilos de ejecución. La programación multi-hilo en un lenguaje como C debe hacerse cuidadosamente, ya que
posibilita la aparición de problemas muy difı́ciles de depurar.
La implementación de aquellos objetos que tengan un ciclo de ejecución propio
se hará en dos ficheros, el fichero fuente y la cabecera con las funciones exportadas. Para explicar cómo se estructurará el código que implementa este tipo de
objetos se utilizará como ejemplo una clase ficticia Singleton, que se codificará en
Control de concurrencia
93
los ficheros singleton.c y singleton.h.
En singleton.h se exportarán las funciones que serán accesibles para el resto de la aplicación: singletonMethod, singletonMethod2, etc. Entre estas funciones
serán necesarias las funciones singletonInit y singletonDeinit para permitir la
creación y la destrucción del objeto.
En el fichero singleton.c se definirán las funciones y estructuras de datos locales. En este caso, será necesaria una estructura de datos en la que mantener
los datos referentes al estado del objeto y los semáforos que se utilizarán para el
control de concurrencia. Será necesario definir al menos:
Un tipo de datos SingletonState que describa el estado interno del objeto
que se está implementando.
Una variable global (pero no accesible desde fuera de este módulo) singletonState de tipo SingletonState que contenga el estado interno del objeto.
Un semáforo singletonMutex para controlar el acceso concurrente a singletonState y que posibilite el control de la ejecución del nuevo hilo utilizando
una condición singletonCond.
Una función singetonThread que contenga el código necesario para que el
objeto creado se comporte como se espera, de acuerdo con las especificaciones hechas en el diseño.
La función singletonInit deberá crear la variable singletonState y lanzar el
nuevo hilo de ejecución, que estará implementado por la función singletonThread.
La función singletonThread consistirá en un bucle que se ejecutará con el
semáforo singletonMutex cerrado. El cuerpo del bucle realizará las acciones necesarias de acuerdo con el estado del objeto. En algún momento, la ejecución
deberá ser detenida en la condición singletonCond. En este momento se liberará el semáforo para permitir a las funciones exportadas modificar el estado del
objeto para variar su comportamiento.
Las funciones exportadas en singleton.h no realizarán el trabajo solicitado
por si mismas, sino que accederán a singletonState, siempre después de haber
cerrado el semáforo singletonMutex, modificarán el estado para que el hilo de
ejecución del objeto sepa las acciones que tiene que realizar y despertarán la ejecución del hilo singletonThread que deberı́a estar parado en la condición, para
que compruebe de nuevo el estado y actúe en consecuencia. De esta forma, el
94
Capı́tulo 5. Implementación de DFBCIn
ciclo de ejecución del objeto es concurrente con la ejecución del hilo principal del
programa, pero los accesos a sus datos privados se mantienen sincronizados, ya
que las funciones exportadas para la aplicación sólo pueden modificar del estado
del objeto en un punto controlado.
En caso de que el ciclo de ejecución del objeto no pueda ser detenido, se
deberá codificar un punto en el que se libere el semáforo y se de paso a un nuevo
proceso (utilizando las funciones del planificador) para permitir que las funciones
exportadas puedan cambiar el estado interno del objeto:
STATE_UNLOCK;
sched_yield();
STATE_LOCK;
5.5.
Tipos de datos de uso general
5.5.1.
Pilas
Las pilas serán utilizadas para almacenar la información necesaria durante
el proceso de interpretación de los ficheros XML que describen los menús de la
interfaz y para almacenar los objetos Menu que se van cargando a medida que el
usuario profundiza en la estructura de menús definida.
Las pilas no necesitarán ser compartidas entre distintos módulos del sistema,
por lo que no será necesario implementar el mecanismo de referencias.
Para implementar la clase Stack (que representará a una pila de contenido
genérico) se definirá un tipo de dato abstracto Stack y una serie de funciones
que operen sobre él. El tipo Stack está completamente encapsulado, el resto de
módulos del sistema no tendrán acceso a su estructura interna.
El API exportado para la manipulación de pilas se compone de las siguientes
operaciones:
Stack newStack(void): Crea una nueva pila.
short isEmptyStack(Stack stack): Devuelve un número distinto de cero
en caso de que la pila esté vacı́a.
void pushStack(Stack stack, void *data): Añade un nuevo dato a la
cima de la pila.
Tipos de datos de uso general
95
void freeStack(Stack stack): Libera la memoria ocupada por la pila.
La pila debe estar vacı́a para que se pueda realizar esta operación, en otro
caso la aplicación terminará debido a un error INVALID OPERATION.
void *popStack(Stack stack): Devuelve el dato de la cima de la pila y
lo borra de la misma.
void *seeStackTop(Stack stack): Devuelve el dato de la cima de la pila,
pero no lo borra de la misma.
5.5.2.
Arrays
Los arrays se utilizarán para almacenar una cantidad indeterminada de datos
de forma que puedan ser compartidos entre distintos módulos del sistema. En
la aplicación, la utilización de los arrays consistirá en crearlos, añadir un cierto
número de elementos y, una vez añadidos todos los datos necesarios, transmitirlos a los módulos que los necesiten para que realicen operaciones sobre los datos
almacenados. No será necesario borrar datos ya existentes o añadir nuevos datos
a un array que ya ha sido compartido.
La implementación de los arrays utilizará el sistema de referencias que se
describió en la sección 5.2.2. Para posibilitar la liberación de toda la memoria
ocupada por un array (incluida la memoria que ocupan sus datos), en la función
de creación del array se deberá especificar una función que sea capaz de liberar
la memoria del tipo de datos que se vaya a almacenar en el array creado. Con
estos dos mecanismos, un array con información puede ser compartido por varios
módulos y puede ser fácilmente liberado cuando dichos módulos dejen de utilizarlo.
Al igual que las pilas, los arrays se implementan utilizando un tipo abstracto
de datos completamente encapsulado y un cierto número de funciones para operar
sobre él.
El API para manipular arrays es:
Array newArray(FreeDataFunc1 freeFunc): Crea un nuevo array en el
que se almacenará un cierto tipo de datos. freeFunc será una función capaz
de liberar la memoria ocupada por una variable del tipo de datos almacenado.
1
el tipo FreeDataFunc se define como void (*FreeDataFunc) (void *data)
96
Capı́tulo 5. Implementación de DFBCIn
void releaseArray(Array array): Se utiliza para señalar que se va a dejar de utilizar el array, eliminando una de las referencias al mismo. Cuando
se libera un array de forma que ya no queden referencias al mismo se libera
también la memoria ocupada por array, incluyendo la memoria ocupada
por todos los elementos almacenados.
void flushArray(Array array): Vacı́a el array liberando la memoria ocupada por todos los datos almacenados en el mismo.
unsigned numArrayElements(Array array): Devuelve el número de elementos almacenados en el array.
void addArray(Array array, void *data): Añade un nuevo elemento al
final del array.
void *getArrayData(Array array, unsigned nth): Devuelve el elemento almacenado en la posición nth del array. El primer elemento se almacena
en la posición 0.
void addArrayReference(Array array): Indica que hay un nuevo módulo utilizando el array añadiendo una nueva referencia al mismo.
La implementación que se ha hecho de los arrays sólo comprueba la sincronización de las llamadas a releaseArray, para evitar que se produzcan dos llamadas
concurrentes con el riesgo de que quede la memoria sin liberar. El resto de funciones no están sincronizadas. Esta implementación no contempla la posibilidad
de que varios módulos intenten añadir datos de forma concurrente.
5.6.
La clase Menu
Los objetos de la case Menu (ver sección 4.5.3) serán compartidos por varios
módulos, pero la creación y destrucción de estos objetos no necesita ser controlada con el sistema de referencias. Los objetos Menu sólo podrán ser creados por
el módulo VXMLS, y serán almacenados en una pila global, de forma que siempre esté accesible el menú mas recientemente utilizado. La destrucción de estos
objetos se hará a medida que se desapilen. De esto se encargarán algunas de las
diferentes acciones (en el sentido de instancias de Action) implementadas.
Por todo ello, el hecho de que los objetos de la clase Menu vayan a ser compartidos no complica su creación y destrucción, pero sı́ será necesario añadir
algún sistema para evitar escrituras concurrentes. Por lo tanto, el tipo de datos
que contendrá los atributos de los objetos de la clase Menu también deberá tener
La clase Menu
97
asociado un semáforo que deberá ser bloqueado antes de acceder a sus contenidos.
El tipo Menu definido no será un tipo completamente encapsulado como en el
caso de los arrays o de las pilas. En este caso el resto de la aplicación deberá conocer la estructura interna del tipo Menu para poder modificar su contenido.
Una variable de tipo Menu será un puntero a una estructura con los siguientes
campos:
short majorVersion, minorVersion: Indican la versión del esquema de
menú utilizado, en caso de que DFBCIn reciba un menú con una versión
mayor a la soportada deberá actualizarse2 .
char header[]: Tı́tulo a mostrar para el menú.
Array options: Sostiene la agregación de objetos Option.
int time: Tiempo que debe pasar sin que se reciba un evento de entrada
para que se disparen las acciones de temporización, si es -1 se supone que
la espera es infinita (no hay acciones de temporización).
Array timeActions: Conjunto de acciones a ejecutar en caso de que termine la temporización.
Array initActions: Conjunto de acciones a ejecutar cuando se cargue el
menú por primera vez.
short currentPosition: Opción actualmente seleccionada.
Key keys[]: Conjunto de objetos Key utilizados para almacenar la información sobre las acciones a realizar en caso de que se detecte un determinado
evento de entrada (ver sección 5.8).
char name[]: Nombre con el que fue solicitado el menú a VXMLS.
Array arguments: Lista con los argumentos que se utilizaron para solicitar
este menú a VXMLS.
pthread mutex t mutex: Semáforo utilizado para la sincronización de los
accesos concurrentes a los contenidos de esta estructura.
Las funciones exportadas para manejar este tipo de datos son sólo las de
creación, destrucción, bloqueo y desbloqueo, ya que el resto de operaciones se
deberán hacer accediendo directamente a los contenidos de la estructura.
2
Esta caracterı́stica aún no está implementada, es una lı́nea de trabajo futura
98
Capı́tulo 5. Implementación de DFBCIn
Menu newMenu(void): Devuelve una variable de tipo Menu ya inicializada
(con memoria reservada tanto para la estructura de datos como para los
arrays que contiene).
void lockMenu(Menu menu): Bloquea menu para evitar el acceso concurrente.
void unlockMenu(Menu menu): Desbloquea menu.
void freeMenu(Menu menu): Libera la memoria ocupada por menu. Nótese
que si a alguno de los arrays que contiene menu le fueron añadidas nuevas
referencias éste no será liberado durante este proceso. De esta forma se pueden preservar acciones a ejecutar aunque se libere el menú que las contiene.
5.7.
La clase Option
Los objetos de la clase Option no serán compartidos directamente, sino que
serán compartidos a través del menú que los contiene. Además, el acceso concurrente a sus contenidos siempre será a través del tipo Menu (de hecho, en el
diseño se especificó que los objetos de la clase Option tienen una relación de composición con la clase Menu, ver la sección 4.5.3). Se supone que los accesos a una
variable de tipo Menu siempre serán sincronizados, por lo que no será necesario
tomar ninguna medida adicional para controlar los accesos a este tipo de objetos.
La implementación de este tipo de objetos no presenta ningún tipo de complicación. Como en casos anteriores, se definirá un tipo de datos Option, no encapsulado, que será un puntero a una estructura en la que se almacenarán los
atributos del objeto. Las funciones exportadas para manejar este tipo de datos
serán únicamente la función de creación y la de destrucción.
El método execute de la clase Option no puede ser implementado directamente. El subsistema Interface (ver sección 4.5.4.2) se encargará de recolectar las
acciones a ejecutar y de enviárselas a Main para que las ejecute.
La estructura de datos implementada tiene sólo dos campos :
char text[]: Texto a mostrar para identificar la opción.
Array actions: Grupo de acciones a ejecutar en caso de que el usuario
seleccione esta opción.
Y las funciones exportadas también son solamente dos:
La clase Key
99
Option newOption(void): Devuelve una variable de tipo Option con memoria reservada.
void freeOption(Option option): Libera la memoria ocupada por la variable option. Al igual que lo comentado para Menu, si las acciones fueron
compartidas por otro módulo, estas no serán liberadas hasta que sean liberadas por este otro módulo.
5.8.
La clase Key
La clase Key se utilizará para almacenar la información sobre las acciones a
realizar para cada evento de entrada detectado3 . Como se puede ver en 4.5.3 la
pulsación de una tecla puede estar asociada a tres tipos de evento: ACTIONS, lo
que provocará la ejecución de un grupo de objetos de la clase Action; EXECUTE, que provocará la ejecución del grupo de objetos Action asociados a la opción
seleccionada; o NULL, que indica que la pulsación de la tecla debe ser ignorada.
Para implementar este tipo de objetos se define un tipo de datos que apunta
a una estructura con dos elementos:
KeyBind keyBind: Indica el tipo de acción a realizar. El tipo KeyBind es
una enumeración que puede tomar los valores KBIND NULL, KBIND EXECUTE, o KBIND ACTIONS que se corresponden con los valores NULL,
EXECUTE y ACTIONS que puede tomar el atributo event de la clase Key.
Array actions: Representa la agregación de acciones a ejecutar.
Como ya se comentó para la clase Option el método execute no se implementa
directamente. Interface se encargará de pasarle el grupo de acciones correspondiente a Main para que las ejecute en caso de que la tecla pulsada lo requiera:
Si la tecla está asociada con un evento EXECUTE se provocará la ejecución de Option.execute, lo que equivale a pasarle a Main el array actions
del elemento de tipo Option situado en la posición indicada por el campo
currentPosition del menú actual en el array options de dicho menú.
Si la tecla está asociada con un evento ACTIONS se le pasará a Main el
array actions.
Si la tecla está asociada a un evento de tipo NULL la llamada a execute
no tiene ningún efecto.
3
Un evento de entrada se produce cuando el usuario pulsa una tecla del mando a distancia
100
Capı́tulo 5. Implementación de DFBCIn
El atributo key de la clase Key no necesita codificarse explı́citamente, ya que
los objetos que definen todas las teclas posibles están almacenados en el atributo
keys del tipo Menu, la posición en la que está almacenada ya indica qué tecla
es. En el fichero input-keys.h se define la enumeración InputKey que se utilizará para indexar los estos objetos.
Por ejemplo: Si el usuario pulsa la tecla SEL, el objeto que represente dicha
tecla se obtendrá accediendo a (menu->keys)[INPT SEL], suponiendo que menu
sea la variable que representa el menú actual.
5.9.
La interfaz Action
5.9.1.
Introducción
Los objetos pertenecientes a la case Action van a ser compartidos por diferentes módulos del sistema. Todas las acciones a ejecutar estarán en un principio en
alguno de los arrays de algún menú que se haya descargado de VXMLS. Después
de esto, se pueden mantener diferentes referencias al mismo array de acciones,
por ejemplo, cuando una tecla pasa a estar vinculada con un array de acciones
tras la ejecución de una acción BindKey el objeto Key que representa a la tecla
vinculada pasa a referenciar el array de acciones que se encuentra entre los datos
del objeto BindKey. En caso de que se detecte que el usuario ha pulsado esa tecla,
el mismo array de acciones también será referenciado por Main para proceder a
su ejecución.
Sin embargo, no es necesario controlar las referencias a las acciones en sı́, ya
que siempre se compartirán a través de un array, y el tipo Array ya implementa
el control de concurrencia (ver sección 5.5.2), asegurando que ni él ni su contenido
serán liberados hasta que todas las referencias hayan sido liberadas.
5.9.2.
Tipos de datos
5.9.2.1.
Implementación de la interfaz Action
Para implementar esta interfaz de forma que sea fácil la creación de nuevas
acciones que la respeten se ha definido un tipo Action que apunta a una estructura
con dos campos:
ActionType actionType: El tipo ActionType se utiliza para definir el tipo
de acción que se está representando (es decir, a qué clase pertenece).
La interfaz Action
101
ActionData actionData: El tipo ActionData representa un campo de datos genéricos desde el que se puede acceder a los diferentes atributos de
cada tipo de acción implementado.
El tipo ActionType será una enumeración en la que se podrán ir añadiendo
los nombres de los nuevos tipos de acción que se vayan definiendo. Los tipos de
acción que se han implementado para el sistema actual son:
NEXT MENU ACTION: Acción para navegar a un nuevo menú
PREVIOUS MENU ACTION: Permite volver a un menú anterior, descartando
los menús que se hayan visitado después de ese.
HIDE MENU ACTION: Oculta el menú actual.
SHOW MENU ACTION: Muestra el menú actual.
PLAY VIDEO ACTION: Inicia la reproducción de un vı́deo.
STOP VIDEO ACTION: Detiene el de vı́deo que se está reproduciendo.
RESUME VIDEO ACTION: Continúa con la reproducción de vı́deo anteriormente detenida.
SEEK VIDEO ACTION: Salta a una determinada posición del vı́deo que se
está reproduciendo.
UNLOAD VIDEO ACTION: Termina la reproducción de vı́deo.
CHANGE OPTION ACTION: Cambia la opción actual.
BIND KEY ACTION: Cambia el evento asociado a una determinada tecla.
SET PROPERTY ACTION: Cambia el valor de una propiedad de personalización.
RELOAD MENU ACTION: Vuelve a solicitar el menú actual a VXMLS.
SHOW TEXT ACTION: Muestra un texto por pantalla.
HIDE TEXT ACTION: Oculta el texto que se estaba mostrando.
LOAD MP3 ACTION: Carga un fichero de audio mp3 e inicia su reproducción.
PAUSE CONTINUE MP3 ACTION: Detiene/continúa la reproducción de audio
en curso.
102
Capı́tulo 5. Implementación de DFBCIn
KILL MP3 PLAYER ACTION: Fuerza la eliminación del reproductor de mp3.
Esto es necesario porque se necesita liberar el dispositivo /dev/dsp para que
la reproducción de vı́deo pueda utilizar la tarjeta de sonido. Si se detiene
la reproducción de audio con la acción PauseContinueMP3 el dispositivo
/dev/dsp sigue estando bloqueado por el reproductor de mp3.
El tipo ActionData será un puntero a una unión en la que se podrán definir
campos para almacenar los datos necesarios para la ejecución de los diferentes
tipos de acción.
5.9.2.2.
Tipos de datos para las acciones concretas
Para las acciones que se han implementado, hay nueve que necesitan información especı́fica que será almacenada en el campo actionData. Sabiendo qué acción
se está tratando (está indicado por el campo actionType) se puede acceder a sus
datos a través de la unión apuntada por ActionData. Estos campos pueden ser
uno de los siguientes:
NextMenuActionData nextMenuActionData: El tipo NextMenuActionData
se utiliza para almacenar la información que se necesita para ejecutar una
acción NextMenu. Apunta a una estructura con los campos:
• char nextMenuName[]: Nombre del menú a solicitar a VXMLS.
• Array arguments: Array con los argumentos con los que se va a solicitar el menú, ordenados de forma nombre 1, valor 1, nombre 2, valor
2, etc.
PreviousMenuActionData previousMenuActionData: Las variables de tipo PreviousMenuActionData contienen la información necesaria para ejecutar una acción PreviousMenu. Apunta a una estructura con dos campos:
• int returnDepth: Número de menús a retroceder.
• short reinit: Si es 0 no se ejecutarán las acciones de inicialización
del menú.
PlayVideoActionData playVideoActionData: En este campo se almacena
la información que se necesita para ejecutar una acción PlayVideo. El tipo
PlayVideoActionData apunta a una estructura con dos campos:
• char videoPath[]: Ruta completa del fichero de vı́deo.
La interfaz Action
103
• Array endActions: Acciones a ejecutar cuando se alcance el final del
vı́deo.
ChangeOptionActionData changeOptionActionData: El tipo ChangeOptionActionData se utiliza para almacenar la información necesaria para la
ejecución de las acciones de tipo ChangeOption. Apunta a una estructura
con los campos:
• int value: Número a sumar a la posición actual, o número de la
opción a seleccionar, dependiendo de si absolute sea 0 o distinto de
0.
• short absolute: Indica si value se debe interpretar como valor a
sumar a la posición actual o como posición de destino.
BindKeyActionData bindKeyActionData: En el tipo BindKeyActionData
se almacena la información necesaria para la ejecución de las acciones BindKey. Apunta a una estructura con los campos:
• InputKey inputKey: Identificador de la tecla que lanzará el evento
definido por key.
• Key key: Ver la sección 5.8.
SeekVideoActionData seekVideoActionData: En este campo se almacenará la información necesaria para ejecutar las acciones SeekVideo. Como
en los casos anteriores, SeekVideoActionData es un tipo que apunta a una
estructura con dos campos:
• SeekVideoType seekVideoType: SeekVideoType es una enumeración
que puede tomar los valores:
◦ SEEK FORWARD: Se debe incrementar la posición actual en la reproducción del vı́deo.
◦ SEEK BACKWARD: Se debe decrementar la posición actual.
◦ SEEK ABSOLUTE: Se debe ir a la posición indicada.
• double time: Posición a la que se debe ir o cantidad a aumentar o
decrementar la posición actual.
SetPropertyActionData setPropertyActionData: Aquı́ se almacena la
información necesaria para ejecutar las acciones SetProperty. El tipo SetPropertyActionData apunta a una estructura con dos campos:
104
Capı́tulo 5. Implementación de DFBCIn
• char name[]: Nombre de la propiedad.
• char value[]: Nuevo valor para la propiedad.
ShowTextActionData showTextActionData: En este campo se almacenará la información necesaria para ejecutar una acción ShowText. El tipo
ShowTextActionData apunta a una estructura con un único campo lines
que contiene un array de strings.
LoadMp3ActionData loadMp3ActionData: En este campo se almacena la
información que se utilizará para ejecutar las acciones de tipo LoadMp3. El
tipo LoadMp3ActionData apunta a una estructura que contiene el campo
path en el que se indica la ruta desde la que se debe cargar el fichero mp3
a reproducir.
5.9.3.
Funciones
El método execute se implementará utilizando una sentencia switch que llamará la función correspondiente de utilizando el campo actionType de la acción
ejecutada.
void executeAction(Action action, GlobalContext globalContext) {
switch (action -> actionType) {
case NEXT_MENU_ACTION:
executeNextMenuAction(action -> actionData
-> nextMenuActionData,
globalContext);
break;
case PREVIOUS_MENU_ACTION:
/* [...] continúa con casos similares */
El argumento globalContext lo pasa Main cuando llama a execute. Contiene información global sobre la ejecución de la aplicación.
Además de las funciones necesarias para simular el método execute también
son necesarias las funciones que posibiliten la creación y destrucción de este tipo
de objetos. El API para el manejo de objetos de la clase Action proporciona,
además de execute, las siguientes funciones:
La interfaz Action
105
void freeAction(Action action): Libera la memoria ocupada por la variable action.
void freeAction(void *data): Alias de la función anterior para utilizar
en la creación de los arrays de acciones (ver sección 5.5.2).
Action newAction(void): Devuelve una nueva acción con actionType como NO ACTION y actionData igual a NULL. Estos campos deberán ser rellenados explı́citamente con los datos correspondientes a la acción que se
quiera representar.
Al igual que la función execute, la función freeAction deberá implementar
una sentencia switch para liberar la memoria ocupada por los datos especı́ficos
de la acción.
void freeAction(Action action) {
if (action -> actionData != NULL) {
freeActionData(action -> actionData, action -> actionType);
}
free(action);
}
static void freeActionData(ActionData actionData,
ActionType actionType) {
switch (actionType) {
case
//
case
//
case
//
NO_ACTION:
Falls through
SHOW_MENU_ACTION:
Falls through
STOP_VIDEO_ACTION:
Falls through
/*
* [...]
*
* Continúa con todas las acciones que no
* necesitan datos adicionales
*/
106
Capı́tulo 5. Implementación de DFBCIn
break;
case NEXT_MENU_ACTION:
freeNextMenuActionData(actionData);
break;
case PREVIOUS_MENU_ACTION:
freePreviousMenuActionData(actionData);
break;
/*
* [...]
*
* Continúa con el resto de funciones de
* liberación de ’actionData’s
*/
default:
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
break;
}
}
Para aquellas acciones que no necesiten datos especı́ficos no será necesario
realizar ninguna acción adicional a la hora de liberar la memoria ocupada ya
que el campo actionType será un puntero NULL. Para el resto de acciones se
deberá añadir el código que se encargue de liberar los tipos de datos que utilizan
para almacenar la información extra.
Las funciones de creación y liberación para los tipos de datos definidos para almacenar la información necesaria para ejecutar las acciones también serán
exportados por el API, ya que serán necesarios para completar las variables devueltas por newAction.
newNextMenuActionData(const char *menuName)
newPreviousMenuActionData(int depth, short reinit)
newPlayVideoActionData(const char *videoPath)
La interfaz Action
107
newChangeOptionActionData(int value, short absolute)
newBindKeyActionData(InputKey inputKey, Key key)
newSeekVideoActionData(SeekVideoType type, double time)
newSetPropertyActionData(char *name, char *value)
newShowTextActionData(void)
newLoadMp3ActionData(const char *path)
void freeNextMenuActionData(ActionData actionData)
El resto de funciones de liberación son idénticas a la anterior con el nombre
de la acción correspondiente.
5.9.4.
¿Cómo instanciar una acción?
Por ejemplo, si se necesita una acción que al ejecutarla se encargue de solicitar
el menú de nombre “mainMenu” con el atributo “test” con valor “yes” a VXMLS
serı́a necesario el siguiente código4 :
Action action;
NextMenuActionData nextMenuActionData;
action = newAction();
nextMenuActionData = newNextMenuActionData("mainMenu");
addArray(nextMenuActionData -> arguments, test);
addArray(nextMenuActionData -> arguments, yes);
action -> actionType = NEXT_MENU_ACTION;
action -> actionData -> nextMenuActionData = nextMenuActionData;
La llamada al método action.execute se implementa utilizando la función
execute(action). Para liberar la memoria ocupada tanto por action como por
nextMenuActionData sólo serı́a necesaria una llamada freeAction(action).
4
test y yes deberán ser strings con memoria reservada e inicializados como “test” y “yes”
respectivamente
108
5.9.5.
Capı́tulo 5. Implementación de DFBCIn
¿Cómo añadir una nueva acción?
Los pasos que se deberán seguir para añadir un nuevo tipo de acción (lo que
en diseño equivaldrı́a a una nueva implementación de la interfaz Action) son:
1. En caso de que sea necesario, crear un nuevo tipo de datos para almacenar
información para ejecutar la acción y las funciones para crear y liberar
variables de ese tipo de datos. Estas funciones deberán ser exportadas para
el resto de la aplicación.
2. Añadir un campo del tipo anteriormente definido a la enumeración ActionData.
3. Añadir un identificador para la nueva acción a la enumeración ActionType.
4. Suponiendo NuevaAccion como nombre del nuevo tipo de acción que se
está creando: implementar las funciones executeNuevaAccion con el código
para ejecutar la acción.
5. Añadir los nuevos casos a los switch de executeAction y freeAction.
5.10.
Implementación del motor de la aplicación
5.10.1.
Main
La implementación de la clase Main consistirá simplemente en la función main,
que será la que se ejecute al iniciar la aplicación.
Los pasos que debe seguir esta función serán:
1. Crear una variable de tipo GlobalContext que será pasada a la función
execute a la hora de ejecutar las acciones para permitir que modifiquen el
estado de la ejecución. En la implementación actual el tipo GlobalContext
solamente contiene una pila en la que se irán almacenando los menús a
medida que el usuario profundiza en la interfaz definida y una bandera
para controlar los cambios en el sistema de propiedades para saber si debe
ser salvado a disco.
2. Inicializar los subsistemas necesarios (lo que en diseño equivale a crear los
objetos Singleton [38]). Para ello deberá ejecutar las funciones ***Init
correspondientes:
propertiesInit()
Implementación del motor de la aplicación
109
vxmlsInit()
graphicsInit(argc, argv)
interfaceInit()
interfaceInputInit()
3. Obtener el menú principal de VXMLS, añadirlo a la pila de menús e indicarle al módulo gráfico que lo muestre por pantalla. Para ello deberá utilizar
el módulo VXMLS y el API del módulo gráfico.
/* Load main menu */
menu = vxmlsGetMenu(ROOT_MENU, NULL);
pushStack(globalContext -> menuStack, menu);
/* Show main menu */
interfaceLoadMenu(menu, 1);
interfaceShowMenu();
4. Esperar a recibir bloques de acciones a ejecutar y ejecutarlos cuando estén
disponibles. Normalmente, el primer bloque de acciones a ejecutar serán
las acciones especificadas en el atributo initActions del menú principal.
Después de la ejecución de cada bloque de acciones se comprueba si se ha
modificado el sistema de propiedades y en caso de que sea ası́, se salva su
contenido en el disco5 .
while (1) {
array = interfaceWaitForActions();
for (i = 0; i < numArrayElements(array); i++) {
action = (Action) getArrayData(array, i);
executeAction(action, globalContext);
}
/* Save the properties to disk if needed */
if (globalContext -> saveProperties) {
propertiesSave();
globalContext -> saveProperties = 0;
5
El sistema de propiedades se dejó de utilizar para almacenar datos cambiantes en cuanto
el control de este tipo de datos pasó a VXMLS, por lo que este paso es innecesario en la
implementación actual
110
Capı́tulo 5. Implementación de DFBCIn
}
/* Release executed actions */
releaseArray(array);
}
5.10.2.
Interface
5.10.2.1.
Introducción
La implementación del objeto de la clase Interface se hará siguiendo los pasos
explicados en la sección 5.4.
La función interfaceInit creará un nuevo hilo de ejecución que se encargará de ejecutar la función interfaceThread que controlará la comunicación con
el módulo gráfico y con InterfaceInput y detectará los arrays de acciones que
deben ser ejecutados.
5.10.2.2.
Banderas de estado
Para definir el estado con el que controlar la ejecución del nuevo hilo de
ejecución se utiliza un sistema de banderas. Las banderas definidas son:
INTST MENU LOAD: Si está activa quiere decir que hay un menú cargado.
INTST SHOW MENU: Las funciones exportadas activarán esta bandera para
indicarle a interfaceThread que debe mostrar el menú cargado.
INTST HIDE MENU: Esta bandera se activará para indicar que se debe ocultar
el menú.
INTST INPUT EVENT: Esta bandera será activada para señalar que hay un
nuevo evento de entrada que atender.
INTST ACTION READY: Si está activa indica que hay acciones listas para ser
ejecutadas.
INTST WAITING ACTION: Se activa para indicar que Main está esperando
para recibir acciones a ejecutar.
INTST NEW FRAME: La activará el sistema de reproducción de vı́deo para
indicar que se ha decodificado un nuevo frame y que debe ser mostrado en
la pantalla.
Implementación del motor de la aplicación
111
INTST VIDEO LOAD: Se activará para indicar que se debe cargar un nuevo
vı́deo.
INTST VIDEO UNLOAD: Se deberá activar para indicar que se debe terminar
la reproducción de vı́deo.
INTST VIDEO PLAY: Con esta bandera se señala que se debe comenzar la
reproducción del vı́deo cargado.
INTST VIDEO STOP: Se utiliza para indicar que se debe detener la reproducción en curso.
INTST VIDEO RESUME: Se activará cuando se desee continuar con la reproducción de vı́deo anteriormente detenida.
INTST VIDEO PLAYBACK: Si está activa indica que se está reproduciendo un
vı́deo (se mantiene activa aunque la reproducción esté temporalmente detenida).
INTST MENU INIT: Se activará si se deben ejecutar las acciones de inicialización de un menú.
INTST VIDEO END: Indica que se ha alcanzado el final del vı́deo.
INTST EXIT: Se utiliza para terminar la aplicación, solamente es necesaria
para las pruebas del sistema.
INTST SHOW TEXT: Se activará para señalar que se debe mostrar un texto en
la pantalla.
INTST HIDE TEXT: Se activará para que interfaceThread oculte el texto
anteriormente mostrado.
Estas banderas no son accesibles para el resto de la aplicación, sólo serán
manipuladas por las funciones exportadas para el resto de módulos y por las
funciones internas de Interface.
5.10.2.3.
Funciones exportadas
La mayorı́a de las funciones exportadas al resto de la aplicación se limitarán
a activar o desactivar algunas de las banderas del estado (siempre después de
cerrar el semáforo) y despertar el hilo interfaceThread para que ejecute las
operaciones pertinentes. Las funciones exportadas son:
112
Capı́tulo 5. Implementación de DFBCIn
void interfaceInit(void): Simula la creación del objeto Singleton [38],
para ello reserva la memoria necesaria para almacenar el estado (definido
por la variable interfaceState) y crea el nuevo hilo interfaceThread.
void interfaceDeinit(void): Destruye el objeto creado por la función
anterior, para ello finaliza el hilo interfaceThread y libera la memoria
ocupada por la variable de estado.
void interfaceLoadMenu(Menu menu, short init): Se utiliza para cargar un nuevo menú en la interfaz. init indica si se deben ejecutar las
acciones de inicialización. Esta función activa la bandera INTST MENU LOAD
y, en caso de que init sea distinto de 0, también activa INTST MENU INIT.
void interfaceUnloadMenu(void): Solicita la descarga del menú anteriormente cargado activando INTST MENU UNLOAD.
void interfaceShowMenu(void): Activa INTST SHOW MENU.
void interfaceHideMenu(void): Activa INTST HIDE MENU.
void interfaceNotifyInputEvent(void): Esta función será utilizada por
InterfaceInput para indicar que se ha detectado un nuevo evento de entrada
que debe ser tratado. Activa la bandera INTST INPUT EVENT.
void interfaceLoadVideo(char *videoFile, Array endActions): Activa INTST VIDEO LOAD, copia videoFile en un campo de la variable de
estado y añade una referencia a endActions, para tener disponibles las
acciones del final del vı́deo en caso de que este se produzca.
void interfacePlayVideo(void): Activa INTST VIDEO PLAY.
void interfaceStopVideo(void): Activa INTST VIDEO STOP.
void interfaceResumeVideo(void): Activa INTST VIDEO RESUME.
void interfaceUnloadVideo(void): Activa INTST VIDEO UNLOAD.
void interfaceShowText(Array lines): Activa INTST SHOW TEXT y carga las lı́neas de texto en el módulo gráfico.
void interfaceHideText(void): Activa INTST HIDE TEXT.
Array interfaceWaitForActions(void): En caso de que no esté activa
la bandera INTST ACTION READY, activa INTST WAITING ACTION y bloquea
la ejecución en la condición actionCond durante el tiempo indicado por el
Implementación del motor de la aplicación
113
campo time del menú cargado (durante tiempo infinito si este valor es -1).
En caso de que finalice la temporización referencia el array timeActions
del menú cargado en el array que se mantiene en el estado con las funciones
listas para ser ejecutadas y activa INTST ACTION READY. Finalmente comprueba si ya está activa al bandera INTST ACTION READY y en caso contrario
vuelve al bloqueo en la condición actionCond.
5.10.2.4.
Ciclo de ejecución
El ciclo de ejecución del hilo interfaceThread se mantiene dormido hasta que
alguna de las funciones anteriores lo despierta. Tras esto comprueba las variables
de estado y ejecuta las funciones que sean necesarias.
En primer lugar, en caso de que no esté activa INTST ACTION READY comprueba si hay acciones listas para ejecutarse:
1. Si está activa la bandera INTST MENU INIT la desactiva y guarda el
array initActions del menú cargado para devolverlo cuando se soliciten las acciones a ejecutar.
2. Si está activa la bandera INTST VIDEO END la desactiva y guarda el
array de acciones endVideoActions para devolverlo como acciones a
ejecutar.
3. Si está activa la bandera INTST INPUT EVENT recuperará la tecla pulsada utilizando la función getInputKey de InterfaceInput y comprobará el objeto Key correspondiente a dicha tecla en el menú cargado.
Si la pulsación de esa tecla está asociada a la ejecución de un grupo de
acciones guardará esas acciones para devolverlas como acciones a ejecutar, en caso de que la tecla esté vinculada a un evento EXECUTE,
guardará el array de acciones de la opción señalada por el parámetro
currentPosition del menú cargado. En caso de que la tecla esté asociada a un evento NULL, la entrada será ignorada (ni siquiera detiene
la temporización).
Si en el paso anterior se ha detectado un array de acciones a ejecutar se
activa la bandera INTST ACTION READY. En caso de que esté activa la bandera INTST WAITING ACTION se despierta a los hilos que estén esperando en
la condición ActionCond (en la función interfaceWaitForActions).
Si está activa la bandera INTST VIDEO PLAY, se desactiva y se comienza la
reproducción de vı́deo utilizando la biblioteca gráfica. También se activa
la bandera INTST VIDEO PLAYBACK y se indica a la biblioteca gráfica que
114
Capı́tulo 5. Implementación de DFBCIn
a partir de ese momento dibuje los menús dentro de un marco, ya que se
superpondrán a la imagen de vı́deo.
Si está activa la bandera INTST NEW FRAME se desactiva y se indica que debe
ser redibujada la pantalla activando una variable local flip.
Para controlar la reproducción del vı́deo se comprueba si esta activa alguna
de las banderas INTST VIDEO STOP o INTST VIDEO RESUME en caso de que
ası́ sea, se desactiva y se utiliza la biblioteca gráfica para detener o continuar
la reproducción en curso.
Si está activa la bandera INTST VIDEO LOAD se desactiva y se carga el fichero
de vı́deo indicado en la variable de estado en el módulo gráfico.
Si está activa la bandera INTST VIDEO UNLOAD se desactiva, se descarga el
vı́deo del módulo gráfico y se indica al mismo que deje de dibujar el marco
de los menús. También se libera el array endVideoActions anteriormente
referenciado durante la ejecución de la función interfaceLoadVideo.
Las banderas INTST SHOW MENU y INTST HIDE MENU provocarán que se cargue o descargue el menú en el módulo gráfico respectivamente en caso de
que estén activadas. Ambas serán desactivadas una vez realizada la operación correspondiente. Para hacer que los cambios realizados sean visibles se
debe redibujar la pantalla, por lo que también se activa la variable flip.
Las banderas INTST SHOW TEXT y INTST HIDE TEXT provocarán un comportamiento similar a las dos anteriores, pero con lı́neas de texto en lugar del
menú.
Por último, en caso de que esté activa la variable flip, utilizará las funciones del API del módulo gráfico para dibujar en pantalla lo que sea necesario. Si está activa la bandera INTST VIDEO PLAYBACK llamará a la función
graphicsBlitVideo para dibujar el frame actual como fondo, después llamará a la función graphicsFlipScreen que dibujará el resto de la interfaz
gráfica (menú y/o texto) y hará los cambios visibles. Tras esto desactiva la
variable flip.
5.10.3.
InterfaceInput
Al igual que Interface la implementación de InterfaceInput también se hará de
acuerdo con lo explicado en la sección 5.4.
Implementación del motor de la aplicación
115
La función intefaceInputInit reservará memoria para almacenar el estado
y creará un nuevo hilo de ejecución interfaceThread que monitorizará la entrada y avisará a Interface cada vez que detecte que el usuario ha pulsado una tecla.
El estado de este subsistema se compone de unas banderas y un espacio para
almacenar un evento detectado.
Las banderas de estado son:
INPTST EVENT DETECTED: Se activará cuando se detecte un evento de entrada.
INPTST EVENT STORED: Se mantendrá activa mientras se tenga almacenado
un evento anteriormente detectado a la espera de que sea consultado por
Interface.
El ciclo de ejecución del hilo que controla este objeto sigue los siguientes pasos:
1. Si no está activa la bandera INPTST EVENT STORED hace una llamada a la
función graphicsTestInput del modulo gráfico. Esta función bloqueará la
ejecución hasta que se detecte un evento de entrada. Una vez obtenido el
evento de entrada, comprueba si éste es válido y, en caso de que ası́ sea,
activa INPTST EVENT DETECTED y INPTST EVENT STORED.
2. Si está activa la bandera INPTST EVENT STORED la desactiva y llama a la
función interfaceNotifyInputEvent.
3. Mientras esté activa la bandera INPTST EVENT STORED detiene su ejecución.
Este módulo exporta la función getInputKey que devuelve el evento almacenado y desactiva la bandera INPTST EVENT STORED. Esta función debe bloquear
el semáforo antes de acceder al estado, por lo que sólo podrá completar su ejecución en caso de que el hilo interfaceInputThread esté dormido. Tras devolver
el evento detectado despierta el hilo para que continúe su ejecución.
Para destruir el objeto creado, se exporta la función interfaceInputDeinit,
que detiene la ejecución del hilo creado por la función de inicialización y libera
la memoria ocupada por la variable de estado.
116
Capı́tulo 5. Implementación de DFBCIn
5.10.4.
Mp3Player
5.10.4.1.
Introducción
Esta clase también es una aplicación del patrón Singleton [38], por lo que se
implementará de acuerdo con lo explicado en 5.4.
Para la reproducción de los ficheros o URLs se utilizará el programa mpg123.
Este programa define un protocolo de comunicación utilizando la entrada y salida estándar, lo que permite controlarlo desde DFBCIn utilizando un pipe y
redirección de ficheros.
5.10.4.2.
Banderas de estado
Las banderas utilizadas para el control del hilo de ejecución de este objeto
son:
PLST LOAD: Se activa para indicar que se debe iniciar la reproducción de un
cierto fichero o URL.
PLST PAUSE CONTINUE: Se activa para indicar que se debe detener la reproducción en curso o continuar con una reproducción anteriormente detenida.
PLST QUIT: Se activa para indicar que se debe terminar la ejecución del
programa mpg123.
PLST PAUSED: Se activará cuando la reproducción esté detenida.
5.10.4.3.
Funciones exportadas
La función mp3Init se encargará de crear el objeto. Para ello ejecuta las
siguientes operaciones:
1. Crea dos pipes, uno para enviar comandos a mpg123 y otro para leer información del mismo.
2. Crear un nuevo proceso del sistema operativo con una llamada fork. La
entrada estándar de este nuevo proceso se redirige al extremo de lectura del
pipe de comandos y la salida estándar se redirige al extremo de escritura
del pipe de información. Tras esto se ejecuta el programa mpg123 con una
llamada exec.
Implementación del módulo VXMLS
117
3. Crear un nuevo hilo de ejecución que monitorice la ejecución de mpg123
utilizando los pipes anteriormente creados. Antes de crear este nuevo hilo,
se asegura que mpg123 está funcionando correctamente leyendo el mensaje
de confirmación del pipe de información.
La función mp3Load llamará a mp3Init en caso de que el objeto no haya sido
creado (es decir, que no exista el hilo de control), tras esto bloquea el estado, activa la bandera PLST LOAD y, en caso de que esté activa la bandera PLST PAUSED,
despierta el hilo de control.
La función mp3PauseContinue obtiene el bloqueo del estado, activa la bandera PLST PAUSE CONTINUE y, en caso de que esté activa la bandera PLST PAUSED,
despierta el hilo de control.
La función mp3Deinit bloquea el estado, activa la bandera PLST QUIT, despierta el hilo de control en caso de que esté activa la bandera PLST PAUSED, libera
el estado, espera a que finalice el hilo y después espera a que termine la ejecución
de mpg123.
5.10.4.4.
Ciclo de ejecución
El hilo de control sigue los siguientes pasos en su ciclo de ejecución:
1. Si está activa la bandera PLST PAUSED bloquea su ejecución en una condición, a la espera de ser despertado por alguna de las funciones exportadas.
2. Si no está activa la bandera PLST PAUSED, lee una lı́nea del pipe de información para asegurarse de que la reproducción no ha llegado a su fin, en cuyo
caso volverá a empezarla desde el principio. Tras esto libera el estado, cede
la CPU para permitir la ejecución de las funciones exportadas y vuelve a
bloquear el estado.
3. Si está activa la bandera PLST LOAD, la desactiva y escribe el comando para
cargar la URL en el pipe de comandos y lee la confirmación del pipe de
información.
4. Si está activa la bandera PLST PAUSE CONTINUE la desactiva y escribe el comando para detener o reanudar la reproducción. Tras esto lee la información
devuelta y activa o desactiva la bandera PLST PAUSED según sea necesario.
5. Si está activa la bandera PLST QUIT escribe el comando para terminar la
ejecución de mpg123 y termina termina el hilo, en caso contrario vuelve al
paso 1.
118
Capı́tulo 5. Implementación de DFBCIn
5.11.
Implementación del módulo VXMLS
5.11.1.
Módulo HTTP
5.11.1.1.
Introducción
La comunicación con VXMLS se hará mediante peticiones GET [35]. Para
poder realizar estas peticiones se utilizará la clase utilidad HTTP que proporciona las funciones httpGet y escapedEncoding que permiten enviar peticiones
GET genéricas al puerto de un servidor. Lógicamente, no se ha realizado una
implementación completa de un cliente HTTP, sino que sólo se pretende que sea
posible realizar peticiones a VXMLS. Aún ası́, el servidor y el puerto se pasan
como parámetros para respetar la modularidad del sistema.
En [42] se definen la sintaxis que deben respetar las URLs (una URL es un tipo especial de URI). La función escapedEncoding transformará cadenas de texto
utilizando la codificación hex hex, para evitar que los caracteres reservados que se
puedan encontrar en esas cadenas varı́en la semántica de la URL. Por ejemplo, si
se quiere pasar un argumento un-&-es-un-caracter-reservado deberá ser previamente codificado para evitar que el caracter & sea interpretado como separador
de argumentos:
strcpy(argumento, "un-&-es-caracter-reservado");
escapedEncoding(argumento);
puts(argumento);
> un-%26-es-caracter-reservado
5.11.1.2.
Peticiones GET
La función httpGet se encargará de hacer una petición GET HTTP sobre una
URL de un cierto servidor que esté escuchando en un puerto determinado. Para
ello se implementarán las partes necesarias del protocolo HTTP.
Para mantener las sesiones de los diferentes usuarios se utilizará un sistema
de cookies [45]. Este sistema se implementará de forma sencilla; sólo se
necesita poder almacenar una única cookie en memoria, se supone que la
sesión sólo será mantenida mientras que el cliente esté funcionando.
Envı́o de peticiones GET HTTP 1.1, contemplando la posibilidad de enviar
el campo Cookie en caso de que sea necesario.
Interpretación de ciertos campos de la cabecera de la respuesta del servidor:
Implementación del módulo VXMLS
119
• Connection: En caso de que contenga el valor close, se puede asumir
que el servidor cerrará la conexión en cuanto termine de enviar el
mensaje.
• Content-Length: Este campo se utilizará para indicar cuántos bytes
se deben leer para obtener la respuesta completa. El servidor HTTP
implementado por Inets [39] devuelve el campo Connection con valor
close en lugar de devolver la longitud de la respuesta.
• Set-Cookie: Este campo indica que se debe almacenar una determinada cookie. La cookie almacenada se enviará en todas las siguientes
peticiones en el campo Cookie de la cabecera GET. Set-Cookie también puede ser utilizado para ordenar al cliente la destrucción de una
cookie previamente almacenada.
• VXMLS-Error : Este campo se ha definido para que VXMLS pueda
advertir a DFBCIn de que el contenido que le envı́a puede no ser el
esperado, ya que se ha producido un error.
5.11.1.3.
Envı́o de peticiones GET
Para enviar peticiones GET se utiliza la función
int httpGet(char *server, int port, char *request, int fileDes)
que ejecuta los siguientes pasos para enviar la petición request al puerto port del
servidor server HTTP y escribir la respuesta en el descriptor de fichero fileDes.
1. Abre un socket y envı́a la petición. De esto se encarga una función interna
sendRequest mediante los pasos:
a) Crear un socket orientado a conexión.
b) Obtener la dirección de server.
c) Conectar con el servidor utilizando el socket.
d ) Crear la cabecera GET a enviar con la URL request y el campo Cookie
en caso de que sea necesario.
e) Enviar la petición
2. Leer la cabecera de la respuesta para obtener el tamaño del contenido (o
asegurarse de que se ha devuelto el campo Connection con valor close) y
guardar una nueva cookie o destruir la cookie almacenada en caso de que
sea necesario. Si se detecta el campo VXMLS-Error se escribirá un aviso
en el error estándar para clarificar las causas del fallo.
120
Capı́tulo 5. Implementación de DFBCIn
3. Leer el resto del mensaje y escribirlo en el fileDes hasta que se alcance
el final, ya sea el marcado por el campo Content-Length o el marcado por
el cierre de la conexión por parte del servidor (en caso de que el anterior
campo no hubiera sido devuelto).
5.11.2.
Parser VXMLS
La estructura de los documentos XML devueltos por VXMLS para las operaciones que deben ser confirmadas es muy sencilla (ver el apéndice A), lo que
facilita la elaboración del parser para interpretarlos.
El API exportado por el parser VXMLS exporta dos funciones al resto módulos: parseVxmlsResponse para interpretar las repuestas de VXMLS y la función
getVxmlsErrorMessage que devuelve un mensaje explicativo en el caso de que
se haya producido algún error.
Para almacenar el estado interno del parser durante el proceso de parsing se
define una estructura con los campos:
AppError error: Para indicar qué error se produjo en caso de que se produjera alguno.
char errorMessage[]: Mensaje devuelto por VXMLS explicando el error.
short inError: Se utiliza para indicar que se está interpretando el contenido de la etiqueta error.
short abort: Se activará en caso de que se detecte un error que obligue a
abortar el proceso de parsing.
En el parser creado se instalarán los siguientes manejadores (ver sección 3.4.3):
startElement: Se instala para manejar la apertura de las etiquetas.
• Si está activo el campo abort no hace nada.
• Si la etiqueta que se está abriendo es la etiqueta ok pone el valor
NO ERROR en el campo error del estado del parser para indicar que
VXMLS confirmó la operación.
• Si la etiqueta que se está abriendo es la etiqueta error pone el valor
VXMLS ERROR en el campo error del estado del parser para indicar que
VXMLS ha fallado al realizar la operación y activa inError.
Implementación del módulo VXMLS
121
• En otro caso, si la etiqueta abierta no es vxmls (etiqueta raı́z del
documento esperado) se pone el valor PARSER ERROR en el campo error
para indicar que la respuesta de VXMLS es incomprensible y se activa
abort.
endElement: Se instala para manejar el cierre de las etiquetas.
• Si está activo el campo abort no hace nada.
• Si la etiqueta que se está cerrando es la etiqueta error desactiva el
campo inError del estado del parser y copia el mensaje de error
errorMessage en una variable global para que pueda ser devuelto por
la función getVxmlsErrorMessage.
characterData: Se instala para manejar las secciones de caracteres. Su
objetivo es copiar la sección de datos de la etiqueta error en el campo
errorMessage del estado del parser. Lo único que deberá hacer esta función será concatenar los datos que recibe como argumentos en el campo
errorMessage en caso de que el campo inError esté activo.
La función AppError ParseVxmlsResponse(int fileDes) creará un nuevo
parser utilizando el API de expat, le instalará los manejadores anteriormente definidos y lo utilizará para parsear los contenidos del fichero descrito por fileDes.
Devuelve un código de error, en caso de que todo haya ido bien, el código devuelto
es NO ERROR.
5.11.3.
Parser para los documentos menu
5.11.3.1.
Introducción
La estructura de un documento XML utilizado para describir un determinado
menú de la interfaz pude ser arbitrariamente profunda (ver el apéndice A) debido
a que algunos tipos de acción pueden contener descripciones de otras acciones en
su interior.
Además de esto, el numero de acciones descritas es susceptible de ser aumentado con frecuencia, por lo que se deberá implementar el parser de forma que el
hecho de añadir nuevas acciones no afecte al código existente.
Para resolver el problema de la profundidad arbitraria se utilizará una pila en
el estado del parser. Las funciones utilizadas por éste podrán utilizar la pila para
almacenar la información necesaria entre la apertura y el cierre de las etiquetas.
En esta pila se almacenará un tipo de datos llamado Context que apunta a una
122
Capı́tulo 5. Implementación de DFBCIn
estructura con dos campos, un campo tag en el que se indicará a qué etiqueta
pertenece el contexto y un campo data en el que se almacenará la información
especı́fica (será de tipo puntero a void, las funciones especı́ficas de cada etiqueta
deberán hacer la transformación de tipo necesaria).
El caso de la extensibilidad se resuelve estructurando el código de forma que
las funciones que controlan la interpretación de la parte “estable” del documento y
las funciones que controlan la parte cambiante estén lo más desacopladas posible6 .
5.11.3.2.
Organización del código
El código se distribuye de la siguiente forma, para permitir añadir las funciones
necesarias para parsear nuevas acciones de forma sencilla:
La cabecera parser.h en la que se define la única función que va a ser
exportada para el resto de la aplicación: parseMenu.
La cabecera parser-types.h en la que se definen los tipos que van a ser
utilizados tanto en la parte estable como en la parte que define las acciones.
Estos tipos son:
• Tag: Enumeración utilizada para definir cada una de las posibles etiquetas que se pueden encontrar de acuerdo con el DTD, por ejemplo,
la etiqueta <option ...> se corresponderá con una variable de tipo
Tag con valor OPTION.
• Context: Contiene la información relativa a una etiqueta abierta, como
se explicó al principio de esta sección.
• ParserData: Es el tipo que tendrá la variable que representa el estado
del parser durante el proceso de interpretación. Apunta a una estructura con dos campos, en uno estará la pila de contextos y en otro
el objeto de la clase Menu que se irá construyendo a medida que se
interpreta la información del documento.
• BindKeyContextData: Este tipo es necesario para almacenar la información que se necesitará durante el proceso de interpretación del contenido de la etiqueta bindKey.
Para el manejo del tipo Context se declaran tres funciones:
6
Como parte cambiante se entiende lo que va dentro de las etiquetas action, sin incluir
éstas
Implementación del módulo VXMLS
123
• Context newContext(Tag tag, void *data): Crea una variable de
tipo Context para la etiqueta representada por tag y con los datos
apuntados por data.
• void freeContext(Context context): Libera el espacio que ocupa
el contexto context pero no el que ocupan sus datos.
• void freeContextAndData(Context context): Libera el espacio reservado para context y los datos que referencia.
En el fichero parser.c se define la función parseMenu y la parte estable de
las funciones que componen el parser, ası́ como las funciones getTag, que
transforma un string con el nombre de una etiqueta en una variable de tipo
Tag; freeContext y freeContextAndData; y getAttrValue que será utilizada por las funciones que manejan la apertura de las etiquetas para obtener los valores de los argumentos XML de esas etiquetas, por ejemplo, para
la apertura <ejemplo nombre="valor"> la llamada getAttrValue(attrs,
"nombre"), devolverá "valor" (attrs es un conjunto de pares nombre-valor
que proporciona la implementación genérica de parser hecha por expat).
Por último, en los ficheros tag-functions.h y tag-functions.c se implementan las funciones que controlarán la apertura y el cierre de las acciones
especı́ficas.
5.11.3.3.
Proceso de parsing
¿Cómo conseguir una independencia lógica aceptable si todas las funciones
trabajan sobre la misma pila y el mismo objeto Menu?
El propio proceso de parsing facilita la solución de este problema. Como se
explicó en la sección 4.5.5.3, las etiquetas menos profundas no se ven afectadas
por las labores de las más profundas ya que estas trabajarán en la pila por encima de sus datos. Por lo tanto, el proceso de parsing de la parte estable de los
documentos puede hacerse sin tener en cuenta los pasos que deberán dar las funciones que interpreten las acciones especı́ficas. Un aspecto que debe tenerse en
cuenta es que las funciones que trabajan con acciones especı́ficas pueden necesitar
utilizar etiquetas action, que son parte de lo que se considera parte estable de
los documentos. Aún ası́ el tratamiento de las etiquetas action puede hacerse de
forma genérica con un pequeño “truco” que se explicará un poco más adelante
en la sección 5.11.3.4.
Dentro de parser.c, las funciones que manejarán los eventos de apertura,
cierre y datos de las etiquetas serán:
124
Capı́tulo 5. Implementación de DFBCIn
manageStartElement: Recibe como argumentos:
• ParserData parserData: La variable de estado del parser.
• const XML Char **attrs: Array de strings con los nombres y los valores de los atributos con los que fue abierta la etiqueta. Se puede
utilizar la función getAttrValue como se explicó anteriormente para
obtener el valor de un determinado argumento de forma sencilla.
• Tag tag: Identifica la etiqueta que se está interpretando.
manageEndElement: Recibe los argumentos:
• ParserData parserData: La variable de estado.
• Tag tag: Identifica la etiqueta que se está cerrando.
manageCharacterData: Recibe los argumentos:
• ParserData parserData: La variable de estado del parser.
• const XML Char **text: Fragmento del contenido PCDATA de la etiqueta.
• int length: Longitud del fragmento de datos text.
• tag Tag: Etiqueta que se está tratando.
Dentro de cada una de estas funciones habrá un bloque switch que ejecutará el código correspondiente en función del valor de tag. Para los valores MENU,
VERSION, TIMER, INIT ACTIONS, OPTION, TEXT, HEADER y ACTION, el código estará dentro de este mismo fichero; estas son las etiquetas que no cambiarán. El
resto de etiquetas (las que definen los diferentes tipos de acción) provocarán una
llamada a alguna de las funciones declaradas en el fichero tag-functions.h.
Estas tres funciones no son las que se instalan directamente como manejadores
en el parser creado con expat [2], las funciones que espera expat tienen unos
argumentos ligeramente diferentes. Los manejadores instalados actuarán como
adaptadores entre expat y las tres funciones anteriormente explicadas.
startElement: Llama a manageStartElement. Recibe los siguientes argumentos del parser creado con expat:
• void *userData: Contiene la variable de estado instalada en el momento de crear el parser. Será convertida al tipo ParserData y se
pasará en el argumento parserData de manageStartElement.
Implementación del módulo VXMLS
125
• const XML Char *name: Nombre de la etiqueta. Se obtendrá el valor
Tag correspondiente usando la función getTag y se pasará en el argumento tag
• const XML Char **attributes: Este valor se pasa tal cual.
endElement: Llama a manageEndElement, su comportamiento es idéntico
al de startElement, pero no recibe el argumento attributes.
characterData: Llama a manageCharacterData. Recibe los siguientes argumentos:
• void *userData: Se convertirá al tipo ParserData y se pasará en el
atributo parserData.
• const XML Char *text: Este atributo se pasa tal cual.
• int length: Al igual que text, se pasa tal cual.
Como se puede ver, characterData no recibe el argumento name, por lo que
en principio no se sabe a qué etiqueta pertenecen los datos. Además, expat
no garantiza que se haga una única llamada a characterData para cada
bloque de datos, sino que puede que los datos de una misma etiqueta se lean
con más de una llamada a esta función. La solución a ambos problemas:
cuando para una etiqueta se necesite leer su contenido, en la apertura de
la misma se apilará un contexto con espacio reservado en el que se irán
concatenando los fragmentos de información leı́da. manageCharacterData
obtendrá el contexto de la cima de la pila y comprobará el valor del atributo
tag del mismo. Este valor se le pasará a la función manageCharacterData.
Es decir, si para una determinada etiqueta es necesario leer su contenido,
es obligatorio que después de que se ejecute manageCharacterData para
dicha etiqueta en la cima de la pila se encuentre un contexto con el valor
tag correspondiente a la etiqueta que se está tratando.
5.11.3.4.
Interpretación de la parte estable de los documentos
Los pasos que se dan para interpretar las etiquetas que forman la parte estable
de los documentos son:
Para la etiqueta menu no es necesario hacer nada, ni en la apertura ni en el
cierre de la misma.
La etiqueta version contiene datos de caracteres en su interior, por lo
tanto, en su apertura se apilará un nuevo contexto con espacio para que
manageCharacterData copie el contenido de la etiqueta (appMalloc devuelve un puntero a un bloque de memoria reservada):
126
Capı́tulo 5. Implementación de DFBCIn
string = (char *) appMalloc(10);
string[0] = ’\0’;
context = newContext(VERSION, string);
pushStack(parserData -> stack, context);
La función manageCharacterData concatenará los datos que vaya leyendo
en el espacio de datos del contexto que se encuentre en la cima de la pila:
context = (Context) seeStackTop (parserData -> stack);
string = (char *) context -> data;
strncat(string, text, length);
Por último, en el cierre de la etiqueta se desapila el contexto de la cima de
la pila, cuya sección de datos contendrá la cadena de caracteres completa
leı́da del interior de la etiqueta, interpreta esta cadena para obtener los
números de versión, los copia en el menú incompleto contenido en el estado
del parser (estas dos operaciones las realiza parseVersion) y comprueba
que la versión está soportada. Por último libera la memoria ocupada por
context y por los datos que contiene.
context = (Context) popStack(parserData -> stack);
if (context -> tag != VERSION) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
parseVersion(context -> data,
&(parserData -> menu -> majorVersion),
&(parserData -> menu -> minorVersion));
if(unsupportedVersion(paserData -> menu)) {
DFBCIN_FATAL(VERSION_ERROR);
}
freeContextAndData(context);
La interpretación de la etiqueta header se implementa de forma similar a
la de la etiqueta version, pero en el cierre de la misma la única operación
que hay que realizar es desapilar el contexto correspondiente y copiar su
contenido en el campo header del menú que se está construyendo:
Implementación del módulo VXMLS
127
context = (Context) popStack(parserData -> stack);
if (context -> tag != HEADER || context -> data == NULL) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
strcpy(parserData -> menu -> header,
(char *) context -> data);
freeContextAndData(context);
Para implementar la interpretación de action hay que tener en cuenta que,
aunque action se haya considerado parte de la estructura estable de los
documentos, las etiquetas que definan nuevas acciones pueden incluir etiquetas action en su interior. Se puede suponer que las acciones siempre
van a ser leı́das en grupos, ya que, como se vio en apartados anteriores, las
acciones se ejecutan en bloques. Por lo tanto, las acciones siempre van a ser
almacenadas dentro de arrays.
Aprovechando esta situación, se definirá una etiqueta especial ARRAY TAG,
que no representa a ninguna etiqueta que pueda estar presente en los documentos, sino que representará un contexto especial cuyo campo de datos
contiene un array en el que se almacenarán las acciones que se vayan interpretando. De esta forma, si una etiqueta necesita recolectar las acciones
que hay en su interior, deberá dejar un contexto ARRAY TAG en la cima de la
pila tras su apertura y en su cierre podrá desapilarlo y recoger las acciones
que se hayan interpretado.
La interpretación de una etiqueta action consistirá, por tanto, en:
• En su apertura, se crea una acción vacı́a y se pone en un contexto en
la cima de la pila a la espera de que las etiquetas anidadas dentro de
action la definan. De esta forma, en la implementación de una acción
especı́fica lo que se deberá hacer es asegurar que, en el cierre de la
etiqueta que define la acción, se deje en la cima de la pila este mismo
contexto con la acción ya completa.
action = newAction();
context = newContext(ACTION, action);
pushStack(parserData -> stack, context);
128
Capı́tulo 5. Implementación de DFBCIn
• En el cierre de action, se desapilará el contexto ACTION que ya deberı́a
tener la acción completa y se añadirá al array del contexto ARRAY TAG
que deberı́a encontrarse en la cima de la pila.
context = (Context) popStack(parserData -> stack);
if (context -> tag != ACTION) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
action = (Action) context -> data;
freeContext(context);
/* Add the complete action to the receiver array */
context = (Context) seeStackTop(parserData -> stack);
if (context -> tag != ARRAY_TAG) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
array = (Array) context -> data;
addArray(array, action);
La implementación del control de la etiqueta initActions sirve como primer ejemplo de aplicación del sistema descrito en el punto anterior para
permitir la recogida de acciones sin afectar al código existente. La apertura
de initActions consistirá simplemente en apilar un contexto ARRAY TAG
con el array initActions del menú en construcción en el campo de datos.
context = newContext(ARRAY_TAG,
parserData -> menu -> initActions);
pushStack(parserData -> stack, context);
En el cierre de la acción sólo sera necesario desapilar en contexto ARRAY TAG
y liberar la memoria ocupada por éste, pero no la ocupada por sus datos,
ya que estos son el array de acciones que ya está referenciado por el menú.
context = (Context) popStack(parserData -> stack);
if (context -> tag != ARRAY_TAG) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
Implementación del módulo VXMLS
129
}
freeContext(context);
La implementación del código para timer actions es muy similar a la de
initActions, con la diferencia de que para timer es necesario leer el atributo time en la apertura de la etiqueta y que esta vez el array de acciones,
lógicamente, será timeActions.
context = newContext(ARRAY_TAG,
parserData -> menu -> timeActions);
if ((value = getAttrValue(attributes, "seconds")) == NULL) {
PARSER_FATAL;
}
pushStack(parserData -> stack, context);
parserData -> menu -> time = atoi(value);
El código que cierra la etiqueta es el mismo que para initActions (ejecutan
el mismo bloque case).
La apertura de la etiqueta option supone la creación de un objeto Option
vacı́o que se apilará dentro de un contexto OPTION, sobre el cual se apilará un
contexto ARRAY TAG para recolectar las acciones que se definen dentro de la
opción.
option = newOption();
context = newContext(OPTION, option);
pushStack(parserData -> stack, context);
context = newContext(ARRAY_TAG, option -> actions);
pushStack(parserData -> stack, context);
En el cierre de la etiqueta, se desapilarán ambos contextos y se añadirá la
opción, ya completa, al array options del menú en construcción. Los contextos serán liberados, pero no sus contenidos.
context = (Context) popStack(parserData -> stack);
if (context -> tag != ARRAY_TAG) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
130
Capı́tulo 5. Implementación de DFBCIn
freeContext(context);
context = (Context) popStack(parserData -> stack);
if (context -> tag != OPTION) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
option = (Option) context -> data;
addArray(parserData -> menu -> options, option);
freeContext(context);
Por último, para implementar el control de los eventos relacionados con
la etiqueta text vuelve a ser necesario apilar un contexto con espacio para almacenar la información de caracteres que será leı́da por la función
manageCharacterData de la misma forma que para las etiquetas version
y header. En el cierre de la etiqueta se desapilará el contexto TEXT apilado
en la apertura y se copiará su contenido en el campo text de la opción que
se está construyendo. Como tras el contexto OPTION fue apilado un contexto
ARRAY TAG es necesario desapilar previamente éste para poder acceder a la
opción. Una vez modificada la opción, el contexto ARRAY TAG volverá a ser
colocado en su sitio.
context = (Context) popStack(parserData -> stack);
if (context -> tag != TEXT || context -> data == NULL) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
strcpy(auxString, (char *) context -> data);
freeContextAndData(context);
auxContext = (Context) popStack(parserData -> stack);
context = (Context) seeStackTop(parserData -> stack);
if (context -> tag != OPTION || context -> data == NULL) {
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
option = (Option) context -> data;
strcpy(option -> text, auxString);
pushStack(parserData -> stack, auxContext);
Implementación del módulo VXMLS
5.11.3.5.
131
Interpretación de las acciones
Las etiquetas utilizadas para definir las acciones que pueden ser realizadas por
DFBCIn se definen en los ficheros tag-functions.c y tag-functions.h. Para
implementarlas se puede suponer, como se explicó para la etiqueta action, que
en el momento de abrir una etiqueta para una determinada acción, en la cima de
la pila habrá un contexto ACTION con una acción vacı́a. Las funciones que controlan la interpretación de este tipo de etiquetas deberán dejar en la cima de la pila
la acción completa, una vez cerrada la etiqueta que se está interpretando. Por
supuesto, en caso de que la acción esté definida por una estructura de etiquetas
anidadas, se pueden apilar nuevos contextos sobre el contexto ACTION.
Por ejemplo, para interpretar la descripción de una acción BindKey se definen
las siguientes funciones:
startBindKeyTag: Utilizará una variable de tipo BindKeyContextData para almacenar la información sobre la acción y la almacenará en un nuevo
contexto que almacenará en la pila del parser. Leerá el atributo key de la
etiqueta y lo escribirá en la variable almacenada en el contexto.
startEventTag: Lee el argumento type de la etiqueta, crea un objeto Key
(ver la sección 5.8) de acuerdo con el mismo (puede ser EXECUTE o NULL)
y lo almacena en el campo correspondiente en los datos del contexto de la
cima de la pila (es el contexto que se apiló en la función startBindKeyTag).
startKeyActionsTag: En este caso se van a leer las acciones que están
asociadas a la tecla, por lo que se crea un objeto Key y se apila un nuevo
contexto ARRAY TAG con el array de acciones de dicho objeto en su sección
de datos para recolectar las acciones.
endKeyActionsTag: En el cierre de la etiqueta keyActions simplemente es
necesario desapilar el contexto ARRAY TAG apilado en la apertura de esta
etiqueta y liberarlo.
endBindKeyTag: Desapilará el contexto de la cima de la pila y obtendrá de él
los datos almacenados durante la interpretación de esta etiqueta (almacenados en una variable de tipo BindKeyContextData). Con esta información ya
se pueden rellenar los campos de la acción que se encontrará en el contexto
de la cima de la pila.
context = (Context) seeStackTop(parserData -> stack);
if (context -> tag != ACTION) {
132
Capı́tulo 5. Implementación de DFBCIn
DFBCIN_FATAL(IMPLEMENTATION_ERROR);
}
actionData = newBindKeyActionData(bindKeyContextData
-> inputKey,
bindKeyContextData
-> key);
action = (Action) context -> data;
action -> actionType = BIND_KEY_ACTION;
action -> actionData = actionData;
5.11.3.6.
¿Cómo añadir una nueva acción?
Para añadir el código necesario para interpretar una nueva acción se deberán
seguir los siguientes pasos:
1. Añadir a la enumeración Tag los identificadores de las nuevas etiquetas que
se vayan a utilizar para describir la acción.
2. Añadir a la función getTag las comparaciones necesarias para que pueda
reconocer las nuevas etiquetas.
3. En caso de que sea necesario, definir el tipo de datos que se utilizará para
almacenar la información en los bloques Context almacenados en la pila.
Añadir los case que hagan falta en la función freeContextAndData para
que sea capaz de liberar los contextos para las nuevas etiquetas.
4. Añadir a manageStartElement el código para manejar la apertura de las
nuevas etiquetas definidas. Como se explicó anteriormente, para evitar tener que modificar en exceso parser.c (y también para evitar que crezca
demasiado), el control de las etiquetas utilizadas para definir acciones se
hace con las funciones de tag-functions.c, por lo tanto, para una etiqueta nuevaEtiqueta se creará una función startNuevaEtiquetaTag en
tag-functions.c y se añadirá un case al switch de manageStartElement
para que la llame cuando se abra dicha etiqueta.
5. Añadir a manageEndElement el código para manejar el cierre de las etiquetas definidas. Si es necesario realizar alguna acción, se procederá de forma
similar al paso anterior creando una función endNuevaEtiquetaTag, en caso contrario, se añadirá un case vacı́o. Esta función aborta con un error
IMPLEMENTATION ERROR en caso de detectar una etiqueta desconocida para
evitar errores al añadir nuevas acciones.
Implementación del módulo VXMLS
133
6. Opcionalmente, añadir el código necesario (creando una nueva función en
tag-functions.c como en el caso anterior) para manejar el contenido de
caracteres de alguna de las etiquetas creadas, pero normalmente valdrá con
el código ya utilizado para text, header y version, que simplemente copia
el contenido en el contexto de la cima de la pila.
5.11.4.
Implementación de la fachada VXMLS
5.11.4.1.
Introducción
En el fichero vxmls.c se implementan las funciones que se encargarán de realizar las peticiones a VXMLS utilizando las funciones del módulo HTTP y de
interpretar sus respuestas utilizando alguno de los parsers.
Durante la inicialización de este módulo se leerá del sistema de propiedades el
nombre y puerto del servidor en el que está funcionando VXMLS, ası́ como el nombre y clave que deberá utilizar DFBCIn para solicitar una nueva sesión de éste. Estos valores se guardarán ya codificados utilizando la función characterEncoding
(ver la sección 5.11.1). Después de esto ejecutará la función vxmlsLogin para
crear la sesión.
El principal problema se encuentra a la hora de pasar la respuesta de VXMLS
a los parsers. Los parsers leen de un descriptor de fichero y, como se vio en la
sección 5.11.1, las respuestas HTTP se escriben en un descriptor de ficheros. La
solución adoptada es unir esos dos descriptores de fichero mediante un pipe. Para
ello se debe ejecutar la llamada httpGet en un hilo distinto al que ejecuta el
parser.
5.11.4.2.
Nuevo hilo para las peticiones
Se definirá una función httpProxy que creará un pipe y un nuevo hilo de
ejecución en el que utilizará la función httpGet para enviar la petición a VXMLS
y escribir en uno de los extremos del pipe. El otro extremo será devuelto como
valor de retorno para permitir ejecutar un parser que lea de él.
static int httpProxy(const char *request) {
ProxyData
int
pthread_t
proxyData
pipeFd[2];
thread;
if (pipe(pipeFd) == -1) {
= NULL;
134
Capı́tulo 5. Implementación de DFBCIn
DFBCIN_FATAL(EXTERNAL_ERROR);
}
proxyData = newProxyData(request, pipeFd[1]);
pthread_create(&thread, NULL, httpProxyThread, proxyData);
return pipeFd[0];
}
proxyData se utiliza para pasarle al hilo creado la cadena con la petición
HTTP request y el descriptor de fichero en el que escribir la respuesta.
La función httpProxyThread utilizará la función httpGet para enviar una
petición GET al servidor VXMLS (utilizando el nombre y puerto obtenidos durante la inicialización de éste módulo). En caso de que el servidor responda con
un código distinto de 200 se simulará que VXMLS ha devuelto un mensaje de
error escribiendo en el pipe un mensaje de acuerdo con el DTD que especifica las
respuestas de VXMLS utilizando la función writeError.
static void writeError(const char *message, int fileDes) {
char errorMessage[MAX_VXMLS_ERROR_MESSAGE];
sprintf(errorMessage,
"<?xml version=’1.0’ encoding=’iso-8859-1’ standalone=’no’?>"
"<!DOCTYPE vxmls SYSTEM \"/dtds/vxmls.dtd\">"
"<vxmls><error>%s</error></vxmls>", message);
write(fileDes, errorMessage, strlen(errorMessage));
}
Una vez terminada la escritura de la respuesta, se cierra el descriptor de fichero
y termina el hilo de ejecución.
5.11.4.3.
Funciones exportadas
Con ayuda de la función httpProxy la implementación de las funciones del
API ofrecido por esta fachada es tan sencilla como:
1. Crear la cadena con la URL a enviar en la petición GET (en la sección 4.4
se describen las URLs a las que se debe solicitar cada posible operación):
Implementación del módulo VXMLS
135
Para vxmlsLogin, siendo vxmlsClient el nombre que debe utilizar
DFBCIn para crear la sesión y vxmlsPasswd la clave que debe utilizar
para identificarse:
sprintf(request, VXMLS_PREFIX
"login?client=%s&passwd=%s",
vxmlsClient, vxmlsPasswd);
Para vxmlsLogout, simplemente VXMLS PREFIX "logout".
Para vxmlsSetPorperty, suponiendo escapedName y escapedValue el
nombre y el nuevo valor de la propiedad ya codificados con la función
escapedEncoding:
sprintf(request, VXMLS_PREFIX
"set_property?name=%s&value=%s",
escapedName, escapedValue);
Para vxmlsGetMenu es necesario un bucle para añadir todos los argumentos.
strcpy(escapedMenuName, menuName);
escapedEncoding(escapedMenuName);
/* Get the request */
sprintf(request, VXMLS_PREFIX "get_menu?menu=%s",
escapedMenuName);
if (arguments != NULL) {
/* Append the parametters */
for (i = 0; i < numArrayElements(arguments); i++) {
name = (char *) getArrayData(arguments, i++);
value = (char *) getArrayData(arguments, i);
/* Encode strings */
strcpy(escapedName, name);
escapedEncoding(escapedName);
strcpy(escapedValue, value);
escapedEncoding(value);
/* Append to the request */
sprintf(request, "%s&%s=%s", request, escapedName,
136
Capı́tulo 5. Implementación de DFBCIn
escapedValue);
}
}
2. Hacer una llamada a httpProxy con la petición generada:
fileDes = httpProxy(request);
3. Utilizar el parser adecuado para interpretar la respuesta de VXMLS:
Para vxmlsGetMenu se utilizará la función parseMenu y se copiarán
los valores con los que fue obtenido el menú:
menu = parseMenu(fileDes);
close(fileDes);
strcpy(menu -> name, menuName);
menu -> arguments = arguments;
addArrayReference(arguments);
return menu;
El resto de funciones utilizarán parseVxmlsResponse y comprobarán
que el resultado devuelto por VXMLS es mensaje de confirmación.
En caso de fallo en la comunicación también se obtendrá un mensaje
de error, como se explicó antes, el hilo que se encarga de obtener la
respuesta escribe un mensaje de error por sı́ mismo si no consigue
contactar correctamente con VXMLS.
if (parseVxmlsResponse(fileDes) != NO_ERROR) {
setErrorMessage(getVxmlsErrorMessage());
DFBCIN_FATAL(VXMLS_ERROR);
}
close(fileDes);
5.12.
Implementación del Módulo gráfico
5.12.1.
Introducción
El módulo gráfico se implementará como una biblioteca dinámica para permitir que se puedan intercambiar distintos módulos de este tipo sin necesidad de
recompilar la aplicación.
Implementación del Módulo gráfico
137
El API que debe implementar la biblioteca para poder ser utilizada como
módulo gráfico se puede ver en el apéndice B. En las siguientes secciones se
explicarán los pasos que se han seguido en la implementación del módulo gráfico
con la ayuda DirectFB. También se ha hecho otra implementación para algunas
pruebas que utiliza la consola de texto en lugar del hardware gráfico.
5.12.2.
Estructuración del código
La implementación de la fachada es idéntica para los dos módulos gráficos
implementados, por lo que el código se organizará en tres directorios, un directorio Common en el que se almacenará el fichero graphics.c y las cabeceras
interface-draw.h, video.h, graphics-init.h y event-catcher.h que definen
las diferentes clases utilidad que componen el módulo gráfico.
Las cabeceras anteriores serán implementadas con el correspondiente fichero
.c en los directorios DFB para el módulo gráfico completo y Character para el
módulo gráfico de pruebas.
5.12.3.
La fachada graphics.c
En el fichero graphics.c se implementan todas las funciones definidas por
el API del módulo gráfico. Excepto las funciones de creación y destrucción, el
resto delegan directamente en la clase correspondiente: InterfaceDraw para las
operaciones de dibujo, EventCatcher para el método testInput y Video para las
operaciones de control de vı́deo.
La función graphicsInit llamará a la funciones de inicialización de los subsistemas que componen este módulo.
void graphicsInit(int argc, char **argv) {
specificInit(argc, argv);
interfaceDrawInit();
videoInit();
}
specificInit se define en el fichero graphicsInit.c de cada uno de los dos
módulos gráficos implementados, para permitir realizar operaciones especı́ficas de
inicialización.
graphicsDeinit llama a las funciones de destrucción de los subsistemas inicializados por graphicsInit.
138
Capı́tulo 5. Implementación de DFBCIn
void graphicsDeinit(void) {
interfaceDrawDeinit();
videoDeinit();
specificDeinit();
}
specificDeinit tiene el mismo cometido que specificInit: permitir operaciones especı́ficas de destrucción.
5.12.4.
La inicialización de DirectFB
Para inicializar el módulo gráfico que utiliza DirectFB, specificInit deberá:
1. Inicializar DirectFB llamando a la función DirectFBInit.
2. Crear la interfaz primaria llamando a DirectFBCreate.
3. Utilizando la interfaz primaria, crear la superficie primaria y el buffer de
eventos de entrada.
La función specificDeinit deberá liberar los recursos creados durante la
inicialización por specificInit.
5.12.5.
Control de eventos de entrada
En el fichero event-catcher.c se implementa la función testInput que deberá detener la ejecución hasta que se detecte que el usuario ha pulsado una de
las teclas soportadas (todas las del mando a distancia y alguna del teclado para
facilitar las pruebas).
El control de entrada utilizando DirectFB se puede realizar mediante dos
métodos: con o sin buffer de eventos. Para la este proyecto se utilizará el control
de entrada utilizando buffer de eventos, de esta forma no es necesario implementar mecanismos adicionales para recordar los eventos que aún no fueron atendidos.
Para esta aplicación sólo interesan los eventos de entrada (en un buffer de
eventos genérico de DirectFB también se almacenan otro tipo de eventos, por
ejemplo eventos relacionados con la gestión de ventanas), estos eventos se pueden
manejar utilizando un tipo especial de buffer de eventos creado con la función
CreateInputEventBuffer de la interfaz principal.
Implementación del Módulo gráfico
139
Un buffer de eventos de entrada de DirectFB puede detectar varios tipos
de eventos:
Eventos de teclas.
Eventos de ejes.
Eventos de botones.
El buffer de eventos de entrada utilizado sólo deberá preocuparse de los eventos de teclas, que pueden ser o bien eventos de pulsación de tecla DIET KEYPRESS,
o bien eventos de liberación de tecla DIET KEYRELEASE.
Para controlar el mando a distancia se utilizará la herramienta LIRC [46].
Esta herramienta está soportada directamente por DirectFB, de forma que los
eventos detectados por LIRC generarán eventos de pulsación y liberación de teclas
en las aplicaciones que utilizan DirectFB. Para ello, en el fichero de configuración de LIRC se deben nombrar los códigos reconocidos para cada tecla del
mando a distancia con un nombre equivalente a alguna de las teclas definidas en
el fichero directfb-keynames.h sin el prefijo DIKS . Por ejemplo, para la tecla
“play” del mando a distancia, se añade la lı́nea PLAY 0x000000000032CCB3 en el
fichero lircd.conf para asociar la tecla PLAY al código generado con la pulsación
de “play” en el mando a distancia. En las aplicaciones DirectFB se detectarán
eventos DIKS PLAY para esa tecla.
Un problema que presenta el uso del mando a distancia es el hecho de que éste
no genera un único evento DIET KEYPRESS cuando se pulsa la tecla y un evento
DIET KEYRELEASE cuando se suelta la tecla, sino que está generando eventos como
si se pulsara la tecla intermitentemente. Esto provoca que el control de la interfaz
se haga incómodo, ya que ante una pulsación de una tecla puede comportarse
como si se pulsara varias veces la misma tecla.
Para solucionar este problema, se descartarán los eventos que se repitan en
un determinado intervalo de tiempo.
Utilizando el buffer de eventos proporcionado por DirectFB, la implementación de la función testInput es sencilla:
1. Llamar a la función waitForEvent del buffer de eventos para detener la
ejecución hasta que haya un evento disponible.
2. Obtener el evento detectado llamando a la función getEvent del buffer de
eventos.
140
Capı́tulo 5. Implementación de DFBCIn
3. Comprobar si el evento detectado es relevante para la aplicación:
a) Comprobar que se trata de la pulsación de una tecla.
b) Comprobar que la tecla pulsada es alguna de las soportadas por la
aplicación.
c) Obtener el valor InputKey utilizado por la aplicación para identificar
a esa tecla. Para esto se utilizará una tabla en la que se relacionan los
eventos DFBInputEvent con las teclas InputKey.
4. Filtrar el caso de que un mismo evento se repita en un pequeño intervalo
de tiempo.
5. En caso de que se haya obtenido un evento válido se devuelve, en otro caso
se vuelve al paso uno.
5.12.6.
Impresión de menús y texto
DirectFB (ver la sección 2.4) define un tipo de interfaz IDirectFBSurface
que representa una superficie en la que realizar operaciones gráficas tales como
copiar el contenido de otras superficies, dibujar rectángulos o lı́neas, imprimir
texto, etc. La superficie mostrada en pantalla es la conocida como superficie primaria.
Las superficies pueden dotarse de un sistema de doble buffer, las operaciones
ejecutadas sólo se harán visibles cuando se ejecute la operación Flip de la superficie, que vuelve visible el buffer invisible (back buffer ) e invisible el buffer visible
(front buffer ). Esto es útil en la superficie primaria, porque de esta forma se puede sincronizar la operación Flip con el refresco del monitor evitando los efectos
desagradables que surgen si se cambia el contenido de la memoria de vı́deo sin
tener en cuenta este factor.
InterfaceDraw proporciona operaciones para cargar y descargar un menú,
cargar y descargar una serie de lı́neas de texto y modificar la forma en la que
se dibujan los menús (si se muestra o no un marco rodeándolos). La función
interfaceDrawFlipBuffer dibujará los elementos cargados en caso de que los
haya.
En la inicialización de este módulo se cargarán las fuentes e imágenes que se
van a utilizar para generar la interfaz con el usuario. DirectFB define la interfaz
IDirectFBFont para representar una fuente, las imágenes se almacenan en superficies. Para cargar los elementos utilizará la interfaz IDirectFBImageProvider para
Implementación del Módulo gráfico
141
leer los ficheros de imagen y la función CreateFont de la interfaz primaria para
crear las fuentes dado un tamaño y un fichero True Type. También se creará una
variable de estado en la que almacenar el menú y el texto cargados y una bandera
para indicar si se debe o no dibujar un marco rodeando al menú.
La función de destrucción deberá liberar todos los recursos anteriormente creados por la función de inicialización.
La función drawMenu bloqueará el menú cargado para asegurar que no se modificará la opción actualmente seleccionada durante el proceso de representarlo
gráficamente. Para dibujar el menú se escribirá el campo header a modo de tı́tulo, las opciones se dibujarán en cinco lı́neas. En caso de que haya más de cinco
opciones, se dibujará la opción actualmente seleccionada en la lı́nea del centro, las
opción siguiente y la anterior en la cuarta y segunda lı́nea y unas flechas arriba y
abajo en la primera y quinta lı́nea. Dibujar una opción supone escribir su campo
text. La opción actualmente seleccionada se dibujará en color amarillo y el resto
en color azul.
La función drawText simplemente escribirá las lı́neas de texto cargadas.
Para centralizar el control sobre la superficie primaria, será InterfaceDraw la
única encargada de ejecutar la operación Flip sobre la misma. Para ello se implementa la función interfaceDrawFlipBuffer, que ejecuta las siguientes operaciones:
1. En caso de que su argumento showBackground sea distinto de 0 copiará la
superficie con la imagen de fondo en el buffer invisible.
2. Si hay un menú cargado, dibujará dicho menú en el buffer invisible.
3. Si hay texto cargado, imprimirá dicho texto en el buffer invisible.
4. Por último ejecutará la operación Flip sobre la superficie primaria durante
el próximo barrido vertical:
DFBCHECK(primary -> Flip(primary, NULL, DSFLIP_WAITFORSYNC));
5.12.7.
Reproducción de vı́deo
DirectFB es capaz de reproducir distintos formatos de vı́deo utilizando su
sistema de proveedores (ver la sección 2.4). Los vı́deos se cargan creando una interfaz IDirectFBVideoProvider, utilizando la función CreateVideoProvider de la
142
Capı́tulo 5. Implementación de DFBCIn
interfaz principal. DirectFB detecta el formato de vı́deo y carga la implementación necesaria de proveedor de vı́deo.
En las pruebas se comparó la reproducción de vı́deos en formato MPEG con
la reproducción de vı́deos en formato AVI, obteniéndose mejores resultados en el
segundo caso.
El proveedor de vı́deo utilizado para reproducir ficheros AVI usa la biblioteca avifile [47]. La versión proporcionada con DirectFB estaba implementada
sobre una versión antigua de avifile y presentaba ciertos problemas que serán
comentados posteriormente, pero que fueron solucionados, en parte, adaptando
la implementación del proveedor para que utilizará una versión más moderna de
avifile.
El procedimiento que se sigue para la carga y reproducción de un vı́deo es el
siguiente:
Con una llamada a la función videoLoad se carga un fichero de vı́deo de
forma que quede listo para ser reproducido. El proceso de carga de un vı́deo
es el siguiente:
1. Crear el proveedor de vı́deo sobre el fichero de vı́deo a reproducir.
2. Obtener la información sobre la resolución del vı́deo a reproducir y
utilizarla para crear una superficie en la que reproducirlo.
3. Crear un rectángulo del máximo tamaño posible (respetando la relación ancho/largo del vı́deo) en el que se va a copiar cada frame en la
superficie primaria.
La función videoPlay iniciará la reproducción del vı́deo en la superficie
creada por videoLoad. Para ello utilizará la función PlayTo del proveedor
de vı́deo creado por videoLoad:
videoProvider -> PlayTo(videoProvider, videoSurface, NULL,
videoCallback, NULL);
El proveedor llamará a la función videoCallback cada vez que esté disponible un nuevo frame. Esta función será utilizada para llamar a la función
instalada por videoSetNewFrameCallback.
La función videoStop detendrá la reproducción de vı́deo utilizando la función Stop del proveedor de vı́deo.
Implementación del Módulo gráfico
143
La función videoBlit copia el frame actual en el buffer no visible de la
superficie primaria (ver la sección 5.12.6). Esta función será llamada por
Interface cada vez que se notifique que hay un nuevo frame.
La función videoUnload elimina el proveedor de vı́deo y libera la superficie
utilizada para reproducir el vı́deo.
Las funciones para obtener la posición actual y para mover la reproducción a una determinada posición utilizan las funciones que proporciona el
proveedor de vı́deo: GetPos y SeekTo.
5.12.7.1.
Detección del final de la reproducción
La interfaz IDirectFBVideoProvider no tiene ninguna función que permita detectar el final del vı́deo directamente, sin embargo, en la definición de la acción
VideoPlay, se especifica un array de acciones que se deberán ejecutar al terminar
el vı́deo.
Por lo tanto, es necesario detectar el final del vı́deo de alguna forma y ejecutar
la función instalada por videoSetVideoEndCallback. Para ello, es necesario crear
un nuevo hilo de ejecución que monitorice el estado del proveedor de vı́deo durante
la reproducción. La función de inicialización creará dicho hilo (observerThread)
y una variable de estado para comunicarse con él de forma similar a la explicada
para Interface e InterfaceInput en las secciones 5.10.2 y 5.10.3.
La primera opción que se barajó fue la de comprobar cada cierto tiempo que
el valor devuelto por la función GetLength y la función GetPos del proveedor de
vı́deo no era el mismo, pero se comprobó que la función GetPos llamada al final
del vı́deo no siempre devolvı́a la última posición.
La solución que se adoptó definitivamente fue comprobar cada cierto tiempo
que el valor devuelto por GetPos habı́a cambiado, de no ser ası́, se puede asumir
que el vı́deo está detenido en el final. Esta solución obliga a disponer de una
bandera VST PLAY que se active sólo cuando el vı́deo se está reproduciendo para
evitar que el hilo detecte el final del vı́deo cuando lo que realmente está sucediendo es que el vı́deo está detenido debido a una llamada a videoStop. También
se necesita otra bandera VST SEEK que se activará en caso de que se ejecute la
función videoGotoVideoPosition, para advertir que no se tenga en cuenta la
próxima comparación ya que podrı́a darse la casualidad de que se ejecutara sobre
la misma posición.
El ciclo de ejecución del hilo observerThread será como sigue:
144
Capı́tulo 5. Implementación de DFBCIn
1. Esperar N segundos.
2. Bloquear la variable de estado.
3. Si está activa la bandera VST SEEK desactivarla y guardar la posición actual
en el campo lastFrame del estado.
4. Si está activa la bandera VST PLAY:
Si el valor devuelto por GetPos es igual a lastFrame entonces se desactiva VST PLAY y se llama a la función instalada para avisar del final
del vı́deo por videoSetVideoEndCallback.
En otro caso, poner el valor devuelto por GetPos en lastFrame.
5. desbloquear la variable de estado y volver a 1.
Para que este hilo funcione correctamente es necesario modificar algunas de
las funciones de video.h (las operaciones sobre la variable de estado se harán
siempre bloqueando el semáforo primero):
videoPlay debe activar la bandera VST PLAY.
videoStop debe desactivar la bandera VST PLAY.
videoGotoVideoPosition debe activar la bandera VST SEEK en caso de que
se salte a una posición anterior.
5.12.8.
Módulo de pruebas
La implementación del módulo gráfico de pruebas consiste simplemente en los
ficheros:
event-catcher.c: Se hace un control de la entrada utilizando las funciones
de la biblioteca stdio.
video.h: Lo único que hacen todas las operaciones es escribir un mensaje
en la consola indicando que fueron llamadas.
graphics-init.c: Las implementaciones de las funciones specificInit y
specificDeinit son vacı́as.
interface-draw.c: Tiene un comportamiento similar al de su homólogo en
la implementación “seria”. Las funciones interfaceDrawBlitBackground
y intefaceDrawSetBox sólo muestran mensajes para indicar que fueron
ejecutadas, los menús se dibujan con cadenas de texto como, por ejemplo:
Etapas de codificación
145
--------------"Menu Principal"
--------------*(0) Axustes
(1) Pelı́culas
Las lı́neas de texto, simplemente, se muestran en la consola.
5.13.
Sistema de propiedades
El sistema de propiedades lee el contenido de un fichero local y lo almacena
en un array que contiene datos del tipo Property.
El tipo Property apunta a una estructura con dos campos:
name: Nombre de la propiedad.
value: Valor de la propiedad.
Las funciones getProperty y setProperty se implementan asegurando la
sincronización mediante la implementación de un sistema de lectores-escritores.
Varios hilos pueden estar leyendo una misma propiedad concurrentemente, pero
un hilo puede modificar una propiedad sólo en caso de que no haya ningún otro
hilo accediendo a la misma.
Esto fue utilizado durante algunas etapas del desarrollo, pero en el sistema
final el sistema de propiedades sólo se utiliza para almacenar datos estáticos, las
propiedades cambiantes serán controladas por VXMLS.
5.14.
Etapas de codificación
El desarrollo final se consiguió después de pasar por los siguientes incrementos.
5.14.1.
Desarrollo del núcleo de la aplicación
En este primer incremento se pretende obtener un sistema con la funcionalidad
mı́nima. Para ello se desarrollan los siguientes módulos:
Las acciones NextMenu y PreviousMenu, para permitir la navegación por
el sistema de menús.
146
Capı́tulo 5. Implementación de DFBCIn
El parser de menús. En este incremento el parser será capaz de interpretar
las etiquetas header, version, option, action, previousMenu y nextMenu.
El control de entrada sólo reconoce la pulsación de las teclas arriba, abajo
y SEL. Por ahora no se utiliza el mando a distancia.
Como aún no se dispone de las acciones BindKey ni ChangeOption, Interface se encargará de cambiar la opción seleccionada cuando se detecte la
pulsación de las teclas arriba o abajo y de mandar ejecutar las acciones de
la opción seleccionada en cuanto se detecte la pulsación de ENTER.
En este incremento InterfaceDraw sólo será capaz de mostrar menús.
No hay módulo VXMLS, la descripción de los menús se lee de ficheros
locales, que se pasan directamente al parser.
Tras esto se obtiene una aplicación que muestra una serie de menús gráficos
descritos por ficheros XML, permitiendo navegar por ellos a través de las opciones
mostradas, utilizando las teclas arriba, abajo y SEL.
5.14.2.
Reproducción básica de vı́deo
En este iteración se añadirán las acciones que permitan iniciar la reproducción
de un vı́deo.
Implementación de las acciones PlayVideo, StopVideo, ResumeVideo y UnloadVideo. Por ahora la acción PlayVideo no contempla las acciones de
finalización.
Modificación del parser para que puedea interpretar las etiquetas que definen estas nuevas acciones.
Implemetación de Video en el módulo gráfico. Por ahora no es necesario
detectar la finalización del vı́deo.
En esta iteración se obtiene una aplicación que ya cumple con los requisitos
mı́nimos que se exigen en la especificación. Muestra una interfaz definida por
ficheros XML y es capaz de reproducir ficheros de vı́deo.
Etapas de codificación
5.14.3.
147
Pruebas en el Set Top Box
Hasta este paso se hizo el desarrollo en un PC. En esta iteración se harán las
pruebas en el Set Top Box y se desarrollará el control del mando a distancia.
Pruebas de rendimiento el el Set Top Box, en este paso se realizaron las
optimizaciones explicadas en el apartado 7.3.1.
Instalación de LIRC y comprobación de su funcionamiento con la aplicación.
Por ahora, las únicas teclas utilizables siguen siendo arriba, abajo y ENTER.
Tras esto ya se tiene el sistema completo, funcionando de forma básica en la
televisión y con el mando a distancia como sistema de entrada.
5.14.4.
Varias mejoras
En esta iteración se terminará de desarrollar el sistema de entrada, se incorporará la temporización, las acciones de personalización y se completará la
implementación del sistema de vı́deo.
Para completar el sistema de entrada:
Implementación completa de testInput para que sea capaz de detectar la
pulsación de todas las teclas del mando a distancia.
Implementación de la acción BindKey para permitir la asociación de teclas
con eventos.
Implementación del mecanismo de inicialización de menús, de forma que
cuando se cargue un menú en Interface se ejecuten las acciones del campo
initActions de dicho menú. Este campo se hace imprescindible, ya que
ahora por defecto las teclas están vinculadas al evento NULL.
Implementación de la acción ChangeOption, ya que ahora ya se puede asociar a una tecla.
Modificación del parser para que sea capaz de interpretar la descripción de
estas nuevas caracterı́sticas.
Para implementar la temporización se debe modificar el método waitForActions de Interface para que devuelva las acciones del campo timeActions del
menú actual en caso de que finalice la temporización. También se deberá modificar el parser para que pueda interpretar el campo timer de los documentos XML.
148
Capı́tulo 5. Implementación de DFBCIn
Para implementar la posibilidad de personalización se crea una nueva acción
SetProperty, que, por ahora, posibilitará definir y modificar los valores para ciertas
propiedades, como por ejemplo definir el lenguaje a utilizar. Esta acción trabajará sobre el sistema de propiedades local descrito en 5.13.
En el sistema de vı́deo se añade el control de finalización, para lo que es necesario modificar también la acción PlayVideo y el parser para que interprete el
contenido de la etiqueta loadVideo como el conjunto de acciones a ejecutar en
caso de que finalice la reproducción.
También se añade una nueva acción SeekVideo para permitir reposicionar la
reproducción. De nuevo, es necesario modificar el parser para que pueda interpretar las etiquetas que definen esta acción.
5.14.5.
Comunicación HTTP
En esta iteración se implementará el sistema de comunicación HTTP, pero
aún no se utilizará con VXMLS, ya que no está desarrollado todavı́a, sino que
obtendrá los ficheros de un servidor HTTP estático.
Una vez finalizada esta iteración ya se tendrá un sistema capaz de mostrar
una interfaz definida remotamente y de reproducir vı́deo, interactuando con el
usuario a través de una televisión y un mando a distancia.
En este punto se detiene temporalmente el desarrollo de DFBCIn y se comienza a implementar VXMLS.
5.14.6.
Integración con VXMLS
En este incremento se completa el desarrollo el módulo VXMLS para que acceda a las URLs del mismo para solicitar los menús o cambiar las propiedades
de personalización. Para esto es necesario modificar también las acciones ChangeProperty y NextMenu.
Al utilizar VXMLS como servidor se implementan dos nuevas caracterı́sticas:
los parámetros para solicitar menús y la selección de lenguaje.
La selección de lenguaje consiste simplemente en modificar la propiedad de
personalización language utilizando una acción SetProperty, pero para poder
definir correctamente la interfaz, es necesario implementar una nueva acción Re-
Etapas de codificación
149
loadMenu para permitir volver a solicitar el menú principal en el nuevo idioma.
Para facilitar la generación de menús, VXMLS acepta parámetros cuando se le
solicita un determinado menú. Para implementar esta caracterı́stica es necesario
modificar la acción NextMenu.
Será también necesario modificar el parser para que pueda interpretar la descripción de la acción ReloadMenu y los parámetros que ahora se incluyen en las
descripciones de las acciones NextMenu.
5.14.7.
Integración con VoDKA
El sistema de vı́deo utilizado hasta ahora es capaz de reproducir vı́deos desde
un fichero local, pero el Set Top Box desarrollado debe reproducir vı́deo obtenido
de un servidor de streaming. Es necesario, por tanto, utilizar un mecanismo que
permita a DFBCIn acceder a este tipo de medios.
En [37] se desarrolla un sistema de ficheros virtual que permite a los reproductores un acceso sencillo a los medios obtenidos mediante la descarga de streaming.
Utilizando este sistema no es necesario realizar ninguna modificación a la
aplicación DFBCIn, simplemente se debe montar un sistema de ficheros virtual y
acceder a los medios disponibles de la misma forma que se accedı́a a los ficheros
locales.
5.14.8.
Reproductor Mp3 y acciones de texto
Finalmente se añaden las acciones LoadMp3, PauseContinueMp3, KillMp3Player, LoadText y UnloadText. Para ello es necesario implementar la clase Mp3Player y llevar a cabo pequeñas modificaciones en la librerı́a gráfica. Como en
los casos anteriores, se deberá modificar el parser de menús para que pueda
interpretar las nuevas etiquetas y el servidor VXMLS para que pueda generarlas.
Capı́tulo 6
Implementación de VXMLS
Índice General
6.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . 152
6.1.1. Generalidades sobre Erlang/OTP . . . . . . . . . . . . 152
6.1.2. ¿Cómo funciona Inets? . . . . . . . . . . . . . . . . . . 152
6.1.3. ¿Cómo funciona Mnesia? . . . . . . . . . . . . . . . . 153
6.1.4. Servidores genéricos . . . . . . . . . . . . . . . . . . . 154
6.1.5. Estructura de la aplicación VXMLS . . . . . . . . . . 154
6.1.6. Tipos de datos . . . . . . . . . . . . . . . . . . . . . . 155
6.1.7. Control de errores . . . . . . . . . . . . . . . . . . . . 156
6.2. Capa Modelo . . . . . . . . . . . . . . . . . . . . . . . . 157
6.2.1. Tipos de datos . . . . . . . . . . . . . . . . . . . . . . 157
6.2.2. Administración de la base de datos . . . . . . . . . . . 159
6.2.3. Funciones de la fachada . . . . . . . . . . . . . . . . . 159
6.3. Capa Vista . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.4. Capa Controlador . . . . . . . . . . . . . . . . . . . . . 167
6.4.1. Generación de la respuesta HTTP . . . . . . . . . . . 167
6.4.2. Traducción de mensajes . . . . . . . . . . . . . . . . . 167
6.4.3. Fachada . . . . . . . . . . . . . . . . . . . . . . . . . . 168
6.4.4. Creación y destrucción de sesiones . . . . . . . . . . . 168
6.4.5. Propiedades de personalización . . . . . . . . . . . . . 169
6.4.6. Generación de menús
. . . . . . . . . . . . . . . . . . 169
6.4.7. Composición de objetos Menu
151
. . . . . . . . . . . . . 171
152
Capı́tulo 6. Implementación de VXMLS
6.4.8. Menús definidos . . . . . . . . . . . . . . . . . . . . . 172
6.4.9. Ejemplos de vxmls get menu . . . . . . . . . . . . . . 173
6.5. Extensibilidad . . . . . . . . . . . . . . . . . . . . . . . 175
6.1.
Introducción
6.1.1.
Generalidades sobre Erlang/OTP
El modelo de programación de Erlang se basa en la creación de procesos que
se ejecutan concurrentemente y que se comunican entre ellos mediante paso de
mensajes. El código se estructura en módulos, cada uno de los cuales implementa
una serie de funciones. Un módulo puede exportar funciones que serán accesibles
al resto, las funciones no exportadas son locales y no afectan para nada a los
demás módulos.
Un proceso Erlang es una unidad de computación aislada, que coexiste concurrentemente con otros procesos en el sistema. Un proceso se crea utilizando la
BIF spawn a la que se le pasa el nombre de un módulo, una función de dicho
módulo y una lista de argumentos. El proceso creado finalizará cuando termine la
ejecución de la función. Estos procesos se ejecutan “dentro” de un nodo Erlang,
cada nodo Erlang ocupa un único proceso del sistema operativo.
Erlang/OTP [4] también se define el concepto de aplicación. Una aplicación
es un paquete de recursos, tales como nombres registrados, módulos y procesos.
La aplicación VXMLS, utilizará, a su vez, algunas aplicaciones que forman
parte del entorno Erlang/OTP: Inets [39] que es un contenedor de servidores para
Internet, Mnesia [40] que es un sistema gestor de bases de datos distribuidas y
Mnemosyne [41] que es una interfaz de consulta para Mnesia.
6.1.2.
¿Cómo funciona Inets?
Inets incluye una implementación de un servidor web compatible con el protocolo HTTP 1.1 [35]. Para comunicarse con aplicaciones externas el servidor
implementa CGI [48] y ESI (Erlang Scripting Interface).
ESI proporciona una interfaz eficiente para acceder a funciones Erlang, se
recomienda la utilización de ESI sobre la utilización de CGI por razones de eficiencia, ya que CGI necesita que el servidor cree un nuevo proceso de sistema
Introducción
153
operativo.
ESI imita a CGI pero sin el sobrecoste de crear un nuevo proceso, una URL
puede llamar a una función Erlang de acuerdo con la siguiente sintaxis:
http://your.server.org/***/Mod[:/]Func(?QueryString|/PathInfo)
El nodo Erlang en el que se ejecuta Inets deberá tener acceso a un módulo Mod
con una función Func con dos argumentos. Si un cliente accede a la URL anterior
se ejecutará Func(Env,Input), donde Env contiene información sobre el cliente e
Input la cadena QueryString o PathInfo de acuerdo con la especificación [48], la
función deberá devolver una cadena de texto siguiendo esta misma especificación.
Para implementar VXMLS se utilizará el sistema ESI, las funciones utilizadas
para recibir las llamadas de Inets serán las de la fachada VXMLSFacade (ver la
sección 4.6.5).
En el fichero de configuración de Inets se debe especificar que URLs se utilizan
de interfaz con ESI y a qué módulos pueden acceder. Para VXMLS, estas funciones se definirán en el módulo vxmls, y únicamente se utilizará una URL para
acceder éstas. Suponiendo que se estén ejecutando unas pruebas con el servidor
en el puerto 8080 de la máquina local:
http://localhost:8080/vxmls/Mod/Func?QueryString
Donde Mod debe ser vxmls y Func podrá ser alguna de las siguientes: login,
logout, get menu o set property.
En la sección 4.4 se explican las peticiones que se pueden enviar a VXMLS.
6.1.3.
¿Cómo funciona Mnesia?
El acceso a una base de datos Mnesia desde una aplicación Erlang es sencillo,
una vez arrancada la aplicación sobre un nodo, se pueden utilizar las funciones
exportadas por el módulo mnesia para manipular las tablas.
Para realizar consultas complejas se debe utilizar Mnemosyne que permite
escribir consultas embebidas en el código Erlang utilizando una construcción llamada query list comprehensions. La sintaxis es la siguiente:
query [ <patrón> || <cuerpo> ] end
Por ejemplo:
154
Capı́tulo 6. Implementación de VXMLS
query
[ Empleado || Empleado <- table(empleado),
Empleado.departmento = ventas
end
6.1.4.
]
Servidores genéricos
Erlang/OTP define el concepto de comportamiento (behaviour ) como una
formalización de patrones de diseño que pueden ser utilizados para programar
algunos problemas genéricos.
Un módulo que utilice un comportamiento deberá exportar ciertas funciones
que que serán llamadas por el sistema mientras el proceso se ejecuta.
En Erlang/OTP, una aplicación cliente-servidor puede ser programada utilizando el comportamiento gen server, este comportamiento será utilizado para
implementar dos servidores en la aplicación VXMLS, el servidor vxmls que representa a la aplicación en sı́ (cuando termina la ejecución del proceso vxmls termina
la ejecución de la aplicación) y el servidor vxmls lang, que mantendrá tablas con
los posibles mensajes a mostrar traducidos a los diferentes idiomas soportados.
Un servidor genérico se crea con la función start del módulo gen server que
crea un proceso con el nombre del servidor y lo mantiene en ejecutándose hasta
que se elimina utilizando alguno de los mecanismos establecidos para detener el
servidor.
6.1.5.
Estructura de la aplicación VXMLS
El código de la aplicación VXMLS se estructura en los siguientes módulos:
vxmls: Contiene las funciones para inicializar y detener la aplicación (start
y stop) y las funciones que atenderán las peticiones de Inets.
vxmls lib: En este módulo se agrupan funciones de diversa ı́ndole.
vxmls xml : Este módulo exporta funciones para la generación de documentos XML.
vxmls menu: Este módulo exporta funciones para la generación de objetos
Menu.
vxmls db: Exporta las funciones para acceder a la capa modelo de VXMLS
y una función para inicializar la base de datos.
Introducción
155
vxmls lang: Exporta funciones para obtener mensajes traducidos a diversos
idiomas.
vxmls conf : Exporta funciones para acceder al fichero de configuración de
VXMLS.
vxmls properties: Exporta las funciones necesarias para atender a la petición
set property.
vxmls login: Exporta las funciones necesarias para atender a las peticiones
login y logout.
vxmls menu dispatcher : Exporta las funciones necesarias para atender a la
petición get menu.
Además de estos, cada menú es generado por un módulo, esto permite añadir
nuevos menús sin necesidad de modificar ni recompilar el código existente. Para
generar un menú llamado, por ejemplo, foo menu, será necesario crear un nuevo
módulo vxmls foo menu. Como norma general, todos los módulos creados con este
objetivo tendrán un nombre de forma vxmls *** menu.
6.1.6.
Tipos de datos
Para representar los objetos necesarios en Erlang se siguieron distintas aproximaciones:
Los objetos valor [44] se representan definiendo un registro con los campos necesarios. Para ocultar la estructura interna del tipo se definirán las
funciones necesarias para acceder a sus campos en un único módulo. Por
ejemplo: la clase Menu 1 se implementa definiendo un registro menu y el
módulo vxmls menu con las funciones para manipularlo.
Las clases utilidad se representan con un módulo que exporte las funciones
requeridas.
Los objetos Singleton se representan utilizando el comportamiento Erlang
gen server.
1
en la forma en que se utiliza en VXMLS, ver sección 4.6.2
156
Capı́tulo 6. Implementación de VXMLS
6.1.7.
Control de errores
La filosofı́a de control de errores de Erlang consiste en asumir siempre que los
argumentos recibidos por una función son correctos, es decir, no hacer control de
errores de forma explı́cita.
Si en algún momento una función llega a una situación inesperada, el proceso
que ejecuta esta función debe morir. Se pueden establecer jerarquı́as de procesos
en las que procesos supervisores monitoricen la ejecución de procesos trabajadores, cuando un trabajador muere debido a un error, un supervisor detecta el fallo
y actúa en consecuencia para corregir el error.
En caso de que se vaya a utilizar una función susceptible de provocar un error,
pero que no necesariamente debe provocar la muerte del proceso, el código se puede ejecutar dentro de un bloque catch que devolverá la tupla {’EXIT’, Razon}
en caso de que el código que contiene alcance algún punto en el que el proceso
finalizarı́a en circunstancias normales.
En la aplicación desarrollada los procesos pueden morir debido a diversas
causas, identificadas por los siguientes valores de Razon:
{open, Atom, File}: Error al abrir un fichero. Atom identifica la causa del
error y File es el nombre del fichero.
{format, Reason, File}: Un fichero leı́do no tiene el formato esperado.
Reason contiene una descripción del error y File es el nombre del fichero.
{not found, Key}: No se ha encontrado un valor almacenado para la clave
Key.
bad request: Se ha recibido una petición HTTP no válida.
incorrect password: La clave del Set Top Box no es correcta.
cookie not valid: La cookie asociada al Set Top Box no es válida.
not logged in: El Set Top Box no tiene asociada una sesión.
no home argument: La aplicación se ha arrancado sin el argumento home,
necesario para acceder a algunos ficheros.
{unknown item, {Name, Value}}: Se ha proporcionado un valor desconocido o una propiedad desconocida. Name indica el nombre de la propiedad
y Value el valor.
Capa Modelo
157
{database error, Reason}: Error alguna operación realizada sobre la base
de datos. Reason puede tomar alguno de los siguientes valores:
• {not found, Key}: No se encontró el registro con la clave Key.
• {reference constraint, Table, Key}: La clave Key utilizada como
referencia a un registro de la tabla Table no está presente en ésta.
• {already exists, Key}: Ya existe un objeto con clave Key.
• user not found: El usuario no se ha encontrado.
Cualquier otro error será considerado desconocido a la hora de notificarlo.
6.2.
Capa Modelo
6.2.1.
Tipos de datos
Las funciones utilizadas para insertar datos en Mnesia utilizan registros para
representar las tuplas de las tablas. Esto implica que en una tabla de la base de
datos se puede almacenar cualquier tipo de dato Erlang. Normalmente sólo se
almacenarán cadenas de texto o átomos, pero en algunos casos también se utilizarán tuplas para representar claves múltiples
En la mayorı́a de los casos un objeto del dominio se relacionará con una tabla de la base de datos, excepto la tabla shown movie que no representa ningún
objeto, sino la relación N a N entre user y movie y el objeto TranslatedMovie,
que no representa ninguna tabla de la base de datos.
En el fichero vxmls tables.hrl se definen los registros que se utilizarán para
almacenar los datos sobre los diferentes objetos del dominio y las tablas de la
base de datos:
set top box: Contiene los campos necesarios para identificar a un Set Top
Box cliente:
• id: Cadena de texto que identifica unı́vocamente al cliente.
• passwd: Contiene la clave encriptada.
• cookie: Referencia a la cookie que identifica la sesión creada para este
Set Top Box.
• user: Referencia al usuario activo para este Set Top Box
cookie: Representa a una cookie utilizada para identificar una sesión.
158
Capı́tulo 6. Implementación de VXMLS
• string: La cadena de texto que define la cookie.
• id: Referencia al Set Top Box para el que esta cookie define una sesión.
language: Representa un idioma.
• id: Identificador ISO del idioma.
• name: Nombre del idioma.
movie: Representa una pelı́cula.
• id: Átomo utilizado para identificar a la pelı́cula.
• file: Fichero que contiene el vı́deo.
• genre id: Referencia al género de la pelı́cula.
• year: Año en el que fue publicada.
• new: Si es true indica que la pelı́cula es un estreno.
movie info: Contiene datos textuales sobre una determinada pelı́cula que
deben ser traducidos a los diferentes idiomas soportados.
• id: Clave compuesta por una referencia a la pelı́cula y otra al idioma.
• synopsys: Lista de cadenas de texto con un resumen de la pelı́cula.
• title: Tı́tulo de la pelı́cula.
genre: Representa un género.
• id: Clave compuesta por un átomo que identifica al género y una
referencia a un idioma.
• name: Nombre traducido del género.
user: Información sobre un usuario de un determinado Set Top Box.
• id: Clave compuesta por el nombre del usuario y una referencia al Set
Top Box.
• lang id: Referencia al idioma seleccionado por el usuario.
shown movie: Representa la tabla utilizada para almacenar la relación entre
un usuario y las pelı́culas que ha visto.
• id: Clave compuesta por una referencia al usuario y una referencia al
Set Top Box.
• movie id: Referencia a la pelı́cula.
Capa Modelo
159
translated movie: Agrupa información sobre una determinada pelı́cula
traducida a un cierto idioma. No representa a ninguna tabla de la base de
datos.
• id: El átomo identificador de la pelı́cula.
• file: El fichero en el que se encuentra el vı́deo.
• genre id: Referencia al género de la pelı́cula.
• year: Año en el que fue publicada.
• synopsys: Resumen.
• title: Tı́tulo.
6.2.2.
Administración de la base de datos
Como no se ha desarrollado ninguna herramienta de administración, en el
módulo vxmls db se exporta una función init que creará todas las tablas necesarias e introducirá los datos indicados por un fichero de configuración.
El fichero de configuración es un fichero de texto en el que se escriben términos Erlang separados por puntos. El formato utilizado para esta aplicación es el
siguiente:
{NombreDeTabla1, ListaDeTuplas1}.
{NombreDeTabla2, ListaDeTuplas2}.
...
Utilizando la función read del módulo vxmls conf se obtiene una lista con
todas las tuplas que definen el contenido de las diferentes tablas. Tras esto se
almacenarán esas tuplas en las tablas recién creadas.
6.2.3.
Funciones de la fachada
6.2.3.1.
Acceso a datos
Las funciones que permiten el acceso a los datos del modelo están implementadas en el módulo vxmls db. La mayorı́a de ellas utilizarán el API de Mnesia
para manipular los datos persistentes almacenados en la base de datos, pero también se exportan las funciones que permiten el acceso a los datos almacenados
en los objetos valor que devuelven algunas operaciones. De esta forma se aisla la
representación interna de dichos objetos valor del resto de módulos.
Las funciones exportadas se pueden clasificar en cuatro categorı́as:
160
Capı́tulo 6. Implementación de VXMLS
Funciones de acceso a datos: Devuelven datos almacenados en la base de
datos.
Funciones de edición datos: Modifican el valor de un campo de alguna tabla
de la base de datos.
Funciones de inserción de datos: Añaden nuevas tuplas a la base de datos.
Funciones de acceso a los campos de un objeto valor : Devuelven el valor de
un atributo de una variable de alguno de los tipos definidos internamente
para agrupar información relacionada.
Una función que se escapa de esta clasificación es la función set property,
que se comporta de forma diferente según la propiedad a modificar:
user: Cambia el usuario referenciado en un Set Top Box.
language: Cambia el idioma referenciado en un usuario.
shown movie: Añade un nuevo registro a la tabla shown movie en caso de
que dicho registro aún no exista.
6.2.3.2.
Transacciones
El procedimiento general para ejecutar una transacción en Mnesia es el siguiente:
1. Crear una función de nivel superior que ejecute las operaciones que conformen la transacción. Por ejemplo, para obtener el registro correspondiente
al usuario activo en un Set Top Box dado el identificador del Set Top Box :
F = fun () ->
[SetTopBox] = mnesia:read({set_top_box, SetTopBoxId}),
[User] = mnesia:read({user,
{SetTopBoxId,
SetTopBox#set_top_box.user}}),
User
end
2. Ejecutar la función transaction del módulo mnesia que toma como argumento una función de nivel superior y la ejecuta de forma atómica dentro
de una transacción. En caso de que se produzca algún error aborta la transacción y ejecuta un rollback. Esta función puede devolver:
Capa Modelo
161
{atomic, Result}: En caso de éxito Mnesia devuelve en Result el
resultado devuelto por la función ejecutada por transaction.
{aborted, Reason}: En caso de error Mnesia devuelve esta tupla.
Reason representa la razón del error.
Para ejecutar las transacciones sobre Mnesia de acuerdo con la filosofı́a adoptada para el control de errores en VXMLS se define mnesia transaction, que
toma como argumento la función de nivel superior que define la transacción y
comprueba que el resultado es correcto, en caso de que transaction devuelva
{aborted, Reason} el proceso termina con el error {database error, Reason},
si devuelve {atomic, Result} devuelve Result.
Una transacción puede ser abortada explı́citamente ejecutando la función
abort del módulo mnesia a la que se le pasa un valor que será la razón devuelta
por transaction.
6.2.3.3.
Funciones de acceso a datos
Este tipo de funciones buscarán un dato en una tabla siguiendo un determinado criterio. Las búsquedas en las tablas de Mnesia se pueden hacer de varias
formas:
Si se conoce la clave primaria del objeto que se quiere buscar se debe utilizar
la función read del módulo mnesia, que devuelve una lista con los registros
almacenados cuya clave coincida con la clave especificada. Las funciones
implementadas de esta forma son:
• get active user: Recibe como argumento el identificador de un Set
Top Box, obtiene el registro que define dicho Set Top Box y después
busca el registro que define el usuario cuya clave está referenciada
en el campo user del Set Top Box obtenido. Aborta con la razón
user not found si el Set Top Box no tiene un usuario asociado.
• get password: Recibe como argumento el identificador de un Set Top
Box, lo busca y devuelve el campo passwd del mismo.
• get id from cookie: Recibe la cadena que identifica a una cookie,
busca el registro que la representa y devuelve el campo id del mismo.
Si se quieren buscar los registros de una tabla que cumplan un determinado
patrón se usará la función match del módulo Mnesia que devuelve una lista
con los registros que cumplen ese patrón. Este tipo de búsqueda lo realizan
las funciones:
162
Capı́tulo 6. Implementación de VXMLS
• get languages: Devuelve todos los registros almacenados en la tabla
language.
• get genres: Recibe como argumento un identificador de idioma y devuelve todos los registros que definen los géneros en ese idioma (los
registros genre cuyo campo language id coincide con el argumento
de la función).
Si se necesitan los registros de una tabla que cumplan una determinada condición se puede utilizar la función select del módulo Mnesia. Esta función
no fue necesaria para implementar ninguna de las búsquedas utilizadas por
VXMLS.
Para búsquedas más complejas se usarán consultas Mnemosyne (ver la sección 6.1.3), por ejemplo, para realizar uniones entre tablas. Las funciones
implementadas con este método son las siguientes:
• get shown movies: Recibe como argumentos un identificador de idioma, un registro de un determinado usuario y un identificador de género.
Debe devolver una lista de registros de tipo translated movie con información traducida al idioma correcto sobre las pelı́culas del género
especificado que no han sido vistas por el usuario. Para ello debe ejecutar una unión entre las tablas movie, movie info y shown movies
y seleccionar de entre estas que se ajusten al idioma y género especificados. La consulta ejecutada es la siguiente:
Q = query
[{Movie, MovieInfo} || Movie <- table(movie),
MovieInfo <- table(movie_info),
ShownMovie <- table(shown_movie),
Movie.genre_id = GenreId,
MovieInfo.id = {Movie.id, LanguageId},
ShownMovie.id = User#user.id,
ShownMovie.movie_id = Movie.id]
end
Las tuplas obtenidas serán procesadas para transformarlas en registros
translated movie.
• get not shown movies: Recibe los mismos argumentos que la anterior más uno nuevo que indica si se deben seleccionar las novedades
o el resto de pelı́culas. En este caso de deben devolver las pelı́culas que no están referenciadas en shown movies para el usuario especificado. Como la función anterior, devuelve una lista de registros
translated movie.
Capa Modelo
163
• get translated movie: Recibe como argumentos el identificador de
una pelı́cula y un identificador de idioma y devuelve la información en
un registro translated movie con información de la pelı́cula traducida
al idioma especificado, para ello necesita hacer una unión entre las
tablas movie y movie info.
Q = query
[{Movie, MovieInfo} || Movie <- table(movie),
MovieInfo <- table(movie_info),
Movie.id = MovieId,
MovieInfo.id = {MovieId, LanguageId}]
end
6.2.3.4.
Funciones de edición de datos
Para editar un campo de un registro almacenado en una tabla de Mnesia se
debe ejecutar una transacción que siga estos pasos:
1. Obtener el registro con los valores viejos, normalmente utilizando la función
wread del módulo mnesia, que obtiene un registro dada su clave y hacer
que la transacción ejecute un bloqueo de escritura.
2. Crear una variable copiando los datos del registro anteriormente obtenido,
excepto los datos que deben ser modificados.
3. Escribir este nuevo registro en la base de datos utilizando la función write
del módulo mnesia.
Las funciones que realizan este tipo de operación son:
set active user: Modifica el usuario activo para un determinado Set Top
Box.
set cookie: Modifica el campo cookie de un Set Top Box para que señale
a una nueva cookie, que también es almacenada en la base de datos por esta
función.
remove cookie: Modifica el campo cookie de un Set Top Box para indicar
que no está asociado a ninguna cookie. También borra de la base de datos
la cookie anteriormente referenciada por el Set Top Box.
164
Capı́tulo 6. Implementación de VXMLS
6.2.3.5.
Funciones de inserción de datos
La inserción de datos se hace utilizando la función write del módulo mnesia
dentro de una transacción. En caso de que se intente insertar un registro con una
clave que ya esté presente en la base de datos, el nuevo registro suplantará al
antiguo.
Las funciones que insertan datos son todas aquellas que empiezan por new.
La mayorı́a solamente son utilizadas durante la inicialización de la base de datos, los únicos datos que se insertarán durante la ejecución normal de VXMLS
son los registros shown movie correspondientes a las pelı́culas que vayan viendo
los usuarios y los valores de las cookies utilizadas para identificar las diferentes
sesiones.
6.2.3.6.
Funciones de acceso a los campos de un objeto valor
Las siguientes funciones simplemente acceden al campo correspondiente del
registro que se les pasa como argumento y devuelven su valor:
get user name: Devuelve el nombre de un usuario.
get language id: Devuelve el identificador ISO de un idioma.
get language name: Devuelve el nombre de un idioma.
get genre name: Devuelve el nombre de un género.
get genre id: Devuelve el identificador de un género.
get translated movie id: Devuelve el identificador de una pelı́cula traducida.
get translated movie title: Devuelve el tı́tulo de una pelı́cula traducida.
get translated movie file: Devuelve el fichero en el que se encuentra el
vı́deo de una pelı́cula traducida.
get translated movie synopsys: Devuelve el resumen de una pelı́cula traducida.
Capa Vista
6.3.
165
Capa Vista
La vista de la aplicación se implementa en un único módulo vxmls xml, que
exporta las funciones menu2xml, error report y ok message. Estas funciones
permiten obtener los documentos XML que serán devueltos en las respuestas de
VXMLS a las peticiones de los Set Top Boxes clientes.
Durante la generación de un documento XML es necesario realizar varias concatenaciones de cadenas de texto (en Erlang una cadena de texto se representa
como una lista de caracteres). La concatenación de listas es una operación costosa, ya que requiere que las dos listas involucradas sean recorridas completamente.
Para evitar este problema, las cadenas se almacenarán en listas arbitrariamente
profundas y se aplanarán al final de todo. De esta forma la función costosa, en
este caso aplanar, sólo se ejecutará una vez.
Por ejemplo, para concatenar la "hola ", con "mundo" se almacenarán en la
lista ["hola ", "mundo"]. Una vez terminada la generación de las cadenas se
obtendrá un lista profunda como, por ejemplo
["ejemplo:", ["hola ", "mundo"], [[[["."]]]]]
la función flatten del módulo lists aplicada a esta lista devolverá la cadena esperada: "ejemplo:hola mundo.".
Para la generación de los documentos XML, se definen las siguientes funciones
internas al módulo:
render empty tag: Recibe como argumentos el nombre de una etiqueta y,
opcionalmente, una lista con pares argumento valor y devuelve una lista que
representa a dicha etiqueta, con los argumentos especificados en caso de que
los haya. También recibe como argumento una cadena que será añadida al
principio de la lista devuelta. Esto es útil para indentar el resultado de
forma que sea más legible.
> flatten(render_empty_tag("", "etiqueta",
[{"atributo", "valor"}])).
> "<etiqueta atributo=\"valor\"/>\n"
>
> flatten(render_empty_tag("", "etiqueta")).
> "<etiqueta/>\n"
render tag: Es similar a la anterior, pero también recibe un argumento a
mayores con el contenido de la etiqueta.
166
Capı́tulo 6. Implementación de VXMLS
> flatten(render_tag("", "etiqueta",
[{"atributo", "valor"}],
"Contenido")).
> "<etiqueta atributo=\"valor\">Contenido</etiqueta>\n"
>
> flatten(render_tag("", "etiqueta", "Contenido")).
> "<etiqueta>Contenido</etiqueta>\n"
Utilizando estas funciones, un registro menu se transforma en una representación XML utilizando la función render tag para una etiqueta menu cuyo contenido es otro conjunto de etiquetas que describen el menú. Para ejecutar esta
función de forma sencilla se permite al módulo vxmls xml acceder a la representación interna de menú.
render_menu(Menu) ->
render_tag("", "menu",
["\n", render_version(Menu#menu.major_version,
Menu#menu.minor_version),
render_header(Menu#menu.header),
render_timer(Menu#menu.timer),
render_init_actions(Menu#menu.init_actions),
render_options(Menu#menu.options)]).
El resto de funciones render *** se implementan de forma similar.
La función xml2menu obtendrá la representación del menú en XML utilizando
la función anterior y le añadirá la cabecera XML para devolver el documento
completo.
Las funciones error report y ok message se pueden implementar utilizando las funciones render tag y render emtpy tag directamente. Por ejemplo, la
función error report es como sigue:
error_report(Reason) ->
[header("vxmls", ?VXMLS_DTD), %% cabecera XML
render_tag("", "vxmls",
["\n", render_tag(" ", "error", Reason)])].
Capa Controlador
6.4.
Capa Controlador
6.4.1.
Generación de la respuesta HTTP
167
Para la generación de los documentos HTTP a devolver se implementa la función http document que recibe como argumentos el contenido del documento y
una lista con campos de la cabecera y sus correspondientes valores.
Esta función aplanará la lista generada antes de devolverla, por lo que el contenido puede estar representado por una lista arbitrariamente profunda (ver la
sección 6.3).
También añadirá automáticamente el campo Content-length de acuerdo con
el la longitud del contenido.
6.4.2.
Traducción de mensajes
En el módulo vxmls lang se implementa un servidor genérico (comportamiento
gen server ), que almacenará en su estado tablas con pares clave-valor en las que
almacenar los mensajes a mostrar en la interfaz. Cada tabla guardará mensajes
para un determinado idioma.
Para almacenar esta información se utilizan las funciones del módulo ets del
entorno Erlang/OTP. Este módulo permite crear tablas similares a las utilizadas
en una base de datos, pero de carácter no persistente.
Las tablas de los diferentes idiomas se cargan bajo demanda en el momento en
el que se solicita un mensaje de un determinado idioma que no fuera sido cargado
anteriormente. Los mensajes para cada idioma se describen en un fichero de texto
de nombre vxmls lang.xx siendo xx el identificador ISO de idioma. Este fichero
debe contener tuplas separadas por puntos de la forma {Clave, Cadena}. Estos
valores se almacenarán en una tabla ets de nombre xx vxmls lang indexadas por
Clave.
Por ejemplo: suponiendo que el fichero vxmls lang.es contiene una lı́nea
{main_menu_header, "Menú Principal"}.
una solicitud del mensaje a mostrar para main menu header en español devolverá la cadena “Menú Principal”.
168
Capı́tulo 6. Implementación de VXMLS
Para ocultar la existencia del proceso servidor al resto de la aplicación, se
exporta la función get message, a la que se le pasan el identificador del mensaje
y el identificador ISO del idioma en el que se necesita el mensaje, obtiene dicho
mensaje del servidor y lo devuelve.
6.4.3.
Fachada
En el módulo vxmls se definen las funciones que serán llamadas por Inets.
Estas funciones deberán capturar los errores en un bloque catch para evitar la
muerte del proceso utilizado por Inets para evaluar la función (ver la sección
6.1.7). De esta forma, se puede generar un mensaje de error explicativo utilizando la función error report del módulo vxmls xml. El módulo error manager
exporta la función format error a la que se le pasa la razón del error y devuelve
una cadena de texto informando de las causas del mismo.
6.4.4.
Creación y destrucción de sesiones
Las funciones login y logout del módulo vxmls utilizan las funciones con el
mismo nombre del módulo vxmls login para crear y destruir respectivamente las
sesiones de los clientes.
login obtendrá los valores de los argumentos client y passwd pasados en la
petición HTTP y comprobará que son válidos usando la función validate client
del módulo vxmls lib. Esta función encriptará la clave en claro pasada en el argumento passwd y comprobará que la cadena coincide con la cadena encriptada
almacenada en la base de datos (que obtiene utilizando las funciones exportadas
por el modelo). En caso de que sea ası́ se considera que el cliente está autorizado
y se crea una sesión generando una cookie para el usuario y almacenándola en
la base de datos. Después de esto genera una respuesta de confirmación con la
función ok message del módulo vxmls xml y devuelve el documento HTTP correspondiente con los campos necesarios para indicarle al cliente que establezca
la cookie.
Para los usuarios registrados (es decir, para los que se ha creado una sesión),
es posible acceder a sus datos utilizando la función validate cookie del módulo
vxmls lib, que recibe como argumento la variable Env pasada por Inets (ver la
sección 6.1.2), obtiene la cookie de la misma y utiliza las funciones del modelo
para obtener el usuario asociado al Set Top Box para el que se creó la cookie. En
caso de que la cookie no sea válida, esta función provoca el error not logged in
o cookie not valid.
Capa Controlador
169
La función logout comprueba la sesión con validate cookie y elimina la
cookie del modelo. Después de esto genera un mensaje de confirmación como en
el caso anterior y devuelve un documento HTTP con los campos necesarios para
indicarle al cliente que elimine la cookie.
En caso de que se detecte algún error, ambas funciones devuelven un mensaje
de error.
case catch ...
% otros casos ...
{’EXIT’, Reason} ->
http_document(error_report(format_error(Reason))
end.
6.4.5.
Propiedades de personalización
La función set property del módulo vxmls delega su trabajo en la función
con el mismo nombre del módulo vxmls properties. Esta función obtendrá el cliente asociado a la sesión utilizando la función validate cookie, el nombre y valor
de la propiedad de los argumentos pasados en la petición HTTP y utilizará la función set property del modelo para realizar los cambios oportunos en el mismo.
Después de esto devuelve un documento HTTP con un mensaje de confirmación.
En caso de error devuelve un documento HTTP con un mensaje de error.
Las propiedades que pueden ser definidas están determinadas por el modelo, en caso de que sea necesario añadir nuevas propiedades de personalización
este módulo no necesita ser modificado, solamente se deberá ampliar la función
set property del módulo vxmls db (ver la sección 6.2.3.1).
6.4.6.
Generación de menús
La función get menu del módulo vxmls delega en la función con el mismo
nombre exportada por el módulo vxmls menu dispatcher. Este módulo se encargará de generar los documentos que representen el menú que se debe mostrar
para cada petición, dependiendo del estado del modelo.
Para añadir flexibilidad a la definición de la interfaz, el módulo vxmls menu dispatcher no generará los menús solicitados directamente, sino que obtendrá el documento XML de otros módulos que pueden ser cargados dinámicamente. De esta forma se puede variar la definición general de la interfaz sin modificar
170
Capı́tulo 6. Implementación de VXMLS
el código existente, incluso sin tener que detener la aplicación.
Los pasos que se siguen para obtener la definición XML de un determinado
menú son los siguientes:
1. Comprobar que la sesión es válida y obtener el identificador del Set Top
Box cliente con la función validate sesion del módulo vxmls lib.
2. Obtener los datos del usuario activo del Set Top Box utilizando las funciones
del modelo.
3. Obtener el valor de argumento menu en la URL, y una lista con el resto de
pares argumento-valor.
4. Generar el documento XML llamando a la función xml document del módulo asociado al menú solicitado. El nombre del módulo que genera el menú será vxmls nombre, donde nombre es el nombre del menú obtenido en el paso
3.
De esta forma, el módulo vxmls menu dispatcher no depende de los nombres
de los menús definidos ni de los argumentos que estos reciben. En caso de que se
desee añadir un nuevo menú, por ejemplo nuevo menu, se deberá escribir un nuevo
módulo vxmls nuevo menu que exporte una función xml document de acuerdo con
la siguiente especificación:
Recibe cuatro argumentos: ClientId, User, LanguageId y Arguments.
• ClientId: Identificador del Set Top Box cliente asociado a la sesión.
• User: Describe el usuario activo en el Set Top Box, los valores de sus
atributos se pueden obtener con las funciones del modelo (ver la sección
6.2.3.6). En caso del que el Set Top Box no tenga asociado un usuario
activo este valor será false.
• LanguageId: Identificador ISO del idioma en el que se debe mostrar
el menú. El módulo vxmls menu dispatcher pasa el idioma por defecto
en este campo en caso de que el Set Top Box no tenga un usuario
asociado.
• Arguments: Lista de pares nombre-valor con los argumentos con los
que se solicita el menú.
Deberá devolver una lista arbitrariamente profunda que represente el documento XML que define el menú solicitado.
En caso de que se produzca algún error, la función debe causar la finalización
del proceso que la ejecuta.
Capa Controlador
6.4.7.
171
Composición de objetos Menu
El módulo vxmls menu exporta una serie de funciones para crear objetos Menu
sin necesidad de conocer la estructura interna de este tipo.
compose: Recibe como argumento una lista de componentes y devuelve el
objeto Menu correspondiente. Los componentes se puede generar con el
resto de funciones exportadas por este módulo.
timer: Genera un objeto Timer, recibe como argumentos el tiempo la lista
de acciones a ejecutar en caso de que éste expire.
action: Genera un objeto Action. Recibe como argumentos el tipo de la
acción y los datos que sean necesarios para definirla.
option: Genera una opción. Recibe como argumentos el texto a mostrar
para la opción y la lista de acciones a ejecutar en caso de que la opción sea
seleccionada.
default change option actions: Devuelve una lista con las acciones para
cambiar la opción seleccionada.
default bind key actions: Devuelve una lista con las acciones que vinculan las teclas arriba, abajo, SEL y STOP con las acciones definidas por
defecto para la navegación por los menús.
default cancel option: Devuelve una opción “Cancelar” genérica, que en
caso de ser seleccionada provoca la vuelta al menú anterior.
default next menu option: Devuelve una opción genérica para saltar a
otro menú.
no show next menu option: Similar a la anterior, pero oculta el menú al
que se salta.
Por ejemplo, para generar el un menú que muestra las opciones para acceder
la selección de idioma o a la selección de usuario se utiliza el siguiente código (se
han eliminado las referencias a los módulos):
xml_document(_Client, _User, LanguageId, []) ->
Header = get_message(LanguageId, settings_header),
Menu = compose([{header, Header},
{init_actions, default_bind_key_actions()},
172
Capı́tulo 6. Implementación de VXMLS
{options, settings_options(LanguageId)}]),
menu2xml(Menu).
settings_options(LanguageId) ->
ChooseUser = get_message(LanguageId, settings_choose_user),
ChooseLanguage = get_message(LanguageId,
settings_choose_language),
[no_show_next_menu_option(ChooseUser, "choose_user_menu"),
no_show_next_menu_option(ChooseLanguage, "language_menu"),
default_cancel_option(LanguageId)].
6.4.8.
Menús definidos
La interfaz definida se compone de los siguientes menús:
main menu: Menú mostrado como raı́z de la interfaz. Sus opciones permiten
el acceso al menú settings menu y a movies menu.
settings menu: Contiene opciones que permiten acceder a los menús choose user menu y language menu.
choose user menu: Permite elegir entre los distintos usuarios asociados al
Set Top Box.
language menu: Permite elegir el idioma en el que se desea que se muestre
la interfaz.
movies menu: Sus opciones permiten acceder al menú genre menu con los
argumentos necesarios, dependiendo de si se desea obtener una lista con
las novedades, una lista con pelı́culas antiguas o una lista con las pelı́culas
vistas.
genre menu: Muestra opciones para acceder al menú movie list menu con
los argumentos necesarios para mostrar las pelı́culas, nuevas, antiguas o
vistas del género seleccionado.
movie list menu: Muestra opciones para acceder al menú movie menu para
una determinada pelı́cula.
movie menu: Sus opciones permiten iniciar la reproducción del vı́deo al
tiempo que se accede al menú video playing menu (que inicialmente estará oculto), o acceder al menú synopsys menu.
Capa Controlador
173
video playing menu: Muestra opciones para avanzar, retroceder, continuar
o terminar la reproducción de vı́deo.
synopsys menu: No tiene opciones, muestra un resumen de la pelı́cula en
pantalla.
video end menu: Aparece cuando un vı́deo finaliza, sus opciones permiten
iniciar de nuevo la reproducción del vı́deo o regresar al menú movie menu.
6.4.9.
Ejemplos de vxmls get menu
6.4.9.1.
main menu
El menú main menu está definido en el módulo vxmls main menu. En un principio deberá mostrar dos opciones, una para acceder al menú de personalización
y otra para acceder al los menús de selección de pelı́culas, pero en caso de que
el Set Top Box no tenga asociado ningún usuario (es la primera vez que accede
a VXMLS) se debe mostrar el menú de selección de usuario directamente. Este
menú no recibe argumentos, a no ser que se solicite como resultado de una acción
ReloadMenu, en cuyo caso recibe el argumento reloaded con valor true (ver la
sección 5.14.6).
En primer lugar, la función xml document comprueba que el argumento User
no es false, de ser ası́, devuelve el resultado obtenido de la función xml document
del módulo vxmls choose user menu que define el menú de selección de usuario.
Una vez comprobado que el Set Top Box tiene asociado un usuario obtiene
la cabecera a mostrar para el menú solicitando el mensaje main menu header al
servidor vxmls lang (ver la sección 6.4.2) y genera la lista de acciones de inicialización y la lista de opciones (para lo que también deberá solicitar a vxmls lang
las cadenas a mostrar para cada una) y obtiene un objeto Menu utilizando la
función compose menu del módulo vxmls menu, explicado en la sección 6.4.7.
El objeto Menu obtenido se transforma en un documento XML utilizando la
función menu2xml del módulo vxmls xml y se devuelve como resultado.
6.4.9.2.
movie list menu
El módulo vxmls movie list menu se encargará de generar los menús que
permiten al usuario seleccionar una pelı́cula. Este menú recibe los parámetros
filter, que puede tener los valores old, premiere o shown, y genre que tiene
como valor el identificador de un género. La función xml document de este módulo
174
Capı́tulo 6. Implementación de VXMLS
devolverá la descripción de un menú que tendrá como opciones las pelı́culas del
género especificado y que cumplan las condiciones impuestas por el argumento
filter.
old: Pelı́culas no vistas y que no sean novedad.
premiere: Pelı́culas no vistas clasificadas como novedad.
shown: Pelı́culas ya vistas.
La generación de este menú pasa por las siguientes fases:
1. Obtener el valor del género y del filtro de entre los argumentos recibidos.
2. Obtener el texto a mostrar en la cabecera del menú, utilizando el servidor
vxmls lang (ver la sección 6.4.2).
3. Obtener la lista de pelı́culas para el género y filtro especificado utilizando
las funciones necesarias del modelo.
4. Generar una opción para cada pelı́cula con ayuda de las funciones del módulo vxmls menu (ver la sección 6.4.7).
5. Con todo esto, crear el objeto Menu utilizando la función compose menu
del módulo vxmls menu.
6. Generar el documento XML con la función menu2xml del módulo vxmls xml.
6.4.9.3.
synopsys menu
Este menú es ligeramente especial ya que no define ninguna opción y no se
llegará a mostrar, se mantiene siempre oculto.
El objetivo de este menú es mostrar en pantalla un resumen sobre una determinada pelı́cula. Para ello, sus acciones de inicialización no enlazarán las teclas
por defecto, como la mayorı́a del resto de menús, sino que mostrarán el resumen
en pantalla (acción ShowText) y enlazarán las teclas SEL y STOP con las acciones para volver al menú anterior, ocultando el resumen.
Para generar este menú, el módulo vxmls synopsys menu exporta una función
xml document que sigue los siguientes pasos:
1. Obtener el identificador de la pelı́cula del argumento movie.
Extensibilidad
175
2. Obtener el resumen sobre la pelı́cula, traducida al idioma correspondiente,
utilizando las funciones del modelo.
3. Crear el objeto menú con las funciones del módulo vxmls menu.
4. Devolver el documento XML obtenido de transformar el objeto obtenido en
el paso anterior utilizando la función menu2xml del módulo vxmls xml.
6.5.
Extensibilidad
Como se explicó en apartados anteriores, DFBCIn se ha diseñado e implementado de forma que fuera sencillo incorporar nuevas funcionalidades extendiendo la
interfaz Action. VXMLS es el encargado de dirigir el comportamiento de DFBCIn,
por lo que en el momento en que DFBCIn sea capaz de realizar nuevas tareas
se debe modificar VXMLS para que pueda comunicarle a DFBCIn que realice
dichas tareas en algún instante determinado.
Para añadir el soporte para una nueva acción en VXMLS es necesario agregar
código en los módulos vmxls menu y vxmls xml.
En vxmls menu es necesario añadir un nuevo caso a action para que pueda
generar un objeto Action para la nueva acción. Ası́ mismo, puede ser necesario añadir un nuevo registro en la cabecera vxmls menu.hrl para almacenar
la información sobre la acción.
En vxmls xml se debe añadir un nuevo caso en la función render action
para que pueda transformar la nueva acción en un bloque XML.
Normalmente, dadas las caracterı́sticas de Erlang, estas modificaciones sólo
suponen añadir una nueva clausula a las funciones ya existentes. Por ejemplo,
cuando se añadió la acción LoadMp3 sólo fue necesario hacer las siguientes modificaciones:
En la cabecera vxmls menu.hrl, se añade un nuevo registro load mp3 con
el campo path para almacenar la información sobre el fichero o URL que
se debe cargar:
-record(action,
{type,
data = false
}).
% Tipo de acción
% Información sobre la misma
176
Capı́tulo 6. Implementación de VXMLS
%%% Registros para almacentar información en el campo
%%% data de action
-record(change_option,
{value,
mode
}).
% An integer
% (?RELATIVE | ?ABSOLUTE)
% Continúa con la definición de otros registros
% para las diferentes acciones
% ...
%%% Nuevo registro para la acción recién a~
nadida
-record(load_mp3,
{path
% The file path (or URI)
}).
En el fichero vxmls menu.erl se debe añadir un nuevo caso a la función
action para que pueda crear acciones de este tipo:
action(show_text, Lines) when list(Lines) ->
Data = #show_text{lines = Lines},
#action{type=show_text, data = Data};
% Constructores para otras acciones
% ...
%%% Nuevo constructor para una acción LoadMp3
action(load_mp3, Path) when list(Path) ->
Data = #load_mp3{path = Path},
#action{type = load_mp3, data = Data}.
En el fichero vxmls xml.erl se añade un nuevo caso para la nueva acción en
la función render action para que pueda generar el código XML para esta
nueva acción:
render_action(Indent, unload_video, _Data) ->
render_empty_tag(Indent, "unloadVideo");
% Otras funciones para renderizar las distintas acciones
Extensibilidad
177
% ...
%%% Nuevo caso para renderizar la función recién a~
nadida
render_action(Indent, load_mp3, Data) ->
render_empty_tag(Indent, "loadMp3",
[{"path", Data#load_mp3.path}]);
% ...
Como se vio en este mismo capı́tulo en la sección 6.4.6, la implementación de
VXMLS posibilita modificar la interfaz definida sin afectar a los módulos existentes, salvo aquellos módulos que son los encargados de devolver los documentos
XML correspondientes a cada menú particular. Por lo tanto, redefinir la interfaz
mostrada para adaptarla a las posibilidades que ofrecen las nuevas acciones sólo
afecta al código de los módulos que definen cada menú.
Capı́tulo 7
Validación
Índice General
7.1. Despliegue del sistema . . . . . . . . . . . . . . . . . . 179
7.2. Tamaño del software para el Set Top Box . . . . . . . 180
7.3. DFBCIn . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
7.3.1. Problemas de rendimiento . . . . . . . . . . . . . . . . 181
7.3.2. Problemas con avifile . . . . . . . . . . . . . . . . . 182
7.4. VXMLS . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
7.4.1. Comportamiento general . . . . . . . . . . . . . . . . . 184
7.4.2. Problemas con Inets . . . . . . . . . . . . . . . . . . . 184
7.5. Streaming . . . . . . . . . . . . . . . . . . . . . . . . . . 184
7.1.
Despliegue del sistema
El sistema utilizado para la validación de las pruebas se compone de los siguientes subsistemas:
DFBCIn se ejecutará en el Set Top Box explicado en la sección 2.2.4.3.
VXMLS se ejecutará en un PC conectado por red al Set Top Box.
VoDKA correrá sobre un cluster de máquinas.
179
180
Capı́tulo 7. Validación
PC ejecutando VXMLS
Cluster servidor de video
Red de interconexion
DFBCIn funcionando en el Set Top Box
Figura 7.1: Despliegue del sistema final
7.2.
Tamaño del software para el Set Top Box
En el Set Top Box utilizado para las pruebas se instaló un disco duro para
facilitar el desarrollo, pero el sistema final deberı́a caber en una memoria flash.
El tamaño de los diferentes componentes de software que se deben instalar es:
Aplicación DFBCIn: Al ser una aplicación escrita C en y enlazada dinámicamente con las bibliotecas utilizadas, el espacio ocupado se reduce a 548
kilobytes, entre los que se cuentan las imágenes y fuentes utilizadas para
dibujar la interfaz y la biblioteca dinámica utilizada para implementar el
módulo gráfico.
DirectFB : A pesar de estar enlazada estáticamente, DirectFB es una
librerı́a muy ligera en cuanto a espacio ocupado, sólo ocupa 4,4 megabytes.
Avifile: La versión utilizada de avifile necesita una instalación de 13 megabytes.
Expat: La biblioteca expat solamente necesita la instalación de 804 kilobytes.
Instalación de Linux : Los más de 40 mega bytes restantes (asumiendo una
memoria Flash de 64 megabytes) son más que suficientes para una instalación de Linux sin X Windows.
El sistema completo puede ser almacenado en una memoria Flash de 64 mega
bytes. Nótese que en caso de que se hubiera utilizado el sistema X Windows esto
DFBCIn
181
serı́a imposible, ya que una instalación tı́pica de dicho sistema ocupa más de esos
64 mega bytes.
7.3.
DFBCIn
La aplicación DFBCIn se ejecutará sobre un Set Top Box conectado a un
televisor, utilizando un mando a distancia como sistema de entrada.
Figura 7.2: DFBCIn funcionando en el Set Top Box
Se observó el correcto funcionamiento del mando a distancia y de la salida de
televisión del Set Top Box.
7.3.1.
Problemas de rendimiento
La tarjeta gráfica incorporada por el Set Top Box utilizado, una SiS 650, no
está soportada por la versión de DirectFB utilizada, esto quiere decir que todas
las operaciones serán realizadas sin ayuda de la aceleración hardware proporcionada por la tarjeta.
Debido a esto, la reproducción de vı́deo que se consiguió en un principio era
demasiado pobre, ya que la CPU no era suficientemente rápida para decodificar
y dibujar todos los frames en tiempo real.
Para aligerar la carga durante, la primera medida tomada fue la de disminuir
la resolución de salida, que originalmente era 800x600, a 640x480, con una profundidad de color de 16 bits.
Con esta medida se consiguió una pequeña mejora, pero el vı́deo seguı́a acusando saltos evidentes. Lo siguiente que se intentó fue adaptar el tamaño del
182
Capı́tulo 7. Validación
vı́deo para evitar tener que realizar escalados a la hora de copiar los frames en la
superficie primaria. Se comprobó que con un vı́deo codificado con un ancho de 640
pixels, de forma que la copia de los frames a la superficie primaria simplemente
consiste en una copia byte a byte, los resultados obtenidos fueron casi aceptables.
La solución definitiva fue codificar los vı́deos con un ancho de 320 pixels. La
copia de los frames a la superficie primaria supone una operación de escalado,
pero escalar al doble de tamaño es relativamente sencillo (sólo se deben duplicar los pixels), y el ahorro computacional que supone trabajar con imágenes que
ocupan la cuarta parte que en el caso anterior es suficiente para que el flujo de
frames sea suave. La perdida de resolución se hace evidente en un monitor, pero
las pruebas en la televisión resultaron bastante aceptables.
Aún ası́, operaciones tales como reproducir vı́deo con un menú dibujado por
encima ralentizan el sistema de forma que el vı́deo vuelve a saltar de forma
desagradable. Para evitar esto de diseñó la interfaz definida por VXMLS de forma
que siempre se detenga el vı́deo antes de dibujar un menú por encima. El menú se
dibujará sobre un frame estático, por lo que no habrá problemas de ralentización.
7.3.2.
Problemas con avifile
La implementación de IDirectFBVideoProvider para ficheros AVI distribuida con DirectFB utiliza la versión 0.6 de la biblioteca avifile. Durante las
pruebas se detectaron varios problemas:
1. Al final de la reproducción, el vı́deo vuelve a empezar, esto imposibilita
por completo la detección del final del vı́deo tal y como se explicó en la
sección 5.12.7.1. El problema se encontró en las fuentes de avifile, que
explı́citamente volvı́an al principio del vı́deo cuando se alcanzaba el final
(presumiblemente debido a un cambio hecho para alguna prueba por los
programadores de DirectFB).
2. Utilizando el proveedor de avifile, DFBCIn se colgaba al ejecutar una
acción VideoStop durante la reproducción de vı́deo. El problema se encontró en un interbloqueo entre Interface (ver la sección 5.10.2) y la reproducción de vı́deo controlada por avifile. Cuando Interface llama a
videoStop para detener la reproducción debe esperar a que esta función
termine, manteniendo su estado bloqueado, avifile intenta terminar de
decodificar un frame antes de detener la reproducción, cuando tiene el
frame disponible ejecuta la función videoCallback que fue instalada en
videoLoad y después deberı́a detener la reproducción. El problema surge porque videoCallback debe ejecutar la función notifyNewFrame para
VXMLS
183
avisar a Interface de que hay un nuevo frame disponible, y esta función
necesita acceder al estado de Interface para modificar sus banderas, por lo
que debe esperar a que Interface libere el semáforo que esta tiene bloqueado
mientras trata de terminar la ejecución de videoStop.
3. Si se produce un fallo durante la reproducción de vı́deo (no deberı́a producirse ningún fallo, pero recuérdese que para terminar la aplicación durante
las pruebas se provoca un fallo de forma intencionada), DFBCIn termina de
forma incontrolada, a veces provocando fallos de segmento y otras quedando
totalmente bloqueada. Este fallo se debe a que la implementación del proveedor de vı́deo para avifile no es completa y no sincroniza la destrucción
del reproductor de vı́deo con la liberación de las superficies utilizadas. Se
puede dar el caso de que se intente escribir un nuevo frame en una superficie
que ya no tiene memoria reservada.
4. La versión 0.6 se avifile utiliza los codecs de Windows en vez de utilizar
una biblioteca nativa de Linux, por lo que se produce una sobrecarga debida
a las emulaciones necesarias.
5. Las funciones GetPos y SeekTo no siempre funcionan correctamente.
El problema 1 se solucionó eliminando esa “caracterı́stica” en las fuentes de
avifile.
El problema 2 se solucionó desplazando la llamada a la función de Interface
interfaceNotifyNewFrame a un nuevo hilo de ejecución. Durante la inicialización de Video (ver la sección 5.12.7) se crea un nuevo hilo interfaceNotifier
que será despertado por la función instalada durante videoLoad como callback en
el proveedor. Una vez despertado este hilo llamará a interfaceNotifyNewFrame
(deberá esperar en algunos casos a que Interface libere su estado, pero ahora no
bloquea la ejecución) y volverá a dormirse (ver la sección 5.12.7).
Para solucionar el problema 2 se reescribió el código del proveedor de vı́deo
proporcionado por DirectFB para adaptarlo a la versión 0-7.34 de avifile,
que ya permite la utilización de otros codecs, como los de la biblioteca de Linux
ffmpeg. En el apéndice C se puede ver la interfaz a implementar para un proveedor de vı́deo. El nuevo proveedor de vı́deo implementado ya no tiene el problema 1.
Los problemas 3 y 5 están todavı́a sin solucionar. El problema de la liberación
de memoria se debe solucionar mejorando la implementación del proveedor de
vı́deo y el segundo problema parece que es debido a un fallo en avifile.
184
Capı́tulo 7. Validación
7.4.
VXMLS
7.4.1.
Comportamiento general
VXMLS se ejecuta sobre un PC convencional, para comprobar que el correcto
funcionamiento durante la ejecución de las pruebas se utilizaron diversas herramientas suministradas con la plataforma Erlang/OTP, como la aplicación tv que
permite ver los contenidos de las tablas de Mnesia y de las tablas ets y la aplicación pman que permite monitorizar los procesos en ejecución dentro de un nodo
Erlang.
No se detectaron problemas en el funcionamiento de VXMLS, la interfaz definida funciona correctamente y los datos almacenados en la base de datos influyen
en la misma como es esperado.
7.4.2.
Problemas con Inets
Durante las pruebas de sistema se comprobó que Inets no cerraba la conexión
inmediatamente a pesar de enviar el campo Connection: close [35] en la cabecera, sino que tardaba una pequeña fracción de tiempo desde que terminaba de
enviar la respuesta hasta que cerraba la conexión. Esto provocaba que la aplicación DFBCIn quedara bloqueada a la espera de que se cerrara la conexión,
lo que derivaba en una molesta latencia entre el momento en el que el usuario
pulsaba una tecla a la que estaba asociada alguna acción que necesitara realizar
una petición a VXMLS para completar su ejecución y el momento en el que los
resultados de la ejecución de dicha acción se hacı́an visibles.
Para solucionar este problema, se implementó VXMLS de forma que añadiera
el campo Content-Length y se hizo que httpGet diera prioridad a la información
de dicho campo sobre el hecho de saber que la conexión va a ser cerrada (ver la
sección 5.11.1).
7.5.
Streaming
Tras las pruebas ejecutadas utilizando ficheros locales para comprobar el correcto funcionamiento de DFBCIn en cuanto a reproducción de vı́deo, se realizaron las pruebas sobre medios servidos por VoDKA.
Para ello se utilizó el sistema de ficheros virtual desarrollado en [37], que
permite el acceso a medios distribuidos por streaming sin modificar la implementación del reproductor de vı́deo, introduciendo una serie de capas que ocultan el
Streaming
185
acceso al medio remoto como si de un fichero local se tratara. En la figura 7.3 se
muestra un diagrama con las capas introducidas por este proxy.
DFBCIn
Proveedor de Video
Proxy
Sistema Virtual de Ficheros
Sistema de Buffers
Protocolo de Streaming
Servidor de Video
Red
Figura 7.3: Capas de acceso al servicio de streaming
La reproducción de vı́deo utilizando esta técnica supone una carga computacional añadida para el Set Top Box lo que en ocasiones puede disminuir la calidad
del vı́deo reproducido, dependiendo de la capacidad del Set Top Box.
Figura 7.4: Cluster servidor de vı́deo utilizado
Capı́tulo 8
Conclusiones y lı́neas futuras
Índice General
8.1. Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . 187
8.2. Continuación de este proyecto . . . . . . . . . . . . . . 189
8.2.1. DFBCIn . . . . . . . . . . . . . . . . . . . . . . . . . . 189
8.2.2. VXMLS . . . . . . . . . . . . . . . . . . . . . . . . . . 190
8.1.
Conclusiones
En este proyecto se ha desarrollado un sistema cliente-servidor para permitir
el acceso controlado a los medios de un servidor de streaming. Para ello se han
utilizado los lenguajes de programación C, C++ y Erlang, ası́ como diferentes recursos existentes para dichos lenguajes. Para la comunicación entre la aplicación
cliente y el servidor se han utilizado los estándares HTTP y XML.
La aplicación cliente fue instalada con éxito en un Set Top Box basado en
Linux, de forma que el usuario puede utilizar un mando a distancia para interactuar con la aplicación al mismo tiempo que ve los resultados en un televisor.
Algunos de los recursos utilizados, como DirectFB, aún están en fase de
desarrollo y presentan ciertas complicaciones y limitaciones, pero en general su
funcionamiento es bueno.
187
188
Capı́tulo 8. Conclusiones y lı́neas futuras
El diseño y la implementación de ambas aplicaciones se ha hecho de forma
que puedan ser extendidas fácilmente para aumentar la funcionalidad que ofrecen.
Los principales puntos a destacar sobre este proyecto son los siguientes:
El lenguaje C es necesario para desarrollar aplicaciones para entornos con
recursos limitados como los Set Top Boxes, pero su uso presenta muchas
complicaciones:
• El control de memoria debe ser explı́cito, esto complica la creación y
de “objetos” que deben ser compartidos por varios módulos. La gestión
de memoria es la mayor fuente de errores cuando se programa en C.
• Es complicado plasmar un diseño hecho en UML en un programa en
C, ya que no es orientado a objetos.
• La programación multi-hilo hace el código confuso y propenso a errores, ya que los distintos hilos se ejecutan sobre el mismo espacio de
memoria de datos. Es necesario añadir explı́citamente el control de
concurrencia, lo que facilita la aparición de errores de tipo interbloqueo y race conditions que no ocurren de forma determinista y son
muy difı́ciles de localizar.
• C no proporciona ningún mecanismo de control de errores, debe codificarse de forma explı́cita.
Erlang es un lenguaje funcional que permite crear aplicaciones que se ejecutan sobre un entorno que se encarga de la gestión de memoria. Las ventajas
observadas tras la programación de VXMLS fueron:
• No es necesario programar el control del memoria.
• Los lenguajes funcionales minimizan los efectos colaterales, lo que disminuye el número de errores introducidos en el código y simplifica la
depuración.
• Erlang permite aislar el control de errores en un proceso que sólo se
dedique a eso. Esto aumenta la claridad del código ya el código de
control de errores no está mezclado con el código que proporciona la
funcionalidad de la aplicación.
• Cada proceso concurrente en Erlang tiene su propio espacio de memoria y no puede afectar al resto de procesos. La única forma que tienen
los procesos para comunicarse entre sı́ es el paso de mensajes. Esto
elimina por completo los problemas de tipo race condition y facilita en
gran manera la detección de interbloqueos.
Continuación de este proyecto
189
• Todo esto hace que el desarrollo de una aplicación en Erlang sea mucho
más rápido que el desarrollo de una aplicación en C.
• En su contra, las aplicaciones consumen más recursos y son más lentas
que las desarrolladas en lenguajes de más bajo nivel.
DirectFB Proporciona una interfaz interesante para acceder al sistema
framebuffer de Linux. Las pruebas realizadas demostraron que es un framework escalable y suficientemente estable, a pesar de estar aún en fase de
desarrollo. El sistema de interfaces y proveedores que utiliza para acceder a
diferentes medios como fuentes, imágenes o vı́deos y a las posibilidades de
aceleración gráfica de las diferentes tarjetas gráficas permite la ampliación
de los formatos y plataformas soportadas de forma sencilla. Durante el desarrollo de este proyecto se pudieron comprobar las caracterı́sticas de este
framework :
• Permite desacoplar la aplicación del hardware subyacente, salvo por las
diferencias de eficiencia entre las plataformas con aceleración hardware
soportada y las que deben ser emuladas por software.
• Aunque es suficientemente robusto, en ocasiones puede dejar el sistema
inestable, sobre todo por culpa de interfaces que no están completamente desarrolladas, como por ejemplo el proveedor de vı́deo utilizado
para ficheros AVI.
• Está implementado para soportar aplicaciones multi-hilo.
• El hecho de utilizar DirectFB para controlar el apartado gráfico de
la aplicación condiciona la forma en la que se va a hacer el control de la
entrada al sistema, pero como se vio en la implementación de DFBCIn,
el código dependiente de DirectFB se puede aislar del resto de la
aplicación fácilmente.
8.2.
Continuación de este proyecto
8.2.1.
DFBCIn
El principal problema de la implementación actual es que la reproducción de
vı́deo se está haciendo sin ayuda de la aceleración hardware proporcionada por
la tarjeta gráfica. Serı́a interesante implementar el soporte de dicha tarjeta para
DirectFB, de esta forma se podrı́an aprovechar mejor las capacidades gráficas
del Set Top Box.
190
Capı́tulo 8. Conclusiones y lı́neas futuras
El proveedor de vı́deo utilizado para reproducir ficheros AVI fue modificado
para solucionar algunos problemas que se detectaron (ver la sección 7.3.2), pero
sigue presentando algunas deficiencias:
Su destrucción no es segura y puede dejar bloqueado el sistema en caso de
que se produzca algún error durante la reproducción de vı́deo.
Las funciones de desplazamiento y la que devuelve la posición actual no
siempre funcionan correctamente.
Para solucionar estos problemas se deberá mejorar la implementación del proveedor de vı́deo proporcionado (usa la librerı́a avifile) o desarrollar uno nuevo
para otro reproductor como Xine o MPlayer1 .
También se debe mejorar el aspecto gráfico de la aplicación permitiendo imágenes en lugar de texto para identificar las opciones, fondo de pantalla configurable,
mosaicos de vı́deos, diferentes aspectos visuales (temas) ...
Se podrán añadir nuevas acciones que permitan, por ejemplo, reposicionar
la reproducción de fichero mp3 que se está reproduciendo, envı́o de mensajes a
otros usuarios, calificación y clasificación de pelı́culas, creación de los perfiles de
usuario (por ahora deben ser creados directamente en la base de datos), etc.
El control de errores debe ser mejorado. Por ahora, cualquier error grave provoca la finalización de la aplicación con un mensaje de error, en el sistema final
será necesario un sistema de recuperación de errores.
Para completar el middleware del Set Top Box será necesario implementar un
API para permitir la ejecución de aplicaciones multimedia externas.
8.2.2.
VXMLS
El servidor VXMLS implementado no es mas que un prototipo para demostrar
el funcionamiento de DFBCIn. Un servidor más completo podrı́a incorporar las
siguientes mejoras:
Incorporar una herramienta de administración que permita añadir información a la base de datos.
Almacenar mucha más información sobre los usuarios y las pelı́culas.
1
MPlayer puede no ser una buena elección, ya que no se amolda a la programación multi-hilo
de DirectFB debido a su uso intensivo de variables globales
Continuación de este proyecto
191
Definir una interfaz mucho más flexible, que tenga en cuenta parámetros
como el perfil del usuario, el dı́a y la hora, ...
Permitir más propiedades de personalización como por ejemplo: imagen de
fondo por defecto, música preferida para los menús, última pelı́cula vista y
hasta qué posición, ...
Recolectar más información sobre los usuarios a medida que interactúan
con la interfaz: cuáles son las secciones más visitadas, qué tipo de pelı́culas
son las más vistas, ...
Proporcionar acceso a otro tipo de servicios como telebanca, información, tcomercio, etc. Para acceder a este tipo de servicios no es necesario modificar
DFBCIn, se puede utilizar la acción SetProperty para obtener los comandos
seleccionados por el usuario.
Apéndice A
DTD utilizados
A.1.
DTD utilizado para la definición de menús
<!ELEMENT menu (header?, version, image?, timer?, initActions?, option*)>
<!ELEMENT header (#PCDATA)>
<!ELEMENT version (#PCDATA)>
<!ELEMENT image EMPTY>
<!ATTLIST image
file CDATA #REQUIRED
cache (yes | no) #IMPLIED>
<!ELEMENT timer (action+)>
<!ATTLIST timer seconds NMTOKEN #REQUIRED>
<!ELEMENT initActions (action+)>
<!ELEMENT option (text, action+)>
<!ELEMENT text (#PCDATA)>
<!ELEMENT
|
|
|
|
|
|
|
|
|
action (changeOption
nextMenu
previousMenu
hideMenu
showMenu
playVideo
stopVideo
resumeVideo
unloadVideo
seekVideo
193
194
Apéndice A. DTD utilizados
|
|
|
|
|
|
setProperty
bindKey
reloadMenu
loadMp3
pauseContinueMp3
killMp3Player)>
<!ELEMENT changeOption EMPTY>
<!ATTLIST changeOption
value NMTOKEN #REQUIRED
mode (relative | absolute) #IMPLIED>
<!ELEMENT nextMenu (menuArgument)*>
<!ATTLIST nextMenu
name CDATA #REQUIRED>
<!ELEMENT menuArgument EMPTY>
<!ATTLIST menuArgument
name NMTOKEN #REQUIRED
value NMTOKEN #REQUIRED>
<!ELEMENT hideMenu EMPTY>
<!ELEMENT showMenu EMPTY>
<!ELEMENT previousMenu EMPTY>
<!ATTLIST previousMenu
depth NMTOKEN #IMPLIED
reinit (yes | no) #IMPLIED>
<!ELEMENT playVideo (action+)>
<!ATTLIST playVideo
file CDATA #REQUIRED>
<!ELEMENT stopVideo EMPTY>
<!ELEMENT resumeVideo EMPTY>
<!ELEMENT unloadVideo EMPTY>
<!ELEMENT seekVideo EMPTY>
<!ATTLIST seekVideo
mode (forward | backward | absolute) #REQUIRED
time NMTOKEN #REQUIRED>
<!ELEMENT bindKey (keyActions | keyEvent)>
<!ATTLIST bindKey key ( PROGRAM | POWER | MENU |TITLE | OSD | LANGUAGE |
ANGLE | SUBTITLE | VOLUP | VOLDOWN | MUTE | LR | 0 |
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | VGATV |ENT | UP |
DOWN | LEFT | RIGHT | SEL | PLAY | STOP | SLOW |
EJECT | REWIND | FORWARD | NEXT | PREVIOUS) #REQUIRED>
DTD utilizado para la especificación de las respuestas de VXMLS
195
<!ELEMENT keyEvent EMPTY>
<!ATTLIST keyEvent type (EXECUTE | NULL) #REQUIRED>
<!ELEMENT keyActions (action+)>
<!ELEMENT setProperty EMPTY>
<!ATTLIST setProperty
name (language) #REQUIRED
value NMTOKEN #REQUIRED>
<!ELEMENT reloadMenu EMPTY>
<!ELEMENT loadMp3 EMPTY>
<!ATTLIST loadMp3 path CDATA #REQUIRED>
<!ELEMENT pauseContinueMp3 EMPTY>
<!ELEMENT killMp3Player EMPTY>
A.2.
DTD utilizado para la especificación de las
respuestas de VXMLS
<!ELEMENT vxmls (ok | error)>
<!ELEMENT ok EMPTY>
<!ELEMENT error (#PCDATA)>
Apéndice B
API del módulo gráfico de
DFBCIn
/* Initilization functions */
void graphicsInit(int argc, char **argv);
void graphicsDeinit(void);
/* Menu drawing functions */
void graphicsLoadMenu(Menu menu);
void graphicsUnloadMenu(void);
void graphicsSetBox(short showBox);
/* Video functions */
void graphicsLoadVideo(const char *videoFile);
void graphicsPlayVideo(void);
void graphicsStopVideo(void);
void graphicsResumeVideo(void);
double graphicsGetVideoPosition(void);
void graphicsGotoVideoPosition(double videoPosition);
void graphicsBlitVideo(void);
void graphicsUnloadVideo(void);
void graphicsSetNewFrameCallback(void (*callback)(void));
void graphicsSetVideoEndCallback(void (*callback)(void));
/* Other graphic functions */
void graphicsLoadText(Array lines);
void graphicsUnloadText(void);
197
198
Apéndice B. API del módulo gráfico de DFBCIn
void graphicsFlipScreen(short showBackground);
/* Input control */
InputKey graphicsTestInput(void);
Apéndice C
Definición de
IDirectFBVideoProvider
DEFINE_INTERFACE(
IDirectFBVideoProvider,
/** Retrieving information **/
/*
* Retrieve information about the video provider’s
* capabilities.
*/
DFBResult (*GetCapabilities) (
IDirectFBVideoProvider
*thiz,
DFBVideoProviderCapabilities *caps
);
/*
* Get a surface description that best matches the video
* contained in the file.
*/
DFBResult (*GetSurfaceDescription) (
IDirectFBVideoProvider
*thiz,
DFBSurfaceDescription
*dsc
);
/** Playback **/
199
200
Apéndice C. Definición de IDirectFBVideoProvider
/*
* Play the video rendering it into the specified rectangle
* of the destination surface.
*
* Optionally a callback can be registered that is called
* for each rendered frame. This is especially important if
* you are playing to a flipping surface. In this case, you
* should flip the destination surface in your callback.
*/
DFBResult (*PlayTo) (
IDirectFBVideoProvider
*thiz,
IDirectFBSurface
*destination,
const DFBRectangle
*destination_rect,
DVFrameCallback
callback,
void
*ctx
);
/*
* Stop rendering into the destination surface.
*/
DFBResult (*Stop) (
IDirectFBVideoProvider
*thiz
);
/** Media Control **/
/*
* Seeks to a position within the stream.
*/
DFBResult (*SeekTo) (
IDirectFBVideoProvider
*thiz,
double
seconds
);
/*
* Gets current position within the stream.
*/
DFBResult (*GetPos) (
IDirectFBVideoProvider
*thiz,
201
double
*seconds
);
/*
* Gets the length of the stream.
*/
DFBResult (*GetLength) (
IDirectFBVideoProvider
*thiz,
double
*seconds
);
/** Color Adjustment **/
/*
* Gets the current video color settings.
*/
DFBResult (*GetColorAdjustment) (
IDirectFBVideoProvider
*thiz,
DFBColorAdjustment
*adj
);
/*
* Adjusts the video colors.
*
* This function only has an effect if the video provider
* supports this operation. Check the providers capabilities
* to find out if this is the case.
*/
DFBResult (*SetColorAdjustment) (
IDirectFBVideoProvider
*thiz,
DFBColorAdjustment
*adj
);
Apéndice D
Tipos de datos en Erlang
Tipos de datos constantes: Son tipos de datos que no pueden ser divididos
en tipos más primitivos.
• Números: por ejemplo 123, -789, 3.14159, 7.8e12, -1.2e-45. Se subdividen en enteros y flotantes.
• Átomos: por ejemplo abc, ’Un átomo con espacios’, lunes, verde,
hola mundo. Son simplemente constantes con nombre.
• Pids: Identificadores de proceso.
Tipos de datos compuestos: Se utilizan para agrupar otros tipos de datos:
• Tuplas: Por ejemplo {a, 12, b}, {}, {1, 2, 3}. Se utilizan par almacenar un número fijo de elementos. Los elementos no tiene porque
ser del mismo tipo.
• Listas: Por ejemplo [], [a, b, 12], [22], [a, ’hola amigo’]. Se
utilizan para almacenar un número variable de elementos. Al igual
que para las tupas, los elementos no tiene que ser del mismo tipo.
A partir de la versión 4.4 se introdujeron dos nuevos tipos:
Registros: Son similares a las tupas, pero con campos con nombre, internamente se representan como tuplas cuyo primer elemento es el nombre del
registro y los siguientes elementos contienen los valores de los campos del
registro.
Funciones de orden superior : El tipo fun representa funciones que pueden
ser pasadas como argumentos a otras funciones y que pueden ser devueltas
como resultado de evaluar una función.
203
204
Apéndice D. Tipos de datos en Erlang
Estos tipos se almacenan en variables. Los nombres de las variables empiezan con mayúsculas. Una variable puede obtener su valor una sola vez, una vez
asignado su valor éste no puede cambiar.
Apéndice E
Instalación y ejecución
E.1.
Estructura de la distribución
En el directorio Interface se encuentran los ficheros necesarios para compilar
e instalar DFBCIn.
En el directorio Server se encuentran los ficheros necesarios para compilar e
instalar VXMLS.
En el directorio libs/DirectFB se encuentra la versión de DirectFB utilizada para el desarrollo de DFBCIn.
En el directorio libs/Avifile se encuentran las dos versiones de avifile
utilizadas en este proyecto.
En el directorio libs/Expat se encuentra la versión de expat utilizada para
este proyecto.
En el directorio doc se encuentra la memoria del proyecto en formato PDF y
las fuentes en LATEXpara generarla.
E.2.
Instalación de VXMLS
E.2.1.
Compilación
En el fichero vxmls.mk se definen ciertas variables que pueden ser modificadas
antes de compilar la aplicación:
205
206
Apéndice E. Instalación y ejecución
PORT : Puerto en el que se va a instalar el servidor.
MNESIA DB DIR: Directorio en el que Mnesia va a crear los ficheros necesarios para mantener la base de datos.
EXTRA ARGS : Argumentos que se pasarán a VXMLS en el script de
arranque. Normalmente no será necesario añadir ninguno.
MAX CLIENTS : Numero máximo de conexiones que aceptará Inets.
ADMIN : Dirección de correo del administrador. Este dato es utilizado por
Inets para generar las páginas HTML que devuelve en caso de error, por lo
que para este proyecto no es relevante.
ERL BIN : Directorio que contiene el compilador y el emulador de Erlang
EPMD BIN : Directorio en el que se encuentra el demonio mapeador de
puertos de Erlang.
NODENAME : Nombre del nodo Erlang en el que se ejecutará el servidor.
MP3 MAIN FILE : Nombre del fichero (o URL) con la música que se reproducirá durante la exposición de menús.
También se definen otras variables que normalmente no deberı́an ser modificadas, excepto EFLAGS de la que puede interesar eliminar la bandera
+debug info para evitar que el compilador introduzca información de depuración en los ficheros compilados.
Tras la edición de este fichero, el comando make deberı́a compilar todos los
ficheros fuente, generar el fichero de configuración utilizado por la aplicación Inets
inets/conf/vxmls.conf y los scrpits vxmls y vxmls initdb en el directorio bin.
E.2.2.
Instalación
Antes de ejecutar VXMLS por primera vez es necesario crear la base de datos, para ello se debe ejecutar el script bin/vxmls initdb, que creará un nuevo
esquema para Mnesia e introducirá los datos indicados en el fichero de configuración vxmls.conf
El script bin/vxmls permite arrancar y detener la ejecución del servidor.
bin/vxmls start: Arranca el servidor en segundo plano
Instalación de DFBCIn
207
bin/vxmls debug: Arranca el servidor en primer plano, para facilitar las
labores de depuración (para terminar la ejecución se debe evaluar la función
vxmls:stop()).
bin/vxmls stop: Detiene la ejecución del servidor.
E.3.
Instalación de DFBCIn
E.3.1.
Requisitos previos
Para el correcto funcionamiento de DFBCIn es necesario realizar ciertas modificaciones en las fuentes de DirectFB y de avifile. En el directorio patches
están los parches necesarios para hacer esas modificaciones:
avifile-patch: Corrige ciertos problemas de en las fuentes de la versión
de avifile distribuida con DirectFB.
avifile-provider-patch: Adapta el proveedor de vı́deo para ficheros AVI
distribuido en el paquete DirectFB-extra en su versión 0.9.16 para que
pueda ser compilado con la versión 0.7.34 de avifile.
avifile0.7-0.7.34-patch: Corrige un error en un fichero de la versión
0.7.34 de avifile.
El primer parche es necesario en caso de que se compile DirectFB-extra con
la versión de avifile distribuida con DirectFB, los otros dos deberán ser aplicados en caso de que se utilice la versión 0.7.34 de avifile.
Además de DirectFB y avifile, también es necesaria la biblioteca expat.
Una vez ejecutadas estas modificaciones se deben compilar e instalar las bibliotecas. Es necesario que DirectFB se compile con soporte para imágenes
PNG y frecuentes True Type. El soporte para SDL es opcional, pero será necesario si se desea ejecutar DFBCIn sobre las X Windows sin necesidad de un
kernel con soporte para el dispositivo framebuffer. El paquete DirectFB-extra
se deberá compilar después de haber instalado DirectFB y avifile.
E.3.2.
Compilación
En el fichero dfbcin.mk se definen ciertas variables que pueden ser modificadas
antes de compilar la aplicación:
208
Apéndice E. Instalación y ejecución
DEFAULT GRAPHLIB : Indica con qué biblioteca gráfica se va a enlazar
DFBCIn
• dfblib: Se enlaza con la biblioteca que utiliza DirectFB.
• charlib: Se enlaza con la biblioteca basada en la consola de texto.
VXMLS PORT : Puerto en el que esta escuchando VXMLS.
HOSTNAME : Nombre de la máquina en la que está funcionando VXMLS.
VXMLS CLIENT : Nombre que utilizará el Set Top Box para registrarse en
VXMLS.
VXMLS PASSWD: Clave que utilizará el Set Top Box para registrarse en
VXMLS.
DFB INCLUDE : Directorio en el que se encuentran las cabeceras de DirectFB.
Una vez editado este fichero, el comando make compilará las fuentes, creará el
fichero de propiedades conf/properties y generará el script bin/vxmls.
E.3.3.
Ejecución
La biblioteca gráfica se encuentra en el directorio lib, para ejecutar DFBCIn
es necesario indicarle al enlazador de código su ubicación, por ejemplo, añadiendo
la ruta a la variable de entorno LD LIBRARY PATH.
El ejecutable dfbcin se encuentra en el directorio src, su ejecución comenzará el funcionamiento de DFBCIn a pantalla completa. Para realizar pruebas es
más cómodo ejecutar el script bin/dfbcin sdl, que permite ejecutar DFBCIn
en una ventana dentro del entorno X-Windows.
Para que funcione DFBCIn es imprescindible que VXMLS esté escuchando en
el puerto y máquina especificados en el fichero dfbcin.mk.
Apéndice F
Glosario
API Application Programming Interface, Conjunto de funciones que proporciona
una biblioteca.
ASF Advanced Streaming Protocol, Protocolo de streaming avanzado. Protocolo
de streaming propietario de Microsoft.
ATM Asynchronous Transfer Mode, Modo de transmisión ası́ncrono. Tecnologı́a
de conmutación basada en la transmisión de celdas de tamaño fijo.
ATSC Advanced Television Systems Comitee, Comité de Sistemas de Televisión
Avanzada.
BIF Built In Function, Función integrada.
CBR Constant Bit Rate, Tasa de bits constante.
CPU Central Processing Unit, Unidad Central de Proceso.
DAC Digital to Analog Converter, Conversor de analógico a digital.
DAO Data Access Object, Objeto de acceso a datos. Patrón de diseño utilizado para desacoplar los métodos de acceso a sistemas de almacenamiento
persistente.
DASE Digital TV Application Software Environment, Estándar para el desarrollo de middleware propuesto por ATSC.
DTD Document Type Definition, Definición de tipo de documento. Especifica
la estructura que debe respetar un documento XML para que se considere
válido.
209
210
Apéndice F. Glosario
DVB Digital Video Broadcasting, Transmisión broadcast de vı́deo digital.
ERTS Erlang Runtime System, Entorno de ejecución de Erlang.
ESI Erlang Scripting Interface, Interfaz similar a CGI, optimizada para aplicaciones Erlang.
CGI Common Gateway Interface, Estándar para comunicar aplicaciones externas con servidores de información.
HTML Hypertext Marckup Language, Lenguaje ampliamente utilizado para la
publicación de documentos de hipertexto en Internet.
HTTP Hypertext Transfer Protocol, Protocolo estándar de Internet utilizado
para la transmisión de documentos a nivel de aplicación.
IRD Integrated Recevier Decoder, Decodificador Receptor Integrado.
ISDB Integrated Services Digital Broadcasting, Estándares para la transmisión
de vı́deo digital utilizados en Japón.
ISO International Organization of Standarization, Organización Internacional de
Estandarización.
LANE Local Area Network Emulation, Emulación de red de área local.
LIRC Linux Infrared Remote Control, Control remoto por infrarrojos para Linux.
MDI Multiple Document Interface, Interfaz multi-documento.
MHP Multimedia Home Plataform, Estándar de plataforma software para televisión interactiva.
MMIO Memmory Mapped Input Output, Registros de un dispositivo a los que
se puede acceder utilizando una dirección de memoria en lugar de usar
instrucciones de E/S especiales.
MPEG Moving Picture Experts Group, Familia de estándares para codificar archivos de contenido audiovisual.
MP3 MPEG Layer 3, Formato de compresión para ficheros de audio.
NTSC National Television Standards Comitee, Comité generador de estándares
para televisión en Estados Unidos.
211
OCAP OpenCable Application Plataform Specification, Especificación de plataforma de aplicación de OpenCable.
PAL Phase Alternation by Line, Estándar de sistema de vı́deo ampliamente utilizado en todo el mundo.
PNG Portable Netwok Graphics, Formato de compresión de imágenes sin pérdidas.
Pixel Cada uno de los puntos que forma una imagen digital.
QoS Quality of Service, Calidad de servicio.
RTOS Real Time Operating System, Sistemas operativos de tiempo real.
RTP Real-Time Transport Protocol, Protocolo de transporte en tiempo real.
RTSP Real-Time Streamming Protocol, Protocolo de streaming en tiempo real.
SDL : Simple DirectMedia Layer, Biblioteca que permite un acceso portable a
los dispositivos gráficos, dispositivos de sonido, ratón y teclado.
SQL Structured Query Language, Lenguaje de consulta estructurado. Lenguaje
utilizado para realizar consultas sobre bases de datos.
SVGA Super VGA, Estándar gráfico mucho más avanzado que VGA.
TCP Transmission Control Protocol, Protocolo estándar de Internet para establecer una enlace orientado a conexión.
URI Uniform Resource Identifier, Identificador uniforme de recursos.
URL Uniform Resource Locator, Localizador uniforme de recursos. Estándar que
permite referirse a un recurso y su localización.
VESA Video Electronic Starndards Association, Organización para el desarrollo
de estándares para hardware gráfico.
VGA Video Graphics Array, Estándar de hardware Gráfico diseñado por IBM.
Proporciona tanto el modo gráfico como el modo texto. La mayorı́a de las
tarjetas de vı́deo actuales proporcionan compatibilidad VGA.
VoD Video on Demand, Vı́deo bajo demanda.
XDML Extensible Document Meta Language, Meta lenguaje de documentos extensible. Permite definir documentos de forma que puedan ser exportados
a varios formatos.
212
Apéndice F. Glosario
XML Extensible Markup Language, Estándar que permite la definición de la
estructura de un documento independientemente de su contenido.
Bibliografı́a
[1] A. Hundt, “DirectFB overview (v1.0).” http://www.directfb.org/documentation/DirectFB overview V0.1.pdf, September 2001.
[2] C. Cooper, “Using expat.” http://www.xml.com/pub/a/1999/09/expat/index.html, september 1999.
[3] J. L. Armstrong, M. C. Williams, C. Wikström, and S. R. Virding, Concurrent Programming in Erlang. Prentice Hall, 2nd edition ed., 1996.
[4] “Introduction to erlang/otp.” http://erlang.org/doc/r9b/doc/system architecture intro/part frame.html.
[5] “Vodka project home page.” http://vodka.lfcia.org/.
[6] I. E. Mateos, “Administración personal de medios,” Master’s thesis, Facultade de Infomática de A Coruña, September 2002.
[7] A. C. Inc, “About darwin streaming server.” http://www.publicsource.apple.com/projects/streaming/.
[8] P. Wilkinson, M. DeSisto, H. Rother, and Y. Wong., “Ibm videocharger 101.
ibm redbook.” International Technical Support Organization, 1999.
[9] Oracle, “Oracle video server administrators guide and command reference,”
1998. Release 3.0 for UNIX.
[10] Oracle, “Oracle video server system technical overview.” Oracle White Paper, 1998. Release 3.0 for UNIX.
[11] Philips, “Webcine server.” http://www.mpeg-4player.com/products/server/index.asp.
[12] I. Cisco Systems, “A distributed video server architecture for flexible
enterprise-wide video delivery.” http://www.cisco.com/warp/public/cc/pd/mxsv/iptv3400/tech/dvsa wp.htm, 2000. White Paper.
213
214
Bibliografı́a
[13] S. M. Inc., “Sun storedge media central streaming server.” http://www.sun.com/storage/media-central/.
[14] S. G. Chan and F. Tobagi, “Hierarchical storage systems for interactive
video-on-demand,” Tech. Rep. CSL-TR-97-723, Stanford University, Computer Systems Laboratory, 1997.
[15] T. Chiueh, C. Venkatramani, and M. Vernick, “Design and implementation
of the stony brook video server,” tech. rep., 1997. Software – Practice and
Experience.
[16] T. Chiueh, C. Venkatramani, and M. Vernick, “Performance evaluation of
stony brook video server,” Tech. Rep. ECSL-TR-24, 1997.
[17] T. Chiueh, C. Venkatramani, and M. Vernick, “Adventures in building the
stony brook video server,” in Proceedings of ACM Multimedia ’96, (Boston,
MA.), 1996.
[18] D. Du, J. Hsieh, and J. Liu, “Building video-on-demand servers using sharedmemory multiprocessors,” tech. rep., Distributed Multimedia Research Center and Computer Science Department, University of Minnesota, and Ronald
J. Vetter, Computer Science Department, North Dakota State University,
1996.
[19] J. Sánchez, V. Gulı́as, A. Valderruten, and J. Mosquera, “State of the art and
design of vod systems,” in International Conference on Information Systems
Analisis, SCI’00-ISAS’00.ISBN 980-07-6694-4, (Orlando, USA), pp. 174–176,
July 2000.
[20] J. Whitehead and M. Wiggins, “Webdav: Ietf standard for collaborative authoring on the web,” IEEE Internet Computing, pp. 34–40, September/October 1998.
[21] “Digital video broadcasting home page.” http://www.dvb.org.
[22] “Dase web site.” http://www.dase.org.
[23] C. T. Laboratiories, “Ocap 2.0 profile,” in OpenCable Application Specification, April 2002.
[24] BartCalder, JonCourtney, BillFoote, LindaKyrnitszke, D. Rivas, C. Saito,
J. V. Loo, and T. Ye, “Javatv api technical overview,” tech. rep., Sun Microsystems, November 2000.
Bibliografı́a
215
[25] “Opentv fact sheet.” http://www.opentv.com/company/docs/CompanyAtGlance.pdf.
[26] “Canal+ technologies.” http://www.canalplus-technologies.com.
[27] “Alticast web site.” http://www.alticast.com.
[28] “Liberate technologies.” http://solutions.liberate.com.
[29] “Microsoft tv.” http://www.microsoft.com/tv/default.asp.
[30] “Nds core middleware.” http://www.nds.com/interactive tv/middleware.html.
[31] “Greg haerr’s microwindows and nanogui page.” http://www.microwindows.org.
[32] “Qt overview.” http://www.trolltech.com/products/qt/.
[33] “Directfb.org.” http://www.directfb.org/.
[34] G. Uytterhoeven, “The linux frame buffer device subsystem.” http://home.tvd.be/cr26864/Linux/Expo/Paper.ps.gz.
[35] R. Fielding, J. Gettys, J. Mogul, H. Frystyk, L. Masinter, P. Leach, and
T. Berners-Lee, “RFC 2616: Hypertext transfrer protocol – http/1.1,” June
1999.
[36] T. Bray, J. Paoli, C. M. Sperberg-McQueen, and E. Maler, “Extensible markup language (xml) 1.0 (second edition).” http://www.w3.org/TR/RECxml, October 2000.
[37] J. P. Fernández, “Desarrollo de un proxy para el acceso a medios bajo la apariencia de un sistema de ficheros,” Master’s thesis, Facultade de Infomática
de A Coruña, September 2002.
[38] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns. ISBN:0201-63361-2, Addison-Wesley, October 1994.
[39] Inets reference manual.
[40] Mnesia reference manual.
[41] Mnemosyne reference manual.
[42] T. Berners-Lee, R. Fielding, U. C. Irvine, and L. Masinter, “RFC 2396:
Uniform resource identifiers (uri): Generic syntax,” Agust 1998.
216
Bibliografı́a
[43] G. E. Kasner and S. T. Pope, “A cookbook for using the model-view controller user interface paradigm in smalltalk-80,” Journal of Object-Oriented
Programming, pp. 26–49, August/September 1988.
[44] D. Alur, J. Crupi, and D. Malks, Core J2EE Patterns: Best Practices and
Desing Strategies. ISBN:0130648841, Prentice Hall/Sun Microsystems Press,
1st ed., June 2001.
[45] D. Kristol and L. Montulli, “RFC 2109: Http state management mechanism,”
February 1997.
[46] “Lirc: Linux infrared remote control.” http://www.lirc.org/.
[47] “Linux avi file library.” http://avifile.sourceforge.net/.
[48] “The common gateway interface specification.” http://hoohoo.ncsa.uiuc.edu/cgi/interface.html.
[49] DirectFB Reference Manual.
[50] “The apache http server project.” http://www.httpd.apache.org/.

Documentos relacionados