Mokabyte

Dal 1996, architetture, metodologie, sviluppo software

  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti
Menu
  • Argomenti
    • Programmazione & Linguaggi
      • Java
      • DataBase & elaborazione dei dati
      • Frameworks & Tools
      • Processi di sviluppo
    • Architetture dei sistemi
      • Sicurezza informatica
      • DevOps
    • Project Management
      • Organizzazione aziendale
      • HR
      • Soft skills
    • Lean/Agile
      • Scrum
      • Teoria della complessità
      • Apprendimento & Serious Gaming
    • Internet & Digital
      • Cultura & Società
      • Conferenze & Reportage
      • Marketing & eCommerce
    • Hardware & Tecnologia
      • Intelligenza artificiale
      • UX design & Grafica
  • Ultimo numero
  • Archivio
    • Archivio dal 2006 ad oggi
    • Il primo sito web – 1996-2005
  • Chi siamo
  • Ventennale
  • Libri
  • Contatti
Cerca
Chiudi

Nel numero:

183 aprile
, anno 2013

Un’applicazione di realtà aumentata con GWT

I parte: WebRTC e la libreria Elemental

Alberto Mancini e Francesca Tosi

Alberto Mancini

Sviluppatore web e mobile, freelance, teacher, sysadmin.
Attualmente lavoro come Java Developer e Architect per l'azienda di cui sono co-fondatore: K-TEQ Srls (http://www.k-teq.com)
#Java #GWT #JavaScript #HTML5 #Flink #MachineLearning

https://www.linkedin.com/in/abmancini/

Alberto Mancini e Francesca Tosi

Francesca Tosi

Freelance, Web & Mobile Developer and Architect, with a passion for fine tuned details.
Co-founder at K-TEQ Srls (http://www.k-teq.com).
GDG-Firenze Lead and founder. Intel Software Innovator.
#Java #GWT #StreamProcessing #MachineLearning

https://www.linkedin.com/in/francescatosij/

MokaByte

Un’applicazione di realtà aumentata con GWT

I parte: WebRTC e la libreria Elemental

Alberto Mancini e Francesca Tosi

Alberto Mancini e Francesca Tosi

  • Questo articolo parla di: Frameworks & Tools, Programmazione & Linguaggi, UX design & Grafica

In questo primo articolo ci occuperemo in particolare di GWT, della libreria Elemental e di come si possa utilizzare una parte di WebRTC, precisamente getUserMedia, in un progetto GWT guadagnando, dall’interno di una applicazione web, l’accesso alla webcam e conservando la possibilità di scrivere codice Java e riutilizzare, nel browser, anche librerie sviluppate per altri contesti.

Introduzione

GWT (Google Web Toolkit) [1], è il toolkit per lo sviluppo in Java di applicazioni “browser based” nato come “prodotto” Google ma oramai trasformatosi a tutti gli effetti un progetto open-source, è diventato in pochi anni uno dei framework di riferimento per lo sviluppo di applicazioni web complesse e il suo utilizzo sembra essere in grande crescita, come riportato da Web Frameworks Usage Statistics [2], che ne rileva una crescita d’impiego negli ultimi 12 mesi intorno al 90%.

GWT mette a disposizione dello sviluppatore Java molte possibilità: la compilazione del codice Java in JavaScript; un sistema di “compilazione condizionale” per gestire le incompatibilità tra i browser; un set di widget ben progettato; dei sistemi di comunicazione con i servizi server-side “amichevoli”, almeno per gli sviluppatori Java, e molto altro.

A fronte di tutta questa potenza, però, l’esigenza di rendere il codice il più possibile indipendente dal tipo di browser rende alle volte piuttosto laborioso l’utilizzo delle più recenti feature dei browser, soprattutto quando queste feature sono supportate solo da alcuni browser e non ancora standardizzate.

L’argomento

In questo articolo, primo di una breve serie, ci poniamo come obiettivo di mostrare come si possa utilizzare una parte di WebRTC [3], precisamente getUserMedia in un progetto GWT guadagnando, dall’interno di una applicazione web, l’accesso alla webcam e conservando la possibilità di scrivere codice Java e riutilizzare, nel browser, anche librerie sviluppate per altri contesti.

Se in questa prima parte ci occuperemo sostanzialmente solo di capire come la libreria GWT “to the metal” chiamata Elemental ci possa aiutare ad utilizzare WebRTC, nelle prossime puntate vedremo come utilizzare WebGL [4] e, con un pizzico di funambolismo informatico, come compilare in JavaScript la libreria NyARToolkit [5], per realizzare una applicazione “browser based”  per fare realtà aumentata (propriamente marker based real-time augmented reality) interamente lato client.

In ogni puntata di questa serie, oltre a cercare di descrivere gli aspetti rilevanti delle tecnologie e degli strumenti utilizzati,  pubblicheremo anche il codice d’esempio pronto per l’uso, che potrete scaricare come allegato dal menu a destra.

Gli strumenti

Per i lettori più impazienti, diciamo subito che è possibile vedere una demo all’indirizzo [6], che richiede il browser Google Chrome per funzionare; il codice, oltre che nell’allegato, si trova all’indirizzo [7] e la versione compilata è reperibile all’indirizzo riportato in [8] in formato WAR per semplicità ma, non utilizzando nessun servizio lato server, può essere pubblicata su qualsiasi web server.

GWT Elemental Library

A partire dalla versione 2.5, GWT  include Elemental [9], una libreria forse non molto conosciuta, e di sicuro poco documentata, ma dalle caratteristiche davvero interessanti per chi voglia utilizzare con GWT le feature HTML5 più recenti dei browser (almeno di quelli basati su WebKit).

Come si legge nella presentazione ufficiale, allo scopo di creare una libreria che potesse includere  “every HTML5 feature”  disponibile sui browser derivati da WebKit, Elemental viene generata automaticamente a partire dai file WebIDL [10] utilizzati dai motori JavaScript e quindi risulta relativamente aggiornata rispetto alle evoluzioni, spesso frenetiche, dei browser.

A fronte di una scarsissima documentazione, usare Elemental in un progetto GWT non è affatto complesso: basta scaricare l’ultima versione di GWT da [11] (la 2.5.1 nel nostro caso), creare un progetto, ad esempio con Eclipse come di consueto,  importare nella build path gwt-elemental.jar  e, infine,  aggiungre nel file .gwt.xml del modulo la riga:


Siamo pronti per utilizzare Elemental.

WebRTC getUserMedia

Nonostante negli ultimi anni ci siano stati molteplici tentativi di definire uno standard per permettere l’accesso alla webcam e al microfono di un computer dall’interno di una applicazione web (quindi eseguita nella “gabbia” del browser), solo di recente, con l’evoluzione di WebRTC, questa esigenza è stata effettivamente soddisfatta. Una descrizione dei tentativi di standard che si sono succeduti è riportata in [12].

WebRTC è una tecnologia che ha come obiettivo quello di rendere possibili le comunicazioni real time tra browser e, nonostante le innumerevoli complessità incontrate, avendo tra i suoi supporter nomi quali Google, Mozilla e Opera, la specifica sta rapidamente trovando implementazione sui alcuni dei browser maggiormente diffusi. Di fatto, come riportato nell’articolo in [13], all’inizio di febbraio è stato fatto un esperimento pubblico di interoperabilità tra Chrome e Mozilla allo scopo di mostrare come lo standard stia giungendo a maturità.

Benchè WebRTC abbia degli scopi di respiro ben più ampio rispetto a quelli che ci proponiamo in questo articolo, la prima componente di WebRTC che ha trovato implementazione relativamente stabile su Chrome, e attualmente disponibile su tutte le piattaforme, è quella che permette appunto di accedere allo stream video proveniente da webcam: getUserMedia.

L’accesso allo stream video attraverso getUserMedia e il suo effettivo utilizzo in una applicazione web richiede l’interazione di diverse “componenti” HTML5 (video, objectURL, canvas) e, come vedremo subito di seguito non è priva di qualche tecnicismo che vale la pena di osservare con attenzione.

GWT Elemental + WebRTC getUserMedia + tag video e canvas…

…ossia come realizzare una applicazione web per fare istantanee con la webcam. Vediamo come entrano in gioco le diverse componenti e quali passi occorre seguire per integrarli al meglio

Il primo elemento di cui abbiamo bisogno per la nostra applicazione è il tag HTML5 che servirà da contenitore per lo stream proveniente dalla webcam. Creare un video element  è estremamente semplice con Elemental:

VideoElement videoElement = Browser.getDocument().createVideoElement();

ed è altrettanto semplice specificare una sorgente:

videoElement.setSrc(url);

o, manipolando il DOM come da specifica [14], aggiungerne molteplici in modo che il browser possa scegliere quella supportata:

SourceElement src = Browser.getDocument().createSourceElement();
src.setType(type);
src.setSrc(source);
videoElement.appendChild(src);

Purtroppo, a fronte della indubbia comodità di utilizzare Elemental per accedere alle features “a basso livello” del browser, utilizzare gli Element di Elemental (che derivano da elemental.dom.Element) assieme ai  plain-old-GWT-widget (che invece utilizzano DOM elements che derivano da com.google.gwt.dom.client.Element) dobbiamo fare qualche altro passaggio.

Innanzi tutto un cast “nativo” (metodo definito in ElementalUtils nei sorgenti):

public final native static com.google.gwt.dom.client.Element
castElementToElement(elemental.dom.Element e) /*-{
       return e;
}-*/;

e quindi, usando la classe Widget, possiamo creare un wrapper intorno al VideoElement:

public class ElementalVideoWidget extends Widget {
       private VideoElement videoElement;
       public ElementalVideoWidget() {
              videoElement = Browser.getDocument().createVideoElement() ;
              setElement(ElementalUtils.castElementToElement(videoElement));
       }
       public void addSource(String source, String type) {
              SourceElement src = Browser.getDocument().createSourceElement();
              src.setType(type);
              src.setSrc(source);
              videoElement.appendChild(src);
       }
       public VideoElement getVideoElement() {
              return videoElement;
       }
}

dove, peraltro, possono trovare spazio alcuni metodi per rendere meno verboso l’utilizzo del VideoElement (p.e. addSource).

Ottenuto un Widget  possiamo direttamente usare il video:

ElementalVideoWidget video = new ElementalVideoWidget();
video.addSource("http://www.w3schools.com/html/mov_bbb.mp4", "video/mp4");
video.addSource("http://www.w3schools.com/html/mov_bbb.ogg", "video/ogg");
RootPanel.get().add( video );

Come detto, avere a disposizione il tag video è essenziale per il nostro sviluppo in quanto è il contenitore necessario per utilizzare il LocalMediaStream [15] che il browser mette a disposizione di una applicazione per accedere allo stream proveniente dalla webcam.

getUserMedia & createObjectUrl

La specifica di WebRTC prevede che, per accedere a quelli che sono chiamati  “user media stream” (video da webcam e audio da microfono, fuori dal nostro scopo)  l’applicazione debba utilizzare la funzione

navigator.getUserMedia(options, successCallback, failCallback)

rinominata, su Chrome, navigator.webkitGetUserMedia, con un chiaro riferimento a WebKit in quanto ancora non standard. Questa funzione provvede a chiedere all’utente l’autorizzazione ad accedere agli stream richiesti, si occupa del setup e li passa alla callback [16].

Natualmente Elemental supporta getUserMedia, ma in questo caso HTML5 risulta essere un bersaglio troppo mobile anche per Elemental e sulla versione corrente di Chrome la getUserMedia disponibile genera codice non utilizzabile  (come risultano inutilizzabili buona parte delle demo che si trovano in rete, probabilmente proprio per questo cambiamento).

Per usare getUserMedia siamo quindi costretti ad abbandonare anche Elemental e scrivere direttamente codice “nativo”, JavaScript insomma,. Grazie a JavaScript Native Interface (JSNI) [17], questo è agevole semplicemente definendo (sempre nella classe ElementalUtils nei sorgenti) un metodo statico:

public native static boolean
getUserVideo(UserMediaCallback callback) /*-{
       if(navigator.webkitGetUserMedia) {
              navigator.webkitGetUserMedia(
              {video: true, toString: function() {return "video";}},
              function(stream) {
                     var s = window.URL.createObjectURL(stream);
                     $entry(callback.@...::onSuccess(Ljava/lang/String;)(s));
              },
              function() {
                     $entry(callback.@...::onFail()());
              });
              return true;
       } else {
              return false;
       }
 }-*/;

dove si vede anche come sia possibile passare un oggetto Java (UserMediaCallback) a un metodo nativo Javascript.

Va osservato che per poter essere utilizzato come source del tag video, lo stream che viene passato alla callback di webKitGetUserMedia deve essere trasformato in un URL. A renderlo possibile ci pensa ancora un altra specifica HTML5 (nata questa volta per permettere l’accesso a file dal browser) [18] e viene utilizzata nella linea di codice

       var s = window.URL.createObjectURL(stream);

L’URLl così generato potrà essere direttamente utilizzato dal video.

Possiamo quindi finalmente scrivere l’intero EntryPoint:

public class ElementalGetUserMediaMinimal implements EntryPoint {
       @Override
       public void onModuleLoad() {
        boolean res = ElementalUtils.getUserVideo(new UserMediaCallback() {
              @Override
              public void onSuccess(String url) {
                     ElementalVideoWidget
                     video = new ElementalVideoWidget();
                     video.getVideoElement().setSrc(url);
                     video.getVideoElement().play();
                     RootPanel.get().add(video);
              }
              @Override
               public void onFail() {
                     Window.alert("FAIL, unauthorized ?");
              }
       });
       if(!res)
              Window.alert("FAIL, no webkitGetUserMedia Support ?");
       }
}

dove è necessario ricordare di chiamare il metodo play() dell’oggetto video, dopo aver inserito l’URL della sorgente, altrimenti il video non partirà.

Sebbene necessario per visuallizzare lo stream proveniente dalla webcam, l’oggetto video (VideoElement) non fornisce accesso al suo contenuto e quindi non permette alla nostra applicazione di elaborare lo stream. Per poter avere accesso allo stream video l’unica possibilità è quella di copiarlo su un canvas (il tag HTML5 ) e poi utilizzare i metodi dell’oggetto canvas per accedere ai fotogrammi.

Creare un canvas, sempre con Elemental, è ancora una volta semplice,

CanvasElement canvas = Browser.getDocument().createCanvasElement();

e per effettuare la copia del video sul canvas bastano le seguenti righe di codice:

canvas.setWidth( video.getVideoElement().getVideoWidth());
canvas.setHeight( video.getVideoElement().getVideoHeight());
elemental.html.CanvasRenderingContext2D ctx =
(CanvasRenderingContext2D) canvasElement.getContext("2d");
ctx.drawImage(videoElement, 0, 0);

dove le prime due righe servono a garantire che il canvas abbia le medesime dimensioni del video (la cui risoluzione non è nota in anticipo essendo dipendente dall’hardware, dal browser, dal sistema operativo).

A tal proposito va osservato che la risoluzione del video stream non è immediatamente disponibile al browser al  momento della chiamata alla callback e quindi  i metodi video.getVideoElement().getVideoWidth() e getVideoHeight() potrebbero non restituire i valori corretti. Per essere sicuri di non utilizzare il video prima della inizializzazione la specifica mette a disposizione l’evento loadedmetadata che l’applicazione può utilizzare per essere informata della effettiva disponibilità delle informazioni sul video:

video.getVideoElement().addEventListener("loadedmetadata", new EventListener() {
       @Override
       public void handleEvent(Event evt) {
              …
       }
});

Una volta copiato il video sul canvas è possibile estrarne il contenuto utilizzando il metodo canvas.toDataUrl(type) che restituisce il contenuto del canvas sotto forma di data URL  (data URI secondo l’originale RFC [19]) direttamente utilizzabile, ad esempio, per popolare immagini, e.g.

Image img = new Image(canvas.toDataUrl("image/png"));

Come immaginabile, e visibile anche dalla demo (per Chrome) [6], una volta trasformati i fotogrammi del video in immagini la loro manipolazione è estremamente semplice.

Conclusioni

Come risulta chiaro dal lavoro svolto fino a questo punto, nel seguire questa strada per raggiungere  l’obiettivo che ci siamo posti dobbiamo riconciliare almeno tre mondi: Elemental, i widget GWT che, anche se spesso considerati poco “appealing”, sono a nostro avviso uno strumento eccezionale con cui lavorare, e le vorticose evoluzioni di Chrome.

Vista l’indubbia complicatezza di rendere utilizzabili da GWT alcune feature di Chrome e di aggirare anche qualche “problema” di Elemental scrivendo pezzi di codice nativo potrebbero venire naturali due domande: la prima è “Non sarebbe meglio scrivere direttamente in JavaScript?”; la seconda è “Non sarebbe meglio abbandonare Elemental e scrivere tutto usando solo JSNI?”.

Entrambe le domande, pur rispettabilissime, a nostro parere hanno risposta negativa. Se infatti procedessimo usando solo JavaScript perderemmo, come detto anche nei paragrafi precedenti,  l’opportunità di utilizzare il compilatore di GWT per “portare” sul browser le librerie che nei prossimi capitoli ci serviranno; oltre a questo perderemmo del tutto anche la comodità (magari opinabile ma da noi ritenuta essenziale) di scrivere codice in Java (tipato, compilato, object-oriended nel modo classico) invece che in JavaScript.

Abbandonare invece Elemental è alla fin fine una possibilità che nella stesura del codice di questo articolo abbiamo accarezzato anche noi: la parte di WebRTC che utilizziamo è piccola (sostanzialmente un solo metodo) e malfunzionante al punto che l’implementazione JSNI si è resa comunque necessaria, video ha una  specifica (per quel tanto che lo abbiamo usato noi) relativamente breve e, se dovessimo basarci su quello di cui  abbiamo avuto bisogno, anche canvas (e l’interazione video/canvas) sarebbe stato semplice da wrappare con codice nativo.

D’altro canto quello che Elemental mette a disposizione è  l’intera specifica degli oggetti con tutti i metodi che erano presenti al momento della generazione della libreria (provate a guardare la quantità di metodi che CanvasElement e CanvasRenderingContext2D mettono a disposizione) risparmiandoci quindi di dover creare a mano metodi nativi ad ogni nuova caratteristiche che vogliamo esplorare.

Sostanzialmente, a nostro parere, il costo in termini di complicatezza che dobbiamo pagare per aggirare qualche problema di Elemental e qualche idiosincrasia tra GWT “classico” e GWT “to the metal” è ampiamente ripagato dal fatto di avere preconfezionati i wrapper per la gran parte degli oggetti che ci possono servire.

 

Riferimenti

[1] Google Web Toolkit

https://developers.google.com/web-toolkit/

 

[2] Statistiche sull’adozione dei framework

http://blog.websitesframeworks.com/2013/03/web-frameworks-statistics-174/

 

[3] WebRTC

http://www.webrtc.org/

 

[4] WebGL

http://www.khronos.org/webgl/

 

[5] NyARToolkit

http://nyatla.jp/nyartoolkit/wp/?page_id=198

 

[6] La demo del progetto

http://www.jooink.com/experiments/ElementalGetUserMediaDemo/

 

[7] Il codice

http://code.google.com/p/elemental-getusermedia-demo/source/…

 

[8] La versione compilata

http://elemental-getusermedia-demo.googlecode.com/files/ElementalGetUserMediaDemo.war

 

[9] Elemental

https://developers.google.com/web-toolkit/articles/elemental?hl=en

 

[10] WebIDL

http://www.w3.org/TR/WebIDL/

 

[11] GWT

https://code.google.com/p/google-web-toolkit

 

[12] Capturing Audio & Video in HTML5

http://www.html5rocks.com/en/tutorials/getusermedia/intro

 

[13] Alfonso Maruccia, “Mozilla e Google si videochiamano con WebRTC”

http://punto-informatico.it/3709886/PI/News/mozilla-google-si-videochiamano-webrtc.aspx

 

[14] video-element WHATWG

http://www.whatwg.org/specs/web-apps/current-work/ – the-video-element

 

[15] Local Media Stream

http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-LocalMediaStream

 

[16] Get User Media

dev.w3.org getusermedia

 

[17] JSNI

https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI

 

[18] The createObjectURL static method

http://www.w3.org/TR/FileAPI/ – dfn-createObjectURL

 

[19] RFC 2397 “The “data” URL scheme”

http://tools.ietf.org/html/rfc2397

 

 

 

 

 

Facebook
Twitter
LinkedIn
Alberto Mancini e Francesca Tosi

Alberto Mancini

Sviluppatore web e mobile, freelance, teacher, sysadmin.
Attualmente lavoro come Java Developer e Architect per l'azienda di cui sono co-fondatore: K-TEQ Srls (http://www.k-teq.com)
#Java #GWT #JavaScript #HTML5 #Flink #MachineLearning

https://www.linkedin.com/in/abmancini/

Alberto Mancini e Francesca Tosi

Francesca Tosi

Freelance, Web & Mobile Developer and Architect, with a passion for fine tuned details.
Co-founder at K-TEQ Srls (http://www.k-teq.com).
GDG-Firenze Lead and founder. Intel Software Innovator.
#Java #GWT #StreamProcessing #MachineLearning

https://www.linkedin.com/in/francescatosij/

Alberto Mancini e Francesca Tosi

Alberto Mancini e Francesca Tosi

Tutti gli articoli
Nello stesso numero
Loading...

Management dal semplice al complesso

I parte: Il Project Management e l'evoluzione della complessità dei sistemi

JBoss Seam: Web Application di nuova generazione

V parte: Il meccanismo di Bijection

Agile Gamification: apprendere le metodologie giocando

II parte: Collaborazione e lavoro per piccoli lotti

Aspetti psicologici nella gestione di progetto

V parte: Team building aziendale. il valore del lavoro di squadra

Nella stessa serie
Loading...

Un’applicazione di realtà aumentata con GWT

V parte: Creare l'applicazione HTML5/JavaScript

Un’applicazione di realtà aumentata con GWT

IV parte: Utilizziamo NyARToolKit

Un’applicazione di realtà aumentata con GWT

III parte: WebGL e oggetti 3D

Un’applicazione di realtà aumentata con GWT

II parte: Introduciamo WebGL

Mokabyte

MokaByte è una rivista online nata nel 1996, dedicata alla comunità degli sviluppatori java.
La rivista tratta di vari argomenti, tra cui architetture enterprise e integrazione, metodologie di sviluppo lean/agile e aspetti sociali e culturali del web.

Imola Informatica

MokaByte è un marchio registrato da:
Imola Informatica S.P.A.
Via Selice 66/a 40026 Imola (BO)
C.F. e Iscriz. Registro imprese BO 03351570373
P.I. 00614381200
Cap. Soc. euro 100.000,00 i.v.

Privacy | Cookie Policy

Contatti

Contattaci tramite la nostra pagina contatti, oppure scrivendo a redazione@mokabyte.it

Seguici sui social

Facebook Linkedin Rss
Imola Informatica
Mokabyte