Streaming JME en el móvil por Mario La Menza
lunes, septiembre 17, 2007 at 11:23PM Introducción
Streaming es un término que hace referencia a la transmisión de audio o video en fragmentos, a través de una red. Y donde, desde la experiencia del usuario, el visionado comienza rápidamente, sin que se requiera la descarga total del archivo en el cliente. Esto es, se solapan la descarga y el visionado.
Ya desde tres años atrás, que es cuando comencé con temas de Multimedia en movilidad, se notaba que la posibilidad de desarrollar aplicaciones para streaming de video en teléfonos móviles era de gran interés para la comunidad de desarrolladores JME. Supongo que a nadie pasaba desapercibido que el siguiente hito en la telefonía movil sería el VOD (Video On Demand) o la TV. Los pocos artículos que trataron en profundidad el tema fueron devorados y citados luego en los principales foros JME. Pero aún hoy la información concreta disponible en la web continúa siendo escasa y en algunos casos confusa.
De los links de primera página, resultantes de googlear “streaming j2me” más de la mitad remiten a foros y solo aparece un artículo de java.net que toca el tema en profundidad. Me refiero a Experiments in Streaming Content in Java ME de Vikram Goyal[1]. Este es un artículo de Agosto del 2006 y sus conclusiones, no resultaban muy alentadoras.
El presente artículo es entonces el fruto de haber desarrollado e investigado sobre streaming de vídeo JME durante los últimos tres años. Y está hecho tanto de lecturas, como de experiencia propia. De alguna manera es el artículo que hubiera querido leer tres años atrás. De forma que espero le resulte útil a quién desee incursionar en el tema.
Se asume una relativa familiaridad con la API JME y no se pretende desarrollar la utilización de la MMAPI más allá de breves introducciones para entender ejemplos puntuales. Además y para no complicar las cosas con problemas de fragmentación nos centraremos en terminales Nokia de la S60.
Problemas propios del streaming en JME
Algunos años atrás la imposibilidad de llevar streaming de vídeo al móvil tenían una sencilla explicación: El exiguo ancho de banda de las redes GPRS sumado a la escasez de recursos de los teléfonos móviles, eran factores tan restrictivos que durante mucho tiempo no tuvo sentido pensar en implementar una API Java que intentara proporcionar acceso a servicios de streaming. Ya desde el inicio, la calidad del resultado, en caso de obtenerse alguno, se sabía muy comprometida.
Pero hay que recordar que allá por el 2000 el consumo de streaming era incipiente aún en los propios ordenadores de mesa. Supongo que la variante más utilizada de streaming, hasta YouTube, habrá sido la videoconferencia.
Los esfuerzos de la comunidad Java en materia de multimedia para móviles recién comenzaron a mediados de 2001. Cuando la Java Community Process acordó desarrollar una especificación formal que tratara esta temática.
Así el 11 de Junio de 2002 resultó aprobada la liberación de la primera Final Release de la JSR-135 con el único voto en contra de Philips que de acuerdo a la información suministrada por el sitio de la JCP (www.jcp.org) estaba en desacuerdo por la estrecha relación entre esta JSR y la Java Media Framework (JMF 1.0).
A esa especificación ( JSR-135) se la conoce también como Multimedia API (MMAPI). Desde esa primer especificación hasta ahora los esfuerzos continúan. De entrarse hoy a la JCP para la JSR-135 nos encontraríamos con que la ultima release fue realizada el 22 de Junio de 2006.
Especificaciones e implementaciones
Lo concreto es que, en materia de móviles, las JSR o especificaciones emanadas de la JCP representan poco más que cartas de intención. Despues de acordadas estas, todavía queda por ver la calidad de la implementación que los fabricantes estén dispuestos a proporcionar a la comunidad JME.
Y las implementaciones, tanto de la MMAPI como de la propia JME, podrían encabezar una ranking de implementaciones defectuosas. En parte algunos de los problemas de implementación de JME actuales vienen de herencia de las primeras máquinas virtuales 1. Pero en el caso de la MMAPI las baja calidad de las implementaciones Java tendrá su origen, en alguna combinación ponderada de los siguientes factores:
- La complejidad computacional del tratamiento multimedia (sobre todo en materia de players y de codecs).
- Los problemas de implementación de la propia API JME (pienso por ejemplo en sus problemas a nivel de sockets).
- El desinterés de los fabricantes, inmersos en una carrera permanente por sacar terminales nuevos, que respondan a tendencias inmediatas del mercado.
- Las operadoras que tal vez no vean claro o no tengan interés en que el streaming ocurra por fuera de sus portales.
En cualquier caso y sea de quién sea la culpa de estas pobres implementaciones, el caso es que muchos desarrolladores han tratado de ejecutar código que, en concordancia con la especificación debería funcionar y les ha fallado, sumiéndolos en el desconcierto. Es moneda corriente en los foros JME encontrar snippets sobre streaming y comentarios intercalados del estilo “ME FUNCIONA !”, “NO ME FUNCIONA !”. Pero la pura realidad es que lograr consumir streaming desde un móvil es muy sencillo si es que la implementación MMAPI lo permite.
Bueno, creo que estoy olvidando a las operadoras. Corrijamos entonces; es sencillo si la implementación de la MMAPI hace lo que tiene que hacer y si la operadora nos deja. Para poder consumir streaming desde una red cualquiera el firewall, si existe, debe permitir tráfico RTSP (sobre TCP, usualmente en el puerto 554) y RTP (que va sobre UDP).
El proyecto Helix Bindings
Uno de los intentos más serios de dotar a los móviles con implementaciones de la MMAPI que soporten RTSP es el llevado a cabo por Helix, que es el desprendimiento open source de RealNetwork. El proyecto llamado JSR-135 bindings se basa en la creación de un middleware que ofrece una interfaz JME coherente con la MMAPI. Conseguida a partir de encapsular la funcionalidad del motor multimedia que llevan los player de RealNetwork (RealPlayer). De esta forma se puede disponer de una implementación de la MMAPI realmente funcional para streaming.
En la Figura 1 se puede observar la relación entre las clases Java de la MMAPI y las clases C++ (recordemos que el motor de RealPlayer esta desarrollado en C++) que extraen la funcionalidad correspondiente y la entregan a la MMAPI.
Además en ningún caso se dispone de código fuente Java relacionado con el corazón de la MMAPI, ya que como vemos en la Fig. 1 todo termina transformándose en llamadas JNI a clases C++.
Streaming Java en un minuto
Para entrar en tema con streaming debemos primero comenzar hablando del par RTSP/RTP. Que son los principales protocolos para streaming (en rigor, también se hace streaming http). Y donde RTSP es un protocolo que utiliza TCP como transporte y que provee al cliente control sobre el servidor (para ordenarle play, pause, etc.). Y RTP es prácticamente un empaquetado del contenido multimedia, propiamente dicho, transportado sobre UDP.
De forma que para acceder a un archivo multimedia en modalidad streaming el primer paso es tener una URL de protocolo RTSP que sirva el archivo deseado desde algún lugar de la red. Los servidores http tradicionales no sirven para esto. Es necesario utilizar servidores de streaming, esto es servidores específicamente diseñados y construidos para distribuir streaming.
Cabe acotar que los protocolos involucrados en una sesión de streaming dependerán de la modalidad de streaming. Si se trata de streaming Unicast (por ejemplo VoD) ambos protocolos son necesarios. Si en cambio la modalidad de streaming es del tipo broadcasting (TV) el control (usualmente provisto por RTSP) se realiza a nivel de cliente y por lo tanto solo se necesita RTP para transportar la media.
Veamos, ahora sí, las tres clases fundamentales de la MMAPI:
Player: Objetos para renderizar contenidos multimedia.
Control: Objeto para controlar el comportamiento de los objetos Player.
Manager: Objeto que provee Players (y fuentes de datos).
La relación entre ellas es bastante intuitiva: Con Manager obtenemos un Player para renderizar y de este, a su vez, obtenemos un objeto Control para controlar el renderizado.
Una vez conozcamos la URL (que será del tipo "rtsp://...."), la especificación dice que una de las formas de obtener un player es utilizando esta URL como parámetro de un método estático de la clase Manager. Esto es, deberíamos escribir en nuestro MIDlet algo parecido a:
java:streaming1.java;
private void streaming(){
try{
player = Manager.createPlayer("rtsp://....");
player.addPlayerListener(this);
player.realize();
player.prefetch();
player.start();
}catch(Exception e){
log("[startStreaming] Diantres!, mi player no funciona"+e.toString());
}
}
En este snippet, la sucesión de métodos que siguen a la definición de nuestro MIDlet como listener se corresponden con el ciclo de vida de un objeto Player. El cual podemos observar en la figura 2.
Este gráfico nos viene a decir que cuando un Player es creado se encuentra en el estado UNREALIZED. En este estado el reproductor no dispone de información suficiente para localizar los recursos necesarios para comenzar la reproducción. Una vez haya localizado los datos pasará al estado REALIZED. Por ejemplo, en caso de querer reproducir un fichero de audio de un servidor a través de una conexión RTSP, la transición se produciría al recibir la respuesta del servidor a la petición que previamente se cursó ya que en este punto el reproductor estaría en condiciones de empezar a recibir los datos a reproducir.
El siguiente estado por el que pasaría es el de PREFETCHED. Se llega a él cuando el reproductor ha recibido suficiente cantidad de datos para comenzar con la reproducción. La utilidad de definir este estado radica en que llegan a él los reproductores que están listos para empezar con la reproducción de datos. De esta manera se reduce el tiempo de latencia desde que se indica que se inicie la reproducción y el instante en que realmente comienza. En este punto, cuando haya dado comienzo la reproducción se llegaría al estado STARTED.
Los móviles que no tienen una implementación de la MMAPI fidedigna suelen devolver un Player nulo, con lo cual nuestro try-catch escaparía por “Diantres!...”.
Para poder probar un MIDlet recibiendo streaming debes, obviamente, conseguirte un servidor de streaming, ya sea montándotelo tú o consiguiendo alguna URL de protocolo RTSP. Para la primera opción puedes por ejemplo instalarte un servidor Helix DNA (que es gratuito), pero obviamente para trabajar con un móvil sobre una red 3G deberás instalarlo sobre un host publicado. Además este servidor solo permite, gratuitamente, distribuir los formatos multimedia propios de RealNetwork (.rm etc.). Si solo te interesa trabajar streaming desde el cliente y probar varios formatos, es mejor localizar algún servidor de esos que normalmente sirven noticias. Aquí os dejo unos cuantas URL’s para que probeis:
- rtsp://media.cbc.ca/cbc.ca/nl/media/news/herenow.rm
- rtsp://rmv8.bbc.net.uk/news/media/avdb/news/world/video/89000/nb/89824_16x9_nb.rm?title="BBC"&author="BBC"©right="(C)%20British%20Broadcasting%20Corporation
- rtsp://rm.bbc.net.uk/news/olmedia/1335000/video/_1335876_raper_vi.rm
- rtsp://a384.v373744.c37374.g.vr.akamaistream.net/ondemand/7/384/37374/1.0/clipdownloads.bbc.co.uk/realmedia/news/media/avdb/news/science_nature/video/115000/nb/115979_16x9_nb.rm?title="BBC"&author="BBC"©right="(C)%20British%20Broadcasting%20Corporation
- rtsp://a844.l1856741633.c18567.g.lr.akamaistream.net/live/D/844/18567/v0001/reflector:41633
Una vez localizado el servidor de streaming el siguiente MIDlet debería funcionarte sin problemas. Al menos en móviles Nokia S60 3rd Edition funciona (yo lo he probado en un N93).
java:StreamingMidlet.java;
package org.lm2a.player;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
public class StreamingMID extends MIDlet implements CommandListener,
PlayerListener, Runnable {
private Display d;
private Form form;
private Alert alert;
private StringItem s;
private Thread streamingThread;
private Player player;
private VideoControl vc;
private String url = "rtsp://media.cbc.ca/cbc.ca/nl/media/news/herenow.rm";
private String label = "news BBC Canada";
public StreamingMID() {
d = Display.getDisplay(this);
form = new Form("Streaming Test");
s = new StringItem(null, label);
form.append(s);
form.addCommand(new Command("Salir", Command.EXIT, 0));
form.addCommand(new Command("Comenzar", Command.OK, 0));
form.setCommandListener(this);
}
protected void startApp() throws MIDletStateChangeException {
d.setCurrent(form);
streamingThread = new Thread(this);
}
protected void pauseApp() {
}
protected void destroyApp(boolean unconditional) {
try {
player.stop();
player.close();
} catch (Exception e) {
log("[destroyApp]Exception: " + e.toString());
}
}
private void streaming() {
try {
player = Manager.createPlayer(url);
player.addPlayerListener(this);//registramos el midlet para escuchar cualquier
//evento generado por el Player.
player.realize();
player.prefetch();
// Obtenemos el objeto VideoControl y lo agregamos al objeto Display
//para escalar y posicionar la imagen.
vc = (VideoControl) player.getControl("VideoControl");
if (vc != null) {
form.append((Item) vc.initDisplayMode(
VideoControl.USE_GUI_PRIMITIVE, null));
vc.setDisplaySize(237,190);//estos valores están ajustados para un N93 y
//deben corresponderse con la medida de la pantalla
//del móvil donde lo ejecutes
}
player.start();
} catch (Exception e) {
log("[startStreaming]Exception: " + e.toString());
try {
player.prefetch();
vc = (VideoControl) player.getControl("VideoControl");
if (vc != null) {
form.append((Item) vc.initDisplayMode(
VideoControl.USE_GUI_PRIMITIVE, null));
vc.setDisplaySize(237,190); }
player.start();
} catch (MediaException e1) {
log("[startStreaming] MediaException: " + e1.toString());
}
}
}
public void commandAction(Command c, Displayable s) {
if (c.getCommandType() == Command.EXIT) {
notifyDestroyed();
} else {
streamingThread.start();
}
}
public void playerUpdate(Player player, String event, Object eventData) {
log("Evento detectado: " + event);
}
public void log(String msg) {
alert = new Alert("Mensaje", msg, null, AlertType.CONFIRMATION);
alert.setTimeout(3000);
d.setCurrent(alert, form);
}
public void run() {
streaming();
}
}
Recordar que si usais una Operadora no rtsp-friendly podeis recibir una excepción del tipo:
MediaException: Prefetch error: -34
Este tipo de errores (de prefetch) suelen deberse a que no hay suficiente ancho de banda (red gprs en vez de 3G) o a que la operadora no permite streaming. Si todo eso está bién asegúrate de que el cliente RealPlayer tenga definido un punto de acceso. Estos códigos de error son propios de Symbian (en http://www.symbianer.com/post/10.html puedes ver una tabla ilustrativa).
En España y al día de hoy Vodafone no es rtsp-friendly y siendo que yo vivo en Canarias y Yoigo aquí usa la red de Vodafone tampoco puedes hacer streaming en Yoigo (tal vez si donde tiene antenas propias). Si usas Movistar debería ejecutar sin mayor problema. Y aunque al día de hoy no lo he probado, en el pasado funcionaba también con Orange.
Averiguando capacidades
Resulta oportuno considerar que la MMAPI contempla el que se puedan averiguar previamente las capacidades multimedias del dispositivo.
Por ejemplo para saber que protocolos soporta:
- getSupportedProtocols(String contentType);
- getSupportedContentTypes(String protocol);
- System.getProperty("streamable.contents");
Pero es conveniente tomar con pinzas sus respuestas. Ya que estas a veces omiten declarar soporte de carácteristicas que en la práctica soportan. O declaran soportar cosas que luego soportan mal o caprichosamente. En particular los tipos devueltos por streamable.contents son muy poco fiables. La conclusión de este confuso panorama es que no se debe dejar de probar en los dispositivos antes de descartar nada, porque los métodos de consulta previa pueden fallar.
Como hacer streaming en dispositivos que no lo soportan
Hay un artículo o más bien una presentación que trató el tema en la JavaOne de 2006 que comentaba este tipo de problemas y proponía una variante de streaming que en términos teóricos parece factible, pero que por alguna razón hasta donde yo sé aún no fue implementada.
Como dijimos antes, la gran mayoría de los teléfonos móviles que hay hoy en el mercado pueden no soportar streaming RTSP. Pero es un hecho que la casi totalidad de ellos soportan el download de objetos multimedia via http. Y esta puede ser la clave para crear un variante de streaming http viable en implementaciones pobres de la MMAPI. Esta variante permitiría llevar streaming a la gran mayoria de los móviles Java que hay hoy en el mercado.
Hay que considerar que un objeto multimedia obtenido por http debe bajarse completo antes de comenzar su renderizado. Pero esto choca frontalmente con la definición de streaming. En la Fig. 3 vemos la hipotética forma de lograrlo. En la red fija creamos un proxy server que se encarga de obtener el archivo multimedia desde su servidor original. Ese archivo es procesado luego en 1, troceándolo en n fragmentos de aproximadamente 2 Kb (1 en el gráfico). Este proxy debería también ser capaz de clasificar los trozos para administrar su servicio hacia los clientes en forma coherente con la temporalidad del contenido, (tal como hace RTP).
Luego de trocearlos, en 2, se le agrega a cada trozo lo necesario para convertirlo en un archivo válido, esto es un Header y un EOF y se lo envía luego utilizando TCP como transporte.
Por el lado del cliente deberían crearse al menos dos threads (hilos) de ejecución, cada uno asociado a un player distinto. De esta forma el cliente actuaría, en 3, como un dispatcher round robin ejecutando en forma concurrente ambos hilos, y conmutando el renderizado de los trozos de forma de solapar la descarga con la ejecución en la forma más imperceptible posible para el usuario.
Es claro que el resultado final no será perfecto y es claro que se debe ser muy cuidadoso en la implementación ya que la operación de destroceado puede resultar estresante para dispositivos de poco poder de cálculo y/o de poca memoria. Pero lo bueno de esta estrategia es que al tener más control sobre el flujo entrante podremos mejorar la experiencia del usuario aunque la MMAPI no colabore. Por ejemplo, si sabemos que accederemos a un archivo que se dividirá en 10 trozos y que cada trozo ocupará 15 segundos de ejecución podríamos contar el tiempo internamente e ir mostrando información al usuario en una barra de progreso de forma que este pudiera saber en todo momento en que posición del objeto multimedia se encuentra. Además como estamos basando nuestro streaming en http es claro que no tendríamos que experimentar problema alguno de restricciones a nivel de operador.
Otro intento de dotar de capacidad para consumir streaming RTSP se puede encontrar en Experiments in Streaming Content in Java ME. En este artículo, Vikram Goyal intenta crear a pulso un soporte para RTSP donde no lo hay. Pero después de muchas páginas de densos desarrollos J2ME el resultado es que el engendro solo comienza a renderizar después de leer entero el archivo multimedia. A lo cual, ni con la mejor buena voluntad se le puede llamar streaming.
En cualquier caso el artículo es rescatable por el tratamiento de cuestiones relativamente atípicas como son la creacion de objetos DataSource customizados o de clases para dar soporte de protocolo RTP o handlers para RTSP.
El problema de la cobertura
Se sabe que las redes wi-fi pueden presentar pérdidas de paquetes y disminuciones o inclusive fallas de cobertura. Habitualmente motivadas por la posición espacial del receptor con respecto a las antenas cercanas.
Si ocurriera que estuviéramos recibiendo streaming en nuestro móvil, las consecuencias de la desaparición de la cobertura dependerían de las circunstancias. Si estamos por ejemplo en movimiento, tal vez el fallo tenga su origen en que, en nuestro desplazamiento, ingresamos a una zona sin cobertura (un tunel por ejemplo).
Este tipo de fallos en situaciones de streaming suele ser soportado por el ring-buffer del player del dispositivo (las arquitecturas de redes contemplan además otros niveles de buffering, pero estos no serían de aplicación en caso de corte de cobertura). Pero si nuestra permanencia fuera de cobertura (por ejemplo en el tunel) es suficientemente como para que el player alcance a consumir los datos contenidos en el buffer del dispositivo, la aplicación necesariamente lanzará un mensaje de error y la sesión mantenida hasta ese momento con el servidor multimedia se verá extinguida. Esto supondría entonces que aunque recuperáramos luego la cobertura, no podríamos proseguir con el visionado del contenido multimedia y deberíamos recomenzar desde el inicio, estableciendo una nueva sesión con el servidor.
Si el tipo de streaming es del llamado VoD (Video On Demand) eventualmente podría considerarse crear un Player (los actuales aún no ofrecen esta funcionalidad) capaz de solicitar al servidor el tramo de contenido que sigue al del punto de interrupción (ya que el protocolo RTSP contempla solicitar el streaming por tramos). Pero si el tipo de streaming es el llamado broadcasting, donde no existe interacción posible con el servidor, en ese caso la recuperación del material distribuido durante la caída resulta imposible. Solo podríamos volver a recibir contenidos desde el momento de la conexión en adelante.
La solución
El tratamiento de esta problemática es el motivo de mi trabajo de tesis y la manera que propongo allí de resolverla es mediante el uso de una arquitectura de doble Proxy, llamada Two Proxy Servers (TPS). Esto es, se requiere un proxy en la red fija (Proxy Server Fixed), que se encargaría de mantener sesión y de almacenar localmente los paquetes RTP mientras ocurre la desconexión del cliente. Y otro en el propio móvil (Proxy Server Wireless) que controlaría el nivel de conexión, pausaria el player (si es que detecta una desconexión) e intentaría luego la reconexión, periódicamente.
Una vez lograda ésta, se comunicaría con el Proxy de la red fija para indicarle, mediante un protocolo propio (se puede ver un borrador de este protocolo en la Tabla 1), que debe recomenzar el envío de paquetes desde una paquete dado.
| Sentido de la comunicación | Sintaxis propuesta | Significado |
| PSF-> PSW | [PORTS:puerto_cliente:puerto_servidor] | Informa al PSW los puertos UDP definidos por el Servidor multimedia para evitar el parseo del tráfico RTSP en el PSW. |
| PSW->PSF | [PING] | Chequea que el PSF esté sirviendo |
| PSF->PSW | [ALIVE] | Responde al PSW la orden ping |
| PSW->PSF | [ACK seq# j] | Informa la recepción de n paqueteses, el último de los cuales fue el número j. |
| PSW->PSF | [CVL seq# i] | Informa reconexión y el número del último paquete recibido. |
| Tabla 1: Tabla 1 Protocolo TPS mínimo |
A nivel de componentes la arquitectura propuesta se puede ver en la figura 4:
Y la interacción de estos componentes puede verse en el diagrama de colaboración de la Figura 5:
Allí podemos observar en 1 como el cliente envía mensajes RTSP al PSW. Este a su vez en 2 los re-envía al PSF, quién a su vez en 3 los hace llegar al servidor. Luego de establecida la sesión y definidos los puertos (los cuales son informados al PSW por el PSF mediante un mensaje PORTS) comienza en 4 el envío de paquetes RTP. Nos referimos al tráfico basado en UDP que transporta los objetos multimedia propiamente dichos. Además de RTP, existen otros formatos propietarios como Real Data Transport (RDT) de RealNetwork. Vemos en 5 como el PSF, además de enviarlo al PSW, lo almacena en una estructura de colas para su posterior utilización en caso de desconexión. En 6 el paquete RTP llega al PSW, quién en 7 lo envía al cliente. En 8 vemos como el PSW implementa un mecanismo de ACK de los paquetes RTP. Y en 9 vemos la implicancia de este ACK; el borrado del material confirmado de la caché del PSF. Más adelante (en Cacheo y estrategia de Acknowledgment) analizaremos en detalle tanto la estructura de colas como la estrategia de acknowledgement que hemos diseñado para nuestra arquitectura.
En 10 observamos que se produce una interrupción de la conexión. Acto seguido en 11 el PSW pausa al cliente para manejar la interrupción sin perder la sesión en el cliente. Inmediatamente vemos en 12 como el PSW intenta la reconexión con el PSF. Esta reconexión se basa en establecer conexión TCP con PSF y en el envío de mensajes tipo PING (del protocolo PSP, ver Tabla 1).
El PSF por su parte sigue recibiendo tráfico UDP por parte del servidor, para quién su cliente, (el propio PSF) no ha sufrido desconexión alguna. Este tráfico es almacenado en la estructura de colas. Cuando finalmente el PSF recibe el mensaje PING del PSW, contesta a este con un mensaje ALIVE y, según observamos en 15, reanuda el envío de paquetes RTP desde la cola (si hubiera más de un cliente el PSW debería enviar el número del último paquete recibido).
Recibido el mensaje ALIVE por parte del PSW, vemos en 16 como este des-pausa el cliente para reanudar la visualización. Y en 17 como comienza el re-envío hacia el cliente de paquetes RTP. En 18 vemos la reanudación de los envíos de ACK. Y en 19 el borrado, de la caché, del material confirmado.
El nivel de generalidad de esta arquitectura de doble proxies la torna suceptible de ser adoptada como patrón, ya que ofrece una solución para tratar la interrupción de la cobertura en medio de cualquier tipo de procesamiento sobre redes wireless.
Implementación de esta estructura
La posibilidad de desarrollar un Proxy RTSP/RTP Java, en un dispositivo móvil, también choca, actualmente, con fallos de la implementación de la API JME. En particular, siendo que el Proxy es en definitiva un servidor local para el cliente, su implementación requiere la apertura de sockets servidores en el dispositivo, lo cual es bastante poco frecuente. Tal vez por ello la implementación JME relacionada con estos aspectos, es poco confiable.
De hecho el método available() de la clase InputStream, (que es el encargado de comunicar al cliente el número de bytes por leer), devuelve siempre cero independientemente de que haya o no bytes por leer. Así un cliente que no esté en conocimiento de esta circunstancia (por ejemplo un cliente desarrollado en otro lenguaje) puede no registrar jamás que haya bytes por leer y la ejecución del protocolo RTSP para peticionar un contenido cualquiera, quedaría así eternamente detenida, esperando la primera respuesta del servidor.
En cualquier caso y como dijimos líneas arriba, al permitir RTSP la petición de segmentos de contenidos, otra alternativa (en lugar del Proxy RTSP en el cliente) es modificar el player para que, una vez reconectado, solicite el tramo que empieza en el punto de la interrupción. Con esta solución no se requiere la estructura de doble proxy en tanto que no tiene sentido almacenar paquetes que puede reenviar el servidor. No obstante, la estructura proxy completa (RTSP/RTP) es realizable, en el caso de móviles Symbian, en C++.
Pero no se debe perder de vista que donde es realmente importante el control de la salida de cobertura es cuando el contenido multimedia es del tipo tiempo real (multicasting). Ya que en tales casos la retransmisión de contenidos, por parte del servidor, es inviable. En tales casos solo se utiliza el protocolo de transporte (RTP). Y siendo que RTP esta soportado por UDP los problemas de sockets TCP no resultan de aplicación y la estructura de doble proxy funciona sin mayor problema. Resultando incluso más adecuada (al evitar el parseo de RTSP) para las limitaciónes de recursos propias de los teléfonos móviles.
Conclusiones
Podemos concluir entonces que se pueden crear players JME RTSP en tanto la MMAPI lo permita. Las soluciones por fuera de ella, no parecen muy viables. Esto significa que SOLO los terminales que lo soporten podrán recibir streaming en un cliente JME. Con lo cual si deseamos desarrollar aplicaciones para ofrecer streaming a teléfonos móviles, debemos saber que cualquier producto Java, hoy por hoy, solo podrá alcanzar un porcentaje muy bajo del mercado actual. A la fecha (Agosto 2007), no creo que los móviles que soporten streaming JME lleguen al 10% del mercado actual de dispositivos.
Pero la renovación de terminales por parte de los usuarios es bastante veloz y en la medida que aparezcan servicios de VoD o TV on-line, muchos usuarios tratarán de renovar sus contratos de operadora con dispositivos que lo posibiliten. Como suele pasar en tecnología, la solución de esto, es solo cuestión de tiempo y, normalmente, de menos tiempo del esperado.
Una vez solucionado el problema a nivel de API todavía resta que los usuarios de nuestra aplicación reciban servicio de una operadora RTSP-friendly.
Finalmente y con respecto al control de la salida de cobertura durante sesiones de streaming, es factible abordar una solucion basada en la arquitectura aquí expuesta (TPS). Y no debe descartarse que en tren de garantizar la recepción completa de contenidos distribuidos en movilidad aparezcan en el futuro proxies dedicados a ofrecer este servicio, del estilo del aquí descripto como PSF y players con capacidad de comportarse como el también aquí descripto PSW. De aquí que se haya considerado oportuno plantear la necesidad de un protocolo propio que permitiera la comunicación entre estos componentes independientemente de quién haya desarrollado cada uno.
Recursos
[1] Experiments in Streaming Content in Java ME , http://today.java.net/pub/au/179
Acerca de los autores
Mario La Menza
Mario La Menza - Líder de Proyectos para desarrollos de movilidad - Inerza SA.
Alvaro Suarez Sarmiento
Grupo de Arquitectura y Concurrencia - ULPGCElsa Macías
Grupo de Arquitectura y Concurrencia - ULPGC
Copyright (c) 2007, Mario La Menza, Alvaro Suarez, Elsa Macías Este documento puede ser distribuido solo bajo los términos y condiciones de la licencia de Documentación de javaHispano v1.0 o posterior (la Última versión se encuentra en /licencias/).
j2me 
Reader Comments