SlideShare a Scribd company logo
Python, WebRTC and You
Saúl Ibarra Corretgé

@saghul
v2
github.com/saghul
WebRTC, anyone?
Have you ever used it?
Internals, anyone?
What is WebRTC?
WebRTC (Web Real-Time Communication) is an API
definition drafted by the World Wide Web Consortium
(W3C) that supports browser-to-browser applications
for voice calling, video chat, and P2P file sharing
without the need of either internal or external plugins.
Python, WebRTC and You (v2)
Python, WebRTC and You (v2)
Python, WebRTC and You (v2)
Well,
everyone
better
Restart
Their
Chrome
You need an adapter
Implementation in browsers is
currently inconsistent

Some APIs are still in flux
rtcninja.js
https://github.com/eface2face/rtcninja.js

Nice name, right?!
Temasys WebRTC
Plugin
Free (as in beer) plugin for IE and
Safari

http://skylink.io/plugin/
WebRTC APIs
getUserMedia

RTCPeerConnection

RTCDataChannel
getUserMedia
if (!rtcninja.hasWebRTC()) {	
console.log('Are you from the past?!');	
return;	
}	
!
rtcninja.getUserMedia(	
// constraints	
{video: true, audio: true},	
!
// successCallback	
function(localMediaStream) {	
var video = document.querySelector('video');	
rtcninja.attachMediaStream(video, localMediaStream);	
},	
!
// errorCallback	
function(err) {	
console.log("The following error occured: " + err);	
}	
);
Python, WebRTC and You (v2)
RTCPeerConnection
Handles streaming of media between
2 peers

Uses state of the art technology

JSEP
RTCPeerConnection (2)
Get local media Send SDP offer
Get local media
Send SDP answer
ICE candidates
Audio / Video
Interactive Connectivity Establishment
ICE
Helps find the best path for media

Solves NAT traversal and other
hostile network problems

Communication Consent Verification

It can trickle!
Python, WebRTC and You (v2)
What about the
signalling?
It’s not specified!

Use SIP, XMPP or roll your own!
Python, WebRTC and You (v2)
Python, WebRTC and You (v2)
RTCDataChannel
P2P, message boundary based
channel for arbitrary data

Implemented using SCTP, different
reliability choices possible
Python, WebRTC and You (v2)
Call Roulette
Python
JavaScript
The Protocol
WebSocket based, JSON payload

Users enter the roulette when they
connect over WebSocket

Session is negotiated / established

No end message, just disconnect the
WebSocket
Saghul’s Imbecile Protocol
(v1)
yo
(v2)
{'yo': 'yo'}
{'jsep': {'sdp': '...',	
'type': 'offer'},	
'yo': 'yo'}
{'jsep': {'sdp': '...',	
'type': 'answer'},	
'yo': 'yo'}
{'candidate': {'candidate': '...',	
'sdpMLineIndex': 1,	
'sdpMid': ''},	
'yo': 'yo'}
Python, WebRTC and You (v2)
Shopping for a
framework
Python >= 3.3, because future!

WebSocket support built-in

Async, because blocking is so 2001

New, because why not?
asyncio + aiohttp
github.com/saghul/CallRoulette
@asyncio.coroutine	
def init(loop):	
app = web.Application(loop=loop)	
app.router.add_route('GET', '/', LazyFileHandler(INDEX_FILE, 'text/html'))	
app.router.add_route('GET', '/ws', WebSocketHandler())	
app.router.add_route('GET', '/static/{path:.*}', StaticFilesHandler(STATIC_FILES))	
!
handler = app.make_handler()	
server = yield from loop.create_server(handler, '0.0.0.0', 8080)	
print("Server started at http://0.0.0.0:8080")	
return server, handler
class StaticFilesHandler:	
def __init__(self, base_path):	
self.base_path = base_path	
self.cache = {}	
!
@asyncio.coroutine	
def __call__(self, request):	
path = request.match_info['path']	
try:	
data, content_type = self.cache[path]	
except KeyError:	
full_path = os.path.join(self.base_path, path)	
try:	
with open(full_path, 'rb') as f:	
content_type, encoding = mimetypes.guess_type(full_path,	
strict=False)	
data = f.read()	
except IOError:	
log.warning('Could not open %s file' % path)	
raise web.HTTPNotFound()	
self.cache[path] = data, content_type	
log.debug('Loaded file %s (%s)' % (path, content_type))	
return web.Response(body=data, content_type=content_type)
class WebSocketHandler:	
def __init__(self):	
self.waiter = None	
!
@asyncio.coroutine	
def __call__(self, request):	
ws = web.WebSocketResponse(protocols=('callroulette-v2',))	
ws.start(request)	
!
conn = Connection(ws)	
if self.waiter is None:	
self.waiter = asyncio.Future(loop=ws._loop)	
fs = [conn.read(), self.waiter]	
done, pending = yield from asyncio.wait(fs, return_when=asyncio.FIRST_COMPLETED)	
if self.waiter not in done:	
# the connection was most likely closed	
self.waiter = None	
return ws	
other = self.waiter.result()	
self.waiter = None	
reading_task = pending.pop()

reading_task.cancel()	
asyncio.async(self.run_roulette(conn, other))	
else:	
self.waiter.set_result(conn)	
!
yield from conn.wait_closed()	
!
return ws
from jsonmodels import models, fields	
from jsonmodels.errors import ValidationError	
!
!
class StringChoiceField(fields.StringField):	
def __init__(self, choices=None, *args, **kw):	
self.choices = choices or []	
super(StringChoiceField, self).__init__(*args, **kw)	
!
def validate(self, value):	
if value not in self.choices:	
raise ValidationError('invalid choice value')	
super(StringChoiceField, self).validate(value)	
!
class Jsep(models.Base):	
type = StringChoiceField(choices=['offer', 'answer'], required=True)	
sdp = fields.StringField(required=True)	
!
class Candidate(models.Base):	
candidate = fields.StringField(required=True)	
sdpMid = fields.StringField(required=True)	
sdpMLineIndex = fields.IntField(required=True)	
!
class YoPayload(models.Base):	
yo = fields.StringField(required=True)	
jsep = fields.EmbeddedField(Jsep)	
candidate = fields.EmbeddedField(Candidate)
@asyncio.coroutine	
def run_roulette(self, peerA, peerB):	
log.info('Running roulette: %s, %s' % (peerA, peerB))	
!
@asyncio.coroutine	
def close_connections():	
yield from asyncio.wait([peerA.close(), peerB.close()],

return_when=asyncio.ALL_COMPLETED)	
!
def parse(data):	
try:	
data = json.loads(data)	
payload = YoPayload(**data)	
payload.validate()	
except Exception as e:	
log.warning('Error parsing payload: %s' % e)	
return None	
return payload
# request offer	
offer_request = YoPayload(yo='yo')	
peerA.write(json.dumps(offer_request.to_struct()))	
!
# get offer	
data = yield from peerA.read(timeout=READ_TIMEOUT)	
if not data:	
yield from close_connections()	
return	
!
offer = parse(data)	
if offer is None or offer.jsep is None or offer.jsep.type != 'offer':	
log.warning('Invalid offer received')	
yield from close_connections()	
return	
!
# send offer	
peerB.write(json.dumps(offer.to_struct()))
# wait for answer	
data = yield from peerB.read(timeout=READ_TIMEOUT)	
if not data:	
yield from close_connections()	
return	
!
answer = parse(data)	
if answer is None or answer.jsep is None or answer.jsep.type != 'answer':	
log.warning('Invalid answer received')	
yield from close_connections()	
return	
!
# dispatch answer	
peerA.write(json.dumps(answer.to_struct()))
# wait for candidates / end	
while True:	
peer_a_read = asyncio.async(peerA.read())	
peer_a_read.other_peer = peerB	
peer_b_read = asyncio.async(peerB.read())	
peer_b_read.other_peer = peerA	
done, pending = yield from asyncio.wait([peer_a_read, peer_b_read],	
return_when=asyncio.FIRST_COMPLETED)	
for task in pending:	
task.cancel()	
for task in done:	
data = task.result()	
if not data:	
break	
# all we can get at this point is trickled ICE candidates	
candidate = parse(data)	
if candidate is None or candidate.candidate is None:	
log.warning('Invalid candidate received!')	
break	
task.other_peer.write(json.dumps(candidate.to_struct()))	
else:	
continue	
break	
# close connections	
yield from close_connections()
Python, WebRTC and You (v2)
In WebRTC trouble?
bettercallsaghul.com
@saghul

More Related Content

PDF
WebRTC, RED and Janus @ ClueCon21
PPTX
Fontconfigことはじめ
PDF
Open ebs 101
PDF
暗黒美夢王とEmacs
PDF
WebSocket / WebRTCの技術紹介
PDF
Building the Game Server both API and Realtime via c#
PDF
TCP/IP Stack Configuration with Configuration Assistant for IBM z/OS CS
ODP
Continguous Memory Allocator in the Linux Kernel
WebRTC, RED and Janus @ ClueCon21
Fontconfigことはじめ
Open ebs 101
暗黒美夢王とEmacs
WebSocket / WebRTCの技術紹介
Building the Game Server both API and Realtime via c#
TCP/IP Stack Configuration with Configuration Assistant for IBM z/OS CS
Continguous Memory Allocator in the Linux Kernel

What's hot (20)

PDF
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
PDF
Fibre Channel 基礎講座
PDF
Virtual Flink Forward 2020: Autoscaling Flink at Netflix - Timothy Farkas
PPTX
Elixirと他言語の比較的紹介 ver.2
PDF
Introduction httpClient on Java11 / Java11時代のHTTPアクセス再入門
PDF
Making Linux do Hard Real-time
PDF
Clean Architectureで設計してRxJSを使った話
PPTX
Apache Flink: Real-World Use Cases for Streaming Analytics
PPTX
Slurmのジョブスケジューリングと実装
PDF
Improve Android System Component Performance
PDF
Mastering Real-time Linux
PDF
グラフデータベース Neptune 使ってみた
PDF
Zynq mp勉強会資料
PDF
クロスユースプラットフォーム~ 秒間10万リクエスト・レスポンスタイム100ms以下を実現するシステム について ~ / YJTC19 in Shibuy...
PDF
Making Linux do Hard Real-time
PDF
Java Just-In-Timeコンパイラ
PPTX
NABShow報告:マルチCDNと最新ストリーミングプロトコル
PDF
Kafka Connect:Iceberg Sink Connectorを使ってみる
PDF
Hadoopデータ基盤とMulti-CloudなML基盤への取り組みの紹介
PDF
PacemakerのMaster/Slave構成の基本と事例紹介(DRBD、PostgreSQLレプリケーション) @Open Source Confer...
エンジニアなら知っておきたい「仮想マシン」のしくみ v1.1 (hbstudy 17)
Fibre Channel 基礎講座
Virtual Flink Forward 2020: Autoscaling Flink at Netflix - Timothy Farkas
Elixirと他言語の比較的紹介 ver.2
Introduction httpClient on Java11 / Java11時代のHTTPアクセス再入門
Making Linux do Hard Real-time
Clean Architectureで設計してRxJSを使った話
Apache Flink: Real-World Use Cases for Streaming Analytics
Slurmのジョブスケジューリングと実装
Improve Android System Component Performance
Mastering Real-time Linux
グラフデータベース Neptune 使ってみた
Zynq mp勉強会資料
クロスユースプラットフォーム~ 秒間10万リクエスト・レスポンスタイム100ms以下を実現するシステム について ~ / YJTC19 in Shibuy...
Making Linux do Hard Real-time
Java Just-In-Timeコンパイラ
NABShow報告:マルチCDNと最新ストリーミングプロトコル
Kafka Connect:Iceberg Sink Connectorを使ってみる
Hadoopデータ基盤とMulti-CloudなML基盤への取り組みの紹介
PacemakerのMaster/Slave構成の基本と事例紹介(DRBD、PostgreSQLレプリケーション) @Open Source Confer...
Ad

Viewers also liked (20)

PDF
Python, WebRTC and You
PDF
Planning libuv v2
PDF
CDRTool: CDR mediation and rating engine for OpenSIPS
PDF
PDF
The Future of the PBX
PDF
WebRTC enabling your OpenSIPS infrastructure
PDF
libuv, NodeJS and everything in between
PDF
Building an Open Source VoIP Hardware Phone
PDF
From SIP to WebRTC and vice versa
PDF
Proyecto Open Pi Phone
PDF
SylkServer: State of the art RTC application server
PDF
Escalabilidad horizontal desde las trincheras
PDF
A deep dive into libuv
PDF
libuv: cross platform asynchronous i/o
PDF
Videoconferencias: el santo grial de WebRTC
PDF
Jitsi: State of the Union
PDF
PDF
Introduction to asyncio
PDF
Rethinking the PBX
PDF
Developing rich SIP applications with SIPSIMPLE SDK
Python, WebRTC and You
Planning libuv v2
CDRTool: CDR mediation and rating engine for OpenSIPS
The Future of the PBX
WebRTC enabling your OpenSIPS infrastructure
libuv, NodeJS and everything in between
Building an Open Source VoIP Hardware Phone
From SIP to WebRTC and vice versa
Proyecto Open Pi Phone
SylkServer: State of the art RTC application server
Escalabilidad horizontal desde las trincheras
A deep dive into libuv
libuv: cross platform asynchronous i/o
Videoconferencias: el santo grial de WebRTC
Jitsi: State of the Union
Introduction to asyncio
Rethinking the PBX
Developing rich SIP applications with SIPSIMPLE SDK
Ad

Similar to Python, WebRTC and You (v2) (20)

PDF
Python, do you even async?
PDF
A deep dive into PEP-3156 and the new asyncio module
PPT
Real-Time Python Web: Gevent and Socket.io
PDF
Tornado in Depth
ODP
Power ai image-pipeline
PDF
DevCon 5 (July 2013) - WebSockets
PDF
Twisted logic
PDF
Websocket 101 in Python
PDF
WebRTC 101 - How to get started building your first WebRTC application
PDF
Building Web APIs that Scale
PDF
SwampDragon presentation: The Copenhagen Django Meetup Group
PDF
Server side story
PPTX
Enhancing Mobile User Experience with WebSocket
PDF
WebSocket Push Fallback - Transcript.pdf
PPTX
WebSockets-Revolutionizing-Real-Time-Communication.pptx
PDF
Java API for WebSocket 1.0: Java EE 7 and GlassFish
PPTX
Python, async web frameworks, and MongoDB
PDF
Demuxed 2020
PDF
Python & Django TTT
PDF
연구자 및 교육자를 위한 계산 및 분석 플랫폼 설계 - PyCon KR 2015
Python, do you even async?
A deep dive into PEP-3156 and the new asyncio module
Real-Time Python Web: Gevent and Socket.io
Tornado in Depth
Power ai image-pipeline
DevCon 5 (July 2013) - WebSockets
Twisted logic
Websocket 101 in Python
WebRTC 101 - How to get started building your first WebRTC application
Building Web APIs that Scale
SwampDragon presentation: The Copenhagen Django Meetup Group
Server side story
Enhancing Mobile User Experience with WebSocket
WebSocket Push Fallback - Transcript.pdf
WebSockets-Revolutionizing-Real-Time-Communication.pptx
Java API for WebSocket 1.0: Java EE 7 and GlassFish
Python, async web frameworks, and MongoDB
Demuxed 2020
Python & Django TTT
연구자 및 교육자를 위한 계산 및 분석 플랫폼 설계 - PyCon KR 2015

More from Saúl Ibarra Corretgé (16)

PDF
JanusCon 2024: Mom there are robots in my meeting
PDF
Challenges running Jitsi Meet at scale during the pandemic
PDF
The Road to End-to-End Encryption in Jitsi Meet
PDF
Jitsi: State of the Union 2020
PDF
Jitsi Meet: our tale of blood, sweat, tears and love
PDF
Jitsi Meet: Video conferencing for the privacy minded
PDF
Jitsi - Estado de la unión 2019
PDF
Get a room! Spot: the ultimate physical meeting room experience
PDF
Going Mobile with React Native and WebRTC
PDF
Going Mobile with React Native and WebRTC
PDF
Jitsi: Estado de la Unión (2018)
PDF
Jitsi: state-of-the-art video conferencing you can self-host
PDF
WebRTC: El epicentro de la videoconferencia y IoT
PDF
Jitsi: Open Source Video Conferencing
PDF
Extendiendo SIP con WebRTC
PDF
De SIP a WebRTC y vice versa
JanusCon 2024: Mom there are robots in my meeting
Challenges running Jitsi Meet at scale during the pandemic
The Road to End-to-End Encryption in Jitsi Meet
Jitsi: State of the Union 2020
Jitsi Meet: our tale of blood, sweat, tears and love
Jitsi Meet: Video conferencing for the privacy minded
Jitsi - Estado de la unión 2019
Get a room! Spot: the ultimate physical meeting room experience
Going Mobile with React Native and WebRTC
Going Mobile with React Native and WebRTC
Jitsi: Estado de la Unión (2018)
Jitsi: state-of-the-art video conferencing you can self-host
WebRTC: El epicentro de la videoconferencia y IoT
Jitsi: Open Source Video Conferencing
Extendiendo SIP con WebRTC
De SIP a WebRTC y vice versa

Recently uploaded (20)

PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Advanced Soft Computing BINUS July 2025.pdf
PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Electronic commerce courselecture one. Pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
PPTX
Spectroscopy.pptx food analysis technology
PDF
Modernizing your data center with Dell and AMD
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
Empathic Computing: Creating Shared Understanding
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Advanced Soft Computing BINUS July 2025.pdf
NewMind AI Monthly Chronicles - July 2025
Electronic commerce courselecture one. Pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
Spectral efficient network and resource selection model in 5G networks
Diabetes mellitus diagnosis method based random forest with bat algorithm
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
MYSQL Presentation for SQL database connectivity
Understanding_Digital_Forensics_Presentation.pptx
Advanced methodologies resolving dimensionality complications for autism neur...
Dropbox Q2 2025 Financial Results & Investor Presentation
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Network Security Unit 5.pdf for BCA BBA.
solutions_manual_-_materials___processing_in_manufacturing__demargo_.pdf
Spectroscopy.pptx food analysis technology
Modernizing your data center with Dell and AMD
“AI and Expert System Decision Support & Business Intelligence Systems”
Empathic Computing: Creating Shared Understanding

Python, WebRTC and You (v2)

  • 1. Python, WebRTC and You Saúl Ibarra Corretgé
 @saghul v2
  • 4. Have you ever used it?
  • 6. What is WebRTC? WebRTC (Web Real-Time Communication) is an API definition drafted by the World Wide Web Consortium (W3C) that supports browser-to-browser applications for voice calling, video chat, and P2P file sharing without the need of either internal or external plugins.
  • 11. You need an adapter Implementation in browsers is currently inconsistent Some APIs are still in flux
  • 13. Temasys WebRTC Plugin Free (as in beer) plugin for IE and Safari http://skylink.io/plugin/
  • 15. getUserMedia if (!rtcninja.hasWebRTC()) { console.log('Are you from the past?!'); return; } ! rtcninja.getUserMedia( // constraints {video: true, audio: true}, ! // successCallback function(localMediaStream) { var video = document.querySelector('video'); rtcninja.attachMediaStream(video, localMediaStream); }, ! // errorCallback function(err) { console.log("The following error occured: " + err); } );
  • 17. RTCPeerConnection Handles streaming of media between 2 peers Uses state of the art technology JSEP
  • 18. RTCPeerConnection (2) Get local media Send SDP offer Get local media Send SDP answer ICE candidates Audio / Video
  • 20. ICE Helps find the best path for media Solves NAT traversal and other hostile network problems Communication Consent Verification It can trickle!
  • 22. What about the signalling? It’s not specified! Use SIP, XMPP or roll your own!
  • 25. RTCDataChannel P2P, message boundary based channel for arbitrary data Implemented using SCTP, different reliability choices possible
  • 29. The Protocol WebSocket based, JSON payload Users enter the roulette when they connect over WebSocket Session is negotiated / established No end message, just disconnect the WebSocket
  • 33. {'jsep': {'sdp': '...', 'type': 'offer'}, 'yo': 'yo'}
  • 34. {'jsep': {'sdp': '...', 'type': 'answer'}, 'yo': 'yo'}
  • 37. Shopping for a framework Python >= 3.3, because future! WebSocket support built-in Async, because blocking is so 2001 New, because why not?
  • 40. @asyncio.coroutine def init(loop): app = web.Application(loop=loop) app.router.add_route('GET', '/', LazyFileHandler(INDEX_FILE, 'text/html')) app.router.add_route('GET', '/ws', WebSocketHandler()) app.router.add_route('GET', '/static/{path:.*}', StaticFilesHandler(STATIC_FILES)) ! handler = app.make_handler() server = yield from loop.create_server(handler, '0.0.0.0', 8080) print("Server started at http://0.0.0.0:8080") return server, handler
  • 41. class StaticFilesHandler: def __init__(self, base_path): self.base_path = base_path self.cache = {} ! @asyncio.coroutine def __call__(self, request): path = request.match_info['path'] try: data, content_type = self.cache[path] except KeyError: full_path = os.path.join(self.base_path, path) try: with open(full_path, 'rb') as f: content_type, encoding = mimetypes.guess_type(full_path, strict=False) data = f.read() except IOError: log.warning('Could not open %s file' % path) raise web.HTTPNotFound() self.cache[path] = data, content_type log.debug('Loaded file %s (%s)' % (path, content_type)) return web.Response(body=data, content_type=content_type)
  • 42. class WebSocketHandler: def __init__(self): self.waiter = None ! @asyncio.coroutine def __call__(self, request): ws = web.WebSocketResponse(protocols=('callroulette-v2',)) ws.start(request) ! conn = Connection(ws) if self.waiter is None: self.waiter = asyncio.Future(loop=ws._loop) fs = [conn.read(), self.waiter] done, pending = yield from asyncio.wait(fs, return_when=asyncio.FIRST_COMPLETED) if self.waiter not in done: # the connection was most likely closed self.waiter = None return ws other = self.waiter.result() self.waiter = None reading_task = pending.pop()
 reading_task.cancel() asyncio.async(self.run_roulette(conn, other)) else: self.waiter.set_result(conn) ! yield from conn.wait_closed() ! return ws
  • 43. from jsonmodels import models, fields from jsonmodels.errors import ValidationError ! ! class StringChoiceField(fields.StringField): def __init__(self, choices=None, *args, **kw): self.choices = choices or [] super(StringChoiceField, self).__init__(*args, **kw) ! def validate(self, value): if value not in self.choices: raise ValidationError('invalid choice value') super(StringChoiceField, self).validate(value) ! class Jsep(models.Base): type = StringChoiceField(choices=['offer', 'answer'], required=True) sdp = fields.StringField(required=True) ! class Candidate(models.Base): candidate = fields.StringField(required=True) sdpMid = fields.StringField(required=True) sdpMLineIndex = fields.IntField(required=True) ! class YoPayload(models.Base): yo = fields.StringField(required=True) jsep = fields.EmbeddedField(Jsep) candidate = fields.EmbeddedField(Candidate)
  • 44. @asyncio.coroutine def run_roulette(self, peerA, peerB): log.info('Running roulette: %s, %s' % (peerA, peerB)) ! @asyncio.coroutine def close_connections(): yield from asyncio.wait([peerA.close(), peerB.close()],
 return_when=asyncio.ALL_COMPLETED) ! def parse(data): try: data = json.loads(data) payload = YoPayload(**data) payload.validate() except Exception as e: log.warning('Error parsing payload: %s' % e) return None return payload
  • 45. # request offer offer_request = YoPayload(yo='yo') peerA.write(json.dumps(offer_request.to_struct())) ! # get offer data = yield from peerA.read(timeout=READ_TIMEOUT) if not data: yield from close_connections() return ! offer = parse(data) if offer is None or offer.jsep is None or offer.jsep.type != 'offer': log.warning('Invalid offer received') yield from close_connections() return ! # send offer peerB.write(json.dumps(offer.to_struct()))
  • 46. # wait for answer data = yield from peerB.read(timeout=READ_TIMEOUT) if not data: yield from close_connections() return ! answer = parse(data) if answer is None or answer.jsep is None or answer.jsep.type != 'answer': log.warning('Invalid answer received') yield from close_connections() return ! # dispatch answer peerA.write(json.dumps(answer.to_struct()))
  • 47. # wait for candidates / end while True: peer_a_read = asyncio.async(peerA.read()) peer_a_read.other_peer = peerB peer_b_read = asyncio.async(peerB.read()) peer_b_read.other_peer = peerA done, pending = yield from asyncio.wait([peer_a_read, peer_b_read], return_when=asyncio.FIRST_COMPLETED) for task in pending: task.cancel() for task in done: data = task.result() if not data: break # all we can get at this point is trickled ICE candidates candidate = parse(data) if candidate is None or candidate.candidate is None: log.warning('Invalid candidate received!') break task.other_peer.write(json.dumps(candidate.to_struct())) else: continue break # close connections yield from close_connections()