Strumenti Utente

Strumenti Sito


lpr-b:lpr-b-09:progetto4

Progetto 4

Questa pagina descrive il IV Progetto di LPR 2009/10.

Descrizione del problema

Si vuole realizzare un juke-box condiviso adatto al XXI secolo. Il sistema dovrà consentire a un insieme di utenti di condividere la propria musica; il sistema seleziona casualmente un brano da questo insieme, e provvede ad inviarlo in streaming a tutti gli utenti collegati. Una caratteristica fondamentale del juke-box condiviso è la sua natura sociale: è importante che tutti gli utenti ascoltino la stessa musica (ciascuno dal proprio computer, magari con una cuffia), in modo da incoraggiare i commenti e gli scambi di opinioni sulla musica che viene trasmessa, in presenza o tramite una chat (che non è parte del presente progetto).

Complessivamente, il sistema si comporta quindi come una web radio, limitata però a un piccolo gruppo di utenti, e il cui palinsenso è costruito in base ai contributi musicali degli utenti stessi.

Ciclo di funzionamento

Ogni utente del juke-bok è dotato di un programma, che normalmente funge da client, ma occasionalmente può fungere anche da server. Al programma vengono forniti, come parametri di riga di comando, l'indirizzo IP di un gruppo multicast, la porta da usare per il multicast e il path di una directory contenente file musicali; ogni gruppo multicast avrà un server e un numero qualunque di client. All'avvio, il programma invia sul gruppo una richiesta di join. Se non viene ricevuta risposta entro 10 secondi, il programma si auto-nomina server per il gruppo multicast, e prosegue normalmente (dovrà svolgere da ora in poi i compiti di client e server). Se invece riceve una accettazione da parte di un server, si predispone a fungere da client per quel server. Una volta stabilito il contatto con il server, il client invia la lista della musica che ha a disposizione, letta dalla directory indicata, attraverso una serie di messaggi disponibile. Dopo aver trasmesso la lista al server, il client si pone in attesa di ricevere dal server un comando play, che contiene l'indicazione di quale brano (fra quelli disponibili al client) eseguire. L'esecuzione consiste nell'invio dei dati audio sul gruppo multicast, per la riproduzione da parte di tutti i client (incluso quello che li sta inviando), come meglio specificato sotto.

Il ciclo di funzionamento del client è dunque duplice:

  1. individua il server
  2. invia la lista dei tuoi brani al server
  3. attendi comando play
  4. esegui lo streaming del brano
  5. goto 3

e

  1. attendi che inizi lo streaming di un brano
  2. riproducilo sul dispositivo audio
  3. goto 1

Il ciclo di funzionamento del server è anch'esso duplice:

  1. attendi richieste di join
  2. servile, ricevendo la lista dei brani
  3. goto 1

e

  1. scegli un brano a caso fra quelli disponibili
  2. invia il comando play al client relativo
  3. attendi che sia terminato lo streaming
  4. goto 1

Per semplicità, il progetto non richiede comportamenti specifici in caso di terminazione o blocco del server o di alcuni client, o di auto-proclamazione di più server dovuta a “race condition” (in pratica, non è richiesta l'implementazione di un vero protocollo distribuito di leader election); gli studenti sono comunque invitati a gestire in maniera ragionevole questi casi.

Documentazione del protocollo

Il juke-box distribuito deve essere implementato in modo che client e server diversi possano interoperare. Per questo motivo, tutte le comunicazioni devono rispettare scrupolosamente un protocollo ben definito.

Il sistema prevede tre flussi distinti di comunicazione:

  • fra client e server (via UDP in multicast) per la registrazione iniziale;
  • fra client e server (via TCP) per i dati di controllo;
  • fra client e client+server (via UDP in multicast) per l'audio in streaming.

Registrazione iniziale

La richiesta di join ha il seguente formato:

0 1 2 3
J O I N

ovvero, un pacchetto di 32 bit contenente i codici ASCII delle lettere che formano la parola “JOIN”. A questa richiesta il server, se presente, risponde con un messaggio di accettazione così composto:

0 1 2 3 4-7 8-9
H E L O indirizzo ip porta

con cui il server invita il nuovo client a contattarlo (via TCP) all'indirizzo IP e porta indicati per la prossima fase della conversazione.

Dati di controllo

L'invio dell'elenco dei brani disponibili di un cliente avviene tramite l'invio di 0 o più messaggi che elencano la musica disponibile. Ogni pacchetto ha il seguente formato:

0 1 2 3 4-5 6- n
M U S I id title \0

in cui l'id è un intero a 16 bit assegnato a discrezione del client (che però deve essere unico, per ogni brano, all'interno del client), mentre il titolo è una stringa ASCII di lunghezza qualunque, terminata dal codice NUL (ovvero, un byte di valore 0). Per semplicità, non sono previsti messaggi per rimuovere un brano una volta aggiunto.

Il server può richiedere al client di iniziare lo streaming di un brano con il comando play, che ha il seguente formato:

0 1 2 3 4-5
P L A Y id

in cui l'id è il valore a suo tempo comunicato dal client che identifica il brano da eseguire. In risposta a un comando play, il client inizia a inviare in streaming i dati audio come indicato sotto.

Audio in streaming

L'invio in streaming di un brano avviene in tre parti: dapprima si invia una intestazione per il file, quindi si inviano, in maniera cadenzata, i frame che compongono l'audio vero e proprio; per finire, si invia un indicatore di terminazione. Per quanto riguarda in particolare i frame contenenti i dati audio, è importante che questi siano inviati alla giusta frequenza, in base anche al formato audio impiegato, in modo da non sovraccaricare i buffer di ricezione degli altri clienti. Sia il client che invia l'audio, sia quelli che lo ricevono, devono lavorare in streaming: _non è accettabile_ trasferire l'intero file prima di riprodurlo. In particolare, il sistema deve essere in grado di gestire brani di lunghezza qualunque, e i client che ricevono l'audio non possono scrivere su disco (e ci sono ovvi limiti a quanti dati audio possono essere tenuti in memoria in attesa di essere riprodotti).

I pacchetti per l'invio dei dati audio sono come segue:

0 1 2 3 4-5 6- n
B E G I id title \0

All'inizio della trasmissione, il client originatore invia un pacchetto BEGI indicando un proprio id (preferibilmente, un numero casuale) che serve a identificare lo stream (e distinguere i dati di questo stream da quelli di altri eventuali stream che altri client potrebbero inviare, erronamente, in contemporanea sul gruppo multicast); viene anche inviato il titolo del brano, che gli altri client possono visualizzare all'utente.

0 1 2 3 4-5 6-7 8-(8+len)
D A T A id len dati

I pacchetti DATA contengono, oltre all'id dello stream, l'indicazione della lunghezza dei dati, seguita dai dati veri e propri.

0 1 2 3 4-5
E N D F id

Al termine della trasmissione dei dati audio, il client originatore invia un pacchetto ENDF, che indica il completamento dello stream.

Accesso all'audio

La classe principale per l'accesso all'audio in Java è la AudioSystem, che fornisce molti metodi statici. Altre classi utili sono la AudioInputStream (un InputStream specializzato per leggere dati audio), la AudioFormat (che rappresenta un formato audio), la SourceDataLine (che rappresenta una sorgente audio - ovvero, un canale di output dall'applicazione verso l'hardware audio). Allo studente si raccomanda di prendere familiarità con queste classi; qui di seguito è comunque riportato un esempio di utilizzo (che usa un File come sorgente dei dati audio, mentre il progetto dovrà ricevere i dati audio da rete).

// Crea:
// un AudioInputStream (in questo caso da un file, ma esiste una variante overloaded che legge i dati audio da un InputStream qualunque),
// un AudioFormat che rappresenta il formato dei dati audio dell'AudioInputStream
// una SourceDataLine che rappresenta un canale di output audio pronto a riprodurre dati nell'AudioFormat indicato
 
File f = new File("/usr/share/sounds/warning.wav");
AudioInputStream ais=AudioSystem.getAudioInputStream(f);
AudioFormat af=ais.getFormat();
SourceDataLine sdl=AudioSystem.getSourceDataLine(af);
 
// Inizializza un buffer di dimensione adeguata a contenere un secondo di audio
 
int bufsize=(int) (af.getSampleRate()*af.getFrameSize());
byte[] buffer = new byte[bufsize];
 
// Apre il canale audio e imposta il volume master al massimo
 
sdl.open(af);
if( sdl.isControlSupported( FloatControl.Type.MASTER_GAIN ) ) {
   FloatControl volume = (FloatControl) sdl.getControl( FloatControl.Type.MASTER_GAIN );
   volume.setValue( volume.getMaximum() );
}
 
// Avvia la riproduzione dell'audio
 
sdl.start();
 
// Legge, a blocchi, i dati dall'AudioInputStream e li scrive sul canale audio
 
int l=1;
while (l>=0) {
   l=ais.read(buffer);
   if (l>=0) sdl.write(buffer, 0,l);
}
 
// Aspetta che sia terminata la riproduzione dell'audio, quindi ferma tutto
 
sdl.drain();
sdl.stop();
sdl.close();

Il sottosistema audio di Java è estendibile tramite plug-in per supportare la riproduzione di formati qualunque, ma non sempre sono disponibili i plug-in giusti. Si raccomanda di utilizzare per le prove dei file in formato WAV (o altri formati basati su PCM come .au).

Requisiti generali e modalità di consegna

Ogni studente che vuole sostenere l'esame di LPR deve consegnare il progetto svolto al docente in forma elettronica. Il progetto deve essere sottomesso individualmente, ma può essere svolto da un gruppo di due studenti al massimo: in questo caso nel progetto va indicato esplicitamente il collega con cui si è collaborato. L'esame orale del corso, che comprende la discussione del progetto, sarà individuale: ciascuno studente è responsabile dell'intero progetto consegnato.

Per motivi organizzativi, gli studenti che intendono sottomettere il progetto sono invitati a comunicarlo al docente (acorradi [at] cli [dot] di [dot] unipi [dot] it).via email entro il 10 gennaio 2011: questa comunicazione non è obbligatoria ma molto gradita.

Per poter essere valutato, il codice del client sviluppato nel progetto deve essere in grado di interagire senza errori con altri client e server (che rispettino il protocollo indicato sopra).

Il codice del progetto deve essere ben commentato e deve essere accompagnato da una relazione di 3-5 pagine che descrive l'organizzazione del codice e da un manuale d'uso che descrive come lanciare il programma.

Codice, relazione e manuale d'uso devono essere inviati per posta elettronica al Prof. Corradini (acorradi [at] cli [dot] di [dot] unipi [dot] it) entro le ore 24 di Lunedì 31 gennaio 2011. Progetti inviati dopo tale scadenza non verranno ammessi, e lo studente dovrà mutuare il corso di LPR dal modulo di Laboratorio del corso di RCL, che verrà erogato nel secondo semestre dell'A.A. 2010/11.

Le modalità di valutazione dei progetti sottomessi e le istruzioni per gli orali verranno pubblicate nei primi giorni di febbraio 2010.

FAQ

  • [23 dicembre 2010] Su segnalazione di alcuni studenti, che ringraziamo, è stata aggiunta tra i parametri forniti al programma da linea di comando anche la porta multicast, che è necessaria.

[Q] Le connessioni TCP per i dati di controllo devono restare sempre aperte o devono essere chiuse e riaperte per inviare il comando PLAY?

[A] Il testo non specifica se la connessione deve essere unica, o riaperta di volta in volta per ogni PLAY. Considerando che il numero di clienti sul gruppo multicast sarà limitato, non appare critico “risparmiare” sul numero di connessioni; d'altra parte, il costo di stabilire e abbattere una connessione ad ogni comando non è giustificato da vantaggi in altre aree, quindi tenere la connessione aperta appare la soluzione migliore (idealmente, si potrebbe tentare di ri-aprirla nel caso la connessione venga interrotta in seguito ad errori).

[Q] Come fa il server a sapere quando il client ha finito di inviare la lista delle canzoni?

[A] Non esiste un messaggio di “termine lista”; il protocollo prevede infatti il caso in cui ulteriori canzoni possono essere aggiunte in un secondo momento. Questo potrebbe essere il caso, per esempio, di un client che usi i meccanismi di notifica del S.O. per accorgersi di quando un nuovo file è stato aggiunto alla directory indicata, e aggiornare così l'elenco mantenuto dal server. Tuttavia, il progetto non richiede di implementare tali meccanismi (così come non prevede un messaggio di rimozione dalla lista nel caso un file venga cancellato dalla directory mentre il juke-box è in esecuzione). Quindi, benché il nostro particolare client invii la lista solo all'inizio, il protocollo è progettato per essere usato anche da clienti più dinamici. Si noti che anche questa seconda caratteristica concorre a mantenere la connessione TCP sempre aperta, come già detto sopra.

[Q] Gli indirizzi internet e le word (usate per gli interi per lunghezze, id dei brani, etc.) vanno spedite in big endian o little endian?

[A] L'ordine da utilizzare è il big-endian, che è l'ordine naturale usato da Java e da praticamente tutti i protocolli standard di Internet (si veda http://en.wikipedia.org/wiki/Endianness#Endianness_in_networking).

lpr-b/lpr-b-09/progetto4.txt · Ultima modifica: 30/12/2010 alle 22:42 (12 anni fa) da Andrea Corradini