Java Midi a Framework

download Java Midi a Framework

of 94

Transcript of Java Midi a Framework

22Java Media Framework e Java Sound (no CD)Objetivos Entender para que serve o Java Media Framework (JMF). Entender para que serve a API Java Sound. Ser capaz de reproduzir mdia de udio e vdeo com JMF. Ser capaz de transmitir fluxos de mdia atravs de uma rede. Ser capaz de capturar, formatar e salvar mdia. Ser capaz de reproduzir sons com a API Java Sound. Ser capaz de reproduzir, gravar e sintetizar MIDI com a API Java Sound. A tev fornece a todos uma imagem, mas o rdio d luz a um milho de imagens em um milho de mentes. Peggy Noonan O barulho no prova nada. A galinha que bota um ovo grita como se tivesse botado um asteride. Mark Twain, Following the Equator A tela grande s faz o filme ficar duas vezes pior. Samuel Goldwyn A vida no uma srie de imagens que mudam medida que se repetem. Andy Warhol

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1113

Sumrio do captulo22.1 22.2 22.3 22.4 22.5 22.6 22.7 Introduo Reproduzindo mdia Formatando e salvando mdia capturada Streaming com RTP Java Sound Reproduzindo amostras de udio Musical Instrument Digital Interface (MIDI) 22.7.1 Reproduo de MIDI 22.7.2 Gravao de MIDI 22.7.3 Sntese de MIDI 22.7.4 A classe MidiDemo Recursos na Internet e na World Wide Web (Estudo de caso opcional) Pensando em objetos: animao e som na viso

22.8 22.9

Resumo Terminologia Exerccios de auto-reviso Respostas aos exerccios de auto-reviso Exerccios Seo especial: construindo seu prprio compilador

22.1 IntroduoEste captulo d continuidade s nossas discusses sobre multimdia do Captulo 18, apresentando algumas das APIs de Java para multimdia que permitem aos programadores melhorar os aplicativos com recursos de vdeo e udio. Em anos recentes, o segmento de multimdia digital do setor de computadores experimentou um crescimento enorme, como demonstra a enorme quantidade de contedo de multimdia disponvel na Internet. Os sites da Web foram transformados de pginas HTML baseadas em texto em experincias intensas em multimdia. Os avanos nas tecnologias de hardware e de software permitiram que os desenvolvedores integrassem multimdia aos aplicativos mais simples. Na faixa superior dos aplicativos de multimdia, o setor de vdeo games usou a programao com multimdia para tirar proveito das mais recentes tecnologias de hardware, como placas de vdeo 3D que criam experincias de realidade virtual para os usurios. Reconhecendo que os aplicativos Java deveriam suportar recursos de vdeo e udio digitais, a Sun Microsystems, a Intel e a Silicon Graphics trabalharam juntas para produzir uma API para multimdia que conhecida como a Java Multimedia Framework (JMF). A API JMF uma das vrias APIs para multimdia em Java. Usando a API JMF, os programadores podem criar aplicativos Java que reproduzem, editam e capturam muitos tipos populares de mdia. A primeira metade deste captulo discute a API JMF. A IBM e a Sun desenvolveram a mais recente especificao de JMF verso 2.0. A Sun oferece uma implementao de referncia JMF 2.1.1 da especificao de JMF que suporta tipos de arquivo de mdia como arquivos Microsoft Audio/Video Interleave (.avi), Macromedia Flash 2 movies (.swf), Future Splash (.spl), MPEG Layer 3 Audio (.mp3), Musical Instrument Digital Interface (MIDI; .mid), vdeos MPEG-1 (.mpeg, .mpg), QuickTime (.mov), Sun Audio (.au), udio Wave (.wav), AIFF (.aiff) e GSM (.gsm). Alm dos exemplos de clipes de mdia fornecidos com os exemplos deste captulo no CD, muitos sites oferecem um suprimento grande de clipes de udio e vdeo que podem ser baixados gratuitamente. Voc pode baixar clipes de mdia destes sites (e muitos outros na Internet) e us-los para testar os exemplos deste captulo. Apresentamos uma lista de sites aqui para que voc possa comear. O Free Audio Clips (www.freeaudioclips.com) um excelente site para diversos tipos de arquivos de udio. O site 13 Even (www.13-even.com.media.html) fornece clipes de udio e vdeo em muitos formatos para seu uso pessoal. Se voc estiver procurando arquivos de udio MIDI para uso na Seo 22.7, d uma olhada nos clipes MIDI gratuitos no endereo www.freestuffgalore.commidi.asp. O Funny Video Clips (www.video-clips.co.uk) oferece material de entretenimen-

1114

JAVA COMO PROGRAMAR

to. O site de downloads da Microsoft (msdn.microsoft.com/downloads) contm uma seo de multimdia que fornece clipes de udio e outros tipos de mdia. Atualmente, o JMF est disponvel como pacote de extenso separado do Java 2 Software Development Kit. O CD que acompanha este livro contm o JMF 2.1.1. A implementao mais recente de JMF pode ser baixada do site oficial de JMF:java.sun.com/products/java-media/jmf

O site da Web para o JMF fornece verses de JMF que tiram proveito das caractersticas de desempenho da plataforma na qual o JMF est sendo executado. Por exemplo, o Windows Performance Pack de JMF fornece amplo suporte a mdias e dispositivos para programas Java que esto executados em plataformas Microsoft Windows (Windows 95/98/NT 4.0/2000). O site oficial da Web para o JMF tambm fornece suporte, informaes e recursos atualizados continuamente para os programadores de JMF.Dica de portabilidade 22.1 Escrever programas com o Windows Performance Pack de JMF reduz a portabilidade daqueles programas para outros sistemas operacionais.

O restante deste captulo discute a API Java Sound e seus extensos recursos para processamento de som. Internamente, o JMF usa Java Sound para suas funes de udio. Nas Sees 22.5 a 22.7, demonstraremos a reproduo de amostras de udio e as funcionalidades para MIDI com o Java Sound, uma extenso-padro do Java 2 Software Development Kit.

22.2 Reproduzindo mdiaO JMF normalmente usado para reproduzir clipes de mdia em aplicativos Java. Muitos aplicativos, como os gerenciadores financeiros, as enciclopdias e os jogos, usam multimdia para ilustrar os recursos do aplicativo, apresentar contedo educacional e divertir os usurios. O JMF oferece diversos mecanismos para reproduzir mdia, e o mais simples deles atravs de objetos que implementam a interface Player. A interface Player (pacote javax.media) estende Controller, que um tratador para mdia suportada por JMF. As seguintes etapas so necessrias para reproduzir um clipe de mdia: 1. Especificar a fonte da mdia; 2. Criar um Player para a mdia; 3. Obter a mdia de sada e os controles de Player; 4. Exibir a mdia e os controles. A classe SimplePlayer (Fig. 22.1) um programa simples em Java que reproduz mdia que demonstra diversos recursos comuns de reprodutores de mdia populares. A demonstrao SimplePlayer pode reproduzir a maioria dos arquivos de mdia suportados por JMF, possivelmente com exceo das verses mais recentes dos formatos. Este aplicativo permite acessar arquivos no computador local que contm tipos de mdia suportados, clicando-se no boto Open File. Clicar no boto Open Location e especificar um URL de mdia permite acessar mdia de uma fonte de mdia, como um dispositivo de captura, um servidor da Web ou uma fonte de streaming. Um dispositivo de captura (discutido na Seo 22.3) l mdia a partir de dispositivos de udio e vdeo como microfones, reprodutores de CD e cmeras. O stream Real-Time Transport Protocol (RTP) um stream de bytes enviados atravs de uma rede a partir de um servidor de streaming. O aplicativo guarda no buffer e reproduz a mdia de streaming no computador cliente.1 2 3 4

// Fig. 22.1: SimplePlayer.java // Abre e reproduz um arquivo de mdia a partir de um // computador local, um URL pblico ou uma sesso RTP

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 1 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1115

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

// Pacotes do ncleo de Java import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; // Pacotes de extenso de Java import javax.swing.*; import javax.media.*; public class SimplePlayer extends JFrame { // reprodutor de mdia em Java private Player player; // componente para contedo visual private Component visualMedia; // componentes de controle para a mdia private Component mediaControl; // continer principal private Container container; // endereos do arquivo de mdia e da mdia private File mediaFile; private URL fileURL; // construtor para SimplePlayer public SimplePlayer() { super( "Simple Java Media Player" ); container = getContentPane(); // painel que contm botes JPanel buttonPanel = new JPanel(); container.add( buttonPanel, BorderLayout.NORTH ); // abrindo um arquivo a partir do boto de diretrio JButton openFile = new JButton( "Open File" ); buttonPanel.add( openFile ); // registra um ActionListener para eventos de openFile openFile.addActionListener( // classe interna annima para tratar eventos de openFile new ActionListener() { // abre e cria "player" para o arquivo public void actionPerformed( ActionEvent event ) { mediaFile = getFile(); if ( mediaFile != null ) { // obtm URL a partir do arquivo try { fileURL = mediaFile.toURL(); }

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 2 de 8).

111665 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

JAVA COMO PROGRAMAR

// caminho para o arquivo no pode ser encontrado catch ( MalformedURLException badURL ) { badURL.printStackTrace(); showErrorMessage( "Bad URL" ); } makePlayer( fileURL.toString() ); } } } // fim do mtodo actionPerformed

// fim do mtodo ActionListener

); // fim da chamada para o mtodo addActionListener // boto de abertura do URL JButton openURL = new JButton( "Open Locator" ); buttonPanel.add( openURL ); // registra um ActionListener para eventos de openURL openURL.addActionListener( // classe interna annima para tratar de eventos de openURL new ActionListener() { // abre e cria um "player" para o localizador de mdia public void actionPerformed( ActionEvent event ) { String addressName = getMediaLocation(); if ( addressName != null ) makePlayer( addressName ); } } // fim do mtodo ActionListener

); // fim da chamada para o mtodo addActionListener // liga a gerao leve nos players para permitir // melhor compatibilidade com componentes GUI de peso leve Manager.setHint( Manager.LIGHTWEIGHT_RENDERER, Boolean.TRUE ); } // fim do construtor SimplePlayer

// mtodo utilitrio para mensagens de erro "pop-up" public void showErrorMessage( String error ) { JOptionPane.showMessageDialog( this, error, "Error", JOptionPane.ERROR_MESSAGE ); } // obtm arquivo do computador public File getFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 3 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1117

125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

JFileChooser.FILES_ONLY ); int result = fileChooser.showOpenDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // obtm endereo da mdia digitado pelo usurio public String getMediaLocation() { String input = JOptionPane.showInputDialog( this, "Enter URL" ); // se o usurio pressionar OK sem digitar dados if ( input != null && input.length() == 0 ) return null; return input; } // cria player com o endereo da mdia public void makePlayer( String mediaLocation ) { // restaura o player e a janela se houver player anterior if ( player != null ) removePlayerComponents(); // endereo da origem da mdia MediaLocator mediaLocator = new MediaLocator( mediaLocation ); if ( mediaLocator == null ) { showErrorMessage( "Error opening file" ); return; } // cria um player a partir de MediaLocator try { player = Manager.createPlayer( mediaLocator ); // registra ControllerListener para tratar de eventos do Player player.addControllerListener( new PlayerEventHandler() ); // chama realize para permitir a gerao da mdia do player player.realize(); } // no existe nenhum player ou o formato no suportado catch ( NoPlayerException noPlayerException ) { noPlayerException.printStackTrace(); } // erro na leitura do arquivo catch ( IOException ioException ) { ioException.printStackTrace();

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 4 de 8).

1118185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243

JAVA COMO PROGRAMAR

} } // fim do mtodo makePlayer

// devolve o player para os recursos do sistema // e restaura a mdia e os controles public void removePlayerComponents() { // remove componente de vdeo anterior, se existe um if ( visualMedia != null ) container.remove( visualMedia ); // remove controle de mdia anterior, se existe um if ( mediaControl != null ) container.remove( mediaControl ); // faz parar o player e devolve os recursos alocados player.close(); } // obtm controles visuais para mdia e player public void getMediaComponents() { // obtm componente visual do player visualMedia = player.getVisualComponent(); // adiciona componente visual, se estiver presente if ( visualMedia != null ) container.add( visualMedia, BorderLayout.CENTER ); // obtm a GUI de controle do player mediaControl = player.getControlPanelComponent(); // adiciona componente de controles, se estiver presente if ( mediaControl != null ) container.add( mediaControl, BorderLayout.SOUTH ); } // fim do mtodo getMediaComponents

// tratador para os eventos ControllerEvents do player private class PlayerEventHandler extends ControllerAdapter { // carrega antecipadamente a mdia assim que o player realizado public void realizeComplete( RealizeCompleteEvent realizeDoneEvent ) { player.prefetch(); } // player pode comear a mostrar a mdia aps a carga antecipada public void prefetchComplete( PrefetchCompleteEvent prefetchDoneEvent ) { getMediaComponents(); // assegura um leiaute vlido para a frame validate(); // comea a reproduzir a mdia

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 5 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1119

244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

player.start(); } // fim do mtodo prefetchComplete

// se fim da mdia, restaura para o incio e pra de reproduzir public void endOfMedia( EndOfMediaEvent mediaEndEvent ) { player.setMediaTime( new Time( 0 ) ); player.stop(); } } // fim da classe interna PlayerEventHandler

// executa o aplicativo public static void main( String args[] ) { SimplePlayer testPlayer = new SimplePlayer(); testPlayer.setSize( 300, 300 ); testPlayer.setLocation( 300, 300 ); testPlayer.setDefaultCloseOperation( EXIT_ON_CLOSE ); testPlayer.setVisible( true ); } } // fim da classe SimplePlayer

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 6 de 8).

1120

JAVA COMO PROGRAMAR

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 7 de 8).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1121

Fig. 22.1

Reproduzindo mdia com a interface Player (parte 8 de 8).

O clipe de mdia precisa ser processado antes de ser reproduzido. Para processar um clipe de mdia, o programa precisa acessar uma fonte de mdia, criar um Controller para aquela fonte e enviar a mdia para a sada. Antes de enviar para a sada, os usurios podem fazer formatao opcional, como mudar um vdeo AVI para um vdeo QuickTime. Embora o JMF oculte do programador o processamento de mdia de baixo nvel (por exemplo, verificar se o arquivo compatvel), programadores e usurios podem configurar como um Player apresenta a mdia. A Seo 22.3 e a Seo 22.4 revelam que capturar e fazer streaming de mdia seguem as mesmas diretrizes. A Seo 22.8 lista diversos sites da Web que tm contedo de mdia suportado por JMF. A Fig. 22.1 apresenta alguns objetos fundamentais para reproduzir mdia. O pacote de extenso de JMF javax.media importado na linha 13 contm a interface Player e outras classes e interfaces necessrias para eventos. A linha 18 declara um objeto Player para reproduzir clipes de mdia. As linhas 30 a 31 declaram os endereos destes clipes como referncias File e URL. As linhas 21 e 24 declaram objetos Component para a exibio do vdeo e para manter os controles. O Component mediaControl permite reproduzir, fazer pausa e parar o clipe de mdia. O Component visualMedia exibe a parte de vdeo de um clipe de mdia (se o clipe de mdia for um vdeo). O JMF fornece geradores de vdeo peso-leve que so compatveis com os componentes peso-leve do Swing (ver Captulo 13). As linhas 107 e 108 no construtor de SimplePlayer especificam que o Player deve desenhar seus componentes GUI e sua parte de vdeo (se existir uma) usando geradores peso-leve, de modo que o reprodutor de mdia ter aparncia semelhante de outros componentes GUI com componentes Swing. Antes de reproduzir a mdia, SimplePlayer exibe uma GUI inicial que consiste em dois botes, Open File e Open Locator, que permitem especificar o endereo da mdia. Os tratadores de eventos para estes dois botes (linhas 52 a 78 e linhas 90 a 101) executam funes semelhantes. Cada boto pede aos usurios que informem um recurso de mdia, como um clipe de udio ou de vdeo, depois cria um Player para a mdia especificada. Quando

1122

JAVA COMO PROGRAMAR

o usuria clica em Open File, a linha 57 chama o mtodo getFile (linhas 120 a 134) para pedir aos usurios que selecionem um arquivo de mdia do computador local. A linha 63 chama o mtodo toURL de File para obter uma representao como URL do nome e endereo do arquivo selecionado. A linha 72 chama o mtodo makePlayer de SimplePlayer (linhas 150 a 187) para criar um Player para a mdia selecionada pelo usurio. Quando os usurios clicam em Open Locator, a linha 95 invoca o mtodo getMedialocation (linhas 137 a 147), pedindo aos usurios que digitem um string que especifica o endereo da mdia. A linha 98 chama o mtodo makePlayer de SimplePlayer para criar um Player para a mdia que est no endereo especificado. O mtodo makePlayer (linhas 150 a 187) faz os preparativos necessrios para criar um Player de clipes de mdia. O argumento String indica o endereo da mdia. As linhas 153 e 154 invocam o mtodo removePlayerComponents (linhas 191 a 203) para remover da frame o componente visual e os controles da GUI do Player anterior, antes de criar um novo Player. A linha 202 invoca o mtodo close de Player para parar toda a atividade do Player e liberar recursos do sistema mantidos pelo Player anterior. O mtodo makePlayer exige um ponteiro para a fonte da qual a mdia recuperada, o que feito instanciando-se um novo MediaLocator para o valor dado pelo argumento String (linhas 157 e 158). O MediaLocator especifica o endereo de uma fonte de mdia, de forma muito parecida como um URL geralmente especifica o endereo de uma pgina da Web. O MediaLocator pode acessar mdia a partir de dispositivos de captura e sesses RTP e a partir de endereos de arquivos. O construtor de MediaLocator exige o endereo da mdia como um String, de modo que todos os URLs devem ser convertidos para Strings como na linha 72. O mtodo makePlayer instancia um novo Player com uma chamada ao mtodo createPlayer de Manager. A classe Manager fornece mtodos static que permitem acessar a maioria dos recursos do JMF. O mtodo createPlayer abre a fonte de mdia especificada e determina o Player apropriado para a fonte de mdia. O mtodo createPlayer dispara uma NoPlayerException se no puder ser encontrado um Player apropriado para o clipe de mdia. Se ocorrerem problemas na conexo com a fonte de mdia, uma IOException disparada. Os ControllerListeners esperam os ControllerEvents que os Players geram, para monitorar o progresso de um Player no processo de tratamento da mdia. As linhas 170 e 171 registram uma instncia da classe interna PlayerEventHandler (linhas 225 a 255) para esperar certos eventos que player gera. A classe PlayerEventHandler estende a classe ControllerAdapter, que fornece implementaes vazias dos mtodos da interface ControllerListener. A classe ControllerAdapter facilita a implementao de ControllerListener para as classes que precisam tratar de apenas alguns tipos de ControllerEvent. Os Players confirmam seu progresso durante o processamento da mdia com base em suas transies de estado. A linha 174 invoca o mtodo realize de Player no estado Realizing para indicar que ele est se conectando sua fonte de mdia e interagindo com ela. Quando um Player completa a realizao, ele gera um RealizeCompleteEvent um tipo de ControllerEvent que ocorre quando o Player completa sua transio para o estado Realized. Este estado indica que o Player completou todos os preparativos necessrios para comear a processar a mdia. O programa invoca o mtodo realizeComplete (linhas 228 a 232) quando o Player gera um RealizeCompleteEvent. A maioria dos reprodutores de mdia tm um recurso de uso de buffer, que armazena localmente um pedao da mdia baixada, de modo que os usurios no precisem esperar que um clipe inteiro seja baixado antes de reproduzilo, j que a leitura de dados da mdia pode levar muito tempo. Invocando o mtodo prefetch de Player, a linha 231 faz a transio de player para o estado Prefetching. Quando o Player carrega antecipadamente um clipe de mdia, o Player obtm controle exclusivo sobre certos recursos do sistema necessrios para reproduzir o clipe. O Player tambm comea a colocar dados da mdia no buffer para reduzir o atraso antes da reproduo do clipe de mdia. Quando o Player completa a carga antecipada, ele passa para o estado Prefetched e est pronto para reproduzir a mdia. Durante esta transio, o Player gera um ControllerEvent do tipo PrefetchCompleteEvent, para indicar que est pronto para exibir a mdia. O Player invoca o mtodo prefetchComplete de PlayerEventHandler (linhas 235 a 246), que exibe a GUI do Player na frame. Aps obter os recursos de hardware, o programa pode obter os componentes de mdia que ele exige. A linha 238 invoca o mtodo getMediaComponents (linhas 206 a 222) para obter os controles da GUI e o componente visual da mdia (se a mdia for um clipe de vdeo) e os anexa ao painel de contedo da janela do aplicativo. O mtodo getVisualComponent de Player (linha 209) obtm o componente visual do clipe de vdeo. Similarmente, a linha 216 invoca o mtodo getControlPanelComponent para devolver os controles da GUI. A GUI (Fig. 22.1) normalmente fornece os seguintes controles:

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1123

1. Um controle deslizante de posicionamento, para pular para certos pontos no clipe de mdia; 2. Um boto de pausa; 3. Um boto de volume que fornece os controles de volume quando se clicar nele com o boto direito do mouse e uma funo de emudecer quando se clicar nele com o boto esquerdo; 4. Um boto de propriedades da mdia que fornece informaes detalhadas sobre a mdia quando se clicar nele com o boto direito do mouse e a taxa de exibio de quadros quando se clicar nele com o boto esquerdo.Observao de aparncia e comportamento 22.1 Invocar o mtodo getVisualComponent de Player devolve null para arquivos de udio, porque no h componente visual a exibir. Observao de aparncia e comportamento 22.2 Invocar o mtodo getControlPanelComponent de Player devolve conjuntos diferentes de controles de GUI, dependendo do tipo de mdia. Por exemplo, o contedo de mdia com stream diretamente de uma conferncia ao vivo no tem uma barra de andamento porque o comprimento da mdia no predeterminado.

Depois de validar o novo leiaute da frame (linha 241), a linha 244 invoca o mtodo start de Player (linha 239) para comear a reproduzir o clipe de mdia.Observao de engenharia de software 22.1 Se o Player no fez a carga antecipada nem realizou a mdia, invocar o mtodo start faz a carga antecipada e realiza a mdia. Dica de desempenho 22.1 Iniciar o Player consome menos tempo se o Player j tiver carregado a mdia antecipadamente antes de start ser chamado.

Quando o clipe de mdia terminar, o Player gera um ControllerEvent do tipo EndOfMediaEvent. A maioria dos reprodutores de mdia reenrolam o clipe de mdia depois de chegar ao fim, de modo que os usurios possam ver ou ouvir novamente a partir do incio. O mtodo endOfMedia (linhas 249 a 253) trata o EndOfMediaEvent e restaura o clipe de mdia para sua posio inicial invocando o mtodo setMediaTime de Player com um novo Time (pacote javax.media) de 0 (linha 251). O mtodo setMediaTime ajusta a posio da mdia para uma posio especfica de tempo e til para pular para uma parte diferente da mdia. A linha 252 invoca o mtodo stop de Player, que termina o processamento da mdia e coloca o Player no estado Stopped. Invocar o mtodo start para um Player Stopped que no tenha sido fechado retoma a reproduo da mdia. Freqentemente, desejvel configurar a mdia antes da apresentao. Na prxima seo, discutimos a interface Processor, que tem mais capacidades de configurao do que a interface Player. Processor permite que um programa formate a mdia e a salve em um arquivo.

22.3 Formatando e salvando mdia capturadaO Java Media Framework consegue reproduzir e salvar a mdia a partir de dispositivos de captura como microfones e cmeras de vdeo. Este tipo de mdia conhecido como mdia capturada. Os dispositivos de captura convertem mdia analgica em mdia digitalizada. Por exemplo, o programa que captura uma voz analgica a partir de um microfone ligado ao computador pode criar um arquivo digitalizado a partir daquela gravao. O JMF pode acessar dispositivos de captura de vdeo que usam drivers Video for Windows. Alm disso, o JMF suporta dispositivos de captura de udio que usam a Direct Sound Interface do Windows ou a Java Sound Interface. O driver Video for Windows fornece interfaces que permitem aos aplicativos Windows acessar e processar mdia a partir de dispositivos de vdeo. Similarmente, Direct Sound e Java Sound so interfaces que permitem acessar dispositivos de som como placas de som em hardware. O Solaris Performance Pack fornece suporte para dispositivos de captura Java Sound e Sun Video na plataforma Solaris. Para obter uma lista completa dos dispositivos suportados pelo JMF, visite o site oficial do JMF na Web.

1124

JAVA COMO PROGRAMAR

O aplicativo SimplePlayer apresentado na Fig. 22.1 permitiu aos usurios reproduzir mdia de um dispositivo de captura. Um string localizador especifica a localizao do dispositivo de captura que a demonstrao SimplePlayer acessa. Por exemplo, para testar as capacidades de captura de SimplePlayer, conecte um microfone ao plug de entrada de microfone de uma placa de som. Digitar o string localizador javasound:// no dilogo de entrada Open Location especifica que a entrada da mdia deve ser feita a partir do dispositivo de captura habilitado para Java Sound. O string localizador inicializa o MediaLocator de que o Player necessita para o material de udio capturado do microfone. Embora SimplePlayer fornea acesso a dispositivos de captura, ele no formata a mdia nem salva os dados capturados. A Fig. 22.2 apresenta um programa que executa estas duas novas tarefas. A classe CapturePlayer fornece mais controle sobre as propriedades da mdia atravs da classe DataSource (pacote javax.media.protocol). A classe DataSource fornece a conexo fonte de mdia, depois abstrai esta conexo para permitir que os usurios a manipulem. Este programa usa uma DataSource para formatar a mdia de entrada e de sada. A DataSource passa a mdia de sada formatada para um Controller, que ir format-la adicionalmente para que ela possa ser salva em um arquivo. O Controller que manipula a mdia um Processor, que estende a interface Player. Finalmente, o objeto que implementa a interface DataSink salva a mdia capturada e formatada. O objeto Processor trata do fluxo de dados da DataSource para o objeto DataSink.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

// Fig. 22.2: CapturePlayer.java // Apresenta e salva a mdia capturada. // Pacotes do ncleo de Java import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; // Pacotes de extenso de Java import javax.swing.*; import javax.swing.event.*; import javax.media.*; import javax.media.protocol.*; import javax.media.format.*; import javax.media.control.*; import javax.media.datasink.*; public class CapturePlayer extends JFrame { // botes de captura e salvamento private JButton captureButton; // componente GUI para salvar os dados capturados private Component saveProgress; // formatos da mdia do dispositivo, formato escolhido pelo usurio private Format formats[], selectedFormat; // controles dos formatos de mdia do dispositivo private FormatControl formatControls[]; // informaes de especificao do dispositivo private CaptureDeviceInfo deviceInfo; // Vector contendo todas as informaes do dispositivo private Vector deviceList;

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 1 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1125

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

// fontes de dados de entrada e sada private DataSource inSource, outSource; // gravador do arquivo para a mdia capturada private DataSink dataSink; // Processor para gerar e salvar a mdia capturada private Processor processor; // construtor para CapturePlayer public CapturePlayer() { super( "Capture Player" ); // painel contendo botes JPanel buttonPanel = new JPanel(); getContentPane().add( buttonPanel ); // boto para acessar e inicializar dispositivos de captura captureButton = new JButton( "Capture and Save File" ); buttonPanel.add( captureButton, BorderLayout.CENTER ); // registra um ActionListener para eventos do captureButton captureButton.addActionListener( new CaptureHandler() ); // ativa a gerao leve para tornar // compatvel com componentes GUI peso leve Manager.setHint( Manager.LIGHTWEIGHT_RENDERER, Boolean.TRUE ); // registra um WindowListener para eventos da frame addWindowListener( // classe interna annima para tratar WindowEvents new WindowAdapter() { // descarta o Processor public void windowClosing( WindowEvent windowEvent ) { if ( processor != null ) processor.close(); } } // fim de WindowAdapter

); // fim da chamada para o mtodo addWindowListener } // fim do construtor

// classe tratadora de aes para configurar dispositivo private class CaptureHandler implements ActionListener { // inicializa e configura dispositivo de captura public void actionPerformed( ActionEvent actionEvent ) { // coloca as informaes disponveis do dispositivo no Vector deviceList = CaptureDeviceManager.getDeviceList( null );

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 2 de 9).

112698 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155

JAVA COMO PROGRAMAR

// se nenhum dispositivo for encontrado, exibe mensagem de erro if ( ( deviceList == null ) | | ( deviceList.size() == 0 ) ) { showErrorMessage( "No capture devices found!" ); return; } // array de nomes de dispositivos String deviceNames[] = new String[ deviceList.size() ]; // armazena todos os nomes de dispositivos em um // array de strings, para fins de exibio for ( int i = 0; i < deviceList.size(); i++ ){ deviceInfo = ( CaptureDeviceInfo ) deviceList.elementAt( i ); deviceNames[ i ] = deviceInfo.getName(); } // obtm o ndice no Vector do dispositivo selecionado int selectDeviceIndex = getSelectedDeviceIndex( deviceNames ); if ( selectDeviceIndex == 1 ) return; // obtm as informaes de dispositivo do dispositivo selecionado deviceInfo = ( CaptureDeviceInfo ) deviceList.elementAt( selectDeviceIndex ); formats = deviceInfo.getFormats(); // se o dispositivo de captura anterior estiver aberto, desconecta-o if ( inSource != null ) inSource.disconnect(); // obtm dispositivo e configura seu formato try { // cria fonte de dados a partir do MediaLocator do dispositivo inSource = Manager.createDataSource( deviceInfo.getLocator() ); // obtm controles de configurao de formato para o dispositivo formatControls = ( ( CaptureDevice ) inSource ).getFormatControls();// obtm a configurao de formato do dispositivo desejada pelo usurio selectedFormat = getSelectedFormat( formats );

if ( selectedFormat == null ) return; setDeviceFormat( selectedFormat );

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 3 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1127

156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

captureSaveFile(); } // fim do try

// no consegue encontrar DataSource a partir do MediaLocator catch ( NoDataSourceException noDataException ) { noDataException.printStackTrace(); } // erro de conexo ao dispositivo catch ( IOException ioException ) { ioException.printStackTrace(); } } } // fim do mtodo actionPerformed

// fim da classe interna CaptureHandler

// configura o formato de sada da mdia capturada pelo dispositivo public void setDeviceFormat( Format currentFormat ) { // configura formato desejado em todos os controles de formato for ( int i = 0; i < formatControls.length; i++ ) { // assegura que o controle de formato pode ser configurado if ( formatControls[ i ].isEnabled() ) { formatControls[ i ].setFormat( currentFormat ); System.out.println ( "Presentation output format currently set as " + formatControls[ i ].getFormat() ); } } } // obtm o ndice no Vector do dispositivo selecionado public int getSelectedDeviceIndex( String[] names ) { // obtm o nome do dispositivo da caixa de dilogo de seleo de dispositivo String name = ( String ) JOptionPane.showInputDialog( this, "Select a device:", "Device Selection", JOptionPane.QUESTION_MESSAGE, null, names, names[ 0 ] ); // se o formato foi selecionado, obtm ndice do nome no array names if ( name != null ) return Arrays.binarySearch( names, name ); // seno, devolve valor indicando seleo invlida else return 1; } // devolve formato selecionado pelo usurio para o dispositivo public Format getSelectedFormat( Format[] showFormats ) { // fim do lao for

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 4 de 9).

1128215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274

JAVA COMO PROGRAMAR

return ( Format ) JOptionPane.showInputDialog( this, "Select a format: ", "Format Selection", JOptionPane.QUESTION_MESSAGE, null, showFormats, null ); } // exibe mensagens de erro public void showErrorMessage( String error ) { JOptionPane.showMessageDialog( this, error, "Error", JOptionPane.ERROR_MESSAGE ); } // obtm arquivo desejado para salvar a mdia capturada public File getSaveFile() { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_ONLY ); int result = fileChooser.showSaveDialog( this ); if ( result == JFileChooser.CANCEL_OPTION ) return null; else return fileChooser.getSelectedFile(); } // mostra o monitor de salvamento dos dados capturados public void showSaveMonitor() { // mostra o dilogo do monitor de salvamento int result = JOptionPane.showConfirmDialog( this, saveProgress, "Save capture in progress...", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE ); // termina salvamento se o usurio pressiona "OK" ou fecha dilogo if ( ( result == JOptionPane.OK_OPTION ) | | ( result == JOptionPane.CLOSED_OPTION ) ) { processor.stop(); processor.close(); System.out.println ( "Capture closed." ); } } // processa mdia capturada e salvada no arquivo public void captureSaveFile() { // array de formatos desejados de salvamento suportados pelas trilhas Format outFormats[] = new Format[ 1 ]; outFormats[ 0 ] = selectedFormat; // formato de sada no arquivo FileTypeDescriptor outFileType = new FileTypeDescriptor( FileTypeDescriptor.QUICKTIME );

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 5 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1129

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

// configura e inicia processador e monitora a captura try { // cria processador a partir do modelo de processador // da fonte de dados especfica, formatos de sada nas trilhas // e formato de sada no arquivo processor = Manager.createRealizedProcessor( new ProcessorModel( inSource, outFormats, outFileType ) ); // tenta criar um gravador de dados para a sada de mdia if ( !makeDataWriter() ) return;// chama start do processador para iniciar alimentao dos dados capturados processor.start();

// obtm controle de monitor para captura e codificao MonitorControl monitorControl = ( MonitorControl ) processor.getControl( "javax.media.control.MonitorControl" ); // obtm componente GUI do controle de monitorao saveProgress = monitorControl.getControlComponent(); showSaveMonitor(); } // fim do try

// nenhum processador pode ser encontrado // para uma fonte de dados especfica catch ( NoProcessorException processorException ) { processorException.printStackTrace(); } // incapaz de realizar atravs do // mtodo createRealizedProcessor catch ( CannotRealizeException realizeException ) { realizeException.printStackTrace(); } // erro de conexo ao dispositivo catch ( IOException ioException ) { ioException.printStackTrace(); } } // fim do mtodo captureSaveFile

// mtodo que inicializa o gravador do arquivo de mdia public boolean makeDataWriter() { File saveFile = getSaveFile(); if ( saveFile == null ) return false; // obtm fonte de dados de sada a partir do processador outSource = processor.getDataOutput();

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 6 de 9).

1130334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393

JAVA COMO PROGRAMAR

if ( outSource == null ) { showErrorMessage( "No output from processor!" ); return false; } // inicia o processo de gravao de dados try { // cria um novo MediaLocator a partir do URL saveFile MediaLocator saveLocator = new MediaLocator ( saveFile.toURL() ); // cria DataSink a partir da fonte de dados de sada e do // arquivo de destino de salvamento especificado pelo usurio dataSink = Manager.createDataSink( outSource, saveLocator ); // registra um DataSinkListener para DataSinkEvents dataSink.addDataSinkListener( // classe interna annima para tratar DataSinkEvents new DataSinkListener () { // se fim da mdia, fecha o gravador de dados public void dataSinkUpdate( DataSinkEvent dataEvent ) { // se a captura foi parada, fecha DataSink if ( dataEvent instanceof EndOfStreamEvent ) dataSink.close(); } } // fim de DataSinkListener

); // fim da chamada ao mtodo addDataSinkListener // comea a salvar dataSink.open(); dataSink.start(); } // fim do try

// DataSink no pode ser encontrada para o arquivo de // salvamento e a fonte de dados especficos catch ( NoDataSinkException noDataSinkException ) { noDataSinkException.printStackTrace(); return false; } // violao enquanto acessava // destino de MediaLocator catch ( SecurityException securityException ) { securityException.printStackTrace(); return false; } // problema na abertura e inicializao de DataSink catch ( IOException ioException ) { ioException.printStackTrace();

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 7 de 9).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1131

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

return false; } return true; } // fim do mtodo makeDataWriter

// mtodo main public static void main( String args[] ) { CapturePlayer testPlayer = new CapturePlayer(); testPlayer.setSize( 200, 70 ); testPlayer.setLocation( 300, 300 ); testPlayer.setDefaultCloseOperation( EXIT_ON_CLOSE ); testPlayer.setVisible( true ); } } // fim da classe CapturePlayer

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 8 de 9).

1132

JAVA COMO PROGRAMAR

Fig. 22.2

Formatando e salvando mdia a partir de dispositivos de captura (parte 9 de 9).

JMF e Java Sound usam fontes de mdia extensivamente, de modo que os programadores entender a disposio dos dados na mdia. O cabealho em uma fonte de dados especifica o formato da mdia e outras informaes essenciais necessrias para reproduzir a mdia. O contedo da mdia normalmente consiste em trilhas de dados, semelhantes s trilhas de msica em um CD. As fontes de mdia podem ter uma ou mais trilhas que contm diversos dados. Por exemplo, um clipe de cinema pode conter uma trilha para vdeo, uma trilha para udio e uma terceira trilha para closed captioning, para os deficientes auditivos. A classe CapturePlayer (Fig. 22.2) ilustra a captura, a configurao de formatos de mdia e o salvamento de mdia a partir de dispositivos de captura suportados pelo JMF. O teste mais simples do programa usa um microfone para entrada de udio. Inicialmente, a GUI tem apenas um boto, no qual o usurio clica para iniciar o processo de configurao. Depois, o usurio seleciona um dispositivo de captura em um dilogo de menu escamotevel. A caixa de dilogo seguinte tem opes para o formato do dispositivo de captura e da sada no arquivo. A terceira caixa de dilogo pede aos usurios para salvar a mdia em um arquivo especfico. A caixa de dilogo final oferece um controle de volume e a opo de monitorar os dados. A monitorao permite que os usurios escutem ou vejam a mdia medida que capturada e salvado, sem modific-la de nenhuma maneira. Muitas tecnologias de captura de mdia oferecem recursos de monitorao. Por exemplo, muitos gravadores de vdeo tm uma tela acoplada para deixar os usurios ver o que a cmera est capturando sem olhar atravs do visor. Em um estdio de gravaes, os produtores podem escutar de uma outra sala a msica ao vivo que est sendo gravada, atravs de fones de ouvido. Monitorar dados diferente de reproduzir dados e no provoca nenhuma alterao no formato da mdia ou afeta os dados que esto sendo enviados para o Processor. No programa CapturePlayer, as linhas 14 a 16 importam os pacotes de extenso de Java javax.media.protocol, javax.media.format e javax.media.control, que contm classes e interfaces para controle de mdia e formatao de dispositivos. A linha 17 importa o pacote de JMF javax.media.datasink, que contm classes para enviar mdia formatada para a sada. O programa usa as classes e interfaces fornecidas por estes pacotes para obter as informaes sobre o dispositivo de captura desejado, configurar o formato do dispositivo de captura, criar um Processor para tratar os dados de mdia capturados, criar um DataSink para gravar os dados de mdia em um arquivo e monitorar o processo de salvamento. CapturePlayer pode configurar o formato da mdia. O JMF fornece a classe Format para descrever os atributos de um formato de mdia, como a taxa de amostragem (que controla a qualidade do som) ou se a mdia deve ser em formato estreo ou mono. Cada formato de mdia codificado de maneira diferente e pode ser reproduzido somente com um tratador de mdia que suporte seu formato particular. A linha 28 declara um array com os Formats que o dispositivo de captura suporta e uma referncia Format para o formato selecionado pelo usurio (selectedFormat). Depois de obter os objetos Format, o programa precisa ter acesso aos controles de formatao do dispositivo de captura. A linha 31 declara um array para guardar os FormatControls que iro configurar o formato do dispositivo de captura. A classe CapturePlayer configura o Format desejado para a fonte de mdia atravs dos FormatControls do dispositivo (linha 31). A referncia CaptureDeviceInfo deviceInfo (linha 34) armazena as informaes do dispositivo de captura, que sero colocadas em um Vector que contm todas as informaes do dispositivo. A classe DataSource conecta os programas a fontes de dados de mdia, como dispositivos de captura. O SimplePlayer da Fig. 22.1 acessou um objeto DataSource invocando o mtodo createPlayer de Manager, passando-lhe um MediaLocator. Entretanto, a classe CapturePlayer acessa a DataSource diretamente. A linha 40 declara duas DataSources inSource conecta com a mdia do dispositivo de captura e outSource conecta fonte de dados de sada na qual a mdia capturada ser salva. O objeto que implementa a interface Processor fornece a funo bsica que controla e processa o fluxo de dados de mdia na classe CapturePlayer (linha 46). A classe tambm cria um objeto que implementa a interface DataSink para gravar os dados capturados em um arquivo (linha 43).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1133

Clicar no boto Capture and Save File configura o dispositivo de captura invocando o mtodo actionPerformed (linhas 93 a 171) na classe interna private CaptureHandler (linhas 90 a 173). Uma instncia da classe interna CaptureHandler registrada para esperar ActionEvents de captureButton (linha 62). O programa fornece aos usurios uma lista dos dispositivos de captura disponveis quando as linhas 96 e 97 invocam o mtodo static getDeviceList de CaptureDeviceManager. O mtodo getDeviceList obtm todos os dispositivos disponveis no computador que suportam o Format especificado. Especificar null como parmetro Format devolve uma lista completa de dispositivos disponveis. A classe CaptureDeviceManager permite que um programa acesse esta lista de dispositivos. As linhas 109 a 119 copiam os nomes de todos os dispositivos de captura para um array de Strings (deviceNames) para fins de exibio. As linhas 122 e 123 invocam o mtodo getSelectedDeviceIndex de CapturePlayer (linhas 195 a 210) para mostrar um dilogo de seleo com uma lista de todos os nomes de dispositivos armazenados no array deviceNames. A chamada para o mtodo showInputDialog (linhas 198 a 201) tem uma lista de parmetros diferente da de exemplos anteriores. Os quatro primeiros parmetros so o componente-pai do dilogo, a mensagem, o ttulo e o tipo de mensagem, como usado em captulos anteriores. Os ltimos trs, que so novos, especificam o cone (neste caso, null), a lista de valores apresentada para o usurio (deviceNames) e a seleo default (o primeiro elemento de deviceNames). Quando o usurio seleciona um dispositivo, o dilogo devolve o string, que usado para devolver o ndice em deviceNames do nome selecionado. Este elemento, que uma instncia de CaptureDeviceInfo, cria e configura um novo dispositivo a partir do qual a mdia desejada pode ser gravada. O objeto CaptureDeviceInfo encapsula as informaes de que o programa precisa para acessar e configurar um dispositivo de captura, como preferncias de localizao e formato. Chamar os mtodos getLocator (linha 143) e getFormats (linha 132) acessa estas pores de informaes. As linhas 129 e 130 acessam o novo CaptureDeviceInfo que o usurio especificou na deviceList. A seguir, as linhas 135 e 136 chamam o mtodo disconnect de inSource para desconectar quaisquer dispositivos de captura abertos anteriormente antes de conectar o novo dispositivo. As linhas 142 e 143 invocam o mtodo createDataSource de Manager para obter o objeto DataSource que se conecta fonte de mdia do dispositivo de captura, passando-lhe o objeto MediaLocator do dispositivo de captura como argumento. O mtodo getLocator de CaptureDeviceInfo devolve o MediaLocator do dispositivo de captura. O mtodo createDataSource, por sua vez, invoca o mtodo connect de DataSource, que estabelece uma conexo com o dispositivo de captura. O mtodo createDataSource dispara uma NoDataSourceException se no pode localizar uma DataSource para o dispositivo de captura. Se houver um erro durante a abertura do dispositivo, ocorre uma IOException. Antes de capturar a mdia, o programa precisa formatar a DataSource como especificado pelo usurio no dilogo de seleo de formato. As linhas 146 e 147 usam o mtodo getFormatControls de CaptureDevice para obter um array de FormatControls para a DataSource inSource. O objeto que implementa a interface FormatControl especifica o formato da DataSource. Os objetos DataSource podem representar outras fontes de mdia, que no dispositivos de captura, de modo que para este exemplo o operador de coero na linha 146 manipula o objeto inSource como um CaptureDevice e acessa mtodos de captura de dispositivo como getFormatControls. A linha 150 invoca o mtodo getSelectedFormat (linhas 213 a 219) para exibir um dilogo de entrada a partir do qual os usurios podem selecionar um dos formatos disponveis. As linhas 176 a 192 chamam o mtodo setDeviceFormat para configurar o formato da mdia de sada para o dispositivo de acordo com o Format selecionado pelo usurio. Cada dispositivo de captura pode ter diversos FormatControls, de modo que setDeviceFormat usa o mtodo setFormat de FormatControl para especificar o formato para cada objeto FormatControl. Formatar a DataSource completa a configurao do dispositivo de captura. O Processor (objeto inSource) converte os dados para o formato de arquivo no qual eles sero salvos. O Processor funciona como conector entre a outSource e o mtodo captureSaveFile, j que a DataSource inSource no reproduz nem salva a mdia: ela serve somente para configurar o dispositivo de captura. A linha 157 invoca o mtodo captureSaveFile (linhas 265 a 322) para executar as etapas necessrias para salvar a mdia capturada em um formato de arquivo reconhecvel. Para criar um Processor, este programa cria primeiro um ProcessorModel, um gabarito para o Processor. O ProcessorModel determina os atributos de um Processor atravs de um conjunto de informaes que inclui um DataSource ou MediaLocator, os formatos desejados para as trilhas de mdia que o Processor ir manipular e um ContentDescriptor que indica o tipo de contedo da sada. A linha 268 cria um novo array de Format (outFormats) que representa os formatos possveis para cada trilha na mdia. A linha 270 configura o formato default para o primeiro elemento do array. Para salvar a sada capturada em um arquivo, o Pro-

1134

JAVA COMO PROGRAMAR

cessor precisa primeiro converter os dados que ele recebe para um formato habilitado pelo arquivo. Um novo FileTypeDescriptor QuickTime (pacote javax.media.format) criado para armazenar uma descrio do tipo de contedo da sada do Processor e armazena em outFileType (linhas 273 e 274). As linhas 282 a 284 usam a DataSource inSource, o array outFormats e o tipo de arquivo outFileType para instanciar um novo ProcessorModel (linhas 283 e 284). Em geral, os Processors precisam ser configurados antes que eles possam processar mdia, mas o deste aplicativo no precisa, j que as linhas 282 a 284 invocam o mtodo createRealizedProcessor de Manager. Este mtodo cria um Processor realizado e configurado, com base no objeto ProcessorModel que lhe foi passado como argumento. O mtodo createRealizedProcessor dispara uma NoProcessorException se o programa no conseguir localizar um Processor para a mdia ou se o JMF no suportar o tipo de mdia. O mtodo dispara uma CannotRealizeException se o Processor no puder ser realizado. Isto pode ocorrer se um outro programa j estiver usando a mdia, bloqueando, portanto, a comunicao com a fonte de mdia.Erro comum de programao 22.1 Tenha cuidado ao especificar formatos de trilhas. Formatos incompatveis para tipos de arquivos de sada especficos impedem que o programa realize o Processor. Observao de engenharia de software 22.2 Lembre-se de que o Processor faz transies atravs de diversos estados antes de ser realizado. O mtodo createProcessor de Manager permite que um programa fornea configurao mais personalizada antes de um Processor ser realizado. Dica de desempenho 22.2 Quando o mtodo createRealizedProcessor configura o Processor, o mtodo bloqueia at que o Processor seja realizado. Isto pode impedir que outras partes do programa sejam executadas. Em alguns casos, usar um ControllerListener para atender aos ControllerEvents pode habilitar um programa a operar de maneira mais eficiente. Quando o Processor est realizado, o ouvinte notificado, de modo que o programa possa prosseguir com o processamento da mdia.

Tendo obtido os dados de mdia do Processor em um formato de arquivo, o programa pode criar um gravador de dados para gravar a sada da mdia em um arquivo. O objeto que implementa a interface DataSink habilita os dados de mdia a serem enviados para a sada em um endereo especfico na maior parte das vezes um arquivo. A linha 287 invoca o mtodo makeDataWriter (linhas 325 a 399) para criar um objeto DataSink que pode salvar o arquivo. O mtodo createDataSink de Manager exige como argumentos a DataSource do Processor e o MediaLocator para o novo arquivo. Dentro de makeDataWriter, as linhas 229 a 242 invocam o mtodo getSaveFile para pedir aos usurios que especifiquem o nome e o endereo do arquivo no qual a mdia deve ser salva. O objeto File saveFile armazena as informaes. O mtodo getDataOutput de Processor (linha 333) devolve a DataSource da qual ele recebeu a mdia. As linhas 344 e 345 criam um novo MediaLocator para saveFile. Usando este MediaLocator e a DataSource, as linhas 349 e 350 criam um objeto DataSink que grava a mdia de sada a partir da DataSource para o arquivo no qual os dados sero salvos, como especificado pelo MediaLocator. O mtodo createDataSink dispara uma NoDataSinkException se ele no puder criar um DataSink que possa ler os dados da DataSource e envi-los para a sada no endereo especificado pelo MediaLocator. Esta falha pode ocorrer como resultado de uma mdia invlida ou um MediaLocator invlido. O programa precisa saber quando parar de enviar dados para a sada, de modo que as linhas 333 a 369 registram um DataSinkListener para esperar por DataSinkEvents. O mtodo dataSinkUpdate de DataSinkListener (linhas 359 a 365) chamado quando ocorre DataSinkEvent. Se o DataSinkEvent um EndOfStreamEvent, indicando que o Processor foi fechado porque a conexo com o fluxo de captura fechou, a linha 364 fecha a DataSink. Chamar o mtodo close de DataSink faz parar a transferncia de dados. O DataSink fechado no pode ser usado novamente.Erro comum de programao 22.2 A sada no arquivo de mdia com um DataSink ficar corrompida se o DataSink no for fechado adequadamente.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD) Observao de engenharia de software 22.3

1135

A mdia capturada pode no gerar um EndOfMediaEvent se o ponto final da mdia no puder ser determinado.

Depois de configurar o DataSink e registrar o ouvinte, a linha 372 chama o mtodo open de DataSink para conectar o DataSink ao destino que o MediaLocator especifica. O mtodo open dispara uma SecurityException se o DataSink tentar gravar em um destino para o qual o programa no tem permisso de gravao, como um arquivo somente de leitura. A linha 373 chama o mtodo start de DataSink para iniciar a transferncia de dados. Neste ponto, o programa retorna do mtodo makeDataWriter de volta para o mtodo captureSaveFile (linhas 265 a 322). Embora o DataSink prepare a si mesmo para receber a transferncia e indique que est pronto chamando start, a transferncia no comea efetivamente enquanto o mtodo start do Processor no chamado. A invocao do mtodo start de Processor comea o fluxo de dados do dispositivo de captura, formata os dados e os transfere para o DataSink. O DataSink grava a mdia no arquivo, o que completa o processo executado pela classe CapturePlayer. Enquanto o Processor codifica os dados e o DataSink os salva em um arquivo, CapturePlayer monitora o processo. O monitoramento fornece um mtodo de supervisionar os dados enquanto o dispositivo de captura os coleta. As linhas 294 a 296 obtm um objeto que implementa a interface MonitorControl (pacote javax.media.control) a partir do Processor chamando o mtodo getControl. A linha 299 chama o mtodo getControlComponent de MonitorControl para obter o componente de GUI que exibe a mdia monitorada. Os MonitorControls normalmente tm uma caixa de marcao para habilitar ou desabilitar a exibio da mdia. Alm disso, os dispositivos de udio tm um controle de volume e os dispositivos de vdeo tm um controle para ajustar a taxa de exibio das frames. A linha 301 invoca o mtodo showSaveMonitor (linhas 245 a 261) para exibir a GUI de monitorao em uma caixa de dilogo. Para terminar a captura, os usurios podem pressionar o boto OK ou fechar a caixa de dilogo (linhas 254 a 261). Para alguns dispositivos de captura de vdeo, o Processor precisa estar em um estado Stopped para habilitar a monitorao do processo de salvamento e captura. At aqui, discutimos os recursos do JMF para acessar, apresentar e salvar contedo de mdia. Nosso exemplo final de JMF demonstra como enviar mdia entre computadores com as capacidades de streaming do JMF.

22.4 Streaming com RTPA mdia de streaming se refere mdia que transferida de um servidor para um cliente em um fluxo contnuo de bytes. O cliente pode comear a reproduzir a mdia enquanto ainda est baixando a mdia do servidor. Os arquivos de mdia de udio e vdeo freqentemente tm vrios megabytes de tamanho. Eventos ao vivo, como transmisses de concertos ou de jogos de futebol, podem ter tamanhos indeterminados. Os usurios podem esperar at que a gravao de um concerto ou jogo seja divulgada, depois baixar a gravao inteira. Entretanto, com as velocidades atuais de conexo da Internet, baixar uma transmisso destas poderia levar dias e normalmente os usurios preferem escutar transmisses ao vivo enquanto elas acontecem. A mdia de streaming permite que os aplicativos clientes reproduzam a mdia atravs da Internet ou atravs de uma rede, sem baixar o arquivo de mdia inteiro de uma vez s. Em um aplicativo com mdia de streaming, o cliente normalmente se conecta a um servidor que envia um fluxo de bytes contendo a mdia de volta para o cliente. O aplicativo cliente coloca em um buffer (i.e., armazena localmente) uma parte da mdia, a qual o cliente comea a reproduzir depois que uma certa quantidade tenha sido recebida. O cliente coloca continuamente mais mdia no buffer, fornecendo aos usurios um clipe ininterrupto, enquanto o trfego na rede no impedir o aplicativo cliente de colocar bytes adicionais no buffer. Com o uso de buffer, o usurio reproduz a mdia segundos depois que inicia o streaming, embora todo o material ainda no tenha sido recebido.Dica de desempenho 22.3 Fazer streaming de mdia para um cliente permite que o cliente reproduza a mdia mais rapidamente do que se o cliente esperar para baixar um arquivo de mdia inteiro.

A demanda por multimdia poderosa em tempo real est aumentando drasticamente, medida que aumenta a velocidade das conexes na Internet. O acesso Internet com banda larga, que fornece conexes de rede em alta velocidade com a Internet para tantos usurios, est se tornando mais popular, embora o nmero de usurios perma-

1136

JAVA COMO PROGRAMAR

nea relativamente pequeno em comparao ao nmero total de usurios da Internet. Com conexes mais rpidas, a mdia de streaming pode possibilitar uma experincia melhor com multimdia. Os usurios com conexes mais lentas ainda podem experimentar a multimdia, mas com qualidade inferior. A ampla variedade de aplicativos que usam mdia de streaming est crescendo. Os aplicativos que fazem streaming de clipes de vdeo para os clientes se expandiram para fornecer transmisses em tempo real. Milhares de estaes de rdio transmitem msica em stream continuamente atravs da Internet. Os aplicativos clientes como o RealPlayer se concentraram no contedo de mdia de streaming com transmisses de rdio ao vivo. Os aplicativos no se limitam ao streaming de udio e vdeo de servidor para cliente. Por exemplo, os aplicativos de teleconferncia e videoconferncia aumentam a eficincia nos negcios do dia-a-dia, reduzindo a necessidade de se viajar grandes distncias para se participar de reunies. O JMF fornece um pacote de mdia de streaming em alguns dos formatos discutidos antes neste captulo. Para obter uma lista completa de formatos, consulte o site oficial do JMF:java.sun.com/products/java-media/jmf/2.1.1/formats.html

O JMF usa o padro de mercado Real-Time Transport Protocol (RTP) para controlar a transmisso de mdia. O RTP foi projetado especificamente para transmitir dados de mdia em tempo real. Os dois mecanismos para fazer streaming de mdia suportada por RTP so pass-la atravs de um DataSink e coloc-la em um buffer. O mecanismo mais fcil de se usar um DataSink, que escreve o contedo de um stream em um host de destino (i.e., um computador cliente), atravs das mesmas tcnicas mostradas na Fig. 22.2 para salvar mdia capturada em um arquivo. Neste caso, entretanto, o URL do MediaLocator do destino seria especificado no seguinte formato:rtp://host:porta/tipoDeContedo

onde host o endereo IP ou nome de host do servidor, porta o nmero da porta na qual o servidor est fazendo streaming da mdia e tipoDeContedo audio ou video. Usar um DataSink como especificado permite que somente um stream seja enviado de cada vez. Para enviar vrios streams (por exemplo, como seria enviado um vdeo de karaok com trilhas separadas para vdeo e udio) para vrios hosts, um aplicativo servidor precisa usar gerenciadores de sesso de RTP. O RTPManager (pacote javax.media.rtp) permite maior controle sobre o processo de streaming, permitindo a especificao de tamanhos de buffers, verificao de erros e relatrios de streaming sobre o atraso de propagao (o tempo que leva para os dados chegarem a seu destino). O programa nas Figs. 22.3 e 22.4 demonstra o streaming com o gerenciador de sesso de RTP. Este exemplo suporta o envio de vrios streams em paralelo, de modo que clientes separados precisam ser abertos para cada stream. Este exemplo no mostra um cliente que pode receber o stream de RTP. O programa na Fig. 22.1 (SimplePlayer) pode testar o servidor RTP especificando um endereo de sesso de RTPrtp://127.0.0.1:4000/audio

como o endereo que o SimplePlayer deve abrir para comear a reproduzir udio. Para executar o servidor de mdia de streaming em um computador diferente, substitua 127.0.0.1 pelo endereo IP ou pelo nome de host do computador servidor. O objetivo da classe RTPServer fazer streaming de contedo de mdia. Como ocorre em exemplos anteriores, o RTPServer (Fig. 22.3) configura a mdia, processa-a e formata-a, e depois a envia para a sada. Os ControllerEvents e os diversos estados do processo de streaming conduzem este processo. O processamento da mdia tem trs partes distintas inicializao do Processor, configurao do Format e transmisso dos dados. O cdigo neste exemplo contm inmeras mensagens de confirmao exibidas no prompt de comando e uma nfase na verificao de erros. Um problema durante o streaming provavelmente terminar o processo inteiro e ser necessrio recomear.

1 2 3 4 5 6

// Fig. 22.3: RTPServer.java // Oferece recursos de configurao e transmisso // para arquivos de mdia suportados por RTP // Pacotes do ncleo de Java import java.io.*;

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 1 de 6).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1137

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

import java.net.*; // Pacotes de extenso de Java import javax.media.*; import javax.media.protocol.*; import javax.media.control.*; import javax.media.rtp.*; import javax.media.format.*; public class RTPServer { // endereo IP, arquivo ou nome do MediaLocator, nmero da porta private String ipAddress, fileName; private int port; // processador que controla o fluxo de dados private Processor processor; // dados de sada do processador a serem enviados private DataSource outSource; // controles configurveis das trilhas de mdia private TrackControl tracks[]; // gerenciador de sesso de RTP private RTPManager rtpManager[]; // construtor para RTPServer public RTPServer( String locator, String ip, int portNumber ) { fileName = locator; port = portNumber; ipAddress = ip; } // inicializa e configura o processador // devolve true se bem-sucedido, false caso contrrio public boolean beginSession() { // obtm MediaLocator de endereo especfico MediaLocator mediaLocator = new MediaLocator( fileName ); if ( mediaLocator == null ) { System.err.println( "No MediaLocator found for " + fileName ); return false; } // cria processador a partir de MediaLocator try { processor = Manager.createProcessor( mediaLocator ); // registra um ControllerListener para o processador // para esperar eventos de transio de estado processor.addControllerListener( new ProcessorEventHandler() ); System.out.println( "Processor configuring..." );

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 2 de 6).

113867 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

JAVA COMO PROGRAMAR

// configura o processador antes de ajust-lo processor.configure(); } // erro de conexo com a fonte catch ( IOException ioException ) { ioException.printStackTrace(); return false; } // exceo disparada quando nenhum processador // pode ser encontrado para fonte de dados especfica catch ( NoProcessorException noProcessorException ) { noProcessorException.printStackTrace(); return false; } return true; } // fim do mtodo beginSession

// tratador ControllerListener para o processador private class ProcessorEventHandler extends ControllerAdapter { // configura formato de sada e realiza // o processador configurado public void configureComplete( ConfigureCompleteEvent configureCompleteEvent ) { System.out.println( "\nProcessor configured." ); setOutputFormat(); System.out.println( "\nRealizing Processor...\n" ); processor.realize(); } // comea a enviar quando o processador est realizado public void realizeComplete( RealizeCompleteEvent realizeCompleteEvent ) { System.out.println( "\nInitialization successful for " + fileName ); if ( transmitMedia() == true ) System.out.println( "\nTransmission setup OK" ); else System.out.println( "\nTransmission failed." ); } // faz parar a sesso de RTP quando no h mdia a enviar public void endOfMedia( EndOfMediaEvent mediaEndEvent ) { stopTransmission(); System.out.println ( "Transmission completed." ); }

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 3 de 6).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1139

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

}

// fim da classe interna ProcessorEventHandler

// configura o formato de sada de todas as trilhas na mdia public void setOutputFormat() { // configura o tipo de contedo da sada para formato suportado por RTP processor.setContentDescriptor( new ContentDescriptor( ContentDescriptor.RAW_RTP ) ); // obtm todos os controles de trilha do processador tracks = processor.getTrackControls(); // formatos de uma trilha suportados por RTP Format rtpFormats[]; // configura cada trilha para o primeiro formato suportado // por RTP encontrado naquela trilha for ( int i = 0; i < tracks.length; i++ ) { System.out.println( "\nTrack #" + ( i + 1 ) + " supports " ); if ( tracks[ i ].isEnabled() ) { rtpFormats = tracks[ i ].getSupportedFormats(); // // // if se existirem formatos da trilha suportados, exibe todos os formatos suportados por RTP e configura o formato de trilha para o primeiro formato suportado ( rtpFormats.length > 0 ) { for ( int j = 0; j < rtpFormats.length; j++ ) System.out.println( rtpFormats[ j ] ); tracks[ i ].setFormat( rtpFormats[ 0 ] ); System.out.println ( "Track format set to " + tracks[ i ].getFormat() ); } else System.err.println ( "No supported RTP formats for track!" ); } } } // fim do if

// fim do lao for

// fim do mtodo setOutputFormat

// envia mdia com valor booleano indicando sucesso public boolean transmitMedia() { outSource = processor.getDataOutput(); if ( outSource == null ) { System.out.println ( "No data source from media!" ); return false;

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 4 de 6).

1140186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244

JAVA COMO PROGRAMAR

} // gerenciadores de stream de RTP para cada trilha rtpManager = new RTPManager[ tracks.length ]; // endereos de sesso de RTP de destino e local SessionAddress localAddress, remoteAddress; // stream RTP que est sendo enviado SendStream sendStream; // endereo IP InetAddress ip; // inicializa endereos de transmisso e envia a mdia para a sada try { // transmite todas as trilhas na mdia for ( int i = 0; i < tracks.length; i++ ) { // instancia um RTPManager rtpManager[ i ] = RTPManager.newInstance(); // adiciona 2 para especificar o nmero de porta do prximo controle; // (o RTP Session Manager usa 2 portas) port += ( 2 * i ); // obtm endereo IP do host a partir do string ipAddress ip = InetAddress.getByName( ipAddress ); // encapsula par de endereos IP para controle e // dados com duas portas dentro do endereo de sesso local localAddress = new SessionAddress( ip.getLocalHost(), port ); // obtm o endereo de sesso remoteAddress remoteAddress = new SessionAddress( ip, port ); // inicializa a sesso rtpManager[ i ].initialize( localAddress ); // abre a sesso de RTP para o destino rtpManager[ i ].addTarget( remoteAddress ); System.out.println( "\nStarted RTP session: " + ipAddress + " " + port); // cria stream de envio na sesso de RTP sendStream = rtpManager[ i ].createSendStream( outSource, i ); // comea a enviar o stream sendStream.start(); System.out.println( "Transmitting Track #" + ( i + 1 ) + " ... " ); } // fim do lao for

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 5 de 6).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1141

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

// comea carga da mdia processor.start(); } // fim de try

// endereo local desconhecido ou endereo remoto no pode ser resolvido catch ( InvalidSessionAddressException addressError ) { addressError.printStackTrace(); return false; } // erro de conexo com a DataSource catch ( IOException ioException ) { ioException.printStackTrace(); return false; }// formato no configurado ou formato invlido configurado na fonte de stream catch ( UnsupportedFormatException formatException ) { formatException.printStackTrace(); return false; }

// transmisso inicializada com sucesso return true; } // fim do mtodo transmitMedia

// faz parar a transmisso e fecha recursos public void stopTransmission() { if ( processor != null ) { // faz parar o processador processor.stop(); // descarta o processador processor.close(); if ( rtpManager != null ) // fecha alvos de destino // e descarta gerenciadores de RTP for ( int i = 0; i < rtpManager.length; i++ ) { // fecha streams para todos os destinos // com um motivo para terminar rtpManager[ i ].removeTargets( "Session stopped." ); // libera os recursos da sesso de RTP rtpManager[ i ].dispose(); } } // fim de if

System.out.println ( "Transmission stopped." ); } } // fim do mtodo stopTransmission

// fim da classe RTPServer

Fig. 22.3

Servindo mdia de streaming com gerenciadores de sesso RTP (parte 6 de 6).

1142

JAVA COMO PROGRAMAR

Para testar RTPServer, a classe RTPServerTest (Fig. 22.4) cria um novo objeto RTPServer e passa para o seu construtor (linhas 35 a 40) trs argumentos um String que representa o endereo da mdia, um String que representa o endereo IP do cliente e um nmero de porta para o contedo de streaming. Estes argumentos contm as informaes que a classe RTPServer precisa para obter a mdia e configurar o processo de streaming. Seguindo a abordagem genrica delineada em SimplePlayer (Fig. 22.1) e CapturePlayer (Fig. 22.2), a classe RTPServer obtm uma fonte de mdia, configura a fonte atravs de um tipo de Controller e envia os dados de sada para um destino especificado. RTPServerTest chama o mtodo beginSession de RTPServer (linhas 44 a 86) para configurar o Processor que controla o fluxo de dados. A linha 47 cria um MediaLocator e o inicializa com o endereo da mdia armazenado em fileName. A linha 58 cria um Processor para os dados especificados por aquele MediaLocator. Diferentemente do programa na Fig. 22.2, este Processor no configurado previamente por um Manager. At que a classe RTPServer configure e realize o Processor, a mdia no pode ser formatada. As linhas 62 e 63 registram um ProcessorEventHandler para reagir aos ControllerEvents de processor. Os mtodos da classe ProcessorEventHandler (linhas 89 a 127) controlam a configurao da mdia enquanto o Processor muda de estados. A linha 68 invoca o mtodo configure de Processor para colocar o Processor no estado Configuring. A configurao ocorre quando o Processor pede ao sistema e mdia as informaes necessrias para programar o Processor para executar a tarefa correta. Ocorre um ConfigureCompleteEvent quando o Processor completa a configurao. O mtodo configureComplete de ProcessEventHandler (linhas 94 a 104) responde a esta transio. O mtodo configureComplete chama o mtodo setOutputFormat (linhas 130 a 175) e depois realiza o Processor (linha 103). Quando a linha 99 invoca o mtodo setOutputFormat, ela configura cada trilha de mdia para um formato de mdia de streaming de RTP. As linhas 133 e 134 no mtodo setOutpuFormat especificam o tipo de contedo da sada chamando o mtodo setContentDescriptor de Processor. O mtodo recebe como argumento um ContentDescriptor inicializado com a constante ContentDescriptor.RAW_RTP. O tipo de contedo da sada de RTP restringe o Processor para suportar somente formatos de trilhas de mdia preparadas para RTP. O tipo de contedo da sada do Processor deve ser configurado antes que as trilhas da mdia sejam configuradas. Uma vez que o Processor esteja configurado, os formatos das trilhas de mdia devem ser ajustados. A linha 137 invoca o mtodo getTrackControls de Processor para obter um array que contm o objeto TrackControl correspondente (pacote javax.nedia.control) para cada trilha da mdia. Para cada TrackControl habilitado, as linhas 144 a 173 obtm um array de todos os Formats de mdia RTP suportados (linha 151) e depois configura o primeiro formato RTP suportado como o formato preferencial para streaming com RTP para aquela trilha (linha 161). Quando o mtodo setOutputFormat retorna, a linha 103 no mtodo configureComplete realiza o Processor. Como ocorre em qualquer realizao de controlador, o Processor pode enviar mdia para a sada assim que ele tenha terminado de realizar a si mesmo. Quando o Processor passa para o estado Realized, o ProcessorEventHandler invoca o mtodo realizeComplete (linhas 107 a 118). A linha 113 invoca o mtodo transmitMedia (linhas 178 a 271), que cria as estruturas necessrias para transmitir a mdia para o Processor. Este mtodo obtm a DataSource do Processor (linha 180), depois declara um array de RTPManagers que so capazes de iniciar e controlar uma sesso de RTP (linha 189). RTPManagers usam um par de objetos SessionAddress com endereos IP idnticos, mas nmeros de porta diferentes um para controle do stream e um para dados da mdia de streaming. O RTPManager recebe cada endereo IP e nmero de porta como um objeto SessionAddress. A linha 192 declara os objetos SessionAddress usados no processo de streaming. O objeto que implementa a interface SendStream (linha 195) faz o streaming com RTP.Observao de engenharia de software 22.4 Para os vdeos que tm mltiplas trilhas, cada SendStream deve ter seu prprio RTPManager gerenciando sua sesso. Cada Track tem seu prprio SendStream.

O bloco try (linhas 201 a 248) do mtodo transmitMedia envia para a sada cada trilha da mdia como um stream de RTP. Primeiro, devem ser criados gerenciadores para as sesses. A linha 207 invoca o mtodo newInstance de RTPManager para instanciar um RTPManager para cada stream de trilha. A linha 211 atribui ao nmero de porta um valor 2 a mais do que o nmero de porta anterior, porque cada trilha usa um nmero de porta para o controle do stream e um para realmente fazer streaming dos dados. As linhas 218 e 219 instanciam um novo

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1143

endereo local de sesso para onde o stream est localizado (i.e., o endereo de RTP que os clientes usam para obter o stream de mdia) com o endereo IP local e um nmero de porta como parmetros. A linha 219 invoca o mtodo getLocalHost de InetAddress para obter o endereo IP local. A linha 222 instancia o endereo de sesso do cliente, que o RTPManager usa como o alvo de destino do stream. Quando a linha 225 chama o mtodo initialize de RTPManager, o mtodo direciona a sesso de streaming local para usar o endereo de sesso local. Usando o objeto remoteAddress como parmetro, a linha 228 chama o mtodo addTarget de RTPManager para abrir a sesso de destino no endereo especificado. Para fazer stream de mdia para vrios clientes, chama o mtodo addTarget de RTPManager para cada endereo de destino. O mtodo addTarget deve ser chamado aps a sesso ser inicializada e antes que qualquer stream seja criado durante a sesso. Agora o programa pode criar os streams na sesso e comear a enviar dados. Os streams so criados na sesso de RTP atual com a DataSource outSource (obtida na linha 180) e o ndice de fonte de stream (i.e., ndice de trilha da mdia) nas linhas 234 e 235. Invocar o mtodo start sobre o SendStream (linha 238) e sobre o Processor (linha 246) inicia a transmisso dos streams de mdia, o que pode provocar excees. Ocorre uma InvalidSessionAddressException quando o endereo de sesso especificado invlido. Ocorre uma UnsupportedFormatException quando um formato de mdia no-suportado especificado ou se o Format da DataSource no foi configurado. Ocorre uma IOException se o aplicativo encontra problemas na rede. Durante o processo de streaming, RTPManagers podem ser usados com classes relacionadas dos pacotes javax.media.rtp e javax.media.rtp.event para controlar o processo de streaming e enviar relatrios para o aplicativo. O programa deve fechar as conexes e parar a transmisso em streaming quando ele atinge o fim da mdia de streaming ou quando o programa termina. Quando o Processor encontra o fim da mdia, ele gera um EndOfMediaEvent. Em resposta, o programa chama o mtodo endOfMedia (linhas 121 a 125). A linha 123 invoca o mtodo stopTransmission (linhas 274 a 303) para parar e fechar o Processor (linhas 279 a 282). Depois de chamar stopTransmission, no possvel retomar o streaming porque ele descarta o Processor e os recursos da sesso de RTP. As linhas 288 a 297 invocam o mtodo removeTargets de RTPManager (linhas 292 e 293) para fechar o streaming para todos os destinos. O mtodo dispose de RTPManager (linha 296) tambm invocado, liberando os recursos mantidos pelas sesses de RTP. A classe RTPServerTest (Fig. 22.4) invoca explicitamente o mtodo stopTransmission quando o usurio termina o aplicativo servidor (linha 40).1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // Fig. 22.4: RTPServerTest.java // Testa a classe para RTPServer // Pacotes do ncleo de Java import java.awt.event.*; import java.io.*; import java.net.*; // Pacotes de extenso de Java import javax.swing.*; public class RTPServerTest extends JFrame { // objeto que trata de streaming com RTP private RTPServer rtpServer; // fontes de mdia e endereos de destino private int port; private String ip, mediaLocation; private File mediaFile; // botes da GUI private JButton transmitFileButton, transmitUrlButton; // construtor para RTPServerTest public RTPServerTest() {

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 1 de 5).

114428 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

JAVA COMO PROGRAMAR

super( "RTP Server Test" ); // registra um WindowListener para eventos da frame addWindowListener( // classe interna annima para tratar WindowEvents new WindowAdapter() { public void windowClosing( WindowEvent windowEvent ) { if ( rtpServer != null ) rtpServer.stopTransmission(); } } // fim de WindowAdpater

); // fim da chamada para o mtodo addWindowListener // painel contendo GUI de boto JPanel buttonPanel = new JPanel(); getContentPane().add( buttonPanel ); // GUI de boto para transmitir arquivo transmitFileButton = new JButton( "Transmit File" ); buttonPanel.add( transmitFileButton ); // registra ActionListener para eventos de transmitFileButton transmitFileButton.addActionListener( new ButtonHandler() ); // GUI para boto de URL de transmisso transmitUrlButton = new JButton( "Transmit Media" ); buttonPanel.add( transmitUrlButton ); // registra ActionListener para eventos de transmitURLButton transmitUrlButton.addActionListener( new ButtonHandler() ); } // fim do construtor

// classe interna que trata eventos do boto de transmisso private class ButtonHandler implements ActionListener { // abre e tenta enviar arquivo para destino digitado pelo usurio public void actionPerformed( ActionEvent actionEvent ) { // se transmitFileButton foi invocado, obtm string de URL do arquivo if ( actionEvent.getSource() == transmitFileButton ) { mediaFile = getFile(); if ( mediaFile != null ) // obtm string de URL do arquivo try { mediaLocation = mediaFile.toURL().toString(); } // caminho para o arquivo no pode ser resolvido catch ( MalformedURLException badURL ) {

Fig. 22.4

Aplicativo para testar a classe RTPServer da Fig. 22.3 (parte 2 de 5).

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1145

89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

badURL.printStackTrace(); } else return; } // fim do if

// seno, transmitMediaButton foi invocado, obtm endereo else mediaLocation = getMediaLocation(); if ( mediaLocation == null ) return; // obtm endereo IP ip = getIP(); if ( ip == null ) return; // obtm nmero de porta port = getPort(); // verifica se o nmero de porta positivo e vlido if ( port track.size() ) track = tracks[ i ]; // configura evento MIDI corrente para primeiro evento na trilha currentEvent = track.get( eventIndex ); // obtm menssagem MIDI do evento message = currentEvent.getMessage(); // inicializao de trilha bem sucedida return true; } // fim do mtodo initializeTrack

// prossegue para prximo evento na trilha public void goNextEvent() { eventIndex++; currentEvent = track.get( eventIndex ); message = currentEvent.getMessage(); }

Fig. 22.7

MidiData carrega arquivos MIDI para reproduo (parte 3 de 4).

1158134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

JAVA COMO PROGRAMAR

// obtm intervalo de tempo entre eventos public int getEventDelay() { // o primeiro intervalo de tempo do evento a sua durao if ( eventIndex == 0 ) return ( int ) currentEvent.getTick(); // diferena de tempo entre evento corrente e o prximo return ( int ) ( track.get( eventIndex + 1 ).getTick() currentEvent.getTick() ); } // retorna se a trilha terminou public boolean isTrackEnd() { // se eventIndex menor do que o nmero de eventos da trilha if ( eventIndex + 1 < track.size() ) return false; return true; } // obtm comando ShortMessage corrente do evento public int getEventCommand() { if ( message instanceof ShortMessage ) { // obtm MidiMessage para fins de acesso noteMessage = ( ShortMessage ) message; return noteMessage.getCommand(); } return 1; } // obtm o nmero da nota do evento corrente public int getNote() { if ( noteMessage != null ) return noteMessage.getData1(); return 1; } // obtm o volume do evento corrente public int getVolume() { return noteMessage.getData2(); } } // fim da classe MidiData

Fig. 22.7

MidiData carrega arquivos MIDI para reproduo (parte 4 de 4).

Para reproduzir um arquivo MIDI com uma seqncia, o programa precisa obter a seqncia MIDI e verificar os aspectos de compatibilidade. O mtodo initialize de MidiData (linhas 73 a 94) obtm uma Sequence de dados MIDI de um arquivo com o mtodo getSequence de MidiSystem (linha 77). A Sequence contm trilhas MIDI, as quais, por sua vez, contm eventos MIDI. Cada evento encapsula uma mensagem MIDI de instrues para os dispositivos MIDI. As trilhas individuais de uma seqncia MIDI so anlogas s trilhas em um CD.

CAPTULO 22 JAVA MEDIA FRAMEWORK E JAVA SOUND (NO CD)

1159

Entretanto, enquanto as trilhas de CD so reproduzidas na ordem, as trilhas MIDI so reproduzidas em paralelo. A trilha MIDI uma seqncia de dados gravada. As MIDIs normalmente contm mltiplas trilhas. O mtodo getSequence tambm pode obter uma seqncia MIDI de um URL ou um InputStream. O mtodo getSequence dispara uma InvalidMidiDataException se o sistema MIDI detecta um arquivo MIDI incompatvel.Dica de portabilidade 22.2 Devido incompatibilidade entre os reconhecedores de arquivos em sistemas operacionais diferentes, os seqenciadores podem no conseguir reproduzir arquivos RMF.

Aps obter uma seqncia MIDI vlida, o programa precisa obter um seqenciador e carregar a seqncia no seqenciador. O mtodo play (linhas 35 a 64) na classe MidiData chama o mtodo getSequencer de MidiSystem (linha 41) para obter um Sequencer para reproduzir a Sequence. A interface Sequencer, que estende a interface MidiDevice (a super-interface para todos os dispositivos MIDI), fornece o dispositivo seqenciador padro para reproduzir os dados MIDI. Se um outro programa est usando o mesmo objeto Sequencer, o mtodo getSequencer dispara uma MidiUnavailableException. A linha 44 chama o mtodo open de Sequencer para se preparar para reproduzir uma Sequence. O mtodo setSequence de Sequencer (linha 47) carrega uma Sequence MIDI no Sequencer e dispara uma InvalidMididException se o Sequencer detectar uma seqncia MIDI irreconhecvel. A linha 50 comea a reproduzir a seqncia MIDI chamando o mtodo start do Sequencer. Alm dos mtodos de reproduo de MIDI, a classe MidiData tambm fornece mtodos que permitem a um programa acessar os eventos e as mensagens de uma seqncia MIDI. Como veremos, a classe MidiDemo (Fig. 22.10) usa a classe MidiData para acessar os dados em um arquivo MIDI para sincronizar o destaque das teclas do piano. Os eventos MIDI so armazenados nas trilhas de MIDI, que so instncias da classe Track (pacote javax.sound.midi). Os eventos MIDI em trilhas MIDI so representados pela classe MidiEvent (pacote javax.sound.midi). Cada evento MIDI contm uma instruo e o tempo em que ela deve ocorrer. Os eventos individuais em uma trilha contm mensagens do tipo MidiMessage que especificam as instrues MIDI para um MidiDevice. Existem trs tipos de mensagens MIDI ShortMessage, SysexMessage e MetaMessage. ShortMessages so instrues musicais explcitas, como as notas especficas a tocar e mudanas de freqncia. As outras duas mensagens, menos usadas, so as SysexMessages, mensagens de uso exclusivo do sistema para dispositivos MIDI, e MetaMessages, que podem indicar para um dispositivo MIDI que o MIDI atingiu o fim de uma trilha. Esta seo trata exclusivamente de ShortMessages que reproduzem notas especficas. A seguir, o programa precisa obter as trilhas e ler seus eventos. O mtodo initializeTrack de MidiData (linhas 97 a 125) invoca o mtodo getTracks da Sequence (linha 100) para obter todas as trilhas na seqncia MIDI. As linhas 108 a 114 determinam a trilha mais longa no MIDI e a configuram como aquela a ser reproduzida. A linha 11