diff --git a/samples/js/OWNERS b/samples/js/OWNERS
deleted file mode 100644
index 79b28349a..000000000
--- a/samples/js/OWNERS
+++ /dev/null
@@ -1,9 +0,0 @@
-braveyao@webrtc.org
-dutton@google.com
-henrika@webrtc.org
-hta@webrtc.org
-juberti@webrtc.org
-kjellander@webrtc.org
-phoglund@webrtc.org
-vikasmarwaha@webrtc.org
-wu@webrtc.org
diff --git a/samples/js/apprtc/app.yaml b/samples/js/apprtc/app.yaml
deleted file mode 100644
index 6ef5e7505..000000000
--- a/samples/js/apprtc/app.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-application: apprtc
-version: 6
-runtime: python27
-threadsafe: true
-api_version: 1
-
-handlers:
-- url: /html
- static_dir: html
-
-- url: /images
- static_dir: images
-
-- url: /js
- static_dir: js
-
-- url: /css
- static_dir: css
-
-- url: /.*
- script: apprtc.app
- secure: always
-
-inbound_services:
-- channel_presence
-
-libraries:
-- name: jinja2
- version: latest
diff --git a/samples/js/apprtc/apprtc.py b/samples/js/apprtc/apprtc.py
deleted file mode 100644
index 3652c8efd..000000000
--- a/samples/js/apprtc/apprtc.py
+++ /dev/null
@@ -1,482 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2011 Google Inc. All Rights Reserved.
-
-"""WebRTC Demo
-
-This module demonstrates the WebRTC API by implementing a simple video chat app.
-"""
-
-import cgi
-import logging
-import os
-import random
-import re
-import json
-import jinja2
-import webapp2
-import threading
-from google.appengine.api import channel
-from google.appengine.ext import db
-
-jinja_environment = jinja2.Environment(
- loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
-
-# Lock for syncing DB operation in concurrent requests handling.
-# TODO(brave): keeping working on improving performance with thread syncing.
-# One possible method for near future is to reduce the message caching.
-LOCK = threading.RLock()
-
-def generate_random(length):
- word = ''
- for _ in range(length):
- word += random.choice('0123456789')
- return word
-
-def sanitize(key):
- return re.sub('[^a-zA-Z0-9\-]', '-', key)
-
-def make_client_id(room, user):
- return room.key().id_or_name() + '/' + user
-
-def get_default_stun_server(user_agent):
- default_stun_server = 'stun.l.google.com:19302'
- if 'Firefox' in user_agent:
- default_stun_server = 'stun.services.mozilla.com'
- return default_stun_server
-
-def get_preferred_audio_receive_codec():
- return 'opus/48000'
-
-def get_preferred_audio_send_codec(user_agent):
- # Empty string means no preference.
- preferred_audio_send_codec = ''
- # Prefer to send ISAC on Chrome for Android.
- if 'Android' in user_agent and 'Chrome' in user_agent:
- preferred_audio_send_codec = 'ISAC/16000'
- return preferred_audio_send_codec
-
-def make_pc_config(stun_server, turn_server, ts_pwd):
- servers = []
- if turn_server:
- turn_config = 'turn:{}'.format(turn_server)
- servers.append({'urls':turn_config, 'credential':ts_pwd})
- if stun_server:
- stun_config = 'stun:{}'.format(stun_server)
- servers.append({'urls':stun_config})
- return {'iceServers':servers}
-
-def create_channel(room, user, duration_minutes):
- client_id = make_client_id(room, user)
- return channel.create_channel(client_id, duration_minutes)
-
-def make_loopback_answer(message):
- message = message.replace("\"offer\"", "\"answer\"")
- message = message.replace("a=ice-options:google-ice\\r\\n", "")
- return message
-
-def handle_message(room, user, message):
- message_obj = json.loads(message)
- other_user = room.get_other_user(user)
- room_key = room.key().id_or_name()
- if message_obj['type'] == 'bye':
- # This would remove the other_user in loopback test too.
- # So check its availability before forwarding Bye message.
- room.remove_user(user)
- logging.info('User ' + user + ' quit from room ' + room_key)
- logging.info('Room ' + room_key + ' has state ' + str(room))
- if other_user and room.has_user(other_user):
- if message_obj['type'] == 'offer':
- # Special case the loopback scenario.
- if other_user == user:
- message = make_loopback_answer(message)
- on_message(room, other_user, message)
- else:
- # For unittest
- on_message(room, user, message)
-
-def get_saved_messages(client_id):
- return Message.gql("WHERE client_id = :id", id=client_id)
-
-def delete_saved_messages(client_id):
- messages = get_saved_messages(client_id)
- for message in messages:
- message.delete()
- logging.info('Deleted the saved message for ' + client_id)
-
-def send_saved_messages(client_id):
- messages = get_saved_messages(client_id)
- for message in messages:
- channel.send_message(client_id, message.msg)
- logging.info('Delivered saved message to ' + client_id)
- message.delete()
-
-def on_message(room, user, message):
- client_id = make_client_id(room, user)
- if room.is_connected(user):
- channel.send_message(client_id, message)
- logging.info('Delivered message to user ' + user)
- else:
- new_message = Message(client_id = client_id, msg = message)
- new_message.put()
- logging.info('Saved message for user ' + user)
-
-def make_media_track_constraints(constraints_string):
- if not constraints_string or constraints_string.lower() == 'true':
- track_constraints = True
- elif constraints_string.lower() == 'false':
- track_constraints = False
- else:
- track_constraints = {'mandatory': {}, 'optional': []}
- for constraint_string in constraints_string.split(','):
- constraint = constraint_string.split('=')
- if len(constraint) != 2:
- logging.error('Ignoring malformed constraint: ' + constraint_string)
- continue
- if constraint[0].startswith('goog'):
- track_constraints['optional'].append({constraint[0]: constraint[1]})
- else:
- track_constraints['mandatory'][constraint[0]] = constraint[1]
-
- return track_constraints
-
-def make_media_stream_constraints(audio, video):
- stream_constraints = (
- {'audio': make_media_track_constraints(audio),
- 'video': make_media_track_constraints(video)})
- logging.info('Applying media constraints: ' + str(stream_constraints))
- return stream_constraints
-
-def maybe_add_constraint(constraints, param, constraint):
- if (param.lower() == 'true'):
- constraints['optional'].append({constraint: True})
- elif (param.lower() == 'false'):
- constraints['optional'].append({constraint: False})
-
- return constraints
-
-def make_pc_constraints(dtls, dscp, ipv6):
- constraints = { 'optional': [] }
- maybe_add_constraint(constraints, dtls, 'DtlsSrtpKeyAgreement')
- maybe_add_constraint(constraints, dscp, 'googDscp')
- maybe_add_constraint(constraints, ipv6, 'googIPv6')
-
- return constraints
-
-def make_offer_constraints():
- constraints = { 'mandatory': {}, 'optional': [] }
- return constraints
-
-def append_url_arguments(request, link):
- for argument in request.arguments():
- if argument != 'r':
- link += ('&' + cgi.escape(argument, True) + '=' +
- cgi.escape(request.get(argument), True))
- return link
-
-# This database is to store the messages from the sender client when the
-# receiver client is not ready to receive the messages.
-# Use TextProperty instead of StringProperty for msg because
-# the session description can be more than 500 characters.
-class Message(db.Model):
- client_id = db.StringProperty()
- msg = db.TextProperty()
-
-class Room(db.Model):
- """All the data we store for a room"""
- user1 = db.StringProperty()
- user2 = db.StringProperty()
- user1_connected = db.BooleanProperty(default=False)
- user2_connected = db.BooleanProperty(default=False)
-
- def __str__(self):
- result = '['
- if self.user1:
- result += "%s-%r" % (self.user1, self.user1_connected)
- if self.user2:
- result += ", %s-%r" % (self.user2, self.user2_connected)
- result += ']'
- return result
-
- def get_occupancy(self):
- occupancy = 0
- if self.user1:
- occupancy += 1
- if self.user2:
- occupancy += 1
- return occupancy
-
- def get_other_user(self, user):
- if user == self.user1:
- return self.user2
- elif user == self.user2:
- return self.user1
- else:
- return None
-
- def has_user(self, user):
- return (user and (user == self.user1 or user == self.user2))
-
- def add_user(self, user):
- if not self.user1:
- self.user1 = user
- elif not self.user2:
- self.user2 = user
- else:
- raise RuntimeError('room is full')
- self.put()
-
- def remove_user(self, user):
- delete_saved_messages(make_client_id(self, user))
- if user == self.user2:
- self.user2 = None
- self.user2_connected = False
- if user == self.user1:
- if self.user2:
- self.user1 = self.user2
- self.user1_connected = self.user2_connected
- self.user2 = None
- self.user2_connected = False
- else:
- self.user1 = None
- self.user1_connected = False
- if self.get_occupancy() > 0:
- self.put()
- else:
- self.delete()
-
- def set_connected(self, user):
- if user == self.user1:
- self.user1_connected = True
- if user == self.user2:
- self.user2_connected = True
- self.put()
-
- def is_connected(self, user):
- if user == self.user1:
- return self.user1_connected
- if user == self.user2:
- return self.user2_connected
-
-@db.transactional
-def connect_user_to_room(room_key, user):
- room = Room.get_by_key_name(room_key)
- # Check if room has user in case that disconnect message comes before
- # connect message with unknown reason, observed with local AppEngine SDK.
- if room and room.has_user(user):
- room.set_connected(user)
- logging.info('User ' + user + ' connected to room ' + room_key)
- logging.info('Room ' + room_key + ' has state ' + str(room))
- else:
- logging.warning('Unexpected Connect Message to room ' + room_key)
- return room
-
-class ConnectPage(webapp2.RequestHandler):
- def post(self):
- key = self.request.get('from')
- room_key, user = key.split('/')
- with LOCK:
- room = connect_user_to_room(room_key, user)
- if room and room.has_user(user):
- send_saved_messages(make_client_id(room, user))
-
-class DisconnectPage(webapp2.RequestHandler):
- def post(self):
- key = self.request.get('from')
- room_key, user = key.split('/')
- with LOCK:
- room = Room.get_by_key_name(room_key)
- if room and room.has_user(user):
- other_user = room.get_other_user(user)
- room.remove_user(user)
- logging.info('User ' + user + ' removed from room ' + room_key)
- logging.info('Room ' + room_key + ' has state ' + str(room))
- if other_user and other_user != user:
- channel.send_message(make_client_id(room, other_user),
- '{"type":"bye"}')
- logging.info('Sent BYE to ' + other_user)
- logging.warning('User ' + user + ' disconnected from room ' + room_key)
-
-
-class MessagePage(webapp2.RequestHandler):
- def post(self):
- message = self.request.body
- room_key = self.request.get('r')
- user = self.request.get('u')
- with LOCK:
- room = Room.get_by_key_name(room_key)
- if room:
- handle_message(room, user, message)
- else:
- logging.warning('Unknown room ' + room_key)
-
-class MainPage(webapp2.RequestHandler):
- """The main UI page, renders the 'index.html' template."""
- def get(self):
- """Renders the main page. When this page is shown, we create a new
- channel to push asynchronous updates to the client."""
-
- # Append strings to this list to have them thrown up in message boxes. This
- # will also cause the app to fail.
- error_messages = []
- # Get the base url without arguments.
- base_url = self.request.path_url
- user_agent = self.request.headers['User-Agent']
- room_key = sanitize(self.request.get('r'))
- stun_server = self.request.get('ss')
- if not stun_server:
- stun_server = get_default_stun_server(user_agent)
- turn_server = self.request.get('ts')
- ts_pwd = self.request.get('tp')
-
- # Use "audio" and "video" to set the media stream constraints. Defined here:
- # http://goo.gl/V7cZg
- #
- # "true" and "false" are recognized and interpreted as bools, for example:
- # "?audio=true&video=false" (Start an audio-only call.)
- # "?audio=false" (Start a video-only call.)
- # If unspecified, the stream constraint defaults to True.
- #
- # To specify media track constraints, pass in a comma-separated list of
- # key/value pairs, separated by a "=". Examples:
- # "?audio=googEchoCancellation=false,googAutoGainControl=true"
- # (Disable echo cancellation and enable gain control.)
- #
- # "?video=minWidth=1280,minHeight=720,googNoiseReduction=true"
- # (Set the minimum resolution to 1280x720 and enable noise reduction.)
- #
- # Keys starting with "goog" will be added to the "optional" key; all others
- # will be added to the "mandatory" key.
- #
- # The audio keys are defined here: talk/app/webrtc/localaudiosource.cc
- # The video keys are defined here: talk/app/webrtc/videosource.cc
- audio = self.request.get('audio')
- video = self.request.get('video')
-
- if self.request.get('hd').lower() == 'true':
- if video:
- message = 'The "hd" parameter has overridden video=' + str(video)
- logging.error(message)
- error_messages.append(message)
- video = 'minWidth=1280,minHeight=720'
-
- if self.request.get('minre') or self.request.get('maxre'):
- message = ('The "minre" and "maxre" parameters are no longer supported. '
- 'Use "video" instead.')
- logging.error(message)
- error_messages.append(message)
-
- audio_send_codec = self.request.get('asc')
- if not audio_send_codec:
- audio_send_codec = get_preferred_audio_send_codec(user_agent)
-
- audio_receive_codec = self.request.get('arc')
- if not audio_receive_codec:
- audio_receive_codec = get_preferred_audio_receive_codec()
-
- # Set stereo to false by default.
- stereo = 'false'
- if self.request.get('stereo'):
- stereo = self.request.get('stereo')
-
- # Options for making pcConstraints
- dtls = self.request.get('dtls')
- dscp = self.request.get('dscp')
- ipv6 = self.request.get('ipv6')
-
- debug = self.request.get('debug')
- if debug == 'loopback':
- # Set dtls to false as DTLS does not work for loopback.
- dtls = 'false'
-
- # token_timeout for channel creation, default 30min, max 1 days, min 3min.
- token_timeout = self.request.get_range('tt',
- min_value = 3,
- max_value = 1440,
- default = 30)
-
- unittest = self.request.get('unittest')
- if unittest:
- # Always create a new room for the unit tests.
- room_key = generate_random(8)
-
- if not room_key:
- room_key = generate_random(8)
- redirect = '/?r=' + room_key
- redirect = append_url_arguments(self.request, redirect)
- self.redirect(redirect)
- logging.info('Redirecting visitor to base URL to ' + redirect)
- return
-
- user = None
- initiator = 0
- with LOCK:
- room = Room.get_by_key_name(room_key)
- if not room and debug != "full":
- # New room.
- user = generate_random(8)
- room = Room(key_name = room_key)
- room.add_user(user)
- if debug != 'loopback':
- initiator = 0
- else:
- room.add_user(user)
- initiator = 1
- elif room and room.get_occupancy() == 1 and debug != 'full':
- # 1 occupant.
- user = generate_random(8)
- room.add_user(user)
- initiator = 1
- else:
- # 2 occupants (full).
- template = jinja_environment.get_template('full.html')
- self.response.out.write(template.render({ 'room_key': room_key }))
- logging.info('Room ' + room_key + ' is full')
- return
-
- if turn_server == 'false':
- turn_server = None
- turn_url = ''
- else:
- turn_url = 'https://computeengineondemand.appspot.com/'
- turn_url = turn_url + 'turn?' + 'username=' + user + '&key=4080218913'
-
- room_link = base_url + '?r=' + room_key
- room_link = append_url_arguments(self.request, room_link)
- token = create_channel(room, user, token_timeout)
- pc_config = make_pc_config(stun_server, turn_server, ts_pwd)
- pc_constraints = make_pc_constraints(dtls, dscp, ipv6)
- offer_constraints = make_offer_constraints()
- media_constraints = make_media_stream_constraints(audio, video)
- template_values = {'error_messages': error_messages,
- 'token': token,
- 'me': user,
- 'room_key': room_key,
- 'room_link': room_link,
- 'initiator': initiator,
- 'pc_config': json.dumps(pc_config),
- 'pc_constraints': json.dumps(pc_constraints),
- 'offer_constraints': json.dumps(offer_constraints),
- 'media_constraints': json.dumps(media_constraints),
- 'turn_url': turn_url,
- 'stereo': stereo,
- 'audio_send_codec': audio_send_codec,
- 'audio_receive_codec': audio_receive_codec
- }
- if unittest:
- target_page = 'test/test_' + unittest + '.html'
- else:
- target_page = 'index.html'
-
- template = jinja_environment.get_template(target_page)
- self.response.out.write(template.render(template_values))
- logging.info('User ' + user + ' added to room ' + room_key)
- logging.info('Room ' + room_key + ' has state ' + str(room))
-
-
-app = webapp2.WSGIApplication([
- ('/', MainPage),
- ('/message', MessagePage),
- ('/_ah/channel/connected/', ConnectPage),
- ('/_ah/channel/disconnected/', DisconnectPage)
- ], debug=True)
diff --git a/samples/js/apprtc/css/main.css b/samples/js/apprtc/css/main.css
deleted file mode 100644
index 15d9eee2b..000000000
--- a/samples/js/apprtc/css/main.css
+++ /dev/null
@@ -1,95 +0,0 @@
-a:link { color: #FFFFFF; }
-a:visited {color: #FFFFFF; }
-html, body {
- background-color: #000000;
- height: 100%;
- font-family: Verdana, Arial, Helvetica, sans-serif;
-}
-body {
- margin: 0;
- padding: 0;
-}
-footer {
- position: absolute;
- bottom: 0;
- width: 100%;
- height: 28px;
- background-color: #3F3F3F;
- color: #FFFFFF;
- font-size: 13px; font-weight: bold;
- line-height: 28px;
- text-align: center;
-}
-#container {
- background-color: #000000;
- position: absolute;
- height: 100%;
- width: 100%;
- margin: 0px auto;
- -webkit-perspective: 1000;
-}
-#card {
- -webkit-transition-duration: 2s;
- -webkit-transform-style: preserve-3d;
-}
-#local {
- position: absolute;
- width: 100%;
- transform: scale(-1, 1);
- -webkit-transform: scale(-1, 1);
- -webkit-backface-visibility: hidden;
-}
-#remote {
- position: absolute;
- width: 100%;
- -webkit-transform: rotateY(180deg);
- -webkit-backface-visibility: hidden;
-}
-#mini {
- position: absolute;
- height: 30%;
- width: 30%;
- bottom: 32px;
- right: 4px;
- opacity: 1.0;
- transform: scale(-1, 1);
- -webkit-transform: scale(-1, 1);
-}
-#localVideo {
- width: 100%;
- height: 100%;
- opacity: 0;
- -webkit-transition-property: opacity;
- -webkit-transition-duration: 2s;
-}
-#remoteVideo {
- width: 100%;
- height: 100%;
- opacity: 0;
- -webkit-transition-property: opacity;
- -webkit-transition-duration: 2s;
-}
-#miniVideo {
- width: 100%;
- height: 100%;
- opacity: 0;
- -webkit-transition-property: opacity;
- -webkit-transition-duration: 2s;
-}
-#hangup {
- font-size: 13px; font-weight: bold;
- color: #FFFFFF;
- width: 128px;
- height: 24px;
- background-color: #808080;
- border-style: solid;
- border-color: #FFFFFF;
- margin: 2px;
-}
-#infoDiv {
- position: absolute;
- float: right;
- background-color: grey;
- margin: 2px;
- display: none;
-}
diff --git a/samples/js/apprtc/full.html b/samples/js/apprtc/full.html
deleted file mode 100644
index b14ac6009..000000000
--- a/samples/js/apprtc/full.html
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/js/apprtc/html/help.html b/samples/js/apprtc/html/help.html
deleted file mode 100644
index 7fd2bf624..000000000
--- a/samples/js/apprtc/html/help.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- WebRtc Demo App Help
-
-
-TODO
-
-
diff --git a/samples/js/apprtc/images/webrtc_black_20p.png b/samples/js/apprtc/images/webrtc_black_20p.png
deleted file mode 100644
index a35c1dff2..000000000
Binary files a/samples/js/apprtc/images/webrtc_black_20p.png and /dev/null differ
diff --git a/samples/js/apprtc/index.html b/samples/js/apprtc/index.html
deleted file mode 100644
index a240f2980..000000000
--- a/samples/js/apprtc/index.html
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-WebRTC Reference App
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/js/apprtc/js/adapter.js b/samples/js/apprtc/js/adapter.js
deleted file mode 120000
index c19e2ce99..000000000
--- a/samples/js/apprtc/js/adapter.js
+++ /dev/null
@@ -1 +0,0 @@
-../../base/adapter.js
\ No newline at end of file
diff --git a/samples/js/apprtc/js/main.js b/samples/js/apprtc/js/main.js
deleted file mode 100644
index 79313dcb9..000000000
--- a/samples/js/apprtc/js/main.js
+++ /dev/null
@@ -1,763 +0,0 @@
-var localVideo;
-var miniVideo;
-var remoteVideo;
-var hasLocalStream;
-var localStream;
-var remoteStream;
-var channel;
-var pc;
-var socket;
-var xmlhttp;
-var started = false;
-var turnDone = false;
-var channelReady = false;
-var signalingReady = false;
-var msgQueue = [];
-// Set up audio and video regardless of what devices are present.
-var sdpConstraints = {'mandatory': {
- 'OfferToReceiveAudio': true,
- 'OfferToReceiveVideo': true }};
-var isVideoMuted = false;
-var isAudioMuted = false;
-// Types of gathered ICE Candidates.
-var gatheredIceCandidateTypes = { Local: {}, Remote: {} };
-var infoDivErrors = [];
-
-function initialize() {
- if (errorMessages.length > 0) {
- for (i = 0; i < errorMessages.length; ++i) {
- window.alert(errorMessages[i]);
- }
- return;
- }
-
- console.log('Initializing; room=' + roomKey + '.');
- card = document.getElementById('card');
- localVideo = document.getElementById('localVideo');
- // Reset localVideo display to center.
- localVideo.addEventListener('loadedmetadata', function(){
- window.onresize();});
- miniVideo = document.getElementById('miniVideo');
- remoteVideo = document.getElementById('remoteVideo');
- resetStatus();
- // NOTE: AppRTCClient.java searches & parses this line; update there when
- // changing here.
- openChannel();
- maybeRequestTurn();
-
- // Caller is always ready to create peerConnection.
- signalingReady = initiator;
-
- if (mediaConstraints.audio === false &&
- mediaConstraints.video === false) {
- hasLocalStream = false;
- maybeStart();
- } else {
- hasLocalStream = true;
- doGetUserMedia();
- }
-}
-
-function openChannel() {
- console.log('Opening channel.');
- var channel = new goog.appengine.Channel(channelToken);
- var handler = {
- 'onopen': onChannelOpened,
- 'onmessage': onChannelMessage,
- 'onerror': onChannelError,
- 'onclose': onChannelClosed
- };
- socket = channel.open(handler);
-}
-
-function maybeRequestTurn() {
- // Allow to skip turn by passing ts=false to apprtc.
- if (turnUrl == '') {
- turnDone = true;
- return;
- }
-
- for (var i = 0, len = pcConfig.iceServers.length; i < len; i++) {
- if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
- turnDone = true;
- return;
- }
- }
-
- var currentDomain = document.domain;
- if (currentDomain.search('localhost') === -1 &&
- currentDomain.search('apprtc') === -1) {
- // Not authorized domain. Try with default STUN instead.
- turnDone = true;
- return;
- }
-
- // No TURN server. Get one from computeengineondemand.appspot.com.
- xmlhttp = new XMLHttpRequest();
- xmlhttp.onreadystatechange = onTurnResult;
- xmlhttp.open('GET', turnUrl, true);
- xmlhttp.send();
-}
-
-function onTurnResult() {
- if (xmlhttp.readyState !== 4)
- return;
-
- if (xmlhttp.status === 200) {
- var turnServer = JSON.parse(xmlhttp.responseText);
- // Create turnUris using the polyfill (adapter.js).
- var iceServers = createIceServers(turnServer.uris,
- turnServer.username,
- turnServer.password);
- if (iceServers !== null) {
- pcConfig.iceServers = pcConfig.iceServers.concat(iceServers);
- }
- } else {
- messageError('No TURN server; unlikely that media will traverse networks. '
- + 'If this persists please report it to '
- + 'discuss-webrtc@googlegroups.com.');
- }
- // If TURN request failed, continue the call with default STUN.
- turnDone = true;
- maybeStart();
-}
-
-function resetStatus() {
- if (!initiator) {
- setStatus('Waiting for someone to join: \
- ' + roomLink + '');
- } else {
- setStatus('Initializing...');
- }
-}
-
-function doGetUserMedia() {
- // Call into getUserMedia via the polyfill (adapter.js).
- try {
- getUserMedia(mediaConstraints, onUserMediaSuccess,
- onUserMediaError);
- console.log('Requested access to local media with mediaConstraints:\n' +
- ' \'' + JSON.stringify(mediaConstraints) + '\'');
- } catch (e) {
- alert('getUserMedia() failed. Is this a WebRTC capable browser?');
- messageError('getUserMedia failed with exception: ' + e.message);
- }
-}
-
-function createPeerConnection() {
- try {
- // Create an RTCPeerConnection via the polyfill (adapter.js).
- pc = new RTCPeerConnection(pcConfig, pcConstraints);
- pc.onicecandidate = onIceCandidate;
- console.log('Created RTCPeerConnnection with:\n' +
- ' config: \'' + JSON.stringify(pcConfig) + '\';\n' +
- ' constraints: \'' + JSON.stringify(pcConstraints) + '\'.');
- } catch (e) {
- messageError('Failed to create PeerConnection, exception: ' + e.message);
- alert('Cannot create RTCPeerConnection object; \
- WebRTC is not supported by this browser.');
- return;
- }
- pc.onaddstream = onRemoteStreamAdded;
- pc.onremovestream = onRemoteStreamRemoved;
- pc.onsignalingstatechange = onSignalingStateChanged;
- pc.oniceconnectionstatechange = onIceConnectionStateChanged;
-}
-
-function maybeStart() {
- if (!started && signalingReady && channelReady && turnDone &&
- (localStream || !hasLocalStream)) {
- setStatus('Connecting...');
- console.log('Creating PeerConnection.');
- createPeerConnection();
-
- if (hasLocalStream) {
- console.log('Adding local stream.');
- pc.addStream(localStream);
- } else {
- console.log('Not sending any stream.');
- }
- started = true;
-
- if (initiator)
- doCall();
- else
- calleeStart();
- }
-}
-
-function setStatus(state) {
- document.getElementById('status').innerHTML = state;
-}
-
-function doCall() {
- var constraints = mergeConstraints(offerConstraints, sdpConstraints);
- console.log('Sending offer to peer, with constraints: \n' +
- ' \'' + JSON.stringify(constraints) + '\'.')
- pc.createOffer(setLocalAndSendMessage,
- onCreateSessionDescriptionError, constraints);
-}
-
-function calleeStart() {
- // Callee starts to process cached offer and other messages.
- while (msgQueue.length > 0) {
- processSignalingMessage(msgQueue.shift());
- }
-}
-
-function doAnswer() {
- console.log('Sending answer to peer.');
- pc.createAnswer(setLocalAndSendMessage,
- onCreateSessionDescriptionError, sdpConstraints);
-}
-
-function mergeConstraints(cons1, cons2) {
- var merged = cons1;
- for (var name in cons2.mandatory) {
- merged.mandatory[name] = cons2.mandatory[name];
- }
- merged.optional.concat(cons2.optional);
- return merged;
-}
-
-function setLocalAndSendMessage(sessionDescription) {
- sessionDescription.sdp = maybePreferAudioReceiveCodec(sessionDescription.sdp);
- pc.setLocalDescription(sessionDescription,
- onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
- sendMessage(sessionDescription);
-}
-
-function setRemote(message) {
- // Set Opus in Stereo, if stereo enabled.
- if (stereo)
- message.sdp = addStereo(message.sdp);
- message.sdp = maybePreferAudioSendCodec(message.sdp);
- pc.setRemoteDescription(new RTCSessionDescription(message),
- onSetRemoteDescriptionSuccess, onSetSessionDescriptionError);
-
- function onSetRemoteDescriptionSuccess() {
- console.log("Set remote session description success.");
- // By now all addstream events for the setRemoteDescription have fired.
- // So we can know if the peer is sending any stream or is only receiving.
- if (remoteStream) {
- waitForRemoteVideo();
- } else {
- console.log("Not receiving any stream.");
- transitionToActive();
- }
- }
-}
-
-function sendMessage(message) {
- var msgString = JSON.stringify(message);
- console.log('C->S: ' + msgString);
- // NOTE: AppRTCClient.java searches & parses this line; update there when
- // changing here.
- path = '/message?r=' + roomKey + '&u=' + me;
- var xhr = new XMLHttpRequest();
- xhr.open('POST', path, true);
- xhr.send(msgString);
-}
-
-function processSignalingMessage(message) {
- if (!started) {
- messageError('peerConnection has not been created yet!');
- return;
- }
-
- if (message.type === 'offer') {
- setRemote(message);
- doAnswer();
- } else if (message.type === 'answer') {
- setRemote(message);
- } else if (message.type === 'candidate') {
- var candidate = new RTCIceCandidate({sdpMLineIndex: message.label,
- candidate: message.candidate});
- noteIceCandidate("Remote", iceCandidateType(message.candidate));
- pc.addIceCandidate(candidate,
- onAddIceCandidateSuccess, onAddIceCandidateError);
- } else if (message.type === 'bye') {
- onRemoteHangup();
- }
-}
-
-function onAddIceCandidateSuccess() {
- console.log('AddIceCandidate success.');
-}
-
-function onAddIceCandidateError(error) {
- messageError('Failed to add Ice Candidate: ' + error.toString());
-}
-
-function onChannelOpened() {
- console.log('Channel opened.');
- channelReady = true;
- maybeStart();
-}
-
-function onChannelMessage(message) {
- console.log('S->C: ' + message.data);
- var msg = JSON.parse(message.data);
- // Since the turn response is async and also GAE might disorder the
- // Message delivery due to possible datastore query at server side,
- // So callee needs to cache messages before peerConnection is created.
- if (!initiator && !started) {
- if (msg.type === 'offer') {
- // Add offer to the beginning of msgQueue, since we can't handle
- // Early candidates before offer at present.
- msgQueue.unshift(msg);
- // Callee creates PeerConnection
- signalingReady = true;
- maybeStart();
- } else {
- msgQueue.push(msg);
- }
- } else {
- processSignalingMessage(msg);
- }
-}
-
-function onChannelError() {
- messageError('Channel error.');
-}
-
-function onChannelClosed() {
- console.log('Channel closed.');
-}
-
-function messageError(msg) {
- console.log(msg);
- infoDivErrors.push(msg);
- updateInfoDiv();
-}
-
-function onUserMediaSuccess(stream) {
- console.log('User has granted access to local media.');
- // Call the polyfill wrapper to attach the media stream to this element.
- attachMediaStream(localVideo, stream);
- localVideo.style.opacity = 1;
- localStream = stream;
- // Caller creates PeerConnection.
- maybeStart();
-}
-
-function onUserMediaError(error) {
- messageError('Failed to get access to local media. Error code was ' +
- error.code + '. Continuing without sending a stream.');
- alert('Failed to get access to local media. Error code was ' +
- error.code + '. Continuing without sending a stream.');
-
- hasLocalStream = false;
- maybeStart();
-}
-
-function onCreateSessionDescriptionError(error) {
- messageError('Failed to create session description: ' + error.toString());
-}
-
-function onSetSessionDescriptionSuccess() {
- console.log('Set session description success.');
-}
-
-function onSetSessionDescriptionError(error) {
- messageError('Failed to set session description: ' + error.toString());
-}
-
-function iceCandidateType(candidateSDP) {
- if (candidateSDP.indexOf("typ relay ") >= 0)
- return "TURN";
- if (candidateSDP.indexOf("typ srflx ") >= 0)
- return "STUN";
- if (candidateSDP.indexOf("typ host ") >= 0)
- return "HOST";
- return "UNKNOWN";
-}
-
-function onIceCandidate(event) {
- if (event.candidate) {
- sendMessage({type: 'candidate',
- label: event.candidate.sdpMLineIndex,
- id: event.candidate.sdpMid,
- candidate: event.candidate.candidate});
- noteIceCandidate("Local", iceCandidateType(event.candidate.candidate));
- } else {
- console.log('End of candidates.');
- }
-}
-
-function onRemoteStreamAdded(event) {
- console.log('Remote stream added.');
- attachMediaStream(remoteVideo, event.stream);
- remoteStream = event.stream;
-}
-
-function onRemoteStreamRemoved(event) {
- console.log('Remote stream removed.');
-}
-
-function onSignalingStateChanged(event) {
- updateInfoDiv();
-}
-
-function onIceConnectionStateChanged(event) {
- updateInfoDiv();
-}
-
-function onHangup() {
- console.log('Hanging up.');
- transitionToDone();
- localStream.stop();
- stop();
- // will trigger BYE from server
- socket.close();
-}
-
-function onRemoteHangup() {
- console.log('Session terminated.');
- initiator = 0;
- transitionToWaiting();
- stop();
-}
-
-function stop() {
- started = false;
- signalingReady = false;
- isAudioMuted = false;
- isVideoMuted = false;
- pc.close();
- pc = null;
- remoteStream = null;
- msgQueue.length = 0;
-}
-
-function waitForRemoteVideo() {
- // Call the getVideoTracks method via adapter.js.
- videoTracks = remoteStream.getVideoTracks();
- if (videoTracks.length === 0 || remoteVideo.currentTime > 0) {
- transitionToActive();
- } else {
- setTimeout(waitForRemoteVideo, 100);
- }
-}
-
-function transitionToActive() {
- reattachMediaStream(miniVideo, localVideo);
- remoteVideo.style.opacity = 1;
- card.style.webkitTransform = 'rotateY(180deg)';
- setTimeout(function() { localVideo.src = ''; }, 500);
- setTimeout(function() { miniVideo.style.opacity = 1; }, 1000);
- // Reset window display according to the asperio of remote video.
- window.onresize();
- setStatus('');
-}
-
-function transitionToWaiting() {
- card.style.webkitTransform = 'rotateY(0deg)';
- setTimeout(function() {
- localVideo.src = miniVideo.src;
- miniVideo.src = '';
- remoteVideo.src = '' }, 500);
- miniVideo.style.opacity = 0;
- remoteVideo.style.opacity = 0;
- resetStatus();
-}
-
-function transitionToDone() {
- localVideo.style.opacity = 0;
- remoteVideo.style.opacity = 0;
- miniVideo.style.opacity = 0;
- setStatus('You have left the call. \
- Click here to rejoin.');
-}
-
-function enterFullScreen() {
- container.webkitRequestFullScreen();
-}
-
-function noteIceCandidate(location, type) {
- if (gatheredIceCandidateTypes[location][type])
- return;
- gatheredIceCandidateTypes[location][type] = 1;
- updateInfoDiv();
-}
-
-function getInfoDiv() {
- return document.getElementById("infoDiv");
-}
-
-function updateInfoDiv() {
- var contents = "Gathered ICE Candidates\n";
- for (var endpoint in gatheredIceCandidateTypes) {
- contents += endpoint + ":\n";
- for (var type in gatheredIceCandidateTypes[endpoint])
- contents += " " + type + "\n";
- }
- if (pc != null) {
- contents += "Gathering: " + pc.iceGatheringState + "\n";
- contents += "
\n";
- contents += "PC State:\n";
- contents += "Signaling: " + pc.signalingState + "\n";
- contents += "ICE: " + pc.iceConnectionState + "\n";
- }
- var div = getInfoDiv();
- div.innerHTML = contents + "
";
-
- for (var msg in infoDivErrors) {
- div.innerHTML += '' +
- infoDivErrors[msg] + '
';
- }
- if (infoDivErrors.length)
- showInfoDiv();
-}
-
-function toggleInfoDiv() {
- var div = getInfoDiv();
- if (div.style.display == "block") {
- div.style.display = "none";
- } else {
- showInfoDiv();
- }
-}
-
-function showInfoDiv() {
- var div = getInfoDiv();
- div.style.display = "block";
-}
-
-function toggleVideoMute() {
- // Call the getVideoTracks method via adapter.js.
- videoTracks = localStream.getVideoTracks();
-
- if (videoTracks.length === 0) {
- console.log('No local video available.');
- return;
- }
-
- if (isVideoMuted) {
- for (i = 0; i < videoTracks.length; i++) {
- videoTracks[i].enabled = true;
- }
- console.log('Video unmuted.');
- } else {
- for (i = 0; i < videoTracks.length; i++) {
- videoTracks[i].enabled = false;
- }
- console.log('Video muted.');
- }
-
- isVideoMuted = !isVideoMuted;
-}
-
-function toggleAudioMute() {
- // Call the getAudioTracks method via adapter.js.
- audioTracks = localStream.getAudioTracks();
-
- if (audioTracks.length === 0) {
- console.log('No local audio available.');
- return;
- }
-
- if (isAudioMuted) {
- for (i = 0; i < audioTracks.length; i++) {
- audioTracks[i].enabled = true;
- }
- console.log('Audio unmuted.');
- } else {
- for (i = 0; i < audioTracks.length; i++){
- audioTracks[i].enabled = false;
- }
- console.log('Audio muted.');
- }
-
- isAudioMuted = !isAudioMuted;
-}
-
-// Mac: hotkey is Command.
-// Non-Mac: hotkey is Control.
-// -D: toggle audio mute.
-// -E: toggle video mute.
-// -I: toggle Info box.
-// Return false to screen out original Chrome shortcuts.
-document.onkeydown = function(event) {
- var hotkey = event.ctrlKey;
- if (navigator.appVersion.indexOf('Mac') != -1)
- hotkey = event.metaKey;
- if (!hotkey)
- return;
- switch (event.keyCode) {
- case 68:
- toggleAudioMute();
- return false;
- case 69:
- toggleVideoMute();
- return false;
- case 73:
- toggleInfoDiv();
- return false;
- default:
- return;
- }
-}
-
-function maybePreferAudioSendCodec(sdp) {
- if (audio_send_codec == '') {
- console.log('No preference on audio send codec.');
- return sdp;
- }
- console.log('Prefer audio send codec: ' + audio_send_codec);
- return preferAudioCodec(sdp, audio_send_codec);
-}
-
-function maybePreferAudioReceiveCodec(sdp) {
- if (audio_receive_codec == '') {
- console.log('No preference on audio receive codec.');
- return sdp;
- }
- console.log('Prefer audio receive codec: ' + audio_receive_codec);
- return preferAudioCodec(sdp, audio_receive_codec);
-}
-
-// Set |codec| as the default audio codec if it's present.
-// The format of |codec| is 'NAME/RATE', e.g. 'opus/48000'.
-function preferAudioCodec(sdp, codec) {
- var fields = codec.split('/');
- if (fields.length != 2) {
- console.log('Invalid codec setting: ' + codec);
- return sdp;
- }
- var name = fields[0];
- var rate = fields[1];
- var sdpLines = sdp.split('\r\n');
-
- // Search for m line.
- for (var i = 0; i < sdpLines.length; i++) {
- if (sdpLines[i].search('m=audio') !== -1) {
- var mLineIndex = i;
- break;
- }
- }
- if (mLineIndex === null)
- return sdp;
-
- // If the codec is available, set it as the default in m line.
- for (var i = 0; i < sdpLines.length; i++) {
- if (sdpLines[i].search(name + '/' + rate) !== -1) {
- var regexp = new RegExp(':(\\d+) ' + name + '\\/' + rate, 'i');
- var payload = extractSdp(sdpLines[i], regexp);
- if (payload)
- sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
- payload);
- break;
- }
- }
-
- // Remove CN in m line and sdp.
- sdpLines = removeCN(sdpLines, mLineIndex);
-
- sdp = sdpLines.join('\r\n');
- return sdp;
-}
-
-// Set Opus in stereo if stereo is enabled.
-function addStereo(sdp) {
- var sdpLines = sdp.split('\r\n');
-
- // Find opus payload.
- for (var i = 0; i < sdpLines.length; i++) {
- if (sdpLines[i].search('opus/48000') !== -1) {
- var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
- break;
- }
- }
-
- // Find the payload in fmtp line.
- for (var i = 0; i < sdpLines.length; i++) {
- if (sdpLines[i].search('a=fmtp') !== -1) {
- var payload = extractSdp(sdpLines[i], /a=fmtp:(\d+)/ );
- if (payload === opusPayload) {
- var fmtpLineIndex = i;
- break;
- }
- }
- }
- // No fmtp line found.
- if (fmtpLineIndex === null)
- return sdp;
-
- // Append stereo=1 to fmtp line.
- sdpLines[fmtpLineIndex] = sdpLines[fmtpLineIndex].concat(' stereo=1');
-
- sdp = sdpLines.join('\r\n');
- return sdp;
-}
-
-function extractSdp(sdpLine, pattern) {
- var result = sdpLine.match(pattern);
- return (result && result.length == 2)? result[1]: null;
-}
-
-// Set the selected codec to the first in m line.
-function setDefaultCodec(mLine, payload) {
- var elements = mLine.split(' ');
- var newLine = new Array();
- var index = 0;
- for (var i = 0; i < elements.length; i++) {
- if (index === 3) // Format of media starts from the fourth.
- newLine[index++] = payload; // Put target payload to the first.
- if (elements[i] !== payload)
- newLine[index++] = elements[i];
- }
- return newLine.join(' ');
-}
-
-// Strip CN from sdp before CN constraints is ready.
-function removeCN(sdpLines, mLineIndex) {
- var mLineElements = sdpLines[mLineIndex].split(' ');
- // Scan from end for the convenience of removing an item.
- for (var i = sdpLines.length-1; i >= 0; i--) {
- var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
- if (payload) {
- var cnPos = mLineElements.indexOf(payload);
- if (cnPos !== -1) {
- // Remove CN payload from m line.
- mLineElements.splice(cnPos, 1);
- }
- // Remove CN line in sdp
- sdpLines.splice(i, 1);
- }
- }
-
- sdpLines[mLineIndex] = mLineElements.join(' ');
- return sdpLines;
-}
-
-// Send BYE on refreshing(or leaving) a demo page
-// to ensure the room is cleaned for next session.
-window.onbeforeunload = function() {
- sendMessage({type: 'bye'});
-}
-
-// Set the video diplaying in the center of window.
-window.onresize = function(){
- var aspectRatio;
- if (remoteVideo.style.opacity === '1') {
- aspectRatio = remoteVideo.videoWidth/remoteVideo.videoHeight;
- } else if (localVideo.style.opacity === '1') {
- aspectRatio = localVideo.videoWidth/localVideo.videoHeight;
- } else {
- return;
- }
-
- var innerHeight = this.innerHeight;
- var innerWidth = this.innerWidth;
- var videoWidth = innerWidth < aspectRatio * window.innerHeight ?
- innerWidth : aspectRatio * window.innerHeight;
- var videoHeight = innerHeight < window.innerWidth / aspectRatio ?
- innerHeight : window.innerWidth / aspectRatio;
- containerDiv = document.getElementById('container');
- containerDiv.style.width = videoWidth + 'px';
- containerDiv.style.height = videoHeight + 'px';
- containerDiv.style.left = (innerWidth - videoWidth) / 2 + 'px';
- containerDiv.style.top = (innerHeight - videoHeight) / 2 + 'px';
-};
diff --git a/samples/js/apprtc/test/test_channel.html b/samples/js/apprtc/test/test_channel.html
deleted file mode 100644
index 1668ce0df..000000000
--- a/samples/js/apprtc/test/test_channel.html
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/js/apprtc/turn-prober/README b/samples/js/apprtc/turn-prober/README
deleted file mode 100644
index 58ba3398a..000000000
--- a/samples/js/apprtc/turn-prober/README
+++ /dev/null
@@ -1,9 +0,0 @@
-This script contains a simple prober that verifies that:
-- CEOD vends TURN server URIs with credentials on demand (mimicking apprtc)
-- rfc5766-turn-server vends TURN candidates from the servers vended by CEOD.
-
-To use simply run ./turn-prober.sh
-If it prints "PASS" (and exits 0) then all is well.
-If it prints a mess of logs (and exits non-0) then something has gone sideways
-and apprtc.appspot.com is probably not working well (b/c of missing TURN
-functionality).
diff --git a/samples/js/apprtc/turn-prober/turn-prober.html b/samples/js/apprtc/turn-prober/turn-prober.html
deleted file mode 100644
index 94cf68ecf..000000000
--- a/samples/js/apprtc/turn-prober/turn-prober.html
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
-
-
-
diff --git a/samples/js/apprtc/turn-prober/turn-prober.sh b/samples/js/apprtc/turn-prober/turn-prober.sh
deleted file mode 100755
index 2a063c58e..000000000
--- a/samples/js/apprtc/turn-prober/turn-prober.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/bash -e
-
-function chrome_pids() {
- ps axuwww|grep $D|grep c[h]rome|awk '{print $2}'
-}
-
-cd $(dirname $0)
-export D=$(mktemp -d)
-
-CHROME_LOG_FILE="${D}/chrome_debug.log"
-touch $CHROME_LOG_FILE
-
-XVFB="xvfb-run -a -e $CHROME_LOG_FILE -s '-screen 0 1024x768x24'"
-if [ -n "$DISPLAY" ]; then
- XVFB=""
-fi
-
-# "eval" below is required by $XVFB containing a quoted argument.
-eval $XVFB chrome \
- --enable-logging=stderr \
- --no-first-run \
- --disable-web-security \
- --user-data-dir=$D \
- --vmodule="*media/*=3,*turn*=3" \
- "file://${PWD}/turn-prober.html" > $CHROME_LOG_FILE 2>&1 &
-CHROME_PID=$!
-
-while ! grep -q DONE $CHROME_LOG_FILE && chrome_pids|grep -q .; do
- sleep 0.1
-done
-
-# Suppress bash's Killed message for the chrome above.
-exec 3>&2
-exec 2>/dev/null
-while [ ! -z "$(chrome_pids)" ]; do
- kill -9 $(chrome_pids)
-done
-exec 2>&3
-exec 3>&-
-
-DONE=$(grep DONE $CHROME_LOG_FILE)
-EXIT_CODE=0
-if ! grep -q "DONE: PASS" $CHROME_LOG_FILE; then
- cat $CHROME_LOG_FILE
- EXIT_CODE=1
-fi
-
-rm -rf $D
-exit $EXIT_CODE
diff --git a/samples/js/base/adapter.js b/samples/js/base/adapter.js
deleted file mode 100644
index 3dd894784..000000000
--- a/samples/js/base/adapter.js
+++ /dev/null
@@ -1,198 +0,0 @@
-var RTCPeerConnection = null;
-var getUserMedia = null;
-var attachMediaStream = null;
-var reattachMediaStream = null;
-var webrtcDetectedBrowser = null;
-var webrtcDetectedVersion = null;
-
-function trace(text) {
- // This function is used for logging.
- if (text[text.length - 1] == '\n') {
- text = text.substring(0, text.length - 1);
- }
- console.log((performance.now() / 1000).toFixed(3) + ": " + text);
-}
-function maybeFixConfiguration(pcConfig) {
- if (pcConfig == null) {
- return;
- }
- for (var i = 0; i < pcConfig.iceServers.length; i++) {
- if (pcConfig.iceServers[i].hasOwnProperty('urls')){
- pcConfig.iceServers[i]['url'] = pcConfig.iceServers[i]['urls'];
- delete pcConfig.iceServers[i]['urls'];
- }
- }
-}
-
-if (navigator.mozGetUserMedia) {
- console.log("This appears to be Firefox");
-
- webrtcDetectedBrowser = "firefox";
-
- webrtcDetectedVersion =
- parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
-
- // The RTCPeerConnection object.
- var RTCPeerConnection = function(pcConfig, pcConstraints) {
- // .urls is not supported in FF yet.
- maybeFixConfiguration(pcConfig);
- return new mozRTCPeerConnection(pcConfig, pcConstraints);
- }
-
- // The RTCSessionDescription object.
- RTCSessionDescription = mozRTCSessionDescription;
-
- // The RTCIceCandidate object.
- RTCIceCandidate = mozRTCIceCandidate;
-
- // Get UserMedia (only difference is the prefix).
- // Code from Adam Barth.
- getUserMedia = navigator.mozGetUserMedia.bind(navigator);
- navigator.getUserMedia = getUserMedia;
-
- // Creates iceServer from the url for FF.
- createIceServer = function(url, username, password) {
- var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
- // Create iceServer with stun url.
- iceServer = { 'url': url };
- } else if (url_parts[0].indexOf('turn') === 0) {
- if (webrtcDetectedVersion < 27) {
- // Create iceServer with turn url.
- // Ignore the transport parameter from TURN url for FF version <=27.
- var turn_url_parts = url.split("?");
- // Return null for createIceServer if transport=tcp.
- if (turn_url_parts.length === 1 ||
- turn_url_parts[1].indexOf('transport=udp') === 0) {
- iceServer = {'url': turn_url_parts[0],
- 'credential': password,
- 'username': username};
- }
- } else {
- // FF 27 and above supports transport parameters in TURN url,
- // So passing in the full url to create iceServer.
- iceServer = {'url': url,
- 'credential': password,
- 'username': username};
- }
- }
- return iceServer;
- };
-
- createIceServers = function(urls, username, password) {
- var iceServers = [];
- // Use .url for FireFox.
- for (i = 0; i < urls.length; i++) {
- var iceServer = createIceServer(urls[i],
- username,
- password);
- if (iceServer !== null) {
- iceServers.push(iceServer);
- }
- }
- return iceServers;
- }
-
- // Attach a media stream to an element.
- attachMediaStream = function(element, stream) {
- console.log("Attaching media stream");
- element.mozSrcObject = stream;
- element.play();
- };
-
- reattachMediaStream = function(to, from) {
- console.log("Reattaching media stream");
- to.mozSrcObject = from.mozSrcObject;
- to.play();
- };
-
- // Fake get{Video,Audio}Tracks
- if (!MediaStream.prototype.getVideoTracks) {
- MediaStream.prototype.getVideoTracks = function() {
- return [];
- };
- }
-
- if (!MediaStream.prototype.getAudioTracks) {
- MediaStream.prototype.getAudioTracks = function() {
- return [];
- };
- }
-} else if (navigator.webkitGetUserMedia) {
- console.log("This appears to be Chrome");
-
- webrtcDetectedBrowser = "chrome";
- webrtcDetectedVersion =
- parseInt(navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
-
- // Creates iceServer from the url for Chrome M33 and earlier.
- createIceServer = function(url, username, password) {
- var iceServer = null;
- var url_parts = url.split(':');
- if (url_parts[0].indexOf('stun') === 0) {
- // Create iceServer with stun url.
- iceServer = { 'url': url };
- } else if (url_parts[0].indexOf('turn') === 0) {
- // Chrome M28 & above uses below TURN format.
- iceServer = {'url': url,
- 'credential': password,
- 'username': username};
- }
- return iceServer;
- };
-
- // Creates iceServers from the urls for Chrome M34 and above.
- createIceServers = function(urls, username, password) {
- var iceServers = [];
- if (webrtcDetectedVersion >= 34) {
- // .urls is supported since Chrome M34.
- iceServers = {'urls': urls,
- 'credential': password,
- 'username': username };
- } else {
- for (i = 0; i < urls.length; i++) {
- var iceServer = createIceServer(urls[i],
- username,
- password);
- if (iceServer !== null) {
- iceServers.push(iceServer);
- }
- }
- }
- return iceServers;
- };
-
- // The RTCPeerConnection object.
- var RTCPeerConnection = function(pcConfig, pcConstraints) {
- // .urls is supported since Chrome M34.
- if (webrtcDetectedVersion < 34) {
- maybeFixConfiguration(pcConfig);
- }
- return new webkitRTCPeerConnection(pcConfig, pcConstraints);
- }
-
- // Get UserMedia (only difference is the prefix).
- // Code from Adam Barth.
- getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
- navigator.getUserMedia = getUserMedia;
-
- // Attach a media stream to an element.
- attachMediaStream = function(element, stream) {
- if (typeof element.srcObject !== 'undefined') {
- element.srcObject = stream;
- } else if (typeof element.mozSrcObject !== 'undefined') {
- element.mozSrcObject = stream;
- } else if (typeof element.src !== 'undefined') {
- element.src = URL.createObjectURL(stream);
- } else {
- console.log('Error attaching stream to element.');
- }
- };
-
- reattachMediaStream = function(to, from) {
- to.src = from.src;
- };
-} else {
- console.log("Browser does not appear to be WebRTC-capable");
-}