Mostrando postagens com marcador flash. Mostrar todas as postagens
Mostrando postagens com marcador flash. Mostrar todas as postagens

sábado, 6 de junho de 2009

DynamicStream garantindo compatibilidade com flash 9

Ao implementar o DynamicStream em seu flash, teoricamente pararia de funcionar em clientes com a versão 9, pois algumas das classes e métodos necessários para seu uso só estão disponíveis apartir da versão 10.

Para evitar isso a Adobe disponibilizou um artigo mostrando como integrar o DynamicStream em um player antigo, para que só utilize o recurso novo quando o plugin do cliente estiver na versão 10, garantindo assim a compatibilidade. Este artigo possui até mesmo um exemplo, que embora funcione sem problemas em flash 9, quando integrado em outros projetos não funciona.

O problema é um bug na classe de referência que a Adobe oferece em diversos de seus artigos sobre essa funcionalidade. O erro fica evidente quando utilizamos um flash player debugger de versão 9 sobre o swf gerado, até mesmo os de exemplo da empresa.

verifyError: Error #1053: Illegal override of play2 in DynamicStream.

Esse erro ocorre porque a classe DynamicStream de referência sobrepõe o método play2 de NetStream apenas para anulá-lo. Com isso o plugin fica obrigado a verificar a existência do método play2 em NetStream e acaba dando o erro, pois tal método só está disponível apartir da versão 10.

Removendo esse método da classe DynamicStream de referência, tudo passa a funcionar, pois o acesso aos recursos existentes nas versões maiores ficam restritos ao conteudo dos métodos, que só serão avaliados em tempo de execução.

Não fosse o Bruno FMS me dar a dica do flash player debugger, perderia mais um bom tempo testando diversos artíficios para tentar fazer o código rodar em flash 9.

terça-feira, 2 de junho de 2009

Iptable para testar fallback para rtmpt no flash

Para testar se o fallback do seu flash player está funcionando corretamente, e acessando o flash media server pela porta 80 com rtmpt, basta configurar seu iptables para rejeitar ou deletar os pacotes da porta 1935, que é a padrão do FMS:

sudo iptables -A OUTPUT -p tcp --dport 1935 -j REJECT

ou

sudo iptables -A OUTPUT -p tcp --dport 1935 -j DROP

Sem esquecer de ao finalizar seus testes limpar o iptables para continuar seus testes:

sudo iptables -F

sexta-feira, 6 de junho de 2008

Como obter a URL do SWF

Essa aqui é básica, mas eu procurei bastante no Google e não encontrei. Acabei só achando no help do próprio flash. Caso você precise obter no seu aplicativo flash qual é o caminho completo do seu SWF, basta utilizar a propriedade loaderURL do objeto loaderInfo contido no seu MovieClip. Veja abaixo:

package {
public class Main extends MovieClip {
function Main() {
trace( "URL do SWF: " + this.loaderInfo.loaderURL );
}
}
}

quinta-feira, 15 de maio de 2008

FullScreen do flash não funciona em players abaixo de 9.0.28, e agora?

Desde a versão 9.0.28 do flash é possível definir seu swf para rodar em tela cheia de maneira bem simples. Também é possivel definir um listener para ser notificado quando o modo de exibição do flash é alterado de normal para fullscreen e vice-versa. Contudo, se você colocar essas funcionalidades, seu swf não rodará em versões anteriores a 9.028.

O jeito então é alterar seu ActionScript para só setar a o fullscreen quando a versão for compatível. Veja abaixo como isso pode ser feito:
import flash.display.StageDisplayState;

public class Main extends MovieClip {

//...

function suportaTelaCheia():Boolean {
//Verifica versao >= que 9.0.28
}
function onBotaoTelaCheiaClique(event:MouseEvent):void {
if( suportaTelaCheia() ) {
stage.displayState = StageDisplayState.FULL_SCREEN;
}
}
function onBotaSaiTelaCheiaClique(event:MouseEvent):void {
if( suportaTelaCheia() ) {
stage.displayState = StageDisplayState.NORMAL;
}
}

//...
}
Apesar da classe flash.display.StageDisplayState não existir no player com versão menor que 9.0.28, a importação dela não trará erro, pois o flash só executa a importação de uma classe quando faz uso dela. Portanto a seleção da versão feita com o método suportaTelaCheia impede seu uso e consequente importação.

O problema maior é quando precisamos definir um listener para sabermos quando o swf entrou em modo fullscreen e quando saiu. Normalmente isso é necessário quando precisamos redimensionar, ou adicionar elementos ao stage. No caso o código, sem o tratamento de versão, ou seja, o que daria erro nas versões de flash anteriores a 9.0.28 ficaria assim:
import flash.events.FullScreenEvent;

public class Main extends MovieClip {

//...

private function onStage(event:Event):void {
stage.addEventListener( FullScreenEvent.FULL_SCREEN, redrawFullScreen );
}

//...

function redrawFullScreen(event:FullScreenEvent):void {
if( event.fullScreen ) {
//Posicionar elementos para tela cheia
} else {
//Posicionar elementos para tamanho normal
}
}

//....

}
O grande problema disso é que o uso da classe FullScreenEvent está na definição do método redrawFullScreen e portanto não é possivel defini-lo somente quando for uma versão ou outra. O jeito então então é mudar a assinatura do método, para ao invés de receber um FullScreenEvent receber um Event. Com essa correção o código ficaria assim:
import flash.events.FullScreenEvent;

public class Main extends MovieClip {

//...

function suportaTelaCheia():Boolean {
//Verifica versao >= que 9.0.28
}

private function onStage(event:Event):void {
if( suportaTelaCheia() ) {
stage.addEventListener( FullScreenEvent.FULL_SCREEN, redrawFullScreen );
}
}

//...

function redrawFullScreen(event:Event):void {
if( event['fullScreen'] ) {
//Posicionar elementos para tela cheia
} else {
//Posicionar elementos para tamanho normal
}
}

//....

}
Repare que além de ter mudado a assinatura do método, mudei também o uso do objeto event. Isso foi feito porque a classe Event não possui o atributo fullScreen declarado, então utilizar event.fullScreen daria erro de compilação. Mas como sabemos que ali há este atributo, podemos utilizá-lo usando a notação event['fullScreen'].

quarta-feira, 14 de maio de 2008

Como instalar o flash 9.0.16 para testar seu swf

É muito importante que ao criar um swf complexo, como um player de vídeo por exemplo, você execute testes em todas as versões de flash player que você deseja que ele suporte. Para isso a Adobe oferece em seu site diversas versões antigas do flash player para download. Contudo, se você está no Windows XP e deseja intalar a versão 9.0.16, mas já possui uma versão mais nova instalada, é preciso seguir os seguintes passos.

Primeiro é preciso desinstalar a versão atual do flash player executando o programa uninstall_flash_player.exe encontrado na página How to uninstall the Adobe Flash Player plug-in and ActiveX control. Depois de executar isso é preciso acessar o regedit e remover a seguinte chave: HKEY_LOCAL_MACHINE\SOFTWARE\Macromedia\FlashPlayer.

Feito isso, reinicie o computador. Ok ok, paciência, estamos lidando com Windows portanto todo cuidado é pouco. Mãos a obra: Iniciar > Reiniciar (Heim?).

Após reiniciado basta instalar a versão 9.0.16 que pode ser baixada em Archived Flash Players available for testing purposes. Nesse link podem ser encontradas diversas outras versões do flash player e é bem possível que o escrito aqui sirva para instalar essas outras versões.

segunda-feira, 14 de janeiro de 2008

Repassando parâmetros rest para funções que esperam parâmetros rest

No ActionScript 3 existe um parâmetro especial, comumente chamando de parâmetro rest, que permite que uma função receba "infinitos" parâmetros. O Java, apartir da versão 5, também possui essa facilidade, que é chamada de varargs. Veja abaixo o exemplo de uma função e o logo abaixo o uso dela:
//Funcao que mostra todos os parametros
public function mostra(... numeros) {
if( numeros && numeros.length > 0 ) {
for each( var i in numeros ) {
trace(i);
}
}
}
//Uso da funcao:
mostra(1);
mostra(1,2,3);
mostra(1,2,3,5,8);
Apesar de ser um recurso muito bom, a forma como ela foi implementada no ActionScript nos traz um problema no momento em que precisamos repassar esses parâmetros para uma outra função que também receba parâmetros opcionais rest.

Isso acontece porque quando a primeira função é chamada, ela é informada com os parâmetros separados por vírgula, mas ela os recebe como um array. Então se você enviar este array para o segundo método, ele será considerado apenas um parâmetro. No Java há um tratamento mais esperto, de forma que quando enviado um array o compilador não permite que mais nenhum parâmetro seja colocado na chamada à função.

Para ilustrar melhor vamos pensar num cenário que mostrei no post passado sobre como testar funções que executem javascript. No caso era uma função proxy para isolarmos a execução de ExternalInterface.call:
public function executarJavaScript(funcao:String, ... parametros) {
try {
return ExternalInterface.call(funcao,parametros);
} catch(erro:Error) {
return null;
}
}
No caso, a função call de ExternalInterface também espera receber parâmetros opcionais, mas como estamos mandando um array, ele achará que estamos mandando apenas dois parâmetros, um com o nome da função javascript e outro com um array. No Google é fácil encontrar uma solução POG e muito feia para isso. Nada mais é que uma cadeia megazord de ifs:
public function executarJavaScript(funcao:String, ... p) {
try {
if( p.length == 0 ) return ExternalInterface.call(funcao,null);
else if( p.length == 1 ) return ExternalInterface.call(funcao,p[0]);
else if( p.length == 2 ) return ExternalInterface.call(funcao,p[0],p[1]);
else if( p.length == 3 ) return ExternalInterface.call(funcao,p[0],p[1],p[2]);
else if( p.length == 4 ) return ExternalInterface.call(funcao,p[0],p[1],p[2],p[3]);
} catch(erro:Error) {
return null;
}
}
Ô coisa feia!

A solução mais elegante para esse problema é utilizar os recurso de reflexão do ActionScript. Ou seja, vamos utilizar o método apply da classe Function, que aguarda os parâmetros como um array, exatamente como recebemos na função ao utilizar um parâmetro rest. Veja como ficaria:
public function executarJavaScript(funcao:String, ... parametros) {
try {
var f:Function = ExternalInterface.call;
var parametrosEnviar:Array = new Array(funcao);
parametrosEnviar = parametrosEnviar.concat(parametros);
return f.apply(null,parametrosEnviar);
} catch(erro:Error) {
return null;
}
}
No caso, concatenamos num array de parâmetros a serem enviados a função e os parâmetros rest. Ao enviá-los pela função apply eles são enviados como se fossem diversos parâmetros separados por vírgula e o método call os recebe corretamente como parâmetros rest.

domingo, 13 de janeiro de 2008

Como testar funções que fazem chamadas javascript no actionscript?

No ActionScript é possível chamar funções javascript utilizando a função ExternalInterface.call. O problema é que essa função é estática, dificultando muito a criação de testes unitários para as funções que necessitam acessar esses recursos externos.

Para fazer isso é preciso isolar a chamada ao javascript em uma função, e no teste unitário substituir essa função por uma outra que apenas a substitua por uma comportamento esperado de acordo com o teste. Vamos ver como fazer isso:

Primeiro isolamos a chamada a ExternalInterface.call em um função que serviria apenas como proxy. Em linguagens menos dinâmicas precisaríamos isolar essa função em uma outra classe pois não seria possível substitui-la posteriormente:
public function executarJavaScript(funcao:String,parametros:Object = null) {
try {
return ExternalInterface.call(funcao,parametros);
} catch(erro:Error) {
return null;
}
}
A classe então que possui essa função precisa ser dinâmica, ou seja deve ter um modificador dynamic, e possuir um atributo da classe Function que recebe no construtor essa função que foi criada:
dynamic public class MinhaClasse {
public var js:Function;
function MinhaClasse() {
js = executarJavaScript;
}
public function executarJavaScript(funcao:String,parametros:Object = null) {
//Conteudo da função já exibido acima :P
}
//Outras funções
}
As demais funções da classe deverão usar então o atributo js como uma função para executar o recurso externo desejado. Veja um exemplo que recupera do javascript a URL onde o swf está sendo exibido:
public function obterUrlAtual():String {
try {
var objJs = js("document.location.href.toString");
if( !objJs ) objJs = js("document.location.toString");
if( !objJs ) return null;
return new String(objJs);
} catch(e:Error) {
return null;
}
}
Para criar um teste unitário com o ASUnit para esta função de exemplo basta configurar de diversas formas o atributo js da classe MinhaClasse de acordo com o comportamento que você deseja testar. Um exemplo é exibido abaixo:
public function testSegundFuncaoJavaScriptEhChamada():void {
var m:MinhaClasse = new MinhaClasse();
m.js = function(funcao:String,parametros:Object = null) {
if(funcao=="document.location.toString") {
return "http://programandosemcafeina.blogspot.com";
} else {
return null;
}
}
var s:String = m.obterUrlAtual();
assertEquals("Quando a primeira função retorna null a segunda função deve ser chamada","http://programandosemcafeina.blogspot.com",s);
}

segunda-feira, 7 de janeiro de 2008

Checar versão do flash de dentro do SWF

Descobrir a versão do flash por javascript é moleza. Uma rápida busca no google lhe traz dezenas de script prontos pra usar. Mas verificar a versão do flash de dentro do SWF foi díficil de encontrar. Até é bem simples. Basta usar o atributo estático version da classe flash.system.Capabilities. Assim:
trace("versao: " + Capabilities.version);
Simples, porém foi díficil encontrar. Como esse código só funciona no Action Script 3, fica parecendo que ele não tem muita utilidade. Mas acontece que existem pequenas diferenças entre as chamadas minor revisions que precisam ser verificadas no projeto em que estou trabalhando.