├── .gitignore
├── .jshintrc
├── LICENSE
├── Procfile
├── README.md
├── config
└── config.json
├── index.js
├── lib
└── server.js
├── package.json
└── public
├── .bowerrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── Gruntfile.js
├── app
├── .buildignore
├── .htaccess
├── 404.html
├── favicon.ico
├── images
│ └── yeoman.png
├── index.html
├── robots.txt
├── scripts
│ ├── adapter.js
│ ├── app.js
│ ├── controllers
│ │ └── room.js
│ ├── directives
│ │ └── videoplayer.js
│ └── services
│ │ ├── io.js
│ │ ├── room.js
│ │ └── videostream.js
├── styles
│ └── main.css
└── views
│ └── room.html
├── bower.json
├── dist
├── .htaccess
├── 404.html
├── favicon.ico
├── fonts
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ └── glyphicons-halflings-regular.woff
├── images
│ └── yeoman.d2754b85.png
├── index.html
├── robots.txt
├── scripts
│ ├── scripts.45ae9c72.js
│ └── vendor.d4336f12.js
├── styles
│ ├── main.d8d6dc72.css
│ └── vendor.cabc70f2.css
└── views
│ └── room.html
├── package.json
└── test
├── .jshintrc
├── karma.conf.js
└── spec
├── controllers
├── about.js
├── home.js
└── main.js
├── directives
└── videoplayer.js
└── services
├── config.js
├── io.js
├── roommanager.js
└── videostream.js
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | npm-debug.log
15 | node_modules
16 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "browser": true,
3 | "globals": {
4 | "io": false
5 | }
6 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Minko Gechev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node index.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AngularJS WebRTC
2 | ================
3 |
4 | Demo project for my [blog post](http://blog.mgechev.com/2014/12/26/multi-user-video-conference-webrtc-angularjs-yeoman/).
5 |
--------------------------------------------------------------------------------
/config/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "PORT": 5555
3 | }
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var config = require('./config/config.json'),
2 | server = require('./lib/server');
3 |
4 | config.PORT = process.env.PORT || config.PORT;
5 |
6 | server.run(config);
7 |
--------------------------------------------------------------------------------
/lib/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express'),
2 | expressApp = express(),
3 | socketio = require('socket.io'),
4 | http = require('http'),
5 | server = http.createServer(expressApp),
6 | uuid = require('node-uuid'),
7 | rooms = {},
8 | userIds = {};
9 |
10 | expressApp.use(express.static(__dirname + '/../public/dist/'));
11 |
12 | exports.run = function (config) {
13 |
14 | server.listen(config.PORT);
15 | console.log('Listening on', config.PORT);
16 | socketio.listen(server, { log: false })
17 | .on('connection', function (socket) {
18 |
19 | var currentRoom, id;
20 |
21 | socket.on('init', function (data, fn) {
22 | currentRoom = (data || {}).room || uuid.v4();
23 | var room = rooms[currentRoom];
24 | if (!data) {
25 | rooms[currentRoom] = [socket];
26 | id = userIds[currentRoom] = 0;
27 | fn(currentRoom, id);
28 | console.log('Room created, with #', currentRoom);
29 | } else {
30 | if (!room) {
31 | return;
32 | }
33 | userIds[currentRoom] += 1;
34 | id = userIds[currentRoom];
35 | fn(currentRoom, id);
36 | room.forEach(function (s) {
37 | s.emit('peer.connected', { id: id });
38 | });
39 | room[id] = socket;
40 | console.log('Peer connected to room', currentRoom, 'with #', id);
41 | }
42 | });
43 |
44 | socket.on('msg', function (data) {
45 | var to = parseInt(data.to, 10);
46 | if (rooms[currentRoom] && rooms[currentRoom][to]) {
47 | console.log('Redirecting message to', to, 'by', data.by);
48 | rooms[currentRoom][to].emit('msg', data);
49 | } else {
50 | console.warn('Invalid user');
51 | }
52 | });
53 |
54 | socket.on('disconnect', function () {
55 | if (!currentRoom || !rooms[currentRoom]) {
56 | return;
57 | }
58 | delete rooms[currentRoom][rooms[currentRoom].indexOf(socket)];
59 | rooms[currentRoom].forEach(function (socket) {
60 | if (socket) {
61 | socket.emit('peer.disconnected', { id: id });
62 | }
63 | });
64 | });
65 | });
66 | };
67 |
68 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-webrtc",
3 | "version": "0.0.0",
4 | "description": "angular-webrtc",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git"
11 | },
12 | "author": "mgechev",
13 | "license": "MIT",
14 | "dependencies": {
15 | "express": "^4.10.6",
16 | "node-uuid": "^1.4.2",
17 | "socket.io": "~0.9.16"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/public/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/public/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/public/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/public/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .tmp
3 | .sass-cache
4 | bower_components
5 |
--------------------------------------------------------------------------------
/public/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "globals": {
21 | "angular": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/public/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | before_script:
5 | - 'npm install -g bower grunt-cli'
6 | - 'bower install'
7 |
--------------------------------------------------------------------------------
/public/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on 2014-12-22 using generator-angular 0.10.0
2 | 'use strict';
3 |
4 | // # Globbing
5 | // for performance reasons we're only matching one level down:
6 | // 'test/spec/{,*/}*.js'
7 | // use this if you want to recursively match all subfolders:
8 | // 'test/spec/**/*.js'
9 |
10 | module.exports = function (grunt) {
11 |
12 | // Load grunt tasks automatically
13 | require('load-grunt-tasks')(grunt);
14 |
15 | // Time how long tasks take. Can help when optimizing build times
16 | require('time-grunt')(grunt);
17 |
18 | // Configurable paths for the application
19 | var appConfig = {
20 | app: require('./bower.json').appPath || 'app',
21 | dist: 'dist'
22 | };
23 |
24 | // Define the configuration for all the tasks
25 | grunt.initConfig({
26 |
27 | // Project settings
28 | yeoman: appConfig,
29 |
30 | // Watches files for changes and runs tasks based on the changed files
31 | watch: {
32 | bower: {
33 | files: ['bower.json'],
34 | tasks: ['wiredep']
35 | },
36 | js: {
37 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
38 | tasks: ['newer:jshint:all'],
39 | options: {
40 | livereload: '<%= connect.options.livereload %>'
41 | }
42 | },
43 | jsTest: {
44 | files: ['test/spec/{,*/}*.js'],
45 | tasks: ['newer:jshint:test', 'karma']
46 | },
47 | styles: {
48 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
49 | tasks: ['newer:copy:styles', 'autoprefixer']
50 | },
51 | gruntfile: {
52 | files: ['Gruntfile.js']
53 | },
54 | livereload: {
55 | options: {
56 | livereload: '<%= connect.options.livereload %>'
57 | },
58 | files: [
59 | '<%= yeoman.app %>/{,*/}*.html',
60 | '.tmp/styles/{,*/}*.css',
61 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
62 | ]
63 | }
64 | },
65 |
66 | // The actual grunt server settings
67 | connect: {
68 | options: {
69 | port: 9000,
70 | // Change this to '0.0.0.0' to access the server from outside.
71 | hostname: 'localhost',
72 | livereload: 35729
73 | },
74 | livereload: {
75 | options: {
76 | open: true,
77 | middleware: function (connect) {
78 | return [
79 | connect.static('.tmp'),
80 | connect().use(
81 | '/bower_components',
82 | connect.static('./bower_components')
83 | ),
84 | connect.static(appConfig.app)
85 | ];
86 | }
87 | }
88 | },
89 | test: {
90 | options: {
91 | port: 9001,
92 | middleware: function (connect) {
93 | return [
94 | connect.static('.tmp'),
95 | connect.static('test'),
96 | connect().use(
97 | '/bower_components',
98 | connect.static('./bower_components')
99 | ),
100 | connect.static(appConfig.app)
101 | ];
102 | }
103 | }
104 | },
105 | dist: {
106 | options: {
107 | open: true,
108 | base: '<%= yeoman.dist %>'
109 | }
110 | }
111 | },
112 |
113 | // Make sure code styles are up to par and there are no obvious mistakes
114 | jshint: {
115 | options: {
116 | jshintrc: '.jshintrc',
117 | reporter: require('jshint-stylish')
118 | },
119 | all: {
120 | src: [
121 | 'Gruntfile.js',
122 | '<%= yeoman.app %>/scripts/{,*/}*.js'
123 | ]
124 | },
125 | test: {
126 | options: {
127 | jshintrc: 'test/.jshintrc'
128 | },
129 | src: ['test/spec/{,*/}*.js']
130 | }
131 | },
132 |
133 | // Empties folders to start fresh
134 | clean: {
135 | dist: {
136 | files: [{
137 | dot: true,
138 | src: [
139 | '.tmp',
140 | '<%= yeoman.dist %>/{,*/}*',
141 | '!<%= yeoman.dist %>/.git{,*/}*'
142 | ]
143 | }]
144 | },
145 | server: '.tmp'
146 | },
147 |
148 | // Add vendor prefixed styles
149 | autoprefixer: {
150 | options: {
151 | browsers: ['last 1 version']
152 | },
153 | dist: {
154 | files: [{
155 | expand: true,
156 | cwd: '.tmp/styles/',
157 | src: '{,*/}*.css',
158 | dest: '.tmp/styles/'
159 | }]
160 | }
161 | },
162 |
163 | // Automatically inject Bower components into the app
164 | wiredep: {
165 | app: {
166 | src: ['<%= yeoman.app %>/index.html'],
167 | ignorePath: /\.\.\//
168 | }
169 | },
170 |
171 | // Renames files for browser caching purposes
172 | filerev: {
173 | dist: {
174 | src: [
175 | '<%= yeoman.dist %>/scripts/{,*/}*.js',
176 | '<%= yeoman.dist %>/styles/{,*/}*.css',
177 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
178 | '<%= yeoman.dist %>/styles/fonts/*'
179 | ]
180 | }
181 | },
182 |
183 | // Reads HTML for usemin blocks to enable smart builds that automatically
184 | // concat, minify and revision files. Creates configurations in memory so
185 | // additional tasks can operate on them
186 | useminPrepare: {
187 | html: '<%= yeoman.app %>/index.html',
188 | options: {
189 | dest: '<%= yeoman.dist %>',
190 | flow: {
191 | html: {
192 | steps: {
193 | js: ['concat', 'uglifyjs'],
194 | css: ['cssmin']
195 | },
196 | post: {}
197 | }
198 | }
199 | }
200 | },
201 |
202 | // Performs rewrites based on filerev and the useminPrepare configuration
203 | usemin: {
204 | html: ['<%= yeoman.dist %>/{,*/}*.html'],
205 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
206 | options: {
207 | assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
208 | }
209 | },
210 |
211 | // The following *-min tasks will produce minified files in the dist folder
212 | // By default, your `index.html`'s will take care of
213 | // minification. These next options are pre-configured if you do not wish
214 | // to use the Usemin blocks.
215 | // cssmin: {
216 | // dist: {
217 | // files: {
218 | // '<%= yeoman.dist %>/styles/main.css': [
219 | // '.tmp/styles/{,*/}*.css'
220 | // ]
221 | // }
222 | // }
223 | // },
224 | // uglify: {
225 | // dist: {
226 | // files: {
227 | // '<%= yeoman.dist %>/scripts/scripts.js': [
228 | // '<%= yeoman.dist %>/scripts/scripts.js'
229 | // ]
230 | // }
231 | // }
232 | // },
233 | // concat: {
234 | // dist: {}
235 | // },
236 |
237 | imagemin: {
238 | dist: {
239 | files: [{
240 | expand: true,
241 | cwd: '<%= yeoman.app %>/images',
242 | src: '{,*/}*.{png,jpg,jpeg,gif}',
243 | dest: '<%= yeoman.dist %>/images'
244 | }]
245 | }
246 | },
247 |
248 | svgmin: {
249 | dist: {
250 | files: [{
251 | expand: true,
252 | cwd: '<%= yeoman.app %>/images',
253 | src: '{,*/}*.svg',
254 | dest: '<%= yeoman.dist %>/images'
255 | }]
256 | }
257 | },
258 |
259 | htmlmin: {
260 | dist: {
261 | options: {
262 | collapseWhitespace: true,
263 | conservativeCollapse: true,
264 | collapseBooleanAttributes: true,
265 | removeCommentsFromCDATA: true,
266 | removeOptionalTags: true
267 | },
268 | files: [{
269 | expand: true,
270 | cwd: '<%= yeoman.dist %>',
271 | src: ['*.html', 'views/{,*/}*.html'],
272 | dest: '<%= yeoman.dist %>'
273 | }]
274 | }
275 | },
276 |
277 | // ng-annotate tries to make the code safe for minification automatically
278 | // by using the Angular long form for dependency injection.
279 | ngAnnotate: {
280 | dist: {
281 | files: [{
282 | expand: true,
283 | cwd: '.tmp/concat/scripts',
284 | src: ['*.js', '!oldieshim.js'],
285 | dest: '.tmp/concat/scripts'
286 | }]
287 | }
288 | },
289 |
290 | // Replace Google CDN references
291 | cdnify: {
292 | dist: {
293 | html: ['<%= yeoman.dist %>/*.html']
294 | }
295 | },
296 |
297 | // Copies remaining files to places other tasks can use
298 | copy: {
299 | dist: {
300 | files: [{
301 | expand: true,
302 | dot: true,
303 | cwd: '<%= yeoman.app %>',
304 | dest: '<%= yeoman.dist %>',
305 | src: [
306 | '*.{ico,png,txt}',
307 | '.htaccess',
308 | '*.html',
309 | 'views/{,*/}*.html',
310 | 'images/{,*/}*.{webp}',
311 | 'fonts/{,*/}*.*'
312 | ]
313 | }, {
314 | expand: true,
315 | cwd: '.tmp/images',
316 | dest: '<%= yeoman.dist %>/images',
317 | src: ['generated/*']
318 | }, {
319 | expand: true,
320 | cwd: 'bower_components/bootstrap/dist',
321 | src: 'fonts/*',
322 | dest: '<%= yeoman.dist %>'
323 | }]
324 | },
325 | styles: {
326 | expand: true,
327 | cwd: '<%= yeoman.app %>/styles',
328 | dest: '.tmp/styles/',
329 | src: '{,*/}*.css'
330 | }
331 | },
332 |
333 | // Run some tasks in parallel to speed up the build process
334 | concurrent: {
335 | server: [
336 | 'copy:styles'
337 | ],
338 | test: [
339 | 'copy:styles'
340 | ],
341 | dist: [
342 | 'copy:styles',
343 | 'imagemin',
344 | 'svgmin'
345 | ]
346 | },
347 |
348 | // Test settings
349 | karma: {
350 | unit: {
351 | configFile: 'test/karma.conf.js',
352 | singleRun: true
353 | }
354 | }
355 | });
356 |
357 |
358 | grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
359 | if (target === 'dist') {
360 | return grunt.task.run(['build', 'connect:dist:keepalive']);
361 | }
362 |
363 | grunt.task.run([
364 | 'clean:server',
365 | 'wiredep',
366 | 'concurrent:server',
367 | 'autoprefixer',
368 | 'connect:livereload',
369 | 'watch'
370 | ]);
371 | });
372 |
373 | grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
374 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
375 | grunt.task.run(['serve:' + target]);
376 | });
377 |
378 | grunt.registerTask('test', [
379 | 'clean:server',
380 | 'concurrent:test',
381 | 'autoprefixer',
382 | 'connect:test',
383 | 'karma'
384 | ]);
385 |
386 | grunt.registerTask('build', [
387 | 'clean:dist',
388 | 'wiredep',
389 | 'useminPrepare',
390 | 'concurrent:dist',
391 | 'autoprefixer',
392 | 'concat',
393 | 'ngAnnotate',
394 | 'copy:dist',
395 | 'cdnify',
396 | 'cssmin',
397 | 'uglify',
398 | 'filerev',
399 | 'usemin',
400 | 'htmlmin'
401 | ]);
402 |
403 | grunt.registerTask('default', [
404 | 'newer:jshint',
405 | 'test',
406 | 'build'
407 | ]);
408 | };
409 |
--------------------------------------------------------------------------------
/public/app/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/public/app/.htaccess:
--------------------------------------------------------------------------------
1 | # Apache Configuration File
2 |
3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access
4 | # to the main server config file (usually called `httpd.conf`), you should add
5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
6 |
7 | # ##############################################################################
8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) #
9 | # ##############################################################################
10 |
11 | # ------------------------------------------------------------------------------
12 | # | Cross-domain AJAX requests |
13 | # ------------------------------------------------------------------------------
14 |
15 | # Enable cross-origin AJAX requests.
16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
17 | # http://enable-cors.org/
18 |
19 | #
20 | # Header set Access-Control-Allow-Origin "*"
21 | #
22 |
23 | # ------------------------------------------------------------------------------
24 | # | CORS-enabled images |
25 | # ------------------------------------------------------------------------------
26 |
27 | # Send the CORS header for images when browsers request it.
28 | # https://developer.mozilla.org/en/CORS_Enabled_Image
29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
31 |
32 |
33 |
34 |
35 | SetEnvIf Origin ":" IS_CORS
36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS
37 |
38 |
39 |
40 |
41 | # ------------------------------------------------------------------------------
42 | # | Web fonts access |
43 | # ------------------------------------------------------------------------------
44 |
45 | # Allow access from all domains for web fonts
46 |
47 |
48 |
49 | Header set Access-Control-Allow-Origin "*"
50 |
51 |
52 |
53 |
54 | # ##############################################################################
55 | # # ERRORS #
56 | # ##############################################################################
57 |
58 | # ------------------------------------------------------------------------------
59 | # | 404 error prevention for non-existing redirected folders |
60 | # ------------------------------------------------------------------------------
61 |
62 | # Prevent Apache from returning a 404 error for a rewrite if a directory
63 | # with the same name does not exist.
64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
65 | # http://www.webmasterworld.com/apache/3808792.htm
66 |
67 | Options -MultiViews
68 |
69 | # ------------------------------------------------------------------------------
70 | # | Custom error messages / pages |
71 | # ------------------------------------------------------------------------------
72 |
73 | # You can customize what Apache returns to the client in case of an error (see
74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
75 |
76 | ErrorDocument 404 /404.html
77 |
78 |
79 | # ##############################################################################
80 | # # INTERNET EXPLORER #
81 | # ##############################################################################
82 |
83 | # ------------------------------------------------------------------------------
84 | # | Better website experience |
85 | # ------------------------------------------------------------------------------
86 |
87 | # Force IE to render pages in the highest available mode in the various
88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
89 |
90 |
91 | Header set X-UA-Compatible "IE=edge"
92 | # `mod_headers` can't match based on the content-type, however, we only
93 | # want to send this header for HTML pages and not for the other resources
94 |
95 | Header unset X-UA-Compatible
96 |
97 |
98 |
99 | # ------------------------------------------------------------------------------
100 | # | Cookie setting from iframes |
101 | # ------------------------------------------------------------------------------
102 |
103 | # Allow cookies to be set from iframes in IE.
104 |
105 | #
106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
107 | #
108 |
109 | # ------------------------------------------------------------------------------
110 | # | Screen flicker |
111 | # ------------------------------------------------------------------------------
112 |
113 | # Stop screen flicker in IE on CSS rollovers (this only works in
114 | # combination with the `ExpiresByType` directives for images from below).
115 |
116 | # BrowserMatch "MSIE" brokenvary=1
117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
118 | # BrowserMatch "Opera" !brokenvary
119 | # SetEnvIf brokenvary 1 force-no-vary
120 |
121 |
122 | # ##############################################################################
123 | # # MIME TYPES AND ENCODING #
124 | # ##############################################################################
125 |
126 | # ------------------------------------------------------------------------------
127 | # | Proper MIME types for all files |
128 | # ------------------------------------------------------------------------------
129 |
130 |
131 |
132 | # Audio
133 | AddType audio/mp4 m4a f4a f4b
134 | AddType audio/ogg oga ogg
135 |
136 | # JavaScript
137 | # Normalize to standard type (it's sniffed in IE anyways):
138 | # http://tools.ietf.org/html/rfc4329#section-7.2
139 | AddType application/javascript js jsonp
140 | AddType application/json json
141 |
142 | # Video
143 | AddType video/mp4 mp4 m4v f4v f4p
144 | AddType video/ogg ogv
145 | AddType video/webm webm
146 | AddType video/x-flv flv
147 |
148 | # Web fonts
149 | AddType application/font-woff woff
150 | AddType application/vnd.ms-fontobject eot
151 |
152 | # Browsers usually ignore the font MIME types and sniff the content,
153 | # however, Chrome shows a warning if other MIME types are used for the
154 | # following fonts.
155 | AddType application/x-font-ttf ttc ttf
156 | AddType font/opentype otf
157 |
158 | # Make SVGZ fonts work on iPad:
159 | # https://twitter.com/FontSquirrel/status/14855840545
160 | AddType image/svg+xml svg svgz
161 | AddEncoding gzip svgz
162 |
163 | # Other
164 | AddType application/octet-stream safariextz
165 | AddType application/x-chrome-extension crx
166 | AddType application/x-opera-extension oex
167 | AddType application/x-shockwave-flash swf
168 | AddType application/x-web-app-manifest+json webapp
169 | AddType application/x-xpinstall xpi
170 | AddType application/xml atom rdf rss xml
171 | AddType image/webp webp
172 | AddType image/x-icon ico
173 | AddType text/cache-manifest appcache manifest
174 | AddType text/vtt vtt
175 | AddType text/x-component htc
176 | AddType text/x-vcard vcf
177 |
178 |
179 |
180 | # ------------------------------------------------------------------------------
181 | # | UTF-8 encoding |
182 | # ------------------------------------------------------------------------------
183 |
184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
185 | AddDefaultCharset utf-8
186 |
187 | # Force UTF-8 for certain file formats.
188 |
189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
190 |
191 |
192 |
193 | # ##############################################################################
194 | # # URL REWRITES #
195 | # ##############################################################################
196 |
197 | # ------------------------------------------------------------------------------
198 | # | Rewrite engine |
199 | # ------------------------------------------------------------------------------
200 |
201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is
202 | # necessary for the following directives to work.
203 |
204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to
205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
207 |
208 | # Also, some cloud hosting services require `RewriteBase` to be set:
209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
210 |
211 |
212 | Options +FollowSymlinks
213 | # Options +SymLinksIfOwnerMatch
214 | RewriteEngine On
215 | # RewriteBase /
216 |
217 |
218 | # ------------------------------------------------------------------------------
219 | # | Suppressing / Forcing the "www." at the beginning of URLs |
220 | # ------------------------------------------------------------------------------
221 |
222 | # The same content should never be available under two different URLs especially
223 | # not with and without "www." at the beginning. This can cause SEO problems
224 | # (duplicate content), therefore, you should choose one of the alternatives and
225 | # redirect the other one.
226 |
227 | # By default option 1 (no "www.") is activated:
228 | # http://no-www.org/faq.php?q=class_b
229 |
230 | # If you'd prefer to use option 2, just comment out all the lines from option 1
231 | # and uncomment the ones from option 2.
232 |
233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
234 |
235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236 |
237 | # Option 1: rewrite www.example.com → example.com
238 |
239 |
240 | RewriteCond %{HTTPS} !=on
241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
243 |
244 |
245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246 |
247 | # Option 2: rewrite example.com → www.example.com
248 |
249 | # Be aware that the following might not be a good idea if you use "real"
250 | # subdomains for certain parts of your website.
251 |
252 | #
253 | # RewriteCond %{HTTPS} !=on
254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
256 | #
257 |
258 |
259 | # ##############################################################################
260 | # # SECURITY #
261 | # ##############################################################################
262 |
263 | # ------------------------------------------------------------------------------
264 | # | Content Security Policy (CSP) |
265 | # ------------------------------------------------------------------------------
266 |
267 | # You can mitigate the risk of cross-site scripting and other content-injection
268 | # attacks by setting a Content Security Policy which whitelists trusted sources
269 | # of content for your site.
270 |
271 | # The example header below allows ONLY scripts that are loaded from the current
272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't
273 | # work as-is for your site!
274 |
275 | # To get all the details you'll need to craft a reasonable policy for your site,
276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
277 | # see the specification: http://w3.org/TR/CSP).
278 |
279 | #
280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
281 | #
282 | # Header unset Content-Security-Policy
283 | #
284 | #
285 |
286 | # ------------------------------------------------------------------------------
287 | # | File access |
288 | # ------------------------------------------------------------------------------
289 |
290 | # Block access to directories without a default document.
291 | # Usually you should leave this uncommented because you shouldn't allow anyone
292 | # to surf through every directory on your server (which may includes rather
293 | # private places like the CMS's directories).
294 |
295 |
296 | Options -Indexes
297 |
298 |
299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 |
301 | # Block access to hidden files and directories.
302 | # This includes directories used by version control systems such as Git and SVN.
303 |
304 |
305 | RewriteCond %{SCRIPT_FILENAME} -d [OR]
306 | RewriteCond %{SCRIPT_FILENAME} -f
307 | RewriteRule "(^|/)\." - [F]
308 |
309 |
310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
311 |
312 | # Block access to backup and source files.
313 | # These files may be left by some text editors and can pose a great security
314 | # danger when anyone has access to them.
315 |
316 |
317 | Order allow,deny
318 | Deny from all
319 | Satisfy All
320 |
321 |
322 | # ------------------------------------------------------------------------------
323 | # | Secure Sockets Layer (SSL) |
324 | # ------------------------------------------------------------------------------
325 |
326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
327 | # prevent `https://www.example.com` when your certificate only allows
328 | # `https://secure.example.com`.
329 |
330 | #
331 | # RewriteCond %{SERVER_PORT} !^443
332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
333 | #
334 |
335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
336 |
337 | # Force client-side SSL redirection.
338 |
339 | # If a user types "example.com" in his browser, the above rule will redirect him
340 | # to the secure version of the site. That still leaves a window of opportunity
341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the
342 | # request. The following header ensures that browser will ONLY connect to your
343 | # server via HTTPS, regardless of what the users type in the address bar.
344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
345 |
346 | #
347 | # Header set Strict-Transport-Security max-age=16070400;
348 | #
349 |
350 | # ------------------------------------------------------------------------------
351 | # | Server software information |
352 | # ------------------------------------------------------------------------------
353 |
354 | # Avoid displaying the exact Apache version number, the description of the
355 | # generic OS-type and the information about Apache's compiled-in modules.
356 |
357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
358 |
359 | # ServerTokens Prod
360 |
361 |
362 | # ##############################################################################
363 | # # WEB PERFORMANCE #
364 | # ##############################################################################
365 |
366 | # ------------------------------------------------------------------------------
367 | # | Compression |
368 | # ------------------------------------------------------------------------------
369 |
370 |
371 |
372 | # Force compression for mangled headers.
373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
374 |
375 |
376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
378 |
379 |
380 |
381 | # Compress all output labeled with one of the following MIME-types
382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
383 | # and can remove the `` and `` lines
384 | # as `AddOutputFilterByType` is still in the core directives).
385 |
386 | AddOutputFilterByType DEFLATE application/atom+xml \
387 | application/javascript \
388 | application/json \
389 | application/rss+xml \
390 | application/vnd.ms-fontobject \
391 | application/x-font-ttf \
392 | application/x-web-app-manifest+json \
393 | application/xhtml+xml \
394 | application/xml \
395 | font/opentype \
396 | image/svg+xml \
397 | image/x-icon \
398 | text/css \
399 | text/html \
400 | text/plain \
401 | text/x-component \
402 | text/xml
403 |
404 |
405 |
406 |
407 | # ------------------------------------------------------------------------------
408 | # | Content transformations |
409 | # ------------------------------------------------------------------------------
410 |
411 | # Prevent some of the mobile network providers from modifying the content of
412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
413 |
414 | #
415 | # Header set Cache-Control "no-transform"
416 | #
417 |
418 | # ------------------------------------------------------------------------------
419 | # | ETag removal |
420 | # ------------------------------------------------------------------------------
421 |
422 | # Since we're sending far-future expires headers (see below), ETags can
423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags.
424 |
425 | # `FileETag None` is not enough for every server.
426 |
427 | Header unset ETag
428 |
429 |
430 | FileETag None
431 |
432 | # ------------------------------------------------------------------------------
433 | # | Expires headers (for better cache control) |
434 | # ------------------------------------------------------------------------------
435 |
436 | # The following expires headers are set pretty far in the future. If you don't
437 | # control versioning with filename-based cache busting, consider lowering the
438 | # cache time for resources like CSS and JS to something like 1 week.
439 |
440 |
441 |
442 | ExpiresActive on
443 | ExpiresDefault "access plus 1 month"
444 |
445 | # CSS
446 | ExpiresByType text/css "access plus 1 year"
447 |
448 | # Data interchange
449 | ExpiresByType application/json "access plus 0 seconds"
450 | ExpiresByType application/xml "access plus 0 seconds"
451 | ExpiresByType text/xml "access plus 0 seconds"
452 |
453 | # Favicon (cannot be renamed!)
454 | ExpiresByType image/x-icon "access plus 1 week"
455 |
456 | # HTML components (HTCs)
457 | ExpiresByType text/x-component "access plus 1 month"
458 |
459 | # HTML
460 | ExpiresByType text/html "access plus 0 seconds"
461 |
462 | # JavaScript
463 | ExpiresByType application/javascript "access plus 1 year"
464 |
465 | # Manifest files
466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
467 | ExpiresByType text/cache-manifest "access plus 0 seconds"
468 |
469 | # Media
470 | ExpiresByType audio/ogg "access plus 1 month"
471 | ExpiresByType image/gif "access plus 1 month"
472 | ExpiresByType image/jpeg "access plus 1 month"
473 | ExpiresByType image/png "access plus 1 month"
474 | ExpiresByType video/mp4 "access plus 1 month"
475 | ExpiresByType video/ogg "access plus 1 month"
476 | ExpiresByType video/webm "access plus 1 month"
477 |
478 | # Web feeds
479 | ExpiresByType application/atom+xml "access plus 1 hour"
480 | ExpiresByType application/rss+xml "access plus 1 hour"
481 |
482 | # Web fonts
483 | ExpiresByType application/font-woff "access plus 1 month"
484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
485 | ExpiresByType application/x-font-ttf "access plus 1 month"
486 | ExpiresByType font/opentype "access plus 1 month"
487 | ExpiresByType image/svg+xml "access plus 1 month"
488 |
489 |
490 |
491 | # ------------------------------------------------------------------------------
492 | # | Filename-based cache busting |
493 | # ------------------------------------------------------------------------------
494 |
495 | # If you're not using a build process to manage your filename version revving,
496 | # you might want to consider enabling the following directives to route all
497 | # requests such as `/css/style.12345.css` to `/css/style.css`.
498 |
499 | # To understand why this is important and a better idea than `*.css?v231`, read:
500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
501 |
502 | #
503 | # RewriteCond %{REQUEST_FILENAME} !-f
504 | # RewriteCond %{REQUEST_FILENAME} !-d
505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
506 | #
507 |
508 | # ------------------------------------------------------------------------------
509 | # | File concatenation |
510 | # ------------------------------------------------------------------------------
511 |
512 | # Allow concatenation from within specific CSS and JS files, e.g.:
513 | # Inside of `script.combined.js` you could have
514 | #
515 | #
516 | # and they would be included into this single file.
517 |
518 | #
519 | #
520 | # Options +Includes
521 | # AddOutputFilterByType INCLUDES application/javascript application/json
522 | # SetOutputFilter INCLUDES
523 | #
524 | #
525 | # Options +Includes
526 | # AddOutputFilterByType INCLUDES text/css
527 | # SetOutputFilter INCLUDES
528 | #
529 | #
530 |
531 | # ------------------------------------------------------------------------------
532 | # | Persistent connections |
533 | # ------------------------------------------------------------------------------
534 |
535 | # Allow multiple requests to be sent over the same TCP connection:
536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
537 |
538 | # Enable if you serve a lot of static content but, be aware of the
539 | # possible disadvantages!
540 |
541 | #
542 | # Header set Connection Keep-Alive
543 | #
544 |
--------------------------------------------------------------------------------
/public/app/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Page Not Found :(
6 |
141 |
142 |
143 |
144 |
Not found :(
145 |
Sorry, but the page you were trying to view does not exist.
146 |
It looks like this was the result of either:
147 |
148 | - a mistyped address
149 | - an out-of-date link
150 |
151 |
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/public/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/app/favicon.ico
--------------------------------------------------------------------------------
/public/app/images/yeoman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/app/images/yeoman.png
--------------------------------------------------------------------------------
/public/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | By @mgechev. Based on this post.
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/public/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/public/app/scripts/adapter.js:
--------------------------------------------------------------------------------
1 | window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
2 | window.RTCIceCandidate = window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate;
3 | window.RTCSessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
4 | window.URL = window.URL || window.mozURL || window.webkitURL;
5 | window.navigator.getUserMedia = window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia;
6 |
7 |
--------------------------------------------------------------------------------
/public/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @ngdoc overview
5 | * @name publicApp
6 | * @description
7 | * # publicApp
8 | *
9 | * Main module of the application.
10 | */
11 |
12 | angular
13 | .module('publicApp', [
14 | 'ngRoute'
15 | ])
16 | .config(function ($routeProvider) {
17 | $routeProvider
18 | .when('/room/:roomId', {
19 | templateUrl: 'views/room.html',
20 | controller: 'RoomCtrl'
21 | })
22 | .when('/room', {
23 | templateUrl: 'views/room.html',
24 | controller: 'RoomCtrl'
25 | })
26 | .otherwise({
27 | redirectTo: '/room'
28 | });
29 | });
30 |
31 | angular.module('publicApp')
32 | .constant('config', {
33 | SIGNALIG_SERVER_URL: undefined
34 | });
35 |
36 | Object.setPrototypeOf = Object.setPrototypeOf || function(obj, proto) {
37 | obj.__proto__ = proto;
38 | return obj;
39 | };
40 |
--------------------------------------------------------------------------------
/public/app/scripts/controllers/room.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @ngdoc function
5 | * @name publicApp.controller:RoomCtrl
6 | * @description
7 | * # RoomCtrl
8 | * Controller of the publicApp
9 | */
10 | angular.module('publicApp')
11 | .controller('RoomCtrl', function ($sce, VideoStream, $location, $routeParams, $scope, Room) {
12 |
13 | if (!window.RTCPeerConnection || !navigator.getUserMedia) {
14 | $scope.error = 'WebRTC is not supported by your browser. You can try the app with Chrome and Firefox.';
15 | return;
16 | }
17 |
18 | var stream;
19 |
20 | VideoStream.get()
21 | .then(function (s) {
22 | stream = s;
23 | Room.init(stream);
24 | stream = URL.createObjectURL(stream);
25 | if (!$routeParams.roomId) {
26 | Room.createRoom()
27 | .then(function (roomId) {
28 | $location.path('/room/' + roomId);
29 | });
30 | } else {
31 | Room.joinRoom($routeParams.roomId);
32 | }
33 | }, function () {
34 | $scope.error = 'No audio/video permissions. Please refresh your browser and allow the audio/video capturing.';
35 | });
36 | $scope.peers = [];
37 | Room.on('peer.stream', function (peer) {
38 | console.log('Client connected, adding new stream');
39 | $scope.peers.push({
40 | id: peer.id,
41 | stream: URL.createObjectURL(peer.stream)
42 | });
43 | });
44 | Room.on('peer.disconnected', function (peer) {
45 | console.log('Client disconnected, removing stream');
46 | $scope.peers = $scope.peers.filter(function (p) {
47 | return p.id !== peer.id;
48 | });
49 | });
50 |
51 | $scope.getLocalVideo = function () {
52 | return $sce.trustAsResourceUrl(stream);
53 | };
54 | });
55 |
--------------------------------------------------------------------------------
/public/app/scripts/directives/videoplayer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @ngdoc directive
5 | * @name publicApp.directive:VideoPlayer
6 | * @description
7 | * # VideoPlayer
8 | */
9 | angular.module('publicApp')
10 | .directive('videoPlayer', function ($sce) {
11 | return {
12 | template: '',
13 | restrict: 'E',
14 | replace: true,
15 | scope: {
16 | vidSrc: '@'
17 | },
18 | link: function (scope) {
19 | console.log('Initializing video-player');
20 | scope.trustSrc = function () {
21 | if (!scope.vidSrc) {
22 | return undefined;
23 | }
24 | return $sce.trustAsResourceUrl(scope.vidSrc);
25 | };
26 | }
27 | };
28 | });
29 |
--------------------------------------------------------------------------------
/public/app/scripts/services/io.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @ngdoc service
5 | * @name publicApp.Io
6 | * @description
7 | * # Io
8 | * Factory in the publicApp.
9 | */
10 | angular.module('publicApp')
11 | .factory('Io', function () {
12 | if (typeof io === 'undefined') {
13 | throw new Error('Socket.io required');
14 | }
15 | return io;
16 | });
17 |
--------------------------------------------------------------------------------
/public/app/scripts/services/room.js:
--------------------------------------------------------------------------------
1 | /* global RTCIceCandidate, RTCSessionDescription, RTCPeerConnection, EventEmitter */
2 | 'use strict';
3 |
4 | /**
5 | * @ngdoc service
6 | * @name publicApp.Room
7 | * @description
8 | * # Room
9 | * Factory in the publicApp.
10 | */
11 | angular.module('publicApp')
12 | .factory('Room', function ($rootScope, $q, Io, config) {
13 |
14 | var iceConfig = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }]},
15 | peerConnections = {},
16 | currentId, roomId,
17 | stream;
18 |
19 | function getPeerConnection(id) {
20 | if (peerConnections[id]) {
21 | return peerConnections[id];
22 | }
23 | var pc = new RTCPeerConnection(iceConfig);
24 | peerConnections[id] = pc;
25 | pc.addStream(stream);
26 | pc.onicecandidate = function (evnt) {
27 | socket.emit('msg', { by: currentId, to: id, ice: evnt.candidate, type: 'ice' });
28 | };
29 | pc.onaddstream = function (evnt) {
30 | console.log('Received new stream');
31 | api.trigger('peer.stream', [{
32 | id: id,
33 | stream: evnt.stream
34 | }]);
35 | if (!$rootScope.$$digest) {
36 | $rootScope.$apply();
37 | }
38 | };
39 | return pc;
40 | }
41 |
42 | function makeOffer(id) {
43 | var pc = getPeerConnection(id);
44 | pc.createOffer(function (sdp) {
45 | pc.setLocalDescription(sdp);
46 | console.log('Creating an offer for', id);
47 | socket.emit('msg', { by: currentId, to: id, sdp: sdp, type: 'sdp-offer' });
48 | }, function (e) {
49 | console.log(e);
50 | },
51 | { mandatory: { offerToReceiveVideo: true, offerToReceiveAudio: true }});
52 | }
53 |
54 | function handleMessage(data) {
55 | var pc = getPeerConnection(data.by);
56 | switch (data.type) {
57 | case 'sdp-offer':
58 | pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
59 | console.log('Setting remote description by offer');
60 | pc.createAnswer(function (sdp) {
61 | pc.setLocalDescription(sdp);
62 | socket.emit('msg', { by: currentId, to: data.by, sdp: sdp, type: 'sdp-answer' });
63 | }, function (e) {
64 | console.log(e);
65 | });
66 | }, function (e) {
67 | console.log(e);
68 | });
69 | break;
70 | case 'sdp-answer':
71 | pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function () {
72 | console.log('Setting remote description by answer');
73 | }, function (e) {
74 | console.error(e);
75 | });
76 | break;
77 | case 'ice':
78 | if (data.ice) {
79 | console.log('Adding ice candidates');
80 | pc.addIceCandidate(new RTCIceCandidate(data.ice));
81 | }
82 | break;
83 | }
84 | }
85 |
86 | var socket = Io.connect(config.SIGNALIG_SERVER_URL),
87 | connected = false;
88 |
89 | function addHandlers(socket) {
90 | socket.on('peer.connected', function (params) {
91 | makeOffer(params.id);
92 | });
93 | socket.on('peer.disconnected', function (data) {
94 | api.trigger('peer.disconnected', [data]);
95 | if (!$rootScope.$$digest) {
96 | $rootScope.$apply();
97 | }
98 | });
99 | socket.on('msg', function (data) {
100 | handleMessage(data);
101 | });
102 | }
103 |
104 | var api = {
105 | joinRoom: function (r) {
106 | if (!connected) {
107 | socket.emit('init', { room: r }, function (roomid, id) {
108 | currentId = id;
109 | roomId = roomid;
110 | });
111 | connected = true;
112 | }
113 | },
114 | createRoom: function () {
115 | var d = $q.defer();
116 | socket.emit('init', null, function (roomid, id) {
117 | d.resolve(roomid);
118 | roomId = roomid;
119 | currentId = id;
120 | connected = true;
121 | });
122 | return d.promise;
123 | },
124 | init: function (s) {
125 | stream = s;
126 | }
127 | };
128 | EventEmitter.call(api);
129 | Object.setPrototypeOf(api, EventEmitter.prototype);
130 |
131 | addHandlers(socket);
132 | return api;
133 | });
134 |
--------------------------------------------------------------------------------
/public/app/scripts/services/videostream.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * @ngdoc service
5 | * @name publicApp.VideoStream
6 | * @description
7 | * # VideoStream
8 | * Factory in the publicApp.
9 | */
10 | angular.module('publicApp')
11 | .factory('VideoStream', function ($q) {
12 | var stream;
13 | return {
14 | get: function () {
15 | if (stream) {
16 | return $q.when(stream);
17 | } else {
18 | var d = $q.defer();
19 | navigator.getUserMedia({
20 | video: true,
21 | audio: true
22 | }, function (s) {
23 | stream = s;
24 | d.resolve(stream);
25 | }, function (e) {
26 | d.reject(e);
27 | });
28 | return d.promise;
29 | }
30 | }
31 | };
32 | });
33 |
--------------------------------------------------------------------------------
/public/app/styles/main.css:
--------------------------------------------------------------------------------
1 | /* Space out content a bit */
2 | body {
3 | padding-top: 20px;
4 | padding-bottom: 20px;
5 | }
6 |
7 | /* Everything but the jumbotron gets side spacing for mobile first views */
8 | .header,
9 | .marketing,
10 | .footer {
11 | padding-left: 15px;
12 | padding-right: 15px;
13 | }
14 |
15 | /* Custom page header */
16 | .header {
17 | border-bottom: 1px solid #e5e5e5;
18 | }
19 | /* Make the masthead heading the same height as the navigation */
20 | .header h3 {
21 | margin-top: 0;
22 | margin-bottom: 0;
23 | line-height: 40px;
24 | padding-bottom: 19px;
25 | }
26 |
27 | /* Custom page footer */
28 | .footer {
29 | padding-top: 19px;
30 | color: #777;
31 | border-top: 1px solid #e5e5e5;
32 | }
33 |
34 | /* Customize container */
35 | @media (min-width: 768px) {
36 | .container {
37 | max-width: 730px;
38 | }
39 | }
40 | .container-narrow > hr {
41 | margin: 30px 0;
42 | }
43 |
44 | /* Main marketing message and sign up button */
45 | .jumbotron {
46 | text-align: center;
47 | border-bottom: 1px solid #e5e5e5;
48 | }
49 | .jumbotron .btn {
50 | font-size: 21px;
51 | padding: 14px 24px;
52 | }
53 |
54 | /* Supporting marketing content */
55 | .marketing {
56 | margin: 40px 0;
57 | }
58 | .marketing p + h4 {
59 | margin-top: 28px;
60 | }
61 |
62 | /* Responsive: Portrait tablets and up */
63 | @media screen and (min-width: 768px) {
64 | /* Remove the padding we set earlier */
65 | .header,
66 | .marketing,
67 | .footer {
68 | padding-left: 0;
69 | padding-right: 0;
70 | }
71 | /* Space out the masthead */
72 | .header {
73 | margin-bottom: 30px;
74 | }
75 | /* Remove the bottom border on the jumbotron for visual effect */
76 | .jumbotron {
77 | border-bottom: 0;
78 | }
79 | }
80 |
81 | body {
82 | background-color: #000;
83 | color: #fff;
84 | }
85 |
86 | video {
87 | width: 100%;
88 | border-radius: 10px;
89 | }
90 |
91 | .video-wrapper:after {
92 | content: ' ';
93 | clear: both;
94 | display: block;
95 | }
96 |
--------------------------------------------------------------------------------
/public/app/views/room.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Allow the browser to use your web cam and after that share the URL from the address bar with the people you want to talk with.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/public/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "public",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "^1.3.0",
6 | "json3": "^3.3.0",
7 | "es5-shim": "^4.0.0",
8 | "bootstrap": "^3.2.0",
9 | "angular-route": "^1.3.0",
10 | "eventEmitter": "~4.2.11"
11 | },
12 | "devDependencies": {
13 | "angular-mocks": "~1.3.0",
14 | "angular-scenario": "~1.3.0"
15 | },
16 | "appPath": "app"
17 | }
18 |
--------------------------------------------------------------------------------
/public/dist/.htaccess:
--------------------------------------------------------------------------------
1 | # Apache Configuration File
2 |
3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access
4 | # to the main server config file (usually called `httpd.conf`), you should add
5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
6 |
7 | # ##############################################################################
8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) #
9 | # ##############################################################################
10 |
11 | # ------------------------------------------------------------------------------
12 | # | Cross-domain AJAX requests |
13 | # ------------------------------------------------------------------------------
14 |
15 | # Enable cross-origin AJAX requests.
16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
17 | # http://enable-cors.org/
18 |
19 | #
20 | # Header set Access-Control-Allow-Origin "*"
21 | #
22 |
23 | # ------------------------------------------------------------------------------
24 | # | CORS-enabled images |
25 | # ------------------------------------------------------------------------------
26 |
27 | # Send the CORS header for images when browsers request it.
28 | # https://developer.mozilla.org/en/CORS_Enabled_Image
29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
31 |
32 |
33 |
34 |
35 | SetEnvIf Origin ":" IS_CORS
36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS
37 |
38 |
39 |
40 |
41 | # ------------------------------------------------------------------------------
42 | # | Web fonts access |
43 | # ------------------------------------------------------------------------------
44 |
45 | # Allow access from all domains for web fonts
46 |
47 |
48 |
49 | Header set Access-Control-Allow-Origin "*"
50 |
51 |
52 |
53 |
54 | # ##############################################################################
55 | # # ERRORS #
56 | # ##############################################################################
57 |
58 | # ------------------------------------------------------------------------------
59 | # | 404 error prevention for non-existing redirected folders |
60 | # ------------------------------------------------------------------------------
61 |
62 | # Prevent Apache from returning a 404 error for a rewrite if a directory
63 | # with the same name does not exist.
64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
65 | # http://www.webmasterworld.com/apache/3808792.htm
66 |
67 | Options -MultiViews
68 |
69 | # ------------------------------------------------------------------------------
70 | # | Custom error messages / pages |
71 | # ------------------------------------------------------------------------------
72 |
73 | # You can customize what Apache returns to the client in case of an error (see
74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.:
75 |
76 | ErrorDocument 404 /404.html
77 |
78 |
79 | # ##############################################################################
80 | # # INTERNET EXPLORER #
81 | # ##############################################################################
82 |
83 | # ------------------------------------------------------------------------------
84 | # | Better website experience |
85 | # ------------------------------------------------------------------------------
86 |
87 | # Force IE to render pages in the highest available mode in the various
88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf.
89 |
90 |
91 | Header set X-UA-Compatible "IE=edge"
92 | # `mod_headers` can't match based on the content-type, however, we only
93 | # want to send this header for HTML pages and not for the other resources
94 |
95 | Header unset X-UA-Compatible
96 |
97 |
98 |
99 | # ------------------------------------------------------------------------------
100 | # | Cookie setting from iframes |
101 | # ------------------------------------------------------------------------------
102 |
103 | # Allow cookies to be set from iframes in IE.
104 |
105 | #
106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
107 | #
108 |
109 | # ------------------------------------------------------------------------------
110 | # | Screen flicker |
111 | # ------------------------------------------------------------------------------
112 |
113 | # Stop screen flicker in IE on CSS rollovers (this only works in
114 | # combination with the `ExpiresByType` directives for images from below).
115 |
116 | # BrowserMatch "MSIE" brokenvary=1
117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
118 | # BrowserMatch "Opera" !brokenvary
119 | # SetEnvIf brokenvary 1 force-no-vary
120 |
121 |
122 | # ##############################################################################
123 | # # MIME TYPES AND ENCODING #
124 | # ##############################################################################
125 |
126 | # ------------------------------------------------------------------------------
127 | # | Proper MIME types for all files |
128 | # ------------------------------------------------------------------------------
129 |
130 |
131 |
132 | # Audio
133 | AddType audio/mp4 m4a f4a f4b
134 | AddType audio/ogg oga ogg
135 |
136 | # JavaScript
137 | # Normalize to standard type (it's sniffed in IE anyways):
138 | # http://tools.ietf.org/html/rfc4329#section-7.2
139 | AddType application/javascript js jsonp
140 | AddType application/json json
141 |
142 | # Video
143 | AddType video/mp4 mp4 m4v f4v f4p
144 | AddType video/ogg ogv
145 | AddType video/webm webm
146 | AddType video/x-flv flv
147 |
148 | # Web fonts
149 | AddType application/font-woff woff
150 | AddType application/vnd.ms-fontobject eot
151 |
152 | # Browsers usually ignore the font MIME types and sniff the content,
153 | # however, Chrome shows a warning if other MIME types are used for the
154 | # following fonts.
155 | AddType application/x-font-ttf ttc ttf
156 | AddType font/opentype otf
157 |
158 | # Make SVGZ fonts work on iPad:
159 | # https://twitter.com/FontSquirrel/status/14855840545
160 | AddType image/svg+xml svg svgz
161 | AddEncoding gzip svgz
162 |
163 | # Other
164 | AddType application/octet-stream safariextz
165 | AddType application/x-chrome-extension crx
166 | AddType application/x-opera-extension oex
167 | AddType application/x-shockwave-flash swf
168 | AddType application/x-web-app-manifest+json webapp
169 | AddType application/x-xpinstall xpi
170 | AddType application/xml atom rdf rss xml
171 | AddType image/webp webp
172 | AddType image/x-icon ico
173 | AddType text/cache-manifest appcache manifest
174 | AddType text/vtt vtt
175 | AddType text/x-component htc
176 | AddType text/x-vcard vcf
177 |
178 |
179 |
180 | # ------------------------------------------------------------------------------
181 | # | UTF-8 encoding |
182 | # ------------------------------------------------------------------------------
183 |
184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
185 | AddDefaultCharset utf-8
186 |
187 | # Force UTF-8 for certain file formats.
188 |
189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml
190 |
191 |
192 |
193 | # ##############################################################################
194 | # # URL REWRITES #
195 | # ##############################################################################
196 |
197 | # ------------------------------------------------------------------------------
198 | # | Rewrite engine |
199 | # ------------------------------------------------------------------------------
200 |
201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is
202 | # necessary for the following directives to work.
203 |
204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to
205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the
206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
207 |
208 | # Also, some cloud hosting services require `RewriteBase` to be set:
209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
210 |
211 |
212 | Options +FollowSymlinks
213 | # Options +SymLinksIfOwnerMatch
214 | RewriteEngine On
215 | # RewriteBase /
216 |
217 |
218 | # ------------------------------------------------------------------------------
219 | # | Suppressing / Forcing the "www." at the beginning of URLs |
220 | # ------------------------------------------------------------------------------
221 |
222 | # The same content should never be available under two different URLs especially
223 | # not with and without "www." at the beginning. This can cause SEO problems
224 | # (duplicate content), therefore, you should choose one of the alternatives and
225 | # redirect the other one.
226 |
227 | # By default option 1 (no "www.") is activated:
228 | # http://no-www.org/faq.php?q=class_b
229 |
230 | # If you'd prefer to use option 2, just comment out all the lines from option 1
231 | # and uncomment the ones from option 2.
232 |
233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
234 |
235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236 |
237 | # Option 1: rewrite www.example.com → example.com
238 |
239 |
240 | RewriteCond %{HTTPS} !=on
241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
243 |
244 |
245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246 |
247 | # Option 2: rewrite example.com → www.example.com
248 |
249 | # Be aware that the following might not be a good idea if you use "real"
250 | # subdomains for certain parts of your website.
251 |
252 | #
253 | # RewriteCond %{HTTPS} !=on
254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
256 | #
257 |
258 |
259 | # ##############################################################################
260 | # # SECURITY #
261 | # ##############################################################################
262 |
263 | # ------------------------------------------------------------------------------
264 | # | Content Security Policy (CSP) |
265 | # ------------------------------------------------------------------------------
266 |
267 | # You can mitigate the risk of cross-site scripting and other content-injection
268 | # attacks by setting a Content Security Policy which whitelists trusted sources
269 | # of content for your site.
270 |
271 | # The example header below allows ONLY scripts that are loaded from the current
272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't
273 | # work as-is for your site!
274 |
275 | # To get all the details you'll need to craft a reasonable policy for your site,
276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or
277 | # see the specification: http://w3.org/TR/CSP).
278 |
279 | #
280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
281 | #
282 | # Header unset Content-Security-Policy
283 | #
284 | #
285 |
286 | # ------------------------------------------------------------------------------
287 | # | File access |
288 | # ------------------------------------------------------------------------------
289 |
290 | # Block access to directories without a default document.
291 | # Usually you should leave this uncommented because you shouldn't allow anyone
292 | # to surf through every directory on your server (which may includes rather
293 | # private places like the CMS's directories).
294 |
295 |
296 | Options -Indexes
297 |
298 |
299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
300 |
301 | # Block access to hidden files and directories.
302 | # This includes directories used by version control systems such as Git and SVN.
303 |
304 |
305 | RewriteCond %{SCRIPT_FILENAME} -d [OR]
306 | RewriteCond %{SCRIPT_FILENAME} -f
307 | RewriteRule "(^|/)\." - [F]
308 |
309 |
310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
311 |
312 | # Block access to backup and source files.
313 | # These files may be left by some text editors and can pose a great security
314 | # danger when anyone has access to them.
315 |
316 |
317 | Order allow,deny
318 | Deny from all
319 | Satisfy All
320 |
321 |
322 | # ------------------------------------------------------------------------------
323 | # | Secure Sockets Layer (SSL) |
324 | # ------------------------------------------------------------------------------
325 |
326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.:
327 | # prevent `https://www.example.com` when your certificate only allows
328 | # `https://secure.example.com`.
329 |
330 | #
331 | # RewriteCond %{SERVER_PORT} !^443
332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L]
333 | #
334 |
335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
336 |
337 | # Force client-side SSL redirection.
338 |
339 | # If a user types "example.com" in his browser, the above rule will redirect him
340 | # to the secure version of the site. That still leaves a window of opportunity
341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the
342 | # request. The following header ensures that browser will ONLY connect to your
343 | # server via HTTPS, regardless of what the users type in the address bar.
344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
345 |
346 | #
347 | # Header set Strict-Transport-Security max-age=16070400;
348 | #
349 |
350 | # ------------------------------------------------------------------------------
351 | # | Server software information |
352 | # ------------------------------------------------------------------------------
353 |
354 | # Avoid displaying the exact Apache version number, the description of the
355 | # generic OS-type and the information about Apache's compiled-in modules.
356 |
357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
358 |
359 | # ServerTokens Prod
360 |
361 |
362 | # ##############################################################################
363 | # # WEB PERFORMANCE #
364 | # ##############################################################################
365 |
366 | # ------------------------------------------------------------------------------
367 | # | Compression |
368 | # ------------------------------------------------------------------------------
369 |
370 |
371 |
372 | # Force compression for mangled headers.
373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping
374 |
375 |
376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
378 |
379 |
380 |
381 | # Compress all output labeled with one of the following MIME-types
382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
383 | # and can remove the `` and `` lines
384 | # as `AddOutputFilterByType` is still in the core directives).
385 |
386 | AddOutputFilterByType DEFLATE application/atom+xml \
387 | application/javascript \
388 | application/json \
389 | application/rss+xml \
390 | application/vnd.ms-fontobject \
391 | application/x-font-ttf \
392 | application/x-web-app-manifest+json \
393 | application/xhtml+xml \
394 | application/xml \
395 | font/opentype \
396 | image/svg+xml \
397 | image/x-icon \
398 | text/css \
399 | text/html \
400 | text/plain \
401 | text/x-component \
402 | text/xml
403 |
404 |
405 |
406 |
407 | # ------------------------------------------------------------------------------
408 | # | Content transformations |
409 | # ------------------------------------------------------------------------------
410 |
411 | # Prevent some of the mobile network providers from modifying the content of
412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
413 |
414 | #
415 | # Header set Cache-Control "no-transform"
416 | #
417 |
418 | # ------------------------------------------------------------------------------
419 | # | ETag removal |
420 | # ------------------------------------------------------------------------------
421 |
422 | # Since we're sending far-future expires headers (see below), ETags can
423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags.
424 |
425 | # `FileETag None` is not enough for every server.
426 |
427 | Header unset ETag
428 |
429 |
430 | FileETag None
431 |
432 | # ------------------------------------------------------------------------------
433 | # | Expires headers (for better cache control) |
434 | # ------------------------------------------------------------------------------
435 |
436 | # The following expires headers are set pretty far in the future. If you don't
437 | # control versioning with filename-based cache busting, consider lowering the
438 | # cache time for resources like CSS and JS to something like 1 week.
439 |
440 |
441 |
442 | ExpiresActive on
443 | ExpiresDefault "access plus 1 month"
444 |
445 | # CSS
446 | ExpiresByType text/css "access plus 1 year"
447 |
448 | # Data interchange
449 | ExpiresByType application/json "access plus 0 seconds"
450 | ExpiresByType application/xml "access plus 0 seconds"
451 | ExpiresByType text/xml "access plus 0 seconds"
452 |
453 | # Favicon (cannot be renamed!)
454 | ExpiresByType image/x-icon "access plus 1 week"
455 |
456 | # HTML components (HTCs)
457 | ExpiresByType text/x-component "access plus 1 month"
458 |
459 | # HTML
460 | ExpiresByType text/html "access plus 0 seconds"
461 |
462 | # JavaScript
463 | ExpiresByType application/javascript "access plus 1 year"
464 |
465 | # Manifest files
466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
467 | ExpiresByType text/cache-manifest "access plus 0 seconds"
468 |
469 | # Media
470 | ExpiresByType audio/ogg "access plus 1 month"
471 | ExpiresByType image/gif "access plus 1 month"
472 | ExpiresByType image/jpeg "access plus 1 month"
473 | ExpiresByType image/png "access plus 1 month"
474 | ExpiresByType video/mp4 "access plus 1 month"
475 | ExpiresByType video/ogg "access plus 1 month"
476 | ExpiresByType video/webm "access plus 1 month"
477 |
478 | # Web feeds
479 | ExpiresByType application/atom+xml "access plus 1 hour"
480 | ExpiresByType application/rss+xml "access plus 1 hour"
481 |
482 | # Web fonts
483 | ExpiresByType application/font-woff "access plus 1 month"
484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
485 | ExpiresByType application/x-font-ttf "access plus 1 month"
486 | ExpiresByType font/opentype "access plus 1 month"
487 | ExpiresByType image/svg+xml "access plus 1 month"
488 |
489 |
490 |
491 | # ------------------------------------------------------------------------------
492 | # | Filename-based cache busting |
493 | # ------------------------------------------------------------------------------
494 |
495 | # If you're not using a build process to manage your filename version revving,
496 | # you might want to consider enabling the following directives to route all
497 | # requests such as `/css/style.12345.css` to `/css/style.css`.
498 |
499 | # To understand why this is important and a better idea than `*.css?v231`, read:
500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
501 |
502 | #
503 | # RewriteCond %{REQUEST_FILENAME} !-f
504 | # RewriteCond %{REQUEST_FILENAME} !-d
505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
506 | #
507 |
508 | # ------------------------------------------------------------------------------
509 | # | File concatenation |
510 | # ------------------------------------------------------------------------------
511 |
512 | # Allow concatenation from within specific CSS and JS files, e.g.:
513 | # Inside of `script.combined.js` you could have
514 | #
515 | #
516 | # and they would be included into this single file.
517 |
518 | #
519 | #
520 | # Options +Includes
521 | # AddOutputFilterByType INCLUDES application/javascript application/json
522 | # SetOutputFilter INCLUDES
523 | #
524 | #
525 | # Options +Includes
526 | # AddOutputFilterByType INCLUDES text/css
527 | # SetOutputFilter INCLUDES
528 | #
529 | #
530 |
531 | # ------------------------------------------------------------------------------
532 | # | Persistent connections |
533 | # ------------------------------------------------------------------------------
534 |
535 | # Allow multiple requests to be sent over the same TCP connection:
536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive.
537 |
538 | # Enable if you serve a lot of static content but, be aware of the
539 | # possible disadvantages!
540 |
541 | #
542 | # Header set Connection Keep-Alive
543 | #
544 |
--------------------------------------------------------------------------------
/public/dist/404.html:
--------------------------------------------------------------------------------
1 | Page Not Found :( Not found :(
Sorry, but the page you were trying to view does not exist.
It looks like this was the result of either:
- a mistyped address
- an out-of-date link
--------------------------------------------------------------------------------
/public/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/dist/favicon.ico
--------------------------------------------------------------------------------
/public/dist/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/dist/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/dist/fonts/glyphicons-halflings-regular.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/public/dist/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/dist/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/dist/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/dist/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/public/dist/images/yeoman.d2754b85.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mgechev/angular-webrtc/9ed1fb4bce1bd09bca5e694fe1fb4cec8941836e/public/dist/images/yeoman.d2754b85.png
--------------------------------------------------------------------------------
/public/dist/index.html:
--------------------------------------------------------------------------------
1 | By @mgechev. Based on this post.
--------------------------------------------------------------------------------
/public/dist/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/public/dist/scripts/scripts.45ae9c72.js:
--------------------------------------------------------------------------------
1 | window.RTCPeerConnection=window.RTCPeerConnection||window.webkitRTCPeerConnection||window.mozRTCPeerConnection,window.RTCIceCandidate=window.RTCIceCandidate||window.mozRTCIceCandidate||window.webkitRTCIceCandidate,window.RTCSessionDescription=window.RTCSessionDescription||window.mozRTCSessionDescription||window.webkitRTCSessionDescription,window.URL=window.URL||window.mozURL||window.webkitURL,window.navigator.getUserMedia=window.navigator.getUserMedia||window.navigator.webkitGetUserMedia||window.navigator.mozGetUserMedia,angular.module("publicApp",["ngRoute"]).config(["$routeProvider",function(a){a.when("/room/:roomId",{templateUrl:"views/room.html",controller:"RoomCtrl"}).when("/room",{templateUrl:"views/room.html",controller:"RoomCtrl"}).otherwise({redirectTo:"/room"})}]),angular.module("publicApp").constant("config",{SIGNALIG_SERVER_URL:void 0}),Object.setPrototypeOf=Object.setPrototypeOf||function(a,b){return a.__proto__=b,a},angular.module("publicApp").controller("RoomCtrl",["$sce","VideoStream","$location","$routeParams","$scope","Room",function(a,b,c,d,e,f){if(!window.RTCPeerConnection||!navigator.getUserMedia)return void(e.error="WebRTC is not supported by your browser. You can try the app with Chrome and Firefox.");var g;b.get().then(function(a){g=a,f.init(g),g=URL.createObjectURL(g),d.roomId?f.joinRoom(d.roomId):f.createRoom().then(function(a){c.path("/room/"+a)})},function(){e.error="No audio/video permissions. Please refresh your browser and allow the audio/video capturing."}),e.peers=[],f.on("peer.stream",function(a){console.log("Client connected, adding new stream"),e.peers.push({id:a.id,stream:URL.createObjectURL(a.stream)})}),f.on("peer.disconnected",function(a){console.log("Client disconnected, removing stream"),e.peers=e.peers.filter(function(b){return b.id!==a.id})}),e.getLocalVideo=function(){return a.trustAsResourceUrl(g)}}]),angular.module("publicApp").factory("Room",["$rootScope","$q","Io","config",function(a,b,c,d){function e(b){if(m[b])return m[b];var c=new RTCPeerConnection(l);return m[b]=c,c.addStream(k),c.onicecandidate=function(a){n.emit("msg",{by:i,to:b,ice:a.candidate,type:"ice"})},c.onaddstream=function(c){console.log("Received new stream"),p.trigger("peer.stream",[{id:b,stream:c.stream}]),a.$$digest||a.$apply()},c}function f(a){var b=e(a);b.createOffer(function(c){b.setLocalDescription(c),console.log("Creating an offer for",a),n.emit("msg",{by:i,to:a,sdp:c,type:"sdp-offer"})},function(a){console.log(a)},{mandatory:{OfferToReceiveVideo:!0,OfferToReceiveAudio:!0}})}function g(a){var b=e(a.by);switch(a.type){case"sdp-offer":b.setRemoteDescription(new RTCSessionDescription(a.sdp),function(){console.log("Setting remote description by offer"),b.createAnswer(function(c){b.setLocalDescription(c),n.emit("msg",{by:i,to:a.by,sdp:c,type:"sdp-answer"})})});break;case"sdp-answer":b.setRemoteDescription(new RTCSessionDescription(a.sdp),function(){console.log("Setting remote description by answer")},function(a){console.error(a)});break;case"ice":a.ice&&(console.log("Adding ice candidates"),b.addIceCandidate(new RTCIceCandidate(a.ice)))}}function h(b){b.on("peer.connected",function(a){f(a.id)}),b.on("peer.disconnected",function(b){p.trigger("peer.disconnected",[b]),a.$$digest||a.$apply()}),b.on("msg",function(a){g(a)})}var i,j,k,l={iceServers:[{url:"stun:stun.l.google.com:19302"}]},m={},n=c.connect(d.SIGNALIG_SERVER_URL),o=!1,p={joinRoom:function(a){o||(n.emit("init",{room:a},function(a,b){i=b,j=a}),o=!0)},createRoom:function(){var a=b.defer();return n.emit("init",null,function(b,c){a.resolve(b),j=b,i=c,o=!0}),a.promise},init:function(a){k=a}};return EventEmitter.call(p),Object.setPrototypeOf(p,EventEmitter.prototype),h(n),p}]),angular.module("publicApp").factory("Io",function(){if("undefined"==typeof io)throw new Error("Socket.io required");return io}),angular.module("publicApp").factory("VideoStream",["$q",function(a){var b;return{get:function(){if(b)return a.when(b);var c=a.defer();return navigator.getUserMedia({video:!0,audio:!0},function(a){b=a,c.resolve(b)},function(a){c.reject(a)}),c.promise}}}]),angular.module("publicApp").directive("videoPlayer",["$sce",function(a){return{template:'',restrict:"E",replace:!0,scope:{vidSrc:"@"},link:function(b){console.log("Initializing video-player"),b.trustSrc=function(){return b.vidSrc?a.trustAsResourceUrl(b.vidSrc):void 0}}}}]);
--------------------------------------------------------------------------------
/public/dist/styles/main.d8d6dc72.css:
--------------------------------------------------------------------------------
1 | body{padding-top:20px;padding-bottom:20px}.footer,.header,.marketing{padding-left:15px;padding-right:15px}.header{border-bottom:1px solid #e5e5e5}.header h3{margin-top:0;margin-bottom:0;line-height:40px;padding-bottom:19px}.footer{padding-top:19px;color:#777;border-top:1px solid #e5e5e5}@media (min-width:768px){.container{max-width:730px}}.container-narrow>hr{margin:30px 0}.jumbotron{text-align:center;border-bottom:1px solid #e5e5e5}.jumbotron .btn{font-size:21px;padding:14px 24px}.marketing{margin:40px 0}.marketing p+h4{margin-top:28px}@media screen and (min-width:768px){.footer,.header,.marketing{padding-left:0;padding-right:0}.header{margin-bottom:30px}.jumbotron{border-bottom:0}}body{background-color:#000;color:#fff}video{width:100%;border-radius:10px}.video-wrapper:after{content:' ';clear:both;display:block}
--------------------------------------------------------------------------------
/public/dist/views/room.html:
--------------------------------------------------------------------------------
1 | Allow the browser to use your web cam and after that share the URL from the address bar with the people you want to talk with.
--------------------------------------------------------------------------------
/public/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "public",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {
6 | "grunt": "^0.4.1",
7 | "grunt-autoprefixer": "^0.7.3",
8 | "grunt-concurrent": "^0.5.0",
9 | "grunt-contrib-clean": "^0.5.0",
10 | "grunt-contrib-concat": "^0.4.0",
11 | "grunt-contrib-connect": "^0.7.1",
12 | "grunt-contrib-copy": "^0.5.0",
13 | "grunt-contrib-cssmin": "^0.9.0",
14 | "grunt-contrib-htmlmin": "^0.3.0",
15 | "grunt-contrib-imagemin": "^0.8.1",
16 | "grunt-contrib-jshint": "^0.10.0",
17 | "grunt-contrib-uglify": "^0.4.0",
18 | "grunt-contrib-watch": "^0.6.1",
19 | "grunt-filerev": "^0.2.1",
20 | "grunt-google-cdn": "^0.4.0",
21 | "grunt-karma": "^0.9.0",
22 | "grunt-newer": "^0.7.0",
23 | "grunt-ng-annotate": "^0.4.0",
24 | "grunt-svgmin": "^0.4.0",
25 | "grunt-usemin": "^2.1.1",
26 | "grunt-wiredep": "^1.7.0",
27 | "jasmine-core": "^2.1.3",
28 | "jshint-stylish": "^0.2.0",
29 | "karma": "^0.12.28",
30 | "karma-jasmine": "^0.3.2",
31 | "karma-phantomjs-launcher": "^0.1.4",
32 | "load-grunt-tasks": "^0.4.0",
33 | "time-grunt": "^0.3.1"
34 | },
35 | "engines": {
36 | "node": ">=0.10.0"
37 | },
38 | "scripts": {
39 | "test": "grunt test"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/public/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "after": false,
23 | "afterEach": false,
24 | "angular": false,
25 | "before": false,
26 | "beforeEach": false,
27 | "browser": false,
28 | "describe": false,
29 | "expect": false,
30 | "inject": false,
31 | "it": false,
32 | "jasmine": false,
33 | "spyOn": false
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/public/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.12/config/configuration-file.html
3 | // Generated on 2014-12-22 using
4 | // generator-karma 0.8.3
5 |
6 | module.exports = function(config) {
7 | 'use strict';
8 |
9 | config.set({
10 | // enable / disable watching file and executing tests whenever any file changes
11 | autoWatch: true,
12 |
13 | // base path, that will be used to resolve files and exclude
14 | basePath: '../',
15 |
16 | // testing framework to use (jasmine/mocha/qunit/...)
17 | frameworks: ['jasmine'],
18 |
19 | // list of files / patterns to load in the browser
20 | files: [
21 | 'bower_components/angular/angular.js',
22 | 'bower_components/angular-mocks/angular-mocks.js',
23 | 'bower_components/angular-route/angular-route.js',
24 | 'app/scripts/**/*.js',
25 | 'test/mock/**/*.js',
26 | 'test/spec/**/*.js'
27 | ],
28 |
29 | // list of files / patterns to exclude
30 | exclude: [],
31 |
32 | // web server port
33 | port: 8080,
34 |
35 | // Start these browsers, currently available:
36 | // - Chrome
37 | // - ChromeCanary
38 | // - Firefox
39 | // - Opera
40 | // - Safari (only Mac)
41 | // - PhantomJS
42 | // - IE (only Windows)
43 | browsers: [
44 | 'PhantomJS'
45 | ],
46 |
47 | // Which plugins to enable
48 | plugins: [
49 | 'karma-phantomjs-launcher',
50 | 'karma-jasmine'
51 | ],
52 |
53 | // Continuous Integration mode
54 | // if true, it capture browsers, run tests and exit
55 | singleRun: false,
56 |
57 | colors: true,
58 |
59 | // level of logging
60 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
61 | logLevel: config.LOG_INFO,
62 |
63 | // Uncomment the following lines if you are using grunt's server to run the tests
64 | // proxies: {
65 | // '/': 'http://localhost:9000/'
66 | // },
67 | // URL root prevent conflicts with the site root
68 | // urlRoot: '_karma_'
69 | });
70 | };
71 |
--------------------------------------------------------------------------------
/public/test/spec/controllers/about.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: AboutCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('publicApp'));
7 |
8 | var AboutCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | AboutCtrl = $controller('AboutCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/public/test/spec/controllers/home.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: HomectrlCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('publicApp'));
7 |
8 | var HomectrlCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | HomectrlCtrl = $controller('HomectrlCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/public/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('publicApp'));
7 |
8 | var MainCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | MainCtrl = $controller('MainCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/public/test/spec/directives/videoplayer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Directive: VideoPlayer', function () {
4 |
5 | // load the directive's module
6 | beforeEach(module('publicApp'));
7 |
8 | var element,
9 | scope;
10 |
11 | beforeEach(inject(function ($rootScope) {
12 | scope = $rootScope.$new();
13 | }));
14 |
15 | it('should make hidden element visible', inject(function ($compile) {
16 | element = angular.element('<-video-player>-video-player>');
17 | element = $compile(element)(scope);
18 | expect(element.text()).toBe('this is the VideoPlayer directive');
19 | }));
20 | });
21 |
--------------------------------------------------------------------------------
/public/test/spec/services/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Service: config', function () {
4 |
5 | // load the service's module
6 | beforeEach(module('publicApp'));
7 |
8 | // instantiate service
9 | var config;
10 | beforeEach(inject(function (_config_) {
11 | config = _config_;
12 | }));
13 |
14 | it('should do something', function () {
15 | expect(!!config).toBe(true);
16 | });
17 |
18 | });
19 |
--------------------------------------------------------------------------------
/public/test/spec/services/io.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Service: Io', function () {
4 |
5 | // load the service's module
6 | beforeEach(module('publicApp'));
7 |
8 | // instantiate service
9 | var Io;
10 | beforeEach(inject(function (_Io_) {
11 | Io = _Io_;
12 | }));
13 |
14 | it('should do something', function () {
15 | expect(!!Io).toBe(true);
16 | });
17 |
18 | });
19 |
--------------------------------------------------------------------------------
/public/test/spec/services/roommanager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Service: RoomManager', function () {
4 |
5 | // load the service's module
6 | beforeEach(module('publicApp'));
7 |
8 | // instantiate service
9 | var RoomManager;
10 | beforeEach(inject(function (_RoomManager_) {
11 | RoomManager = _RoomManager_;
12 | }));
13 |
14 | it('should do something', function () {
15 | expect(!!RoomManager).toBe(true);
16 | });
17 |
18 | });
19 |
--------------------------------------------------------------------------------
/public/test/spec/services/videostream.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Service: VideoStream', function () {
4 |
5 | // load the service's module
6 | beforeEach(module('publicApp'));
7 |
8 | // instantiate service
9 | var VideoStream;
10 | beforeEach(inject(function (_VideoStream_) {
11 | VideoStream = _VideoStream_;
12 | }));
13 |
14 | it('should do something', function () {
15 | expect(!!VideoStream).toBe(true);
16 | });
17 |
18 | });
19 |
--------------------------------------------------------------------------------