diff --git a/Contributors.md b/Contributors.md new file mode 100644 index 00000000..addec8bc --- /dev/null +++ b/Contributors.md @@ -0,0 +1 @@ +Juliano Teixeira diff --git a/kurento-hello-world-rxjs/README.md b/kurento-hello-world-rxjs/README.md new file mode 100644 index 00000000..af38f973 --- /dev/null +++ b/kurento-hello-world-rxjs/README.md @@ -0,0 +1,122 @@ +[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0) +[![Documentation badge](https://readthedocs.org/projects/fiware-orion/badge/?version=latest)](http://doc-kurento.readthedocs.org/en/latest/) +[![Docker badge](https://img.shields.io/docker/pulls/fiware/orion.svg)](https://hub.docker.com/r/fiware/stream-oriented-kurento/) +[![Support badge]( https://img.shields.io/badge/support-sof-yellowgreen.svg)](http://stackoverflow.com/questions/tagged/kurento) + +[![][KurentoImage]][Kurento] + +Copyright © 2013-2016 [Kurento]. Licensed under [Apache 2.0 License]. + +kurento-hello-world-rxjs +=================== + +Kurento Node Tutorial: Hello World (WebRTC in loopback using rxjs). + +Running this tutorial +--------------------- + +In order to run this tutorial, please read the following [instructions]. + +What is Kurento +--------------- + +Kurento is an open source software project providing a platform suitable +for creating modular applications with advanced real-time communication +capabilities. For knowing more about Kurento, please visit the Kurento +project website: http://www.kurento.org. + +Kurento is part of [FIWARE]. For further information on the relationship of +FIWARE and Kurento check the [Kurento FIWARE Catalog Entry] + +Kurento is part of the [NUBOMEDIA] research initiative. + +Documentation +------------- + +The Kurento project provides detailed [documentation] including tutorials, +installation and development guides. A simplified version of the documentation +can be found on [readthedocs.org]. The [Open API specification] a.k.a. Kurento +Protocol is also available on [apiary.io]. + +Source +------ + +Code for other Kurento projects can be found in the [GitHub Kurento Group]. + +News and Website +---------------- + +Check the [Kurento blog] +Follow us on Twitter @[kurentoms]. + +Issue tracker +------------- + +Issues and bug reports should be posted to the [GitHub Kurento bugtracker] + +Licensing and distribution +-------------------------- + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Contribution policy +------------------- + +You can contribute to the Kurento community through bug-reports, bug-fixes, new +code or new documentation. For contributing to the Kurento community, drop a +post to the [Kurento Public Mailing List] providing full information about your +contribution and its value. In your contributions, you must comply with the +following guidelines + +* You must specify the specific contents of your contribution either through a + detailed bug description, through a pull-request or through a patch. +* You must specify the licensing restrictions of the code you contribute. +* For newly created code to be incorporated in the Kurento code-base, you must + accept Kurento to own the code copyright, so that its open source nature is + guaranteed. +* You must justify appropriately the need and value of your contribution. The + Kurento project has no obligations in relation to accepting contributions + from third parties. +* The Kurento project leaders have the right of asking for further + explanations, tests or validations of any code contributed to the community + before it being incorporated into the Kurento code-base. You must be ready to + addressing all these kind of concerns before having your code approved. + +Support +------- + +The Kurento project provides community support through the [Kurento Public +Mailing List] and through [StackOverflow] using the tags *kurento* and +*fiware-kurento*. + +Before asking for support, please read first the [Kurento Netiquette Guidelines] + +[documentation]: http://www.kurento.org/documentation +[FIWARE]: http://www.fiware.org +[GitHub Kurento bugtracker]: https://github.com/Kurento/bugtracker/issues +[GitHub Kurento Group]: https://github.com/kurento +[kurentoms]: http://twitter.com/kurentoms +[Kurento]: http://kurento.org +[Kurento Blog]: http://www.kurento.org/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: http://www.kurento.org/blog/kurento-netiquette-guidelines +[Kurento Public Mailing list]: https://groups.google.com/forum/#!forum/kurento +[KurentoImage]: https://secure.gravatar.com/avatar/21a2a12c56b2a91c8918d5779f1778bf?s=120 +[Apache 2.0 License]: http://www.apache.org/licenses/LICENSE-2.0 +[NUBOMEDIA]: http://www.nubomedia.eu +[StackOverflow]: http://stackoverflow.com/search?q=kurento +[Read-the-docs]: http://read-the-docs.readthedocs.org/ +[readthedocs.org]: http://kurento.readthedocs.org/ +[Open API specification]: http://kurento.github.io/doc-kurento/ +[apiary.io]: http://docs.streamoriented.apiary.io/ +[instructions]: http://www.kurento.org/docs/current/tutorials/node/tutorial-helloworld.html diff --git a/kurento-hello-world-rxjs/keys/README.md b/kurento-hello-world-rxjs/keys/README.md new file mode 100644 index 00000000..9603ced3 --- /dev/null +++ b/kurento-hello-world-rxjs/keys/README.md @@ -0,0 +1,7 @@ +[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0) +[![Documentation badge](https://readthedocs.org/projects/fiware-orion/badge/?version=latest)](http://doc-kurento.readthedocs.org/en/latest/) +[![Docker badge](https://img.shields.io/docker/pulls/fiware/orion.svg)](https://hub.docker.com/r/fiware/stream-oriented-kurento/) +[![Support badge]( https://img.shields.io/badge/support-sof-yellowgreen.svg)](http://stackoverflow.com/questions/tagged/kurento) + +This folder contains a dummy self-signed certificate only for demo purposses, +**DON'T USE IT IN PRODUCTION**. diff --git a/kurento-hello-world-rxjs/keys/server.crt b/kurento-hello-world-rxjs/keys/server.crt new file mode 100644 index 00000000..65e608da --- /dev/null +++ b/kurento-hello-world-rxjs/keys/server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBjCCAe4CCQCuf5QfyX2oDDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTE0MDkyOTA5NDczNVoXDTE1MDkyOTA5NDczNVowRTELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AMJOyOHJ+rJWJEQ7P7kKoWa31ff7hKNZxF6sYE5lFi3pBYWIY6kTN/iUaxJLROFo +FhoC/M/STY76rIryix474v/6cRoG8N+GQBEn4IAP1UitWzVO6pVvBaIt5IKlhhfm +YA1IMweCd03vLcaHTddNmFDBTks7QDwfenTaR5VjKYc3OtEhcG8dgLAnOjbbk2Hr +8wter2IeNgkhya3zyoXnTLT8m8IMg2mQaJs62Xlo9gs56urvVDWG4rhdGybj1uwU +ZiDYyP4CFCUHS6UVt12vADP8vjbwmss2ScGsIf0NjaU+MpSdEbB82z4b2NiN8Wq+ +rFA/JbvyeoWWHMoa7wkVs1MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAYLRwV9fo +AOhJfeK199Tv6oXoNSSSe10pVLnYxPcczCVQ4b9SomKFJFbmwtPVGi6w3m+8mV7F +9I2WKyeBHzmzfW2utZNupVybxgzEjuFLOVytSPdsB+DcJomOi8W/Cf2Vk8Wykb/t +Ctr1gfOcI8rwEGKxm279spBs0u1snzoLyoimbMbiXbC82j1IiN3Jus08U07m/j7N +hRBCpeHjUHT3CRpvYyTRnt+AyBd8BiyJB7nWmcNI1DksXPfehd62MAFS9e1ZE+dH +Aavg/U8VpS7pcCQcPJvIJ2hehrt8L6kUk3YUYqZ0OeRZK27f2R5+wFlDF33esm3N +dCSsLJlXyqAQFg== +-----END CERTIFICATE----- diff --git a/kurento-hello-world-rxjs/keys/server.csr b/kurento-hello-world-rxjs/keys/server.csr new file mode 100644 index 00000000..6615b130 --- /dev/null +++ b/kurento-hello-world-rxjs/keys/server.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMJOyOHJ+rJWJEQ7P7kKoWa31ff7hKNZxF6sYE5l +Fi3pBYWIY6kTN/iUaxJLROFoFhoC/M/STY76rIryix474v/6cRoG8N+GQBEn4IAP +1UitWzVO6pVvBaIt5IKlhhfmYA1IMweCd03vLcaHTddNmFDBTks7QDwfenTaR5Vj +KYc3OtEhcG8dgLAnOjbbk2Hr8wter2IeNgkhya3zyoXnTLT8m8IMg2mQaJs62Xlo +9gs56urvVDWG4rhdGybj1uwUZiDYyP4CFCUHS6UVt12vADP8vjbwmss2ScGsIf0N +jaU+MpSdEbB82z4b2NiN8Wq+rFA/JbvyeoWWHMoa7wkVs1MCAwEAAaAAMA0GCSqG +SIb3DQEBCwUAA4IBAQBMszYHMpklgTF/3h1zAzKXUD9NrtZp8eWhL06nwVjQX8Ai +EaCUiW0ypstokWcH9+30chd2OD++67NbxYUEucH8HrKpOoy6gs5L/mqgQ9Npz3OT +TB1HI4kGtpVuUQ5D7L0596tKzMX/CgW/hRcHWl+PDkwGhQs1qZcJ8QN+YP6AkRrO +5sDdDB/BLrB9PtBQbPrYIQcHQ7ooYWz/G+goqRxzZ6rt0aU2uAB6l7c82ADLAqFJ +qlw+xqVzEETVfqM5TXKK/wV3hgm4oSX5Q4SHLKF94ODOkWcnV4nfIKz7y+5XcQ3p +PrGimI1br07okC5rO9cgLCR0Ks20PPFcM0FvInW/ +-----END CERTIFICATE REQUEST----- diff --git a/kurento-hello-world-rxjs/keys/server.key b/kurento-hello-world-rxjs/keys/server.key new file mode 100644 index 00000000..a69a0a27 --- /dev/null +++ b/kurento-hello-world-rxjs/keys/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAwk7I4cn6slYkRDs/uQqhZrfV9/uEo1nEXqxgTmUWLekFhYhj +qRM3+JRrEktE4WgWGgL8z9JNjvqsivKLHjvi//pxGgbw34ZAESfggA/VSK1bNU7q +lW8Foi3kgqWGF+ZgDUgzB4J3Te8txodN102YUMFOSztAPB96dNpHlWMphzc60SFw +bx2AsCc6NtuTYevzC16vYh42CSHJrfPKhedMtPybwgyDaZBomzrZeWj2Cznq6u9U +NYbiuF0bJuPW7BRmINjI/gIUJQdLpRW3Xa8AM/y+NvCayzZJwawh/Q2NpT4ylJ0R +sHzbPhvY2I3xar6sUD8lu/J6hZYcyhrvCRWzUwIDAQABAoIBACwt56TW3MZxqZtN +8WYsUZheUispJ/ZQMcLo5JjOiSV1Jwk+gpJtyTse291z+bxagzP02/CQu4u32UVa +cmE0cp+LHO4zB8964dREwdm8P91fdS6Au/uwG5LNZniCFCQZAFvkv52Ef4XbzQen +uf4rKWerHBck6K0C5z/sZXxE6KtScE2ZLUmkhO0nkHM6MA6gFk2OMnB+oDTOWWPt +1mlreQlzuMYG/D4axviRYrOSYCE5Qu1SOw/DEOLQqqeBjQrKtAyOlFHZsIR6lBfe +KHMChPUcYIwaowt2DcqH/A+AFXRtaifa6DvH8Yul+2vAp47UEpaenVfM5bpN33XV +EzerjtECgYEA+xiXzblek67iQgRpc9eHSoqs4iRLhae8s8kpAG51Jz46Je+Dmium +XV769oiUGUxBeoUb7ryW+4MOzHJaA1BfGejQSvwLIB9e4cnikqnAArcqbcAcOCL1 +aYYDiSmSmN/AokNZlPKEBFXP9bzXrU9smQJWNTHlcRl7JXfnwF+jwNsCgYEAxhpE +SBr9vlUVHNh/S6C5i80NIYg6jCy2FgsmuzEqmcqV0pTyzegmq8bru+QmuvoUj2o4 +nVv4J9d1fLF6ECUVk9aK8UdJOOB6hAfurOdJCArgrsY/9t4uDzXfbPCdfSNQITE0 +XgeNGQX1EzvwwkBmyZKk0kLIr3syP8ZCWfXDROkCgYBR+dF1pJMv++R6UR5sZ20P +9P5ERj0xwXVl7MKqFWXCDhrFz9BTQPTrftrIKgbPy4mFCnf4FTHlov/t11dzxYWG +2+9Ey8yGDDfZ1yNVZn39ZPdBJXsRCLi+XrZAzYXCyyoEz6ArdJGNKMbgH2r6dfeq +bIzgiQ2zQvJlZSQQNiksCQKBgCgwzAmU8EXdHRttEOZXBU3HnBJhgP9PUuHGAWWY +4/uvjhXbAiekIbRX9xt3fiQQ+HrgIfxK3F246K0TlKAR5f7IWAf7Xm+bmz+OHG4X +vklTa6IJtpBvIwkS9PE1H75zm54gTW+GOKoK+12bm4zNZA0hIy9FPVHcvKUTpAJ8 +SdGBAoGAHLtJnB1NO4EgO6WtLQMXt7HrIbup8eZi8/82gC3422C+ooKIrYQ07qSw +nBOO/G0OB4yd6vCE2x5+TWSSCYGgG5A8aIv5qP76RP4hovGHxG/y2tfotw5UuOrh +nFWlTP4Urs8PeykvK9ao8r/T8BnPIC16U6ENYvAc0mRlFA2j1GA= +-----END RSA PRIVATE KEY----- diff --git a/kurento-hello-world-rxjs/kurento-rxjs.js b/kurento-hello-world-rxjs/kurento-rxjs.js new file mode 100644 index 00000000..ccfdd610 --- /dev/null +++ b/kurento-hello-world-rxjs/kurento-rxjs.js @@ -0,0 +1,83 @@ +var kurento = require('kurento-client'); +const Rx = require('rxjs'); +const op = require('rxjs/operators'); + +/* + * Helper methods + */ +const exec = fn => op.flatMap(v => fn(v).pipe(op.mapTo(v))); +const append = (observableFn, key) => op.flatMap(args => observableFn(args).pipe(op.map(x => ({ ...args, [key]: x })))); +const createMediaElement = pipeline => Rx.bindNodeCallback(pipeline.create); +const mediaElementConnect = (source, sink) => Rx.bindNodeCallback(source.connect.bind(source))(sink); + +/* + * Create Kurento stream + */ +const createKurento$ = (wss, kurentoServerUrl, sessionHandler) => { + let kurentoClient = null; + const ws$ = new Rx.Subject() + .pipe( + append(ctx => Rx.iif(() => !kurentoClient, Rx.bindNodeCallback(kurento).call(ctx, kurentoServerUrl), Rx.of(kurentoClient)), 'client') + ); + wss.on('connection', ws => { + console.log('connecion'); + var sid = null; + var request = ws.upgradeReq; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, err => sid = request.session.id); + + ws.on('message', _message => ws$.next({ ws, sid, message: JSON.parse(_message) })); + ws.on('error', e => ws$.error(e)); + ws.on('close', _ => ws$.complete()); + }); + return ws$; +}; + +/* + * Filter stream by message.id + */ +const onMessage = m => op.filter(({ message }) => message.id === m); + +/* + * Create Kurento media pipeline + */ +const createPipeline = () => Rx.pipe( + append(ctx => Rx.bindNodeCallback(ctx.client.create).call(ctx, 'MediaPipeline'), 'pipeline'), + op.catchError(_ => ctx.pipeline.release()) +); + +/* + * Create media elements + */ +const createMediaElements = (...elements) => Rx.pipe( + append(ctx => + Rx.forkJoin(...elements.map(el => createMediaElement(ctx.pipeline)(el[0]))).pipe( + op.map(v => v.reduce((acc, el, i) => ({ ...acc, [elements[i][1]]: el }), {} )) + ) + , 'elements') +) + +/* + * Connect media elements + */ +const connect = source => ({ to: sink =>exec(v => mediaElementConnect(v.elements[source], v.elements[sink]))}); + +/* + * Select created media element and provide additional APIs + */ +const withElement = element => ({ + do: fn => op.tap(ctx => fn(ctx.elements[element], ctx)), + append: (methodFn, argsFn, key) => append(ctx => { + const m = Rx.bindNodeCallback(methodFn(ctx.elements[element]).bind(ctx.elements[element])); + return argsFn ? m(argsFn(ctx)) : m(); + }, key), + exec: (methodFn, argsFn) => exec(ctx => { + const m = Rx.bindNodeCallback(methodFn(ctx.elements[element]).bind(ctx.elements[element])); + return argsFn ? m(argsFn(ctx)) : m(); + }) +}); + +module.exports = { createKurento$, onMessage, createPipeline, createMediaElements, connect, withElement } \ No newline at end of file diff --git a/kurento-hello-world-rxjs/package.json b/kurento-hello-world-rxjs/package.json new file mode 100644 index 00000000..1da8398b --- /dev/null +++ b/kurento-hello-world-rxjs/package.json @@ -0,0 +1,20 @@ +{ + "name": "kurento-hello-world", + "version": "6.6.2-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "~4.12.4", + "express-session": "~1.10.3", + "kurento-client": "Kurento/kurento-client-js", + "minimist": "^1.1.1", + "rxjs": "^6.2.1", + "ws": "~1.0.1" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/kurento-hello-world-rxjs/server.js b/kurento-hello-world-rxjs/server.js new file mode 100755 index 00000000..1061357f --- /dev/null +++ b/kurento-hello-world-rxjs/server.js @@ -0,0 +1,170 @@ +var path = require('path'); +var url = require('url'); +var cookieParser = require('cookie-parser') +var express = require('express'); +var session = require('express-session') +var minimist = require('minimist'); +var ws = require('ws'); +var kurento = require('kurento-client'); +var fs = require('fs'); +var https = require('https'); +const Rx = require('rxjs'); +const op = require('rxjs/operators'); +const k$ = require('./kurento-rxjs'); // kurento rxjs framework + + +var argv = minimist(process.argv.slice(2), { + default: { + as_uri: 'https://localhost:8443/', + ws_uri: 'ws://localhost:8888/kurento' + } +}); + +var options = +{ + key: fs.readFileSync('keys/server.key'), + cert: fs.readFileSync('keys/server.crt') +}; + +var app = express(); + +/* +* Management of sessions +*/ +app.use(cookieParser()); + +var sessionHandler = session({ + secret : 'none', + rolling : true, + resave : true, + saveUninitialized : true +}); + +app.use(sessionHandler); + +/* + * Definition of global variables. + */ +var sessions = {}; +var candidatesQueue = {}; + +/* + * Server startup + */ +var asUrl = url.parse(argv.as_uri); +var port = asUrl.port; +var server = https.createServer(options, app).listen(port, function() { + console.log('Kurento Tutorial started'); + console.log('Open ' + url.format(asUrl) + ' with a WebRTC capable browser'); +}); + +var wss = new ws.Server({ + server : server, + path : '/helloworld' +}); + +const ws$ = k$.createKurento$(wss, argv.ws_uri, sessionHandler); + +/* + * Helper private functions + */ +const handleOnIceCandidate = (webRtcEndpoint, { ws }) => { + webRtcEndpoint.on('OnIceCandidate', event => { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); +}; +const addPipelineToSessions = (webRtcEndpoint, { sid, pipeline }) => { + sessions[sid] = { + 'pipeline': pipeline, + 'webRtcEndpoint': webRtcEndpoint + } +}; +const processCandidate = (webRtcEndpoint, { sid }) => { + if (candidatesQueue[sid]) { + while(candidatesQueue[sid].length) { + var candidate = candidatesQueue[sid].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } +} +const sendSdpAnswer = (_, { ws, sdpAnswer }) => { + console.log('Sending sdp answer', sdpAnswer); + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : sdpAnswer + })) +} + + +/* + * Core functionality + */ + +// create start rxjs pipeline (not to be confused with kurento pipeline) +const start$ = ws$.pipe( + k$.onMessage('start'), // filter by message.id === 'start' + k$.createPipeline(), // create Kurento media pipeline + k$.createMediaElements( + ['WebRtcEndpoint', 'endpoint'] // create one WebRtcEndpoint with id 'endpoint' + ), + k$.connect('endpoint').to('endpoint'), // create a loopback + k$.withElement('endpoint').do(processCandidate), // process candidate + k$.withElement('endpoint').do(handleOnIceCandidate), // handle onIceCandidate + k$.withElement('endpoint').append(el => el.processOffer, ctx => ctx.message.sdpOffer, 'sdpAnswer'), // process SDP offer + k$.withElement('endpoint').do(addPipelineToSessions), // add pipeline to the sessions array + k$.withElement('endpoint').exec(el => el.gatherCandidates), // gather candidates + k$.withElement('endpoint').do(sendSdpAnswer) // send SDP Answer +) + +// create stop rxjs pipeline (not to be confused with kurento pipeline) +const stop$ = ws$.pipe( + k$.onMessage('stop'), + op.tap(({ sid }) => { + if (sessions[sid]) { + var pipeline = sessions[sid].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sid]; + delete candidatesQueue[sid]; + } + }) +) + +// crete onIceCandidate rxjs pipeline (not to be confused with kurento pipeline) +const onIceCandidate$ = ws$.pipe( + k$.onMessage('onIceCandidate'), + op.tap(({ sid, message }) => { + var candidate = kurento.getComplexType('IceCandidate')(message.candidate); + + if (sessions[sid]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sid].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sid]) { + candidatesQueue[sid] = []; + } + candidatesQueue[sid].push(candidate); + } + }) +) + +// merge all rxjs pipelines and also add default pipeline to send out invalid massage +// and subscribe +Rx.merge(start$, stop$, onIceCandidate$).pipe( + op.defaultIfEmpty(({ ws, message }) => { + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + }) +).subscribe(); + +app.use(express.static(path.join(__dirname, 'static'))); \ No newline at end of file diff --git a/kurento-hello-world-rxjs/static/bower.json b/kurento-hello-world-rxjs/static/bower.json new file mode 100644 index 00000000..54370bd7 --- /dev/null +++ b/kurento-hello-world-rxjs/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-hello-world", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "http://www.kurento.org/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "adapter.js": "v0.2.9", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "master" + } +} diff --git a/kurento-hello-world-rxjs/static/css/kurento.css b/kurento-hello-world-rxjs/static/css/kurento.css new file mode 100644 index 00000000..376484ef --- /dev/null +++ b/kurento-hello-world-rxjs/static/css/kurento.css @@ -0,0 +1,53 @@ +/* + * (C) Copyright 2014-2015 Kurento (http://kurento.org/) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@CHARSET "UTF-8"; + +html { + position: relative; + min-height: 100%; +} + +body { + padding-top: 40px; + body +} + +video,#console { + display: block; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, box-shadow + ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} + +#console { + min-height: 120px; + max-height: 360px; +} + +.col-md-2 { + width: 80px; + padding-top: 190px; +} diff --git a/kurento-hello-world-rxjs/static/img/kurento.png b/kurento-hello-world-rxjs/static/img/kurento.png new file mode 100644 index 00000000..6f1a4ad3 Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/kurento.png differ diff --git a/kurento-hello-world-rxjs/static/img/naevatec.png b/kurento-hello-world-rxjs/static/img/naevatec.png new file mode 100644 index 00000000..05ee7041 Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/naevatec.png differ diff --git a/kurento-hello-world-rxjs/static/img/pipeline.png b/kurento-hello-world-rxjs/static/img/pipeline.png new file mode 100644 index 00000000..fad16bba Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/pipeline.png differ diff --git a/kurento-hello-world-rxjs/static/img/spinner.gif b/kurento-hello-world-rxjs/static/img/spinner.gif new file mode 100644 index 00000000..8be8ba33 Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/spinner.gif differ diff --git a/kurento-hello-world-rxjs/static/img/transparent-1px.png b/kurento-hello-world-rxjs/static/img/transparent-1px.png new file mode 100644 index 00000000..9da19eac Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/transparent-1px.png differ diff --git a/kurento-hello-world-rxjs/static/img/urjc.gif b/kurento-hello-world-rxjs/static/img/urjc.gif new file mode 100644 index 00000000..cd8a7703 Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/urjc.gif differ diff --git a/kurento-hello-world-rxjs/static/img/webrtc.png b/kurento-hello-world-rxjs/static/img/webrtc.png new file mode 100644 index 00000000..d47e2e4c Binary files /dev/null and b/kurento-hello-world-rxjs/static/img/webrtc.png differ diff --git a/kurento-hello-world-rxjs/static/index.html b/kurento-hello-world-rxjs/static/index.html new file mode 100644 index 00000000..dad9bbe3 --- /dev/null +++ b/kurento-hello-world-rxjs/static/index.html @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial 1: Hello World + + + +
+ +
+ +
+ +
+
+

Local stream

+ +
+
+ + Start +
+
+ + Stop +
+
+

Remote stream

+ +
+
+
+
+

+
+
    +
    +
    +
    +
    + + + + + diff --git a/kurento-hello-world-rxjs/static/js/index.js b/kurento-hello-world-rxjs/static/js/index.js new file mode 100644 index 00000000..58aba3f1 --- /dev/null +++ b/kurento-hello-world-rxjs/static/js/index.js @@ -0,0 +1,188 @@ +/* + * (C) Copyright 2014-2015 Kurento (http://kurento.org/) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +var ws = new WebSocket('wss://' + location.host + '/helloworld'); +var videoInput; +var videoOutput; +var webRtcPeer; +var state = null; + +const I_CAN_START = 0; +const I_CAN_STOP = 1; +const I_AM_STARTING = 2; + +window.onload = function() { + console = new Console(); + console.log('Page loaded ...'); + videoInput = document.getElementById('videoInput'); + videoOutput = document.getElementById('videoOutput'); + setState(I_CAN_START); +} + +window.onbeforeunload = function() { + ws.close(); +} + +ws.onmessage = function(message) { + var parsedMessage = JSON.parse(message.data); + console.info('Received message: ' + message.data); + + switch (parsedMessage.id) { + case 'startResponse': + startResponse(parsedMessage); + break; + case 'error': + if (state == I_AM_STARTING) { + setState(I_CAN_START); + } + onError('Error message from server: ' + parsedMessage.message); + break; + case 'iceCandidate': + webRtcPeer.addIceCandidate(parsedMessage.candidate) + break; + default: + if (state == I_AM_STARTING) { + setState(I_CAN_START); + } + onError('Unrecognized message', parsedMessage); + } +} + +function start() { + console.log('Starting video call ...') + + // Disable start button + setState(I_AM_STARTING); + showSpinner(videoInput, videoOutput); + + console.log('Creating WebRtcPeer and generating local sdp offer ...'); + + var options = { + localVideo: videoInput, + remoteVideo: videoOutput, + onicecandidate : onIceCandidate + } + + webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, function(error) { + if(error) return onError(error); + this.generateOffer(onOffer); + }); +} + +function onIceCandidate(candidate) { + console.log('Local candidate' + JSON.stringify(candidate)); + + var message = { + id : 'onIceCandidate', + candidate : candidate + }; + sendMessage(message); +} + +function onOffer(error, offerSdp) { + if(error) return onError(error); + + console.info('Invoking SDP offer callback function ' + location.host); + var message = { + id : 'start', + sdpOffer : offerSdp + } + sendMessage(message); +} + +function onError(error) { + console.error(error); +} + +function startResponse(message) { + setState(I_CAN_STOP); + console.log('SDP answer received from server. Processing ...'); + webRtcPeer.processAnswer(message.sdpAnswer); +} + +function stop() { + console.log('Stopping video call ...'); + setState(I_CAN_START); + if (webRtcPeer) { + webRtcPeer.dispose(); + webRtcPeer = null; + + var message = { + id : 'stop' + } + sendMessage(message); + } + hideSpinner(videoInput, videoOutput); +} + +function setState(nextState) { + switch (nextState) { + case I_CAN_START: + $('#start').attr('disabled', false); + $('#start').attr('onclick', 'start()'); + $('#stop').attr('disabled', true); + $('#stop').removeAttr('onclick'); + break; + + case I_CAN_STOP: + $('#start').attr('disabled', true); + $('#stop').attr('disabled', false); + $('#stop').attr('onclick', 'stop()'); + break; + + case I_AM_STARTING: + $('#start').attr('disabled', true); + $('#start').removeAttr('onclick'); + $('#stop').attr('disabled', true); + $('#stop').removeAttr('onclick'); + break; + + default: + onError('Unknown state ' + nextState); + return; + } + state = nextState; +} + +function sendMessage(message) { + var jsonMessage = JSON.stringify(message); + console.log('Senging message: ' + jsonMessage); + ws.send(jsonMessage); +} + +function showSpinner() { + for (var i = 0; i < arguments.length; i++) { + arguments[i].poster = './img/transparent-1px.png'; + arguments[i].style.background = 'center transparent url("./img/spinner.gif") no-repeat'; + } +} + +function hideSpinner() { + for (var i = 0; i < arguments.length; i++) { + arguments[i].src = ''; + arguments[i].poster = './img/webrtc.png'; + arguments[i].style.background = ''; + } +} + +/** + * Lightbox utility (to display media pipeline image in a modal dialog) + */ +$(document).delegate('*[data-toggle="lightbox"]', 'click', function(event) { + event.preventDefault(); + $(this).ekkoLightbox(); +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..48e341a0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3 @@ +{ + "lockfileVersion": 1 +}