lunedì 3 novembre 2008

Java Media Framework eqepa tutorial 1: creiamo un lettore multimediale.

Introduzione.
Lo scopo di questo tutorial è quello di aiutare a compiere i primi passi con le JMF, framework di sviluppo java per il multimedia. Iniziamo con i link indispensabili. Lo staff di eqepa consiglia vivamente netbeans come tool di sviluppo java per la facilità di utilizzo e la sua ide di sviluppo grafica. Indispensabili inoltre le JMF che consentono un utilizzo immediato di librerie per il multimedia, scaricatele e istallatele ovviamente prima di avviare il nostro progetto.
Il codice di questo esempio lo trovate invece qui.
Per utilizzare il codice da noi fatto scompattate l'archivio e aprite la cartella "lettore_eqepa" tramite il pulsante "open project".

Prima di partire con la guida premetto che è importante conoscere un minimo la struttura e la filosofia del framework che utilizzeremo; per quanto possibile spiegherò man mano il codice ma consiglio di leggere qualche introduzione sulle jmf. Potete trovare articoli utili qui (in inglese) e qui (in italiano).
Inoltre aggiungo che il risultato di questa guida è realizzabile anche tramite un unico file (ovvero un'unica classe) ma noi applichiamo per quanto possibile le "best practices" dettate dall'ingegneria del software, ovvero al posto di fare codice velocemente preferiamo fare codice lentamente ma di qualità.
La nostra sarà una panoramica dettagliata il più possibile sulla soluzione da noi data, non si tratta "della Soluzione" ma di "una soluzione". Spero che il metodo in cui presento il tutorial sia utile, nel caso contrario vi prego di farmelo sapere. Ricordate sempre che siamo agli inizi di questa nostra avventura di eqepa e siamo più che contenti se ci aiutate a migliorare.

Ok, iniziamo.
Creeremo un lettore multimediale capace di leggere MP3 e avi.

Aprite netbeans e caricate il progetto; possiamo vedere quindi che il nostro progetto si compone di cinque classi:


-Main.java: è la nostra "main class" ovvero la classe che verrà istanziata per prima.
-InterfacciaLettore.java: è il cuore dell'applicazione, contiene tutti i metodi e le componenti visuali necessari al programma.
-MyActionListener.java: implementa l'interfaccia ActionListener, necessaria per il funzionamento del bottone (vedremo in seguito).
-MyControllerListener.java: estende la classe ControllerAdapter, necessaria per il corretto import delle componenti visuali del player(vedremo in seguito).
-ContenitoreManiglie.java: questa è una classe statica che contiene le maniglie degli oggetti istanziati. E' necessaria per la comunicazione tra le varie classi.

Nel metodo main della classe Main viene immediatamente istanziato un oggetto di tipo InterfacciaLettore e vengono visualizzate le sue componenti grafiche grazie al metodo setVisible(); passiamo quindi all'analisi della classe InterfacciaLettore.
Quello che vogliamo è selezionare un file multimediale dal filesystem e aprirlo.
A questo scopo, dopo aver "registrato" se stesso nel ContenitoreManiglie e settato il metodo di chiusura dell'applicazione, l'oggetto di tipo InterfacciaLettore istanzia, setta e aggiunge al pannello un bottone. Come molti (anzi tutti) sapranno alla pressione di un bottone si genera un evento; se al bottone viene associato un ActionListener tramite il metodo addActionListener(), alla pressione viene chiamato il metodo ActionPerformed dell'ActionListener associato al bottone. In questo caso è del tutto inutile (perchè abbiamo un solo bottone) ma associamo al bottone istanziato un ActionCommand ovvero una stringa che contraddistingue il bottone. Quando l'utente preme quindi il bottone "search file" si genererà un evento e verrà chiamato il metodo ActionPerformed del MyActionListener. Quest'ultimo metodo controlla di quale bottone si tratti (con il test sull'ActionCommand) e così fa partire il metodo ScegliFile() di InterfacciaLettore.
Quest'ultimo metodo istanzia un nuovo oggetto di tipo FileDialog (una finestra di ricerca nel filesystem) e quindi setta l'attributo filename alla stringa corrispondente all'indirizzo del file da aprire. Il return finale serve per controllare che l'utente ha selezionato o meno un file da aprire (questo è uno dei pochi controlli che abbiamo inserito, il resto lo lasciamo a voi). Torniamo quindi all'ActionListener: se l'utente ha scelto un file la stringa filename sarà diversa da "file:///nullnull" e, in questo caso proviamo a creare un lettore adatto al file selezionato richiamando (sotto le clausole try-catch) il metodo setPlayer() di InterfacciaLettore.
La creazione del player è una delle cose più accattivanti (per la sua semplicità) delle JMF; basta richiamare il metodo statico createPlayer() della classe Manager ed assegnare la maniglia che questo metodo ritorna, al nostro player.
A questo punto potremo già richiamare il metodo start() del player e vedere, anzi ascoltare i primi risultati di questa nostra avventura nelle JMF. Perchè ho detto ascoltare? Perchè con l'invocazione del metodo start() il player comincia a leggere e elaborare il file scelto ma non vedremo alcuna controparte grafica di tale elaborazione.
Per ovviare a questo problema dobbiamo invocare i metodi getVisualComponent() e getControlPanelComponent(). Il primo ci ritorna un oggetto di tipo component contenente le informazioni grafiche relative al file aperto (naturalmente questo oggetto sarà null se il file è audio) mentre il secondo ci ritorna un Component contenente dei pulsanti base e una barra di navigazione del file. A questo punto un problema a cui tutti (a parte gli utenti del nostro blog e chi ha la pazienza di leggere TUTTA la documentazione prima di fare esperimenti) vanno subito a sbattere la testa è il seguente: appena richiamato il metodo start() del player tanto vale richiamare i metodi che ci ritornano le componenti visuali e inserirle nel frame. Questo comporta sempre un'eccezione che dice "impossibile prendere le componenti grafiche di un player non realizzato".
Tale eccezione parla da sola se avete letto le introduzioni che vi ho consigliato (in particolare quella di Mokabyte).
Quindi il tempo per passare dall'istruzione start() a getVisualComponent() oppure getControlPanelComponent() non è sufficiente per la realizzazione completa del player. In rete, in particolare sui forum, si trovano soluzioni a questo problema poco "eleganti" come quella di usare il metodo Thread.sleep(int m) tra un'istruzione e l'altra per bloccare il thread del frame per m millisecondi prima di andare avanti con il return delle componenti visuali. Naturalmente Sun ha pensato a questo e noi utilizzeremo la soluzione proposta.
Possiamo tornare al nostro codice.
Associamo al player un ControllerListener creato da noi: la tecnica è sempre la stessa, come per l'actionListener, creiamo una nostra classe MyControllerListener che estende ControllerAdapter, facciamo l'override del metodo realizeComplete che viene chiamato quando il player è finalmente realizzato ed il gioco è fatto.
Quest'ultimo metodo, per comodità, richiama il metodo setComponentiVisuali() di InterfacciaLettore che richiede le componenti grafiche del player e le associa agli attributi CompCentrale e CompInBasso (mi scuso per il nome di questo attributo ma proprio non mi veniva in mente un nome migliore).
Ecco il risultato:

Interfaccia iniziale:
Scelta del file:
Player MP3:


Player AVI:
Naturalmente non pretendete che il player vi faccia vedere film in codec particolari come divx o H.264. Comunque sia i prossimi tutorial andranno ancora a fondo nella programmazione con JMF e (sempre esami permettendo) approfondiremo questo argomento.

Ora il lavoro è solo vostro: commentate!

1 commenti:

Anonimo ha detto...

Il link per il documento con su scritto il codice apre un vecchio indirizzo di megaupload che purtroppo non è più in funzione... È comunque possibile ottenere il codice del lettore eqepa? Sarei molto interessato a realizzare un progetto simile per un esame imminente ;)

Posta un commento