Top Banner
Python, WebRTC and You Saúl Ibarra Corretgé @saghul v2
49

Python, WebRTC and You (v2)

Jul 29, 2015

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Python, WebRTC and You (v2)

Python, WebRTC and YouSaúl Ibarra Corretgé

@saghul

v2

Page 2: Python, WebRTC and You (v2)

github.com/saghul

Page 3: Python, WebRTC and You (v2)

WebRTC, anyone?

Page 4: Python, WebRTC and You (v2)

Have you ever used it?

Page 5: Python, WebRTC and You (v2)

Internals, anyone?

Page 6: Python, WebRTC and You (v2)

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.

Page 7: Python, WebRTC and You (v2)
Page 8: Python, WebRTC and You (v2)
Page 9: Python, WebRTC and You (v2)
Page 10: Python, WebRTC and You (v2)

Well, everyone better Restart Their Chrome

Page 11: Python, WebRTC and You (v2)

You need an adapter

Implementation in browsers is currently inconsistent

Some APIs are still in flux

Page 12: Python, WebRTC and You (v2)

rtcninja.js

https://github.com/eface2face/rtcninja.js

Nice name, right?!

Page 13: Python, WebRTC and You (v2)

Temasys WebRTC Plugin

Free (as in beer) plugin for IE and Safari

http://skylink.io/plugin/

Page 14: Python, WebRTC and You (v2)

WebRTC APIs

getUserMedia

RTCPeerConnection

RTCDataChannel

Page 15: Python, WebRTC and You (v2)

getUserMediaif (!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); });

Page 16: Python, WebRTC and You (v2)
Page 17: Python, WebRTC and You (v2)

RTCPeerConnection

Handles streaming of media between 2 peers

Uses state of the art technology

JSEP

Page 18: Python, WebRTC and You (v2)

RTCPeerConnection (2)Get local media Send SDP offer

Get local media

Send SDP answer

ICE candidates

Audio / Video

Page 19: Python, WebRTC and You (v2)

Interactive Connectivity Establishment

Page 20: Python, WebRTC and You (v2)

ICEHelps find the best path for media

Solves NAT traversal and other hostile network problems

Communication Consent Verification

It can trickle!

Page 21: Python, WebRTC and You (v2)
Page 22: Python, WebRTC and You (v2)

What about the signalling?

It’s not specified!

Use SIP, XMPP or roll your own!

Page 23: Python, WebRTC and You (v2)
Page 24: Python, WebRTC and You (v2)
Page 25: Python, WebRTC and You (v2)

RTCDataChannel

P2P, message boundary based channel for arbitrary data

Implemented using SCTP, different reliability choices possible

Page 26: Python, WebRTC and You (v2)
Page 27: Python, WebRTC and You (v2)

Call Roulette

Page 28: Python, WebRTC and You (v2)

Python

JavaScript

Page 29: Python, WebRTC and You (v2)

The ProtocolWebSocket based, JSON payload

Users enter the roulette when they connect over WebSocket

Session is negotiated / established

No end message, just disconnect the WebSocket

Page 30: Python, WebRTC and You (v2)

Saghul’s Imbecile Protocol (v1)

Page 31: Python, WebRTC and You (v2)

yo (v2)

Page 32: Python, WebRTC and You (v2)

{'yo': 'yo'}

Page 33: Python, WebRTC and You (v2)

{'jsep': {'sdp': '...', 'type': 'offer'}, 'yo': 'yo'}

Page 34: Python, WebRTC and You (v2)

{'jsep': {'sdp': '...', 'type': 'answer'}, 'yo': 'yo'}

Page 35: Python, WebRTC and You (v2)

{'candidate': {'candidate': '...', 'sdpMLineIndex': 1, 'sdpMid': ''}, 'yo': 'yo'}

Page 36: Python, WebRTC and You (v2)
Page 37: 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?

Page 38: Python, WebRTC and You (v2)

asyncio + aiohttp

Page 39: Python, WebRTC and You (v2)

github.com/saghul/CallRoulette

Page 40: Python, WebRTC and You (v2)

@asyncio.coroutinedef 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

Page 41: Python, WebRTC and You (v2)

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)

Page 42: Python, WebRTC and You (v2)

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

Page 43: Python, WebRTC and You (v2)

from jsonmodels import models, fieldsfrom 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)

Page 44: Python, WebRTC and You (v2)

@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

Page 45: Python, WebRTC and You (v2)

# 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()))

Page 46: Python, WebRTC and You (v2)

# 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()))

Page 47: Python, WebRTC and You (v2)

# 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()

Page 48: Python, WebRTC and You (v2)
Page 49: Python, WebRTC and You (v2)

In WebRTC trouble?

bettercallsaghul.com@saghul