inmensia |
Flash
Juan Mellado, 31 Mayo, 2010 - 09:08
Por el momento no resulta muy espectacular, ya que las películas Flash que requieren más proceso se ejecutan de una forma bastante lenta. Va requerir un poco más de optimización. Para lo que sirven en realidad este tipo de proyectos es para darse cuenta de que muchas cosas que se hacen "por inercia" con Flash se pueden hacer también con otras tecnologías. El principal problema es que no existen herramientas tan sencillas y productivas como Flash para hacerlas. Hay es donde se tienen que poner las pilas los desarrolladores. Y evidentemente falta que Internet Explorer empiece a soportar todas estas tecnologías. Microsoft debería tomar nota para la versión 9 de su navegador, que ya le toca. Silverlight y HTML5 pueden coexistir. Según la web del proyecto, el código fuente de Smokescreen se liberará dentro de un tiempo. No obstante, a dia de hoy se puede analizar el JavaScript que se ejecuta en la página de las demos: http://smokescreen.us/demos/js/smokescreen.0.1.3-min.js Leyendo entre líneas, se puede ver que han implementado un parser de ficheros SWF, de igual forma que se observa algunos nombres de clases del core del runtime de Flash para el player. Lo que no parece tener soporte el reproductor es para ActionScript, al menos en su versión 3, aunque teniendo sólo el código "minificado" es bastante complicado de saber a ciencia cierta. En cualquier caso, si bien estos "experimentos" resultan interesantes, la guerra entre Flash y HTML5 se ha enfriado un poco con la liberación de la versión Flash 10.1 (beta) que ya se puede ejecutar en móviles con Android 2.2 (Froyo). Google lo tiene claro: dar soporte para Flash, y otros ingenios como Unity por ejemplo. Si los usuarios no quieren utilizarlo que no lo hagan, es algo opcional. En la pasada Google I/O 2010 se pudieron ver bastantes cosas relativas a este tema, incluida una presentación de una aplicación de Adobe, aún en pañales, para generar animaciones directamente en HTML5 de forma similar a como se hace actualmente en Flash.
Juan Mellado, 22 Mayo, 2010 - 08:30
Lo que han hecho es crear un SDK que incluye un reproductor sobre el que se tiene que ejecutar la aplicación Flash generada. Es como el reproductor standalone normal de Adobe, pero con un peculiaridad añadida. Al tiempo que se reproduce la aplicación se reproduce un fichero con información real capturada por los sensores de un coche en dos trayectos distintos. Uno durante un desplazamiento por una ciudad, y otro durante un viaje por una autopista. Y esa información se hace llegar al programa Flash en forma de eventos. De forma similar a como se gestionan los eventos de ratón o teclado por ejemplo. Todos los eventos disponibles están definidos en un paquete de clases ActionScript llamado "volkswagen.miniapps.interfaces", que también se incluye dentro del SDK, y que hay que importar dentro de la aplicación. Para recibir notificaciones sobre un evento determinado basta con definir un handler sobre él de la forma acostumbrada. Por ejemplo, para ser notificado en todo momento de la velocidad del coche, basta con escribir el siguiente sencillo código: import volkswagen.miniapps.interfaces.*; La velocidad del coche, la marcha engranada, el consumo de combustible, la latitud, longitud y altura respecto al nivel del mar, la temperatura exterior, e incluso la apertura o cierre de las puertas, el abrochado o desabrochado de los cinturones de seguridad, el encendido o apagado de las luces, pasando por la aceleración lineal, lateral o la fuerza que se está ejerciendo sobre los pedales, son algunos de los eventos que se pueden monitorizar. Incluso hay un evento que avisa si se está pulsando la bocina. El reproductor que incluye el SDK permite además simular eventos, introduciendo manualmente los valores que queramos, para poder probar las aplicaciones de una forma más precisa, ya que es imposible que los recorridos de ejemplo cubran todas las posibilidades. El plazo de recepción del concurso empezó a primeros de mayo y terminará a finales de junio. En la web se pueden ver las ideas propuestas hasta la fecha y las aplicaciones enviadas. Incluso permiten enviar juegos ¡que no distraigan a los conductores!. El tamaño de las aplicaciones está limitado a 380x380, y se insiste en que los controles han de ser grandes y con un alto contraste debido a que van a visualizarse en pantallas normalmente pequeñas y expuestas a la luz ambiente. Hay 6.000 euros en premios a repartir entre los tres primeros clasificados, más algún extra, como la posibilidad de optar a una beca si eres estudiante, o de disfrutar de un coche de la marca durante un fin de semana. En las bases del concurso se explican los requerimientos para entrar concursar, la cesión de derechos y demás de la forma acostumbrada.
Juan Mellado, 8 Mayo, 2010 - 09:00
Después de haber conseguido parsear con éxito un fichero Flash mediante un programa en JavaScript, me he animado a programar una máquina virtual, también en JavaScript, que ejecute el código ActionScript 3 leído del fichero SWF sin utilizar el plugin de Adobe. La ventaja de hacerlo en JavaScript es que ambos lenguajes se basan en la misma especificación ECMA-262, por lo que muchas cosas pueden traducirse directamente de un lenguaje a otro. Pero eso es en un mundo ideal, en la vida real ocurre que cada cual implementa la especificación de una forma ligeramente distinta, aparte de que la propia especificación deja margen para que ciertas partes se implementen de forma arbitraria. De hecho, es algo bastante conocido que la implementación del propio JavaScript varía de un navegador a otro. Para simplificar estoy suponiendo que el comportamiento del código ejecutado en JavaScript es lo suficientemente parecido al de ActionScript 3. Siempre habrá tiempo para ajustar las pequeñas diferencias que aparezcan. Para el código estándar que no fuerce la mezcla de tipos de forma innecesaria, ni se base en efectos laterales, debería bastar. Y de hecho, ya he conseguido ejecutar sin mayor problema pequeños programas ActionScript 3 compuestos por operaciones matemáticas, comparaciones y bucles. Siguiendo las especificaciones de Adobe, la ejecución de una función en ActionScript 3 requiere que la máquina virtual instancie una estructura de datos locales a dicha ejecución compuesta por una pila de operandos, una pila de ámbitos (scope) y un conjunto de registros. LocalDataArea = function(){Para empezar me he centrado en los opcodes que manipulan los registros o la pila de operandos, ya que normalmente son los más fáciles de implementar en cualquier tipo de emulador, y permiten tener en muy poco tiempo un prototipo ejecutando código. La estructura principal de la máquina vitual es un simple switch que determina el código a ejecutar en función del opcode actual sobre el contador de programa. switch(opcode){Dejo para más adelante las consideraciones acerca del rendimiento, y realizar pruebas para ver es si mejor, o no, tener una función para cada opcode con el objetivo de agilizar la localización e invocación del código asociado a cada opcode. De momento me he centrado en implementar la mayor cantidad de opcodes posibles, unos cincuenta en estos momentos, sobre un total de aproximadamente ciento cincuenta con los que cuenta la especificación. Una de las cosas "divertidas" de este proyecto es que me permite depurar código ActionScript 3 directamente desde el propio navegador mediante Firebug. Un fichero SWF generado en modo debug incluye opcodes de depuración con nombres de ficheros, números de línea y nombres de variable. Cuando se produce un error, como el intento de ejecución de un opcode no implementado, me basta con poner un punto de parada y ver que línea concreta del código fuente original ha generado el error, e incluso el nombre de las variables que contiene cada registro local en ese momento. ![]() Buscando proyectos similares por Internet me he encontrado con un fork de Gordon por Brian McKelvey. Tiene implementado el parser de ficheros abcFile (que contienen el código ActionScript 3 compilado) aprovechando la clase base de lectura de Gordon, pero aún poca cosa de la máquina virtual en si misma. De hecho, me ha dado la impresión de lo que pretende no es construir una máquina virtual que interprete el código en tiempo de ejecución, sino trasladar los opcodes a código JavaScript directamente en forma de texto para luego evaluar la cadena resultante. Habrá que seguirle la pista.
Juan Mellado, 1 Mayo, 2010 - 10:54
A partir de su versión 9, los ficheros Flash admiten bloques de código escritos en ActionScript 3 embebidos en su interior. Aunque lo que almacenan esos bloques dentro de los ficheros no es el código fuente, sino un conjunto de descriptores y cadenas de opcodes resultantes de su compilación. La máquina virtual de ActionScript del plugin de Flash interpreta en tiempo de ejecución esa información y ejecuta el código. Ha estado leyendo las especificaciones de la máquina virtual de Flash, y he acabado escribiendo un pequeño parser en JavaScript que lee las estructuras que contienen el código ActionScript de un fichero SWF dado. Aunque al final me ha interesado más la forma de encontrar el punto de entrada para su ejecución. Un fichero SWF se compone de una serie de bloques con un tipo y unos datos que deben interpretarse en función de ese tipo. Los bloques con el código ActionScript son de tipo DoABC y contienen una estructura de datos llamada abcFile que almacena un pool de constantes, las definiciones de las clases y métodos, y los scripts en forma de cadenas de opcodes. Opcodes de bastante alto nivel, por cierto. Es decir, que no se limitan a operaciones básicas de acceso a memoria, manipulación de registros y control de flujo, como los mnemotécnicos de un microprocesador ordinario, sino que incluyen operaciones tales como instanciar un objeto de una clase o "escapar" un elemento XML. Como resultado de ese procesamiento de alto nivel, una parte importante de la máquina virtual es la gestión de nombres. De paquetes, de clases, de métodos, de variables, ... Algunos conocidos en tiempo de compilación, otros resueltos en tiempo de ejecución, pero todos ellos englobados bajo el término multinames, compuestos por un namespace y un name. Por ejemplo, el multiname "com.inmensia.flash.test:Main" se compone del namespace "com.inmensia.flash.test" y el name "Main". Para probar mi parser he generado un fichero SWF con FlashDevelop utilizando el siguiente código extremadamente sencillo: package {En la siguiente imagen puede verse el objeto abcFile resultante de la ejecución del parser y capturado desde Firebug: ![]() Puede verse como todas las estructuras están rellenas con los valores leidos del SWF. No obstante, el punto de entrada del código dentro un frame que viene indicado en otro bloque del fichero SWF. Más concretamente en uno de tipo SymbolClass. Este bloque almacena una lista de los símbolos de alto nivel almacenados en el fichero, con un id numérico y un nombre. El nombre del símbolo con id igual a 0 es el que sirve de punto de entrada para la ejecución del código contenido en el fichero. La secuencia completa que he seguido para encontrar el primer opcode de la clase "Main" del ejemplo es la siguiente: En el siguiente diagrama puede verse la secuencia de pasos seguida para localizar el punto de entrada saltando de estructura en estructura, además del contenido del bloque SymbolClass referido anteriormente: ![]() He dejado que se vea el pool de strings completo, para que se aprecie la lista de cadenas que se monta a partir de un ejemplo tan sencillo, y la importancia de los nombres dentro de la máquina virtual. Como se observa, se parte de "Main", o lo que es lo mismo, del namespace "" (vacío) y del name "Main". En el pool de strings se localiza el id 3 correspondiente al name, y en el pool de namespaces el id 1 correspondiente a un namespace con id de nombre 1 (vacío) y tipo 22 que indica que corresponde al nombre de un paquete. En la documentación existe una serie de constantes definidas para estos tipos. A continuación, con el id del name y del namespace se localiza el multiname dentro del pool de multinames. Y con el id de dicho multiname la clase asociada dentro del pool de instances. Finalmente, para cada clase existe un atributo iinit que es el id de su método de entrada (constructor), y que permite encontrar la lista de opcodes a ejecutar dentro del pool de method bodies. Como comprobación final he decompilado los opcodes para verificar que se correspondían con el código ActionScript de partida: 208 getlocal_0Sin entrar en mucho detalle, se observa que el método se limita a guardar el scope (ámbito) en la pila, llamar al constructor de la clase base (flash.display.Sprite) sin argumentos, y retornar sin devolver ningún valor. Lo que concuerda con lo esperado. Por último, comentar que este punto de entrada es para una clase concreta como la del ejemplo. El punto de entrada real del fichero [en su totalidad] está determinado por la propiedad init del último registro del pool de scripts dentro del objeto abcFile, que lo que hace precisamente es instanciar nuestra clase "Main" de ejemplo.
Juan Mellado, 17 Abril, 2010 - 09:13
Antecedentes Ufffff....... Dejo a cada cual la interpretación de las decisiones empresariales y su conveniencia, yo prefiero centrarme en la parte técnica. Y más concretamente en la ejecución nativa de aplicaciones Flash sobre un navegador con tecnologías estándar frente al uso de plugins específicos. El formato SWF Ahora bien, ¿qué tiene dentro un fichero "Flash"?, y sobre todo, ¿cómo podemos sustituirlos utilizando tecnología estándar? Los ficheros que contienen las aplicaciones para Flash tienen extensión SWF. Y las especificaciones técnicas de este formato de archivos son definidas por Adobe que las hace pública con cada nueva versión. Muy a grandes rasgos, lo que contiene un fichero SWF es una cabecera y una serie de bloques denominados tags. Cada tag es de un tipo que determina la información que contiene y como debe interpretarse. Hay tags que contienen recursos, como imágenes, sonidos, músicas, vídeos, fuentes de texto, o cualquier tipo de información binaria arbitraria. Otros tags que contienen la definición y posicionamiento de determinados objetos, como botones, figuras, textos, o estilos creados con el IDE. Y otros últimos tags que contienen acciones para el control de la reproducción, como saltar a un determinado frame por ejemplo, e incluso código compilado en ActionScript. En consecuencia, simplificando muchísimo el asunto, el plugin de Adobe consta de al menos cuatro elementos principales: un parser de ficheros SWF, un motor de control y renderizado, una máquina virtual para ejecutar ActionScript, y un conjunto de librerías que proporcionan el runtime que utiliza el código ActionScript. Un Parser En líneas generales, la tarea de hacer un parser de ficheros SWF no es complicada, y lo digo por experiencia propia. No sólo porque exista un documento que describa en detalle el formato, sino porque hay bastante código fuente desarrollado que puede utilizarse como referencia. Empezando por las clases Java de SWFUtils liberadas como código abierto por parte de Adobe dentro del Flex SDK. Montar el proyecto y empezar a depurar la carga de un fichero SWF paso a paso es cuestión de minutos. Y de hecho, es bastante recomendable hacerlo, ya que los fuentes contienen información adicional que no se encuentra en la documentación oficial. Hacer un parser equivalente en JavaScript no es complicado, y vuelvo a hablar por experiencia propia. Aunque en este aspecto la referencia, e hilo conductor de este post, es el proyecto Gordon de Tobias Schneider, también de código abierto. Lo que hace Gordon es una petición del fichero SWF al servidor web a través de un objeto XMLHttpRequest y procesa el bloque de datos recibidos. Los ficheros SWF están comprimidos en formato zip, por lo que antes los descomprime, también desde JavaScript, mediante Los Recursos Embebidos Los formatos de imágenes soportados por Flash son JPEG, PNG y GIF. En HTML se pueden referenciar con la tradicional etiqueta <img>. Y con JavaScript se pueden leer e instanciar de forma dinámica utilizando el elemento canvas de HTML5 a través de su CanvasRenderingContext2D para acabar referenciándolas luego por su uri. var context = canvas.getContext("2d");Los sonidos se pueden reproducir en HTML5 mediante la etiqueta <audio>. Aunque otro cantar son los formatos soportados y los codecs disponibles. Flash soporta ADPCM, MP3, Nellymoser y Speex. En HTML5 se pueden embeber sonidos mediante una referencia en el HTML del cliente, e incluso reproducirlos usando streaming. <audio src="audio.spx" type="audio/ogg; codecs=speex">Lo que parece estar más limitado actualmente en JavaScript es la creación dinámica de sonidos, ya que hasta donde yo alcanzo, no existe un método estándar que permita instanciarlos en tiempo de ejecución con el objetivo de obtener una referencia que alimente al atributo "src" de la etiqueta. Aunque incluso para esto hay una solución: el uso de "data URI", tal y como se describe en el RFC2397. Es decir, serializando el stream de bytes del sonido en una cadena de texto en base 64. src="data:video/ogg,OggS%00%02%00%00 ..."Como nota al margen, comentar que Google utiliza esta técnica con las pequeñas imágenes que presenta a veces en la página de resultados de su famoso buscador. Flash soporta vídeo en formato Sorenson H.263. Y en HTML5 se pueden reproducir mediante la etiqueta <video>. Aunque la "guerra de los codecs de vídeo" ha dejado un poco coja la especificación del estándar, al no exigir ninguna implementación concreta. En cualquier caso, en HTML5 los ficheros de vídeo se referencian de igual forma que los ficheros de audio. <video src="videofile.ogg">Tanto los sonidos como los vídeos son tratados de una manera uniforme por HTML5, con funciones accesibles en JavaScript que permiten gestionarlos (play, pause, seek, volume, ...), y eventos relacionados con los cambios que se producen en los mismos (onplay, onseeked, onvolumechange, ...) var audio = new Audio("audio.ogg");Las fuentes de texto utilizadas dentro de un fichero SWF pueden tomarse del PC local donde se ejecute el reproductor, o pueden ir embebidas dentro del propio fichero SWF. Y aunque nada haga sospecharlo, aquí es donde el asunto se pone interesante. Hasta donde yo alcanzo, no se pueden instanciar fuentes de forma dinámica con HTML5. La definición de las fuentes dentro de los navegadores es cosa de CSS. Con la propiedad "font-family" se pueden tomar las fuentes del PC local, y con la regla "@font-face" de un servidor web. Y repito, esto no es parte de HTML5, sino de CSS3. @font-face {Y más interesante todavía. Si que se pueden generar fuentes de texto de forma dinámica con JavaScript, pero utilizando SVG, una tecnología totalmente distinta, mucho más antigua, y que tampoco es parte de HTML5. En los ficheros SWF se almacenan las secuencias de trazos que deben dibujarse para representar cada carácter de cada fuente de texto de forma individual. Y esas "secuencias", llamadas tradicionalmente "paths", juegan un papel importante dentro de todo este circo tecnológico. La definición de fuentes en SVG se realiza de una forma bastante natural a partir de ellos. SVG es una tecnología para gráficos vectoriales, y en este aspecto es mucho más similar a la idea original de Flash que el elemento canvas de HTML5. El resto de recursos que pueden encontrarse dentro de un fichero SWF son las propias películas en sí que se construyen con el IDE. La parte más importante de Flash. La definición de cada fotograma de cada película desglosada hasta el mínimo detalle. El conjunto de cada primitiva básica que se utiliza (línea, rectángulo, círculo, ...) junto con su estilo (color, trama, efecto, ...) y su matriz de transformación (traslación, rotación, escalado, ...). Un vector de objetos y comandos que nuevamente tiene un equivalente natural en los paths de SVG. En SVG se puede indicar una lista de comandos de forma abreviada asociada a un elemento: <path d="M 100 100 L 300 100 L 200 300 z" />El equivalente con HTML5 es ir llamando función a función: context.beginPath();El hecho de que el formato final en que se almacenan las películas de Flash se representen mejor en SVG que utilizando el elemento canvas de HTML5 es bastante significativo. Quizás deba ser este el camino a seguir. Y me refiero al hecho de que la solución puede pasar por usar una tecnología estándar específica para la representación de gráficos vectoriales en el navegador, no sólo el elemento canvas de HTML5 de propósito más general y posiblemente más orientado a la gestión de gráficos rasterizados. SVG además soporta animaciones, eventos, e incluso la ejecución de scripts. Todo lo demás El plugin de Flash contiene una interesante pieza de código llamada AVM2 (ActionScript Virtual Machine 2). La máquina virtual encargada de interpretar el código ActionScript 3 compilado que se encuentra dentro de los ficheros SWF. Adobe, en un movimiento bastante A Flash siempre le estará faltando la ejecución nativa de ActionScript 3 en los navegadores. Liberando su máquina virtual se facilitaba que los navegadores la incorporasen permitiendo dicha ejecución, pero eso es algo que de momento no ha ocurrido. Aunque no estoy yo muy convencido de que vayamos a tener JavaScript como lenguaje de referencia para siempre en los navegadores. La etiqueta <script> de HTML se creó para dar cabida a cualquier tipo de lenguaje, y ya va siendo que la utilicemos. ActionScript es bastante conocido por seguir el estándar ECMA-262, pero no tanto por implementar también el ECMA-357, lo que añade tipos, funciones y operadores para procesar XML de forma nativa dentro del propio lenguaje. Realizar un intérprete de ActionScript en JavaScript es factible, pero el rendimiento que se pueda obtener es discutible. Es algo sobre lo que no tengo una idea muy clara definida. Habrá que esperar a que algún proyecto con cierta solvencia lo implemente, posiblemente Gordon, o hacer algunas pruebas por mi cuenta y riesgo. Y por último, la pieza que completa el puzzle es el runtime de Flash. Quien esté dispuesto a implementar un visor de ficheros SWF ha de estar dispuesto a implementar todos los paquetes de clases de máximo nivel que ofrece el Flash Player API, o al menos los de uso más frecuente. Tarea que ya han acometido proyectos como Gnash, el reproductor GNU de ficheros SWF. Concluyendo Pero si el formato SWF no se adapta a nuestras necesidades, y resulta un tanto pesado su tratamiento para conseguir unas simples animaciones, ¿por qué no usamos otro? ¡Precisamente es lo que está haciendo Adobe! En el famoso vídeo, sobre el minuto 04:14, se puede ver que en realidad la animación hecha en Flash se exporta a un fichero en formato FXG. Un formato definido por la propia Adobe, y cuyas especificaciones técnicas son públicas. El formato FXG Cambiar el formato SWF a otro que tenga más cosas preprocesadas de cara a su tratamiento por parte de JavaScript puede ser la solución. Puede que no sea tan compacto como el original, pero con el previsible e inevitable aumento del ancho de banda disponible esto no debería importar. Un apunte final |