vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 1 | #!/usr/bin/python2.4 |
| 2 | # |
| 3 | # Copyright 2011 Google Inc. All Rights Reserved. |
| 4 | |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 5 | """WebRTC Demo |
| 6 | |
| 7 | This module demonstrates the WebRTC API by implementing a simple video chat app. |
| 8 | """ |
| 9 | |
| 10 | import cgi |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 11 | import logging |
| 12 | import os |
| 13 | import random |
| 14 | import re |
| 15 | import json |
| 16 | import jinja2 |
| 17 | import webapp2 |
| 18 | import threading |
| 19 | from google.appengine.api import channel |
| 20 | from google.appengine.ext import db |
| 21 | |
| 22 | jinja_environment = jinja2.Environment( |
| 23 | loader=jinja2.FileSystemLoader(os.path.dirname(__file__))) |
| 24 | |
| 25 | # Lock for syncing DB operation in concurrent requests handling. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 26 | # TODO(brave): keeping working on improving performance with thread syncing. |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 27 | # One possible method for near future is to reduce the message caching. |
| 28 | LOCK = threading.RLock() |
| 29 | |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 30 | def generate_random(length): |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 31 | word = '' |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 32 | for _ in range(length): |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 33 | word += random.choice('0123456789') |
| 34 | return word |
| 35 | |
| 36 | def sanitize(key): |
| 37 | return re.sub('[^a-zA-Z0-9\-]', '-', key) |
| 38 | |
| 39 | def make_client_id(room, user): |
| 40 | return room.key().id_or_name() + '/' + user |
| 41 | |
vikasmarwaha@webrtc.org | 6e7c203 | 2013-08-05 22:05:20 +0000 | [diff] [blame] | 42 | def get_default_stun_server(user_agent): |
| 43 | default_stun_server = 'stun.l.google.com:19302' |
| 44 | if 'Firefox' in user_agent: |
| 45 | default_stun_server = 'stun.services.mozilla.com' |
| 46 | return default_stun_server |
| 47 | |
wu@webrtc.org | bc189fb | 2013-09-13 20:11:47 +0000 | [diff] [blame] | 48 | def get_preferred_audio_receive_codec(): |
| 49 | return 'opus/48000' |
| 50 | |
| 51 | def get_preferred_audio_send_codec(user_agent): |
| 52 | # Empty string means no preference. |
| 53 | preferred_audio_send_codec = '' |
| 54 | # Prefer to send ISAC on Chrome for Android. |
| 55 | if 'Android' in user_agent and 'Chrome' in user_agent: |
| 56 | preferred_audio_send_codec = 'ISAC/16000' |
| 57 | return preferred_audio_send_codec |
| 58 | |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 59 | def make_pc_config(stun_server, turn_server, ts_pwd): |
| 60 | servers = [] |
| 61 | if turn_server: |
| 62 | turn_config = 'turn:{}'.format(turn_server) |
| 63 | servers.append({'url':turn_config, 'credential':ts_pwd}) |
| 64 | if stun_server: |
| 65 | stun_config = 'stun:{}'.format(stun_server) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 66 | servers.append({'url':stun_config}) |
| 67 | return {'iceServers':servers} |
| 68 | |
| 69 | def create_channel(room, user, duration_minutes): |
| 70 | client_id = make_client_id(room, user) |
| 71 | return channel.create_channel(client_id, duration_minutes) |
| 72 | |
| 73 | def make_loopback_answer(message): |
| 74 | message = message.replace("\"offer\"", "\"answer\"") |
| 75 | message = message.replace("a=ice-options:google-ice\\r\\n", "") |
| 76 | return message |
| 77 | |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 78 | def handle_message(room, user, message): |
| 79 | message_obj = json.loads(message) |
| 80 | other_user = room.get_other_user(user) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 81 | room_key = room.key().id_or_name() |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 82 | if message_obj['type'] == 'bye': |
| 83 | # This would remove the other_user in loopback test too. |
| 84 | # So check its availability before forwarding Bye message. |
| 85 | room.remove_user(user) |
| 86 | logging.info('User ' + user + ' quit from room ' + room_key) |
| 87 | logging.info('Room ' + room_key + ' has state ' + str(room)) |
| 88 | if other_user and room.has_user(other_user): |
| 89 | if message_obj['type'] == 'offer': |
| 90 | # Special case the loopback scenario. |
| 91 | if other_user == user: |
| 92 | message = make_loopback_answer(message) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 93 | on_message(room, other_user, message) |
| 94 | |
| 95 | def get_saved_messages(client_id): |
| 96 | return Message.gql("WHERE client_id = :id", id=client_id) |
| 97 | |
| 98 | def delete_saved_messages(client_id): |
| 99 | messages = get_saved_messages(client_id) |
| 100 | for message in messages: |
| 101 | message.delete() |
| 102 | logging.info('Deleted the saved message for ' + client_id) |
| 103 | |
| 104 | def send_saved_messages(client_id): |
| 105 | messages = get_saved_messages(client_id) |
| 106 | for message in messages: |
| 107 | channel.send_message(client_id, message.msg) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 108 | logging.info('Delivered saved message to ' + client_id) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 109 | message.delete() |
| 110 | |
| 111 | def on_message(room, user, message): |
| 112 | client_id = make_client_id(room, user) |
| 113 | if room.is_connected(user): |
| 114 | channel.send_message(client_id, message) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 115 | logging.info('Delivered message to user ' + user) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 116 | else: |
| 117 | new_message = Message(client_id = client_id, msg = message) |
| 118 | new_message.put() |
| 119 | logging.info('Saved message for user ' + user) |
| 120 | |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 121 | def make_media_track_constraints(constraints_string): |
| 122 | if not constraints_string or constraints_string.lower() == 'true': |
| 123 | track_constraints = True |
| 124 | elif constraints_string.lower() == 'false': |
| 125 | track_constraints = False |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 126 | else: |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 127 | track_constraints = {'mandatory': {}, 'optional': []} |
| 128 | for constraint_string in constraints_string.split(','): |
| 129 | constraint = constraint_string.split('=') |
| 130 | if len(constraint) != 2: |
| 131 | logging.error('Ignoring malformed constraint: ' + constraint_string) |
| 132 | continue |
| 133 | if constraint[0].startswith('goog'): |
| 134 | track_constraints['optional'].append({constraint[0]: constraint[1]}) |
braveyao@webrtc.org | f354e1f | 2013-03-20 00:23:55 +0000 | [diff] [blame] | 135 | else: |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 136 | track_constraints['mandatory'][constraint[0]] = constraint[1] |
braveyao@webrtc.org | f354e1f | 2013-03-20 00:23:55 +0000 | [diff] [blame] | 137 | |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 138 | return track_constraints |
| 139 | |
| 140 | def make_media_stream_constraints(audio, video): |
| 141 | stream_constraints = ( |
| 142 | {'audio': make_media_track_constraints(audio), |
| 143 | 'video': make_media_track_constraints(video)}) |
| 144 | logging.info('Applying media constraints: ' + str(stream_constraints)) |
| 145 | return stream_constraints |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 146 | |
| 147 | def make_pc_constraints(compat): |
| 148 | constraints = { 'optional': [] } |
| 149 | # For interop with FireFox. Enable DTLS in peerConnection ctor. |
| 150 | if compat.lower() == 'true': |
| 151 | constraints['optional'].append({'DtlsSrtpKeyAgreement': True}) |
vikasmarwaha@webrtc.org | cee0dfb | 2013-09-20 21:26:07 +0000 | [diff] [blame] | 152 | # Disable DTLS in peerConnection ctor for loopback call. The value |
| 153 | # of compat is false for loopback mode. |
| 154 | else: |
| 155 | constraints['optional'].append({'DtlsSrtpKeyAgreement': False}) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 156 | return constraints |
| 157 | |
vikasmarwaha@webrtc.org | 59a0667 | 2013-05-16 01:05:19 +0000 | [diff] [blame] | 158 | def make_offer_constraints(): |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 159 | constraints = { 'mandatory': {}, 'optional': [] } |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 160 | return constraints |
| 161 | |
| 162 | def append_url_arguments(request, link): |
| 163 | for argument in request.arguments(): |
| 164 | if argument != 'r': |
| 165 | link += ('&' + cgi.escape(argument, True) + '=' + |
| 166 | cgi.escape(request.get(argument), True)) |
| 167 | return link |
| 168 | |
| 169 | # This database is to store the messages from the sender client when the |
| 170 | # receiver client is not ready to receive the messages. |
| 171 | # Use TextProperty instead of StringProperty for msg because |
| 172 | # the session description can be more than 500 characters. |
| 173 | class Message(db.Model): |
| 174 | client_id = db.StringProperty() |
| 175 | msg = db.TextProperty() |
| 176 | |
| 177 | class Room(db.Model): |
| 178 | """All the data we store for a room""" |
| 179 | user1 = db.StringProperty() |
| 180 | user2 = db.StringProperty() |
| 181 | user1_connected = db.BooleanProperty(default=False) |
| 182 | user2_connected = db.BooleanProperty(default=False) |
| 183 | |
| 184 | def __str__(self): |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 185 | result = '[' |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 186 | if self.user1: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 187 | result += "%s-%r" % (self.user1, self.user1_connected) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 188 | if self.user2: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 189 | result += ", %s-%r" % (self.user2, self.user2_connected) |
| 190 | result += ']' |
| 191 | return result |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 192 | |
| 193 | def get_occupancy(self): |
| 194 | occupancy = 0 |
| 195 | if self.user1: |
| 196 | occupancy += 1 |
| 197 | if self.user2: |
| 198 | occupancy += 1 |
| 199 | return occupancy |
| 200 | |
| 201 | def get_other_user(self, user): |
| 202 | if user == self.user1: |
| 203 | return self.user2 |
| 204 | elif user == self.user2: |
| 205 | return self.user1 |
| 206 | else: |
| 207 | return None |
| 208 | |
| 209 | def has_user(self, user): |
| 210 | return (user and (user == self.user1 or user == self.user2)) |
| 211 | |
| 212 | def add_user(self, user): |
| 213 | if not self.user1: |
| 214 | self.user1 = user |
| 215 | elif not self.user2: |
| 216 | self.user2 = user |
| 217 | else: |
| 218 | raise RuntimeError('room is full') |
| 219 | self.put() |
| 220 | |
| 221 | def remove_user(self, user): |
| 222 | delete_saved_messages(make_client_id(self, user)) |
| 223 | if user == self.user2: |
| 224 | self.user2 = None |
| 225 | self.user2_connected = False |
| 226 | if user == self.user1: |
| 227 | if self.user2: |
| 228 | self.user1 = self.user2 |
| 229 | self.user1_connected = self.user2_connected |
| 230 | self.user2 = None |
| 231 | self.user2_connected = False |
| 232 | else: |
| 233 | self.user1 = None |
| 234 | self.user1_connected = False |
| 235 | if self.get_occupancy() > 0: |
| 236 | self.put() |
| 237 | else: |
| 238 | self.delete() |
| 239 | |
| 240 | def set_connected(self, user): |
| 241 | if user == self.user1: |
| 242 | self.user1_connected = True |
| 243 | if user == self.user2: |
| 244 | self.user2_connected = True |
| 245 | self.put() |
| 246 | |
| 247 | def is_connected(self, user): |
| 248 | if user == self.user1: |
| 249 | return self.user1_connected |
| 250 | if user == self.user2: |
| 251 | return self.user2_connected |
| 252 | |
| 253 | class ConnectPage(webapp2.RequestHandler): |
| 254 | def post(self): |
| 255 | key = self.request.get('from') |
| 256 | room_key, user = key.split('/') |
| 257 | with LOCK: |
| 258 | room = Room.get_by_key_name(room_key) |
| 259 | # Check if room has user in case that disconnect message comes before |
| 260 | # connect message with unknown reason, observed with local AppEngine SDK. |
| 261 | if room and room.has_user(user): |
| 262 | room.set_connected(user) |
| 263 | send_saved_messages(make_client_id(room, user)) |
| 264 | logging.info('User ' + user + ' connected to room ' + room_key) |
| 265 | logging.info('Room ' + room_key + ' has state ' + str(room)) |
| 266 | else: |
| 267 | logging.warning('Unexpected Connect Message to room ' + room_key) |
| 268 | |
| 269 | |
| 270 | class DisconnectPage(webapp2.RequestHandler): |
| 271 | def post(self): |
| 272 | key = self.request.get('from') |
| 273 | room_key, user = key.split('/') |
| 274 | with LOCK: |
| 275 | room = Room.get_by_key_name(room_key) |
| 276 | if room and room.has_user(user): |
| 277 | other_user = room.get_other_user(user) |
| 278 | room.remove_user(user) |
| 279 | logging.info('User ' + user + ' removed from room ' + room_key) |
| 280 | logging.info('Room ' + room_key + ' has state ' + str(room)) |
| 281 | if other_user and other_user != user: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 282 | channel.send_message(make_client_id(room, other_user), |
| 283 | '{"type":"bye"}') |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 284 | logging.info('Sent BYE to ' + other_user) |
| 285 | logging.warning('User ' + user + ' disconnected from room ' + room_key) |
| 286 | |
| 287 | |
| 288 | class MessagePage(webapp2.RequestHandler): |
| 289 | def post(self): |
| 290 | message = self.request.body |
| 291 | room_key = self.request.get('r') |
| 292 | user = self.request.get('u') |
| 293 | with LOCK: |
| 294 | room = Room.get_by_key_name(room_key) |
| 295 | if room: |
| 296 | handle_message(room, user, message) |
| 297 | else: |
| 298 | logging.warning('Unknown room ' + room_key) |
| 299 | |
| 300 | class MainPage(webapp2.RequestHandler): |
| 301 | """The main UI page, renders the 'index.html' template.""" |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 302 | def get(self): |
| 303 | """Renders the main page. When this page is shown, we create a new |
| 304 | channel to push asynchronous updates to the client.""" |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 305 | |
| 306 | # Append strings to this list to have them thrown up in message boxes. This |
| 307 | # will also cause the app to fail. |
| 308 | error_messages = [] |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 309 | # Get the base url without arguments. |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 310 | base_url = self.request.path_url |
vikasmarwaha@webrtc.org | 6e7c203 | 2013-08-05 22:05:20 +0000 | [diff] [blame] | 311 | user_agent = self.request.headers['User-Agent'] |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 312 | room_key = sanitize(self.request.get('r')) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 313 | stun_server = self.request.get('ss') |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 314 | if not stun_server: |
| 315 | stun_server = get_default_stun_server(user_agent) |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 316 | turn_server = self.request.get('ts') |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 317 | |
| 318 | ts_pwd = self.request.get('tp') |
| 319 | |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 320 | # Use "audio" and "video" to set the media stream constraints. Defined here: |
| 321 | # http://dev.w3.org/2011/webrtc/editor/getusermedia.html#idl-def-MediaStreamConstraints |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 322 | # |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 323 | # "true" and "false" are recognized and interpreted as bools, for example: |
| 324 | # "?audio=true&video=false" (Start an audio-only call.) |
| 325 | # "?audio=false" (Start a video-only call.) |
| 326 | # If unspecified, the stream constraint defaults to True. |
| 327 | # |
| 328 | # To specify media track constraints, pass in a comma-separated list of |
| 329 | # key/value pairs, separated by a "=". Examples: |
| 330 | # "?audio=googEchoCancellation=false,googAutoGainControl=true" |
| 331 | # (Disable echo cancellation and enable gain control.) |
| 332 | # |
| 333 | # "?video=minWidth=1280,minHeight=720,googNoiseReduction=true" |
| 334 | # (Set the minimum resolution to 1280x720 and enable noise reduction.) |
| 335 | # |
| 336 | # Keys starting with "goog" will be added to the "optional" key; all others |
| 337 | # will be added to the "mandatory" key. |
| 338 | # |
| 339 | # The audio keys are defined here: |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 340 | # https://code.google.com/p/webrtc/source/browse/trunk/talk/app/webrtc/localaudiosource.cc |
| 341 | # |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 342 | # The video keys are defined here: |
| 343 | # https://code.google.com/p/webrtc/source/browse/trunk/talk/app/webrtc/videosource.cc |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 344 | audio = self.request.get('audio') |
| 345 | video = self.request.get('video') |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 346 | |
| 347 | if self.request.get('hd').lower() == 'true': |
| 348 | if video: |
| 349 | message = 'The "hd" parameter has overridden video=' + str(video) |
| 350 | logging.error(message) |
| 351 | error_messages.append(message) |
| 352 | video = 'minWidth=1280,minHeight=720' |
| 353 | |
| 354 | if self.request.get('minre') or self.request.get('maxre'): |
| 355 | message = ('The "minre" and "maxre" parameters are no longer supported. ' |
| 356 | 'Use "video" instead.') |
| 357 | logging.error(message) |
| 358 | error_messages.append(message) |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 359 | |
wu@webrtc.org | bc189fb | 2013-09-13 20:11:47 +0000 | [diff] [blame] | 360 | audio_send_codec = self.request.get('asc') |
| 361 | if not audio_send_codec: |
| 362 | audio_send_codec = get_preferred_audio_send_codec(user_agent) |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 363 | |
wu@webrtc.org | bc189fb | 2013-09-13 20:11:47 +0000 | [diff] [blame] | 364 | audio_receive_codec = self.request.get('arc') |
| 365 | if not audio_receive_codec: |
| 366 | audio_receive_codec = get_preferred_audio_receive_codec() |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 367 | |
| 368 | # Set stereo to false by default. |
vikasmarwaha@webrtc.org | 1993a55 | 2013-05-13 18:48:09 +0000 | [diff] [blame] | 369 | stereo = 'false' |
| 370 | if self.request.get('stereo'): |
| 371 | stereo = self.request.get('stereo') |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 372 | |
| 373 | # Set compat to true by default. |
| 374 | compat = 'true' |
| 375 | if self.request.get('compat'): |
| 376 | compat = self.request.get('compat') |
| 377 | |
| 378 | debug = self.request.get('debug') |
| 379 | if debug == 'loopback': |
| 380 | # Set compat to false as DTLS does not work for loopback. |
| 381 | compat = 'false' |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 382 | |
| 383 | # token_timeout for channel creation, default 30min, max 2 days, min 3min. |
| 384 | token_timeout = self.request.get_range('tt', |
| 385 | min_value = 3, |
| 386 | max_value = 3000, |
| 387 | default = 30) |
| 388 | |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 389 | unittest = self.request.get('unittest') |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 390 | if unittest: |
| 391 | # Always create a new room for the unit tests. |
| 392 | room_key = generate_random(8) |
| 393 | |
| 394 | if not room_key: |
| 395 | room_key = generate_random(8) |
| 396 | redirect = '/?r=' + room_key |
| 397 | redirect = append_url_arguments(self.request, redirect) |
| 398 | self.redirect(redirect) |
| 399 | logging.info('Redirecting visitor to base URL to ' + redirect) |
| 400 | return |
| 401 | |
| 402 | user = None |
| 403 | initiator = 0 |
| 404 | with LOCK: |
| 405 | room = Room.get_by_key_name(room_key) |
| 406 | if not room and debug != "full": |
| 407 | # New room. |
| 408 | user = generate_random(8) |
| 409 | room = Room(key_name = room_key) |
| 410 | room.add_user(user) |
| 411 | if debug != 'loopback': |
| 412 | initiator = 0 |
| 413 | else: |
| 414 | room.add_user(user) |
| 415 | initiator = 1 |
| 416 | elif room and room.get_occupancy() == 1 and debug != 'full': |
| 417 | # 1 occupant. |
| 418 | user = generate_random(8) |
| 419 | room.add_user(user) |
| 420 | initiator = 1 |
| 421 | else: |
| 422 | # 2 occupants (full). |
| 423 | template = jinja_environment.get_template('full.html') |
| 424 | self.response.out.write(template.render({ 'room_key': room_key })) |
| 425 | logging.info('Room ' + room_key + ' is full') |
| 426 | return |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 427 | |
braveyao@webrtc.org | f354e1f | 2013-03-20 00:23:55 +0000 | [diff] [blame] | 428 | room_link = base_url + '?r=' + room_key |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 429 | room_link = append_url_arguments(self.request, room_link) |
andrew@webrtc.org | bab2aa5 | 2013-10-03 22:37:29 +0000 | [diff] [blame] | 430 | turn_url = 'https://computeengineondemand.appspot.com/' |
vikasmarwaha@webrtc.org | 222e994 | 2013-04-06 05:58:15 +0000 | [diff] [blame] | 431 | turn_url = turn_url + 'turn?' + 'username=' + user + '&key=4080218913' |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 432 | token = create_channel(room, user, token_timeout) |
| 433 | pc_config = make_pc_config(stun_server, turn_server, ts_pwd) |
| 434 | pc_constraints = make_pc_constraints(compat) |
vikasmarwaha@webrtc.org | 59a0667 | 2013-05-16 01:05:19 +0000 | [diff] [blame] | 435 | offer_constraints = make_offer_constraints() |
andrew@webrtc.org | 20078e2 | 2013-10-05 02:26:50 +0000 | [diff] [blame] | 436 | media_constraints = make_media_stream_constraints(audio, video) |
| 437 | template_values = {'error_messages': error_messages, |
| 438 | 'token': token, |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 439 | 'me': user, |
| 440 | 'room_key': room_key, |
| 441 | 'room_link': room_link, |
| 442 | 'initiator': initiator, |
| 443 | 'pc_config': json.dumps(pc_config), |
| 444 | 'pc_constraints': json.dumps(pc_constraints), |
| 445 | 'offer_constraints': json.dumps(offer_constraints), |
vikasmarwaha@webrtc.org | 222e994 | 2013-04-06 05:58:15 +0000 | [diff] [blame] | 446 | 'media_constraints': json.dumps(media_constraints), |
vikasmarwaha@webrtc.org | 1993a55 | 2013-05-13 18:48:09 +0000 | [diff] [blame] | 447 | 'turn_url': turn_url, |
wu@webrtc.org | bc189fb | 2013-09-13 20:11:47 +0000 | [diff] [blame] | 448 | 'stereo': stereo, |
| 449 | 'audio_send_codec': audio_send_codec, |
| 450 | 'audio_receive_codec': audio_receive_codec |
vikasmarwaha@webrtc.org | 98fce15 | 2013-02-27 23:22:10 +0000 | [diff] [blame] | 451 | } |
| 452 | if unittest: |
| 453 | target_page = 'test/test_' + unittest + '.html' |
| 454 | else: |
| 455 | target_page = 'index.html' |
| 456 | |
| 457 | template = jinja_environment.get_template(target_page) |
| 458 | self.response.out.write(template.render(template_values)) |
| 459 | logging.info('User ' + user + ' added to room ' + room_key) |
| 460 | logging.info('Room ' + room_key + ' has state ' + str(room)) |
| 461 | |
| 462 | |
| 463 | app = webapp2.WSGIApplication([ |
| 464 | ('/', MainPage), |
| 465 | ('/message', MessagePage), |
| 466 | ('/_ah/channel/connected/', ConnectPage), |
| 467 | ('/_ah/channel/disconnected/', DisconnectPage) |
| 468 | ], debug=True) |