WebRTC in Perl

{

Using Mojolicious




What
Why
How

WebRTC Rationale


Real-time Communication Between Browsers
Is Peer-to-Peer



http://www.html5rocks.com/en/tutorials/webrtc/basics/



...




Why

No plugin required
Completely browser based
Non-browser clients possible
How





How

Use signaling to exchange endpoint details
Signaling is the backend
Directly connect, if possible
Relay serv...
#!/OPT/PERL

FUNCTION APPENDDIV(DATA)
VAR DIV

USE
USE

MOJOLICIOUS ::LITE;
JSON;

{
= DOCUMENT.CREA TEELEMENT('DIV');
= D...


Gist

https://gist.github.com/brianmed/6927400







$ perl datachannel.pl daemon
Luanch two instances of chrome; perhaps
stable and canary
Click answerer in one wi...
Screenshot of answerer
Upcoming SlideShare
Loading in...5
×

Webrtc mojo

918

Published on

WebRTC DataChannel example in Perl and Mojolicious.

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
918
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Webrtc mojo

  1. 1. WebRTC in Perl { Using Mojolicious
  2. 2.    What Why How WebRTC Rationale
  3. 3.  Real-time Communication Between Browsers Is Peer-to-Peer  http://www.html5rocks.com/en/tutorials/webrtc/basics/  What
  4. 4.    Why No plugin required Completely browser based Non-browser clients possible
  5. 5. How
  6. 6.     How Use signaling to exchange endpoint details Signaling is the backend Directly connect, if possible Relay server used if nothing else works
  7. 7. #!/OPT/PERL FUNCTION APPENDDIV(DATA) VAR DIV USE USE MOJOLICIOUS ::LITE; JSON; { = DOCUMENT.CREA TEELEMENT('DIV'); = DATA; FUNCTION RETURNSDP() { APPENDDIV("ANSWERER: SEND SDP"); DIV.INNER HTML SOCKET.SEND({ SENDER: 'ANSWERER', VAR CHATOUTPUT '/' => SUB { MY $SELF = SHIFT; = DOCUMENT.GE TELEME NTBYID('CHA T-OUTPUT'); SDP: ANSWERER.LOCALDES CRIP TION CHATOUTP UT.INS ERTBEFORE(DIV, CHATOUTPUT.FIRS TCHILD); GET }); } DIV.TABINDE X $SELF->RENDER("INDE X"); = 0; DIV.FOCUS(); }; IF (ISFIREFOX) } { ANSWERER.SETRE MOTE D ESCRIP TION(NEW MOZRTCS ESS ION DE SCRIP TION(OFFER SDP), FUNCTION() ANSWERER.CREATE ANSWE R(FUNCTION MY '/WSCHANNEL/:TYPE' => SUB { $SELF = SHIFT; MY $TYPE = $SELF->PARAM("TYPE "); VAR ICESERVERS WEBSOCKET ICESERVERS: ={ $SELF->APP->LOG->DEBUG("W EBSOCKET OPENED: }, ONERROR, }); $ID = MOJO::IOLOOP->RECURRING(4 => SUB { ("OFFERER" EQ $TYPE) { IF (- F "/TMP/SDP.ANSWERER") { $SELF->APP->LOG->DEBUG("SENDING SDP: TO OFFERER FROM ANSWERER"); MY $TXT = MOJO::UTIL::SLURP("/TMP/SDP.ANSWERE R"); MY $REF = JSON::FROM_JSON($TX T); $SELF->SEND({JSON => $REF}); UNLINK ("/TMP/SDP.ANSWERER"); MOJO::UTIL::SPURT(SCA LAR LOCALTIME(TIME), "/TMP/SDP.OFFERER.SENT"); } OPTIONAL: ={ [{DTLSSRTPK EYA GREEMENT: {RTPDA TAC HA NNE LS: TRUE }] ={ [], { OFFERTORECE IVE AUDIO: OFFERTORECE IVE V IDEO: VAR MEDIACONS TRA INTS VAR CHATINPUT IF (E.KEYCODE APPENDDIV(THIS.VALUE); FUNCTION SETCHANNEL EV ENTS ( CHANNEL, CHANNELNA MEF OR CONSOLEOUTP UT) CHANNEL.ONERROR FALSE = ONERROR; OFFERER MOZRTCPE ERC ONNECTION(ICES ERV ERS, OPTIONA L RTPD A TA CHANNELS ); = NEW WEBKITRTCPEE RC ONNECTION(ICESE RV ERS, OPTIONAL R TPD A TA CHA NNELS ); = FUNCTION () { CONSOLE.LOG("CHANNEL: ONOPEN"); } DOCUMENT.GETELE ME NTB Y ID('CHA T- INP UT').DIS ABLED ELSE { OFFERER = FALSE; }; } } = OFFERER.CREATED A TA CHANNEL('RTCD A TA CHANNEL', { RELIABLE : TRUE { VAR URL; IF }); ("OFFERER" == TYPE) { URL = "<%= URL_FOR('/WSCHANNEL/OFFERE R')->TO_AB S->SCHEME('WS') %>"; } CONNECTION FROM OFFERER"); = OFFERERDATAC HANNEL; ELSE CONSOLE.LOG("CONSOLE : OFFERER"); { URL = "<%= URL_FOR('/WSCHANNEL/ANSWERER')->TO_ ABS->SCHE ME('WS') %>"; } SETCHANNEL EVE NTS (OFFERER DA TAC HANNEL, 'OFFERER'); CONSOLE.LOG("CONNECTING: SOCKET = FUNCTION (EVENT) { CONSOLE.LOG("ONICECANDIDA TE : OFFERER"); IF (EVENT.CANDIDA TE ) SENDCANDIDA TE (EVE NT.CANDIDA TE ); IF (!EVENT.CANDIDA TE ) RETURNSDP(); FROM OFFERER"); " + URL); = NEW W EBSOCKET(URL); = FUNCTION () { " + URL); SOCKET.ONOPEN APPENDDIV("CONNECTED: }; SOCKET.ONMESSA GE = FUNCTION (E) { = JSON.PARSE(E.DATA); " + DATA.SENDER); { == 'OFFERER') { APPENDDIV("RECV: SDP : OFFERER"); CREATEANSWER(DA TA. SDP); }; VAR DATA CONSOLE.LOG("ONMESSA GE : FUNCTION SENDCANDIDA TE () { IF (DATA.SDP) APPENDDIV("OFFERER: SEND CANDIDA TE "); IF (DATA.SENDER SOCKET.SEND({ SENDER: 'OFFERER', } CANDIDATE : EVENT.CANDIDA TE } } }); ELSE }); } $SELF->ON(MESSAGE => SUB { MY ($SELF, $MSG) = @_; FUNCTION RETURNSDP() { APPENDDIV("RECV: SDP : ANSWERER"); IF (ISFIREFOX) { APPENDDIV("OFFERER: SEND SDP"); SOCKET.SEND({ SENDER: 'OFFERER', SDP: OFFERER.LOCALDE SCRIP TION }); $TYPE"); ($$RET{SENDER} && "OFFERER" EQ $$RET{SENDER} && $$RET{SDP}) { MOJO::UTIL::SPURT($MSG, "/TMP/SDP.OFFERER"); ELSE OFFERER.CREATE OFFER(FUNCTION } (SESSIONDES CRIP TION) { OFFERER.SETLOCA LD ESCRIP TION(SE SS ION DES CRIP TION); }, ONERROR, MEDIA CONS TRA INTS); FUNCTION ONERROR( ERR) { CONSOLE.LOG(ERR); } FUNCTION CREATEANSWER(OFFER SDP) { { ANSWERER = NEW MOZRTCP EER CONNE CTION( ICE SERVE RS , IF (ISFIREFOX) OPTIONAL R TP DA TAC HANNE LS ); } ELSE { = NEW WEBKITRTCP EER CONNE CTION(ICE SERVE RS, ANSWERERDATA CHA NNE L OPTIONAL RTP DA TAC HANNELS ); IF = ANSWERER.CREA TED A TA CHANNEL('RTCD A TA CHANNEL', { RELIABLE : TRUE }); SETCHANNEL EVE NTS (ANSWERE RD A TA CHANNEL, 'ANSWERER'); CONNECTION $CODE."); = ANSWERERDATA CHANNEL; CONSOLE.LOG("CONSOLE : ANSWER"); ANSWERER.ONICECANDIDA TE = FUNCTION (EVENT) { CONSOLE.LOG("ONICECANDIDA TE : ANSWERER"); }; IF (EVENT.CANDIDA TE ) SENDCANDIDA TE (); IF (!EVENT.CANDIDA TE ) RETURNSDP(); APP->START; }; __DATA__ FUNCTION SENDCANDIDA TE () } SOCKET.PUSH SOCKET.SEND SOCKET.SEND({ }); = SOCKET.SEND; = FUNCTION (DATA ) { " + DATA .SENDER); CONSOLE.LOG("SEND: SENDER: 'ANSWERER', CANDIDATE : EVENT.CANDIDA TE } ("ANSWERER" === DATA.SENDER && DATA.CANDIDA TE) { APPENDDIV("RECV: CANDIDA TE : ANSWERER"); IF (ISFIREFOX) { OFFERER.ADDICECANDIDA TE (NEW MOZRTCICECA NDIDA TE ({ SDPMLINEINDE X: DATA .CANDIDA TE .SDP ML INEINDE X, CANDIDATE: DATA.CANDIDA TE .CANDIDA TE })); } ELSE { OFFERER.ADDICECANDIDA TE (NEW RTCICECA NDIDA TE ({ SDPMLINEINDE X: DATA .CANDIDA TE .SDP ML INEINDE X, CANDIDATE: DATA.CANDIDA TE .CANDIDA TE })); } }; { APPENDDIV("ANSWERER: SEND CANDIDA TE"); @@ INDEX.HTML.EP ("OFFERER" === DATA.SENDER && DATA.CANDIDA TE) { APPENDDIV("RECV: CANDIDA TE : OFFERER"); IF (ISFIREFOX) { ANSWERER.ADDICECANDIDA TE (NEW MOZRTCICE CANDIDA TE ({ SDPMLINEINDE X: DATA .CANDIDA TE .SDP ML INEINDE X, CANDIDATE: DATA.CANDIDA TE .CANDIDA TE })); } ELSE { ANSWERER.ADDICECANDIDA TE (NEW RTCICECANDIDA TE ({ SDPMLINEINDE X: DATA .CANDIDA TE .SDP ML INEINDE X, CANDIDATE: DATA.CANDIDA TE .CANDIDA TE })); } } ANSWERER } }); CLOSED WITH STATUS RTCSES S IOND ESCRIP TION(DA TA.SDP)); } } } ($$RET{SENDER} && $$RET{CANDIDA TE}) { FOREACH MY $SUFFIX (QW (001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 END)) { IF ("END" EQ $SUFFIX) { $SELF->APP->LOG->DEBUG("W E ARE OUT OF CANDIDA TE SUFFIXES."); LAST; } MY $F = "/TMP/CANDIDA TE .$$RE T{SENDER}.$SUFFIX"; IF (-F $F) { NEXT; } MOJO::UTIL::SPURT($MS G, $F); LAST; } { OFFERER.SETREMOTE DE SCRIP TION(NEW } IF ($$RET{SENDER} && "ANSWERER" EQ $$RET{SENDER} && $$RET{SDP}) { MOJO::UTIL::SPURT($MSG, "/TMP/SDP.ANSWERER"); } { OFFERER.SETREMOTE DE SCRIP TION(NEW MOZRTCSE SS IOND ES CRIP TION(DA TA.SDP)); } } } <SCRIPT> VAR ISFIREFOX = !!NAVIGATOR.MOZGE T USER ME DIA; VAR CONNECTION; + EVENT.DATA); SOCKET.PUSH(JSON.S TRINGIFY (DA TA)); }; } </SCRIPT> Code – 350 lines!! = ''; THIS.FOCUS(); }; CHANNEL.ONOPEN OFFERERDATA CHANNEL $SELF->ON(FINISH => SUB { MY ($SELF, $CODE, $REASON) = @_; $SELF->APP->LOG->DEBUG("W EBSOCKET MOJO::IOLOOP->REMOVE($ID); UNLINK ("/TMP/SDP.$TYPE "); }); A MESSAGE :' </SCRIPT> { = NEW OFFERER.ONICECANDIDA TE IF THIS.VALUE + 'RECEIVED { IF (ISFIREFOX) } IF = FUNCTION (EVENT) { CONSOLE.LOG("CHANNEL: ONMESSAGE "); APPENDDIV(CHANNEL NAMEF OR CONSOLEOUTP UT }; } IF CONSOLE.LOG(CONNECTION); CONNECTION.SEND(THIS.VALUE); CHANNEL.ONMESSAGE VAR OFFERER, ANSWERER, ANSWERERDA TA CHA NNE L, OFFERERDA TA CHA NNEL; ELSE TYPE: { } }; ANSWERER"); = DOCUMENT.GE TELE MENTB YID('CHA T-INPUT'); = FUNCTION(E) { !== 13 || !THIS.VALUE) RETURN; CHATINPUT.ONKE YP RES S } FALSE, FUNCTION CONNECTS IGNALER( TYP E ) (- F "/TMP/SDP.OFFERER.SENT") { FOREACH MY $F (GLOB("/TMP/CANDIDA TE .OFFERE R.*")) { $SELF->APP->LOG->DEBUG("SENDING $F: TO ANSWERER MY $TXT = MOJO::UTIL::SLURP($F); MY $REF = JSON::FROM_JSON($ TXT); $SELF->SEND({JSON => $REF}); UNLINK($F); } UNLINK ("/TMP/SDP.OFFE RER.SE NT"); { MANDATORY : } $SELF->APP->LOG->DEBUG("MSG: $MSG: MY $RET = JSON::FROM_JSON($MS G); FUNCTION() MEDIACONS TRA INTS ); } OPTIONAL: FOREACH MY { (- F "/TMP/SDP.OFFERER") { MY $TXT = MOJO::UTIL::SLURP("/TMP/SDP.OFFERER"); MY $REF = JSON::FROM_JSON($TX T); $SELF->APP->LOG->DEBUG("SENDING SDP: TO ANSWERER $SELF->SEND({JSON => $REF}); UNLINK ("/TMP/SDP.OFFE RER "); RTCS ESS ION DES CRIP TION(OFFER SDP), (SESSIONDESCRIP TION) { ANSWERER.SETLOCA LDE SCRIP TION(SE SS ION DES CRIP TION); }, ONERROR, }, ONERROR); FUNCTION CREATEOFFE R() $F (GLOB("/TMP/CANDIDA TE.ANSWERE R.*")) { $SELF->APP->LOG->DEBUG("SENDING $F: TO OFFERER FROM MY $TXT = MOJO::UTIL::SLURP($F); MY $REF = JSON::FROM_JSON($TX T); $SELF->SEND({JSON => $REF}); UNLINK ($F); ANSWERER.CREATE ANSWE R(FUNCTION TRUE}, }; IF IF { ANSWERER.SETRE MOTE D ESCRIP TION(NEW VAR OPTIONA L RTPD A TA CHANNELS IF MEDIACONS TRA INTS ); } ELSE $TYPE"); # INCREASE INACTIV ITY TIMEOUT FOR CONNECTION A BIT MOJO::IOLOOP->STREAM($SELF->TX->CONNE CTION)->TIME OUT(300); MY (SESSIONDESCRIP TION) { ANSWERER.SETLOCA LDE SCRIP TION(SE SS ION DES CRIP TION); [{ URL: 'STUN:23.21.150.121' }] }; { HAS WORKED IN CHROME.<BR /> CREATE OFFER SHOULD BE CLICKED BY THE "OFFERER".<BR /> <BUTTON ID="CONNECT-OFFE RER ">CONNECT AS OFFERER</BUTTON> <BUTTON ID="CONNECT-ANSWERE R">CONNECT AS ANSWERER</BUTTON><BR /> <BUTTON ID="CREATE-OFFER">C REA TE OFFER</BUTTON> <INPUT TYPE="TE XT" ID="CHAT-INPUT" STYLE="FONT-S IZE: 1.2EM;" PLACEHOLDER="CHA T <DIV ID="CHAT-OUTPUT"></DIV> <SCRIPT> DOCUMENT.GE TE LE MENTB YID('CONNE CT-OFFERE R').ONCLICK = FUNCTION () { THIS.DISAB LED = TRUE; CONNECTS IGNALE R("OFFERE R"); }; DOCUMENT.GE TE LE MENTB YID('CONNE CT-ANSWE RER').ONCLICK = FUNCTION () { THIS.DISAB LED = TRUE; CONNECTS IGNALE R("ANSWE RER "); }; DOCUMENT.GE TE LE MENTB YID('CREA TE-OFFE R').ONCLICK = FUNCTION () { CREATEOFFER(); }; MESSAGE " DISABLED>
  8. 8.  Gist https://gist.github.com/brianmed/6927400
  9. 9.     $ perl datachannel.pl daemon Luanch two instances of chrome; perhaps stable and canary Click answerer in one window; offerer in the other; then click Create Offer in offerer window Chat session should initiate Run me
  10. 10. Screenshot of answerer
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×