Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Python, WebRTC and You
Saúl Ibarra Corretgé

@saghul
print(“hello!”)
@saghul

FOSDEM

Open Source
github.com/saghul
Listen at your own risk
Do you know about
WebRTC?
Have you ever used it?
What is WebRTC?
WebRTC (Web Real-Time Communication) is an API
definition drafted by the World Wide Web Consortium
(W3C) th...
You need an adaptor
Implementation in browsers is
currently inconsistent

Some APIs are still in flux

I’ll be using rtcnin...
WebRTC APIs
getUserMedia

RTCPeerConnection

RTCDataChannel
getUserMedia
if (!rtcninja.hasWebRTC()) {	
console.log('Are you from the past?!');	
return;	
}	
!
rtcninja.getUserMedia(	
...
RTCPeerConnection
Handles streaming of media between
2 peers

Uses state of the art technology

ICE for NAT traversal
RTCPeerConnection (2)
Get local media
Send SDP offer Get local media
Send SDP answer
Audio / Video
RTCDataChannel
P2P, message boundary based
channel for arbitrary data

Implemented using SCTP, different
reliability choice...
What about the
signalling?
It’s not specified!

Use SIP, XMPP, or your own!
Call Roulette
Saghul’s Imbecile Protocol
The Protocol
Users enter the roulette when they
connect over WebSocket

3 types of messages: offer_request,
offer and answer...
Shopping for a
framework
Python >= 3.3, because future!

WebSocket support built-in

Async, because blocking is so 2001

N...
asyncio + aiohttp
@asyncio.coroutine	
def init(loop):	
app = web.Application(loop=loop)	
app.router.add_route('GET', '/', LazyFileHandler(IN...
class StaticFilesHandler:	
def __init__(self, base_path):	
self.base_path = base_path	
self.cache = {}	
!
@asyncio.corouti...
class WebSocketHandler:	
def __init__(self):	
self.waiter = None	
!
@asyncio.coroutine	
def __call__(self, request):	
ws =...
@asyncio.coroutine	
def run_roulette(self, peerA, peerB, initial_reading_task):	
log.info('Running roulette: %s, %s' % (pe...
# send offer	
data = dict(type='offer', sdp=data['sdp']);	
peerB.write(json.dumps(data))	
!
# wait for answer	
data = yiel...
Questions?
bettercallsaghul.com
@saghul
Python, WebRTC and You
Python, WebRTC and You
Python, WebRTC and You
Python, WebRTC and You
Python, WebRTC and You
Python, WebRTC and You
Python, WebRTC and You
Python, WebRTC and You
Upcoming SlideShare
Loading in …5
×

Python, WebRTC and You

10,576 views

Published on

Slides from the talk given at FOSDEM 2015 about WebRTC and using Python as the backend.

Published in: Technology

Python, WebRTC and You

  1. 1. Python, WebRTC and You Saúl Ibarra Corretgé
 @saghul
  2. 2. print(“hello!”) @saghul FOSDEM Open Source
  3. 3. github.com/saghul
  4. 4. Listen at your own risk
  5. 5. Do you know about WebRTC?
  6. 6. Have you ever used it?
  7. 7. 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.
  8. 8. You need an adaptor Implementation in browsers is currently inconsistent Some APIs are still in flux I’ll be using rtcninja https://github.com/eface2face/rtcninja.js
  9. 9. WebRTC APIs getUserMedia RTCPeerConnection RTCDataChannel
  10. 10. 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); } );
  11. 11. RTCPeerConnection Handles streaming of media between 2 peers Uses state of the art technology ICE for NAT traversal
  12. 12. RTCPeerConnection (2) Get local media Send SDP offer Get local media Send SDP answer Audio / Video
  13. 13. RTCDataChannel P2P, message boundary based channel for arbitrary data Implemented using SCTP, different reliability choices possible This is the game-changer Did I mention it’s P2P?
  14. 14. What about the signalling? It’s not specified! Use SIP, XMPP, or your own!
  15. 15. Call Roulette
  16. 16. Saghul’s Imbecile Protocol
  17. 17. The Protocol Users enter the roulette when they connect over WebSocket 3 types of messages: offer_request, offer and answer No end message, just disconnect the WebSocket
  18. 18. Shopping for a framework Python >= 3.3, because future! WebSocket support built-in Async, because blocking is so 2001 New, because hype!
  19. 19. asyncio + aiohttp
  20. 20. @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
  21. 21. 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)
  22. 22. class WebSocketHandler: def __init__(self): self.waiter = None ! @asyncio.coroutine def __call__(self, request): ws = web.WebSocketResponse(protocols=('callroulette',)) ws.start(request) ! conn = Connection(ws) if self.waiter is None: self.waiter = asyncio.Future() 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() asyncio.async(self.run_roulette(conn, other, reading_task)) else: self.waiter.set_result(conn) ! yield from conn.wait_closed() ! return ws
  23. 23. @asyncio.coroutine def run_roulette(self, peerA, peerB, initial_reading_task): log.info('Running roulette: %s, %s' % (peerA, peerB)) ! def _close_connections(): peerA.close() peerB.close() ! # request offer data = dict(type='offer_request'); peerA.write(json.dumps(data)) ! # get offer # I cannot seem to cancel the reading task that was started before, which is the # only way one can know if the connection was closed, so use if for the initial # reading try: data = yield from asyncio.wait_for(initial_reading_task, READ_TIMEOUT) except asyncio.TimeoutError: data = '' if not data: return _close_connections() ! data = json.loads(data) if data.get('type') != 'offer' or not data.get('sdp'): log.warning('Invalid offer received') return _close_connections()
  24. 24. # send offer data = dict(type='offer', sdp=data['sdp']); peerB.write(json.dumps(data)) ! # wait for answer data = yield from peerB.read(timeout=READ_TIMEOUT) if not data: return _close_connections() ! data = json.loads(data) if data.get('type') != 'answer' or not data.get('sdp'): log.warning('Invalid answer received') return _close_connections() ! # dispatch answer data = dict(type='answer', sdp=data['sdp']); peerA.write(json.dumps(data)) ! # wait for end fs = [peerA.read(), peerB.read()] yield from asyncio.wait(fs, return_when=asyncio.FIRST_COMPLETED) ! # close connections return _close_connections()
  25. 25. Questions? bettercallsaghul.com @saghul

×