Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Python, WebRTC and You (v2)

1.739 Aufrufe

Veröffentlicht am

Slides from the talk I gave at PyGrunn 2015 about using Python (asyncio + aiohttp) as the backend for WebRTC applications.

Veröffentlicht in: Technologie
  • Most customers to online casinos usually head straight to the game that looks or sounds the best. This is a mistake. Look for online slots that offer the highest payout rates. TRY NOW https://t.co/pH7WFFYCKo
    Sind Sie sicher, dass Sie …  Ja  Nein
    Ihre Nachricht erscheint hier

Python, WebRTC and You (v2)

  1. 1. Python, WebRTC and You Saúl Ibarra Corretgé
 @saghul v2
  2. 2. github.com/saghul
  3. 3. WebRTC, anyone?
  4. 4. Have you ever used it?
  5. 5. Internals, anyone?
  6. 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.
  7. 7. Well, everyone better Restart Their Chrome
  8. 8. You need an adapter Implementation in browsers is currently inconsistent Some APIs are still in flux
  9. 9. rtcninja.js https://github.com/eface2face/rtcninja.js Nice name, right?!
  10. 10. Temasys WebRTC Plugin Free (as in beer) plugin for IE and Safari http://skylink.io/plugin/
  11. 11. WebRTC APIs getUserMedia RTCPeerConnection RTCDataChannel
  12. 12. 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); } );
  13. 13. RTCPeerConnection Handles streaming of media between 2 peers Uses state of the art technology JSEP
  14. 14. RTCPeerConnection (2) Get local media Send SDP offer Get local media Send SDP answer ICE candidates Audio / Video
  15. 15. Interactive Connectivity Establishment
  16. 16. ICE Helps find the best path for media Solves NAT traversal and other hostile network problems Communication Consent Verification It can trickle!
  17. 17. What about the signalling? It’s not specified! Use SIP, XMPP or roll your own!
  18. 18. RTCDataChannel P2P, message boundary based channel for arbitrary data Implemented using SCTP, different reliability choices possible
  19. 19. Call Roulette
  20. 20. Python JavaScript
  21. 21. 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
  22. 22. Saghul’s Imbecile Protocol (v1)
  23. 23. yo (v2)
  24. 24. {'yo': 'yo'}
  25. 25. {'jsep': {'sdp': '...', 'type': 'offer'}, 'yo': 'yo'}
  26. 26. {'jsep': {'sdp': '...', 'type': 'answer'}, 'yo': 'yo'}
  27. 27. {'candidate': {'candidate': '...', 'sdpMLineIndex': 1, 'sdpMid': ''}, 'yo': 'yo'}
  28. 28. Shopping for a framework Python >= 3.3, because future! WebSocket support built-in Async, because blocking is so 2001 New, because why not?
  29. 29. asyncio + aiohttp
  30. 30. github.com/saghul/CallRoulette
  31. 31. @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, '', 8080) print("Server started at") return server, handler
  32. 32. 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)
  33. 33. 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
  34. 34. 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)
  35. 35. @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
  36. 36. # 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()))
  37. 37. # 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()))
  38. 38. # 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()
  39. 39. In WebRTC trouble? bettercallsaghul.com @saghul