Sei sulla pagina 1di 93

!"#$%&'()%" #% +,-.

/0
12" 3'$",4
056,7 8(6,")9#: /$%;%
16$,(#%$ %7 8#2"&2$&9: <%=,%

>?2" @%5"9#%"
169)"A'695,& B"A6",,$
>C2D2

WebRTC
Web81C 1uLorlal 1oplcs
WhaL ls Web81C?
Pow Lo use Web81C
Web81C eer-Lo-eer Medla
Web81C roLocols and lL1l SLandards
Web81C W3C Al Cvervlew
seudo Code WalkLhrough
racucal blLs
1Au SummlL 8angkok 2013 2
WhaL ls Web81C?
Web81C ls volce & vldeo ln Lhe browser"
Access Lo camera and mlcrophone wlLhouL a
plugln
no proprleLary plugln requlred!
Audlo/vldeo dlrecL from browser Lo browser
Why does lL mauer?
Medla can sLay local
Moblle devlces evenLually dropplng volce channel
anyway
Cames
1Au SummlL 8angkok 2013 4
1he 8rowser 81C luncuon
Web81C adds new 8eal-
1lme Communlcauon (81C)
luncuon bullL-ln Lo
browsers
no download
no llash or oLher pluglns
ConLalns
Audlo and vldeo codecs
AblllLy Lo negouaLe peer-Lo-
peer connecuons
Lcho cancellauon, packeL loss
concealemenL
ln Chrome & llrefox Loday,
lnLerneL Lxplorer someume
and Safarl evenLually
P11 or WebSockeLs

Cn-Lhe-wlre proLocols
81C Als CLher Als
nauve CS Servlces
Web
Server
!avaScrlpL/P1ML/CSS
Web
8rowser
8rowser
81C
luncuon
(Slgnallng)
(Medla or uaLa)
P11 or WebSockeLs

Slgnallng
Server
1Au SummlL 8angkok 2013 3
8eneLs of Web81C
E%$ 1,C,?%;,$
SLreamllned developmenL -
one plauorm
Slmple Als - deLalled
knowledge of 81C proLocols
noL needed
nA1 Lraversal only uses
expenslve relays when no
oLher cholce
Advanced volce and vldeo
codecs wlLhouL llcenslng
E%$ F9,$
no download or lnsLall -
easy Lo use
All communlcaLon
encrypLed - prlvaLe
8ellable sesslon
esLabllshmenL
[usL works"
LxcellenL volce and vldeo
quallLy
Many more cholces for real-
ume communlcauon
1Au SummlL 8angkok 2013 6
Web81C SupporL of Muluple Medla
Muluple sources of audlo and vldeo are assumed
and supporLed
All medla, volce and vldeo, and feedback messages
are muluplexed over Lhe same LransporL address
SLereo Audlo
8rowser M
on Moblle
8rowser L
on LapLop
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
WebCam vldeo
1Au SummlL 8angkok 2013 7
Web81C 1rlangle
8oLh browsers runnlng Lhe same web appllcauon from web
server
eer Connecuon esLabllshed beLween Lhem wlLh Lhe help of
Lhe web server
Web Server
(Appllcauon)
8rowser M
(8unnlng P1ML3 Appllcauon
from Web Server)
8rowser L
(8unnlng P1ML3 Appllcauon
from Web Server)
eer Connecuon (Audlo, vldeo, and/or uaLa)
1Au SummlL 8angkok 2013 8
Web81C 1rapezold
Slmllar Lo Sl 1rapezold
Web Servers communlcaLe uslng Sl or !lngle or proprleLary
Could become lmporLanL ln Lhe fuLure.
Web Server A
(Appllcauon A)
eer Connecuon (Audlo and/or vldeo)
Web Server 8
(Appllcauon 8)
Sl
or !lngle
8rowser M
(8unnlng P1ML3 Appllcauon
from Web Server A)
8rowser 1
(8unnlng P1ML3 Appllcauon
from Web Server 8)


1Au SummlL 8angkok 2013 9
Web81C and Sl
Sl (Sesslon lnluauon roLocol) ls a slgnallng proLocol used by servlce
provlders and enLerprlses for real-ume communcauon
eer Connecuon appears as a sLandard 81 sesslon, descrlbed by Su
Sl LndpolnL musL supporL 81CWL8 medla exLenslons

Web Server
Sl CllenL

eer Connecuon (Audlo and/or vldeo)
Sl Server
Sl
Sl
8rowser M
1Au SummlL 8angkok 2013 10
Web81C and !lngle
Web Server
!lngle CllenL
eer Connecuon (Audlo and/or vldeo)
xM Server
!lngle
!lngle
8rowser M
!lngle ls a slgnallng exLenslon Lo xM (LxLenslble Messaglng and
resence roLocol, aka !abber)
eer Connecuon Su can be mapped Lo !lngle
!lngle LndpolnL musL supporL 81CWL8 Medla exLenslons
1Au SummlL 8angkok 2013 11
Web81C and S1n
eer Connecuon LermlnaLes on a S1n CaLeway
Audlo Cnly
Lncrypuon ends aL CaLeway
Web Server
8rowser M S1n CaLeway
eer Connecuon (Audlo)
hone
1Au SummlL 8angkok 2013 12
Web81C !"#$ Sl
8rowser runs a Sl user AgenL by runnlng !avaScrlpL from Web Server
S81 medla connecuon uses Web81C Als
ueLalls ln [dra-leu-slpcore-websockeL] LhaL denes Sl LransporL over
WebSockeLs
GH
Web Server
S81 Medla
Sl roxy/8eglsLrar Server
8rowser M
(runnlng !avaScrlpL Sl uA)
8rowser 1
(runnlng !avaScrlpL Sl uA)


P11
(P1ML3/CSS/
!avaScrlpL)
WebSockeL
(Sl)
WebSockeL (Sl)
P11
(P1ML3/CSS/
!avaScrlpL)
1Au SummlL 8angkok 2013 13
Web81C Slgnallng Approaches
Slgnallng ls requlred for exchange of candldaLe LransporL
addresses, codec lnformauon, medla keylng lnformauon
Many opuons - cholce ls up Lo web developer
GI
1Au SummlL 8angkok 2013 14
Pow Lo use Web81C
Web81C usage ln brlef
SeL up eer
Connecuon
CbLaln Local
Medla
Lxchange
Cer/Answer
Auach Medla
or uaLa
CeL more medla
All medla added
eer Connecuon esLabllshed
Auach more medla or daLa
8eady for call
1Au SummlL 8angkok 2013 16
Web81C usage ln brlef
getUserMedia()
Audlo and/or vldeo
ConsLralnLs
user permlsslons
8rowser musL ask before
allowlng a page Lo access
mlcrophone or camera
MediaStream
MediaStreamTrack
Capablllues
SLaLes (semngs)
SeL up eer
Connecuon
CbLaln Local
Medla
Lxchange
Cer/Answer
Auach Medla
or uaLa
CeL more medla
All medla added
eer Connecuon esLabllshed
Auach more medla or daLa
8eady for call
1Au SummlL 8angkok 2013 17
Web81C usage ln brlef
RTCPeerConnection
ulrecL medla
8eLween Lwo peers
lCL processlng
Su processlng
u1Ml supporL
uaLa channels
ldenuLy verlcauon
SLausucs reporung
SeL up eer
Connecuon
CbLaln Local
Medla
Lxchange
Cer/Answer
Auach Medla
or uaLa
CeL more medla
All medla added
eer Connecuon esLabllshed
Auach more medla or daLa
8eady for call
1Au SummlL 8angkok 2013 18
Web81C usage ln brlef
addStream()
uoesn'L change medla sLaLe!
removeStream()
uluo!
createDataChannel()
uepends on LransporL
SeL up eer
Connecuon
CbLaln Local
Medla
Lxchange
Cer/Answer
Auach Medla
or uaLa
CeL more medla
All medla added
eer Connecuon esLabllshed
Auach more medla or daLa
8eady for call
1Au SummlL 8angkok 2013 19
Web81C usage ln brlef
createOffer(),
createAnswer()
setLocalDescription(),
setRemoteDescription()

Applylng Su answer makes
Lhe maglc happen
SeL up eer
Connecuon
CbLaln Local
Medla
Lxchange
Sesslon
uescrlpuons
Auach Medla
or uaLa
CeL more medla
All medla added
eer Connecuon esLabllshed
Auach more medla or daLa
8eady for call
1Au SummlL 8angkok 2013 20
Web81C usage - a blL more deLall
SeL up eer
Connecuon
CbLaln Local
Medla
Lxchange
Sesslon
uescrlpuons
Auach Medla or
uaLa
CeL more medla
Auach more medla or daLa
%&# () %"*+,-"+*
.$,++&-
1Au SummlL 8angkok 2013 21
Su oer/answer
Sesslon uescrlpuons
Sesslon uescrlpuon roLocol creaLed for use by
Sl ln semng up volce (and vldeo) calls
uescrlbes real-ume medla aL low level of deLall
Whlch l addresses and porLs Lo use
Whlch codecs Lo use
Cer/answer model (!SL)
Cne slde sends an Su oer llsung whaL lL wanLs
Lo send and whaL lL can recelve
CLher slde replles wlLh an Su answer llsung whaL
lL wlll recelve and send
1Au SummlL 8angkok 2013 22
Web81C eer-Lo-eer Medla
Medla llows ln Web81C
Web Server


lnLerneL
8ouLer


8rowser L

Pome Wlll
8ouLer

8rowser 1

8rowser M

Coee Shop
Wlll 8ouLer


8rowser u

1Au SummlL 8angkok 2013 24
Medla wlLhouL Web81C
Web Server


lnLerneL
8ouLer


8rowser L

Pome Wlll
8ouLer

8rowser 1

8rowser M

Coee Shop
Wlll 8ouLer


8rowser u

1Au SummlL 8angkok 2013 23
eer-Lo-eer Medla wlLh Web81C
Web Server


lnLerneL
8ouLer


8rowser L

Pome Wlll
8ouLer

8rowser 1

8rowser M

Coee Shop
Wlll 8ouLer


8rowser u

1Au SummlL 8angkok 2013 26
Web Server


lnLerneL
8ouLer wlLh
nA1


8rowser L

Pome Wlll
wlLh nA1

8rowser 1

8rowser M

MosL browsers are behlnd nA1s
on Lhe lnLerneL, whlch
compllcaLes Lhe esLabllshmenL
of peer-Lo-peer medla sesslons.

Coee Shop
Wlll wlLh
nA1


8rowser u

nA1 CompllcaLes eer-Lo-eer Medla
1Au SummlL 8angkok 2013 27
WhaL ls a nA1?
neLwork Address 1ranslaLor (nA1)
used Lo map an lnslde address (usually a
prlvaLe l address) Lo ouLslde address
(usually a publlc l address) aL Layer 3
neLwork Address and orL 1ranslauon
(nA1) also changes Lhe LransporL porL
number (Layer 4)
1hese are oen [usL called nA1s as well
Cne reason for nA1 ls Lhe l address
shorLage
1Au SummlL 8angkok 2013 28
lnLerneL
Pome Wlll
wlLh nA1

8rowser 1
192.168.0.6


8rowser M
192.168.0.3

CuLslde" ubllc l Address
203.0.113.4
lnslde" rlvaLe l Addresses
192.168.x.x
nA1 Lxample
1Au SummlL 8angkok 2013 29
nA1s and Appllcauons
nA1s are compauble wlLh cllenL/server proLocols
such as web, emall, eLc.
Powever, nA1s generally block peer-Lo-peer
communlcauon
1yplcal nA1 Lraversal for vol and vldeo
servlces Loday use a medla relay whenever Lhe
cllenL ls behlnd a nA1
Cen done wlLh an S8C - Sesslon 8order
ConLroller
1hls ls a ma[or expense and compllcauon ln
exlsung vol and vldeo sysLems
Web81C has a bullL-ln nA1 Lraversal sLraLegy:
lnLeracuve ConnecuvlLy LsLabllshmenL (lCL)
1Au SummlL 8angkok 2013 30
Web Server


lnLerneL
8ouLer wlLh
nA1


8rowser L

Pome Wlll
wlLh nA1

8rowser 1

8rowser M

lCL connecuvlLy checks can
oen esLabllsh a dlrecL peer-
Lo-peer sesslon beLween
browsers behlnd dlerenL
nA1s

Coee Shop
Wlll wlLh
nA1


8rowser u

eer-Lo-eer Medla 1hrough nA1
1Au SummlL 8angkok 2013 31
lCL ConnecuvlLy Checks
ConnecuvlLy Lhrough nA1 can be achleved uslng lCL
connecuvlLy checks
8rowsers exchange a llsL of candldaLes
Local: read from neLwork lnLerfaces
8eexlve: obLalned uslng a S1un Server
8elayed: obLalned from a 1u8n Server (medla relay)
8rowsers auempL Lo send S1un packeLs Lo Lhe
candldaLe llsL recelved from oLher browser
Checks performed by boLh sldes aL same ume
lf one S1un packeL geLs Lhrough, a response ls senL
and Lhls connecuon used for communlcauon
1u8n relay wlll be lasL resorL (lowesL prlorlLy)
1Au SummlL 8angkok 2013 32
Web Server


lnLerneL
8ouLer wlLh
nA1


8rowser L

Pome Wlll
wlLh nA1

8rowser 1

8rowser M

lf boLh browsers are
behlnd Lhe same nA1,
connecuvlLy checks can
oen esLabllsh a
connecuon LhaL never
leaves Lhe nA1.

Coee Shop
Wlll wlLh
nA1


8rowser u

2 Medla Can SLay Local Lo nA1
1Au SummlL 8angkok 2013 33
Web Server


lnLerneL
8ouLer wlLh
nA1


8rowser L

Pome Wlll
wlLh nA1
203.0.113.4


8rowser 1

8rowser M
192.168.0.3

Coee Shop
Wlll wlLh
nA1


8rowser u

1u8n Server
198.31.100.2

lCL uses S1un and 1u8n
servers ln Lhe publlc
lnLerneL Lo help wlLh nA1
Lraversal.

S1un Server
198.31.100.9

lCL Servers
1Au SummlL 8angkok 2013 34
Web Server


lnLerneL
8ouLer wlLh
nA1


8rowser L

Pome Wlll
wlLh nA1
203.0.113.4


8rowser 1

8rowser M
192.168.0.3

Coee Shop
Wlll wlLh
nA1


8rowser u

1u8n Server
198.31.100.2

8rowser sends S1un LesL
packeL Lo S1un server Lo
learn lLs publlc l address
(address of Lhe nA1).

S1un Server
198.31.100.9

8rowser Cuerles S1un Server
1Au SummlL 8angkok 2013 33
Web Server


lnLerneL
8ouLer wlLh
nA1


8rowser L

Pome Wlll
wlLh nA1

8rowser 1

8rowser M

Coee Shop
Wlll wlLh
nA1


8rowser u

1u8n Server as a
Medla 8elay

ln some cases, connecuvlLy
checks fall, and a 1u8n
Medla 8elay on Lhe publlc
lnLerneL musL be used.

S1un
Server

1u8n Server Can 8elay Medla
1Au SummlL 8angkok 2013 36
Web81C roLocols and lL1l
SLandards
Web81C: A !olnL SLandards LorL
lnLerneL Lnglneerlng 1ask lorce (lL1l) and World
Wlde Web Consoruum (W3C) are worklng LogeLher on
Web81C
lL1l
roLocols - blLs on wlre"
Maln proLocols are already 8lCs, buL many exLenslons ln
progress
81CWL8 (8eal-1lme Communlcauons on Lhe Web) Worklng
Croup ls Lhe maln focus, buL oLher WCs lnvolved as well
hup://www.leu.org
W3C
Als - used by !avaScrlpL code ln P1ML3
hup://www.w3c.org
1Au SummlL 8angkok 2013 38
Web81C roLocols
l
uu
SC1
WebSockeL S81 Su
1u8n
S1un
lCL
1C
1LS
1ransporL Layer
neLwork Layer
Appllcauon Layer
u1LS
P11
Sl ls noL shown as lL ls opuonal
1Au SummlL 8angkok 2013 39
lL1l 81CWL8 uocumenLs
!"#$%&'( *+(,& -&.
Cvervlew
Cvervlew: 8eal 1lme roLocols for
8rowser-based AppllcaLlons"
drafL-leLf-rLcweb-
overvlew
use Cases and 8equlremenLs
Web 8eal-1lme CommunlcaLlon
use-cases and 8equlremenLs"
drafL-leLf-rLcweb-
use-cases-and-
requlremenLs
81 usage
Web 8eal-1lme CommunlcaLlon
(Web81C): Medla 1ransporL and
use of 81"
drafL-leLf-rLcweb-
rLp-usage
SecurlLy ArchlLecLure 81CWL8 SecurlLy ArchlLecLure"
drafL-leLf-rLcweb-
securlLy-arch
1hreaL Model
SecurlLy ConslderaLlons for 81C-
Web"
drafL-leLf-rLcweb-
securlLy
uaLa Channel 81CWeb uaLa Channels"
drafL-leLf-rLcweb-
daLa-channel
!SL
!avaScrlpL Sesslon LsLabllshmenL
roLocol"
drafL-leLf-rLcweb-
[sep
Audlo
Web81C Audlo Codec and
rocesslng 8equlremenLs"
drafL-leLf-rLcweb-
audlo
CuallLy of Servlce
uSC and oLher packeL marklngs
for 81CWeb CoS"
drafL-leLf-rLcweb-
qos

1Au SummlL 8angkok 2013 40
Codecs
MandaLory Lo lmplemenL (M1l) audlo codecs are
seuled on Cpus and C.711 (nally!)
vldeo ls noL yeL declded!
8lC 6716 .
1Au SummlL 8angkok 2013 41
Web81C W3C Al Cvervlew
1wo prlmary Al secuons
Pandllng local medla
Medla CapLure and SLreams (geLuserMedla)
speclcauon
1ransmlmng medla
Web81C (eer Connecuon) speclcauon
1Au SummlL 8angkok 2013 43
Local Medla Pandllng
ln Lhls example
CapLured 4 local medla sLreams
CreaLed 3 medla sLreams from Lhem
SenL sLreams over eer Connecuon
Sources
resenLer vldeo
uemonsLrauon
vldeo
Audlo
resenLauon
vldeo
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
CapLured
MedlaSLreams
CreaLed
MedlaSLreams
1racks
Audlo" 1rack
resenLauon"
1rack
Audlo" 1rack
resenLer"
1rack
Audlo" 1rack
uemonsLrauon"
1rack
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
8rowser M
1Au SummlL 8angkok 2013 44
Local Medla Pandllng
Sources
Lncoded LogeLher
Can'L manlpulaLe lndlvldually
Sources
resenLer vldeo
uemonsLrauon
vldeo
Audlo
resenLauon
vldeo
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
CapLured
MedlaSLreams
CreaLed
MedlaSLreams
1racks
Audlo" 1rack
resenLauon"
1rack
Audlo" 1rack
resenLer"
1rack
Audlo" 1rack
uemonsLrauon"
1rack
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
8rowser M
1Au SummlL 8angkok 2013 43
Local Medla Pandllng
1racks (MediaStreamTrack)
1led Lo a source
LxlsL prlmarlly as parL of SLreams, slngle medla Lype
Clobally unlque lds, opuonally browser-labeled
Sources
resenLer vldeo
uemonsLrauon
vldeo
Audlo
resenLauon
vldeo
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
CapLured
MedlaSLreams
CreaLed
MedlaSLreams
1racks
Audlo" 1rack
resenLauon"
1rack
Audlo" 1rack
resenLer"
1rack
Audlo" 1rack
uemonsLrauon"
1rack
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
8rowser M
1Au SummlL 8angkok 2013 46
Local Medla Pandllng
CapLured MediaStream
8eLurned from getUserMedia()
ermlsslon check requlred Lo obLaln
Sources
resenLer vldeo
uemonsLrauon
vldeo
Audlo
resenLauon
vldeo
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
CapLured
MedlaSLreams
CreaLed
MedlaSLreams
1racks
Audlo" 1rack
resenLauon"
1rack
Audlo" 1rack
resenLer"
1rack
Audlo" 1rack
uemonsLrauon"
1rack
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
8rowser M
1Au SummlL 8angkok 2013 47
Local Medla Pandllng
MediaStream
All conLalned Lracks are synchronlzed
Can be creaLed, Lransmlued, eLc.
Sources
resenLer vldeo
uemonsLrauon
vldeo
Audlo
resenLauon
vldeo
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
CapLured
MedlaSLreams
CreaLed
MedlaSLreams
1racks
Audlo" 1rack
resenLauon"
1rack
Audlo" 1rack
resenLer"
1rack
Audlo" 1rack
uemonsLrauon"
1rack
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
8rowser M
1Au SummlL 8angkok 2013 48
Local Medla Pandllng
Semngs
CurrenL values of source properues (helghL, wldLh,
eLc.)
Lxposed on MediaStreamTrack
Capablllues
Allowed values for source properues
Lxposed on MediaStreamTrack
ConsLralnLs
8equesLed ranges for Lrack properues
used ln getUserMedia(), applyConstraints()
1Au SummlL 8angkok 2013 49
1ransmlmng medla
Slgnallng channel
non-sLandard
MusL exlsL Lo seL up eer Connecuon
eer Connecuon
Llnks LogeLher Lwo peers
Add/8emove Medla SLreams
addStream(), removeStream()
Pandlers for lCL or medla change
uaLa Channel supporL
1Au SummlL 8angkok 2013 30
eer Connecuon
"Llnks" LogeLher Lwo peers
vla new RTCPeerConnection()
CeneraLes Sesslon uescrlpuon oers/answers
createOffer(), createAnswer()
lrom Su answers, lnluaLes medla
setLocalDescription(), setRemoteDescription()
Cers/answers MuS1 be relayed by appllcauon
code!
lCL candldaLes can also be relayed and added by app
addIceCandidate()
1Au SummlL 8angkok 2013 31
eer Connecuon
Pandlers for slgnallng, lCL or medla change
onsignalingstatechange
onicecandidate,
oniceconnectionstatechange
onaddstream, onremovestream
onnegotiationneeded
A few oLhers
1Au SummlL 8angkok 2013 32
eer Connecuon
LxLra" Als
uaLa
u1Ml
SLausucs
ldenuLy
Crouped separaLely ln Web81C spec
buL really parL of RTCPeerConnection
denluon
all are mandaLory Lo lmplemenL
1Au SummlL 8angkok 2013 33
uaLa Channel Al
RTCDataChannel createDataChannel()
Congurable wlLh
ordered
maxRetransmits, maxRetransmitTime
negotiated
id
rovldes RTCDataChannel wlLh
send()
onopen, onerror, onclose, onmessage*
1Au SummlL 8angkok 2013 34
u1Ml Al
RTCDTMFSender createDTMFSender()
AssoclaLes Lrack lnpuL parameLer wlLh Lhls
RTCPeerConnection
RTCDTMFSender provldes
boolean canInsertDTMF()
insertDTMF()
ontonechange
(oLher sLu)
1Au SummlL 8angkok 2013 33
SLausucs Al
getStats()
Callback reLurns sLausucs for glven Lrack
SLausucs avallable (local/remoLe) are:
8yLes/packeLs xmlued
8yLes/packeLs recelved
May be useful for congesuon-based
ad[usLmenLs
1Au SummlL 8angkok 2013 36
ldenuLy Al
setIdentityProvider(),
getIdentityAssertion()
used Lo verlfy ldenuLy vla Lhlrd parLy, e.g.,
lacebook ConnecL
8oLh meLhods are opuonal
onidentity handler called aer any
verlcauon auempL
RTCPeerConnection.peerIdentity holds
any verled ldenuLy asseruon
1Au SummlL 8angkok 2013 37
seudo Code WalkLhrough
seudo Code
Close Lo real code, buL . . .
no P1ML, no slgnallng channel, noL
asynchronous, and Al ls sull ln ux
uon'L expecL Lhls Lo work anywhere
1Au SummlL 8angkok 2013 39
8ack Lo rsL dlagram
Moblle browser "calls" lapLop browser
Lach sends medla Lo Lhe oLher
SLereo Audlo
8rowser M
on Moblle
8rowser L
on LapLop
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
WebCam vldeo
1Au SummlL 8angkok 2013 60
Moblle browser code ouLllne
We wlll look nexL aL each of Lhese
. . . excepL for creaung Lhe slgnallng
channel
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

var signalingChannel =
createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();
1Au SummlL 8angkok 2013 61
Moblle browser produces . . .
AL leasL 3 calls Lo getUserMedia()
1hree calls Lo new MediaStream()
App sends sLream ids, Lhen sLreams
Sources
resenLer vldeo
uemonsLrauon vldeo
Audlo
resenLauon vldeo
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
CapLured MedlaSLreams CreaLed MedlaSLreams 1racks
Audlo" 1rack
resenLauon" 1rack
Audlo" 1rack
resenLer" 1rack
Audlo" 1rack
uemonsLrauon" 1rack
lronL Camera vldeo
8ear Camera vldeo
Mlcrophone Audlo
Appllcauon Sharlng vldeo
8rowser M
1Au SummlL 8angkok 2013 62
funcuon geLMedla() [1]
CeL audlo
(CeL wlndow vldeo - ouL of scope)
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

. . .
1Au SummlL 8angkok 2013 63
funcuon geLMedla() [2]
CeL fronL-faclng camera
CeL rear-faclng camera
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

. . .
constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
1Au SummlL 8angkok 2013 64
funcuon creaLeC()
CreaLe RTCPeerConnection
SeL handlers
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};
pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
1Au SummlL 8angkok 2013 63
Moblle browser consumes . . .
8ecelves Lhree medla sLreams
Chooses one
Sends Lracks Lo ouLpuL channels
Le Peadphone
ulsplay
8lghL Peadphone
SLereo SLream
Mono SLream
Audlo & vldeo SLream
Slnks MedlaSLreams
8lghL" 1rack
Le" 1rack
Mono" Lrack
vldeo" 1rack
8lghL" 1rack
Le" 1rack
1racks
(Audlo & vldeo SLream selecLed)
8rowser M
1Au SummlL 8angkok 2013 66
luncuon handlelncomlngSLream()
lf lncomlng sLream has vldeo Lrack, seL Lo
av_stream and dlsplay lL
lf lL has Lwo audlo Lracks, musL be sLereo
CLherwlse, musL be Lhe mono sLream
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
1Au SummlL 8angkok 2013 67
luncuon show_av(sL)
1urn sLreams lnLo u8Ls
SeL as source for medla elemenLs
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

display.srcObject =
new MediaStream(st.getVideoTracks()[0]);
left.srcObject =
new MediaStream(st.getAudioTracks()[0]);
right.srcObject =
new MediaStream(st.getAudioTracks()[1]);
1Au SummlL 8angkok 2013 68
Moblle browser code ouLllne
We wlll look nexL aL each of Lhese
. . . excepL for creaung Lhe slgnallng
channel
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

var signalingChannel =
createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();
1Au SummlL 8angkok 2013 69
funcuon auachMedla() [1]
CreaLe 3 new sLreams, all wlLh same
audlo buL dlerenL vldeo
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration
. . .
1Au SummlL 8angkok 2013 70
funcuon auachMedla() [2]
Auach all 3 sLreams Lo eer Connecuon
Send sLream ids Lo peer (before sLreams!)
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));

1Au SummlL 8angkok 2013 71
Moblle browser code ouLllne
We wlll look nexL aL each of Lhese
. . . excepL for creaung Lhe slgnallng
channel
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

var signalingChannel =
createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();
1Au SummlL 8angkok 2013 72
funcuon call()
Ask browser Lo creaLe Su oer
SeL oer as local descrlpuon
Send oer Lo peer
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
1Au SummlL 8angkok 2013 73
Pow do we geL Lhe Su answer?
Slgnallng channel provldes message
lf Su, seL as remoLe descrlpuon
lf lCL candldaLe, Lell Lhe browser
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var microphone, application, front, rear;
var presentation, presenter, demonstration;
var remote_av, stereo, mono;
var display, left, right;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

getMedia();
createPC();
attachMedia();
call();

function getMedia() {
// get local audio (microphone)
navigator.getUserMedia({"audio": true }, function (stream) {
microphone = stream;
}, e);

// get local video (application sharing)
///// This is outside the scope of this specification.
///// Assume that 'application' has been set to this stream.
//

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "front"}}};
navigator.getUserMedia(constraint, function (stream) {
front = stream;
}, e);

constraint =
{"video": {"mandatory": {"videoFacingModeEnum": "rear"}}};
navigator.getUserMedia(constraint, function (stream) {
rear = stream;
}, e);
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function attachMedia() {
presentation =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
application.getVideoTracks()[0]]); // Presentation
presenter =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
front.getVideoTracks()[0]]); // Presenter
demonstration =
new MediaStream(
[microphone.getAudioTracks()[0], // Audio
rear.getVideoTracks()[0]]); // Demonstration

pc.addStream(presentation);
pc.addStream(presenter);
pc.addStream(demonstration);

signalingChannel.send(
JSON.stringify({ "presentation": presentation.id,
"presenter": presenter.id,
"demonstration": demonstration.id
}));
}

function call() {
pc.createOffer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.getVideoTracks().length == 1) {
av_stream = st;
show_av(av_stream);
} else if (st.getAudioTracks().length == 2) {
stereo = st;
} else {
mono = st;
}
}

function show_av(st) {
display.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
left.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
right.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[1]));
}

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};

signalingChannel.onmessage = function (msg) {
var signal = JSON.parse(msg.data);

if (signal.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(signal.sdp), s, e);
} else {
pc.addIceCandidate(
new RTCIceCandidate(signal.candidate));
}
};
1Au SummlL 8angkok 2013 74
And now Lhe lapLop browser . . .
WaLch for Lhe followlng
We seL up medla *aer* recelvlng Lhe oer
buL Lhe slgnallng channel sull musL exlsL rsL!
Also, need Lo save lncomlng sLream lds
1Au SummlL 8angkok 2013 73
Slgnallng channel message ls Lrlgger
SeL up C and medla lf noL already done
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);


. . .



};
1Au SummlL 8angkok 2013 76
Slgnallng channel message ls Lrlgger
lf Su, *also* answer
8uL lf nelLher Su nor lCL candldaLe, musL
be seL of lncomlng sLream lds, so save
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

signalingChannel.onmessage = function (msg) {

. . .
if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};
1Au SummlL 8angkok 2013 77
luncuon preparelorlncomlngCall()
no suprlses here
Medla obLalned ls a llule dlerenL
8uL auached Lhe same way
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

createPC();

getMedia();

attachMedia();
1Au SummlL 8angkok 2013 78
luncuon answer()
createAnswer() auLomaucally uses
value of remoteDescription when
generaung new Su
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
1Au SummlL 8angkok 2013 79
LapLop browser consumes . . .
1hree lnpuL sLreams
All have same # of audlo and vldeo Lracks
need sLream ids Lo dlsungulsh
1racks MedlaSLreams Slnks
ulsplay
ulsplay
Speaker
ulsplay
Audlo" 1rack
resenLauon" 1rack
Audlo" 1rack
resenLer" 1rack
Audlo" 1rack
uemonsLrauon" 1rack
resenLauon SLream
resenLer SLream
uemonsLrauon SLream
(All vldeo sLreams selecLed)
8rowser L
1Au SummlL 8angkok 2013 80
luncuon handlelncomlngSLream()
use ids Lo dlsungulsh sLreams
LxLracL one audlo and all vldeo Lracks
Asslgn Lo elemenL sources
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

if (st.id === incoming.presentation) {
speaker.srcObject =
new MediaStream(st.getAudioTracks()[0]);
win1.srcObject =
new MediaStream(st.getVideoTracks()[0]);
} else if (st.id === incoming.presenter) {
win2.srcObject =
new MediaStream(st.getVideoTracks()[0]);
} else {
win3.srcObject =
new MediaStream(st.getVideoTracks()[0]);
}
1Au SummlL 8angkok 2013 81
8lghL Mlcrophone
WebCam
Le Mlcrophone
SLereo SLream
Mono SLream
Audlo & vldeo SLream
Sources CreaLed MedlaSLreams
8lghL Audlo
vldeo
Le Audlo
CapLured
MedlaSLreams
8lghL" 1rack
Le" 1rack
Mono" 1rack
vldeo" 1rack
8lghL" 1rack
Le" 1rack
1racks
8rowser L
LapLop browser produces . . .
1hree calls Lo getUserMedia()
1hree calls Lo new MediaStream()
no sLream ids needed
1Au SummlL 8angkok 2013 82
luncuon geLMedla() [1]
8equesL webcam vldeo
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

navigator.getUserMedia({"video": true}, function (stream) {
webcam = stream;
}, e);

. . .
1Au SummlL 8angkok 2013 83
luncuon geLMedla() [2]
8equesL le and rlghL audlo sLreams
Save Lhem as left and right varlables
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

. . .

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint,
function (stream) {left = stream;}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint,
function (stream) {right = stream;}, e);
1Au SummlL 8angkok 2013 84
luncuon auachMedla()
CreaLe new sLreams
Add Lhem Lo C for Lransmlsslon
var pc;
var configuration =
{"iceServers":[{"url":"stun:198.51.100.9"},
{"url":"turn:198.51.100.2",
"credential":"myPassword"}]};
var webcam, left, right;
var av, stereo, mono;
var incoming;
var speaker, win1, win2, win3;
function s(sdp) {} // stub success callback

function e(error) {} // stub error callback


var signalingChannel = createSignalingChannel();

function prepareForIncomingCall() {
createPC();

getMedia();

attachMedia();
}

function createPC() {
pc = new RTCPeerConnection(configuration);

pc.onicecandidate = function (evt) {
signalingChannel.send(
JSON.stringify({ "candidate": evt.candidate }));
};

pc.onaddstream =
function (evt) {handleIncomingStream(evt.stream);};
}

function getMedia() {

navigator.getUserMedia({"video": true }, function (stream) {
webcam = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "left"}}};
navigator.getUserMedia(constraint, function (stream) {
left = stream;
}, e);

constraint =
{"audio": {"mandatory": {"audioDirectionEnum": "right"}}};
navigator.getUserMedia(constraint, function (stream) {
right = stream;
}, e);
}

function attachMedia() {
av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
}

function answer() {
pc.createAnswer(gotDescription, e);

function gotDescription(desc) {
pc.setLocalDescription(desc, s, e);

signalingChannel.send(JSON.stringify({ "sdp": desc }));
}
}

function handleIncomingStream(st) {
if (st.id === incoming.presentation) {
speaker.src = URL.createObjectURL(
new MediaStream(st.getAudioTracks()[0]));
win1.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else if (st.id === incoming.presenter) {
win2.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
} else {
win3.src = URL.createObjectURL(
new MediaStream(st.getVideoTracks()[0]));
}
}


signalingChannel.onmessage = function (msg) {
if (!pc) {
prepareForIncomingCall();
}
var sgnl = JSON.parse(msg.data);

if (sgnl.sdp) {
pc.setRemoteDescription(
new RTCSessionDescription(sgnl.sdp), s, e);
answer();
} else if (sgnl.candidate) {
pc.addIceCandidate(new RTCIceCandidate(sgnl.candidate));
} else {
incoming = sgnl;
}
};

av = new MediaStream(
[webcam.getVideoTracks()[0], // Video
left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio
stereo = new MediaStream(
[left.getAudioTracks()[0], // Left audio
right.getAudioTracks()[0]]); // Right audio

mono = left; // Treat the left audio as the mono stream

pc.addStream(av);
pc.addStream(stereo);
pc.addStream(mono);
1Au SummlL 8angkok 2013 83
ln real code . . .
1he error callbacks musL do someLhlng useful
MeLhods wlLh callbacks are asynchronous!
need Lo walL for callbacks before conunulng
Slgnallng channel ls C,$D lmporLanL
1Au SummlL 8angkok 2013 86
racucal blLs
Slgnallng
Audlo and vldeo
uaLa channel
More lnfo onllne
1Au SummlL 8angkok 2013 87
Slgnallng
ueclslon ls mosL challenglng parL of Web81C
Server-slde code provldes
Locaung and ldenufylng peer browser
Message (slgnallng) relaylng
osslbly medla relaylng/Lranscodlng lf a 1u8n
server
Cpuons
WrlLe your own server code
AnyLhlng real-ume" wlll do
use exlsung servlces
ubnub, llre8ase, Coogle App Lnglne
1Au SummlL 8angkok 2013 88
Audlo and vldeo
very recenL agreemenL on how Lo send and
synchronlze muluple ows of same Lype
lor lnLerop, keep lL one of each only
Sull no agreemenL on codecs
Chrome: v8 and P.264
llrefox: v8
1Au SummlL 8angkok 2013 89
uaLa Channel
Mlnlmum safe chunk slze sull under debaLe
A few kb should be okay
roLocol eld ls for app use
1Au SummlL 8angkok 2013 90
CreaL onllne resources
lnformauonal slLes
webrLc.org
hLml3rocks.com/en/LuLorlals/webrLc/baslcs
webrLchacks.com
Cames/demos/apps
www.cubeslam.com
shlnydemos.com/facekaL
sharefesL.me (glLhub.com/eer3/SharefesL)
1Au SummlL 8angkok 2013 91
1he Web81C School ls Lhe web's premler locauon for Web81C
lnLegraLor and ueveloper educauon. AssoclaLed wlLh Lhe
Lralnlng programs are Lhe lndusLry supporLed cerucauons, Lhe
Web81C School Cualled lnLegraLor (WSCl`) and Web81C
School Cualled ueveloper (WSCu`). lor more lnformauon
and onllne demos, please vlslL

hup://www.webrLcschool.com
1ralnlng and Cerucauon
1Au SummlL 8angkok 2013 92
Cuesuons?



hup://webrLcbook.com



1Au SummlL 8angkok 2013 93

Potrebbero piacerti anche