martes, diciembre 05, 2006

Distribuir el JMF con nuestras aplicaciones

Programar con las librerías del JMF es un auténtico tormento, hacer que funcionen es más difícil todavía, y realizar el despliegue de estas aplicaciones a gran escala es casi imposible. Normalmente intento evitar usar el JMF, pero muchas veces no queda más remedio. En estos casos intento usar la versión multiplataforma de estas librerías, ya que simplemente es necesario incluir los jars en el CLASSPATH para que funcione.

El número de formatos soportados por la versión multiplataforma del JMF es patético, si bien es cierto que el soporte de las versiones para las Windows y Linux no es mucho mejor, al menos es "algo" mejor. Por esto a veces no queda más remedio que usar este tipo de versiones. Tan sólo queda esperar que el panorama mejore ahora con el cambio de licencia del software de Java.

Si usas el JMF en una aplicación la solución más evidente es indicar al usuario que tiene que instalar el JMF. En el caso de Linux la instalación del JMF en Linux no está al alcance al usuario medio. En el caso de Windows la instalación es mucho más sencilla, pero si el usuario tiene activadas las actualizaciones automáticas de java, cada vez que el usuario actualice su versión java, el JMF dejará de funcionar y tendrá que instalarlo de nuevo. Esta situación normalmente es inadmisible para cualquier persona/empresa que distribuya aplicaciones en Java.

Por último, si te gusta (como a mi) distribuir la aplicación con su propio JRE (para evitar tener que explicarle al usuario que es eso de JAVA), también es deseable integrar el JMF para que el usuario no tenga que instalar ningún paquete adicional.

Integrar la versión multiplataforma del JMF en nuestras aplicaciones no tiene ninguna historia, es una librería empaquetada en un jar como cualquier otra libería en java. El caso de las versiones específicas para cada una de las plataformas es distinto. A continuación voy a comentar mis experiencias a la hora de distribuir el JMF con mis aplicaciones.

Distribuir JMF 2.1.1e (Solaris/Linux Performance Pack) en Linux

Para poder distribuir el JMF con nuestras aplicaciones en Linux, primero tenemos que instalarlo en una máquina con Linux (una opción es usar VMware, si no tenemos ninguna máquina con Linux). Una vez instalado deberemos copiar todo el contenido del directorio lib y el fichero readme.html a un subdirectorio de nuestra aplicación que podemos denominar jmf_linux. El fichero readme.html hay que distribuirlo para cumplir con la licencia de distribución del JMF.

En el fichero de arranque de nuestra aplicación debemos establecer una serie de variables de entorno para que nuestra aplicación reconozca el JMF (todas estas sentencias deberían estar en el script de arranque de la aplicación).

export JMF_HOME="${INSTALL_PATH}/jmf_linux"
export CLASSPATH="${JMF_HOME}/jmf.jar:${JMF_HOME}/mediaplayer.jar:${CLASSPATH}"
export LD_LIBRARY_PATH="${JMF_HOME}:${LD_LIBRARY_PATH}"

Además de estas variables debería estar establecida previamente la variable de entorno $INSTALL_PATH, indicando el directorio de instalación. Posteriormente en el mismo script ejecutaremos nuestra aplicación indicando el classpath correcto, por ejemplo:

java -cp $CLASSPATH:lib/app.jar Aplicacion

Distribuir JMF 2.1.1e (Windows Performance Pack) en Windows
La distribución del JMF en Windows es bastante más complicada que en Linux, ya que en Windows no hay un método sencillo de indicar en donde se encuentran las librerías nativas (por defecto intenta buscarlas en C:\Windows\system32, que es donde las pone el instalador el JMF para Windows). En primer lugar debemos instalar el JMF en un ordenador con Windows, una vez instalado localizaremos el directorio de instalación (normalmente C:\Archivos de Programa\JMF2.1.1e) y copiaremos todos los contenidos del subdirectorio lib y el fichero readme.html en un subdirectorio de nuestra aplicación que llamaremos jmf_windows.

A partir de aquí empieza más complicado, ya que primero deberemos localizar las dlls necesarias para el JMF en el directorio de librerías del sistema (normalmente C:\Windows\system32) y copiar esas dll al directorio raiz de nuestra aplicación. Las dlls que necesitamos son las siguientes: jmacm.dll, jmam.dll, jmcvid.dll, jmdaud.dll, jmdaudc.dll, jmddraw.dll, jmfjawt.dll, jmg723.dll, jmgdi.dll, jmgsm.dll, jmh261.dll, jmh263enc.dll, jmjpeg.dll, jmmci.dll, jmmpa.dll, jmmpegv.dll, jmutil.dll, jmvcm.dll, jmvfw.dll, jmvh263.dll, jsound.dll

La razón por la que debemos copiarlas al directorio raíz de nuestra aplicación es que Windows por defecto también busca las librerías nativas en el directorio de ejecución del programa. Hay que tener cuidado con este detalle, ya que debemos asegurarnos que el directorio de ejecución es siempre ese, incluso cuando se ejecuta desde accesos directos y similares.

Una vez creado el directorio jmf_windows y copiadas las dlls al directorio raiz de nuestra aplicación podremos arrancar nuestra aplicación a través de un fichero .bat, por ejemplo:

java -cp .;lib\app.jar;jmf_windows\jmf.jar;jmf_windows\sound.jar Aplicacion

Crear un instalador multiplataforma

La mejor opción para crear un instalador multiplataforma para aplicaciones en Java es IzPack, este instalador proporciona muchas opciones que podemos consultar en su documentación, pero la que más nos interesa es la posibilidad de indicar paquetes diferentes para diferentes plataformas (en nuestro caso Windows y Linux). Podemos meter en un paquete el núcleo de nuestra aplicación, y en dos paquetes diferentes los dos JMFs, uno para Linux y otro para Windows.

Así en el fichero xml en el que definimos la generación del instalador indicaremos dos paquetes similares a los siguientes:

<pack name="Java Media Framework" required="yes">

<os family="unix" />

<description>Java Media Framework</description>


<fileset dir="jmf_linux" targetdir="$INSTALL_PATH/jmf_linux" />

<file src="aplicacion.sh" targetdir="$INSTALL_PATH" os="unix" />

<executable os="unix" keep="true" failure="warn" stage="never"

targetfile="$INSTALL_PATH/aplicacion.sh" />

</pack>


<pack name="Java Media Framework" required="yes">


<os family="windows" />


<description>Java Media Framework</description>


<fileset dir="jmf_windows" targetdir="$INSTALL_PATH/jmf_windows" />


<fileset dir="jmf_windows_dll" targetdir="$INSTALL_PATH" />


<file src="aplicacion.bat" targetdir="$INSTALL_PATH" os="windows" />


</pack>



De esta forma podremos estar seguros que en cada plataforma estará disponible el JMF apropiado.

Problemas comunes del JMF en Linux
Las aplicaciones el JMF no funcionan en las últimas distribuciones de Linux, ya que con el Toolkit por defecto de java (sun.awt.X11.XTookit) en Linux no funcionan las aplicaciones del JMF. Por esto al ejecutar las aplicaciones jmfregistry y jmfstudio es necesario editar los ficheros y añadir a la línea de ejecución de la aplicación el Toolkit adecuado, por ejemplo en la última línea del jmfregistry:

exec java -Dawt.toolkit=sun.awt.motif.MToolkit JMFRegistry

Para depurar la carga de librerías nativas en Linux disponemos de múltiples métodos, uno de ellos es localizar el pid de nuestra aplicación y revisar el fichero /proc/num_pid/maps para ver si se ha cargado la librería necesaria. Otra opción es establecer la variable de entorno LD_DEBUG y obtendremos información sobre la carga de librerías dinámicas en la salida estándar del programa:

export LD_DEBUG=xxx

Para ver los posibles valores que puede tomar LD_DEBUG podemos revisar la página del manual con "man ld.so". En la misma página del manual se puede ver que con la variable de entorno LD_DEBUG_OUTPUT podemos indicarle un fichero en el que volcar esta información.

También podemos obtener información muy valiosa del log del jmf. Para activar la generación de este fichero de log e indicar el directorio en el que queremos que lo escriba (cuidado con tener permiso de escritura en ese directorio), tenemos que ejecutar la aplicación jmfregistry.

Además de la carga de librerías nativas en Linux, hay otra serie de errores difíciles de depurar en Linux. A menudo tenemos aplicaciones que hacen uso intensivo de Threads y nos funcionan en Windows y en Linux nos encontramos con un desagradable deadlock. Java proporciona un sistema para poder depurar este tipo de errores, ya que si enviamos la señal nº 3 al proceso este mostrará en la salida estándar información sobre los threads y posibles deadlocks. Si nuestra aplicación tiene el pid nº 101 (podemos localizar el pid de la aplicación con el comando ps), podemos hacerlo así:

kill -3 101

Me imagino que muchos de vosotros habreis tenido problemas en la distribución de aplicaciones multiplataforma en Java, me gustaría escuchar vuestras experiencias al respecto a través de comentarios.

Edición: Hay que tener la precaución de poner el jmf.properties con los codecs que necesitamos en el directorio en el que se encuentre el jmf.jar.

Del.icio.us Meneame