[Java] Desktop en Audio streamen

Pagina: 1
Acties:
  • 109 views sinds 30-01-2008
  • Reageer

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
We gaan met onze projectgroep een digitale leeromgeving maken. Dit betekent dat er een server is waar verschillende studenten en een docent op kunnen inloggen. Docenten kunnen dan hun beeldscherminhoud en microfoongeluid overzenden naar de studenten.

Mijn vraag is, hoe kunnen we dit het beste aanpakken?
Voor het beeldscherm zat ik te denken aan
- 3x per seconde een screenshot maken
- dit steeds vergelijken met de vorige en gelijke pixels in de nieuwe transparant maken
- het nieuwe plaatje croppen (dus de kleinste rechthoek pakken zonder dat er niet-transparante pixels kwijtraken)
- het nieuwe plaatje met JPEG comprimeren en naar een socket schrijven

Het probleem daarbij is alleen dat de te verzenden data bij grove veranderingen erg groot kan worden (zeg, 30 kB, dus 90 kB p/s). Daarom kunnen we misschien beter een echte videocodec als MPEG of de open-source Theora gebruiken, maar hoe doen we dit in Java?

Ik heb natuurlijk wat uitgezocht, maar het officiele JMF (java media foundation/framework?) is al best oud en ondersteunt niet veel. Daarnaast is het wenselijk dat de gebruiker niks anders dan Java hoeft te installeren (dus de functionaliteit moet ofwel bij de java runtime zitten, of in de programmamap aanwezig kunnen zijn)

Wie kan ons in de goede richting wijzen? Pakken we het misschien helemaal verkeerd aan?

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
ik heb inmiddels het maken van een screencapture uitgezocht

Java:
1
2
3
4
5
6
                    // get current display contents
                    final GC gc = new GC(display);
                    System.out.println("Make image");
                    final Image cap = new Image(display, display.getBounds());
                    System.out.println("Copy data");
                    gc.copyArea(cap, 0, 0);

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 08:22
Als je het niet perse helemaal van scratch hoeft te ontwerpen en implementeren voor jouw opleiding is VNCj wellicht een optie.

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
het hoeft niet per se van scratch, maar met VNCj kun je alleen een awt/swing interface doorsturen. wat wij willen overzenden is de beeldscherminhoud, ongeacht waar deze vandaan komt

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 08:22
Ah, ok. Volledige remote control dus, niet alleen over een java appliatie.
Volgens mij zit je dan best aardig in juistede richting te denken.

Je zou kunnen kijken naar het RFB protocol.
In de meest simpele vorm wordt daarbij de schermbuffer van de server gefragmenteerd (opgedeeld in kleinere rechthoeken) en die fragmenten worden naar de remote kant gestuurd. Het scherm wordt daar dan van linksboven naar rechtsonder rij voor rij opgebouwd en na de initiële opbouw worden alleen wijzigingen doorgestuurd. Je kan dan inderdaad JPEG compressie toepassen om de benodigde bandbreedte wat in te perken.

Vanaf de client stuur je alleen keyboard en mouse events, die je aan de server kant met de standaard Robot klasse kan vertalen naar lokale handelingen.

  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 30-11 11:20

voodooless

Sound is no voodoo!

Met bovenstaande code kun je ook geen screenshot maken van je hele scherm. Dat kan wel hiermee: http://schmidt.devlib.org/java/save-screenshot.html#source

Daarnaast: VNCj is open source, niets houdt je tegen om het uit te breiden om gebruik te maken van het volledige scherm natuurlijk :)

En doe je dat niet dan loont nog altijd een kijkje naar het VNC protocol. Je krijgt dan al veel kado, zoals een viewer bijvoorbeeld :)

[ Voor 19% gewijzigd door voodooless op 01-05-2007 13:40 ]

Do diamonds shine on the dark side of the moon :?


  • MisterData
  • Registratie: September 2001
  • Laatst online: 27-11 20:42
Ik zou op de server de gewone VNC server draaien, eventueel met een Java-toevoeging die je zelf schrijft voor audio-streaming. Op de client draai je dan de Java VNC-client (zit standaard bij VNC geloof ik!) met daarbij de client die je zelf schrijft voor de audio. Ik zou het wiel niet opnieuw gaan uitvinden :-) De VNC-server doet precies wat je wil (inclusief compressie) voor het beeld en is snel genoeg...

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 08:22
voodooless schreef op dinsdag 01 mei 2007 @ 13:37:
Met bovenstaande code kun je ook geen screenshot maken van je hele scherm. Dat kan wel hiermee: http://schmidt.devlib.org/java/save-screenshot.html#source
Eenvoudiger kan het nog met de Robot klasse. Die heeft een methode om een volledige screendump te maken rechtstreeks naar een BufferedImage :)
Ben je echt in een paar regels klaar.

Edit: whoops, die gebruiken ze hier ook, de hele lap faciliterende code er omheen bracht mij even van de wijs :)

[ Voor 12% gewijzigd door Kwistnix op 01-05-2007 15:08 ]


  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Ah, ok. Volledige remote control dus, niet alleen over een java appliatie.
Volgens mij zit je dan best aardig in juistede richting te denken.
laat het duidelijk zijn dat alleen de beelscherminhoud gezien hoeft te worden. Niemand hoeft van afstand de pc van een ander te besturen
Met bovenstaande code kun je ook geen screenshot maken van je hele scherm. Dat kan wel hiermee:
en toch heb ik hem getest en doet hij dat wel degelijk ;)
het is ook SWT, geen AWT. misschien dat je daarom verward bent
Ik zou op de server de gewone VNC server draaien
twee problemen:
- alles moet in 1 geintegreerde omgeving
- het versturen moet vanaf een client. onze server werkt min of meer als aanmeldserver en doorstuurhub, maar doet verder niet veel. daarbij moeten twee docenten afwisselend kunnen uitzenden
Eenvoudiger kan het nog met de Robot klasse. Die heeft een methode om een volledige screendump te maken rechtstreeks naar een BufferedImage
die had ik ook al gevonden, maar omdat we SWT gebruiken leek het me beter om dan ook met SWT de screendump te maken
In de meest simpele vorm wordt daarbij de schermbuffer van de server gefragmenteerd (opgedeeld in kleinere rechthoeken) en die fragmenten worden naar de remote kant gestuurd. Het scherm wordt daar dan van linksboven naar rechtsonder rij voor rij opgebouwd en na de initiële opbouw worden alleen wijzigingen doorgestuurd. Je kan dan inderdaad JPEG compressie toepassen om de benodigde bandbreedte wat in te perken.
dat kunnen we denk ik wel programmeren

[ Voor 17% gewijzigd door 2playgames op 01-05-2007 19:02 ]


  • BestTested!
  • Registratie: Oktober 2003
  • Laatst online: 01-12 17:31
2playgames schreef op dinsdag 01 mei 2007 @ 18:41:
dat kunnen we denk ik wel programmeren
Als je dan JPEG gaat gebruiken, dan is het misschien aan te raden de discrete cosine transform (compressie techniek van JPEG) zelf te schrijven (of te vinden via google), dan kan je zelf wat experimenten met kwaliteit/grootte van de screens om door te sturen.

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
misschien, maar we hebben niet onbeperkt de tijd en die kunnen we misschien beter aan andere dingen besteden

maar kent iemand een goede manier om een SWT image naar JPEG om te zetten? ImageLoader.save kan wel JPEG naar een stream schrijven, maar daarbij kun je de kwaliteit niet instellen dus dat is eigenlijk te basic

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Ik ben nu bezig met het opnemen en streamen van de audio. Het opnemen is makkelijk zat (met Java Sound), en het streamen ook (bytes schrijven), maar ik zit in de knel met het comprimeren van het geluid.

Voor MP3 en de andere MPEG formaten moet je een licentie betalen, dus dat is geen optie.
Ogg Vorbis lijkt me wel wat, maar hier kan ik voor Java alleen een decoder vinden (JOrbis), geen enkele encoder (niet eens eentje die met JNI aan libvorbis of zoiets is gekoppeld).

Java Sound kan zelf (geloof ik) algoritmen als A-law en Mu-law gebruiken, maar deze leveren weer niet genoeg compressie (slechts 50%).

Het JMF is, zoals hierboven gezegd, outdated en incompleet.

(note: het gaat hier om gesproken geluid)

Het kan toch niet dat niemand ooit eerder geluid in Java heeft gecomprimeerd? Kan iemand ons weer in de goede richting wijzen?

edit: heb misschien iets gevonden: http://jspeex.sourceforge.net/

[ Voor 3% gewijzigd door 2playgames op 05-05-2007 00:02 ]


  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 08:22

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Interessant, maar helaas. Voor LAMEonJ moet je iets speciaals installeren (en dat wil de opdrachtgever niet), en JLayer kan alleen decoderen

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 08:22
2playgames schreef op zaterdag 05 mei 2007 @ 13:27:
Interessant, maar helaas. Voor LAMEonJ moet je iets speciaals installeren (en dat wil de opdrachtgever niet), en JLayer kan alleen decoderen
Ow, ik zie inderdaad nu pas dat LAMEOnJ de JNIEasy runtime nodig heeft. Ik dacht dat ze via de standaard JNI een wrapper hadden geschreven voor de lame DLL, maar dat is dus niet zo.

  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
ik ben eens wat met JSpeex bezig gegaan, en het lukt wel een beetje, maar niet echt. het gaat om een "client" (die audio opneemt en verstuurt) en een "server" (die de audio ontvangt en opslaat).

de documentatie is niet perfect *ahum* dus heb ik aan de hand van de bijgeleverde voorbeelden maar wat in elkaar geknutseld. ik heb nu alle Exceptions eruit, maar het resultaat is nog niet goed. als ik een halve minuut geluid opneem aan de client-kant, maakt de server hier een .wav file van 25 minuten met onherkenbaar geluid van (maar dat is al beter dan stilte :p)

hier is de relevante code:

programma dat audio opneemt
Java:
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
byte[] encbuf;
                SpeexEncoder enc = new SpeexEncoder();
                enc.init(0, 5, 8000, 1);
                int framesPerRead = 1;
                int packSize = 2 * enc.getChannels() * enc.getFrameSize();
                int len = packSize * framesPerRead;
                byte[] buffer = new byte[len];

                sock = new Socket(address, port);

                while(true) {

                    // read from input (and exit if line stopped)
                    if(line.read(buffer, 0, len) == 0) break;

                    // encode data
                    try {
                        for(int i = 0; i < framesPerRead; i++)
                            enc.processData(buffer, i*packSize, packSize);
                    } catch (IllegalArgumentException x) {
                        System.out.println("Buffer length: " + buffer.length);
                        x.printStackTrace();
                        System.exit(1);
                    }

                    // retrieve encoded data
                    encbuf = new byte[enc.getProcessedDataByteSize()];
                    if(enc.getProcessedData(encbuf, 0) > 0) {

                        // write encoded data
                        sock.getOutputStream().write(encbuf);

                    }
                }
                
                sock.close();


ontvanger:
Java:
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
                sock = new ServerSocket(port);
                Socket socka = sock.accept();
                System.out.println("Accepted!");

                SpeexDecoder dec = new SpeexDecoder();
                dec.init(0, 8000, 1, false);
                
                int frameSize = ((NbCodec.NB_FRAME_SIZE[NbEncoder.NB_QUALITY_MAP[5]]) + 7) >> 3;
                byte[] buffer = new byte[frameSize];
                byte[] decbuf;

                AudioFileWriter writer = new PcmWaveWriter(dec.getSampleRate(), dec.getChannels());
                writer.open("output.wav");
                writer.writeHeader(null);


                while(true) {
                    // read data from socket
                    if(socka.getInputStream().read(buffer) == 0) break;
                    dec.processData(buffer, 0, frameSize);
                    dec.processData(false);
                    
                    // write processed data to file
                    decbuf = new byte[dec.getProcessedDataByteSize()];
                    if(dec.getProcessedData(decbuf, 0) > 0) {
                        writer.writePacket(decbuf, 0, decbuf.length);
                    }
                }

                writer.close();
                sock.close();


ik heb nog niet eerder met audio gewerkt, dus dit is allemaal nieuw voor mij. kan een expert op dit gebied vertellen wat ik misschien verkeerd doe?

JSpeex API: http://jspeex.sourceforge.net/doc/index.html

  • voodooless
  • Registratie: Januari 2002
  • Laatst online: 30-11 11:20

voodooless

Sound is no voodoo!

Voor audio zou ik eens kijken naar H323 protocollen. Die zijn wat minder gevoeling voor lange delays en pakketten mogen wegvallen. Helaas zijn deze wel wat lastiger door NAT netwerken, maar veel routers kunnen daar tegenwoordig (zeker door de opkomst van voip) wel mee overweg.

Voor java zijn hier ook prima api's voor te vinden, met het oude JMF kun je dit prima doen bijvoorbeeld. Compressie krijg je dan al automatisch in bijna elke gewenste vorm. Voor spraak kun je bijvoorbeeld prima u-Law gebruiken.

Do diamonds shine on the dark side of the moon :?


  • 2playgames
  • Registratie: Februari 2005
  • Laatst online: 01-06 15:19
Helaas zijn deze wel wat lastiger door NAT netwerken, maar veel routers kunnen daar tegenwoordig (zeker door de opkomst van voip) wel mee overweg.
dat zal geen probleem zijn. in eerste instantie gaan we werken met een client-server systeem (dus geen P2P gesprekken). later moeten we dit misschien wel, dan kan het lastig worden

en inderdaad, als ik het nog eens bekijk is het JMF niet zo achterhaald als het lijkt
Pagina: 1