Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

104

Transcript of Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Page 1: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 2: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 3: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Trabajo Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación

Monitorización mediante streaming de vídeo para

entrenamiento personal

Autor:

Antonio Clavaín Mateo

Tutor:

Mª Teresa Ariza Gómez

Profesor titular

Departamento de Ingeniería Telemática Escuela Técnica Superior de Ingeniería

Universidad de Sevilla Sevilla, 2014

Page 4: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 5: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Trabajo Fin de Grado: Monitorización mediante streaming de vídeo para entrenamiento personal

Autor: Antonio Clavaín Mateo

Tutor: Mª Teresa Ariza Gómez

El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros:

Presidente:

Vocales:

Secretario:

Acuerdan otorgarle la calificación de:

Sevilla, 2014

El Secretario del Tribunal

Page 6: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 7: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

A mi familia

Page 8: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 9: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

i

Agradecimientos

En primer lugar me gustaría agradecer a Mª Teresa Ariza Gómez por brindarme la oportunidad de trabajar en un proyecto divertido y que combinaba tantos elementos diferentes. Esto último es, en esencia, lo que me ha permitido disfrutar a lo largo del desarrollo mientras aprendía a solucionar los obstáculos con los que me he ido encontrando. Gracias también por su esfuerzo y dedicación, por sus consejos y correcciones, y por ayudar a que este proyecto saliera adelante.

Gracias, también, a todos los profesores que me han formado a lo largo de estos años, y que indirectamente han contribuido enormemente en la realización de este proyecto.

Quisiera agradecer, por último, a mi familia:

A mis padres y a mi hermana, por las horas dedicadas a revisar conmigo la documentación, por las palabras de apoyo y por permitirme usar sus equipos, en cualquier momento, para realizar pruebas de implementación.

A Belén, por animarme a continuar y a buscar siempre alguna solución ante los problemas que me surgían. Gracias también por el magnífico diseño del icono de la aplicación para Android.

Y en definitiva, a todos los que me han permitido realizar pruebas de testeo sobre sus teléfonos móviles y tablets.

Antonio Clavaín Mateo

Sevilla, 2014

Page 10: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 11: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

iii

Resumen

El avance de la tecnología ha permitido que podamos disponer de un ordenador en el bolsillo conectado a internet. Esto unido al creciente uso de las tecnologías de comunicación, nos brinda un amplio escenario para el desarrollo de aplicaciones en las que se usen las capacidades de transmisión de vídeo, para la monitorización mediante streaming de vídeo a través de la red.

En este proyecto se ha diseñado una infraestructura para que cualquier persona que disponga de un Smartphone o Tablet con el sistema operativo Android y una conexión a internet, pueda desde su casa realizar sesiones de ejercicios mientras es monitorizado por un especialista, a través de la cámara del dispositivo.

El principal objetivo del proyecto es lograr un sistema que nos permita transmitir vídeo en streaming desde un dispositivo móvil hacia un servidor web, donde el monitor previamente ha creado una sesión de ejercicios.

Page 12: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 13: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

v

Abstract

The technology improvement has allowed us to carry a powerful device with Internet connection in our pockets. Also, with the growing use of communication technologies, we can develop easily applications for video streaming over the network.

In this project it has been designed an infrastructure that allows anyone owning a Smartphone or tablet with Android operating system and an internet connection to make workouts from home while is being monitored by a specialist through the device’s camera.

The main objective of the project is to achieve a system that allows us to transmit video streaming from a mobile device to a web server, where the monitor has previously created a workout.

Page 14: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Índice

Agradecimientos i

Resumen iii

Abstract v

Índice vi

Índice de Tablas ix

Índice de Figuras xi

Acrónimos 12

1 Introducción 1 1.1 Motivaciones 1 1.2 Objetivos 1 1.3 Agentes del sistema 2 1.4 Arquitectura del sistema 2 1.5 Esquema de la Memoria 6

2 Tecnología 9 2.1 Servidor HTTP de Apache 9 2.2 Lenguaje de programación PHP 10 2.3 Gestor de bases de datos MySQL 10 2.4 Lenguaje de programación JavaScript 11 2.5 Lenguaje de formato de datos JSON 12 2.6 Android Studio 13 2.7 Wowza Streaming Engine 13 2.8 Protocolo RTSP 14 2.9 Protocolo RTMP 14

3 Servidor HTTP y MySQL 15 3.1 Base de Datos 15 3.2 Desarrollo 19

4 Aplicación Monitor 21 4.1 Desarrollo 22

5 Aplicación Android 29 5.1 Librerias 30 5.2 Desarrollo 30

6 Conclusiones 37 6.1 Objetivos logrados 37 6.2 Vías de mejora 38

7 Bibliogrfía 39

8 Anexos 41 8.1 Anexo I: Código fuente 41

8.1.1 Código fuente Servidor 41

Page 15: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

vii

8.1.2 Código fuente del Monitor 43 8.1.3 Código fuente Aplicación Android 56

8.2 Anexo II: Manual de instalación y uso del servidor 73 8.3 Anexo III: Manual de instalación y uso del Monitor 74 8.4 Anexo IV: Manual de instalación y uso de la aplicación Android 77

Indice Alfabético 81

Page 16: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 17: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

ix

ÍNDICE DE TABLAS

Tabla 1. Requisitos en la base de datos 17

Tabla 2. Requisitos del servidor 19

Tabla 3. Requisitos del servidor del monitor 21

Tabla 4. Requisitos de la aplicación de Android 29

Tabla 5. Credenciales válidas 77

Page 18: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 19: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

xi

ÍNDICE DE FIGURAS

Figura 1. Ejemplo de arquitectura del sistema. 3

Figura 2. Inicio de sesión del monitor 3

Figura 3. El monitor crea una sesión. 4

Figura 4. Inicio de sesión del usuario. 4

Figura 5. El usuario elige una sesión. 5

Figura 6. El usuario elige un ejercicio. 5

Figura 7. Arquitectura implementada para la realización del proyecto 6

Figura 8. Base de datos heredada (parte 1) 16

Figura 9. Base de datos heredada (parte 2) 17

Figura 10. Tablas que usamos en el sistema. 18

Figura 11. Contenido del historial de sesiones creadas 18

Figura 12. Formulario de inicio de sesión de la interfaz web del monitor 22

Figura 13. Formulario de creación de la sesión 22

Figura 14. Página de gestión de la sesión 23

Figura 15. Se ha añadido un ejercicio 24

Figura 16. Se ha eliminado un ejercicio 24

Figura 17. Un usuario ha entrado en la sesión 25

Figura 18. El usuario ha sido expulsado del servidor 26

Figura 19. Página para ver streaming de vídeo y enviar mensajes al usuario 26

Figura 20. Formulario inicio de sesión app Android 30

Figura 21. Formulario con mensaje de error 31

Figura 22. Lista de sesiones con contenido 32

Figura 23. Lista de sesiones activas vacía 32

Figura 24. Rutina actualizada 33

Figura 25. Rutina vacía 33

Figura 26. Reproducción del vídeo informativo 34

Figura 27. Información completa del ejercicio 34

Figura 28. Recepción del mensaje del monitor 35

Figura 29. Ejecución de las sentencias SQL en PHPMyAdmin 73

Page 20: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

xii

Acrónimos

3GGP 3rd Generation Partnership Project AJAX Asynchronous JavaScript And XML AMP Apache+MySQL+PHP Apache Web Server HTTP API Application Programming Interface COM Component Object Model CORBA Common Object Request Broker Architecture DOM Document Object Model GPL General Public License HTML HyperText Markup Language HTTP HyperText Transfer Protocol IDE Integrated Development Environment IMAP Internet Message Access Protocol IP Internet Protocol JSON JavaScript Object Notation LDAP Lightweight Directory Access Protocol MPEG Moving Picture Experts Group PHP Hypertext Pre-processor POP3 Post Office Protocol POSIX Portable Operating System Interface UniX

RIA Rich Internet Applications RTMP Real Time Messaging Protocol RTSP Real Time Streaming Protocol SAX Simple API for XML SNMP Simple Network Management Protocol SQL Structured Query Language TCP Transmission Control Protocol UDP User Datagram Protocol WDDX Web Distributed Data eXchange XAMPP X (cualquier sistema operativo)+ Apache+ MySQL+ PHP+ Perl XHTML eXtensible HyperText Markup Language XML eXtensible Markup Language

Page 21: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

1

1 INTRODUCCIÓN

l avance de la tecnología ha permitido que podamos disponer de un ordenador en el bolsillo conectado a internet. Esto unido al creciente uso de las tecnologías de comunicación, nos brinda un amplio escenario para el desarrollo de aplicaciones en las que se usen las capacidades de transmisión de vídeo,

para la monitorización mediante streaming de vídeo a través de la red.

En este proyecto se ha diseñado una infraestructura para que cualquier persona que disponga de un Smartphone o Tablet con el sistema operativo Android y una conexión a internet, pueda desde su casa realizar sesiones de ejercicios mientras es monitorizado por un especialista, a través de la cámara del dispositivo.

El principal objetivo del proyecto es lograr un sistema que nos permita transmitir vídeo en streaming desde un dispositivo móvil hacia un servidor web, donde el monitor previamente ha creado una sesión de ejercicios.

1.1 Motivaciones

En la actualidad, vivimos en una sociedad que impone un estilo de vida muy acelerado, en la que muchas personas no tienen tiempo libre para dedicarlo al ocio. A pesar de ello, somos conscientes de la necesidad de llevar una vida sana con hábitos saludables, en cuanto a alimentación y a actividad física. La idea de esta aplicación radica en la imposibilidad de muchas personas de realizar deporte a diario en un entorno seguro, controlado por un especialista, evitando así las lesiones que se puedan producir debido a una incorrecta ejecución del ejercicio. Así, esta aplicación dota a cualquier persona de una herramienta con la que hacer ejercicio mientras es monitorizado por un especialista, con el único requisito de disponer de un Smartphone o una tablet con el sistema operativo Android y con conexión a Internet. Esto le permitirá realizar ejercicio desde casa, sin necesidad de desplazarse al gimnasio, y con las mismas ventajas de supervisión de los ejercicios por parte de un preparador físico profesional. Todo esto es posible gracias al avance tecnológico tanto en las redes de comunicación como en la capacidad de procesamiento de los móviles actuales.

1.2 Objetivos

El principal objetivo del proyecto es lograr un sistema que nos permita transmitir vídeo en streaming desde un dispositivo móvil hacia un servidor web, donde el monitor previamente ha creado una sesión de ejercicios. Para ello se debe hacer uso del servidor que contiene la información del gimnasio (usuarios, ejercicios, etc.), y montar un nuevo servidor con PHP donde el monitor iniciará la sesión y almacenará la información acerca de los ejercicios elegidos para la rutina, así como de los usuarios conectados. También será necesaria una aplicación para Android que permita conectarse a ambos servidores: al primero para hacer el login y elegir la

E

El objeto de la educación es formar seres aptos para

gobernarse a sí mismos, y no para ser gobernados por

los demás.

- Herbert Spencer -

Page 22: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Introducción

2

sesión de ejercicios, y al segundo para obtener la lista de ejercicios, la información de cada ejercicio, y realizar el streaming desde el usuario hasta el monitor. Y por último, también es necesario implementar el servidor para el monitor que citamos anteriormente, las funciones de éste será poder conectarse al servidor del gimnasio y, tras obtener un listado de todos los ejercicios disponibles, brindar al monitor la posibilidad de crear una sesión personalizada con los ejercicios que desee. También debe poder administrar los usuarios conectados (denegar el acceso, observar vía streaming o enviar mensajes de texto).

1.3 Agentes del sistema

Servidor: Denominaremos servidor, al servidor del gimnasio. En la base de datos ubicada en este servidor, de nombre ‘gymserver’, se almacena toda la información que debe ser persistente. Ésta es: información acerca de

las cuentas de usuario, tabla con todos los ejercicios disponibles para añadir a las rutinas, información acerca de estos ejercicios, e información de las sesiones creadas por los monitores. Se almacena un historial de las sesiones creadas y también una tabla en la que se registran las sesiones activas para los usuarios.

Este servidor también es el encargado de interactuar al comienzo de la aplicación de Android, a la que le pasará la información necesaria para establecer una conexión con el servidor del monitor que ha creado la sesión de ejercicios que haya elegido.

Aplicación Monitor: Denominaremos de esta manera a la aplicación web disponible en el servidor del monitor. Puede estar situado en el mismo servidor del gimnasio o en otro equipo diferente. Cada servidor solo puede gestionar una sesión de ejercicios a la vez, por lo que se debería montar un servidor por cada monitor del gimnasio. Se trata de una aplicación web que permite al monitor realizar una lista de ejercicios a partir de la lista completa almacenada en el servidor definido en el apartado anterior. Toda la información acerca de los ejercicios es almacenada en el servidor del gimnasio, por lo que, cuando el monitor elige añadir un ejercicio a su sesión se realiza una transferencia tanto de la información acerca del ejercicio (nombre, descripción y duración), como de un vídeo descriptivo del ejercicio que posteriormente se enviará al cliente de la aplicación Android. Además, el monitor dispondrá de un panel de administración de usuarios desde el que puede denegar el acceso a ciertos usuarios o entrar en la sala de visualización. En esta sala podrá ver el streaming de vídeo obtenido de la cámara delantera del dispositivo desde el que el cliente está ejecutando la aplicación de Android, así como enviar mensajes de texto al cliente. También le será posible volver a la gestión de la sesión para modificar la lista de ejercicios o acceder a la sala con otro usuario conectado. Por último, toda la información generada en la sesión, vídeos e información acerca de ejercicios y usuarios, se borra cuando el monitor cierra la sesión.

Aplicación Android: Será la aplicación que usarán los clientes para hacer ejercicio cómodamente desde casa. Se trata de una aplicación Android compatible con móviles a partir de la versión 4.0 (debido a los requisitos de una librería que es usada para realizar el streaming de vídeo). Con la aplicación el usuario iniciará la sesión en el servidor, y tras elegir una sesión, el servidor le entrega la información acerca del servidor del monitor. A partir de entonces toda la comunicación se realiza exclusivamente con ese servidor, liberando de carga al servidor del gimnasio. A través del monitor obtendrá la lista de ejercicios de la sesión, así como la información de estos ejercicios. También se realiza el streaming de vídeo y la recepción de mensajes escritos por el monitor. En el apartado 5, dedicado a la aplicación para Android veremos en profundidad las clases y los layouts que intervienen en la aplicación.

1.4 Arquitectura del sistema

Nuestro sistema se compone de tres elementos, tal y como vimos anteriormente: servidor del gimnasio, servidor del monitor, y aplicación Android. El servidor del gimnasio es único, y en él se almacenan información de usuarios, ejercicios, sesiones activas, etc. Por otra parte encontramos el servidor del monitor, cuyo número puede variar según las condiciones del gimnasio que implemente el sistema. Este servidor también puede montarse en el mismo equipo del servidor del gimnasio. Y por último se encuentra la aplicación para dispositivos Android cuyo número depende de los clientes del gimnasio.

En la siguiente imagen se muestra un ejemplo de implementación sencillo, donde los dos servidores (el del gimnasio y el del monitor) están desacoplados y tan solo existe un cliente [Figura 1].

Page 23: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

3 Monitorización mediante streaming de vídeo para entrenamiento personal

A continuación vemos como sería la interacción entre los tres elementos en un funcionamiento normal del sistema, en el que tanto el monitor [Figura 2] como el usuario [Figura 4] inician sesión, el monitor crea una sesión de ejercicios [Figura 3] y el usuario elige la sesión del monitor [Figura 5] y escoge un ejercicio [Figura 6].

El monitor introduce su usuario y contraseña en el formulario, al pulsar sobre el botón de enviar se realiza una consulta a la base de datos del servidor. Se comprueba si los parámetros son válidos, y en caso de serlo se carga la página para crear la sesión de ejercicios.

Figura 1. Ejemplo de arquitectura del sistema.

Figura 2. Inicio de sesión del monitor

Page 24: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Introducción

4

El monitor rellena un nuevo formulario en el que describe la sesión a crear (nombre, fecha y límite de usuarios). Una vez pulsa el botón de enviar se inserta en la base de datos del servidor en la tabla ‘sesiones’. Una vez creada la sesión, se carga la página de gestión de la sesión en la que se muestra un listado con todos los ejercicios disponibles para que el monitor añada a su rutina.

Para ello, se debe realizar otra consulta a la base de datos del servidor que devuelva todas las filas de la tabla ‘ejercicios’. Cuando se recibe la respuesta el monitor ya puede añadir ejercicios a su rutina mediante el botón

añadir situado a la derecha de cada ejercicio. Esta información (la de ejercicios disponibles para el usuario) se almacena en el servidor monitor. Por lo que será necesario que el cliente obtenga la dirección IP del servidor monitor, a continuación veremos cómo se resuelve esta particularidad.

Figura 3. El monitor crea una sesión.

Figura 4. Inicio de sesión del usuario.

Page 25: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

5 Monitorización mediante streaming de vídeo para entrenamiento personal

El usuario, desde la aplicación Android, envía una petición de login al servidor pasando como parámetros el usuario y la contraseña introducidos en el formulario. Cuando el servidor recibe los parámetros, comprueba que estos existen en la base de datos del servidor y envía la respuesta al cliente. Si se recibe una respuesta positiva, se carga la nueva actividad y envía una petición al servidor para obtener el listado de todas las sesiones activas. La respuesta contiene la información de todas las sesiones activas en el servidor.

Cuando el usuario pulsa sobre la lista y elige una de las sesiones, accede a toda la información de la sesión seleccionada. De ahí obtiene la dirección IP del servidor monitor, necesaria porque a partir de este momento todas las comunicaciones se harán con el servidor monitor, hasta que el usuario decida salir de la sesión. Posteriormente, se lanza la nueva actividad de la aplicación para Android. Una vez aquí, se realiza una consulta al servidor monitor, que responde con una lista de todos los ejercicios de la sesión.

Al igual que cuando el usuario elegía la sesión, al pulsar sobre el listado del ejercicio se detecta el ejercicio que ha pulsado y se carga una nueva actividad con toda la información correspondiente a ese ejercicio. En la nueva actividad la aplicación carga mediante HTTP el vídeo que está almacenado en el servidor monitor (y este a su vez, lo descargó del servidor cuando añadió el ejercicio a su rutina).

Figura 5. El usuario elige una sesión.

Figura 6. El usuario elige un ejercicio.

Page 26: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Introducción

6

Además, pone un socket a la escucha para recibir los mensajes enviados por el monitor, con lo que obtenemos un canal de feedback entre el usuario y el monitor, por este canal el monitor comunicará al cliente si está realizando correctamente el ejercicio o dar otro tipo de consejos.

Por último, llevará a cabo la función de streaming de vídeo gracias a la librería ‘libstreaming’, que nos permitirá acceder a la cámara del dispositivo y realizar la transmisión de vídeo hacia el servidor de streaming instalado en el servidor monitor. El servidor de streaming implementado para la realización del proyecto es Wowza, cuya función queda descrita en el apartado Tecnologías subapartado Wowza Streaming Engine.

En la siguiente figura se muestra un esquema descriptivo de las tecnologías empleadas en cada nodo del sistema.

1.5 Esquema de la Memoria

La memoria del proyecto esta estructurada en capítulos, para facilitar la búsqueda de información. El esquema de la Memoria es el siguiente:

Capítulo 1. Introducción: En este capítulo se expone la motivación del proyecto, así como los objetivos que se propone cumplir. También describe a un nivel general la arquitectura de nuestro sistema completo, así como de las funciones de cada elemento de la arquitectura.

Capítulo 2. Tecnología: En este capítulo se hace una introducción a todas las tecnologías usadas para la completa realización del proyecto, detallando en qué contribuye cada una de ellas en el correcto funcionamiento de nuestro sistema.

Capítulo 3. Servidor HTTP y MySQL: En este apartado se define la función que cumple este elemento de nuestra arquitectura. Mostrando las tablas de la base de datos que son usadas por nuestro sistema, y las funciones de PHP que permiten la interacción entre el servidor y el resto de agentes que componen el sistema.

Capítulo 4. Aplicación Monitor: Muestra las funciones que cumple el servidor del monitor. Describe detalladamente el funcionamiento de cada archivo utilizado por este servidor, así como los archivos en los que almacena la información acerca de los ejercicios y de los usuarios activos.

Capítulo 5. Aplicación Android: En este capítulo se muestra en profundidad la aplicación desarrollada para Android. Describiendo sus elementos más importantes (clases y layouts que componen la aplicación), una descripción de los requisitos de los dispositivos Android, y una introducción a la librería utilizada para realizar streaming de vídeo desde la cámara del dispositivo hacia el servidor del monitor.

Capítulo 6. Conclusiones: Se describen las conclusiones del proyecto, entre los que se incluyen los objetivos que se han conseguido alcanzar, las posibles vías de mejora para un trabajo posterior, así como las

Figura 7. Arquitectura implementada para la realización del proyecto

Page 27: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

7 Monitorización mediante streaming de vídeo para entrenamiento personal

conclusiones personales de la elaboración del proyecto.

Capítulo 7. Bibliografía: Índice en el que se expone la bibliografía consultada para la elaboración del proyecto.

Capítulo 8. Anexos: Los anexos están formados por el código fuente de los tres elementos del sistema (servidor, servidor del monitor y aplicación Android). Además, también incluye manuales para la instalación y el uso de todo el sistema.

Page 28: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 29: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

9

2 TECNOLOGÍA

as herramientas y tecnologías elegidas para el desarrollo de un proyecto deben ser escogidas cautelosamente, ya que pueden suponer el fracaso de éste o pueden aumentar su complejidad, para lo cual se debe conocer cuáles son las distintas alternativas y las necesidades de nuestro proyecto. Para

este proyecto se ha considerado el uso de software de código abierto preferentemente o en su defecto versiones libres del software.

Por lo que se decidió usar la arquitectura AMP (Apache+MySQL+PHP) sobre un entorno Windows por ser la arquitectura más extendida en el entorno web. Para instalar este software se ha hecho uso del paquete XAMPP, que proporciona una arquitectura AMP sobre cualquier plataforma (en nuestro caso Windows).

Además, el sistema se servirá del lenguaje de programación JavaScript para dotar a la página web del monitor de ciertas funciones extras que hacen que la interfaz del usuario sea más práctica y sencilla de usar.

También se usará JSON, un formato para serializar datos, que nos permitirá llevar a cabo la comunicación entre los servidores y la aplicación de Android, así como almacenar datos temporales en el servidor del monitor de una forma estructurada.

Puesto que en flash no se puede reproducir un flujo de datos RTSP, que es el protocolo que se utilizara para realizar el streaming de vídeo en la aplicación Android, debe usarse un servidor de streaming. Este servidor nos transformará el flujo RTSP en un flujo RTMP que ya si puede ser incrustado en nuestro reproductor flash.

2.1 Servidor HTTP de Apache

Apache es un servidor HTTP de código abierto, modular, extensible y multiplataforma, desarrollado por Apache Software Foundation disponible para equipos UNIX, Windows y Mac OS. La Free Software Foundation no considera la Licencia Apache compatible con la versión 2 de la GNU General Public License (GPL), pero permite la distribución de derivados de código abierto y cerrado a partir de su código fuente original. Debido a su arquitectura, Apache es altamente configurable y seguro, cualidades que le han proporcionado una gran aceptación. Según la última encuesta de Netcraft1, sigue siendo el servidor web más utilizado,

1 Uso del servidor Apache http://news.netcraft.com/archives/2014/08/27/august-2014-web-server-survey.html

L

Un ordenador es para mí la herramienta más

sorprendente que hayamos ideado. Es el equivalente a

una bicicleta para nuestras mentes.

- Steve Jobs -

Page 30: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Tecnología

10

aunque lejos de su pico alcanzado en 2005 del 70%, actualmente el 51% de los sitios web activos se encuentran en servidores Apache.

Entre las muchas características del servidor Apache se encuentra un elaborado índice de directorios, un directorio de alias, negociación de contenidos, informe de errores HTTP configurable, gestión de recursos para procesos hijos, integración de imágenes del lado del servidor, reescritura de las URL.

El servidor Apache no posee una interfaz de usuario gráfica para su administración, toda la configuración de Apache se confía a un único archivo. Se trata de un sencillo archivo de configuración llamado ‘httpd.conf’ que se puede modificar con un simple editor de texto.

Su función consiste en servir las páginas web que se le solicitan mediante las peticiones HTTP GET. Su modularidad, permite que se le añadan módulos que incrementan su funcionalidad. En nuestro caso será necesario añadir el módulo de PHP, ya que como veremos a continuación, usaremos este lenguaje para realizar las consultas a la base de datos y para dotar de dinamismo a la aplicación web del monitor.

Para nuestro sistema será necesario, como mínimo, instalar un servidor HTTP de Apache (en el caso de que el servidor y el servidor del monitor estén acoplados). Y será necesario montar otro servidor por cada monitor que queramos añadir al sistema.

2.2 Lenguaje de programación PHP

PHP (PHP Hypertext Pre-processor) es un lenguaje de programación interpretado de alto nivel, diseñado para el desarrollo web de contenido dinámico y el cual puede ser embebido en páginas HTML. Lo que distingue a PHP frente a otros lenguajes de programación como JavaScript, el cual se ejecuta en la máquina cliente, es,

que el código PHP es ejecutado en el servidor.

Aunque el desarrollo de PHP está concentrado en la programación de scripts en el lado del servidor, también tiene la posibilidad de manejar imágenes, archivos PDF, películas Flash, manejo de archivos XHTML y XML. PHP tiene soporte para una gran cantidad

de Bases de Datos, así como ODBC (el estándar abierto de Conexión con Bases de Datos). PHP cuenta también con soporte para comunicarse con otros servicios usando protocolos tales como LDAP, IMAP, SNMP, POP3, HTTP, COM, etc. PHP soporta WDDX para el intercambio de datos entre lenguajes de programación web. PHP puede utilizar objetos Java de forma transparente como objetos PHP y la extensión CORBA puede ser utilizada para acceder a objetos remotos.

PHP tiene unas características muy útiles para el procesamiento de texto, desde expresiones regulares POSIX extendidas o tipo Perl hasta procesadores de documentos XML. Para procesar y acceder a documentos XML, soporta los estándares SAX y DOM, utilizando la extensión XSLT para transformar documentos XML.

Tras ejecutar el código PHP el servidor genera una respuesta y la envía al cliente. Este lenguaje está desarrollado por PHP Group y es de código libre. En nuestro sistema es necesario instalar PHP tanto en el servidor como en el servidor del monitor. Ya que ambos deben ejecutar código PHP. El servidor para realizar consultas sobre la base de datos, y el servidor del monitor para realizar consultas sobre la base de datos del servidor y para ofrecer una aplicación web dinámica para el monitor.

2.3 Gestor de bases de datos MySQL

MySQL nace como un proyecto para crear un sistema de bases de datos de software libre por parte de la empresa sueca MySQL AB en 1995, que en 2008 fue comprada por Sun Microsystems, y ésta lo fue por Oracle en 2009.

MySQL es un sistema de gestión de bases de datos relacional, multihilo y multiusuario, pero a pesar de su popularidad, este sistema todavía no tiene algunas de las características que poseen otros sistemas comerciales. Sin embargo, estas diferencias son cada vez más pequeñas.

Las principales característica de MySQL son:

Sistema de base de datos relacionales

Arquitectura cliente/servidor

Page 31: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

11 Monitorización mediante streaming de vídeo para entrenamiento personal

Compatibilidad con SQL. En este momento soporta SQL:2003, aunque tiene muchas extensiones

SubSELECTS. Es capaz de procesar consultas del tipo: SELECT * FROM table1 WHERE x IN (SELECT y FROM table2)

Vistas (Views). Son consultas SQL cuyos resultados son tratados como si fueran una tabla diferente, permitiendo tener diferentes vistas de una misma tabla.

Stored Procedures. Son consultas SQL que están almacenadas en la base de datos. Como las vistas, pueden incrementar la eficiencia y simplificar la administración de la base de datos.

Triggers. Son comandos SQL que se ejecutan automáticamente en ciertas operaciones (INSERT, UPDATE y DELETE).

Unicode. Además, MySQL soporta casi cualquier tipo de codificación de caracteres, como Latin-1 y Latin-2.

Full-text search. Esta característica acelera y simplifica la búsqueda de palabras que se encuentran almacenadas en campos de tipo texto.

Replicación. Permite copiar el contenido de una base de datos a múltiples ordenadores. Esta característica se usa para mejorar la protección contra fallos, y para acelerar las consultas.

Transacciones. Una transacción es un conjunto de operaciones sobre una base de datos que funcionan como un bloque. El sistema asegura que o bien se han ejecutado todas las operaciones, o ninguna. Esto es válido incluso si hay una caída del sistema, un fallo eléctrico, o cualquier otro desastre.

Restricciones sobre claves externas (foreign keys). Son reglas con las que se asegura la integridad de las relaciones entre tablas.

Lenguajes de programación. Hay un gran número de APIs y librerías para desarrollar .

Sin embargo, a pesar de esta importante lista de caracteristicas, MySQL tiene una serie de limitaciones:

Cuando MySQL usa las tablas del tipo MyISAM, el sistema de bloqueo (locking) de datos solo funciona para tablas enteras. Eso significa que si queremos modificar una tabla y queremos que nadie más pueda interferir en la operación, la única manera es bloquear totalmente el acceso a la tabla entera. Este problema se puede resolver usando tablas del tipo InnoDB que soportan bloqueo por registro.

MySQL no permite añadir tipos de datos definidos por el usuario.

MySQL no soporta XML.

MySQL no ofrece funcionalidad para aplicaciones en tiempo real.

El sistema de triggers aún no está maduro.

Una de las características más atractivas de MySQL es su licencia. MySQL es un proyecto de software libre. Desde Junio de 2000 se libera bajo la licencia GPL (GNU Public License). Sin embargo, si se quiere usar MySQL para un fin comercial sin la necesidad de proporcionar el codigo fuente de nuestra aplicación, entonces se puede comprar una licencia comercial.

Nuestro sistema dispone de una base de datos situada en el servidor. Sobre ella realizarán consultas e inserciones de datos tanto el servidor como el servidor del monitor. En esta base de datos se lleva el registro de usuarios, ejercicios, información acerca de los ejercicios, el historial de sesiones y las sesiones activas.

2.4 Lenguaje de programación JavaScript

JavaScript es un lenguaje de programación que se utiliza principalmente para crear páginas web dinámicas.

Una página web dinámica es aquella que incorpora efectos como texto que aparece y desaparece, animaciones, acciones que se activan al pulsar botones y ventanas con mensajes de aviso al usuario.

Técnicamente, JavaScript es un lenguaje de programación interpretado, por lo que no es

Page 32: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Tecnología

12

necesario compilar los programas para ejecutarlos. En otras palabras, los programas escritos con JavaScript se pueden probar directamente en cualquier navegador sin necesidad de procesos intermedios.

A pesar de su nombre, JavaScript no guarda ninguna relación directa con el lenguaje de programación Java. Legalmente, JavaScript es una marca registrada de la empresa Sun Microsystems.

A principios de los años 90, la mayoría de usuarios que se conectaban a Internet lo hacían con módems a una velocidad máxima de 28.8 kbps. En esa época, empezaban a desarrollarse las primeras aplicaciones web y por tanto, las páginas web comenzaban a incluir formularios complejos.

Con unas aplicaciones web cada vez más complejas y una velocidad de navegación tan lenta, surgió la necesidad de un lenguaje de programación que se ejecutara en el navegador del usuario. De esta forma, si el usuario no rellenaba correctamente un formulario, no se le hacía esperar mucho tiempo hasta que el servidor volviera a mostrar el formulario indicando los errores existentes.

Brendan Eich, un programador que trabajaba en Netscape, pensó que podría solucionar este problema adaptando otras tecnologías existentes (como ‘ScriptEase’) al navegador Netscape Navigator 2.0, que iba a lanzarse en 1995. Inicialmente, Eich denominó a su lenguaje ‘LiveScript’.

Posteriormente, Netscape firmó una alianza con Sun Microsystems para el desarrollo del nuevo lenguaje de programación. Además, justo antes del lanzamiento Netscape decidió cambiar el nombre por el de JavaScript. La razón del cambio de nombre fue exclusivamente por marketing, ya que Java era la palabra de moda en el mundo informático y de Internet de la época.

La primera versión de JavaScript fue un completo éxito y Netscape Navigator 3.0 ya incorporaba la siguiente versión del lenguaje, la versión 1.1. Al mismo tiempo, Microsoft lanzó JScript con su navegador Internet Explorer 3. JScript era una copia de JavaScript al que le cambiaron el nombre para evitar problemas legales.

Para evitar una guerra de tecnologías, Netscape decidió que lo mejor sería estandarizar el lenguaje JavaScript. De esta forma, en 1997 se envió la especificación JavaScript 1.1 al organismo ECMA (European Computer

Manufacturers Association).

ECMA creó el comité TC39 con el objetivo de "estandarizar un lenguaje de script multiplataforma e

independiente de cualquier empresa". El primer estándar que creó el comité TC39 se denominó ECMA-262, en el que se definió por primera vez el lenguaje ECMAScript.

Por este motivo, algunos programadores prefieren la denominación ‘ECMAScript’ para referirse al lenguaje JavaScript. De hecho, JavaScript no es más que la implementación que realizó la empresa Netscape del estándar ECMAScript.

La organización internacional para la estandarización (ISO) adoptó el estándar ECMA-262 a través de su comisión IEC, dando lugar al estándar ISO/IEC-16262.

Además en el servidor del monitor se usarán las librerías de jQuery y JWPlayer.

jQuery es una biblioteca de JavaScript, creada inicialmente por John Resig, que permite simplificar la manera de interactuar con los documentos HTML, manipular el árbol DOM, manejar eventos, desarrollar animaciones y agregar interacción con la técnica AJAX a páginas web. Fue presentada el 14 de enero de 2006 en el BarCamp NYC.

jQuery es software libre y de código abierto, posee un doble licenciamiento bajo la Licencia MIT y la Licencia Pública General de GNU v2, permitiendo su uso en proyectos libres y privados. jQuery, al igual que otras bibliotecas, ofrece una

serie de funcionalidades basadas en JavaScript que de otra manera requerirían de mucho más código, es decir, con las funciones propias de esta biblioteca se logran grandes resultados en menos tiempo y espacio

JW Player es un reproductor multimedia HTML5 / Flash integrable. Es compatible con una amplia gama de plataformas (incluyendo soporte móvil ) y los formatos de medios. Es de código abierto y fácil de configurar, personalizar y ampliar.

2.5 Lenguaje de formato de datos JSON

Con la creciente popularidad de los servicios Web, XML se ha convertido prácticamente de facto en el

Page 33: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

13 Monitorización mediante streaming de vídeo para entrenamiento personal

estándar para transmisión de datos. Pero se necesita transmitir a través de Internet muchos más bytes de información para realizar una tarea que se podría llevar a cabo con un flujo de información mucho más pequeño.

Así se han desarrollado nuevas formas de compresión XML e, incluso, nuevos formatos XML completos, tales como Binary XML (XML binario). Todas estas soluciones funcionan ampliando o añadiéndose a XML, conviniendo los aspectos de compatibilidad descendente en un asunto a tener en cuenta. Douglas Crockford, un experimentado ingeniero software, propuso un nuevo formato de datos construido sobre JavaScript llamado JSON, JavaScript Object Notation (notación de objetos JavaScript).

JSON es un formato de datos muy ligero basado en un subconjunto de la sintaxis de JavaScript: literales de matrices y objetos. Como usa la sintaxis JavaScript, las definiciones JSON pueden incluirse dentro de archivos JavaScript y acceder a ellas sin ningún análisis adicional como los necesarios con lenguajes basados en XML.

Su popularización llegó sobre 2001 gracias al apoyo incondicional de Douglas Crockford. Yahoo! ayudó en gran manera a su difusión a raíz de la inclusión de este formato en algunos de sus servicios web más innovadores. En diciembre de 2006, Google comenzaría a ofrecer sus feeds en JSON para su protocolo web GData.

Pese a que JSON se basa en la notación Javascript, está considerado como un lenguaje independiente de formato de datos cuya especificación es descrita en RFC4627.

La elección de JSON se debe a que resulta muy simple tanto para humanos como para máquinas, y a que está orientado a las estructuras de datos de los lenguajes de programación modernos.

Por tanto, JSON es una alternativa a XML, que nos permite codificar y descodificar datos de una forma sencilla para la comunicación entre los servidores y la aplicación Android. También lo usaremos para almacenar en el servidor del monitor, mediante ficheros temporales que se borrarán al finalizar la sesión, información acerca de los ejercicios.

2.6 Android Studio

Es un IDE (Entorno de Desarrollo Integrado) desarrollado por Google y que aún se encuentra en estado beta. Sin embargo, es bastante completo y nos permite crear aplicaciones para Android de una forma muy cómoda y sencilla. Proporcionándonos además un depurador muy completo, que facilita la labor de programación. Reduciendo considerablemente el tiempo empleado en resolver errores de programación y comportamientos no deseados.

2.7 Wowza Streaming Engine

Wowza Streaming Engine (conocida como Wowza Media Server en versiones anteriores a la versión 4) es un software de servidor de medios de streaming unificado desarrollado por Wowza Media Systems. El servidor se utiliza para la transmisión en directo y bajo demanda de vídeo, audio y aplicaciones RIA (Rich Internet Applications) a través de redes IP. El servidor es una aplicación desarrollada en Java para los siguientes sistemas operativos: Linux, Mac OS X, Solaris, Unix y Windows.

Wowza Streaming Engine puede transmitir a múltiples tipos de clientes y dispositivos de reproducción de forma simultánea, incluyendo el reproductor de Adobe Flash, Microsoft Silverlight player, Apple QuickTime Player y dispositivos iOS (iPad, iPhone, iPod Touch), teléfonos móviles 3GPP (Android, BlackBerry OS, Symbian, etc), IPTV set-top boxes (Amino, Enseo, Roku, Streamit y otros), y las consolas de juegos como Wii, Xbox y PS3.

Wowza Streaming Engine es compatible con protocolos de transmisión estándar. En el lado de reproducción, estos incluyen RTMP (y las variantes RTMPS, RTMPT, RTMPE, RTMPTE), HDS, HLS, MPEG DASH, RTSP, Smooth Streaming, y MPEG-TS (unicast y multicast). En el lado de retranmisión en directo el servidor

Page 34: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Tecnología

14

puede retransmitir vídeo y audio a través de RTP, RTSP, RTMP, MPEG-TS (unicast y multicast). También es compatible con los flujos de entrada a través de los protocolos RTSP y WOWZ de dispositivos Android y iOS móviles que funcionan con la aplicación de codificación móvil Wowza GoCoder, disponible tanto en Google Play como en la AppStore por 4.99$.

Para el streaming bajo demanda, Wowza Streaming Engine puede transmitir varios tipos de archivos de audio y vídeo. Tipos de archivo admitidos incluyen MP4 (contenedor QuickTime - Mp4, .f4v, mov, m4a, m4v, .mp4a, .mp4v, .3gp, y .3g2), FLV (Flash Video - flv), y el contenido MP3 (Mp3).

Puesto que no es posible reproducir directamente un flujo de streaming RTSP en HTML ni en un reproductor flash, surge la necesidad de o bien instalar un complemento como VLC player al navegador o bien instalar un servidor de streaming que convierta el flujo RTSP en flujo RTMP. RTMP es un protocolo desarrollado por Adobe y que sí permite ser incrustado en un reproductor flash. Para solucionar este problema opté por instalar Wowza Streaming Engine y usar el reproductor Flash de JWPlayer, ya que el reproductor flash es menos agresivo con el diseño de la página que incrustar un complemento del navegador. Además, nos evitamos problemas de incompatibilidad del navegador del monitor con dicho complemento, así como problemas de funcionamiento cuando el navegador reciba nuevas actualizaciones. Por tanto será necesario instalar al menos un servidor de streaming en nuestro sistema para convertir el flujo RTSP en flujo RTMP.

2.8 Protocolo RTSP

RTSP (Real Time Streaming Protocol) o protocolo de flujo en tiempo real es un protocolo que establece y controla uno o muchos flujos sincronizados de datos (audio y vídeo). RTSP es un protocolo no orientado a conexión, en lugar de esto el servidor mantiene una sesión asociada a un identificador, en la mayoría de los casos RTSP usa TCP para datos de control del reproductor y UDP para los datos de audio y vídeo aunque también puede usar TCP en caso de que sea necesario. En el transcurso de una sesión RTSP, un cliente puede abrir y cerrar varias conexiones de transporte hacia el servidor con tal de satisfacer las necesidades del protocolo. En nuestro sistema, es la aplicación Android la que genera el flujo de vídeo que es enviado mediante RTSP hacia el servidor de streaming. Cuando este flujo sea convertido a RTMP será posible reproducirlo en el reproductor flash JWPlayer incrustado en la página web desde la que el monitor puede interactuar con el usuario de la aplicación Android.

2.9 Protocolo RTMP

RTMP (Real Time Messaging Protocol) es un protocolo desarrollado por Macromedia para realizar streaming de audio y vídeo a través de Internet entre un servidor y reproductor flash. Macromedia fue comprada por Adobe en 2008. Este protocolo está basado en TCP, lo que puede provocar un pequeño retraso en el envío de vídeo mayor al que se produce cuando se envía sobre UDP. Sin embargo, ésto hace que el ancho de banda se pueda ajustar dinámicamente al estado de la red.

En nuestro sistema el servidor de streaming es el encargado de convertir el flujo RTSP creado por la aplicación móvil en un flujo RTMP que es el formato que consume el reproductor flash JWPlayer.

Page 35: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

15

3 SERVIDOR HTTP Y MYSQL

al y como definimos anteriormente, este será el servidor del gimnasio, el encargado de almacenar toda la información persistente del sistema y de realizar en primera instancia la comunicación con la aplicación de Android. Está basado en los Proyectos Fin de Carrera "Aplicación android para la

rehabilitación física de usuario usando MySQL, PHP y codificación JSON" de Antonio José Díaz Lora y "Personal trainer, entrenador personal para android" de Juan García Piosa.

Para hacer que nuestro sistema sea compatible con todo el trabajo anterior y conseguir que las aplicaciones previamente desarrolladas puedan seguir funcionando correctamente, usaré como base el trabajo de ambos proyectos. Añadiendo las nuevas funcionalidades a partir del código previo, y procurando no alterar lo ya construido, adaptándome a la base de datos y a las funciones PHP construidas previamente.

Entre estas nuevas funciones destaca la necesidad de transferir a la aplicación móvil información acerca del servidor del monitor, con el que la aplicación Android debe comunicarse para obtener información de la rutina de ejercicios y de realizar el streaming de vídeo.

3.1 Base de Datos

Puesto que pretendemos partir del trabajo realizado anteriormente, debemos conocer de qué base de datos partimos. En las figuras [Figura 8] y [Figura 9] puede observarse un diagrama del modelo entidad-relación de la base de datos.

T

El hombre tiene mil planes para sí mismo. El azar, sólo

uno para cada uno.

- Mencio -

Page 36: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Servidor HTTP y MySQL

16

Figura 8. Base de datos heredada (parte 1)

Page 37: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

17 Monitorización mediante streaming de vídeo para entrenamiento personal

Conociendo el contexto desde el que partimos, definimos las nuevas necesidades de nuestro sistema.

Código de requisito Descripción

R01 Usuarios y sus datos

R02 Ejercicios e información detallada de éstos

R03 Historial de las sesiones creadas en nuestro sistema

R04 Sesiones activas para los usuarios de la aplicación Android

Tabla 1. Requisitos en la base de datos

Figura 9. Base de datos heredada (parte 2)

Page 38: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Servidor HTTP y MySQL

18

Para poder lograr los nuevos requisitos debemos hacer uso de las siguientes tablas en la base de datos [Figura 10]

Como podemos observar, tres de ellas ya existían anteriormente (‘usuarios’, ‘ejercicios’ y

‘explicacionesdeejercicios’). Por lo que haremos uso de ellas para cumplir con los requisitos R01 y R02.

La tabla ‘sesiones’ nos permitirá añadir una fila cada vez que se cree una nueva sesión y llevar un historial de todas las sesiones creadas (R03). En la tabla se almacena el nombre de la sesión, el nombre del monitor, la fecha, el número de usuarios máximos que permite la sesión, la dirección IP y puerto en el que escucha el servidor HTTP del monitor y si la sesión está activa (R04). En la [Figura 11] puede verse una captura del contenido de esta tabla.

Se debe añadir una nueva fila cuando se crea la sesión, y modificar el valor del campo ‘activa’ cuando el monitor cierre la sesión. También se almacena la dirección (dirección IP y puerto) del servidor del monitor, ya que el usuario de la aplicación Android deberá comunicarse con este nuevo servidor cuando escoja una sesión de ejercicios. La última fila de la tabla mostrada en la [Figura 11] muestra el contenido almacenado de una sesión activa.

Para que el servidor del monitor pueda acceder a la base de datos ubicada en el servidor es necesario que se disponga de un usuario con los permisos para insertar datos y realizar consultas en las tablas de la base de datos ‘gymserver’. Para ello debemos crear un usuario, que posteriormente se usará en el archivo de configuración del monitor. En el anexo ‘Código fuente del servidor’, se encuentra un archivo con todas las sentencias necesarias para preparar la base de datos del servidor.

Figura 10. Tablas que usamos en el sistema.

Figura 11. Contenido del historial de sesiones creadas

Page 39: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

19 Monitorización mediante streaming de vídeo para entrenamiento personal

3.2 Desarrollo

El servidor también debe responder a las solicitudes HTTP enviadas por los usuarios de la aplicación Android. Debe permitir el acceso a nuestro sistema (login) si el usuario utiliza un usuario y contraseña válidos y también debe dar información a estos usuarios de las sesiones a las que puede entrar. Además, debe proporcionar la información necesaria para que los clientes establezcan una nueva conexión con el servidor del monitor. Esto se logra con los ficheros PHP contenidos en la carpeta del servidor (‘gymserver’).

Código de requisito Descripción

R05 Comprobar identificación de la app Android

R06 Enviar información del monitor (IP y puerto)

Tabla 2. Requisitos del servidor

Para hacer nuestro sistema compatible con los trabajos anteriormente realizados se usan los ficheros ‘connectbd.php’ y ‘funciones_bd.php’ que nos proporcionan una serie de funciones útiles para realizar

consultas en nuestra base de datos, a la que se accede con los parámetros establecidos en el fichero ‘config.php’.

En el fichero ‘acces.php’ se lleva a cabo el intento de identificación del usuario utilizando la función login del archivo ‘funciones_bd.php’. Cuando realizamos la petición desde el dispositivo móvil es necesario enviar mediante el método POST el nombre y la contraseña introducidos por el usuario en el formulario de acceso. La respuesta que el servidor devuelve a la aplicación de Android es un objeto JSON con el id ‘logstatus’ y que

toma los valores 0 para una identificación errónea y 1 para una identificación correcta. Esto satisface el requisito R05 de nuestro servidor.

El último fichero PHP es ‘listasesiones.php’. Cuando se realiza esta petición no es necesario enviar ningún parámetro. El servidor realiza una consulta a la base de datos para obtener un listado de las sesiones activas. Y envía estos datos encapsulados en un array de tipo JSON, que almacena un objeto JSON por cada sesión activa. El objeto JSON almacena toda la información disponible de la sesión a la que identifica. Entre los datos que el servidor envía en cada objeto JSON es la dirección IP y el puerto del servidor del monitor (R06), por lo que el usuario de la aplicación móvil ya será capaz de comunicarse con el monitor para obtener información de la rutina y para comenzar a realizar el streaming de vídeo.

Page 40: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 41: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

21

4 APLICACIÓN MONITOR

a aplicación del monitor se monta sobre un servidor web con el que el cliente de la aplicación Android debe comunicarse una vez se ha escogido la sesión de ejercicios. El monitor usa la aplicación para crear la sesión a través de la interfaz web. Además, la interfaz web incluye otras opciones avanzadas como

control de usuarios y la edición de la rutina de ejercicios. También podrá ver al usuario de la aplicación Android a través de la cámara del dispositivo móvil mediante streaming de vídeo. Para ello será necesario disponer de un servidor de streaming (Wowza Streaming Engine) y un servidor HTTP.

La función de esta aplicación es la de liberar de carga al servidor principal, el que almacena toda la información de usuarios, ejercicios, etcétera. Por lo que es necesario que se almacene toda la información necesaria en archivos temporales, para que el servidor del monitor pueda responder él mismo a las peticiones de la aplicación Android, sin necesidad de consultar al servidor central. Además, debido al carácter temporal de las sesiones creadas por los monitores toda la información generada no será necesaria una vez se termine la sesión, por lo que se procederá a eliminar todos los archivos temporales.

Entre los requisitos del sistema encontramos:

Código de requisito Descripción

R07 Iniciar y cerrar sesión

R08 Crear y eliminar las sesiones

R09 Gestión de ejercicios

R10 Gestión de usuarios

R11 Recepción de streaming de vídeo

R12 Envío de mensajes de texto a los usuarios de la app Android

Tabla 3. Requisitos del servidor del monitor

L

Probamos por medio de la lógica, pero descubrimos

por medio de la intuición.

- Henri Poincaré -

Page 42: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Aplicación Monitor

22

4.1 Desarrollo

El requisito R07 se cumple con los archivos ‘index.php’ y ‘logout.php’. En el primer archivo se imprime un

formulario para que el monitor inicie sesión que puede verse en la [Figura 13].

Cuando el monitor introduce el usuario y contraseña se comprueba en la base de datos del servidor principal si son válidos. En caso de que no lo sean se recargará el formulario, si por el contrario las credenciales son válidas el monitor podrá acceder al sistema y continuar creando la sesión de ejercicios.

En el archivo ‘logout.php’ se lleva a cabo el cierre de sesión del monitor, pero también se comprueba si se ha

eliminado la sesión de ejercicios. Esto es necesario ya que se debe modificar el contenido del campo ‘activa’

de la tabla de sesiones de la base de datos y eliminar todos los archivos temporales que se crean mientras la sesión está disponible para los usuarios de la aplicación Android. En caso de que toda esta información no haya sido eliminada, se borran tanto los archivos como el registro de la base de datos del servidor principal. La acción de cerrar sesión se lleva a cabo cuando el monitor hace click sobre “Log out” en la cabecera de la

página de gestión de la sesión.

Para satisfacer el requisito R08 se usan los archivos ‘inicio.php’ y ‘killsession.php’. En ‘inicio.php’ se carga un

nuevo formulario en el que se nos solicita introducir los datos de la sesión que queremos crear (nombre de la sesión, fecha y número límite de usuarios) [Figura 14]. La sesión se registra en la tabla de sesiones de la base de datos.

Figura 12. Formulario de inicio de sesión de la interfaz web del monitor

Figura 13. Formulario de creación de la sesión

Page 43: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

23 Monitorización mediante streaming de vídeo para entrenamiento personal

El archivo ‘killsession.php’ elimina toda la información temporal que se ha generado en la sesión (información

de los ejercicios y de los usuarios) y modifica el registro de la tabla de sesiones de la base de datos estableciendo a 0 el campo ‘activa’. Esto se realiza cuando el monitor pulsa sobre “Cerrar sesión de ejercicios”

en la página de gestión de la sesión, ‘gestion.php’, que se carga cuando se ha creado la sesión. Esta página se muestra en la [Figura 15].

En esta página se muestra el listado de ejercicios activos de la sesión, el listado completo de ejercicios con el que el monitor puede añadir ejercicios a la sesión, y el listado de usuarios.

El listado completo de ejercicios se obtiene de la base de datos, mediante una consulta a la tabla de ejercicios de la base de datos del servidor principal. Estos ejercicios se muestran en el centro de la pantalla del monitor y a su derecha se muestra un icono para añadir el ejercicio a la sesión del monitor. Esto se realiza mediante el fichero ‘addejercicio.php’ que realiza una consulta a la base de datos para obtener toda la información

disponible del ejercicio y la almacena en un archivo llamado ‘info’ codificada en formato JSON.

Este fichero se almacena en una carpeta cuyo nombre es el número del identificador del ejercicio en la base de datos. A su vez, se realiza la descarga del vídeo explicativo del ejercicio en formato mp4 que se encuentra en el servidor principal, y se almacena en la misma carpeta. Así, cuando los usuarios reproduzcan en su aplicación Android el vídeo explicativo lo reproducirán desde el servidor del monitor, liberando la carga del servidor principal. Además de esta obtención de información del ejercicio, se almacena el ejercicio en una variable del ámbito de la sesión de PHP (de forma que no se elimina cuando se termina de ejecutar la petición HTTP).

Posteriormente, en el archivo ‘listaejercicios.php’, que realiza la carga del contenido del listado de ejercicios activos, se accede a esta variable y se imprimen todos los ejercicios activos. También se añade un icono para eliminar el ejercicio de la sesión. Este fichero también se encarga de la creación de un fichero ‘listaejerciciosjson.php’ donde se imprime en formato JSON los ejercicios activos, siendo esto lo que devuelve el servidor HTTP al usuario de la aplicación Android cuando solicita información de la rutina de la sesión. En la [Figura 16] se muestra el resultado tras añadir un ejercicio en la sesión.

Figura 14. Página de gestión de la sesión

Page 44: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Aplicación Monitor

24

Cuando se pulsa sobre el icono para eliminar el ejercicio se realiza una petición sobre el fichero ‘delejercicio.php’ que se encarga de eliminar el ejercicio de la variable donde se almacena el listado. Y

también, puesto que un mismo ejercicio puede añadirse más de una vez a la sesión, se comprueba cuantas veces se encontraba el ejercicio. En caso de que solo existiera una vez, se elimina la información almacenada de forma temporal (el vídeo explicativo y el archivo con la información del ejercicio en formato JSON). En la [Figura 17] se muestra cómo quedaría la sesión anterior tras añadir dos nuevos ejercicios y eliminar el primer ejercicio.

Estos cuatro archivos (‘addejercicio.php’, ‘listaejercicios.php’, ‘listaejerciciosjson.php’ y ‘delejercicio.php’)

Figura 15. Se ha añadido un ejercicio

Figura 16. Se ha eliminado un ejercicio

Page 45: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

25 Monitorización mediante streaming de vídeo para entrenamiento personal

nos permiten llevar a cabo la gestión de los ejercicios de la sesión, cumpliendo así con el requisito R09.

Para la gestión de usuarios (R10) hacemos uso de 5 archivos. Dos de ellos se piden mediante HTTP desde la aplicación Android y son: ‘reciberutina.php’ y ‘eliminausuario.php’. El primer archivo se pide cuando el

cliente de la aplicación solicita la información acerca de la rutina, en este archivo se comprueba que sea un nuevo usuario (podría ser una recarga del listado) y si existen plazas libres en la sesión.

En caso afirmativo, se le da la información de la rutina y se añade al usuario con el que se inició sesión en la aplicación Android en el listado. En la [Figura 18] puede verse el panel de gestión cuando un usuario ha entrado a la sesión.

Cuando el usuario cierra la actividad encargada de cargar el listado de ejercicios de la sesión se considera que el usuario ha querido abandonar la sesión, por lo que se realiza una petición sobre ‘eliminausuario.php’

enviando mediante el método POST el usuario, en el servidor se elimina a este usuario de la lista y se deja su plaza libre para otro usuario.

Los otros 3 archivos los solicita el monitor en su interfaz web. Uno de ellos es ‘listausuarios.php’ que se

encarga de cargar la lista de usuarios (tanto activos como expulsados) que puede verse en las páginas ‘gestion.php’ y ‘stream.php’. Carga iconos para llevar a cabo expulsiones del servidor, readmisiones de usuarios expulsados, y para comenzar a ver el streaming de vídeo del usuario.

Para realizar estas tres funciones es necesario almacenar información acerca del usuario (nombre de usuario y dirección IP del cliente). Esta información se almacena en los archivos temporales ‘usuariosactivos.json’ y

‘usuariosbaneados.json’ codificada en formato JSON.

Los dos archivos restantes que son solicitados desde la interfaz web del monitor son ‘ban.php’ y ‘unban.php’.

Estos ficheros se usan para expulsar y readmitir a usuarios en el servidor. Esto se hace añadiendo y eliminando la dirección IP del usuario de la aplicación Android en el archivo ‘.htaccess’ del servidor del monitor.

Denegando así las nuevas peticiones HTTP de este usuario. En la [Figura 19] se muestra como se ha expulsado a un usuario del servidor del monitor.

Figura 17. Un usuario ha entrado en la sesión

Page 46: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Aplicación Monitor

26

Por último, será en el archivo ‘stream.php’ donde se satisfagan los requisitos R11 y R12. A este fichero se

accede desde la interfaz web del monitor, y en ella se muestra el reproductor de JWPlayer donde se reproduce el streaming de vídeo del usuario que se desee observar, y un cuadro de texto para enviar mensajes al usuario de la aplicación Android. En la [Figura 20] se puede ver el contenido de la página ‘stream.php’.

Para enviar el mensaje de texto se usa el archivo ‘enviamsg.php’ que se encarga de enviar mediante un socket UDP el contenido del cuadro de texto.

Figura 18. El usuario ha sido expulsado del servidor

Figura 19. Página para ver streaming de vídeo y enviar mensajes al usuario

Page 47: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

27 Monitorización mediante streaming de vídeo para entrenamiento personal

Además de estos ficheros PHP se usa código en JavaScript con la intención de proporcionar una interfaz más dinámica y sencilla para el monitor. El archivo ‘enviamensaje.js’ nos permite enviar el mensaje hacia el cliente

de la aplicación Android sin necesidad de recargar la página del monitor, no cortando así la reproducción del streaming de vídeo. Además, permite enviar el mensaje cuando se detecta que el monitor ha pulsado la tecla “enter” sobre el cuadro de texto, facilitando así el manejo de la interfaz.

También se ha añadido código JavaScript en los ficheros PHP que nos permite cargar contenido de otros ficheros PHP en elementos “div” de otras páginas, además de recargar el listado de usuarios cada 5 segundos.

Por lo que el usuario verá cuando se conecta un nuevo usuario en un margen de cero a cinco segundos y sin necesidad de recargar la página. Algo que dota de mucho dinamismo a la interfaz del monitor.

Page 48: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 49: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

29

5 APLICACIÓN ANDROID

n este capítulo se profundizará en la fase de desarrollo de la aplicación desarrollada para Android, llamada ‘ProjectGym’. Esta aplicación será la que utilicen los usuarios para realizar una rutina de

ejercicios creadas por el monitor mientras éste los supervisa en tiempo real. Por lo que la función principal de la aplicación es realizar streaming de vídeo desde la cámara del dispositivo hacia el servidor del monitor. A continuación se especifican los requisitos de la aplicación:

Código de requisito Descripción

R13 Iniciar y cerrar sesión

R14 Obtener información de las sesiones

R15 Establecer comunicación con el monitor

R16 Recibir rutina

R17 Obtener información de ejercicios

R18 Realizar streaming de vídeo hacia el servidor

R19 Escuchar mensajes del monitor

Tabla 4. Requisitos de la aplicación de Android

Para realizar la función de streaming de vídeo haremos uso de una librería desarrollada por fyhertz, desarrollador de una de las aplicaciones de mayor éxito para realizar streaming desde un dispositivo Android, spydroid-ipcamera. Para hacer uso de esta librería debemos establecer como requisito que para usar la aplicación se debe disponer de un dispositivo con una versión Android compatible, como mínimo, con la API 15 (Ice Cream Sandwich).

E

Un hombre que no se alimenta de sus sueños envejece

pronto.

- William Shakespeare -

Page 50: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Aplicación Android

30

5.1 Librerias

En el desarrollo de la aplicación de Android he usado principalmente dos librerías, la primera es la clase Httppostaux heredada de los trabajos anteriores y que me ha facilitado la tarea de enviar peticiones HTTP, añadiendo los parámetros que hicieran falta en cada caso mediante el método POST, y de recibir la correspondiente respuesta del navegador.

De la segunda librería ya he hablado anteriormente, es la librería ‘libstreaming’ desarrollada por fyhertz. Esta librería nos brinda la posibilidad de hacer streaming de vídeo ejerciendo el dispositivo móvil como servidor RTPS o como cliente RTPS que envía el flujo a otro servidor.

Cómo ya se vio en la introducción se optó por usar la segunda forma, puesto que para incrustar el flujo RTPS directamente en la web del monitor sería necesario instalar un complemento al navegador, como por ejemplo vlc-player. Y esto podría generar en el futuro errores en el funcionamiento del sistema, por ejemplo que una actualización del navegador deje de soportar el complemento instalado.

Sin embargo, enviando el flujo RTPS hacia un servidor de streaming que lo procese y lo convierta a RTMP nos permite incrustar el streaming de vídeo en un reproductor flash (JWPlayer) que no necesita que el monitor realice ninguna modificación de la configuración de su navegador.

5.2 Desarrollo

Para ver el desarrollo de la aplicación de Android se irán describiendo las clases y layouts que intervienen en la aplicación por el orden en que aparecen cuando el usuario realiza una ejecución normal de la aplicación. La primera actividad, la que se lanza al ejecutar la aplicación, es ‘InicioSesion’. En ella se muestra un formulario [Figura 21] para que el usuario introduzca sus credenciales, estos se envían al servidor central mediante el método POST de HTTP. El servidor comprueba si las credenciales son válidas o no, y envía la correspondiente respuesta. En la aplicación móvil se procesa la respuesta, si ha sido positiva se carga la actividad ‘ListaSesiones’ cumpliendo el requisito R13.

Figura 20. Formulario inicio de sesión app Android

Page 51: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

31 Monitorización mediante streaming de vídeo para entrenamiento personal

En el caso de que el servidor no pueda verificar las credenciales que el usuario ha introducido en el formulario, envía una respuesta negativa mostrando la aplicación el correspondiente mensaje de error [Figura 22]

Además, el formulario nos da la posibilidad de almacenar los credenciales en las SharedPreferences de la aplicación, de forma que si seleccionamos la opción “Recordar usuario” la próxima vez que se ejecute la

aplicación se iniciará sesión de forma automática sin necesidad de rellenar el formulario.

En la actividad ‘ListaSesiones’ se envía una petición al servidor central para obtener el listado de sesiones activas (R14). El servidor responde con el contenido de la tabla de la base de datos ‘sesiones’ tras ser filtrada

por el campo ‘activa’ para obtener las filas que corresponden a sesiones activas. Esta actividad se encarga de obtener la respuesta, y de crear un listado con ella [Figura 23].

Figura 21. Formulario con mensaje de error

Page 52: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Aplicación Android

32

Además, se añade un detector de eventos, para detectar que el listado se estira hacia abajo. Cuando se detecta este evento se lanza de nuevo la petición, y se actualiza la lista de sesiones activas. En la [Figura 24] se muestra el resultado tras haber realizado la recarga del listado de sesiones activas.

También se añade un ‘listener’ a la lista para detectar las pulsaciones sobre los elementos de la lista. Cuando el

usuario pulsa sobre un elemento de la lista se accede a toda la información obtenida mediante la petición HTTP anterior. Esta información contiene la dirección IP y puerto en el que se encuentra el servidor del monitor, información totalmente necesaria ya que a partir de aquí nos comunicaremos directamente con el servidor del monitor. Tras obtener esta información se lanza la actividad ‘ListaEjercicios’.

En la actividad ‘ListaEjercicios’ se envía una petición al servidor del monitor para obtener el listado de ejercicios, que devuelve la información codificada en formato JSON. Tras recibir la respuesta la aplicación Android crea un listado, en el que cada elemento identifica a un ejercicio de la rutina.

En la [Figura 25] se muestra el resultado cuando se realiza una petición al servidor central y la tabla “sesiones”

no tiene ninguna sesión activa.

Figura 23. Lista de sesiones activas vacía Figura 22. Lista de sesiones con contenido

Page 53: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

33 Monitorización mediante streaming de vídeo para entrenamiento personal

Al igual que en la actividad anterior, se añaden un detector de eventos para actualizar la lista [Figura 26] y un ‘listener’ para detectar la elección de alguno de los elementos de la lista. Esta actividad nos asegura cumplir con los requisitos R15 y R16 de la tabla de requisitos de la aplicación Android.

Al detectar una pulsación sobre un elemento de la lista se carga la actividad ‘Ejercicio’. Esta actividad es la que realiza más tareas, ya que es la que se encarga de realizar el streaming de vídeo, obtener la información del ejercicio (texto y vídeo) y de quedar a la escucha de mensajes enviados por el monitor. Cumpliendo así los requisitos R18, R17 y R19 respectivamente.

La actividad reproduce el vídeo informativo del ejercicio, ubicado en el servidor del monitor, a pantalla completa [Figura 27].

Además se define que cuando el usuario pulse sobre la pantalla se aparezca un elemento ‘Dialog’ que muestra

toda la información disponible acerca del ejercicio, como la duración, la finalidad que se persigue y una descripción detallada sobre cómo realizar el ejercicio. Este elemento informativo puede observarse en la [Figura 28].

La información se obtiene mediante una petición HTTP al servidor del monitor, lanzada al comienzo de la actividad. El resultado devuelto por el servidor del monitor es la información codificada en formato JSON.

Figura 25. Rutina vacía Figura 24. Rutina actualizada

Page 54: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Aplicación Android

34

También inicia el streaming de vídeo haciendo uso de la librería ‘libstreaming’, para ello debe crear una sesión mediante la clase SessionBuilder en la que se especifican los parámetros que queremos establecer para nuestro streaming de vídeo. Entre estos parámetros podemos incluir el ancho y el alto del vídeo, la tasa de refresco de la imagen, el formato del codificador de audio y vídeo, la cámara que se desea utilizar, entre otros. También debe indicarse la ruta del servidor de streaming donde se publicará el flujo RTSP. En este caso se ha escogido

Figura 26. Reproducción del vídeo informativo

Figura 27. Información completa del ejercicio

Page 55: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

35 Monitorización mediante streaming de vídeo para entrenamiento personal

que el servidor de streaming se busque en el mismo equipo que el servidor del monitor, pero en el puerto 1935 que es el que usa Wowza Streaming Engine por defecto. Por lo que la ruta estará compuesta por la IP y el puerto del servidor de streaming, el path ‘/live/’ seguido del nombre del usuario que realiza el streaming. La ruta que se introduce en el reproductor flash para reproducir el flujo de datos ya convertido a RTMP sería ‘rtmp://IP_monitor:1935/live/usuario’. El usuario con el que se publica el flujo de datos RTSP también debe

estar creado en el servidor, se ha usado el usuario ‘antonio’ con contraseña ‘1234’.

Por último, se debe crear un socket UDP que quede a la escucha de mensajes enviados por el monitor a través del cuadro de texto incluido en la página ‘stream.php’ de la interfaz web del monitor. Cuando se recibe un mensaje, éste se muestra en un ‘Dialog’ en el que el usuario puede leer el mensaje enviado por el monitor que

lo está supervisando, tal y como puede verse en la [Figura 29]

Para continuar navegando por los ejercicios el usuario no tiene más que retroceder y escoger un nuevo ejercicio del listado. Además, debido a que el monitor debe tener el control de los usuarios activos en la sesión por cuestiones como el límite de plazas o el poder gestionar los usuarios expulsándolos o readmitiéndolos, el usuario debe avisar al monitor cuando abandona la sesión. Para ello se envía una petición HTTP al servidor monitor, pasando como parámetro el nombre del usuario, cuando el usuario destruye la actividad ‘ListaEjercicios’. Esto quiere decir, cuando el usuario abandona el listado de la rutina y vuelve al listado de sesiones disponibles, el servidor del monitor elimina al usuario como usuario activo de la sesión y deja libre una plaza para otro usuario interesado.

Figura 28. Recepción del mensaje del monitor

Page 56: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 57: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

37

6 CONCLUSIONES

a realización de este proyecto ha supuesto un reto al combinar varias tecnologías, muchas de ellas estudiadas en profundidad durante el trascurso de la carrera y otras sin embargo totalmente desconocidas hasta el momento de afrontar el proyecto.

Combina el desarrollo web y el desarrollo de aplicaciones para dispositivos Android, junto con la transmisión de vídeo a través de la red. La integración de todas las tecnologías involucradas en el proyecto ha sido uno de los primeros obstáculos que presentaba el proyecto. Además ya se contaba con parte del código y base de datos de un proyecto anterior, lo que ha supuesto realizar un estudio para reutilizar el código heredado. Otro de los escollos que presentaba el proyecto, era la transmisión de vídeo utilizando el protocolo RSTP en dispositivos móviles Android.

Se han seleccionado soluciones de código abierto o gratuitas (en el caso del servidor de streaming Wowza y del reproductor de vídeo flash JWPlayer) para la implementación de la compleja arquitectura del sistema.

6.1 Objetivos logrados

Una vez terminado el proyecto, debemos valorar el estado final de nuestro sistema, analizando si hemos alcanzados los objetivos que nos propusimos. Finalmente, los objetivos alcanzados son:

El monitor puede crear una rutina de ejercicios, eligiendo entre los ejercicios disponibles en la base de datos.

El monitor puede gestionar los usuarios de la sesión, expulsando y readmitiendo las conexiones de éstos.

El monitor puede ver mediante streaming de vídeo al usuario que realiza el ejercicio.

El monitor puede enviar mensajes al usuario de la aplicación Android, con el fin de informar al usuario sobre su progreso.

El usuario de la aplicación Android puede entrar y salir de las sesiones en cualquier momento, permitiendo así que el usuario pueda elegir el momento de realización de los ejercicios, ajustándose a su disponibilidad horaria.

L

Muy débil es la razón si no llega a comprender que hay

muchas cosas que la sobrepasan.

- Blaise Pascal-

Page 58: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Conclusiones

38

Tanto la aplicación web como la aplicación Android sólo pueden acceder al sistema si están correctamente acreditados.

Conseguimos liberar de carga al servidor central, el que posee la base de datos, al distribuir la carga entre los servidores de los distintos monitores que componen el sistema.

Lo que nos permite afirmar que el proyecto ha alcanzado los objetivos que nos marcamos al comienzo del diseño de éste.

6.2 Vías de mejora

Sin embargo, existen muchas formas de mejorar ambas aplicaciones, ya sea modificando el sistema o añadiendo funcionalidades extra. Algunas posibles mejoras serían:

Crear una nueva aplicación para el monitor que no sea web y que reciba el flujo de vídeo directamente en RTSP, eliminando así el servidor de streaming del sistema.

Crear una aplicación para gestionar la información de la base de datos para: crear y editar usuarios, añadir ejercicios, etcétera.

Añadir la posibilidad de que el monitor pueda guardar listas de ejercicios, para no tener que crear siempre las rutinas desde cero.

Añadir otras opciones para la comunicación entre el monitor y el usuario, por ejemplo mediante streaming de audio.

.

Page 59: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

39

7 BIBLIOGRFÍA

Juan Diego Gauchat, El Gran libro de HTML5, CSS3 y Javascript. Ed. S.A. Marcombo, 2013.

Rebecca Murphey, Fundamentos de jQuery. Ed. Bocoup LLC, 2011

Marko Gargenta, Learning Android. Ed. O'Reilly Media, 2011

Olivier Heurtel, PHP y MySQL. Ed. Eni, 2009

Douglas Crockford, JavaScript: The Good Parts. Ed. O'Reilly Media, 2008

Mohamed J. Kabir, Apache Server 2 Bible. Madrid: Anaya Multimedia, 2003

Fyhertz, Repositorio librería Android ‘libstreaming’. Disponible en: https://github.com/fyhertz/libstreaming

Fyhertz, Spydroid-ipcamera. Disponible en: https://play.google.com/store/apps/details?id=net.majorkernelpanic.spydroid

Google Inc., Documentación y recusos para Android. Disponible en: http://developer.android.com/index.html

StackOverflow, Recursos para programadores. Disponible en: http://stackoverflow.com/

Wowza Streaming Engine, Documentación. Disponible en: http://www.wowza.com/products/streaming-engine/documentation

JWPlayer, Documentación. Disponible en: http://support.jwplayer.com/

Mehdi Achour, Friedhelm Betz, otros, PHP Manual. Disponible en: http://php.net/manual/es/index.php

Jeff Gilfelt, ‘Android Action Bar Style Generator’. Disponible en: http://jgilfelt.github.io/android-actionbarstylegenerator/

Page 60: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 61: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

41

8 ANEXOS

8.1 Anexo I: Código fuente

8.1.1 Código fuente Servidor

config.php <?php define("DB_HOST", "localhost");//Nombre del host define("DB_USER", "root");//Nombre usuario de la base de datos define("DB_PASSWORD", "");//Contraseña de la base de datos define("DB_DATABASE", "gymserver");//Nombre de la base de datos. ?>

listasesiones.php <?php require_once 'config.php'; $host=DB_HOST; $username=DB_USER; $password=DB_PASSWORD; $db_name=DB_DATABASE; $i =0; //Conecta con la base de datos $con=mysql_connect("$host", "$username", "$password")or die("cannot connect"); mysql_select_db("$db_name")or die("cannot select DB"); //Realiza la sentencia SQL para obtener todas las listas que estan activas $sql = "SELECT * FROM sesiones WHERE active=1"; $result = mysql_query($sql); $json = array(); //Obtiene los valores obtenidos y los almacena en un array mediante un bucle if(mysql_num_rows($result)){ while($row=mysql_fetch_assoc($result)){ $json[$i]=$row; // Se almacena la información de la sesión en la posicion i del array $json $i++; } } //Cierra conexion mysql_close($con); //Codifica en JSON echo json_encode($json); ?>

sentenciassql.sql -- -- Base de datos: `gymserver` -- CREATE DATABASE IF NOT EXISTS `gymserver` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; USE `gymserver`; -- -- Elimina las tablas, si existen -- DROP TABLE IF EXISTS `explicacionesdeejercicios`; DROP TABLE IF EXISTS `usuarios`; DROP TABLE IF EXISTS `sesiones`; DROP TABLE IF EXISTS `sesiones_activas`; DROP TABLE IF EXISTS `ejercicios`; -- -- Crea el usuario que se usa desde el servidor del monitor -- CREATE USER 'monitor'@`%` IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON gymserver.* TO `monitor`@'%' WITH GRANT OPTION; -- -- Estructura de tabla para la tabla `ejercicios` -- CREATE TABLE IF NOT EXISTS `ejercicios` ( `Id_Ejercicio` int(11) NOT NULL AUTO_INCREMENT, `Descripcion` varchar(120) DEFAULT NULL, `Finalidad` varchar(45) DEFAULT NULL, `Duracion` varchar(45) DEFAULT NULL, PRIMARY KEY (`Id_Ejercicio`)

Page 62: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

42

) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=21 ; -- -- Volcado de datos para la tabla `ejercicios` -- INSERT INTO `ejercicios` (`Id_Ejercicio`, `Descripcion`, `Finalidad`, `Duracion`) VALUES (1, 'Aumentar y levantar gluteos', 'Desarrollo muscular', '4 series de 10 repeticiones'), (2, 'Burpees', 'Desarrolo muscular de pectorales', '4 series de 10 repeticiones'), (3, 'Flexiones Spartan', 'Desarrollo muscular de pectorales', '4 series de 10 repeticiones'), (4, 'Flexiones', 'Desarrollo muscular de pectorales', '4 series de 10 repeticiones'), (5, 'Flexiones explosivas', 'Desarrollo y concentracion de biceps', '4 series de 10 repeticiones'), (6, 'Abdominales (8 minutos)', 'Sesion intensa de abdominales', '8 minutos'), (7, 'Flexiones Hindu', 'Desarrollo muscular de pectorales', '4 series de 10 repeticiones'), (8, 'Levantar piernas en posicion horizontal', 'Calentamiento piernas', '3 series de 10 repeticiones'), (9, 'Press frances con mancuernas', 'Desarrollo de triceps y brazos', '3 series de 10 repeticiones'), (10, 'Sesion fitness (8 minutos)', 'Desarrollo del abdomen', '8 minutos'), (11, 'Elevaciones laterales con mancuernas', 'Desarrollo de espalda y dorsales', '3 series de 10 repeticiones'), (12, 'Remo horizontal con mancuernas ', 'Desarrollos musculos del hombro', '3 series de 10 repeticiones'), (13, 'Abdominales', 'Desarrollo de abdominales', '3 series de 10 repeticiones'), (14, 'Curl de biceps alterno en pie', 'Desarrollo de biceps', '3 series de 10 repeticiones'), (15, 'Flexines entre dos bancos', 'Desarrollo de triceps', '3 series de 10 repeticiones'), (16, 'Curls en pie con barra', 'Desarrollo de biceps y lumbares', '3 series de 10 repeticiones'), (17, 'Curl antebrazos en supinacion con barra', 'Desarrollo de antebrazos', '3 series de 10 repeticiones'), (18, 'Elevaciones laterales tronco inclinado', 'Desarrollo de hombros', '3 series de 10 repeticiones'), (19, 'Sentadillas', 'Desarrollo de cuadriceps', '3 series de 10 repeticiones'), (20, 'Sentadillas con barra ', 'Desarrollo de cuadriceps', '3 series de 10 repeticiones'); -- -- Estructura de tabla para la tabla `explicacionesdeejercicios` -- CREATE TABLE IF NOT EXISTS `explicacionesdeejercicios` ( `Id_Ejercicio` int(11) NOT NULL, `descripcion` varchar(1000) DEFAULT NULL, FOREIGN KEY (`Id_Ejercicio`) REFERENCES `ejercicios` (`Id_Ejercicio`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- -- Volcado de datos para la tabla `explicacionesdeejercicios` -- INSERT INTO `explicacionesdeejercicios` (`Id_Ejercicio`, `descripcion`) VALUES (1, 'Sentado,con los antebrazos apoyados en los muslos,sostenemos una barra con las palmas de las manos \r\nhacia arriba y flexionamos hacia atras las palmas de las manos.'), (2, 'Pongase en cuclillas, hasta apoyar las manos en el suelo. Estire las piernas con un saltito. Manteniendo los codos pegados al busto haga una flexion de brazos. Suba y, al mismo tiempo que da otro saltito, acerque nuevamente las piernas al cuerpo. Hecho esto, vuelva a la posicion erguida.'), (3, 'Ponte de pie y mirando hacia la maquina, agarra la cuerda con ambas manos, inspira y realiza la extension del codo llevando la cuerda hacia abajo, hacia tus caderas. Espira el aire al final del ejercicio. '), (4, 'De pie, con las piernas separadas a la anchura de los hombros, sosten una barra Z, cogiendola por el agarre abierto (no por el centro). Flexiona los brazos haciendo subir la barra hasta el pecho \r\ny luego inicia el movimiento de descenso controlado a la posicion original. \r\nDebes mantener tu postura recta durante todo el ejercicio.'), (14, 'En pie, las piernas separadas el ancho de los hombros. Tronco recto. Brazos completamente estirados en los costados. Manteniendo el brazo y el hombre inmoviles, doble el antebrazo. Durante el movimiento, el codo debe estar inmovil, para evitar que trabajen los musculos frontales del hombro y aislar la carga de trabajo en el biceps. Vuelva a la posicion inicial y repita el mismo movimiento con el otro brazo.'); -- -- Estructura de tabla para la tabla `usuarios` -- CREATE TABLE IF NOT EXISTS `usuarios` ( `Id_Usuario` int(11) NOT NULL AUTO_INCREMENT, `nombre` varchar(30) CHARACTER SET utf8 DEFAULT NULL, `apellidos` varchar(30) CHARACTER SET utf8 DEFAULT NULL, `dni` varchar(10) CHARACTER SET utf8 DEFAULT NULL, `direccion` varchar(40) CHARACTER SET utf8 DEFAULT NULL, `movil` varchar(20) CHARACTER SET utf8 DEFAULT NULL, `username` varchar(20) CHARACTER SET utf8 NOT NULL, `passw` varchar(20) CHARACTER SET utf8 NOT NULL, PRIMARY KEY (`Id_Usuario`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=8 ; -- -- Volcado de datos para la tabla `usuarios` -- INSERT INTO `usuarios` (`Id_Usuario`, `nombre`, `apellidos`, `dni`, `direccion`, `movil`, `username`, `passw`) VALUES (1, 'Antonio', 'Clavain Mateo', '77172251-Y', '[email protected]', '635093456', 'antonio', '1234'), (2, 'Monitor', 'Apellidos', '22222222-R', '[email protected]', '605786714', 'monitor', '1234'), (3, 'Lucas', 'Lelorean', '12341234S', 'C/ Martir berasategui', '223344556', 'prueba', '1234'), (4, 'usuarionoregistrado', NULL, NULL, NULL, NULL, 'user0', 'root'), (5, 'Antonio', 'Apellidos', '22222222-R', '[email protected]', '605786714', 'antonio_monitor', '1234'); -- -- Creamos la tabla que almacena el historial de sesiones -- CREATE TABLE IF NOT EXISTS `sesiones` (

Page 63: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

43 Monitorización mediante streaming de vídeo para entrenamiento personal

`Id_Sesion` int(11) NOT NULL AUTO_INCREMENT, `nombre_sesion` varchar(45) DEFAULT NULL, `user_monitor` varchar(45) DEFAULT NULL, `fecha` varchar(45) DEFAULT NULL, `num_usuarios` int(11) DEFAULT NULL, `ip_monitor` varchar(20) DEFAULT NULL, `activa` Boolean DEFAULT TRUE, PRIMARY KEY (`Id_Sesion`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

8.1.2 Código fuente del Monitor

addejercicio.php

<?php session_start(); set_time_limit(0); // Establece el tiempo máximo de ejecución ilimitado // Puesto que se recibe el numero del ejercicio y el nombre // en la misma variable, se divide en un array que contiene // el Id_Ejercicio y la descripcion (nombre del ejercicio) // Además, este array se añade a la variable $_SESSION['lista'] $array = explode(' ', $_POST['numejercicio'], 2); $array['Id_Ejercicio'] = $array[0]; unset($array[0]); $array['descripcion'] = $array[1]; unset($array[1]); if(isset($_SESSION['lista'])){ array_push($_SESSION['lista'], $array); } else{ $_SESSION['lista'] = array($array); } // Obtiene toda la información acerca del ejercicio de la // base de datos del servidor, y se crea un fichero donde // se almacena esta información en formato JSON. // Se crea un directorio cuyo nombre es el Id_Ejercicio // para almacenar tanto la información como el vídeo require_once 'funciones_bd.php'; $id = $array['Id_Ejercicio']; $db = new funciones_BD(); $sql = "SELECT ejercicios.Id_Ejercicio,ejercicios.Descripcion, ejercicios.Finalidad,ejercicios.Duracion,explicacionesdeejercicios.descripcion FROM ejercicios INNER JOIN explicacionesdeejercicios ON ejercicios.Id_Ejercicio=explicacionesdeejercicios.Id_Ejercicio WHERE ejercicios.Id_Ejercicio='$id'"; $result = mysql_query($sql); $json[0] = mysql_fetch_assoc($result); if(!is_dir("ejercicios/")){ mkdir("ejercicios/"); } if(!is_dir("ejercicios/$id")){ mkdir("ejercicios/$id"); } $file = fopen("ejercicios/$id/info","w+"); fwrite($file, json_encode($json,true)); fclose($file); // Realiza la descarga del video, que se encuentra en el servidor $url = 'http://'.DB_HOST."/gymserver/ejercicios/$id/video.mp4"; $newfname = "ejercicios/$id/video.mp4"; $file = fopen ($url, "rb"); if($file) { $newf = fopen ($newfname, "wb"); if($newf) while(!feof($file)) { fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 ); } } if($file) { fclose($file); } if($newf) { fclose($newf); } // Carga de nuevo la página de gestión de la sesión header('Location: gestion.php'); exit();

Page 64: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

44

ban.php <?php $user = $_POST['nomusuario']; // Elimina el usuario del archivo codificado en JSON donde se almacena su nombre y su IP $file = fopen("usuariosactivos.json","r+"); $json = json_decode(fread($file,filesize("usuariosactivos.json")),true); fclose($file); $newarray = array(); $count = count($json); for($i=0;$i<$count;$i++){ if($json[$i]['nombre']!=$user){ array_push($newarray, $json[$i]); }else{ $ip = $json[$i]['ip']; // Almacena la IP del usuario, para bloquearla posteriormente. } } unlink("usuariosactivos.json"); // Borra el contenido anterior $file = fopen("usuariosactivos.json","a+"); fwrite($file, json_encode($newarray,true)); // Escribe el nuevo fichero sin el usuario baneado fclose($file); // Borra el usuario de usuarios.php (Para que no se muestre en el panel) $file = fopen("usuarios.php","r+"); $newfile = fopen("usuarios2.php","a+"); while (($line = fgets($file)) !== false) { if(strpos($line,$user) == false){ fwrite($newfile, $line); } } fclose($newfile); fclose($file); unlink("usuarios.php"); rename("usuarios2.php","usuarios.php"); // Añade el usuario en usuariosbaneados.php (Para que se muestre en el panel) $file = fopen("usuariosbaneados.php","a+"); fwrite($file, "<li>".$user." <input type='image' src='check.png' name='nomusuario' value='$user'>\r\n"); fclose($file); // Añade el usuario baneado a un fichero json: usuariosbaneados.json $file = fopen("usuariosbaneados.json","r+"); $json = json_decode(fread($file,filesize("usuariosbaneados.json")),true); fclose($file); $count = count($json); $json[$count] = array('nombre' => $user, 'ip' => $ip); unlink("usuariosbaneados.json"); // Borra el contenido anterior $file = fopen("usuariosbaneados.json","a+"); fwrite($file, json_encode($json,true)); // Escribe el fichero añadiendo el usuario baneado fclose($file); // Añade el usuario como deny en .htaccess para denegar el acceso al servidor $file = fopen(".htaccess","a+"); fwrite($file, "\r\ndeny from $ip"); fclose($file); // Recarga el panel de monitor header('Location: gestion.php'); exit(); ?>

config.php <?php define("DB_HOST", "192.168.16.132"); // Dirección IP del servidor define("DB_USER", "monitor"); // Nombre de usuario de la base de datos (usuario con permisos, se debe poder insetar filas y hacer consultas) define("DB_PASSWORD", "password"); // Contraseña del usuario introducido define("DB_DATABASE", "gymserver"); // Nombre de la base de datos. ?>

delejercicio.php <?php session_start(); $count = count($_SESSION['lista']); // Se vacia $_SESSION['lista'] haciendo una copia en $array $array = $_SESSION['lista']; unset($_SESSION['lista']); $_SESSION['lista'] = array(); // Se almacena en $id el id del ejercicio que se desea eliminar $id = $array[$_POST['numejercicio']]['Id_Ejercicio']; $borrar = 0; // Bucle para recorrer toda la lista, añadiendo en $_SESSION['lista'] los ejercicios que no se han borrado for($i=0;$i<$count;$i++){ if($i != $_POST['numejercicio']){ // Se añaden todas las posiciones del array excepto sobre la que se ha pulsado

Page 65: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

45 Monitorización mediante streaming de vídeo para entrenamiento personal

array_push($_SESSION['lista'], $array[$i]); } if($id == $array[$i]['Id_Ejercicio']){ $borrar++; // Como un ejercicio se puede repetir en la rutina, comprobamos cuantas veces existe el ejercicio } } if($borrar == 1){ // Si el ejercicio solo existia una vez en la rutina borramos la info y el video unlink("ejercicios/$id/video.mp4"); unlink("ejercicios/$id/info"); rmdir("ejercicios/$id"); } header('Location: gestion.php'); exit();

eliminausuario.php <?php // Esta página php se solicita desde la aplicación móvil cuando se abandona // la sesión. Sirve para eliminar al usuario de la lista de usuarios activos. // Se debe pasar como parámetro el usuario del cliente. $user = $_POST['user']; // Elimina el usuario del archivo codificado en JSON donde se almacena la IP $file = fopen("usuariosactivos.json","r+"); $json = json_decode(fread($file,filesize("usuariosactivos.json")),true); fclose($file); $newarray = array(); $count = count($json); // Se recorre toda la lista de usuarios activos for($i=0;$i<$count;$i++){ if($json[$i]['nombre']!=$user){ array_push($newarray, $json[$i]); // Si el usuario no es el que deseamos // borrar se añade al nuevo array. } } unlink("usuariosactivos.json"); // Borra el contenido anterior $file = fopen("usuariosactivos.json","a+"); fwrite($file, json_encode($newarray,true)); // Escribe el nuevo fclose($file); // Borra el usuario de usuarios.php (Para que no se muestre en el panel) $file = fopen("usuarios.php","r+"); $newfile = fopen("usuarios2.php","a+"); while (($line = fgets($file)) !== false) { if(strpos($line,$user) == false){ fwrite($newfile, $line); } } fclose($newfile); fclose($file); unlink("usuarios.php"); rename("usuarios2.php","usuarios.php"); ?>

enviamsg.php <?php // Código ejecutado cuando el monitor pulsa el boton de enviar // o teclea la letra enter. Recibe como parámetro el usuario // y el mensaje a transmitir. $user = $_POST['user']; $message = $_POST['msg']; // Primero debemos obtener la IP del usuario al que queremos enviar el mensaje $file = fopen("usuariosactivos.json","r+"); $json = json_decode(fread($file,filesize("usuariosactivos.json")),true); fclose($file); $count = count($json); // Bucle para localizar al usuario for($i=0;$i<$count;$i++){ if($json[$i]['nombre']==$user){ // Se obtiene la ip del usuario al que queremos enviar el mensaje $ip = $json[$i]['ip']; } } // Puerto establecido en la aplicación móvil para quedar a la espera de mensajes $server_port = 12345; if ($socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP)) { // Crea socket udp // Se envia el mensaje a la ip y puerto del usuario socket_sendto($socket, $message, strlen($message), 0, $ip, $server_port); } ?>

Page 66: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

46

estilo.css body { background-color: #FFA44D; height:97%; } html{margin:0;padding:0;height:100%;} #formsession{ position: absolute; top: 50%; left: 50%; height: 30%; width: 20%; margin: -15% 0 0 -10%; } #recuadroformulario{ background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; width: 80%; } #formulario{ width: 80%; height: 40%; margin-top: 10%; } #divformuser{ height: 20%; width: 100%; padding-top: 3%; } #divformpass{ height: 20%; width: 100%; padding-top: 3%; } #formbutton{ background-color: #FFA44D; font-weight: bold; width: 50%; height: 20%; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 5px; cursor: pointer; } #formbutton:active{ text-shadow: 0 -1px 0 #444, 0 0 5px #ffd, 0 0 8px #fff; box-shadow: 0 1px 0 #666, 0 2px 0 #444, 0 2px 2px rgba(0,0,0,0.9); -webkit-transform: translateY(3px); transform: translateY(3px); } #footer2{ font-weight: lighter; font-size: 10px; clear: both; position: relative; z-index: 10; height: 3em; float: right; } #footer{ font-weight: lighter; font-size: 10px; clear: both; position: absolute; z-index: 10; height: 3em; float: right;

Page 67: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

47 Monitorización mediante streaming de vídeo para entrenamiento personal

top: 97%; left: 100%; height: 6px; width: 200px; margin: -5px 0 0 -200px; } header { display:block; background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; width: 60%; margin-bottom: 2%; } #welcome{ margin: 2%; } #logout{ float: right; margin-right: 2%; } #ejerciciosactivos{ float: left; margin-right: 3%; margin-left: 5%; display:block; background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; padding: 2%; } #listado{ display:block; background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; float: left; padding: 2%; margin: 0 2%; width: 25%; } #usuariosactivos{ float: right; margin-left: 3%; margin-right: 5%; display:block; background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; padding: 2%; } #formsesion{ background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; position: absolute; top: 50%; left: 50%; height: 30%; width: 50%; margin: -10% 0 0 -25%; padding: 10px; } #divejercicio{

Page 68: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

48

padding-top: 10px; } #stream{ float: right; margin-left: 5%; margin-right: 10%; background-color: #FFFFFF; -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; padding: 2%; }

gestion.php <?php session_start(); ?> <html> <head> <meta charset="ISO-8859-1"> <title>Gestión de la sesión</title> <link href="estilo.css" rel="stylesheet" type="text/css"> <link rel="icon" type="image/png" href="favicon.png" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script type="text/javascript"> // Establece un intervalo de recarga cada 5 segundos para cargar el elemento #usuariosactivos // Cuando se carga gestion.php incrusta el contenido de listausuarios.php y listaejercicios.php en los elementos correspondientes $().ready(function() { $("#usuariosactivos").load("listausuarios.php"); $("#ejerciciosactivos").load("listaejercicios.php"); setInterval("recarga()",5000); }); function recarga(){ // Sobreescribe el contenido del elemento cuyo id es #usuariosactivos // con los datos obtenidos de la consulta a la página listausuarios.php jQuery.post("listausuarios.php",function( data ) { jQuery("#usuariosactivos").html(data); }); } </script> </head> <body> <center> <header> <span id="welcome"> Bienvenido <?php echo (isset($_SESSION['user']) ? $_SESSION['user'] : "Visitor"); ?> - <?php echo (isset($_SESSION['sesion']) ? $_SESSION['sesion'] : "Sesion"); ?> <a href="killsession.php">Cerrar sesión de ejercicios</a> </span> <span id="logout"> <a href="logout.php">Log out</a> </span> </header> </center> <aside id="ejerciciosactivos"> </aside> <section id="listado"> <form action="addejercicio.php" method="POST"> <?php // Realiza la consulta a la base de datos del servidor para obtener la lista de ejercicios require_once 'funciones_bd.php'; $db = new funciones_BD(); $sql = "SELECT ejercicios.Id_Ejercicio, ejercicios.descripcion FROM ejercicios"; $result = mysql_query($sql); if(mysql_num_rows($result)){ $i=1; // Bucle para añadir todo el listado de ejercicios while($row=mysql_fetch_assoc($result)){ // Por cada ejercicio se coloca el Id_Ejercicio, el nombre, y un icono que cuando se hace click sobre el añade el ejercicio a la lista de ejercicios ?> <div id="divejercicio"> <span id="numero"><?php echo $row['Id_Ejercicio'];?></span> <span id="ejercicio"><?php echo $row['descripcion'];?></span> <input style="float: right;" type="image" src="plus.png" name="numejercicio" value="<?php echo $row['Id_Ejercicio']." ".$row['descripcion']; ?>"> </div> <?php $i++;

Page 69: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

49 Monitorización mediante streaming de vídeo para entrenamiento personal

} } ?> </form> </section> <aside id="usuariosactivos"> </aside> </body> <footer id="footer">Creado por Antonio Clavaín Mateo</footer> </html>

index.php <?php session_start(); ?> <html> <head> <meta charset="utf-8"> <title>Index - Monitor de gym</title> <link href="estilo.css" rel="stylesheet" type="text/css"> <link rel="icon" type="image/png" href="favicon.png" /> </head> <body> <?php // Si no se ha enviado aún el formulario se carga el formulario if (!isset($_POST['setsession'])){ ?> <div id="formsession"> <center> <img src="logo.png"> <div id="recuadroformulario"> <form action="index.php" method="POST"> <div id="formulario"> <div id="divformuser"> <input type="text" name="user" id="user" autofocus="autofocus" placeholder="User" /><br> </div> <div id="divformpass"> <input type="password" name="pass" id="pass" placeholder="Password"/> </div> <input type="hidden" name="setsession" value="ok"/><br> <input id="formbutton" type="submit" value="Sign in" /> </div> </form> </div> </center> </div> <?php } else{ // Si se ha enviado el formulario se carga este bloque $usuario = $_POST['user']; $passw = $_POST['pass']; require_once 'funciones_bd.php'; $db = new funciones_BD(); // Se comprueba en la base de datos si el usuario y la contraseña son válidos if(!$db->login($usuario,$passw)){ // Si se produce un error se carga de nuevo el formulario unset($_POST['setsession']); header('Location: '.$_SERVER['PHP_SELF']); exit(); } else { // Si el usuario y la contraseña son válidos se carga la pagina de inicio (nuevo formulario para crear la sesion) $_SESSION['user'] = $usuario; header('Location: inicio.php'); exit(); } } ?> <div style="width: 40%;"></div> <footer id="footer">Creado por Antonio Clavaín Mateo</footer> </body> </html>

inicio.php <?php session_start(); ?> <html>

Page 70: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

50

<head> <meta charset="ISO-8859-1"> <title>Index - Monitor de gym</title> <link href="estilo.css" rel="stylesheet" type="text/css"> <link rel="icon" type="image/png" href="favicon.png" /> </head> <body> <?php // Si se ha enviado el formulario if(isset($_POST['createsession'])){ $_SESSION['sesion'] = $_POST['nomsesion']; $sesion = $_SESSION['sesion']; $user = $_SESSION['user']; $expdate = $_POST['expdate']; $numusers = $_POST['numusers']; $_SESSION['maxusuarios'] = $numusers; $host= gethostname(); $ip = gethostbyname($host); $ip = $ip . ":" . $_SERVER['SERVER_PORT']; // Inserta la sesión en la tabla que registra el historial de todas las sesiones que se han creado en el servidor require_once 'funciones_bd.php'; $db = new funciones_BD(); $sql = "INSERT INTO sesiones (nombre_sesion, user_monitor, fecha, num_usuarios, ip_monitor, activa) VALUES ('$sesion','$user','$expdate','$numusers', ‘$ip’, 1)"; if(mysql_query($sql)){ header('Location: gestion.php'); exit(); }else{ unset($_POST['createsession']); header('Location: inicio.php'); exit(); } } else{ // Si no se ha enviado el formulario, o si ha habido algun error a la hora de insertar // la sesión en la base de datos, se carga el formulario ?> <center> <header> <span id="welcome"> Welcome <?php echo (isset($_SESSION['user']) ? $_SESSION['user'] : "Visitor"); ?> </span> <span id="logout"> <a href="logout.php">Log out</a> </span> </header> </center> <section id="formsesion"> <form action="inicio.php" method="POST"> <center> Nombre de la sesión: <input style="margin: 2%;" type="text" name="nomsesion" id="nomsesion" autofocus="autofocus" placeholder="Nombre de la sesión" /><br> Fecha de expiración: <input style="margin: 2%;" type="text" name="expdate" id="expdate" placeholder="dd/mm/yyyy HH:mm"/> Número máximo de usuarios: <input style="margin: 2%;" type="text" name="numusers" id="numusers" placeholder="20"/> <input type="hidden" name="createsession" value="ok"/><br> <input style="margin: 2%;" id="formbutton" type="submit" value="Aceptar" /> </center> </form> </section> <div style="width: 200px;"> </div> <footer id="footer">Creado por Antonio Clavaín Mateo</footer> </body> </html> <?php } ?>

killsession.php <?php session_start(); // Elimina la sesión de la tabla de sesiones activas para los usuarios require_once 'funciones_bd.php'; $db = new funciones_BD(); $user = $_SESSION['user']; $sql = "UPDATE sesiones SET active=0 WHERE user_monitor='$user' AND activa=1"; if( mysql_query($sql)){ unset($_POST['setsession']); unset($_SESSION['lista']);

Page 71: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

51 Monitorización mediante streaming de vídeo para entrenamiento personal

// Borra el archivo donde se almacenan los usuarios conectados if(is_file("usuarios.php")){ unlink("usuarios.php"); } // Borra el archivo donde se almacenan los usuarios baneados if(is_file("usuariosbaneados.php")){ unlink("usuariosbaneados.php"); } // Borra el archivo donde se almacenan los usuarios conectados en formato JSON if(is_file("usuariosactivos.json")){ unlink("usuariosactivos.json"); } // Borra el archivo donde se almacenan los usuarios baneados en formato JSON if(is_file("usuariosbaneados.json")){ unlink("usuariosbaneados.json"); } // Borra el archivo donde se almacenan los ejercicios añadidos a la rutina en formato JSON if(is_file("listaejerciciosjson.php")){ unlink("listaejerciciosjson.php"); } // Borra todo el contenido de la carpeta ejercicios $directories = glob('ejercicios/*'); foreach($directories as $directory){ unlink($directory."/info"); unlink($directory."/video.mp4"); rmdir($directory); } // Reestablece el archivo .htaccess copy(".htaccess.back",".htaccess"); // Carga la web de inicio header('Location: inicio.php'); exit(); }else{ // No se ha podido borrar de la BBDD header('Location: gestion.php'); exit(); } ?>

listaejercicios.php <?php session_start(); // El contenido de este fichero se incrusta en el elemento #ejerciciosactivos (en gestion.php) cada vez que se carga la página gestion.php // Escribe en el fichero listaejerciciosjson.php en formato JSON el contenido de la variable $_SESSION['lista'], // donde se almacenan los ejercicios de la rutina $count = 0; if(isset($_SESSION['lista'])){ $count = count($_SESSION['lista']); $file = fopen("listaejerciciosjson.php","w"); fwrite($file, json_encode($_SESSION['lista'],true)); fclose($file); } ?> Lista de ejercicios activos <form action="delejercicio.php" method="POST"> <ul> <?php // Por cada ejercicio activo se escribe en un listado su id, su nombre y un icono que al ser pulsado elimina el ejercicio de la lista for($i=0;$i<$count;$i++){ echo "<li>".$_SESSION['lista'][$i]['Id_Ejercicio']." ".$_SESSION['lista'][$i]['descripcion']; ?> <input type="image" src="circle-x.png" name="numejercicio" value="<?php echo $i; ?>"> <?php } ?> </ul> </form>

listausuarios.php <?php session_start(); error_reporting(E_ERROR | E_PARSE); // El contenido de este fichero se incrusta en el elemento #usuariosactivos (en gestion.php) // cada 5 segundos y cada vez que se carga la página gestion.php ?> <form action="ban.php" method="POST"> Lista de usuarios activos (

Page 72: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

52

<?php // Carga el contenido almacenado en usuariosactivos.json (Los usuarios que están conectados al servidor del monitor en formato JSON) if(is_file("usuariosactivos.json")){ $file = fopen("usuariosactivos.json","r+"); $json = json_decode(fread($file,filesize("usuariosactivos.json")),true); $count = count($json); fclose($file); }else{ $count = 0; } // Muestra el numero de usuarios activos frente al numero máximo de usuarios echo $count."/".$_SESSION['maxusuarios']." )"; ?> <ul> <?php // Imprime un listado con los usuarios activos if(is_file("usuarios.php")){ include("usuarios.php"); } ?> </ul> </form> <form action="unban.php" method="POST"> Usuarios baneados: <ul> <?php // Imprime un listado con los usuarios baneados if(is_file("usuariosbaneados.php")){ include("usuariosbaneados.php"); } ?> </ul> </form>

logout.php <?php session_start(); // Se encarga de cerrar la sesión del monitor if(isset($_SESSION['lista'])){ require_once 'funciones_bd.php'; $db = new funciones_BD(); $user = $_SESSION['user']; $sql = " UPDATE sesiones SET active=0 WHERE user_monitor='$user' AND activa=1"; if( mysql_query($sql)){ unset($_POST['setsession']); unset($_SESSION['lista']); // Borra el archivo donde se almacenan los usuarios conectados if(is_file("usuarios.php")){ unlink("usuarios.php"); } // Borra el archivo donde se almacenan los usuarios baneados if(is_file("usuariosbaneados.php")){ unlink("usuariosbaneados.php"); } // Borra el archivo donde se almacenan los usuarios conectados en formato JSON if(is_file("usuariosactivos.json")){ unlink("usuariosactivos.json"); } // Borra el archivo donde se almacenan los usuarios baneados en formato JSON if(is_file("usuariosbaneados.json")){ unlink("usuariosbaneados.json"); } // Borra el archivo donde se almacenan los ejercicios añadidos a la rutina en formato JSON if(is_file("listaejerciciosjson.php")){ unlink("listaejerciciosjson.php"); } // Borra todo el contenido de la carpeta ejercicios $directories = glob('ejercicios/*'); foreach($directories as $directory){ unlink($directory."/info"); unlink($directory."/video.mp4"); rmdir($directory); } // Reestablece el archivo .htaccess copy(".htaccess.back",".htaccess"); }

Page 73: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

53 Monitorización mediante streaming de vídeo para entrenamiento personal

} unset($_POST['setsession']); unset($_SESSION['user']); header('Location: index.php'); exit(); ?>

recibeejercicio.php <?php // Devuelve el contenido del fichero info (en formato JSON) del ejercicio cuyo id se pasa como parametro mediante POST $id = $_POST['id_ejercicio']; include("ejercicios/$id/info"); ?>

reciberutina.php

<?php session_start(); // Esta página se solicita desde la aplicación para Android. // Devuelve el listado de los ejercicios de la sesión. $user = $_POST['user']; $ip = $_SERVER['REMOTE_ADDR']; $monitor = $_POST['user_monitor']; // Obtiene el numero máximo de usuarios de la sesión consultando a la base de datos del servidor require_once 'funciones_bd.php'; $db = new funciones_BD(); $sql = "SELECT `num_usuarios` FROM sesiones WHERE `user_monitor`='$monitor' AND activa=1"; $result = mysql_query($sql); $maxusuarios=mysql_fetch_assoc($result); $maxusuarios = $maxusuarios['num_usuarios']; // Cargamos los usuarios activos de la sesión (almacenados en formato json) if(is_file("usuariosactivos.json")){ $file = fopen("usuariosactivos.json","r+"); $json = json_decode(fread($file,filesize("usuariosactivos.json")),true); $count = count($json); fclose($file); }else{ $count = 0; } // La variable $add nos ayudará a saber si el usuario ya es un usuario activo // que estaria realizando una recarga del listado de ejercicios $add = true; for($i=0;$i<$count;$i++){ if($json[$i]['nombre']==$user){ $add = false; // Si vemos que el usuario que hace la petición ya es un usuario activo // no se debe añadir el nuevo usuario ($add = false) } } // Si hay espacio para el usuario o si se trata de una recarga del listado se le devuelve el listado de ejercicios if($count < $maxusuarios || !$add){ if($add){ // Si hay que añadir al usuario se añade al fichero usuariosactivos.json en formato JSON $json[$count] = array('nombre' => $user, 'ip' => $ip); unlink("usuariosactivos.json"); // Borra el contenido anterior $file = fopen("usuariosactivos.json","a+"); fwrite($file, json_encode($json,true)); fclose($file); } // Si no el usuario no está en usuarios.php se escribe una nueva línea en el fichero // que añade en un listado el nombre del usuario, un icono para acceder a su streaming de video, // y otro icono para expulsar al usuario de la sesión if(strpos(file_get_contents("usuarios.php"),$user) == false) { $file = fopen("usuarios.php","a+"); fwrite($file, "<li>".$user." <input onclick = \"this.form.action = 'stream.php'\" type='image' src='monitor.png' name='stream' value='$user'> <input type='image' src='circle-x.png' name='nomusuario' value='$user'> \r\n"); fclose($file); } // Se carga el fichero de listaejerciciosjson.php que contiene en formato JSON la rutina de la sesión. // Esto es lo que recibirá el usuario en su dispositivo Android. // La aplicación con esos datos crea un listado de los ejercicios. header('Location: listaejerciciosjson.php'); exit(); } ?>

Page 74: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

54

stream.php <?php session_start(); error_reporting(E_ERROR | E_PARSE); ?> <html> <head> <meta charset="ISO-8859-1"> <title>Monitorización</title> <link href="estilo.css" rel="stylesheet" type="text/css"> <link rel="icon" type="image/png" href="../favicon.png" /> <script src="js/jquery-1.9.1.min.js" type="text/javascript"></script> <script type="text/javascript" src="js/jwplayer.js"></script> <script src="js/player.js" type="text/javascript"></script> <script>jwplayer.key = "MuITxOH+IIJfhefhnNwH4wWy8kxDRv18HKjpGw=="</script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script type="text/javascript"> // Establece un intervalo de recarga cada 5 segundos para cargar el elemento #usuariosactivos // Cuando se carga stream.php incrusta el contenido de listausuarios.php en el elemento correspondiente $().ready(function() { $("#usuariosactivos").load("listausuarios.php"); setInterval("recarga()",5000); }); function recarga(){ // Sobreescribe el contenido del elemento cuyo id es #usuariosactivos // con los datos obtenidos de la consulta a la página listausuarios.php jQuery.post("listausuarios.php",function( data ) { jQuery("#usuariosactivos").html(data); }); } </script> <script src="js/enviamensaje.js"></script> </head> <body> <center> <header> <span id="welcome"> Welcome <?php echo (isset($_SESSION['user']) ? $_SESSION['user'] : "Visitor"); ?> - <a href="gestion.php"><?php echo (isset($_SESSION['sesion']) ? $_SESSION['sesion'] : "Sesion"); ?></a> <a href="killsession.php">Cerrar sesión de ejercicios</a> </span> <span id="logout"> <a href="logout.php">Log out</a> </span> </header> </center> <body> <aside id="usuariosactivos" style="float: left;"> </aside> <?php // Incrusta el contenido html que muestra el reproductor JWPlayer, la url que se le pasa // es http://localhost:1935/live/+nombre de usuario que es el canal del servidor de streaming // en el que publican los usuarios de las aplicaciones Android // En $_POST['stream'] se almacena el nombre del usuario que queremos observar via streaming $path = "rtmp://localhost:1935/live/".$_POST['stream']; echo '<div id="stream"><div id="video_preview"><div id="player"></div><div class="clear"></div><input hidden="true" type="text" id="stream_url" value="'; echo $path; echo '"/><br /><form id="for" action="" method="POST" onKeyPress="submitenter(this,event)"><input hidden="true" id="user" type="true" name="user" value="'.$_POST['stream'].'" /><textarea id="msg" style="height: 80px; width: 580px;" name="msg"></textarea> <button id="submit">Enviar</button></div></div>'; ?> <br><br> </body> <footer id="footer">Creado por Antonio Clavaín Mateo</footer> </html>

unban.php <?php $user = $_POST['nomusuario']; // Borra el usuario de usuariosbaneados.php (Para que no se muestre en el panel) $file = fopen("usuariosbaneados.php","r+"); $newfile = fopen("usuariosbaneados2.php","a+"); while (($line = fgets($file)) !== false) { if(strpos($line,$user) == false){ fwrite($newfile, $line); } } fclose($newfile);

Page 75: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

55 Monitorización mediante streaming de vídeo para entrenamiento personal

fclose($file); unlink("usuariosbaneados.php"); rename("usuariosbaneados2.php","usuariosbaneados.php"); // Borra el usuario de usuariosbaneados.json $file = fopen("usuariosbaneados.json","r+"); $json = json_decode(fread($file,filesize("usuariosbaneados.json")),true); fclose($file); $newarray = array(); $count = count($json); for($i=0;$i<$count;$i++){ if($json[$i]['nombre']!=$user){ array_push($newarray, $json[$i]); }else{ $ip = $json[$i]['ip']; // Almacena la IP del usuario, para bloquearla posteriormente. } } unlink("usuariosbaneados.json"); // Borra el contenido anterior $file = fopen("usuariosbaneados.json","a+"); fwrite($file, json_encode($newarray,true)); // Escribe el nuevo fclose($file); // Borra el usuario de .htaccess para volver a permitir el acceso $file = fopen(".htaccess","r+"); $newfile = fopen(".htaccess2","a+"); while (($line = fgets($file)) !== false) { if(strpos($line,$ip) == false){ fwrite($newfile, $line); } } fclose($newfile); fclose($file); unlink(".htaccess"); rename(".htaccess2",".htaccess"); // Recarga el panel de monitor header('Location: gestion.php'); exit(); ?>

enviamensaje.js // Función que captura el evento submit del formulario que contiene el cuadro de texto que se le desea enviar al usuario $(document).ready(function(){ $('#for').on('submit',function(e) { // Realiza una petición sobre enviamsg.php, enviando por método POST todos los datos // del formulario (nombre de usuario del cliente y contenido del cuadro de texto) // Pero sin que se recargue la página del navegador, haciendo que no se corte // momentaneamente la reproducción del streaming de video $.ajax({ url:'enviamsg.php', data:$(this).serialize(), type:'POST', success:function(data){ console.log(data); $("#for")[0].reset(); // Vacía el cuadro de texto del formulario } }); e.preventDefault(); // Provoca que no se recargue la página actual return false; }); }); // Esta función realiza lo mismo que la anterior, pero cuando detecta que se ha introducido la tecla enter en el cuadro de texto function submitenter(myfield,e) { var keycode; if (window.event) keycode = window.event.keyCode; else if (e) keycode = e.which; else return true; if (keycode == 13) { // Realiza una petición sobre enviamsg.php, enviando por método POST todos los datos // del formulario (nombre de usuario del cliente y contenido del cuadro de texto) // Pero sin que se recargue la página del navegador, haciendo que no se corte // momentaneamente la reproducción del streaming de video $.ajax({ url:'enviamsg.php', data:$("#for").serialize(), type:'POST', success:function(data){ console.log(data); $("#for")[0].reset(); // Vacía el cuadro de texto del formulario }

Page 76: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

56

}); e.preventDefault(); // Provoca que no se recargue la página actual return false; } else return true;

}

8.1.3 Código fuente Aplicación Android

AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.proyecto.antonio.projectdesign" > <uses-permission android:name="android.permission.CAMERA"> </uses-permission> <uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.flash" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.VIBRATE"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".InicioSesion" android:label="@string/app_name" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".Ejercicio" android:screenOrientation="landscape"/> <activity android:name=".ListaSesiones" /> <activity android:name=".ListaEjercicios"/> </application> </manifest>

InicioSesion.java package com.proyecto.antonio.projectdesign; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.os.SystemClock; import android.os.Vibrator; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.CheckBox; import android.widget.EditText; import android.widget.TextView; import com.proyecto.antonio.projectdesign.library.Httppostaux; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; public class InicioSesion extends Activity { String URL_connect = "192.168.16.131:80"; // Valor por defecto @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

Page 77: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

57 Monitorización mediante streaming de vídeo para entrenamiento personal

setContentView(R.layout.activity_iniciosesion); // Carga la configuración desde SharedPreferences (almacena IP del servidor, y usuario y // contraseña si se especifica en el checkbox) SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); if(settings != null) { URL_connect = settings.getString("IP", ""); String usuario = settings.getString("user", ""); String password = settings.getString("pass", ""); if(!password.equals("")){ // Si se especifica recordar, se inicia sesión automáticamente new asynclogin().execute(usuario,password); } } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Añade las opciones del menú con el archivo my.xml getMenuInflater().inflate(R.menu.my, menu); return true; } /* * Especifica las funciones a realizar en caso de que se pulse una opción u otra */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.ip: optionIP(); return true; case R.id.logout: SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.remove("password"); editor.commit(); return true; default: return super.onOptionsItemSelected(item); } } /* * Muestra un mensaje para introducir la IP y el puerto del servidor * Almacena la configuración en SharedPreferences de la app */ public void optionIP(){ AlertDialog.Builder alert = new AlertDialog.Builder(this); alert.setTitle("Introduce la IP y el puerto del servidor"); alert.setMessage("Formato: "+URL_connect); final EditText input = new EditText(this); alert.setView(input); alert.setPositiveButton("Aceptar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String value = input.getText().toString(); SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putString("IP", value); editor.commit(); URL_connect = value; } }); alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); alert.show(); } /* * Función que se ejecuta cuando el usuario pulsa en el botón del formulario * Desde esta funcion se lanza la consulta al servidor, y que si los datos * son correctos se lanza la siguiente actividad */ public void ir_ListaSesiones(View view){ // Extreamos los datos de usuario y contraseña introducidos en el formulario EditText user = (EditText) findViewById(R.id.email); EditText pass = (EditText) findViewById(R.id.password); String usuario = user.getText().toString(); String passw = pass.getText().toString(); // Se verifica si los datos introducidos están en blanco (nos ahorramos la consulta) if( checklogindata(usuario, passw)==true){

Page 78: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

58

// Ejecuta asíncronamente la función que realiza la consulta al servidor new asynclogin().execute(usuario,passw); }else{ // Si los datos están vacíos se muestra mensaje de error err_login(); } } // Vibra y muestra un mensaje de error de color rojo en el formulario public void err_login(){ Vibrator vibrator =(Vibrator) getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(400); TextView tv = (TextView) findViewById(R.id.error); tv.setVisibility(View.VISIBLE); // Muestra texto de color rojo indicando el error } // Vibra y elimina el mensaje de error, en caso de que estuviera visible public void login_ok(){ Vibrator vibrator =(Vibrator) getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(200); TextView tv = (TextView) findViewById(R.id.error); tv.setVisibility(View.INVISIBLE); } /* * Esta función es la que realiza la petición al servidor y comprueba el resultado obtenido * Es lanzadada desde el método doInBackground de la clase asíncrona * Recibe como parámetros el usuario y la contraseña (del formulario o de SharedPreferences) * Devuelve false si el login es erroneo y true si es correcto */ public boolean loginstatus(String username ,String password ) { int logstatus=0; /* * Creamos un ArrayList del tipo nombre-valor para agregar los datos recibidos por los * parametros anteriores y enviarlo mediante POST a nuestro sistema para relizar la * validacion */ ArrayList<NameValuePair> postparameters2send= new ArrayList<NameValuePair>(); postparameters2send.add(new BasicNameValuePair("usuario",username)); postparameters2send.add(new BasicNameValuePair("password",password)); // Realizamos la peticion y como respuesta se obtiene un array JSON Httppostaux post =new Httppostaux(); JSONArray jdata=post.getserverdata(postparameters2send, "http://"+URL_connect+"/gymserver/acces.php"); SystemClock.sleep(500); // Para simular que tarda más en iniciar sesión // Si lo que recibimos no es null y no está vacío if (jdata != null && jdata.length() > 0){ // Se crea un objeto JSONObject y se rellena con el unico elemento del JSONArray recibido JSONObject json_data; try { json_data = jdata.getJSONObject(0); logstatus=json_data.getInt("logstatus"); } catch (JSONException e) { e.printStackTrace(); } // Si recibimos un 0 significa que hemos tenido un error, devuelve false if (logstatus==0){ Log.e("loginstatus ", "invalido"); return false; } else{ //Si recibimos un 1 significa que no se produjeron errores, devuelve true return true; } }else{ // Se devuelve false, se produjo un error al obtener el JSONArray del servidor return false; } } // Función que comprueba que ningún campo del formulario ha quedado en blanco public boolean checklogindata(String username ,String password ){ if (username.equals("") || password.equals("")){ // Si alguno de los campos está vacio devuelve false return false; }else{ // Si ninguno de los dos campos está vacio devuelve true return true; } } /* CLASE ASYNCTASK * * Clase asíncrona que se encarga de lanzar la consulta y de reaccionar a la respuesta obtenida * Ya sea mostrando el mensaje de error o lanzando la nueva actividad si los datos son * correctos. Esta clase debe ser asíncrona para no provocar problemas de inconsistencia en la * app debido a los tiempos de respuesta del servidor.

Page 79: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

59 Monitorización mediante streaming de vídeo para entrenamiento personal

*/ class asynclogin extends AsyncTask< String, String, String > { String user,pass; private ProgressDialog pDialog; // Muestra un cartel para informar al usuario de que se esta llevando a cabo la autenticacion protected void onPreExecute() { pDialog = new ProgressDialog(InicioSesion.this); pDialog.setMessage("Autenticando...."); pDialog.setIndeterminate(false); pDialog.setCancelable(false); pDialog.show(); } // Función que se ejecuta en segundo plano, en ella se llama a la función loginstatus() // que es la encargada de realizar la consulta al servidor y de obtener la respuesta protected String doInBackground(String... params) { user=params[0]; pass=params[1]; // Realizamos la consulta, recibimos la respuesta y analizamos los datos en segundo plano. if (loginstatus(user,pass)==true){ return "ok"; // Login valido }else{ return "err"; // Login invalido } } /* * Función que se ejecuta tras doInBackground(), en ella se comprueba el resultado * y según haya sido erroneo o válido, muestra el mensaje de error en el formulario * o lanza la nueva actividad (almacenando los datos necesarios en SharedPreferences) */ protected void onPostExecute(String result) { pDialog.dismiss(); // Eliminamos el cartel que informa de la autenticación if (result.equals("ok")){ SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.putString("user", user); CheckBox cb = (CheckBox) findViewById(R.id.recordar); if(cb.isChecked()){ editor.putString("pass",pass); // Si se ha pulsado recordar se almacena la contraseña } editor.commit(); Intent i=new Intent(InicioSesion.this, ListaSesiones.class); login_ok(); startActivity(i); }else{ err_login(); } } } } Layout: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MyActivity" android:background="#FFA44D"> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:background="#FFA44D" android:weightSum="1" android:id="@+id/linearLayout"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center" android:layout_marginBottom="@dimen/activity_vertical_margin"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/logo" android:layout_gravity="center_vertical"/>

Page 80: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

60

</LinearLayout> <LinearLayout android:id="@+id/email_login_form" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center" android:layout_marginTop="30dp" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:layout_marginRight="@dimen/activity_horizontal_margin" android:background="@drawable/myborder" android:gravity="bottom"> <EditText android:id="@+id/email" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/prompt_user" android:inputType="textEmailAddress" android:maxLines="1" android:singleLine="true" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginTop="7dp"/> <EditText android:id="@+id/password" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/prompt_password" android:imeActionId="@+id/login" android:imeActionLabel="@string/action_sign_in_short" android:imeOptions="actionUnspecified" android:inputType="textPassword" android:maxLines="1" android:singleLine="true" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginBottom="7dp"/> <CheckBox android:id="@+id/recordar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Recordar usuario" android:layout_marginLeft="10dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/error" android:text="Usuario y/o contraseña incorrectos" android:visibility="invisible" android:textColor="#FF0000" android:layout_marginLeft="20dp"/> <Button android:id="@+id/email_sign_in_button" style="?android:textAppearanceSmall" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="@string/action_sign_in" android:textStyle="bold" android:layout_margin="10dp" android:textSize="19dp" android:onClick="ir_ListaSesiones"/> </LinearLayout> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:text="Creado por Antonio Clavaín Mateo" android:id="@+id/textView" android:layout_gravity="right|bottom" android:layout_alignParentBottom="true" android:layout_alignRight="@+id/linearLayout" android:layout_alignEnd="@+id/linearLayout" /> </RelativeLayout>

ListaSesiones.java package com.proyecto.antonio.projectdesign; import android.app.Activity;

Page 81: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

61 Monitorización mediante streaming de vídeo para entrenamiento personal

import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener; import com.proyecto.antonio.projectdesign.library.Httppostaux; import org.apache.http.NameValuePair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; /** * Created by Antonio on 22/07/2014. */ public class ListaSesiones extends Activity { SwipeRefreshLayout swipeRefreshLayout; ListView lv; List<String> array_list = new ArrayList<String>(); String URL_connect = ""; JSONArray jdata; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_listasesiones); // Carga la IP del servidor almacenada en sharedpreferences SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); if(settings != null) { URL_connect = settings.getString("IP", ""); } // Carga el listview que será modificado cada vez que se realice la consulta al servidor lv = (ListView) findViewById(R.id.listasesiones); // Establece el listener y los colores del swiperefreshlayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swiperefreshlayout); swipeRefreshLayout.setOnRefreshListener(onRefreshListener); swipeRefreshLayout.setColorScheme(R.color.c1, R.color.c2, R.color.c3, R.color.c4); // Realiza en segundo plano la consulta para obtener la lista de sesiones activas new asyncsql().execute(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Añade las opciones del menú con el archivo my.xml getMenuInflater().inflate(R.menu.my, menu); return true; } /* * Especifica las funciones a realizar en caso de que se pulse una opción u otra */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.logout: SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); editor.remove("pass"); editor.commit(); finish(); return true; default: return super.onOptionsItemSelected(item); } } /* * Establece las funciones a realizar por el listener del swiperefreshlayout. * Cuando detecta un evento realiza una nueva peticion al servidor * El evento se disparará cuando desplacemos hacia abajo el swiperefreshlayout */ OnRefreshListener onRefreshListener = new OnRefreshListener(){ @Override

Page 82: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

62

public void onRefresh() { new Handler().postDelayed(new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(false); // Realiza de nuevo la consulta para obtener las sesiones activas new asyncsql().execute(); } }, 2000); } }; /* CLASE ASYNCTASK * * Clase asíncrona que se encarga de lanzar la consulta y de imprimir el resultado en el * listview de la actividad. Donde se muestran las sesiones activas en el servidor. * Esta clase debe ser asíncrona para no provocar problemas de inconsistencia en la * app debido a los tiempos de respuesta del servidor. */ class asyncsql extends AsyncTask< String, String, String > { protected void onPreExecute() { } protected String doInBackground(String... params) { ArrayList<NameValuePair> postparameters2send= new ArrayList<NameValuePair>(); // Realizamos la peticion y como respuesta se obtiene un array JSON Httppostaux post =new Httppostaux(); jdata=post.getserverdata(postparameters2send, "http://"+ URL_connect+"/gymserver/listasesiones.php"); array_list = new ArrayList<String>(); // Si lo que recibimos no es null y no está vacío if (jdata!=null && jdata.length() > 0){ String nom_sesion,user_monitor; // Bucle para obtener todos los JSONObject contenidos en el JSONArray recibido for(int i=0;i<jdata.length();i++){ try { // Se crea un objeto JSONObject y se rellena con // el elemento i del JSONArray recibido JSONObject json_data = jdata.getJSONObject(i); // Se obtienen los nombres de la sesion y del monitor nom_sesion = json_data.getString("nombre_sesion"); user_monitor = json_data.getString("user_monitor"); // Se añaden al array de string, que es lo que se mostrará en el listview array_list.add("Sesión: "+nom_sesion+"\nMonitor: "+user_monitor+"\n"); } catch (JSONException e) { e.printStackTrace(); } } }else{ // Se devuelve "err", se produjo un error al obtener el JSONArray del servidor return "err"; } return "ok"; } /* * Función que se ejecuta tras doInBackground(), en ella se comprueba el resultado de la * consulta. Si es positivo se actualiza el listview con el nuevo array de string donde * están almacenadas las sesiones y los monitores. Si es negativo se muestra un aviso * de que no existen sesiones activas. */ protected void onPostExecute(String result) { if (result.equals("ok")) { // Actualiza el listview ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(ListaSesiones.this, android.R.layout.simple_list_item_1, array_list ); lv.setAdapter(arrayAdapter); // Gestiona clicks en la lista de sesiones lv.setClickable(true); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { /* * Detecta la posición en la que se ha hecho click, se accede al objeto JSON * que se corresponde con esa posicion en el JSONArray. Obtiene los datos de * nombre de la sesion, del monitor y la IP del monitor. Y lanza la actividad * ListaEjercicios añadiendo los datos obtenidos de la sesión. */ @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { try { JSONObject json_data = jdata.getJSONObject(position); String nom_sesion = json_data.getString("nombre_sesion");

Page 83: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

63 Monitorización mediante streaming de vídeo para entrenamiento personal

String user_monitor = json_data.getString("user_monitor"); String ip_monitor = json_data.getString("ip_monitor"); Intent myIntent = new Intent(ListaSesiones.this, ListaEjercicios.class); myIntent.putExtra("nom_sesion",nom_sesion); myIntent.putExtra("user_monitor",user_monitor); myIntent.putExtra("ip_monitor",ip_monitor); startActivity(myIntent); }catch (JSONException e) { e.printStackTrace(); } } }); } else { array_list.add("No hay sesiones activas"); ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(ListaSesiones.this, android.R.layout.simple_list_item_1, array_list ); lv.setAdapter(arrayAdapter); } } } } Layout: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFA44D" android:padding="10dp"> <TextView android:id="@+id/textlist" android:text="Sesiones activas: " android:layout_width="fill_parent" android:layout_height="wrap_content" android:padding="@dimen/activity_horizontal_margin" android:textSize="20dp" android:background="#F58547"/> <android.support.v4.widget.SwipeRefreshLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/swiperefreshlayout" android:layout_marginTop="@dimen/activity_vertical_margin"> <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/listasesiones" android:layout_marginTop="@dimen/activity_vertical_margin" /> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>

ListaEjercicios.java package com.proyecto.antonio.projectdesign; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; import android.support.v4.widget.SwipeRefreshLayout; import android.util.Log; import android.view.View; import android.view.Window; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import com.proyecto.antonio.projectdesign.library.Httppostaux; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List;

Page 84: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

64

/** * Created by Antonio on 22/07/2014. */ public class ListaEjercicios extends Activity { SwipeRefreshLayout swipeRefreshLayout; ListView lv; List<String> array_list = new ArrayList<String>(); String URL_connect = ""; JSONArray jdata; String user; String user_monitor; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_listaejercicios); // Carga el usuario que hizo el login, que se encuentra almacenado en sharedpreferences SharedPreferences settings = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE); if(settings != null) { user = settings.getString("user", ""); Log.d("usuario: ",user); } // Obtiene los datos de la sesion de ejercicios obtenidos en la actividad ListaSesiones Intent myIntent = getIntent(); String nom_sesion = myIntent.getStringExtra("nom_sesion"); user_monitor= myIntent.getStringExtra("user_monitor"); URL_connect = myIntent.getStringExtra("ip_monitor"); // Modifica los textview para informar al usuario de la sesión de ejercicios en la que // se encuentra y del monitor que la creó TextView tv = (TextView) findViewById(R.id.nombresesion); tv.setText("Sesión: "+nom_sesion); tv = (TextView) findViewById(R.id.nombremonitor); tv.setText("Monitor: "+user_monitor); // Carga el listview que será modificado cada vez que se realice la consulta al servidor lv = (ListView) findViewById(R.id.listasesiones); // Establece el listener y los colores del swiperefreshlayout swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swiperefreshlayout); swipeRefreshLayout.setOnRefreshListener(onRefreshListener); swipeRefreshLayout.setColorScheme(R.color.c1, R.color.c2, R.color.c3, R.color.c4); // Realiza en segundo plano la consulta para obtener la lista de ejercicios de la sesión new asyncgetejercicios().execute(); } /* * Cuando se destruye la actividad informamos al servidor del monitor de que nos vamos a * desconectar de la sesión, se realiza de forma asíncrona. */ @Override protected void onDestroy(){ super.onDestroy(); new asyncborra().execute(); } /* * Establece las funciones a realizar por el listener del swiperefreshlayout. * Cuando detecta un evento realiza una nueva peticion al servidor * El evento se disparará cuando desplacemos hacia abajo el swiperefreshlayout */ SwipeRefreshLayout.OnRefreshListener onRefreshListener = new SwipeRefreshLayout.OnRefreshListener(){ @Override public void onRefresh() { new Handler().postDelayed(new Runnable() { @Override public void run() { swipeRefreshLayout.setRefreshing(false); // Realiza de nuevo la consulta para obtener la rutina de ejercicios new asyncgetejercicios().execute(user); } }, 2000); } }; /* CLASE ASYNCTASK * * Clase asíncrona que se encarga de lanzar la consulta y de imprimir el resultado en el * listview de la actividad. Donde se muestran la lista de ejercicios de la sesión * Esta clase debe ser asíncrona para no provocar problemas de inconsistencia en la * app debido a los tiempos de respuesta del servidor. */ class asyncgetejercicios extends AsyncTask< String, String, String > { protected void onPreExecute() { }

Page 85: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

65 Monitorización mediante streaming de vídeo para entrenamiento personal

protected String doInBackground(String... params) { /* * Creamos un ArrayList del tipo nombre-valor para agregar los datos recibidos y * enviarlo mediante POST a nuestro sistema para iniciar * sesión en el servidor del monitor */ ArrayList<NameValuePair> postparameters2send= new ArrayList<NameValuePair>(); postparameters2send.add(new BasicNameValuePair("user",user)); postparameters2send.add(new BasicNameValuePair("user_monitor",user_monitor)); // Realizamos la peticion y como respuesta se obtiene un array JSON Httppostaux post =new Httppostaux(); jdata=post.getserverdata(postparameters2send, "http://"+URL_connect+"/monitorgym/reciberutina.php"); // Si lo que recibimos no es null y no está vacío if (jdata!=null && jdata.length() > 0){ String nom_ejercicio; array_list = new ArrayList<String>(); // Bucle para obtener todos los JSONObject contenidos en el JSONArray recibido for(int i=0;i<jdata.length();i++){ try { // Se crea un objeto JSONObject y se rellena con // el elemento i del JSONArray recibido JSONObject json_data = jdata.getJSONObject(i); // Se obtiene el nombre del ejercicio y se añada al array nom_ejercicio = json_data.getString("descripcion"); array_list.add("Ejercicio: "+nom_ejercicio+"\n"); } catch (JSONException e) { e.printStackTrace(); } } }else{ // Se devuelve "err", se produjo un error al obtener el JSONArray del servidor array_list = new ArrayList<String>(); return "err"; } return "ok"; } /* * Función que se ejecuta tras doInBackground(), en ella se comprueba el resultado de la * consulta. Si es positivo se actualiza el listview con el nuevo array de string donde * están almacenadas los ejercicios de la sesión. Si es negativo se muestra un aviso * de que se ha producido un error. */ protected void onPostExecute(String result) { if (result.equals("ok")) { // Actualiza el listview ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(ListaEjercicios.this, android.R.layout.simple_list_item_1, array_list ); lv.setAdapter(arrayAdapter); // Gestiona clicks en la lista de sesiones lv.setClickable(true); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { /* * Detecta la posición en la que se ha hecho click, se accede al objeto JSON * que se corresponde con esa posicion en el JSONArray. Obtiene los datos del * ejercicio y lanza la actividad Ejercicio añadiendo estos datos en el intent. */ @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) { try { JSONObject json_data = jdata.getJSONObject(position); String id_ejercicio = json_data.getString("Id_Ejercicio"); String nom_ejercicio = json_data.getString("descripcion"); Intent myIntent = getIntent(); String user_monitor= myIntent.getStringExtra("user_monitor"); String nom_sesion = myIntent.getStringExtra("nom_sesion"); myIntent = new Intent(ListaEjercicios.this, Ejercicio.class); myIntent.putExtra("id_ejercicio",id_ejercicio); myIntent.putExtra("nom_sesion",nom_sesion); myIntent.putExtra("user_monitor",user_monitor); myIntent.putExtra("ip_monitor",URL_connect); myIntent.putExtra("nom_ejercicio",nom_ejercicio); startActivity(myIntent); }catch (JSONException e) { e.printStackTrace(); } } }); }

Page 86: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

66

else { array_list.add("Lista no disponible. Recargue en unos segundos"); ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(ListaEjercicios.this, android.R.layout.simple_list_item_1, array_list ); lv.setAdapter(arrayAdapter); } } } /* CLASE ASYNCTASK * * Clase asíncrona que se encarga de notificar al servidor del monitor de que el usuario * va a salir de la sesión de ejercicios. Se realiza de forma asíncrona para no dejar la * aplicación colgada esperando la respuesta. */ class asyncborra extends AsyncTask< String, String, String > { protected void onPreExecute() { } protected String doInBackground(String... params) { /* * Creamos un ArrayList del tipo nombre-valor para agregar el nombre del usuario y * enviarlo mediante POST a nuestro sistema para eliminarlo de la sesión en el * servidor del monitor. */ ArrayList<NameValuePair> postparameters2send= new ArrayList<NameValuePair>(); postparameters2send.add(new BasicNameValuePair("user",user)); // Realizamos la peticion HTTP Httppostaux post =new Httppostaux(); post.getserverdata(postparameters2send, "http://"+URL_connect+"/monitorgym/eliminausuario.php"); return "ok"; } protected void onPostExecute(String result) { } } } Layout: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFA44D" android:padding="10dp"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="#F58547" android:padding="@dimen/activity_horizontal_margin" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/nombresesion" android:textSize="16dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/nombremonitor" android:textSize="16dp"/> </LinearLayout> <android.support.v4.widget.SwipeRefreshLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/swiperefreshlayout" android:layout_marginTop="@dimen/activity_vertical_margin"> <ListView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/listasesiones" android:layout_marginTop="@dimen/activity_vertical_margin" /> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>

Ejercicio.java package com.proyecto.antonio.projectdesign; import android.app.Activity; import android.app.AlertDialog; import android.content.Context;

Page 87: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

67 Monitorización mediante streaming de vídeo para entrenamiento personal

import android.content.DialogInterface; import android.content.Intent; import android.graphics.ImageFormat; import android.hardware.Camera; import android.media.MediaPlayer; import android.os.AsyncTask; import android.os.Bundle; import android.os.PowerManager; import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Log; import android.view.Menu; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.MediaController; import android.widget.TextView; import android.widget.VideoView; import com.proyecto.antonio.projectdesign.library.Httppostaux; import net.majorkernelpanic.streaming.Session; import net.majorkernelpanic.streaming.SessionBuilder; import net.majorkernelpanic.streaming.audio.AudioQuality; import net.majorkernelpanic.streaming.rtsp.RtspClient; import net.majorkernelpanic.streaming.video.VideoQuality; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by Antonio on 26/07/2014. */ public class Ejercicio extends Activity implements RtspClient.Callback, Session.Callback, SurfaceHolder.Callback{ String URL_connect = ""; JSONArray jdata; String id_ejercicio; String nom_sesion, user_monitor, nom_ejercicio, finalidad, duracion, descripcion; // Para recepcion de mensajes del monitor HiloMensajes myDatagramReceiver; DatagramSocket datagramSocket; // Variables necesarias para usar la libreria de libstream // log tag public final static String TAG = Ejercicio.class.getSimpleName(); // surfaceview private static net.majorkernelpanic.streaming.gl.SurfaceView mSurfaceView; // Rtsp session private Session mSession; private static RtspClient mClient; // Usuario y contraseña del publicador de Wowza Media Streaming public static final String PUBLISHER_USERNAME = "antonio"; public static final String PUBLISHER_PASSWORD = "1234"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Actividad a pantalla completa (sin titulo ni barra estado) requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_ejercicio); // Recoge datos pasados desde la actividad ListaEjercicios Intent myIntent = getIntent(); id_ejercicio = myIntent.getStringExtra("id_ejercicio"); nom_sesion = myIntent.getStringExtra("nom_sesion"); user_monitor= myIntent.getStringExtra("user_monitor"); URL_connect = myIntent.getStringExtra("ip_monitor"); // Crea un hilo nuevo, en el que se escuchará la llegada de mensajes del monitor

Page 88: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

68

myDatagramReceiver = new HiloMensajes(); myDatagramReceiver.start(); try { mSurfaceView = (net.majorkernelpanic.streaming.gl.SurfaceView) findViewById(R.id.camera_preview); mSurfaceView.getHolder().addCallback(this); // Ejecuta la funcion initRtspClient(), detallada más adelante. Encargada de comenzar // el streaming de video. initRtspClient(); }catch(Exception e){ Log.d("Error en cámara","Excepcion: " + e); } try { // Carga el vídeo del ejercicio desde el servidor VideoView videoView = (VideoView) findViewById(R.id.videoView); videoView.setVideoPath("http://"+URL_connect+"/monitorgym/ejercicios/"+id_ejercicio+"/video.mp4"); videoView.setMediaController(new MediaController(this)); videoView.setOnPreparedListener (new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { mp.setLooping(true); mp.setVolume(0,0); } }); // Establece que el vídeo se reproduzca a pantalla completa DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); android.widget.FrameLayout.LayoutParams params = (android.widget.FrameLayout.LayoutParams) videoView.getLayoutParams(); params.width = metrics.widthPixels; params.height = metrics.heightPixels; params.leftMargin = 0; params.rightMargin = 0; params.bottomMargin = 0; params.topMargin = 0; videoView.setLayoutParams(params); // Comienza la reproducción del video videoView.requestFocus(); videoView.start(); }catch(Exception e){ Log.d("Error en video","Excepcion: " + e); } // Realiza una consulta asíncrona para obtener la información acerca del ejercicio new asyncinfo().execute(); } /* * Esta función muestra un mensaje en el que se describe la información acerca del ejercicio: * · Nombre de la sesión * · Nombre del monitor * · Nombre del ejercicio * · Finalidad del ejercicio * · Duración * · Descripción del ejercicio */ public void mostrar_Descripcion(View view){ AlertDialog.Builder alert = new AlertDialog.Builder(this); // Si se produce algun error al traer la información del ejercicio desde el // servidor del monitor, se muestra un mensaje informando sobre ello. nom_ejercicio = nom_ejercicio != null ? nom_ejercicio : "no encontrado"; finalidad = finalidad != null ? finalidad:"no encontrada"; duracion = duracion != null?duracion:"no encontrada"; descripcion = descripcion != null ? descripcion :"no encontrada"; alert.setTitle(nom_sesion+" - "+user_monitor+" - " + nom_ejercicio); alert.setMessage("Finalidad: " + finalidad + "\nDuración: " + duracion + "\nDescripción: "+ descripcion); alert.show(); } /* CLASE ASYNCTASK * * Clase asíncrona que se encarga de realizar una consulta al servidor del monitor * para obtener información acerca del ejercicio (nombre, finalidad, duracion y descripcion). * Esta clase debe ser asíncrona para no provocar problemas de inconsistencia en la * app debido a los tiempos de respuesta del servidor. */ class asyncinfo extends AsyncTask< String, String, String > { protected void onPreExecute() { } protected String doInBackground(String... params) { /* * Creamos un ArrayList del tipo nombre-valor para agregar el id del ejercicio y

Page 89: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

69 Monitorización mediante streaming de vídeo para entrenamiento personal

* enviarlo mediante POST al servidor del monitor para que le devuelva la información * acerca del ejercicio. */ ArrayList<NameValuePair> postparameters2send= new ArrayList<NameValuePair>(); postparameters2send.add(new BasicNameValuePair("id_ejercicio",id_ejercicio)); // Realizamos la peticion HTTP y obtenemos un JSONArray como respuesta Httppostaux post =new Httppostaux(); jdata=post.getserverdata(postparameters2send, "http://"+URL_connect+"/monitorgym/recibeejercicio.php"); // Si lo que recibimos no es null y no está vacío if (jdata!=null && jdata.length() > 0){ try { // Se crea un objeto JSONObject y se rellena con // el unico elemento del JSONArray recibido JSONObject json_data = jdata.getJSONObject(0); // Se almacena la información recibida nom_ejercicio = json_data.getString("Descripcion"); finalidad = json_data.getString("Finalidad"); duracion = json_data.getString("Duracion"); descripcion = json_data.getString("descripcion"); } catch (JSONException e) { e.printStackTrace(); } }else{ // Se ha producido un error, respuesta no válida return "err"; } return "ok"; } protected void onPostExecute(String result) { } } /* * Función que se ejecuta cuando se recibe una trama por el socket abierto para la recepción * de mensajes del monitor. En la función se llama a la función alert() vista anteriormente * pasandole como argumento el mensaje recibido en el socket. */ private Runnable updateTextMessage = new Runnable() { public void run() { if (myDatagramReceiver == null) return; alert(myDatagramReceiver.getLastMessage()); } }; /* * Clase que hereda de la clase Thread. Nos permite crear otro hilo en nuestra actividad, * este hilo será el encargado de quedar a la escucha en el puerto 12345 de mensajes * que envia el monitor desde su panel de chat. Además, ejecutará la función updateTextMessage * para mostrar en pantalla el mensaje recibido mediante una alerta. */ public class HiloMensajes extends Thread{ private boolean bKeepRunning = true; private String lastMessage = ""; public void run(){ String message; byte[] lmessage = new byte[400]; DatagramPacket packet = new DatagramPacket(lmessage, lmessage.length); try { datagramSocket = new DatagramSocket(12345); // Abre socket en el puerto 12345 } catch (SocketException e1) { e1.printStackTrace(); } try { while(bKeepRunning) { datagramSocket.receive(packet); // Recibe el mensaje message = new String(lmessage, 0, packet.getLength()); lastMessage = message; // Almacena el mensaje en la variable lastMessage runOnUiThread(updateTextMessage); // Ejecuta la función que vimos antes para // mostrar el mensaje por pantalla } }catch (Exception e){ e.printStackTrace(); } } // Elimina el hilo que está en continua ejecución, termina el bucle public void kill() { bKeepRunning = false; } // Método getter para acceder a la variable lastMessage desde otra clase public String getLastMessage() { return lastMessage;

Page 90: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

70

} } @Override protected void onResume() { super.onResume(); // Método necesario para el correcto funcionamiento de la libreria libstreaming toggleStreaming(); } @Override protected void onPause(){ super.onPause(); // Método necesario para el correcto funcionamiento de la libreria libstreaming toggleStreaming(); } /* * Función que comienza la sesión de streaming por RTSP, haciendo uso de la librería * libstreaming. Se establecen los parámetros deseados, entre los que destacan: * parámetros de video (ancho, alto, fps), de la cámara, codificacion del audio y del video, * y la dirección a la que dirigiremos el flujo RTSP (en nuestro caso, la dirección del * servidor de streaming de Wowza Streaming Engine). */ private void initRtspClient() { // Configura el SessionBuilder VideoQuality vq = new VideoQuality(720,480,30,2000000); mSession = SessionBuilder.getInstance() .setContext(getApplicationContext()) .setAudioEncoder(SessionBuilder.AUDIO_NONE) .setAudioQuality(new AudioQuality(0, 0)) .setVideoEncoder(SessionBuilder.VIDEO_H264) .setSurfaceView(mSurfaceView).setPreviewOrientation(0) .setCamera(Camera.CameraInfo.CAMERA_FACING_FRONT) .setVideoQuality(vq) .setCallback(this).build(); // Crea y configura el cliente de RTSP, estableciendo la sesión creada anteriormente mClient = new RtspClient(); mClient.setSession(mSession); mClient.setCallback(this); String ip, port, path; Pattern uri = Pattern.compile("rtsp://(.+):(\\d+)/(.+)"); String user = getSharedPreferences("ProjectGym", Context.MODE_PRIVATE).getString("user", ""); String STREAM_URL = "rtsp://"; boolean copy = true; // Usando la dirección que empleamos para conectar al servidor del monitor, eliminamos el // puerto HTTP, y añadimos el puerto (1935) del servidor de streaming y el path deseado. for(int i=0;i<URL_connect.length() && copy;i++){ if(URL_connect.charAt(i) != ':'){ STREAM_URL = STREAM_URL + URL_connect.charAt(i); }else{ copy = false; } } STREAM_URL += ":1935/live/"; Matcher m = uri.matcher(STREAM_URL+user); m.find(); ip = m.group(1); port = m.group(2); path = m.group(3); // Especifica el usuario y contraseña de un publicador válido para el servidor de streaming mClient.setCredentials(PUBLISHER_USERNAME, PUBLISHER_PASSWORD); // Especifica la dirección IP y puerto del servidor de streaming mClient.setServerAddress(ip, Integer.parseInt(port)); // Especifica el path deseado para el streaming de video mClient.setStreamPath("/" + path); } // Método necesario para el correcto funcionamiento de la libreria libstreaming private void toggleStreaming() { if (!mClient.isStreaming()) { // Start camera preview mSession.startPreview(); // Start video stream mClient.startStream(); } else { // already streaming, stop streaming // stop camera preview mSession.stopPreview(); // stop streaming mClient.stopStream(); }

Page 91: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

71 Monitorización mediante streaming de vídeo para entrenamiento personal

} @Override public void onDestroy() { super.onDestroy(); // Libera los objetos usados para el streaming de video (cámara, sesión establecida, etc). mClient.release(); mSession.release(); mSurfaceView.getHolder().removeCallback(this); // Cierra el socket y acaba con el hilo datagramSocket.close(); myDatagramReceiver.kill(); } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onSessionError(int reason, int streamType, Exception e) { switch (reason) { case Session.ERROR_CAMERA_ALREADY_IN_USE: break; case Session.ERROR_CAMERA_HAS_NO_FLASH: break; case Session.ERROR_INVALID_SURFACE: break; case Session.ERROR_STORAGE_NOT_READY: break; case Session.ERROR_CONFIGURATION_NOT_SUPPORTED: break; case Session.ERROR_OTHER: break; } if (e != null) { alert(e.getMessage()); e.printStackTrace(); } } /* * Clase que crea un AlertDialog y lo muestra por pantalla. Usado tanto para mostrar errores * como para mostrar los mensajes enviados por el monitor desde su panel de chat. */ private void alert(final String msg) { final String error = (msg == null) ? "Error desconocido: " : msg; if(!isFinishing()) { AlertDialog.Builder builder = new AlertDialog.Builder(Ejercicio.this); builder.setMessage(error).setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } } ); AlertDialog dialog = builder.create(); dialog.show(); } } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onRtspUpdate(int message, Exception exception) { switch (message) { case RtspClient.ERROR_CONNECTION_FAILED: case RtspClient.ERROR_WRONG_CREDENTIALS: alert(exception.getMessage()); exception.printStackTrace(); break; } } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onPreviewStarted() { } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onSessionConfigured() { } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onSessionStarted() { } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onSessionStopped() {

Page 92: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

72

} // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void surfaceCreated(SurfaceHolder holder) { } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void surfaceDestroyed(SurfaceHolder holder) { } // Método necesario para el correcto funcionamiento de la libreria libstreaming @Override public void onBitrateUpdate(long bitrate) { } } Layout: <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" android:onClick="mostrar_Descripcion" android:clickable="true" android:layout_weight="1" android:background="#FFA44D"> <net.majorkernelpanic.streaming.gl.SurfaceView android:id="@+id/camera_preview" android:layout_width="200dp" android:layout_height="180dp" android:layout_gravity="right|bottom"/> <VideoView android:id="@+id/videoView" android:layout_height="match_parent" android:layout_width="match_parent" /> </FrameLayout>

Page 93: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

73 Monitorización mediante streaming de vídeo para entrenamiento personal

8.2 Anexo II: Manual de instalación y uso del servidor

Para realizar la instalación del servidor tan solo necesitamos copiar la carpeta ‘gymserver’ dentro de la carpeta

pública del servidor HTTP. El servidor debe tener el módulo de PHP instalado y disponer también de una base de datos. Por comodidad, puede usarse el paquete XAMPP que incluye todo el software necesario.

Para terminar, se deberán ejecutar las sentencias SQL contenidas en el fichero ‘sentenciassql.sql’ contenido en

la misma carpeta. Puede usarse una interfaz gráfica, como PHPMyAdmin, para hacer esto de una forma más cómoda.

Con esto, ya tendríamos la estructura de la base de datos necesaria para el funcionamiento de nuestro sistema.

Para hacer uso del servidor tan solo necesitamos que los procesos del servidor HTTP y de la base de datos estén activos.

Figura 29. Ejecución de las sentencias SQL en PHPMyAdmin

Page 94: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

74

8.3 Anexo III: Manual de instalación y uso del Monitor

Para la instalación de la aplicación del monitor tan solo debemos copiar la carpeta ‘monitorgym’ en la carpeta

pública del servidor HTTP en el que deseamos instalar la aplicación web.

También debemos instalar el servidor de streaming Wowza Streaming Engine. Puediendo descargarlo desde la página web http://www.wowza.com/pricing/trial. Puesto que en nuestro sistema se usan los parámetros por defecto (se publica en la ruta ‘/live’ y se escucha en el puerto 1935) no será necesario realizar grandes cambios en la configuración. Sin embargo, sí que es necesario crear el publicador que se usa en la aplicación Android para transmitir el flujo RTSP hacia el servidor de streaming. Para ello podemos hacer uso del ‘manager’ que

se instala junto al servidor Wowza, que nos permite gestionar el servidor a través de una interfaz web. Debemos acceder en la pestaña ‘Server’ y escogiendo en el menú de la izquierda la opción de ‘Publishers’ se nos mostrará el siguiente menú.

Haciendo click en ‘+Add Publisher’se nos mostrará un menú en el que introducir los datos para crear el

publicador. Se debe introducir el usuario “antonio” y la contraseña “1234”.

Por último, para poder acceder a la aplicación web del monitor debemos asegurarnos que tanto el proceso del servidor HTTP como el del servidor de streaming están activos.

Con un navegador web accedemos a la carpeta ‘monitorgym’ que hemos copiado, y se nos cargará la página de acceso al sistema. Esta página carga un formulario para introducir el usuario y la contraseña del monitor.

Page 95: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

75 Monitorización mediante streaming de vídeo para entrenamiento personal

Tras introducir unas credenciales válidas, se mostrará otro formulario en el que el monitor debe introducir los datos de la sesión que desea crear, que son: nombre, límite de usuarios, y fecha.

Una vez introducido los datos se crea la sesión y se procede a cargar la página de gestión del monitor.

Se muestran tres paneles, el panel central muestra todos los ejercicios contenidos en la base de datos. Suponen los ejercicios que el monitor puede añadir a la rutina. El panel de la izquierda muestra la rutina creada por el monitor. Mientras que el panel de la derecha muestra el listado de usuarios conectados a la sesión y de usuarios expulsados.

El monitor puede escoger los ejercicios que desea añadir a la rutina simplemente pulsando en el icono ‘+’

situado a la derecha de cada ejercicio. Y eliminarlos de la rutina haciendo click sobre el icono del aspa situado a la derecha de cada ejercicio del panel de la izquierda.

Por último, se disponen de los botones para comenzar a reproducir el streaming de un usuario, expulsarlo o readmitirlo en el servidor, todos ubicados a la derecha del nombre de los usuarios de la aplicación Android que están conectados a la sesión creada por el monitor. El icono con forma de monitor es el que activa la reproducción del streaming de vídeo, el icono del aspa sirve para expulsar a un usuario de la sesión. Por último, para readmitir a un usuario expulsado se debe pulsar el icono ‘check’ añadido a la derecha del nombre

del usuario.

Page 96: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

76

En la página para visualizar el streaming de vídeo de un usuario disponemos del listado de usuarios de la sesión, el reproductor flash y un cuadro de texto.

El cuadro de texto nos permite enviar mensajes al usuario de la aplicación Android, en el reproductor se puede ver al usuario haciendo el ejercicio, mientras que el panel de la izquierda con el listado de usuarios nos permite gestionar los usuarios. Podemos cambiar el usuario que queremos observar, expulsar a algún usuario o readmitir a otro en la sesión.

Page 97: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

77 Monitorización mediante streaming de vídeo para entrenamiento personal

8.4 Anexo IV: Manual de instalación y uso de la aplicación Android

Para instalar la aplicación desarrollada para dispositivos con sistema operativo Android tan solo será necesario copiar el fichero ‘app-release.apk’ contenido en la carpeta ProjectDesign/app/ y pegarlo en el dispositivo Android. Con un gestor de ficheros, localizar el archivo copiado y ejecutarlo.

Posiblemente, sea necesario cambiar en los ajustes del dispositivo para permitir la instalación de aplicaciones desde ‘Orígenes desconocidos’, para permitir que se instalen aplicaciones que no provienen de la PlayStore, la

tienda digital de Android.

Al ejecutar la aplicación se nos solicitará introducir unos credenciales válidos. Pueden usarse algunos de la siguiente tabla:

Usuario Contraseña

antonio 1234

monitor 1234

user0 root

antonio_monitor 1234

Tabla 5. Credenciales válidas

Page 98: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

78

Antes de introducir los credenciales se debe introducir la dirección IP y el puerto en el que escucha el servidor HTTP. Para ello se dispone de una opción en el menú desplegable.

Una vez validada la cuenta de usuario se muestra el listado de sesiones activas. El usuario deberá escoger la sesión que desee.

Page 99: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

79 Monitorización mediante streaming de vídeo para entrenamiento personal

Una vez dentro de la sesión se muestra un listado con todos los ejercicios de la rutina. El usuario deberá elegir el ejercicio que desea realizar.

Una vez elegido el ejercicio se comienza a realizar el streaming de vídeo, mientras que se reproduce remotamente el vídeo explicativo del ejercicio ubicado en el servidor del monitor.

Page 100: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Anexos

80

Cuando el usuario pulsa la pantalla del dispositivo se muestra un mensaje con una descripción completa del ejercicio, en el que se detalla la duración, el objetivo que se persigue con el ejercicio, así como una descripción paso a paso sobre cómo realizar el ejercicio correctamente.

Además, en esta pantalla la aplicación estará a la escucha de mensajes enviados por el monitor. Cuando este envía el mensaje se muestra en pantalla tal y como se muestra en la siguiente captura.

Una vez terminado el ejercicio, el usuario debe pulsar hacia atrás y escoger otro ejercicio si desea continuar con la sesión. Si desea salir de la sesión debe pulsar hacia atrás hasta el listado de sesiones. Una vez en el listado de sesiones, el usuario tiene disponible en el menú de opciones la opción de ‘Cerrar sesión’.

Page 101: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

81

INDICE ALFABÉTICO

3gp, 14

A

Agentes del sistema Aplicación Android, 2 Aplicación Monitorid, 2 Servidor, 1

AJAX, 12 AMP, 12, 9 Android, i, iii, v, vi, vii, ix, xi, 1, 2, 5, 6, 7, 9, 13, 14, 15, 17, 18,

19, 21, 22, 23, 25, 26, 27, 29, 30, 32, 33, 37, 38, 39, 53, 54, 56, 74, 75, 76, 77

Apache, vi, 12, 9, 10, 39 API, 12, 29 Aplicación, vi, vii, 2, 6, 15, 21, 29, 56 Arquitectura del sistema, 2

B

Bases de Datos, 10

C

claves externas, 11 COM, 12, 10 Comandos SQL

DELETE, 11 INSERT, 11 UPDATE, 11

CORBA, 12, 10

D

dirección IP, 4, 5, 18, 19, 25, 32, 70, 78 DOM, 12, 10, 12

E

ECMA, 12

F

f4v, 14 flash, 9, 14, 30, 35, 37, 56, 76 FLV, 14 foreign keys. Véase claves externas

G

GData, 13 GET, 10 GPL, 12, 9, 11

H

HTML, 12, 10, 12, 14 HTTP, vi, 12, 5, 6, 9, 10, 15, 18, 19, 21, 23, 25, 30, 32, 33, 35,

66, 69, 70, 73, 74, 78 httpd.conf. Véase Servidor Apache

I

IMAP, 12, 10 InnoDB, 11, 42, 43 interfaz, xi, 9, 10, 21, 25, 26, 27, 35, 73, 74

J

JavaScript, vi, 12, 9, 10, 11, 12, 13, 27, 39 jQuery, 12, 39, 48, 54 JSON, vi, 12, 9, 12, 13, 15, 19, 23, 24, 25, 32, 33, 41, 43, 44, 45,

51, 52, 53, 58, 62, 65 JWPlayer, 12, 14, 26, 30, 37, 39, 54

L

LDAP, 12, 10 libstreaming, 6, 30, 34, 39, 70, 71, 72 Linux, 13 login, 1, 5, 19, 49, 58, 59, 60, 64

M

m4a, 14 m4v, 14 mov, 14 MP3, 14 Mp4, 14 mp4a, 14 mp4v, 14 MyISAM, 11 MySQL, vi, 12, 6, 9, 10, 11, 15, 39

N

Netcraft, 9 nodo, 6

O

ODBC, 10

P

Perl, 12, 10 PHP, vi, 12, 1, 6, 9, 10, 15, 19, 23, 27, 39, 49, 73 POP3, 12, 10 POSIX, 12, 10

Page 102: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal

Indice Alfabético

82

R

RIA, 12, 13 RTMP, vi, 12, 9, 13, 14, 30, 35 RTSP, vi, 12, 9, 13, 14, 34, 38, 70, 74

S

SAX, 12, 10 SELECT, 11, 41, 43, 48, 53 Servidores

Apache, 9 Base de Datos, 15 HTTP, 9

Smartphone, iii, v, 1 SNMP, 12, 10 Stored Procedures, 11

T

Tablet, iii, 1 TCP, 12, 14 tecnologías, iii, 1, 6, 9, 12, 37 Transacciones, 11

Triggers, 11

U

UDP, 12, 14, 26, 35, 45 Unicode, 11 URL, 10, 56, 57, 58, 61, 62, 64, 65, 66, 67, 68, 69, 70

V

Views. Véase Vistas Vistas, 11

W

WDDX, 12, 10 Wowza, vi, 6, 13, 14, 21, 35, 37, 39, 67, 70, 74

X

XAMPP, 12, 9, 73 XML, 12, 10, 11, 12, 13 XSLT, 10

Page 103: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal
Page 104: Monitorizaci³n mediante streaming de vdeo para entrenamiento personal