diff --git a/tutorials/javascript-node/.gitignore b/tutorials/javascript-node/.gitignore new file mode 100644 index 0000000000..02b9dc2692 --- /dev/null +++ b/tutorials/javascript-node/.gitignore @@ -0,0 +1,11 @@ +.classpath +.project +.settings/ +.jshintrc +node_modules/ +bower_components/ +package-lock.json +*~ +.DS_Store +npm-debug.log +*reports diff --git a/tutorials/javascript-node/README.md b/tutorials/javascript-node/README.md new file mode 100644 index 0000000000..a586329326 --- /dev/null +++ b/tutorials/javascript-node/README.md @@ -0,0 +1,186 @@ +[![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 tutorial for Node.js +============================ +Examples on usage of the Kurento Node.js Client. + +This project contains a set of simple applications built with JavaScript Kurento +Client APIs ([kurento-client-js] and [kurento-utils-js]) for [Node.js]. + +The source code of this project can be cloned from the [GitHub repository]. + +Installation instructions +------------------------- + +Be sure to have installed [Node.js] in your system: + +```bash +curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - +sudo apt-get install -y nodejs +``` + +It is recommended to update NPM to the latest version: + +```bash +sudo npm install npm -g +``` + + +Install node modules and bower components + +```bash +npm install +``` + +Run the application and have fun ... + +```bash +npm start +``` + +Parameters +---------- + +The Node.js server accept an optional parameter with the URI of the MediaServer +WebSocket endpoint, being set by default at ws://localhost:8888/kurento. You can +define its value by using the ```ws_uri``` flag: + +```bash +npm start -- --ws_uri=ws://example.com:8888/kurento +``` + +It also accept an optional parameter with the URI of the application server root +that will serve the overlay image, being by default at https://localhost:8443/. +You can define its value by using the ```as_uri``` flag: + +```bash +npm start -- --as_uri=https://example.org:8443/ +``` + +For example, if you would like to start the node server in the localhost using +the port 8081, then the command is the following: + +```bash +npm start -- --as_uri=https://localhost:8081/ +``` + +Please notice that the double dash separator (```--```) is [on +purpose](https://www.npmjs.org/doc/cli/npm-run-script.html#description). + + +Kurento +======= + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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/ +[GitHub repository]: https://github.com/Kurento/kurento-tutorial-node +[kurento-client-js]: https://github.com/Kurento/kurento-client-js +[kurento-utils-js]: https://github.com/Kurento/kurento-utils-js +[Node.js]: http://nodejs.org/ diff --git a/tutorials/javascript-node/bin/set-versions.sh b/tutorials/javascript-node/bin/set-versions.sh new file mode 100755 index 0000000000..e7823485d1 --- /dev/null +++ b/tutorials/javascript-node/bin/set-versions.sh @@ -0,0 +1,226 @@ +#!/usr/bin/env bash + +#/ Version change helper. +#/ +#/ This shell script will traverse all subprojects and change their +#/ versions to the one provided as argument. +#/ +#/ +#/ Arguments +#/ ========= +#/ +#/ +#/ +#/ Base version number to set. When '--release' is used, this version will +#/ be used as-is; otherwise, a nightly/snapshot indicator will be appended. +#/ +#/ should be in a format compatible with Semantic Versioning, +#/ such as "1.2.3" or, in general terms, "..". +#/ +#/ --release +#/ +#/ Use version numbers intended for Release builds, such as "1.2.3". If this +#/ option is not given, a nightly/snapshot indicator is appended: "-dev". +#/ +#/ Optional. Default: Disabled. +#/ +#/ --git-add +#/ +#/ Add changes to the Git stage area. Useful to leave everything ready for a +#/ commit. +#/ +#/ Optional. Default: Disabled. + + + +# Shell setup +# =========== + +# Bash options for strict error checking. +set -o errexit -o errtrace -o pipefail -o nounset + +# Check dependencies. +command -v jq >/dev/null || { + echo "ERROR: Dependency 'jq' is not installed; please install it" + exit 1 +} + +# Trace all commands. +set -o xtrace + + + +# Parse call arguments +# ==================== + +CFG_VERSION="" +CFG_RELEASE="false" +CFG_GIT_ADD="false" + +while [[ $# -gt 0 ]]; do + case "${1-}" in + --release) + CFG_RELEASE="true" + ;; + --git-add) + CFG_GIT_ADD="true" + ;; + *) + CFG_VERSION="$1" + ;; + esac + shift +done + + + +# Config restrictions +# =================== + +if [[ -z "$CFG_VERSION" ]]; then + echo "ERROR: Missing " + exit 1 +fi + +REGEX='^[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$' +[[ "$CFG_VERSION" =~ $REGEX ]] || { + echo "ERROR: '$CFG_VERSION' must be compatible with Semantic Versioning: .." + exit 1 +} + +echo "CFG_VERSION=$CFG_VERSION" +echo "CFG_RELEASE=$CFG_RELEASE" +echo "CFG_GIT_ADD=$CFG_GIT_ADD" + + + +# Internal variables +# ================== + +if [[ "$CFG_RELEASE" == "true" ]]; then + VERSION="$CFG_VERSION" +else + VERSION="${CFG_VERSION}-dev" +fi + + + +# Helper functions +# ================ + +# Run jq, which doesn't have "in-place" mode like other UNIX tools. +function run_jq() { + [[ $# -ge 2 ]] || { + echo "ERROR [run_jq]: Missing argument(s): " + return 1 + } + local FILTER="$1" + local FILE="$2" + + local TEMP + TEMP="$(mktemp)" + + /usr/bin/jq "$FILTER" "$FILE" >"$TEMP" && mv "$TEMP" "$FILE" +} + +# Add the given file(s) to the Git stage area. +function git_add() { + [[ $# -ge 1 ]] || { + echo "ERROR [git_add]: Missing argument(s): [ ...]" + return 1 + } + + if [[ "$CFG_GIT_ADD" == "true" ]]; then + git add -- "$@" + fi +} + + + +# Apply version +# ============= + +# Dirs that contain common dependencies (kurento-client and kurento-utils). +DIRS_COMMON=( + kurento-chroma + kurento-crowddetector + kurento-hello-world + kurento-magic-mirror + kurento-one2many-call + kurento-one2one-call + kurento-platedetector + kurento-pointerdetector +) + +# Dirs that contain module dependencies. +MODULES=( + chroma + crowddetector + platedetector + pointerdetector +) + +for DIR in "${DIRS_COMMON[@]}"; do + pushd "$DIR" + run_jq ".version = \"$VERSION\"" package.json + if [[ "$CFG_RELEASE" == "true" ]]; then + run_jq " + .dependencies.\"kurento-client\" = \"$VERSION\" + " package.json + run_jq " + .dependencies.\"kurento-utils\" = \"$VERSION\" + " static/bower.json + else + run_jq " + .dependencies.\"kurento-client\" = \"git+https://github.com/Kurento/kurento-client-js.git\" + " package.json + run_jq " + .dependencies.\"kurento-utils\" = \"git+https://github.com/Kurento/kurento-utils-bower.git\" + " static/bower.json + fi + git_add \ + package.json \ + static/bower.json + popd +done + +for MODULE in "${MODULES[@]}"; do + pushd "kurento-${MODULE}" + if [[ "$CFG_RELEASE" == "true" ]]; then + run_jq " + .dependencies.\"kurento-module-${MODULE}\" = \"$VERSION\" + " package.json + else + run_jq " + .dependencies.\"kurento-module-${MODULE}\" = \"git+https://github.com/Kurento/kurento-module-${MODULE}-js.git\" + " package.json + fi + git_add \ + package.json + popd +done + +pushd kurento-module-tests-api/ +run_jq ".version = \"$VERSION\"" package.json +if [[ "$CFG_RELEASE" == "true" ]]; then + run_jq " + .dependencies.\"kurento-client\" = \"$VERSION\" + | .dependencies.\"kurento-module-chroma\" = \"$VERSION\" + | .dependencies.\"kurento-module-crowddetector\" = \"$VERSION\" + | .dependencies.\"kurento-module-platedetector\" = \"$VERSION\" + | .dependencies.\"kurento-module-pointerdetector\" = \"$VERSION\" + " package.json +else + run_jq " + .dependencies.\"kurento-client\" = \"git+https://github.com/Kurento/kurento-client-js.git\" + | .dependencies.\"kurento-module-chroma\" = \"git+https://github.com/Kurento/kurento-module-chroma-js.git\" + | .dependencies.\"kurento-module-crowddetector\" = \"git+https://github.com/Kurento/kurento-module-crowddetector-js.git\" + | .dependencies.\"kurento-module-platedetector\" = \"git+https://github.com/Kurento/kurento-module-platedetector-js.git\" + | .dependencies.\"kurento-module-pointerdetector\" = \"git+https://github.com/Kurento/kurento-module-pointerdetector-js.git\" + " package.json +fi +git_add \ + package.json +popd + +echo "Done!" diff --git a/tutorials/javascript-node/kurento-chroma/README.md b/tutorials/javascript-node/kurento-chroma/README.md new file mode 100644 index 0000000000..b48395a884 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/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-chroma +============== + +Kurento Node.js Tutorial: WebRTC in loopback with chroma filter. + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/module-chromafilter.html diff --git a/tutorials/javascript-node/kurento-chroma/keys/README.md b/tutorials/javascript-node/kurento-chroma/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/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/tutorials/javascript-node/kurento-chroma/keys/server.crt b/tutorials/javascript-node/kurento-chroma/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/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/tutorials/javascript-node/kurento-chroma/keys/server.csr b/tutorials/javascript-node/kurento-chroma/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/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/tutorials/javascript-node/kurento-chroma/keys/server.key b/tutorials/javascript-node/kurento-chroma/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/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/tutorials/javascript-node/kurento-chroma/package.json b/tutorials/javascript-node/kurento-chroma/package.json new file mode 100644 index 0000000000..1d1f8b7c3f --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/package.json @@ -0,0 +1,20 @@ +{ + "name": "kurento-chroma", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "^4.17.0", + "express-session": "^1.17.0", + "minimist": "^1.1.1", + "ws": "^7.1.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "kurento-module-chroma": "git+https://github.com/Kurento/kurento-module-chroma-js.git" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-chroma/server.js b/tutorials/javascript-node/kurento-chroma/server.js new file mode 100755 index 0000000000..a8d54f8ca5 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/server.js @@ -0,0 +1,310 @@ +/* + * (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 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'); +kurento.register('kurento-module-chroma'); + +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 = {}; +var kurentoClient = null; + +/* + * 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 : '/' +}); + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws, req) { + var sessionId = null; + var request = req; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, function(err) { + sessionId = request.session.id; + console.log('Connection received with sessionId ' + sessionId); + }); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'start': + sessionId = request.session.id; + start(sessionId, ws, message.sdpOffer, function(error, sdpAnswer) { + if (error) { + return ws.send(JSON.stringify({ + id : 'error', + message : error + })); + } + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : sdpAnswer + })); + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function start(sessionId, ws, sdpOffer, callback) { + if (!sessionId) { + return callback('Cannot use undefined sessionId'); + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + createMediaElements(pipeline, ws, function(error, webRtcEndpoint, filter) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + connectMediaElements(webRtcEndpoint, filter, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + pipeline.release(); + return callback(error); + } + + sessions[sessionId] = { + 'pipeline' : pipeline, + 'webRtcEndpoint' : webRtcEndpoint + } + return callback(null, sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); + }); + }); + }); + }); +} + +function createMediaElements(pipeline, ws, callback) { + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + return callback(error); + } + + var options = { + window: kurento.getComplexType('chroma.WindowParam')({ + topRightCornerX: 5, + topRightCornerY: 5, + width: 30, + height: 30 + }) + } + pipeline.create('chroma.ChromaFilter', options, function(error, filter) { + if (error) { + return callback(error); + } + + return callback(null, webRtcEndpoint, filter); + }); + }); +} + +function connectMediaElements(webRtcEndpoint, filter, callback) { + webRtcEndpoint.connect(filter, function(error) { + if (error) { + return callback(error); + } + + // const appServerUrl = url.format(asUrl); + const appServerUrl = "http://files.openvidu.io"; + filter.setBackground(appServerUrl + '/img/mario.jpg', function(error) { + if (error) { + return callback(error); + } + + filter.connect(webRtcEndpoint, function(error) { + if (error) { + return callback(error); + } + + return callback(null); + }); + }); + }); +} + +function stop(sessionId) { + if (sessions[sessionId]) { + var pipeline = sessions[sessionId].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sessionId]; + delete candidatesQueue[sessionId]; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.register.complexTypes.IceCandidate(_candidate); + + if (sessions[sessionId]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sessionId].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-chroma/static/bower.json b/tutorials/javascript-node/kurento-chroma/static/bower.json new file mode 100644 index 0000000000..8c0eabdb0e --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-chroma", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-chroma/static/css/kurento.css b/tutorials/javascript-node/kurento-chroma/static/css/kurento.css new file mode 100644 index 0000000000..6ae4fcc9c6 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/static/css/kurento.css @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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/tutorials/javascript-node/kurento-chroma/static/img/kurento.png b/tutorials/javascript-node/kurento-chroma/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/mario.jpg b/tutorials/javascript-node/kurento-chroma/static/img/mario.jpg new file mode 100644 index 0000000000..b7a6367d81 Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/mario.jpg differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/naevatec.png b/tutorials/javascript-node/kurento-chroma/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/pipeline.png b/tutorials/javascript-node/kurento-chroma/static/img/pipeline.png new file mode 100644 index 0000000000..8439557a8d Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/spinner.gif b/tutorials/javascript-node/kurento-chroma/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-chroma/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/urjc.gif b/tutorials/javascript-node/kurento-chroma/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-chroma/static/img/webrtc.png b/tutorials/javascript-node/kurento-chroma/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-chroma/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-chroma/static/index.html b/tutorials/javascript-node/kurento-chroma/static/index.html new file mode 100644 index 0000000000..374f62db76 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/static/index.html @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial: WebRTC Chroma + + + +
+ +
+ +
+ +
+
+

Local stream

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

Remote stream

+ +
+
+
+
+

+
+
    +
    +
    +
    +
    + + + + + diff --git a/tutorials/javascript-node/kurento-chroma/static/js/index.js b/tutorials/javascript-node/kurento-chroma/static/js/index.js new file mode 100644 index 0000000000..5c1741ff96 --- /dev/null +++ b/tutorials/javascript-node/kurento-chroma/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); +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('Sending 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/tutorials/javascript-node/kurento-crowddetector/README.md b/tutorials/javascript-node/kurento-crowddetector/README.md new file mode 100644 index 0000000000..c1c47172d1 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/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-crowddetector +===================== + +Kurento Node.js Tutorial: WebRTC in loopback with crowd detector filter. + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/module-crowddetector.html diff --git a/tutorials/javascript-node/kurento-crowddetector/keys/README.md b/tutorials/javascript-node/kurento-crowddetector/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/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/tutorials/javascript-node/kurento-crowddetector/keys/server.crt b/tutorials/javascript-node/kurento-crowddetector/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/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/tutorials/javascript-node/kurento-crowddetector/keys/server.csr b/tutorials/javascript-node/kurento-crowddetector/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/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/tutorials/javascript-node/kurento-crowddetector/keys/server.key b/tutorials/javascript-node/kurento-crowddetector/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/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/tutorials/javascript-node/kurento-crowddetector/package.json b/tutorials/javascript-node/kurento-crowddetector/package.json new file mode 100644 index 0000000000..65db787418 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/package.json @@ -0,0 +1,20 @@ +{ + "name": "kurento-crowddetector", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "^4.17.0", + "express-session": "^1.17.0", + "minimist": "^1.1.1", + "ws": "^7.1.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "kurento-module-crowddetector": "git+https://github.com/Kurento/kurento-module-crowddetector-js.git" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-crowddetector/server.js b/tutorials/javascript-node/kurento-crowddetector/server.js new file mode 100755 index 0000000000..9475384cb0 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/server.js @@ -0,0 +1,359 @@ +/* + * (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 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'); +kurento.register('kurento-module-crowddetector'); + +const RegionOfInterest = kurento.getComplexType('crowddetector.RegionOfInterest'); +const RegionOfInterestConfig = kurento.getComplexType('crowddetector.RegionOfInterestConfig'); +const RelativePoint = kurento.getComplexType('crowddetector.RelativePoint'); + +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 = {}; +var kurentoClient = null; + +/* + * 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 : '/' +}); + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws, req) { + var sessionId = null; + var request = req; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, function(err) { + sessionId = request.session.id; + console.log('Connection received with sessionId ' + sessionId); + }); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'start': + sessionId = request.session.id; + start(sessionId, ws, message.sdpOffer, function(error, type, data) { + if (error) { + return ws.send(JSON.stringify({ + id : 'error', + message : error + })); + } + switch (type) { + case 'sdpAnswer': + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : data + })); + break; + case 'crowdDetectorDirection': + ws.send(JSON.stringify({ + id : 'crowdDetectorDirection', + event_data : data + })); + break; + case 'crowdDetectorFluidity': + ws.send(JSON.stringify({ + id : 'crowdDetectorFluidity', + event_data : data + })); + break; + case 'crowdDetectorOccupancy': + ws.send(JSON.stringify({ + id : 'crowdDetectorOccupancy', + event_data : data + })); + break; + } + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function start(sessionId, ws, sdpOffer, callback) { + if (!sessionId) { + return callback('Cannot use undefined sessionId'); + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + createMediaElements(pipeline, ws, function(error, webRtcEndpoint, filter) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + connectMediaElements(webRtcEndpoint, filter, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + filter.on('CrowdDetectorDirection', function (_data){ + return callback(null, 'crowdDetectorDirection', _data); + }); + + filter.on('CrowdDetectorFluidity', function (_data){ + return callback(null, 'crowdDetectorFluidity', _data); + }); + + filter.on('CrowdDetectorOccupancy', function (_data){ + return callback(null, 'crowdDetectorOccupancy', _data); + }); + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + pipeline.release(); + return callback(error); + } + + sessions[sessionId] = { + 'pipeline' : pipeline, + 'webRtcEndpoint' : webRtcEndpoint + } + return callback(null, 'sdpAnswer', sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); + }); + }); + }); + }); +} + +function createMediaElements(pipeline, ws, callback) { + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + return callback(error); + } + + var options = { + rois: [ + RegionOfInterest({ + id: 'roi1', + points: [ + RelativePoint({x: 0 , y: 0 }), + RelativePoint({x: 0.5, y: 0 }), + RelativePoint({x: 0.5, y: 0.5}), + RelativePoint({x: 0 , y: 0.5}) + ], + regionOfInterestConfig: RegionOfInterestConfig({ + occupancyLevelMin: 10, + occupancyLevelMed: 35, + occupancyLevelMax: 65, + occupancyNumFramesToEvent: 5, + fluidityLevelMin: 10, + fluidityLevelMed: 35, + fluidityLevelMax: 65, + fluidityNumFramesToEvent: 5, + sendOpticalFlowEvent: false, + opticalFlowNumFramesToEvent: 3, + opticalFlowNumFramesToReset: 3, + opticalFlowAngleOffset: 0 + }) + }) + ] + } + pipeline.create('crowddetector.CrowdDetectorFilter', options, function(error, filter) { + if (error) { + return callback(error); + } + + return callback(null, webRtcEndpoint, filter); + }); + }); +} + +function connectMediaElements(webRtcEndpoint, filter, callback) { + webRtcEndpoint.connect(filter, function(error) { + if (error) { + return callback(error); + } + + filter.connect(webRtcEndpoint, function(error) { + if (error) { + return callback(error); + } + + return callback(null); + }); + }); +} + +function stop(sessionId) { + if (sessions[sessionId]) { + var pipeline = sessions[sessionId].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sessionId]; + delete candidatesQueue[sessionId]; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + + if (sessions[sessionId]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sessionId].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-crowddetector/static/bower.json b/tutorials/javascript-node/kurento-crowddetector/static/bower.json new file mode 100644 index 0000000000..49aac2fa84 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-crowddetector", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-crowddetector/static/css/kurento.css b/tutorials/javascript-node/kurento-crowddetector/static/css/kurento.css new file mode 100644 index 0000000000..6ae4fcc9c6 --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/static/css/kurento.css @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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/tutorials/javascript-node/kurento-crowddetector/static/img/kurento.png b/tutorials/javascript-node/kurento-crowddetector/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/img/naevatec.png b/tutorials/javascript-node/kurento-crowddetector/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/img/pipeline.png b/tutorials/javascript-node/kurento-crowddetector/static/img/pipeline.png new file mode 100644 index 0000000000..c447cc62ef Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/img/spinner.gif b/tutorials/javascript-node/kurento-crowddetector/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-crowddetector/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/img/urjc.gif b/tutorials/javascript-node/kurento-crowddetector/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/img/webrtc.png b/tutorials/javascript-node/kurento-crowddetector/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-crowddetector/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-crowddetector/static/index.html b/tutorials/javascript-node/kurento-crowddetector/static/index.html new file mode 100644 index 0000000000..1ca97656de --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/static/index.html @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial: WebRTC Crowd Detector + + + +
    + +
    + +
    + +
    +
    +

    Local stream

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

    Remote stream

    + +
    +
    +
    +
    +

    +
    +
      +
      +
      +
      +
      + + + + + diff --git a/tutorials/javascript-node/kurento-crowddetector/static/js/index.js b/tutorials/javascript-node/kurento-crowddetector/static/js/index.js new file mode 100644 index 0000000000..d12b20ee7c --- /dev/null +++ b/tutorials/javascript-node/kurento-crowddetector/static/js/index.js @@ -0,0 +1,217 @@ +/* + * (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); +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; + case 'CrowdDetectorDirection': + crowdDetectorDirection(parsedMessage); + break; + case 'CrowdDetectorFluidity': + crowdDetectorFluidity(parsedMessage); + break; + case 'CrowdDetectorOccupancy': + crowdDetectorOccupancy(parsedMessage); + break; + default: + if (state == I_AM_STARTING) { + setState(I_CAN_START); + } + onError('Unrecognized message', parsedMessage); + } +} + +function crowdDetectorDirection(message) { + console.log("Direction event received in roi " + message.event_data.roiID + + " with direction " + message.event_data.directionAngle); +} + +function crowdDetectorFluidity(message) { + console.log("Fluidity event received in roi " + message.event_data.roiID + + ". Fluidity level " + message.event_data.fluidityPercentage + + " and fluidity percentage " + message.event_data.fluidityLevel); +} + +function crowdDetectorOccupancy(message) { + console.log("Occupancy event received in roi " + message.event_data.roiID + + ". Occupancy level " + message.event_data.occupancyPercentage + + " and occupancy percentage " + message.event_data.occupancyLevel); +} + +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('Sending 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/tutorials/javascript-node/kurento-hello-world/README.md b/tutorials/javascript-node/kurento-hello-world/README.md new file mode 100644 index 0000000000..81e4094022 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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 +=================== + +Kurento Node.js Tutorial: Hello World (WebRTC in loopback). + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/tutorial-helloworld.html diff --git a/tutorials/javascript-node/kurento-hello-world/keys/README.md b/tutorials/javascript-node/kurento-hello-world/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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/tutorials/javascript-node/kurento-hello-world/keys/server.crt b/tutorials/javascript-node/kurento-hello-world/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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/tutorials/javascript-node/kurento-hello-world/keys/server.csr b/tutorials/javascript-node/kurento-hello-world/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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/tutorials/javascript-node/kurento-hello-world/keys/server.key b/tutorials/javascript-node/kurento-hello-world/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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/tutorials/javascript-node/kurento-hello-world/package.json b/tutorials/javascript-node/kurento-hello-world/package.json new file mode 100644 index 0000000000..f4a7d04d33 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/package.json @@ -0,0 +1,19 @@ +{ + "name": "kurento-hello-world", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "^4.17.0", + "express-session": "^1.17.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "minimist": "^1.1.1", + "ws": "^7.1.0" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-hello-world/server.js b/tutorials/javascript-node/kurento-hello-world/server.js new file mode 100755 index 0000000000..debb605061 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/server.js @@ -0,0 +1,280 @@ +/* + * (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 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'); + +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 = {}; +var kurentoClient = null; + +/* + * 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' +}); + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws, req) { + var sessionId = null; + var request = req; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, function(err) { + sessionId = request.session.id; + console.log('Connection received with sessionId ' + sessionId); + }); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'start': + sessionId = request.session.id; + start(sessionId, ws, message.sdpOffer, function(error, sdpAnswer) { + if (error) { + return ws.send(JSON.stringify({ + id : 'error', + message : error + })); + } + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : sdpAnswer + })); + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function start(sessionId, ws, sdpOffer, callback) { + if (!sessionId) { + return callback('Cannot use undefined sessionId'); + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + createMediaElements(pipeline, ws, function(error, webRtcEndpoint) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + connectMediaElements(webRtcEndpoint, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + pipeline.release(); + return callback(error); + } + + sessions[sessionId] = { + 'pipeline' : pipeline, + 'webRtcEndpoint' : webRtcEndpoint + } + return callback(null, sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); + }); + }); + }); + }); +} + +function createMediaElements(pipeline, ws, callback) { + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + return callback(error); + } + + return callback(null, webRtcEndpoint); + }); +} + +function connectMediaElements(webRtcEndpoint, callback) { + webRtcEndpoint.connect(webRtcEndpoint, function(error) { + if (error) { + return callback(error); + } + return callback(null); + }); +} + +function stop(sessionId) { + if (sessions[sessionId]) { + var pipeline = sessions[sessionId].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sessionId]; + delete candidatesQueue[sessionId]; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + + if (sessions[sessionId]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sessionId].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-hello-world/static/bower.json b/tutorials/javascript-node/kurento-hello-world/static/bower.json new file mode 100644 index 0000000000..47238f7f5d --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-hello-world/static/css/kurento.css b/tutorials/javascript-node/kurento-hello-world/static/css/kurento.css new file mode 100644 index 0000000000..6ae4fcc9c6 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/static/css/kurento.css @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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/tutorials/javascript-node/kurento-hello-world/static/img/kurento.png b/tutorials/javascript-node/kurento-hello-world/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/img/naevatec.png b/tutorials/javascript-node/kurento-hello-world/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/img/pipeline.png b/tutorials/javascript-node/kurento-hello-world/static/img/pipeline.png new file mode 100644 index 0000000000..fad16bbab0 Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/img/spinner.gif b/tutorials/javascript-node/kurento-hello-world/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-hello-world/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/img/urjc.gif b/tutorials/javascript-node/kurento-hello-world/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/img/webrtc.png b/tutorials/javascript-node/kurento-hello-world/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-hello-world/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-hello-world/static/index.html b/tutorials/javascript-node/kurento-hello-world/static/index.html new file mode 100644 index 0000000000..7cdf29c98a --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/static/index.html @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial 1: Hello World + + + +
      + +
      + +
      + +
      +
      +

      Local stream

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

      Remote stream

      + +
      +
      +
      +
      +

      +
      +
        +
        +
        +
        +
        + + + + + diff --git a/tutorials/javascript-node/kurento-hello-world/static/js/index.js b/tutorials/javascript-node/kurento-hello-world/static/js/index.js new file mode 100644 index 0000000000..a7c9c7e156 --- /dev/null +++ b/tutorials/javascript-node/kurento-hello-world/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('Sending 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/tutorials/javascript-node/kurento-magic-mirror/README.md b/tutorials/javascript-node/kurento-magic-mirror/README.md new file mode 100644 index 0000000000..7d532fde87 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/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-magic-mirror +==================== + +Kurento Node.js Tutorial: WebRTC in loopback with faceoverlay filter (magic mirror). + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/tutorial-magicmirror.html diff --git a/tutorials/javascript-node/kurento-magic-mirror/keys/README.md b/tutorials/javascript-node/kurento-magic-mirror/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/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/tutorials/javascript-node/kurento-magic-mirror/keys/server.crt b/tutorials/javascript-node/kurento-magic-mirror/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/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/tutorials/javascript-node/kurento-magic-mirror/keys/server.csr b/tutorials/javascript-node/kurento-magic-mirror/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/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/tutorials/javascript-node/kurento-magic-mirror/keys/server.key b/tutorials/javascript-node/kurento-magic-mirror/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/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/tutorials/javascript-node/kurento-magic-mirror/package.json b/tutorials/javascript-node/kurento-magic-mirror/package.json new file mode 100644 index 0000000000..c3888428bf --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/package.json @@ -0,0 +1,19 @@ +{ + "name": "kurento-magic-mirror", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "^4.17.0", + "express-session": "^1.17.0", + "minimist": "^1.1.1", + "ws": "^7.1.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-magic-mirror/server.js b/tutorials/javascript-node/kurento-magic-mirror/server.js new file mode 100755 index 0000000000..b58ca1b770 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/server.js @@ -0,0 +1,302 @@ +/* + * (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 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'); + +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 = {}; +var kurentoClient = null; + +/* + * 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 : '/magicmirror' +}); + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws, req) { + var sessionId = null; + var request = req; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, function(err) { + sessionId = request.session.id; + console.log('Connection received with sessionId ' + sessionId); + }); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'start': + sessionId = request.session.id; + start(sessionId, ws, message.sdpOffer, function(error, sdpAnswer) { + if (error) { + return ws.send(JSON.stringify({ + id : 'error', + message : error + })); + } + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : sdpAnswer + })); + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function start(sessionId, ws, sdpOffer, callback) { + if (!sessionId) { + return callback('Cannot use undefined sessionId'); + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + createMediaElements(pipeline, ws, function(error, webRtcEndpoint, faceOverlayFilter) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + connectMediaElements(webRtcEndpoint, faceOverlayFilter, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + pipeline.release(); + return callback(error); + } + + sessions[sessionId] = { + 'pipeline' : pipeline, + 'webRtcEndpoint' : webRtcEndpoint + } + return callback(null, sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); + }); + }); + }); + }); +} + +function createMediaElements(pipeline, ws, callback) { + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + return callback(error); + } + + pipeline.create('FaceOverlayFilter', function(error, faceOverlayFilter) { + if (error) { + return callback(error); + } + + // const appServerUrl = url.format(asUrl); + const appServerUrl = "http://files.openvidu.io"; + faceOverlayFilter.setOverlayedImage(appServerUrl + '/img/mario-wings.png', + -0.35, -1.2, 1.6, 1.6, function(error) { + if (error) { + return callback(error); + } + + return callback(null, webRtcEndpoint, faceOverlayFilter); + }); + }); + }); +} + +function connectMediaElements(webRtcEndpoint, faceOverlayFilter, callback) { + webRtcEndpoint.connect(faceOverlayFilter, function(error) { + if (error) { + return callback(error); + } + + faceOverlayFilter.connect(webRtcEndpoint, function(error) { + if (error) { + return callback(error); + } + + return callback(null); + }); + }); +} + +function stop(sessionId) { + if (sessions[sessionId]) { + var pipeline = sessions[sessionId].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sessionId]; + delete candidatesQueue[sessionId]; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + + if (sessions[sessionId]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sessionId].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/bower.json b/tutorials/javascript-node/kurento-magic-mirror/static/bower.json new file mode 100644 index 0000000000..a7b040b7d6 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-magic-mirror", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/css/kurento.css b/tutorials/javascript-node/kurento-magic-mirror/static/css/kurento.css new file mode 100644 index 0000000000..6ae4fcc9c6 --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/static/css/kurento.css @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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/tutorials/javascript-node/kurento-magic-mirror/static/img/kurento.png b/tutorials/javascript-node/kurento-magic-mirror/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/mario-wings.png b/tutorials/javascript-node/kurento-magic-mirror/static/img/mario-wings.png new file mode 100644 index 0000000000..cc89003676 Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/mario-wings.png differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/naevatec.png b/tutorials/javascript-node/kurento-magic-mirror/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/pipeline.png b/tutorials/javascript-node/kurento-magic-mirror/static/img/pipeline.png new file mode 100644 index 0000000000..90f8da795c Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/spinner.gif b/tutorials/javascript-node/kurento-magic-mirror/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-magic-mirror/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/urjc.gif b/tutorials/javascript-node/kurento-magic-mirror/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/img/webrtc.png b/tutorials/javascript-node/kurento-magic-mirror/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-magic-mirror/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/index.html b/tutorials/javascript-node/kurento-magic-mirror/static/index.html new file mode 100644 index 0000000000..26c4fe450d --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/static/index.html @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial 2: WebRTC Magic Mirror + + + +
        + +
        + +
        + +
        +
        +

        Local stream

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

        Remote stream

        + +
        +
        +
        +
        +

        +
        +
          +
          +
          +
          +
          + + + + + diff --git a/tutorials/javascript-node/kurento-magic-mirror/static/js/index.js b/tutorials/javascript-node/kurento-magic-mirror/static/js/index.js new file mode 100644 index 0000000000..c718bc508f --- /dev/null +++ b/tutorials/javascript-node/kurento-magic-mirror/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 + '/magicmirror'); +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('Sending 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/tutorials/javascript-node/kurento-module-tests-api/package.json b/tutorials/javascript-node/kurento-module-tests-api/package.json new file mode 100644 index 0000000000..0a33b05319 --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/package.json @@ -0,0 +1,47 @@ +{ + "name": "kurento-module-tests-api", + "version": "6.18.1-dev", + "description": "Kurento Module Tests API", + "homepage": "https://kurento.openvidu.io/", + "license": "ALv2", + "author": "Kurento (https://kurento.openvidu.io/)", + "contributors": [ + { + "name": "Raul Benitez 'rbenitez'", + "email": "raulbenitezmejias@gmail.com" + } + ], + "scripts": { + "test": "scripts/test" + }, + "repository": { + "type": "git", + "url": "Kurento/kurento-tutorial-node" + }, + "bugs": { + "email": "kurento@googlegroups.com" + }, + "keywords": [ + "API", + "Kurento", + "WebRTC" + ], + "dependencies": { + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "kurento-module-chroma": "git+https://github.com/Kurento/kurento-module-chroma-js.git", + "kurento-module-crowddetector": "git+https://github.com/Kurento/kurento-module-crowddetector-js.git", + "kurento-module-platedetector": "git+https://github.com/Kurento/kurento-module-platedetector-js.git", + "kurento-module-pointerdetector": "git+https://github.com/Kurento/kurento-module-pointerdetector-js.git" + }, + "devDependencies": { + "fs-extra": "^0.20.1", + "node-static": "^0.7.6", + "qunit-cli": "KurentoForks/qunit-cli#0.2.1", + "qunit-reporter-junit": "KurentoForks/qunit-reporter-junit#1.0.3", + "qunit-reporter-lcov": "^1.0.2", + "qunitjs": "1.18.0", + "xml2js": "^0.4.9", + "wock": "^0.1.1", + "dockerode": "2.2.6" + } +} diff --git a/tutorials/javascript-node/kurento-module-tests-api/scripts/test b/tutorials/javascript-node/kurento-module-tests-api/scripts/test new file mode 100755 index 0000000000..63fa6da735 --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/scripts/test @@ -0,0 +1,113 @@ +#!/usr/bin/env node + +var spawn = require('child_process').spawnSync + +var kurentoClient = 'node_modules/kurento-client/lib' + +// ChromaFilter Module +var argv = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/ChromaFilter.js', + '--prefix','', + '--module','chromaFilter' +] + +var result = spawn('qunit-cli', argv.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +var argvPrefix = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/ChromaFilter.js', + '--prefix','chroma.', + '--module','chromaFilter' +] + +spawn('qunit-cli', argvPrefix.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +// CrowdDetector Module +argv = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/CrowdDetector.js', + '--prefix','', + '--module','crowdDetector' +] + +spawn('qunit-cli', argv.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +argvPrefix = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/CrowdDetector.js', + '--prefix','crowddetector.', + '--module','crowdDetector' +] + +spawn('qunit-cli', argvPrefix.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +// PointerDetector Module +argv = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/PointerDetector.js', + '--prefix','', + '--module','pointerDetector' +] + +spawn('qunit-cli', argv.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +argvPrefix = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/PointerDetector.js', + '--prefix','pointerdetector.', + '--module','pointerDetector' +] + +spawn('qunit-cli', argvPrefix.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +// PlateDetector Module +argv = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/PlateDetector.js', + '--prefix','', + '--module','plateDetector' +] + +spawn('qunit-cli', argv.concat(process.argv.slice(2)), {stdio: 'inherit'}) + +argvPrefix = +[ + '-c', 'kurentoClient:'+kurentoClient, + '-c', 'node_modules/qunit-reporter-junit', + '-c', 'wock:node_modules/wock', + '-c', 'test/_common.js', + 'test/PlateDetector.js', + '--prefix','platedetector.', + '--module','plateDetector' +] + +spawn('qunit-cli', argvPrefix.concat(process.argv.slice(2)), {stdio: 'inherit'}) diff --git a/tutorials/javascript-node/kurento-module-tests-api/test/ChromaFilter.js b/tutorials/javascript-node/kurento-module-tests-api/test/ChromaFilter.js new file mode 100644 index 0000000000..7ba096242f --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/test/ChromaFilter.js @@ -0,0 +1,82 @@ +/* + * (C) Copyright 2013-2014 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. + * + */ + +/** + * + *

          + * Module tested: + *

            + *
          • ChromaFilter + *
          + * + * + * @author Raúl Benítez "rbenitez" (raulbenitezmejias@gmail.com) + * @version 1.0.0 + * + */ + +if (typeof QUnit == 'undefined') { + QUnit = require('qunit-cli'); + QUnit.load(); + + kurentoClient = require('../node_modules/kurento-client/lib'); + + require('./_common'); +}; + +kurentoClient.register('kurento-module-chroma') + +if (QUnit.config.prefix == undefined) + QUnit.config.prefix = ''; + +QUnit.module(QUnit.config.prefix + 'ChromaFilter', lifecycle); + +QUnit.asyncTest('Create ' + QUnit.config.prefix + 'Chroma Filter', function () { + var self = this; + + QUnit.expect(2); + + function onerror(error) { + _onerror(error); + }; + + self.pipeline.create('kurento.WebRtcEndpoint', function (error, webRtcEndpoint) { + if (error) return onerror(error); + + QUnit.notEqual(webRtcEndpoint, undefined, 'webRtcEndpoint'); + + var options = { + window: kurentoClient.getComplexType(QUnit.config.prefix + 'WindowParam')({ + topRightCornerX: 5, + topRightCornerY: 5, + width: 30, + height: 30 + }) + } + + self.pipeline.create(QUnit.config.prefix + 'ChromaFilter', options, function (error, chromaFilter) { + if (error) return onerror(error); + + QUnit.notEqual(chromaFilter, undefined, 'chromaFilter'); + + QUnit.start(); + }) + .catch(onerror) + + }) + .catch(onerror) +}); diff --git a/tutorials/javascript-node/kurento-module-tests-api/test/CrowdDetector.js b/tutorials/javascript-node/kurento-module-tests-api/test/CrowdDetector.js new file mode 100644 index 0000000000..2f30235370 --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/test/CrowdDetector.js @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2013-2014 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. + * + */ + +/** + * + *

          + * Module tested: + *

            + *
          • CrowDetector + *
          + * + * + * @author Raúl Benítez "rbenitez" (raulbenitezmejias@gmail.com) + * @version 1.0.0 + * + */ + +if (typeof QUnit == 'undefined') { + QUnit = require('qunit-cli'); + QUnit.load(); + + kurentoClient = require('../node_modules/kurento-client/lib'); + + require('./_common'); +}; + +kurentoClient.register('kurento-module-crowddetector') + +if (QUnit.config.prefix == undefined) + QUnit.config.prefix = ''; + +QUnit.module(QUnit.config.prefix + 'CrowdDetector', lifecycle); + +QUnit.asyncTest('Create ' + QUnit.config.prefix + 'CrowdDetector', function () { + var self = this; + + QUnit.expect(5); + + function onerror(error) { + _onerror(error); + }; + + const RegionOfInterest = kurentoClient.getComplexType(QUnit.config.prefix + 'RegionOfInterest'); + const RegionOfInterestConfig = kurentoClient.getComplexType(QUnit.config.prefix + 'RegionOfInterestConfig'); + const RelativePoint = kurentoClient.getComplexType(QUnit.config.prefix + 'RelativePoint'); + + QUnit.notEqual(RegionOfInterest, undefined, 'RegionOfInterest'); + QUnit.notEqual(RegionOfInterestConfig, undefined, 'RegionOfInterestConfig'); + QUnit.notEqual(RelativePoint, undefined, 'RelativePoint'); + + + self.pipeline.create('WebRtcEndpoint', function (error, webRtcEndpoint) { + if (error) return onerror(error); + + QUnit.notEqual(webRtcEndpoint, undefined, 'webRtcEndpoint'); + + var options = { + rois: [ + RegionOfInterest({ + id: 'roi1', + points: [ + RelativePoint({x: 0 , y: 0 }), + RelativePoint({x: 0.5, y: 0 }), + RelativePoint({x: 0.5, y: 0.5}), + RelativePoint({x: 0 , y: 0.5}) + ], + regionOfInterestConfig: RegionOfInterestConfig({ + occupancyLevelMin: 10, + occupancyLevelMed: 35, + occupancyLevelMax: 65, + occupancyNumFramesToEvent: 5, + fluidityLevelMin: 10, + fluidityLevelMed: 35, + fluidityLevelMax: 65, + fluidityNumFramesToEvent: 5, + sendOpticalFlowEvent: false, + opticalFlowNumFramesToEvent: 3, + opticalFlowNumFramesToReset: 3, + opticalFlowAngleOffset: 0 + }) + }) + ] + } + + self.pipeline.create(QUnit.config.prefix + 'CrowdDetectorFilter', options, function (error, crowdDetectorFilter) { + if (error) return onerror(error); + + QUnit.notEqual(crowdDetectorFilter, undefined, 'CrowdDetectorFilter'); + + QUnit.start(); + }) + .catch(onerror) + + }) + .catch(onerror) +}); \ No newline at end of file diff --git a/tutorials/javascript-node/kurento-module-tests-api/test/PlateDetector.js b/tutorials/javascript-node/kurento-module-tests-api/test/PlateDetector.js new file mode 100644 index 0000000000..2b17c12e58 --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/test/PlateDetector.js @@ -0,0 +1,73 @@ +/* + * (C) Copyright 2013-2014 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. + * + */ + +/** + * + *

          + * Module tested: + *

            + *
          • PlateDetector + *
          + * + * + * @author Raúl Benítez "rbenitez" (raulbenitezmejias@gmail.com) + * @version 1.0.0 + * + */ + +if (typeof QUnit == 'undefined') { + QUnit = require('qunit-cli'); + QUnit.load(); + + kurentoClient = require('../node_modules/kurento-client/lib'); + + require('./_common'); +}; + +kurentoClient.register('kurento-module-platedetector') + +if (QUnit.config.prefix == undefined) + QUnit.config.prefix = ''; + +QUnit.module(QUnit.config.prefix + 'PlateDetector', lifecycle); + +QUnit.asyncTest('Create ' + QUnit.config.prefix + 'PlateDetector', function () { + var self = this; + + QUnit.expect(2); + + function onerror(error) { + _onerror(error); + }; + + self.pipeline.create('kurento.WebRtcEndpoint', function (error, webRtcEndpoint) { + if (error) return onerror(error); + + QUnit.notEqual(webRtcEndpoint, undefined, 'webRtcEndpoint'); + + self.pipeline.create(QUnit.config.prefix + 'PlateDetectorFilter', function (error, plateDetectorFilter) { + if (error) return onerror(error); + + QUnit.notEqual(plateDetectorFilter, undefined, 'PlateDetectorFilter'); + + QUnit.start(); + }) + .catch(onerror) + + }) + .catch(onerror) +}); \ No newline at end of file diff --git a/tutorials/javascript-node/kurento-module-tests-api/test/PointerDetector.js b/tutorials/javascript-node/kurento-module-tests-api/test/PointerDetector.js new file mode 100644 index 0000000000..00cdb0a6fd --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/test/PointerDetector.js @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2013-2014 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. + * + */ + +/** + * + *

          + * Module tested: + *

            + *
          • PointerDetector + *
          + * + * + * @author Raúl Benítez "rbenitez" (raulbenitezmejias@gmail.com) + * @version 1.0.0 + * + */ + +if (typeof QUnit == 'undefined') { + QUnit = require('qunit-cli'); + QUnit.load(); + + kurentoClient = require('../node_modules/kurento-client/lib'); + + require('./_common'); +}; + +kurentoClient.register('kurento-module-pointerdetector') + +if (QUnit.config.prefix == undefined) + QUnit.config.prefix = ''; + +QUnit.module(QUnit.config.prefix + 'PointerDetector', lifecycle); + +QUnit.asyncTest('Create ' + QUnit.config.prefix + 'PointerDetector', function () { + var self = this; + + QUnit.expect(4); + + function onerror(error) { + _onerror(error); + }; + + const PointerDetectorWindowMediaParam = kurentoClient.getComplexType('pointerdetector.PointerDetectorWindowMediaParam'); + const WindowParam = kurentoClient.getComplexType('pointerdetector.WindowParam'); + + QUnit.notEqual(PointerDetectorWindowMediaParam, undefined, 'PointerDetectorWindowMediaParam'); + QUnit.notEqual(WindowParam, undefined, 'WindowParam'); + + + self.pipeline.create('kurento.WebRtcEndpoint', function (error, webRtcEndpoint) { + if (error) return onerror(error); + + QUnit.notEqual(webRtcEndpoint, undefined, 'webRtcEndpoint'); + + var options = { + calibrationRegion: WindowParam({ + topRightCornerX: 5, + topRightCornerY:5, + width:30, + height: 30 + }) + }; + + self.pipeline.create(QUnit.config.prefix + 'PointerDetectorFilter', options, function (error, pointerDetectorFilter) { + if (error) return onerror(error); + + QUnit.notEqual(pointerDetectorFilter, undefined, 'PointerDetectorFilter'); + + QUnit.start(); + }) + .catch(onerror) + + }) + .catch(onerror) +}); \ No newline at end of file diff --git a/tutorials/javascript-node/kurento-module-tests-api/test/_common.js b/tutorials/javascript-node/kurento-module-tests-api/test/_common.js new file mode 100644 index 0000000000..a77fa6a048 --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/test/_common.js @@ -0,0 +1,356 @@ +/* + * (C) Copyright 2013-2014 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 Docker = require('dockerode'); +var spawn = require('child_process').spawn; + +URL_VIDEO_FILES = "http://files.openvidu.io/video/"; +URL_BARCODES = URL_VIDEO_FILES + "filter/barcodes.webm"; +URL_FIWARECUT = URL_VIDEO_FILES + "filter/fiwarecut.webm"; +URL_PLATES = URL_VIDEO_FILES + "filter/plates.webm"; +URL_POINTER_DETECTOR = URL_VIDEO_FILES + "filter/pointerDetector.mp4"; +URL_SMALL = URL_VIDEO_FILES + "format/small.webm"; + +/** + * Set an assert error and re-start the test so it can fail + */ +function onerror(error) { + if (error) + QUnit.pushFailure(error.message || error, error.stack); + + QUnit.start(); +}; + +_onerror = onerror; + +/** + * Do an asynchronous HTTP GET request both on Node.js & browser + */ +doGet = function doGet(url, onsuccess, onerror) { + // Node.js + if (typeof XMLHttpRequest == 'undefined') + require('http').get(url, onsuccess).on('error', onerror); + + // browser + else { + var xhr = new XMLHttpRequest(); + + xhr.open("get", url); + xhr.send(); + + xhr.addEventListener('load', function (event) { + onsuccess(xhr.response); + }); + xhr.addEventListener('error', onerror); + }; +}; + +/** + * Manage timeouts in an object-oriented style + */ +Timeout = function Timeout(id, delay, ontimeout) { + if (!(this instanceof Timeout)) + return new Timeout(id, delay, ontimeout); + + var timeout; + + function _ontimeout(message) { + this.stop(); + + ontimeout(message); + }; + + this.start = function () { + var delay_factor = delay * Timeout.factor; + + timeout = setTimeout(_ontimeout.bind(this), delay_factor, + 'Time out ' + id + ' (' + delay_factor + 'ms)'); + }; + + this.stop = function () { + clearTimeout(timeout); + }; +}; + +const REPORTS_DIR = 'reports' + +function writeReport(ext, data) { + var path = REPORTS_DIR + '/' + QUnit.config.module + "_" + QUnit.config.prefix + require( + '../package.json').name + '.' + ext + + require('fs-extra').outputFile(path, data, function (error) { + if (error) return console.trace(error); + + console.log(ext + ' report saved at ' + path); + }); +} + +function fetchReport(type, report) { + var ext = type + if (type == 'junit') ext = 'xml' + + report = report[ext] + + // Node.js - write report to file + if (typeof window === 'undefined') + writeReport(ext, report) + + // browser - write report to console + else { + var textarea = document.getElementById(type); + + textarea.value = report; + textarea.style.height = textarea.scrollHeight + "px"; + textarea.style.visibility = "visible"; + } +} + +// Check if use docker or local + +function isDockerContainer(callback) { + var isDocker = false; + var cat = spawn('cat', ['/proc/1/cgroup']) + .on('error', onerror) + + cat.stdout.on('data', function (data) { + var lines = data.toString('utf8').split('\n'); + for (var i = 0; i < lines.length; i++) { + if (lines[i].substr(lines[i].length - 1) != "") { + if (lines[i].substr(lines[i].length - 1) != "/") { + isDocker = true; + callback(isDocker); + return; + } + } + } + callback(isDocker); + }); + + cat.stderr.on('data', function (data) { + // The file is not exist + callback(isDocker); + }); +} + +function getIpDocker(callback) { + isDockerContainer(function (isDocker) { + var hostIp; + if (isDocker) { + var grep = spawn('grep', ['default']); + var ip = spawn('ip', ['route']); + + ip.stdout.pipe(grep.stdin); + grep.stdout.on('data', function (data) { + callback(data.toString( + "utf8").split(" ")[2]) + }); + } else { + var grep = spawn('grep', ['docker']); + var ip = spawn('ip', ['route']); + + ip.stdout.pipe(grep.stdin); + grep.stdout.on('data', function (data) { + var ips = data.toString( + "utf8").split(" "); + callback(ips[ips.length - 2]) + }); + } + }) +} + +function getopts(args, opts) { + var result = opts.default || {}; + args.replace( + new RegExp("([^?=&]+)(=([^&]*))?", "g"), + function ($0, $1, $2, $3) { + result[$1] = decodeURI($3); + }); + + return result; +}; + +// Only process arguments for browsers +try { + var args = getopts(location.search, { + default: { + + } + }); +} catch (e) {} + +QUnit.jUnitReport = fetchReport.bind(undefined, 'junit') +QUnit.lcovReport = fetchReport.bind(undefined, 'lcov') + +// Tell QUnit what WebSocket servers to use + +QUnit.config.urlConfig.push({ + id: "timeout_factor", + label: "Timeout factor", + value: { + '0.5': '0.5x', + '0.75': '0.75x', + '1': '1x', + '2': '2x', + '3': '3x', + '5': '5x', + '10': '10x' + }, + tooltip: "Multiply the timeouts window by this factor. Default is 1x" +}, { + id: "ws_uri", + label: "WebSocket server", + value: { + 'ws://127.0.0.1:8888/kurento': 'localhost (port 8888)' + }, + tooltip: "Exec the tests using a real WebSocket server instead of a mock" +}); + +var ws_uri; + +if (args != undefined && args.ws_uri != undefined) { + ws_uri = args.ws_uri; +} else { + ws_uri = QUnit.config.ws_uri; +} +var ws_port = QUnit.config.ws_port; +var scope = QUnit.config.scope; +var container; + +// Tests lifecycle + +lifecycle = { + setup: function () { + var self = this; + if (ws_uri == undefined) { + // var WebSocket = wock(proxy); + // ws_uri = new WebSocket(); + ws_uri = 'ws://127.0.0.1:8888/kurento'; + }; + + if (ws_port == undefined) { + ws_port = "8888"; + } + + if (scope == undefined) { + scope = "local"; + } + + if (scope == "local") { + Timeout.factor = parseFloat(QUnit.config.timeout_factor) || 1; + + QUnit.config.testTimeout = 30000 * Timeout.factor; + + var options = { + request_timeout: 5000 * Timeout.factor + }; + + this.kurento = new kurentoClient(ws_uri, options); + + this.kurento.then(function () { + this.create('MediaPipeline', function (error, pipeline) { + if (error) return onerror(error); + + self.pipeline = pipeline; + + QUnit.start(); + }); + }, + onerror); + } else if (scope == "docker") { + getIpDocker(function (ip) { + var hostIp = ip; + console.log("Docker IP:", hostIp); + docker = new Docker(); + docker.run('kurento/kurento-media-server:dev', [], [ + process.stdout, + process.stderr + ], { + Tty: false, + 'PortBindings': { + "8888/tcp": [{ + "HostIp": "", + "HostPort": ws_port.toString() + }] + } + }, function (err, data, container) { + if (err) console.error(err); + }).on('container', function (container_) { + container = container_; + container.inspect(function (err, data) { + container.inspect(function (err, data) { + container.inspect(function (err, data) { + var ipDocker = data.NetworkSettings + .IPAddress; + ipDocker = hostIp; + ws_uri = 'ws://' + + ipDocker + + ":" + ws_port + "/kurento"; + Timeout.factor = parseFloat(QUnit.config + .timeout_factor) || 1; + + QUnit.config.testTimeout = 30000 * + Timeout.factor; + + var options = { + request_timeout: 5000 * Timeout.factor + }; + + self.kurento = new kurentoClient(ws_uri, + options); + self.kurento.then(function () { + this.create('MediaPipeline', + function (error, pipeline) { + if (error) return onerror( + error); + + self.pipeline = pipeline; + + QUnit.start(); + }); + }, + onerror); + + }) + }) + }) + }); + }); + } + + QUnit.stop(); + }, + + teardown: function () { + if (scope == "docker") { + this.kurento.close(); + QUnit.stop(); + container.stop(function (error, data) { + console.log("Container KMS stopped.") + container.remove(function (error, data) { + console.log("Container KMS removed.") + QUnit.start(); + }) + }); + } else { + if (this.pipeline) + this.pipeline.release(function (error) { + if (error) console.error(error); + }); + this.kurento.close(); + } + } +}; diff --git a/tutorials/javascript-node/kurento-module-tests-api/test/readme.md b/tutorials/javascript-node/kurento-module-tests-api/test/readme.md new file mode 100644 index 0000000000..30d3d0f940 --- /dev/null +++ b/tutorials/javascript-node/kurento-module-tests-api/test/readme.md @@ -0,0 +1,28 @@ +[![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]. + +JavaScript Kurento Module Tests API +=================================== +Tests are autonomous, only requirement is to exec previously ```npm install``` +to have installed all the dev dependencies. + +## Environments + +### Node.js + +To exec test in Node.js, you only need to exec ```npm test``` that will launch +all the tests automatically using [QUnit-cli]. + +If you need to use a WebSocket endpoint different from the default one, you can exec the underlying test command with +```node_modules/.bin/qunit-cli -c kurentoClient:. -c wock:node_modules/wock -c test/_common.js -c test/_proxy.js test/*.js``` and append the *ws_uri* parameter. + + +[KurentoImage]: https://secure.gravatar.com/avatar/21a2a12c56b2a91c8918d5779f1778bf?s=120 +[Apache 2.0 License]: http://www.apache.org/licenses/LICENSE-2.0 +[website]: https://kurento.openvidu.io/ diff --git a/tutorials/javascript-node/kurento-one2many-call/README.md b/tutorials/javascript-node/kurento-one2many-call/README.md new file mode 100644 index 0000000000..9adb304720 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/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-one2many-call +===================== + +Kurento Node.js Tutorial: WebRTC one to many video call. + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/tutorial-one2many.html diff --git a/tutorials/javascript-node/kurento-one2many-call/keys/README.md b/tutorials/javascript-node/kurento-one2many-call/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/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/tutorials/javascript-node/kurento-one2many-call/keys/server.crt b/tutorials/javascript-node/kurento-one2many-call/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/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/tutorials/javascript-node/kurento-one2many-call/keys/server.csr b/tutorials/javascript-node/kurento-one2many-call/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/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/tutorials/javascript-node/kurento-one2many-call/keys/server.key b/tutorials/javascript-node/kurento-one2many-call/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/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/tutorials/javascript-node/kurento-one2many-call/package.json b/tutorials/javascript-node/kurento-one2many-call/package.json new file mode 100644 index 0000000000..868f7b20e1 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/package.json @@ -0,0 +1,17 @@ +{ + "name": "kurento-one2many-call", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "express": "^4.17.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "minimist": "^1.1.1", + "ws": "^7.1.0" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-one2many-call/server.js b/tutorials/javascript-node/kurento-one2many-call/server.js new file mode 100755 index 0000000000..5cd61d49ec --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/server.js @@ -0,0 +1,384 @@ +/* + * (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 path = require('path'); +var url = require('url'); +var express = require('express'); +var minimist = require('minimist'); +var ws = require('ws'); +var kurento = require('kurento-client'); +var fs = require('fs'); +var https = require('https'); + +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(); + +/* + * Definition of global variables. + */ +var idCounter = 0; +var candidatesQueue = {}; +var kurentoClient = null; +var presenter = null; +var viewers = []; +var noPresenterMessage = 'No active presenter. Try again later...'; + +/* + * 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 : '/one2many' +}); + +function nextUniqueId() { + idCounter++; + return idCounter.toString(); +} + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws) { + + var sessionId = nextUniqueId(); + console.log('Connection received with sessionId ' + sessionId); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'presenter': + startPresenter(sessionId, ws, message.sdpOffer, function(error, sdpAnswer) { + if (error) { + return ws.send(JSON.stringify({ + id : 'presenterResponse', + response : 'rejected', + message : error + })); + } + ws.send(JSON.stringify({ + id : 'presenterResponse', + response : 'accepted', + sdpAnswer : sdpAnswer + })); + }); + break; + + case 'viewer': + startViewer(sessionId, ws, message.sdpOffer, function(error, sdpAnswer) { + if (error) { + return ws.send(JSON.stringify({ + id : 'viewerResponse', + response : 'rejected', + message : error + })); + } + + ws.send(JSON.stringify({ + id : 'viewerResponse', + response : 'accepted', + sdpAnswer : sdpAnswer + })); + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function startPresenter(sessionId, ws, sdpOffer, callback) { + clearCandidatesQueue(sessionId); + + if (presenter !== null) { + stop(sessionId); + return callback("Another user is currently acting as presenter. Try again later ..."); + } + + presenter = { + id : sessionId, + pipeline : null, + webRtcEndpoint : null + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + stop(sessionId); + return callback(error); + } + + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + stop(sessionId); + return callback(error); + } + + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + presenter.pipeline = pipeline; + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + stop(sessionId); + return callback(error); + } + + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + presenter.webRtcEndpoint = webRtcEndpoint; + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + stop(sessionId); + return callback(error); + } + + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + callback(null, sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + stop(sessionId); + return callback(error); + } + }); + }); + }); + }); +} + +function startViewer(sessionId, ws, sdpOffer, callback) { + clearCandidatesQueue(sessionId); + + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + presenter.pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + stop(sessionId); + return callback(error); + } + viewers[sessionId] = { + "webRtcEndpoint" : webRtcEndpoint, + "ws" : ws + } + + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + stop(sessionId); + return callback(error); + } + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + presenter.webRtcEndpoint.connect(webRtcEndpoint, function(error) { + if (error) { + stop(sessionId); + return callback(error); + } + if (presenter === null) { + stop(sessionId); + return callback(noPresenterMessage); + } + + callback(null, sdpAnswer); + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + stop(sessionId); + return callback(error); + } + }); + }); + }); + }); +} + +function clearCandidatesQueue(sessionId) { + if (candidatesQueue[sessionId]) { + delete candidatesQueue[sessionId]; + } +} + +function stop(sessionId) { + if (presenter !== null && presenter.id == sessionId) { + for (var i in viewers) { + var viewer = viewers[i]; + if (viewer.ws) { + viewer.ws.send(JSON.stringify({ + id : 'stopCommunication' + })); + } + } + presenter.pipeline.release(); + presenter = null; + viewers = []; + + } else if (viewers[sessionId]) { + viewers[sessionId].webRtcEndpoint.release(); + delete viewers[sessionId]; + } + + clearCandidatesQueue(sessionId); + + if (viewers.length < 1 && !presenter) { + console.log('Closing kurento client'); + kurentoClient.close(); + kurentoClient = null; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + + if (presenter && presenter.id === sessionId && presenter.webRtcEndpoint) { + console.info('Sending presenter candidate'); + presenter.webRtcEndpoint.addIceCandidate(candidate); + } + else if (viewers[sessionId] && viewers[sessionId].webRtcEndpoint) { + console.info('Sending viewer candidate'); + viewers[sessionId].webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-one2many-call/static/bower.json b/tutorials/javascript-node/kurento-one2many-call/static/bower.json new file mode 100644 index 0000000000..5a6bafe5a6 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-one2many-call", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-one2many-call/static/css/kurento.css b/tutorials/javascript-node/kurento-one2many-call/static/css/kurento.css new file mode 100644 index 0000000000..bd3ff15da6 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/static/css/kurento.css @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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; +} + +#videoContainer { + position: absolute; + float: left; +} + +#videoBig { + width: 640px; + height: 480px; + top: 0; + left: 0; + z-index: 1; +} + +div#videoSmall { + width: 240px; + height: 180px; + padding: 0px; + position: absolute; + top: 15px; + left: 400px; + cursor: pointer; + z-index: 10; + padding: 0px; +} + +div.dragged { + cursor: all-scroll !important; + border-color: blue !important; + z-index: 10 !important; +} diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/kurento.png b/tutorials/javascript-node/kurento-one2many-call/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/naevatec.png b/tutorials/javascript-node/kurento-one2many-call/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/pipeline.png b/tutorials/javascript-node/kurento-one2many-call/static/img/pipeline.png new file mode 100644 index 0000000000..3360c0d8ac Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/spinner.gif b/tutorials/javascript-node/kurento-one2many-call/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-one2many-call/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/urjc.gif b/tutorials/javascript-node/kurento-one2many-call/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/img/webrtc.png b/tutorials/javascript-node/kurento-one2many-call/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-one2many-call/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-one2many-call/static/index.html b/tutorials/javascript-node/kurento-one2many-call/static/index.html new file mode 100644 index 0000000000..199144c67e --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/static/index.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial 3: Video Call 1 to N with WebRTC + + + +
          + +
          + +
          + +
          +
          + +
          +

          +
          +
            +
            +
            +
            +
            + +
            +
            +
            +
            + + + + + diff --git a/tutorials/javascript-node/kurento-one2many-call/static/js/index.js b/tutorials/javascript-node/kurento-one2many-call/static/js/index.js new file mode 100644 index 0000000000..09c4c4a1e0 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2many-call/static/js/index.js @@ -0,0 +1,186 @@ +/* + * (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 + '/one2many'); +var video; +var webRtcPeer; + +window.onload = function() { + console = new Console(); + video = document.getElementById('video'); + + document.getElementById('call').addEventListener('click', function() { presenter(); } ); + document.getElementById('viewer').addEventListener('click', function() { viewer(); } ); + document.getElementById('terminate').addEventListener('click', function() { stop(); } ); +} + +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 'presenterResponse': + presenterResponse(parsedMessage); + break; + case 'viewerResponse': + viewerResponse(parsedMessage); + break; + case 'stopCommunication': + dispose(); + break; + case 'iceCandidate': + webRtcPeer.addIceCandidate(parsedMessage.candidate) + break; + default: + console.error('Unrecognized message', parsedMessage); + } +} + +function presenterResponse(message) { + if (message.response != 'accepted') { + var errorMsg = message.message ? message.message : 'Unknow error'; + console.warn('Call not accepted for the following reason: ' + errorMsg); + dispose(); + } else { + webRtcPeer.processAnswer(message.sdpAnswer); + } +} + +function viewerResponse(message) { + if (message.response != 'accepted') { + var errorMsg = message.message ? message.message : 'Unknow error'; + console.warn('Call not accepted for the following reason: ' + errorMsg); + dispose(); + } else { + webRtcPeer.processAnswer(message.sdpAnswer); + } +} + +function presenter() { + if (!webRtcPeer) { + showSpinner(video); + + var options = { + localVideo: video, + onicecandidate : onIceCandidate + } + + webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendonly(options, function(error) { + if(error) return onError(error); + + this.generateOffer(onOfferPresenter); + }); + } +} + +function onOfferPresenter(error, offerSdp) { + if (error) return onError(error); + + var message = { + id : 'presenter', + sdpOffer : offerSdp + }; + sendMessage(message); +} + +function viewer() { + if (!webRtcPeer) { + showSpinner(video); + + var options = { + remoteVideo: video, + onicecandidate : onIceCandidate + } + + webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, function(error) { + if(error) return onError(error); + + this.generateOffer(onOfferViewer); + }); + } +} + +function onOfferViewer(error, offerSdp) { + if (error) return onError(error) + + var message = { + id : 'viewer', + sdpOffer : offerSdp + } + sendMessage(message); +} + +function onIceCandidate(candidate) { + console.log('Local candidate' + JSON.stringify(candidate)); + + var message = { + id : 'onIceCandidate', + candidate : candidate + } + sendMessage(message); +} + +function stop() { + if (webRtcPeer) { + var message = { + id : 'stop' + } + sendMessage(message); + dispose(); + } +} + +function dispose() { + if (webRtcPeer) { + webRtcPeer.dispose(); + webRtcPeer = null; + } + hideSpinner(video); +} + +function sendMessage(message) { + var jsonMessage = JSON.stringify(message); + console.log('Sending 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/tutorials/javascript-node/kurento-one2one-call/README.md b/tutorials/javascript-node/kurento-one2one-call/README.md new file mode 100644 index 0000000000..e173d63fc6 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/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-one2one-call +==================== + +Kurento Node.js Tutorial: WebRTC one to one video call. + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/tutorial-one2one.html diff --git a/tutorials/javascript-node/kurento-one2one-call/keys/README.md b/tutorials/javascript-node/kurento-one2one-call/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/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/tutorials/javascript-node/kurento-one2one-call/keys/server.crt b/tutorials/javascript-node/kurento-one2one-call/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/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/tutorials/javascript-node/kurento-one2one-call/keys/server.csr b/tutorials/javascript-node/kurento-one2one-call/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/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/tutorials/javascript-node/kurento-one2one-call/keys/server.key b/tutorials/javascript-node/kurento-one2one-call/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/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/tutorials/javascript-node/kurento-one2one-call/package.json b/tutorials/javascript-node/kurento-one2one-call/package.json new file mode 100644 index 0000000000..9225ca4964 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/package.json @@ -0,0 +1,17 @@ +{ + "name": "kurento-one2one-call", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "express": "^4.17.0", + "minimist": "^1.1.1", + "ws": "^7.1.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-one2one-call/server.js b/tutorials/javascript-node/kurento-one2one-call/server.js new file mode 100755 index 0000000000..ed21b11f50 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/server.js @@ -0,0 +1,457 @@ +/* + * (C) Copyright 2014 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 path = require('path'); +var express = require('express'); +var ws = require('ws'); +var minimist = require('minimist'); +var url = require('url'); +var kurento = require('kurento-client'); +var fs = require('fs'); +var https = require('https'); + +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(); + +/* + * Definition of global variables. + */ + +var kurentoClient = null; +var userRegistry = new UserRegistry(); +var pipelines = {}; +var candidatesQueue = {}; +var idCounter = 0; + +function nextUniqueId() { + idCounter++; + return idCounter.toString(); +} + +/* + * Definition of helper classes + */ + +// Represents caller and callee sessions +function UserSession(id, name, ws) { + this.id = id; + this.name = name; + this.ws = ws; + this.peer = null; + this.sdpOffer = null; +} + +UserSession.prototype.sendMessage = function(message) { + this.ws.send(JSON.stringify(message)); +} + +// Represents registrar of users +function UserRegistry() { + this.usersById = {}; + this.usersByName = {}; +} + +UserRegistry.prototype.register = function(user) { + this.usersById[user.id] = user; + this.usersByName[user.name] = user; +} + +UserRegistry.prototype.unregister = function(id) { + var user = this.getById(id); + if (user) delete this.usersById[id] + if (user && this.getByName(user.name)) delete this.usersByName[user.name]; +} + +UserRegistry.prototype.getById = function(id) { + return this.usersById[id]; +} + +UserRegistry.prototype.getByName = function(name) { + return this.usersByName[name]; +} + +UserRegistry.prototype.removeById = function(id) { + var userSession = this.usersById[id]; + if (!userSession) return; + delete this.usersById[id]; + delete this.usersByName[userSession.name]; +} + +// Represents a B2B active call +function CallMediaPipeline() { + this.pipeline = null; + this.webRtcEndpoint = {}; +} + +CallMediaPipeline.prototype.createPipeline = function(callerId, calleeId, ws, callback) { + var self = this; + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + pipeline.create('WebRtcEndpoint', function(error, callerWebRtcEndpoint) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[callerId]) { + while(candidatesQueue[callerId].length) { + var candidate = candidatesQueue[callerId].shift(); + callerWebRtcEndpoint.addIceCandidate(candidate); + } + } + + callerWebRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + userRegistry.getById(callerId).ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + pipeline.create('WebRtcEndpoint', function(error, calleeWebRtcEndpoint) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[calleeId]) { + while(candidatesQueue[calleeId].length) { + var candidate = candidatesQueue[calleeId].shift(); + calleeWebRtcEndpoint.addIceCandidate(candidate); + } + } + + calleeWebRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + userRegistry.getById(calleeId).ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + callerWebRtcEndpoint.connect(calleeWebRtcEndpoint, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + calleeWebRtcEndpoint.connect(callerWebRtcEndpoint, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + }); + + self.pipeline = pipeline; + self.webRtcEndpoint[callerId] = callerWebRtcEndpoint; + self.webRtcEndpoint[calleeId] = calleeWebRtcEndpoint; + callback(null); + }); + }); + }); + }); + }) +} + +CallMediaPipeline.prototype.generateSdpAnswer = function(id, sdpOffer, callback) { + this.webRtcEndpoint[id].processOffer(sdpOffer, callback); + this.webRtcEndpoint[id].gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); +} + +CallMediaPipeline.prototype.release = function() { + if (this.pipeline) this.pipeline.release(); + this.pipeline = null; +} + +/* + * 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 : '/one2one' +}); + +wss.on('connection', function(ws) { + var sessionId = nextUniqueId(); + console.log('Connection received with sessionId ' + sessionId); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + userRegistry.unregister(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'register': + register(sessionId, message.name, ws); + break; + + case 'call': + call(sessionId, message.to, message.from, message.sdpOffer); + break; + + case 'incomingCallResponse': + incomingCallResponse(sessionId, message.from, message.callResponse, message.sdpOffer, ws); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + var message = 'Coult not find media server at address ' + argv.ws_uri; + return callback(message + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function stop(sessionId) { + if (!pipelines[sessionId]) { + return; + } + + var pipeline = pipelines[sessionId]; + delete pipelines[sessionId]; + pipeline.release(); + var stopperUser = userRegistry.getById(sessionId); + var stoppedUser = userRegistry.getByName(stopperUser.peer); + stopperUser.peer = null; + + if (stoppedUser) { + stoppedUser.peer = null; + delete pipelines[stoppedUser.id]; + var message = { + id: 'stopCommunication', + message: 'remote user hanged out' + } + stoppedUser.sendMessage(message) + } + + clearCandidatesQueue(sessionId); +} + +function incomingCallResponse(calleeId, from, callResponse, calleeSdp, ws) { + + clearCandidatesQueue(calleeId); + + function onError(callerReason, calleeReason) { + if (pipeline) pipeline.release(); + if (caller) { + var callerMessage = { + id: 'callResponse', + response: 'rejected' + } + if (callerReason) callerMessage.message = callerReason; + caller.sendMessage(callerMessage); + } + + var calleeMessage = { + id: 'stopCommunication' + }; + if (calleeReason) calleeMessage.message = calleeReason; + callee.sendMessage(calleeMessage); + } + + var callee = userRegistry.getById(calleeId); + if (!from || !userRegistry.getByName(from)) { + return onError(null, 'unknown from = ' + from); + } + var caller = userRegistry.getByName(from); + + if (callResponse === 'accept') { + var pipeline = new CallMediaPipeline(); + pipelines[caller.id] = pipeline; + pipelines[callee.id] = pipeline; + + pipeline.createPipeline(caller.id, callee.id, ws, function(error) { + if (error) { + return onError(error, error); + } + + pipeline.generateSdpAnswer(caller.id, caller.sdpOffer, function(error, callerSdpAnswer) { + if (error) { + return onError(error, error); + } + + pipeline.generateSdpAnswer(callee.id, calleeSdp, function(error, calleeSdpAnswer) { + if (error) { + return onError(error, error); + } + + var message = { + id: 'startCommunication', + sdpAnswer: calleeSdpAnswer + }; + callee.sendMessage(message); + + message = { + id: 'callResponse', + response : 'accepted', + sdpAnswer: callerSdpAnswer + }; + caller.sendMessage(message); + }); + }); + }); + } else { + var decline = { + id: 'callResponse', + response: 'rejected', + message: 'user declined' + }; + caller.sendMessage(decline); + } +} + +function call(callerId, to, from, sdpOffer) { + clearCandidatesQueue(callerId); + + var caller = userRegistry.getById(callerId); + var rejectCause = 'User ' + to + ' is not registered'; + if (userRegistry.getByName(to)) { + var callee = userRegistry.getByName(to); + caller.sdpOffer = sdpOffer + callee.peer = from; + caller.peer = to; + var message = { + id: 'incomingCall', + from: from + }; + try{ + return callee.sendMessage(message); + } catch(exception) { + rejectCause = "Error " + exception; + } + } + var message = { + id: 'callResponse', + response: 'rejected: ', + message: rejectCause + }; + caller.sendMessage(message); +} + +function register(id, name, ws, callback) { + function onError(error) { + ws.send(JSON.stringify({id:'registerResponse', response : 'rejected ', message: error})); + } + + if (!name) { + return onError("empty user name"); + } + + if (userRegistry.getByName(name)) { + return onError("User " + name + " is already registered"); + } + + userRegistry.register(new UserSession(id, name, ws)); + try { + ws.send(JSON.stringify({id: 'registerResponse', response: 'accepted'})); + } catch(exception) { + onError(exception); + } +} + +function clearCandidatesQueue(sessionId) { + if (candidatesQueue[sessionId]) { + delete candidatesQueue[sessionId]; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + var user = userRegistry.getById(sessionId); + + if (pipelines[user.id] && pipelines[user.id].webRtcEndpoint && pipelines[user.id].webRtcEndpoint[user.id]) { + var webRtcEndpoint = pipelines[user.id].webRtcEndpoint[user.id]; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + if (!candidatesQueue[user.id]) { + candidatesQueue[user.id] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-one2one-call/static/bower.json b/tutorials/javascript-node/kurento-one2one-call/static/bower.json new file mode 100644 index 0000000000..9d92016bc0 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/static/bower.json @@ -0,0 +1,29 @@ +{ + "name": "kurento-one2one-call", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "draggabilly": "~1.2.4", + "demo-console": "1.5.1", + "ekko-lightbox": "~3.3.0", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-one2one-call/static/css/kurento.css b/tutorials/javascript-node/kurento-one2one-call/static/css/kurento.css new file mode 100644 index 0000000000..bd3ff15da6 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/static/css/kurento.css @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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; +} + +#videoContainer { + position: absolute; + float: left; +} + +#videoBig { + width: 640px; + height: 480px; + top: 0; + left: 0; + z-index: 1; +} + +div#videoSmall { + width: 240px; + height: 180px; + padding: 0px; + position: absolute; + top: 15px; + left: 400px; + cursor: pointer; + z-index: 10; + padding: 0px; +} + +div.dragged { + cursor: all-scroll !important; + border-color: blue !important; + z-index: 10 !important; +} diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/kurento.png b/tutorials/javascript-node/kurento-one2one-call/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/naevatec.png b/tutorials/javascript-node/kurento-one2one-call/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/pipeline.png b/tutorials/javascript-node/kurento-one2one-call/static/img/pipeline.png new file mode 100644 index 0000000000..25e02cccfd Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/spinner.gif b/tutorials/javascript-node/kurento-one2one-call/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-one2one-call/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/urjc.gif b/tutorials/javascript-node/kurento-one2one-call/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/img/webrtc.png b/tutorials/javascript-node/kurento-one2one-call/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-one2one-call/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-one2one-call/static/index.html b/tutorials/javascript-node/kurento-one2one-call/static/index.html new file mode 100644 index 0000000000..5a559775b3 --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/static/index.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial 4: Video Call 1 to 1 with WebRTC + + + +
            + +
            + +
            + +
            +
            + +
            +
            + +
            + +
            +
            +
            + +
            +
            + +
            + +
            +
            +

            +
            +
              +
              +
              +
              +
              + +
              +
              + +
              +
              +
              +
              + + + + + diff --git a/tutorials/javascript-node/kurento-one2one-call/static/js/index.js b/tutorials/javascript-node/kurento-one2one-call/static/js/index.js new file mode 100644 index 0000000000..0e0cefd47f --- /dev/null +++ b/tutorials/javascript-node/kurento-one2one-call/static/js/index.js @@ -0,0 +1,328 @@ +/* + * (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 + '/one2one'); +var videoInput; +var videoOutput; +var webRtcPeer; + +var registerName = null; +const NOT_REGISTERED = 0; +const REGISTERING = 1; +const REGISTERED = 2; +var registerState = null + +function setRegisterState(nextState) { + switch (nextState) { + case NOT_REGISTERED: + $('#register').attr('disabled', false); + $('#call').attr('disabled', true); + $('#terminate').attr('disabled', true); + break; + + case REGISTERING: + $('#register').attr('disabled', true); + break; + + case REGISTERED: + $('#register').attr('disabled', true); + setCallState(NO_CALL); + break; + + default: + return; + } + registerState = nextState; +} + +const NO_CALL = 0; +const PROCESSING_CALL = 1; +const IN_CALL = 2; +var callState = null + +function setCallState(nextState) { + switch (nextState) { + case NO_CALL: + $('#call').attr('disabled', false); + $('#terminate').attr('disabled', true); + break; + + case PROCESSING_CALL: + $('#call').attr('disabled', true); + $('#terminate').attr('disabled', true); + break; + case IN_CALL: + $('#call').attr('disabled', true); + $('#terminate').attr('disabled', false); + break; + default: + return; + } + callState = nextState; +} + +window.onload = function() { + console = new Console(); + setRegisterState(NOT_REGISTERED); + var drag = new Draggabilly(document.getElementById('videoSmall')); + videoInput = document.getElementById('videoInput'); + videoOutput = document.getElementById('videoOutput'); + document.getElementById('name').focus(); + + document.getElementById('register').addEventListener('click', function() { + register(); + }); + document.getElementById('call').addEventListener('click', function() { + call(); + }); + document.getElementById('terminate').addEventListener('click', function() { + stop(); + }); +} + +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 'registerResponse': + resgisterResponse(parsedMessage); + break; + case 'callResponse': + callResponse(parsedMessage); + break; + case 'incomingCall': + incomingCall(parsedMessage); + break; + case 'startCommunication': + startCommunication(parsedMessage); + break; + case 'stopCommunication': + console.info("Communication ended by remote peer"); + stop(true); + break; + case 'iceCandidate': + webRtcPeer.addIceCandidate(parsedMessage.candidate) + break; + default: + console.error('Unrecognized message', parsedMessage); + } +} + +function resgisterResponse(message) { + if (message.response == 'accepted') { + setRegisterState(REGISTERED); + } else { + setRegisterState(NOT_REGISTERED); + var errorMessage = message.message ? message.message + : 'Unknown reason for register rejection.'; + console.log(errorMessage); + alert('Error registering user. See console for further information.'); + } +} + +function callResponse(message) { + if (message.response != 'accepted') { + console.info('Call not accepted by peer. Closing call'); + var errorMessage = message.message ? message.message + : 'Unknown reason for call rejection.'; + console.log(errorMessage); + stop(true); + } else { + setCallState(IN_CALL); + webRtcPeer.processAnswer(message.sdpAnswer); + } +} + +function startCommunication(message) { + setCallState(IN_CALL); + webRtcPeer.processAnswer(message.sdpAnswer); +} + +function incomingCall(message) { + // If bussy just reject without disturbing user + if (callState != NO_CALL) { + var response = { + id : 'incomingCallResponse', + from : message.from, + callResponse : 'reject', + message : 'bussy' + + }; + return sendMessage(response); + } + + setCallState(PROCESSING_CALL); + if (confirm('User ' + message.from + + ' is calling you. Do you accept the call?')) { + showSpinner(videoInput, videoOutput); + + var options = { + localVideo : videoInput, + remoteVideo : videoOutput, + onicecandidate : onIceCandidate + } + + webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, + function(error) { + if (error) { + console.error(error); + setCallState(NO_CALL); + } + + this.generateOffer(function(error, offerSdp) { + if (error) { + console.error(error); + setCallState(NO_CALL); + } + var response = { + id : 'incomingCallResponse', + from : message.from, + callResponse : 'accept', + sdpOffer : offerSdp + }; + sendMessage(response); + }); + }); + + } else { + var response = { + id : 'incomingCallResponse', + from : message.from, + callResponse : 'reject', + message : 'user declined' + }; + sendMessage(response); + stop(true); + } +} + +function register() { + var name = document.getElementById('name').value; + if (name == '') { + window.alert("You must insert your user name"); + return; + } + + setRegisterState(REGISTERING); + + var message = { + id : 'register', + name : name + }; + sendMessage(message); + document.getElementById('peer').focus(); +} + +function call() { + if (document.getElementById('peer').value == '') { + window.alert("You must specify the peer name"); + return; + } + + setCallState(PROCESSING_CALL); + + showSpinner(videoInput, videoOutput); + + var options = { + localVideo : videoInput, + remoteVideo : videoOutput, + onicecandidate : onIceCandidate + } + + webRtcPeer = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, function( + error) { + if (error) { + console.error(error); + setCallState(NO_CALL); + } + + this.generateOffer(function(error, offerSdp) { + if (error) { + console.error(error); + setCallState(NO_CALL); + } + var message = { + id : 'call', + from : document.getElementById('name').value, + to : document.getElementById('peer').value, + sdpOffer : offerSdp + }; + sendMessage(message); + }); + }); + +} + +function stop(message) { + setCallState(NO_CALL); + if (webRtcPeer) { + webRtcPeer.dispose(); + webRtcPeer = null; + + if (!message) { + var message = { + id : 'stop' + } + sendMessage(message); + } + } + hideSpinner(videoInput, videoOutput); +} + +function sendMessage(message) { + var jsonMessage = JSON.stringify(message); + console.log('Sending message: ' + jsonMessage); + ws.send(jsonMessage); +} + +function onIceCandidate(candidate) { + console.log('Local candidate' + JSON.stringify(candidate)); + + var message = { + id : 'onIceCandidate', + candidate : candidate + } + sendMessage(message); +} + +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/tutorials/javascript-node/kurento-platedetector/README.md b/tutorials/javascript-node/kurento-platedetector/README.md new file mode 100644 index 0000000000..d79214591d --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/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-platedetector +===================== + +Kurento Node.js Tutorial: WebRTC in loopback with plate detector filter. + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/module-platedetector.html diff --git a/tutorials/javascript-node/kurento-platedetector/keys/README.md b/tutorials/javascript-node/kurento-platedetector/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/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/tutorials/javascript-node/kurento-platedetector/keys/server.crt b/tutorials/javascript-node/kurento-platedetector/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/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/tutorials/javascript-node/kurento-platedetector/keys/server.csr b/tutorials/javascript-node/kurento-platedetector/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/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/tutorials/javascript-node/kurento-platedetector/keys/server.key b/tutorials/javascript-node/kurento-platedetector/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/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/tutorials/javascript-node/kurento-platedetector/package.json b/tutorials/javascript-node/kurento-platedetector/package.json new file mode 100644 index 0000000000..d25f745cc4 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/package.json @@ -0,0 +1,20 @@ +{ + "name": "kurento-platedetector", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "^4.17.0", + "express-session": "^1.17.0", + "minimist": "^1.1.1", + "ws": "^7.1.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "kurento-module-platedetector": "git+https://github.com/Kurento/kurento-module-platedetector-js.git" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-platedetector/server.js b/tutorials/javascript-node/kurento-platedetector/server.js new file mode 100755 index 0000000000..9620aaf1ee --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/server.js @@ -0,0 +1,308 @@ +/* + * (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 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'); +kurento.register('kurento-module-platedetector'); + +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 = {}; +var kurentoClient = null; + +/* + * 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 : '/' +}); + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws, req) { + var sessionId = null; + var request = req; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, function(err) { + sessionId = request.session.id; + console.log('Connection received with sessionId ' + sessionId); + }); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'start': + sessionId = request.session.id; + start(sessionId, ws, message.sdpOffer, function(error, type, data) { + if (error) { + return ws.send(JSON.stringify({ + id : 'error', + message : error + })); + } + switch (type) { + case 'sdpAnswer': + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : data + })); + break; + case 'plateDetected': + ws.send(JSON.stringify({ + id : 'plateDetected', + data : data + })); + break; + } + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function start(sessionId, ws, sdpOffer, callback) { + if (!sessionId) { + return callback('Cannot use undefined sessionId'); + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + createMediaElements(pipeline, ws, function(error, webRtcEndpoint, filter) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + connectMediaElements(webRtcEndpoint, filter, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + filter.on('PlateDetected', function (data){ + return callback(null, 'plateDetected', data); + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + pipeline.release(); + return callback(error); + } + + sessions[sessionId] = { + 'pipeline' : pipeline, + 'webRtcEndpoint' : webRtcEndpoint + } + return callback(null, 'sdpAnswer', sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); + }); + }); + }); + }); +} + +function createMediaElements(pipeline, ws, callback) { + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + return callback(error); + } + + pipeline.create('platedetector.PlateDetectorFilter', function(error, filter) { + if (error) { + return callback(error); + } + + return callback(null, webRtcEndpoint, filter); + }); + }); +} + +function connectMediaElements(webRtcEndpoint, filter, callback) { + webRtcEndpoint.connect(filter, function(error) { + if (error) { + return callback(error); + } + + filter.connect(webRtcEndpoint, function(error) { + if (error) { + return callback(error); + } + + return callback(null); + }); + }); +} + +function stop(sessionId) { + if (sessions[sessionId]) { + var pipeline = sessions[sessionId].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sessionId]; + delete candidatesQueue[sessionId]; + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + + if (sessions[sessionId]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sessionId].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-platedetector/static/bower.json b/tutorials/javascript-node/kurento-platedetector/static/bower.json new file mode 100644 index 0000000000..831e11cdc4 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-platedetector", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-platedetector/static/css/kurento.css b/tutorials/javascript-node/kurento-platedetector/static/css/kurento.css new file mode 100644 index 0000000000..6ae4fcc9c6 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/static/css/kurento.css @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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/tutorials/javascript-node/kurento-platedetector/static/img/kurento.png b/tutorials/javascript-node/kurento-platedetector/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/img/naevatec.png b/tutorials/javascript-node/kurento-platedetector/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/img/pipeline.png b/tutorials/javascript-node/kurento-platedetector/static/img/pipeline.png new file mode 100644 index 0000000000..c00c7a7b86 Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/img/spinner.gif b/tutorials/javascript-node/kurento-platedetector/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-platedetector/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/img/urjc.gif b/tutorials/javascript-node/kurento-platedetector/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/img/webrtc.png b/tutorials/javascript-node/kurento-platedetector/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-platedetector/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-platedetector/static/index.html b/tutorials/javascript-node/kurento-platedetector/static/index.html new file mode 100644 index 0000000000..4c679eac4f --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/static/index.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial: WebRTC Plate Detector + + + +
              + +
              + +
              + +
              +
              +

              Local stream

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

              Remote stream

              + +
              +
              +
              +
              +

              +
              +
                +
                +
                +
                +
                + + + + + diff --git a/tutorials/javascript-node/kurento-platedetector/static/js/index.js b/tutorials/javascript-node/kurento-platedetector/static/js/index.js new file mode 100644 index 0000000000..6d7d1ac0d0 --- /dev/null +++ b/tutorials/javascript-node/kurento-platedetector/static/js/index.js @@ -0,0 +1,195 @@ +/* + * (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); +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 'plateDetected': + plateDetected(parsedMessage); + 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 plateDetected(message) { + console.log("License plate detected " + message.data.plate); +} + +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('Sending 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/tutorials/javascript-node/kurento-pointerdetector/README.md b/tutorials/javascript-node/kurento-pointerdetector/README.md new file mode 100644 index 0000000000..fbd7025fe3 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/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-pointerdetector +======================= + +Kurento Node.js Tutorial: WebRTC in loopback with a pointer detector filter. + +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: https://kurento.openvidu.io/. + +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]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/ +[Kurento Blog]: https://kurento.openvidu.io/blog +[Kurento FIWARE Catalog Entry]: http://catalogue.fiware.org/enablers/stream-oriented-kurento +[Kurento Netiquette Guidelines]: https://kurento.openvidu.io/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]: https://kurento.openvidu.io/docs/current/tutorials/node/module-pointerdetector.html diff --git a/tutorials/javascript-node/kurento-pointerdetector/keys/README.md b/tutorials/javascript-node/kurento-pointerdetector/keys/README.md new file mode 100644 index 0000000000..9603ced356 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/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/tutorials/javascript-node/kurento-pointerdetector/keys/server.crt b/tutorials/javascript-node/kurento-pointerdetector/keys/server.crt new file mode 100644 index 0000000000..65e608dad5 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/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/tutorials/javascript-node/kurento-pointerdetector/keys/server.csr b/tutorials/javascript-node/kurento-pointerdetector/keys/server.csr new file mode 100644 index 0000000000..6615b13047 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/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/tutorials/javascript-node/kurento-pointerdetector/keys/server.key b/tutorials/javascript-node/kurento-pointerdetector/keys/server.key new file mode 100644 index 0000000000..a69a0a279d --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/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/tutorials/javascript-node/kurento-pointerdetector/package.json b/tutorials/javascript-node/kurento-pointerdetector/package.json new file mode 100644 index 0000000000..fb5e14f2db --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/package.json @@ -0,0 +1,20 @@ +{ + "name": "kurento-pointerdetector", + "version": "6.18.1-dev", + "private": true, + "scripts": { + "postinstall": "cd static && bower install" + }, + "dependencies": { + "cookie-parser": "^1.3.5", + "express": "^4.17.0", + "express-session": "^1.17.0", + "minimist": "^1.1.1", + "ws": "^7.1.0", + "kurento-client": "git+https://github.com/Kurento/kurento-client-js.git", + "kurento-module-pointerdetector": "git+https://github.com/Kurento/kurento-module-pointerdetector-js.git" + }, + "devDependencies": { + "bower": "^1.4.1" + } +} diff --git a/tutorials/javascript-node/kurento-pointerdetector/server.js b/tutorials/javascript-node/kurento-pointerdetector/server.js new file mode 100755 index 0000000000..3327343107 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/server.js @@ -0,0 +1,373 @@ +/* + * (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 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'); +kurento.register('kurento-module-pointerdetector'); + +const PointerDetectorWindowMediaParam = kurento.getComplexType('pointerdetector.PointerDetectorWindowMediaParam'); +const WindowParam = kurento.getComplexType('pointerdetector.WindowParam'); + +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 = {}; +var kurentoClient = null; + +/* + * 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 : '/' +}); + +/* + * Management of WebSocket messages + */ +wss.on('connection', function(ws, req) { + var sessionId = null; + var request = req; + var response = { + writeHead : {} + }; + + sessionHandler(request, response, function(err) { + sessionId = request.session.id; + console.log('Connection received with sessionId ' + sessionId); + }); + + ws.on('error', function(error) { + console.log('Connection ' + sessionId + ' error'); + stop(sessionId); + }); + + ws.on('close', function() { + console.log('Connection ' + sessionId + ' closed'); + stop(sessionId); + }); + + ws.on('message', function(_message) { + var message = JSON.parse(_message); + console.log('Connection ' + sessionId + ' received message ', message); + + switch (message.id) { + case 'start': + sessionId = request.session.id; + start(sessionId, ws, message.sdpOffer, function(error, type, data) { + if (error) { + return ws.send(JSON.stringify({ + id : 'error', + message : error + })); + } + switch (type) { + case 'sdpAnswer': + ws.send(JSON.stringify({ + id : 'startResponse', + sdpAnswer : data + })); + break; + case 'WindowIn': + ws.send(JSON.stringify({ + id : 'WindowIn', + roiId : data.windowId + })); + break; + case 'WindowOut': + ws.send(JSON.stringify({ + id : 'WindowOut', + roiId : data.windowId + })); + break; + } + }); + break; + + case 'stop': + stop(sessionId); + break; + + case 'calibrate': + calibrate(sessionId); + break; + + case 'onIceCandidate': + onIceCandidate(sessionId, message.candidate); + break; + + default: + ws.send(JSON.stringify({ + id : 'error', + message : 'Invalid message ' + message + })); + break; + } + + }); +}); + +/* + * Definition of functions + */ + +// Recover kurentoClient for the first time. +function getKurentoClient(callback) { + if (kurentoClient !== null) { + return callback(null, kurentoClient); + } + + kurento(argv.ws_uri, function(error, _kurentoClient) { + if (error) { + console.log("Could not find media server at address " + argv.ws_uri); + return callback("Could not find media server at address" + argv.ws_uri + + ". Exiting with error " + error); + } + + kurentoClient = _kurentoClient; + callback(null, kurentoClient); + }); +} + +function start(sessionId, ws, sdpOffer, callback) { + if (!sessionId) { + return callback('Cannot use undefined sessionId'); + } + + getKurentoClient(function(error, kurentoClient) { + if (error) { + return callback(error); + } + + kurentoClient.create('MediaPipeline', function(error, pipeline) { + if (error) { + return callback(error); + } + + createMediaElements(pipeline, ws, function(error, webRtcEndpoint, filter) { + if (error) { + pipeline.release(); + return callback(error); + } + + if (candidatesQueue[sessionId]) { + while(candidatesQueue[sessionId].length) { + var candidate = candidatesQueue[sessionId].shift(); + webRtcEndpoint.addIceCandidate(candidate); + } + } + + connectMediaElements(webRtcEndpoint, filter, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + + webRtcEndpoint.on('IceCandidateFound', function(event) { + var candidate = kurento.getComplexType('IceCandidate')(event.candidate); + ws.send(JSON.stringify({ + id : 'iceCandidate', + candidate : candidate + })); + }); + + filter.on('WindowIn', function (_data) { + return callback(null, 'WindowIn', _data); + }); + + filter.on('WindowOut', function (_data) { + return callback(null, 'WindowOut', _data); + }); + + var options1 = PointerDetectorWindowMediaParam({ + id: 'window0', + height: 50, + width: 50, + upperRightX: 500, + upperRightY: 150 + }); + filter.addWindow(options1, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + }); + + var options2 = PointerDetectorWindowMediaParam({ + id: 'window1', + height: 50, + width:50, + upperRightX: 500, + upperRightY: 250 + }); + filter.addWindow(options2, function(error) { + if (error) { + pipeline.release(); + return callback(error); + } + }); + + webRtcEndpoint.processOffer(sdpOffer, function(error, sdpAnswer) { + if (error) { + pipeline.release(); + return callback(error); + } + + sessions[sessionId] = { + 'pipeline' : pipeline, + 'webRtcEndpoint' : webRtcEndpoint, + 'pointerDetector' : filter + } + return callback(null, 'sdpAnswer', sdpAnswer); + }); + + webRtcEndpoint.gatherCandidates(function(error) { + if (error) { + return callback(error); + } + }); + }); + }); + }); + }); +} + +function createMediaElements(pipeline, ws, callback) { + pipeline.create('WebRtcEndpoint', function(error, webRtcEndpoint) { + if (error) { + return callback(error); + } + + var options = { + calibrationRegion: WindowParam({ + topRightCornerX: 5, + topRightCornerY:5, + width:30, + height: 30 + }) + }; + + pipeline.create('pointerdetector.PointerDetectorFilter', options, function(error, filter) { + if (error) { + return callback(error); + } + + return callback(null, webRtcEndpoint, filter); + }); + }); +} + +function connectMediaElements(webRtcEndpoint, filter, callback) { + webRtcEndpoint.connect(filter, function(error) { + if (error) { + return callback(error); + } + + filter.connect(webRtcEndpoint, function(error) { + if (error) { + return callback(error); + } + + return callback(null); + }); + }); +} + +function stop(sessionId) { + if (sessions[sessionId]) { + var pipeline = sessions[sessionId].pipeline; + console.info('Releasing pipeline'); + pipeline.release(); + + delete sessions[sessionId]; + delete candidatesQueue[sessionId]; + } +} + + +function calibrate(sessionId) { + if (sessions[sessionId] && sessions[sessionId].pointerDetector) { + var pointerDetector = sessions[sessionId].pointerDetector; + pointerDetector.trackColorFromCalibrationRegion(function(error) { + console.info('Error calibrating region', error); + }); + } +} + +function onIceCandidate(sessionId, _candidate) { + var candidate = kurento.getComplexType('IceCandidate')(_candidate); + + if (sessions[sessionId]) { + console.info('Sending candidate'); + var webRtcEndpoint = sessions[sessionId].webRtcEndpoint; + webRtcEndpoint.addIceCandidate(candidate); + } + else { + console.info('Queueing candidate'); + if (!candidatesQueue[sessionId]) { + candidatesQueue[sessionId] = []; + } + candidatesQueue[sessionId].push(candidate); + } +} + +app.use(express.static(path.join(__dirname, 'static'))); diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/bower.json b/tutorials/javascript-node/kurento-pointerdetector/static/bower.json new file mode 100644 index 0000000000..7c1f3570ec --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/static/bower.json @@ -0,0 +1,28 @@ +{ + "name": "kurento-pointerdetector", + "description": "Kurento Browser JavaScript Tutorial", + "authors": [ + "Kurento " + ], + "main": "index.html", + "moduleType": [ + "globals" + ], + "license": "ALv2", + "homepage": "https://kurento.openvidu.io/", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "webrtc-adapter": "v7.4.0", + "bootstrap": "~3.3.0", + "ekko-lightbox": "~3.3.0", + "demo-console": "1.5.1", + "kurento-utils": "git+https://github.com/Kurento/kurento-utils-bower.git" + } +} diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/css/kurento.css b/tutorials/javascript-node/kurento-pointerdetector/static/css/kurento.css new file mode 100644 index 0000000000..6ae4fcc9c6 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/static/css/kurento.css @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2014-2015 Kurento (https://kurento.openvidu.io/) + * + * 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; +} + +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/tutorials/javascript-node/kurento-pointerdetector/static/img/kurento.png b/tutorials/javascript-node/kurento-pointerdetector/static/img/kurento.png new file mode 100644 index 0000000000..6f1a4ad3b2 Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/kurento.png differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/img/naevatec.png b/tutorials/javascript-node/kurento-pointerdetector/static/img/naevatec.png new file mode 100644 index 0000000000..05ee7041db Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/naevatec.png differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/img/pipeline.png b/tutorials/javascript-node/kurento-pointerdetector/static/img/pipeline.png new file mode 100644 index 0000000000..d0fd3fe49b Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/pipeline.png differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/img/spinner.gif b/tutorials/javascript-node/kurento-pointerdetector/static/img/spinner.gif new file mode 100644 index 0000000000..8be8ba338d Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/spinner.gif differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/img/transparent-1px.png b/tutorials/javascript-node/kurento-pointerdetector/static/img/transparent-1px.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/transparent-1px.png differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/img/urjc.gif b/tutorials/javascript-node/kurento-pointerdetector/static/img/urjc.gif new file mode 100644 index 0000000000..cd8a7703ae Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/urjc.gif differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/img/webrtc.png b/tutorials/javascript-node/kurento-pointerdetector/static/img/webrtc.png new file mode 100644 index 0000000000..d47e2e4cb3 Binary files /dev/null and b/tutorials/javascript-node/kurento-pointerdetector/static/img/webrtc.png differ diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/index.html b/tutorials/javascript-node/kurento-pointerdetector/static/index.html new file mode 100644 index 0000000000..cc787cb110 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/static/index.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Kurento Tutorial: WebRTC Pointer Detector + + + +
                + +
                + +
                + +
                +
                +

                Local stream

                + +
                + +
                +

                Remote stream

                + +
                +
                +
                +
                +

                +
                +
                  +
                  +
                  +
                  +
                  + + + + + diff --git a/tutorials/javascript-node/kurento-pointerdetector/static/js/index.js b/tutorials/javascript-node/kurento-pointerdetector/static/js/index.js new file mode 100644 index 0000000000..ecc8494632 --- /dev/null +++ b/tutorials/javascript-node/kurento-pointerdetector/static/js/index.js @@ -0,0 +1,227 @@ +/* + * (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); +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; + case 'WindowIn': + windowIn(parsedMessage); + break; + case 'WindowOut': + windowOut(parsedMessage); + break; + case 'error': + if (state == I_AM_STARTING) { + setState(I_CAN_START); + } + onError("Error message from server: " + parsedMessage.message); + break; + default: + if (state == I_AM_STARTING) { + setState(I_CAN_START); + } + onError('Unrecognized message', parsedMessage); + } +} + +function windowIn(message) { + console.log("Window in detected in " + message.roiId); +} + +function windowOut(message) { + console.log("Window out detected in " + message.roiId); +} + +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 calibrate() { + if (webRtcPeer) { + console.log("Calibrating..."); + var message = { + id : 'calibrate' + } + 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'); + $('#calibrate').attr('disabled', true); + $('#calibrate').removeAttr('onclick'); + break; + + case I_CAN_STOP: + $('#start').attr('disabled', true); + $('#stop').attr('disabled', false); + $('#stop').attr('onclick', 'stop()'); + $('#calibrate').attr('disabled', false); + $('#calibrate').attr('onclick', 'calibrate()'); + break; + + case I_AM_STARTING: + $('#start').attr('disabled', true); + $('#start').removeAttr('onclick'); + $('#stop').attr('disabled', true); + $('#stop').removeAttr('onclick'); + $('#calibrate').attr('disabled', true); + $('#calibrate').removeAttr('onclick'); + break; + + default: + onError('Unknown state ' + nextState); + return; + } + state = nextState; +} + +function sendMessage(message) { + var jsonMessage = JSON.stringify(message); + console.log('Sending 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(); +});