├── .gitignore ├── LICENSE ├── Readme.md ├── bin ├── hbbtv.js ├── index.js ├── start-cs.js └── start-terminal.js ├── index.js ├── lib ├── cs-launcher-dial-client.js ├── cs-launcher-dial-server.js ├── hbbtv-app2app-server.js ├── hbbtv-cs-manager.js ├── hbbtv-dial-client.js ├── hbbtv-dial-server.js ├── hbbtv-terminal-manager.js └── hbbtv.js ├── package.json ├── test ├── app2app-local-client.js ├── app2app-remote-client.js └── hbbtv-nodejs-client.js └── www ├── cs-app.html ├── hbbtv-app.html └── js ├── hbbtv-manager-polyfill.js └── hbbtvlib.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.project 2 | .idea 3 | node_modules 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Node.js `hbbtv` module 2 | ==================== 3 | 4 | The Node.js `hbbtv` module is a feature complete implementation of the HbbTV 2.0 Companion Screen components: 5 | 6 | * **HbbTV App Launch**: Launching a Companion Screen application from an HbbTV application 7 | * **CS App Launch**: Launching a broadcast independent HbbTV application on an HbbTV terminal from a Companion Screen application. 8 | * **App2App Communication**: Exchange text and binary messages between HbbTV and Companion Screen applications 9 | 10 | Please refer to the [HbbTV 2.0 spec document][hbbtv20spec] for more details especially to the Companion Screen related 11 | sections 8.2.6 and 14. 12 | 13 | The module is developed by the [Fraunhofer FOKUS´s](https://www.fokus.fraunhofer.de/) Competence Center [Future Applications and Media - FAME](https://www.fokus.fraunhofer.de/fame). 14 | Please contact us for more details or if you need help to integrate this module in your product. 15 | 16 | If you find Bugs please submit a new GitHub issue. 17 | 18 | Requirements 19 | =========== 20 | 21 | * [Node.js](https://nodejs.org/). Tested with: 22 | * Node.js v0.12.5 on Windows, Mac and Linux. 23 | * Node.js v0.10.25 on Windows. 24 | * npm (will be installed with Node.js) 25 | 26 | Setup 27 | ===== 28 | 29 | * **Install globally**: use `npm install hbbtv -g` to install the module. The `-g` option installs the module globally which is preferred if 30 | the module is used just as CLI. After the module is installed the `hbbtv` command will be available ([Usage section](#usage) 31 | explains how to use the `hbbtv` command). On Mac and Linux you may need to install the module using `sudo npm install hbbtv -g`. 32 | Some optional components require Python 2.7, please ignore related error logs if Python 2.7 is not installed on your machine 33 | or use `npm install hbbtv -g --no-optional` to not install optional components. 34 | * **Update globally installed module**: just run `npm update -g hbbtv` 35 | * **Install locally**: This is not preferred if you want to use the module as CLI. Use `npm install hbbtv` without `-g` 36 | option will create a `node_modules` folder with the `hbbtv` module in it. Please note that the `hbbtv` command will be not 37 | available if you install the module locally. Another way to install locally is to clone this git repository and run `npm install` (or `npm install --no-optional`) 38 | in the home folder to install dependencies. [Usage section](#usage) explains how to use this module if it is installed locally. 39 | * **Update locally installed module**: just run `npm update hbbtv` in the same folder where you installed the module (where you ran `npm install hbbtv`). If the module is cloned from git, run `git pull` and then `npm install`. 40 | * **Integrate in other Node.js applications**: To integrate this module in your Node.js application just add `"hbbtv": ""` (replace `` with the actual version) to 41 | the `dependencies` element of the `package.json` in your application. In your application use `var hbbtv = require("hbbtv")` to 42 | bind the module. For more details about the APIs supported in this module please refer to the [API Documentation](#api-documentation) section. 43 | * **Update module integrated in 3rd party applications**: replace `` with a newer version in `package.json` of your Node.js application 44 | 45 | Usage 46 | ===== 47 | 48 | The `hbbtv` module can be started as HbbTV Terminal (`terminal` mode) or as Companion Screen (`cs` mode): 49 | 50 | * If it is started in `terminal` mode, your machine will be seen as a HbbTV 2.0 CS compliant Terminal. 51 | You can use any HbbTV DIAL client to launch HbbTV applications and use any WebSocket client for App2App communication. 52 | The following command can be used to start in `terminal` mode on port `8080`: 53 | * `hbbtv` installed globally 54 | 55 | ``` 56 | hbbtv -m terminal -p 8080 57 | ``` 58 | 59 | * `hbbtv` installed locally 60 | 61 | ``` 62 | cd /path/to/folder/hbbtv/bin 63 | node hbbtv.js -m terminal -p 8080 64 | ``` 65 | 66 | * If it is started in `cs` mode, it will turn you machine in a companion screen that runs a CSLauncher and HbbTV DIAL Client. 67 | The HbbTV Terminal started in previous step will be able to discover companion screens running a CSLauncher and to launch CS applications. 68 | Since the discovery and communication between HbbTV terminals and CSLaunchers is not part of the HbbTV 2.0 Spec, we used here also the DIAL 69 | protocol to discover CSLaunchers and launch CS Applications. The CSLauncher acts as DIAL Server that offers a non-stoppable 70 | DIAL application called `Famium` which is already registered in the [DIAL registry][dial-reg]. The application accepts DIAL launch requests 71 | in the body of the related HTTP POST requests in the same format as specified in section 72 | 14.4.2 "Payload format for Install and Launch operations" of the [HbbTV 2.0 spec document][hbbtv20spec]. 73 | The following command can be used to start the `hbbtv` module in `cs` mode on port `8090`: 74 | 75 | * `hbbtv` installed globally 76 | 77 | ``` 78 | hbbtv -m cs -p 8090 79 | ``` 80 | 81 | * `hbbtv` installed locally 82 | 83 | ``` 84 | cd /path/to/folder/hbbtv/bin 85 | node hbbtv.js -m cs -p 8090 86 | ``` 87 | 88 | * To display `hbbtv` usage options use the following command: 89 | 90 | * `hbbtv` installed globally 91 | 92 | ``` 93 | hbbtv -h 94 | ``` 95 | 96 | * `hbbtv` installed locally 97 | 98 | ``` 99 | cd /path/to/folder/hbbtv/bin 100 | node hbbtv.js -h 101 | ``` 102 | 103 | Examples 104 | ======== 105 | 106 | Run Example HbbTV App and CS Web App hosted on GitHub 107 | ----------------------------------------------------- 108 | 109 | The fastest way to test this module is by using the example HbbTV and CS applications hosted on github.io: 110 | * HbbTV App: `http://fraunhoferfokus.github.io/node-hbbtv/www/hbbtv-app.html` 111 | * CS Web App: `http://fraunhoferfokus.github.io/node-hbbtv/www/cs-app.html` 112 | 113 | ### Run example: 114 | 115 | 1. start `hbbtv` module in `terminal` mode: `hbbtv -m terminal -p 8080` 116 | 2. start `hbbtv` module in `cs` mode: `hbbtv -m cs -p 8090` 117 | > It is possible to start `hbbtv` in `terminal` and `cs` mode on different ports on the same device. 118 | > For better understanding, it is recommended to use two different devices one for `terminal` and one for `cs`. both devices must be 119 | > in the same network in order to discover and communicate with each others using DIAL and WebSocket. 120 | 121 | 3. open CS Web App `http://fraunhoferfokus.github.io/node-hbbtv/www/cs-app.html#port=8090` in a browser on the same device from previous step where `hbbtv` is started in `cs` mode (the port must be the same as in previous step). 122 | 4. follow the instructions in the CS App opened in the browser in previous step: You will be able to discover the HbbTV Terminal started in first step, launch an HbbTV App on it and open a WebSocket communication channel to the remote App2App Endpoint of the discovered terminal. 123 | 5. After the HbbTV App is launched on the terminal, it will be able to discover CSLaunchers, launch CS Web Apps and create WebSocket communication channels to the local App2App Endpoint. 124 | 125 | Develop HbbTV App 126 | ----------------- 127 | 128 | * The following example shows an HbbTV Application that discovers CSLaunchers, launches a CS Web App on a discovered CSLauncher and creates 129 | a WebSocket connection to the local App2App Endpoint. The following steps are needed to run this example: 130 | * Start `hbbtv` in `terminal` mode: `hbbtv -m terminal -p 8080` 131 | * Please don't open this application manually in the Browser. Use the example CS Web App described in next subsection to launch this application. 132 | * The HbbTV Web App needs to include the JavaScript Lib `hbbtv-manager-polyfill.js` 133 | * Please refer to section 8.2.6 of the HbbTV 2.0 Spec document for more details about the JavaScript API of the HbbTVCSManager. 134 | 135 | ```html 136 | 137 | 138 | 139 | 140 | 141 | HbbTV App 142 | 143 | 144 | 145 | 209 | 210 | 211 | ``` 212 | 213 | Develop Companion Screen Web App 214 | -------------------------------- 215 | 216 | * The following example show a CS Web Application that discovers HbbTV Terminals, launches an HbbTV App on a discovered terminal and creates 217 | a WebSocket connection to the App2App Endpoint of the discovered Terminal. The following steps are needed to run this example: 218 | * Start `hbbtv` in `cs` mode: `hbbtv -m cs -p 8090` 219 | * Open the CS Web App in a Browser on the same device and append `#port=8090` to the URL: `http://www.example.com/cs-app.html#port=8090` 220 | * The CS Web App needs to include the JavaScript Lib `hbbtv-manager-polyfill.js` 221 | * The following example uses all JavaScript functions required to discover terminals, launch HbbTV Apps and create WebSocket connection. 222 | API documentation coming soon. 223 | 224 | ```html 225 | 226 | 227 | 228 | 229 | 230 | Companion Screen App 231 | 232 | 233 | 234 | 299 | 300 | 301 | ``` 302 | 303 | Develop Node.js HbbTV CS Client 304 | ------------------------------- 305 | 306 | the `hbbtv` module can also used to implement HbbTV CS Node.js clients without the need to develop CS Web App that runs 307 | in the Browser. This is for example useful to run HbbTV CS test cases or to use in Node.js applications to discover HbbTV 308 | terminals, launch HbbTV applications and create WebSocket connections to the remote App2App Endpoint of discovered terminals. 309 | The following example illustrates the usage of supported features 310 | 311 | ```javascript 312 | var hbbtv = require("hbbtv"); 313 | var HbbTVDialClient = hbbtv.HbbTVDialClient; 314 | var WebSocket = hbbtv.WebSocket; 315 | // create a hbbTVDialClient instance and add listeners for ready, stop, found and error events 316 | var hbbTVDialClient = new HbbTVDialClient().on("ready", function () { 317 | console.log("HbbTV DIAL Client is ready"); 318 | }).on("stop", function () { 319 | console.log("HbbTV DIAL Client is stopped"); 320 | }).on("found", function (terminal) { 321 | // found events are triggered each time a new HbbTV terminal is found 322 | console.log("HbbTV Terminal ", terminal.getFriendlyName()," (", terminal.getAppLaunchURL(), ") found"); 323 | var channel = (""+Math.random()).substr(2,16); 324 | // launch HbbTV App on each discovered terminal 325 | terminal.launchHbbTVApp({ 326 | "appUrlBase": "http://fraunhoferfokus.github.io/node-hbbtv/www/hbbtv-app.html", 327 | "appLocation": "?channel="+channel 328 | }, function (launchRes,err) { 329 | if(err){ 330 | console.error("Error on launch HbbTV App", err); 331 | } 332 | else { 333 | console.log("HbbTV App launched successfully: ",launchRes || ""); 334 | // create App2App connection after application is launched 335 | createConnection(terminal, channel); 336 | } 337 | }); 338 | }).on("error", function (err) { 339 | console.error(err); 340 | }); 341 | var createConnection = function (terminal, channel) { 342 | var app2appRemoteBaseUrl = terminal.getApp2AppURL(); 343 | var ws = new WebSocket(app2appRemoteBaseUrl + channel); 344 | ws.binaryType = "arraybuffer"; 345 | ws.onopen = function(evt) { 346 | console.log("Connection waiting ..."); 347 | }; 348 | ws.onclose = function(evt) { 349 | console.log("Connection closed."); 350 | }; 351 | ws.onerror = function (evt) { 352 | console.log("Connection error."); 353 | }; 354 | ws.onmessage = function(evt) { 355 | if (evt.data == "pairingcompleted") { 356 | console.log("pairing complete"); 357 | ws.onmessage = function(evt) { 358 | console.log( "Received Message: " + evt.data); 359 | }; 360 | var data = "Hello from Companion Screen"; 361 | ws.send(data); 362 | var array = [0,1,2,3,4,5,6,7,8,9]; 363 | data = typeof Buffer != "undefined"?new Buffer(array): new Int8Array(array).buffer; 364 | ws.send(data); 365 | } else { 366 | console.log("Unexpected message received from terminal."); 367 | ws.close(); 368 | } 369 | }; 370 | }; 371 | hbbTVDialClient.start(); 372 | // hbbTVDialClient.stop(); 373 | ``` 374 | 375 | Android HbbTV CS Client 376 | ----------------- 377 | Please contact us for more details 378 | 379 | iOS HbbTV CS Client 380 | ------------- 381 | Please contact us for more details 382 | 383 | Cordova HbbTV CS Client 384 | ----------------- 385 | Please contact us for more details 386 | 387 | API Documentation 388 | ================= 389 | 390 | The Node.js `hbbtv` module is designed to be integrated in existing products like set-top-boxes or TV sets with Node.js 391 | environment. The current implementation launches HbbTV and Companion Screen applications in the default browser. This is 392 | practicable to test HbbTV 2.0 CS features on the same environment developers use to develop their applications. To 393 | use this module in real products, the HbbTV UA needs to be launched instead of the default browser. This can be easily done 394 | by making the HbbTV UA as default Browser or just by starting it directly from this module just by replacing the lines of 395 | code where the default Browser is launched with your launch command. 396 | 397 | API documentation for the `hbbtv-manager-polyfill.js` and the `hbbtv` module and its sub-components coming soon. 398 | 399 | Contribution 400 | ============ 401 | 402 | We are grateful for any valuable contribution, like [issue reports](https://github.com/fraunhoferfokus/node-hbbtv/issues), 403 | [tests](https://github.com/fraunhoferfokus/node-hbbtv/tree/master/test) or [pull requests](https://github.com/fraunhoferfokus/node-hbbtv/pulls). 404 | 405 | Moreover, we would love to hear which exciting apps you have created using the `hbbtv` Node.js module. 406 | 407 | License 408 | ======= 409 | 410 | Free for non commercial use released under the GNU Lesser General Public License v3.0, See LICENSE file. 411 | 412 | Contact us for commercial use 413 | 414 | Copyright (c) 2015 [Fraunhofer FOKUS](https://www.fokus.fraunhofer.de/) 415 | 416 | Contact 417 | ======= 418 | 419 | * [Fraunhofer FOKUS - Competence Center FAME // Future Applications and Media](http://www.fokus.fraunhofer.de/fame) 420 | * 421 | 422 | [hbbtv20spec]: http://www.hbbtv.org/wp-content/uploads/2015/07/HbbTV-SPEC20-00023-001-HbbTV_2.0.1_specification_for_publication_clean.pdf 423 | [dial-reg]: http://www.dial-multiscreen.org/dial-registry/namespace-database#TOC-Registered-Names 424 | -------------------------------------------------------------------------------- /bin/hbbtv.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var program = require("commander"); 22 | var package = require("../package.json"); 23 | program.version(package.version) 24 | .allowUnknownOption(false) 25 | .option("-m, --mode ", "select mode. It is either 'terminal' to start as HbbTV Terminal or 'cs' to start as Companion Screen", /^(terminal|cs)$/i) 26 | .option("-p, --port ", "specify the port number of the HbbTV Terminal or CS Launcher. e.g. 8080",parseInt) 27 | .option("-i, --interdevsync-url ", "specify the URL of the inter-device synchronisation CSS-CII server. Applies to 'terminal' mode only. '{hostname}' substring will be replaced with the hostname of this server, as viewed by the client. Optional.") 28 | .option("-u, --useragent ", "specify the user agent string to be advertised. Applies to 'terminal' mode only. Optional.") 29 | .option("-o, --opn-args ", "specify the arguments to opn (used for launching apps), separated by | characters. e.g. firefox|-no-remote. Optional.") 30 | .option("-f, --friendly-name ", "specify the device name to be advertised (overriding system hostname). Applies to 'terminal' mode only. Optional.") 31 | 32 | program.parse(process.argv); 33 | var port = program.port>0 && program.port || null; 34 | var mode = program.mode || null; 35 | var interDevSyncUrl = program["interdevsyncUrl"] || null; 36 | var userAgent = program["useragent"] || null; 37 | var opn_params = program["opnArgs"] ? program["opnArgs"].split('|') : undefined; 38 | var friendlyName = program["friendlyName"] || null; 39 | 40 | if(port){ 41 | global.PORT = port; 42 | global.OPN_PARAMS = opn_params; 43 | if(mode == "terminal"){ 44 | global.INTERDEVSYNC_URL = interDevSyncUrl; 45 | global.USERAGENT = userAgent; 46 | global.FRIENDLY_NAME = friendlyName; 47 | require("./start-terminal.js"); 48 | } 49 | else if(mode == "cs"){ 50 | require("./start-cs.js"); 51 | } 52 | else { 53 | program.help(); 54 | } 55 | } 56 | else{ 57 | program.help(); 58 | } 59 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /******************************************************************************* 3 | * 4 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library. If not, see . 18 | * 19 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 20 | * 21 | ******************************************************************************/ 22 | require("./hbbtv.js"); -------------------------------------------------------------------------------- /bin/start-cs.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var PORT = global.PORT; 22 | if(!PORT){ 23 | console.log("variable 'global.PORT' is missing or not a valid port"); 24 | process.exit(1); 25 | } 26 | var hbbtv = require("../index.js"); 27 | var HbbTVTerminalManager = hbbtv.HbbTVTerminalManager; 28 | var CsLauncherDialServer = hbbtv.CsLauncherDialServer; 29 | var http = require('http'); 30 | var express = require("express"); 31 | var app = express(); 32 | var DIAL_PREFIX = "/dial"; 33 | app.set("port",PORT); 34 | app.set("dial-prefix",DIAL_PREFIX); 35 | http.globalAgent.maxSockets = 100; 36 | var counter = 0; 37 | var httpServer = http.createServer(app); 38 | 39 | var csLauncherDialServer = new CsLauncherDialServer(app).on("ready", function () { 40 | console.log("HbbTV CS Launcher is ready"); 41 | counter++; 42 | }).on("stop", function () { 43 | console.log("HbbTV CS Launcher is stopped"); 44 | if(--counter == 0){ 45 | process.exit(); 46 | } 47 | }).on("error", function (err) { 48 | console.error(err); 49 | }); 50 | 51 | var hbbTVTerminalManager = new HbbTVTerminalManager(httpServer).on("ready", function () { 52 | counter++; 53 | console.log("HbbTV Terminal Manager is ready"); 54 | }).on("stop", function () { 55 | console.log("HbbTV Terminal Manager is stopped"); 56 | if(--counter == 0){ 57 | process.exit(); 58 | } 59 | }).on("error", function (err) { 60 | console.error(err); 61 | }); 62 | 63 | httpServer.listen(PORT, function() { 64 | console.log("HbbTV Companion Screen is listening on port ", PORT); 65 | //console.log("***** Please append the hash query '#port="+PORT+"'"," to the URL of your CS Web App.\n***** The JavaScript Lib 'hbbtv-manager-polyfill.js' must be included in the CS Web App"); 66 | hbbTVTerminalManager.start(); 67 | csLauncherDialServer.start(); 68 | }); 69 | 70 | process.on('SIGINT', function() { 71 | console.log("Stopping HbbTV Companion Screen. Please wait ..."); 72 | hbbTVTerminalManager.stop(); 73 | csLauncherDialServer.stop(); 74 | setTimeout(function () { 75 | process.exit(); 76 | },3000); 77 | }); -------------------------------------------------------------------------------- /bin/start-terminal.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2013 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var PORT = global.PORT; 22 | var INTERDEVSYNC_URL = global.INTERDEVSYNC_URL; 23 | var USERAGENT = global.USERAGENT; 24 | var OPN_PARAMS = global.OPN_PARAMS; 25 | if(!PORT){ 26 | console.log("variable 'global.PORT' is missing or not a valid port"); 27 | process.exit(1); 28 | } 29 | var hbbtv = require("../index.js"); 30 | var HbbTVApp2AppServer = hbbtv.HbbTVApp2AppServer; 31 | var HbbTVDialServer = hbbtv.HbbTVDialServer; 32 | var HbbTVCsManager = hbbtv.HbbTVCsManager; 33 | var http = require('http'); 34 | var express = require("express"); 35 | var app = express(); 36 | var DIAL_PREFIX = "/dial"; 37 | var CS_MANAGER_PREFIX = "/csmanager"; 38 | http.globalAgent.maxSockets = 100; 39 | var counter = 0; 40 | app.set("port",PORT); 41 | app.set("dial-prefix",DIAL_PREFIX); 42 | app.set("cs-manager-prefix", CS_MANAGER_PREFIX); 43 | app.set("inter-dev-sync-url", INTERDEVSYNC_URL); 44 | app.set("user-agent", USERAGENT); 45 | app.set("opn-params", OPN_PARAMS); 46 | // The HTTP Server is used to in the HbbTVApp2AppServer and the HbbTVDialServer 47 | var httpServer = http.createServer(app); 48 | 49 | var hbbtvApp2AppServer = new HbbTVApp2AppServer(httpServer).on("ready", function () { 50 | console.log("HbbTV App2App Server is ready"); 51 | counter++; 52 | }).on("stop", function () { 53 | console.log("HbbTV App2App Server is stopped"); 54 | if(--counter == 0){ 55 | process.exit(); 56 | } 57 | }).on("error", function (err) { 58 | console.error("HbbTVApp2AppServer Error", err); 59 | }); 60 | 61 | var hbbtvDialServer = new HbbTVDialServer(app).on("ready", function () { 62 | console.log("HbbTV DIAL Server is ready"); 63 | counter++; 64 | }).on("stop", function () { 65 | console.log("HbbTV DIAL Server is stopped"); 66 | if(--counter == 0){ 67 | process.exit(); 68 | } 69 | }).on("error", function (err) { 70 | console.error("HbbTVDialServer Error", err); 71 | }); 72 | 73 | var hbbTVCsManager = new HbbTVCsManager(httpServer).on("ready", function () { 74 | console.log("HbbTV CS Manager is ready"); 75 | counter++; 76 | }).on("stop", function () { 77 | console.log("HbbTV CS Manager is stopped"); 78 | if(--counter == 0){ 79 | process.exit(); 80 | } 81 | }).on("error", function (err) { 82 | console.error("HbbTVCSManager Error", err); 83 | }); 84 | 85 | httpServer.listen(PORT, function() { 86 | console.log("HbbTV Terminal is listening on port ", PORT); 87 | //console.log("***** The JavaScript Lib 'hbbtv-manager-polyfill.js' must be included in the HbbTV App"); 88 | hbbtvApp2AppServer.start(); 89 | hbbtvDialServer.start(); 90 | hbbTVCsManager.start(); 91 | }); 92 | 93 | process.on('SIGINT', function() { 94 | console.log("Stopping HbbTV Terminal. Please wait ..."); 95 | hbbtvApp2AppServer.stop(); 96 | hbbtvDialServer.stop(); 97 | hbbTVCsManager.stop(); 98 | setTimeout(function () { 99 | process.exit(); 100 | },3000); 101 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2013 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | module.exports = require('./lib/hbbtv'); -------------------------------------------------------------------------------- /lib/cs-launcher-dial-client.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var dial = require("peer-dial"); 22 | var util = require("util"); 23 | var events = require("events"); 24 | 25 | var CsLauncher = function (dialDevice, appInfo) { 26 | var appLaunchURL = dialDevice && dialDevice.applicationUrl && (dialDevice.applicationUrl+"/Famium") || null; 27 | var osId = appInfo && appInfo.additionalData && appInfo.additionalData.X_FAMIUM_CS_OS_ID || null; 28 | 29 | dialDevice && (dialDevice.appLaunchURL = appLaunchURL); 30 | dialDevice && (dialDevice.osId = osId); 31 | 32 | var getAppLaunchURL = function () { 33 | return appLaunchURL; 34 | }; 35 | 36 | var getInfo = function () { 37 | return dialDevice || null; 38 | }; 39 | 40 | var launchCsApp = function (launchData,callback) { 41 | var launchReq = launchData; 42 | dialDevice.launchApp("Famium",launchReq, "text/plain", function (launchRes, err) { 43 | if(typeof launchRes != "undefined"){ 44 | callback && callback(launchRes,err); 45 | } 46 | else if(err){ 47 | callback && callback(null,err); 48 | } 49 | }); 50 | }; 51 | 52 | Object.defineProperty(this,"launchCsApp", { 53 | get: function(){ 54 | return launchCsApp; 55 | } 56 | }); 57 | 58 | Object.defineProperty(this,"getInfo", { 59 | get: function(){ 60 | return getInfo; 61 | } 62 | }); 63 | 64 | Object.defineProperty(this,"getAppLaunchURL", { 65 | get: function(){ 66 | return getAppLaunchURL; 67 | } 68 | }); 69 | }; 70 | 71 | var CsLauncherDialClient = function () { 72 | var dialClient = new dial.Client(); 73 | var self = this; 74 | var csLaunchers = {}; 75 | dialClient.on("ready",function(){ 76 | self.emit("ready"); 77 | }).on("found",function(deviceDescriptionUrl, headers){ 78 | dialClient.getDialDevice(deviceDescriptionUrl, function (dialDevice, err) { 79 | if(dialDevice){ 80 | dialDevice.getAppInfo("Famium", function (appInfo, err) { 81 | if(appInfo){ 82 | var csLauncher = new CsLauncher(dialDevice, appInfo); 83 | csLaunchers[deviceDescriptionUrl] = csLauncher; 84 | self.emit("found", csLauncher); 85 | } 86 | else if(err){ 87 | // TODO check if this is an error or not 88 | //var error = new Error("Error on get CS Launcher FAMIUM App Info or DIAL device is not a FAMIUM CS Launcher", err.message); 89 | //self.emit("error", error); 90 | } 91 | }); 92 | } 93 | else if(err){ 94 | var error = new Error("Error on get device description from "+deviceDescriptionUrl, err.message); 95 | self.emit("error", error); 96 | } 97 | }); 98 | }).on("disappear", function(deviceDescriptionUrl, headers){ 99 | var csLauncher = csLaunchers[deviceDescriptionUrl]; 100 | delete csLaunchers[deviceDescriptionUrl]; 101 | self.emit("disappear",deviceDescriptionUrl, csLauncher); 102 | }).on("stop", function(){ 103 | self.emit("stop"); 104 | }).on("error", function (err) { 105 | self.emit("error",err); 106 | }); 107 | 108 | var start = function () { 109 | dialClient.start(); 110 | return this; 111 | }; 112 | 113 | var refresh = function () { 114 | dialClient.refresh(); 115 | return this; 116 | }; 117 | 118 | var stop = function () { 119 | dialClient.stop(); 120 | return this; 121 | }; 122 | 123 | Object.defineProperty(this,"start", { 124 | get: function(){ 125 | return start; 126 | } 127 | }); 128 | 129 | Object.defineProperty(this,"refresh", { 130 | get: function(){ 131 | return refresh; 132 | } 133 | }); 134 | 135 | Object.defineProperty(this,"stop", { 136 | get: function(){ 137 | return stop; 138 | } 139 | }); 140 | }; 141 | 142 | util.inherits(CsLauncherDialClient, events.EventEmitter); 143 | module.exports = CsLauncherDialClient; -------------------------------------------------------------------------------- /lib/cs-launcher-dial-server.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var HbbTVDialServer = require("./hbbtv-dial-server.js"); 22 | var util = require("util"); 23 | 24 | var CsLauncherDialServer = function (expressApp) { 25 | var isCsLauncher = true; 26 | HbbTVDialServer.call(this,expressApp,isCsLauncher); 27 | }; 28 | 29 | util.inherits(CsLauncherDialServer, HbbTVDialServer); 30 | module.exports = CsLauncherDialServer; -------------------------------------------------------------------------------- /lib/hbbtv-app2app-server.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var ws = require("ws"); 22 | var util = require("util"); 23 | var events = require("events"); 24 | var WebSocketServer = ws.Server; 25 | var HbbTVApp2AppServer = function (httpServer) { 26 | var wsServer = null; 27 | var pendingLocalConnections = null; 28 | var pendingRemoteConnections = null; 29 | var handlePendingConnectionsChanged = function (channel) { 30 | var channelPendingLocalConnections = pendingLocalConnections[channel] || []; 31 | var channelPendingRemoteConnections = pendingRemoteConnections[channel] || []; 32 | while(channelPendingLocalConnections.length>0 && channelPendingRemoteConnections.length>0){ 33 | var localConnection = channelPendingLocalConnections.pop(); 34 | var remoteConnection = channelPendingRemoteConnections.pop(); 35 | localConnection.pair = remoteConnection; 36 | remoteConnection.pair = localConnection; 37 | localConnection && (localConnection.readyState == ws.OPEN) && localConnection.send("pairingcompleted"); 38 | remoteConnection && (remoteConnection.readyState == ws.OPEN) && remoteConnection.send("pairingcompleted"); 39 | } 40 | if(channelPendingLocalConnections.length == 0){ 41 | delete pendingLocalConnections[channel]; 42 | } 43 | if(channelPendingRemoteConnections.length == 0){ 44 | delete pendingRemoteConnections[channel]; 45 | } 46 | }; 47 | var handleConnectionClosed = function (connection) { 48 | if(connection.local) { 49 | var channelPendingLocalConnections = pendingLocalConnections[connection.channel] || []; 50 | var index = channelPendingLocalConnections.indexOf(connection); 51 | index >= 0 && channelPendingLocalConnections.splice(index, 1); 52 | if(channelPendingLocalConnections.length == 0){ 53 | delete pendingLocalConnections[connection.channel]; 54 | } 55 | } 56 | else if(connection.remote){ 57 | var channelPendingRemoteConnections = pendingRemoteConnections[connection.channel] || []; 58 | var index = channelPendingRemoteConnections.indexOf(connection); 59 | index >= 0 && pendingRemoteConnections.splice(index, 1); 60 | if(channelPendingRemoteConnections.length == 0){ 61 | delete pendingRemoteConnections[connection.channel]; 62 | } 63 | } 64 | }; 65 | 66 | var handleConnectionReceived = function(connection) { 67 | var req = connection.upgradeReq; 68 | if(req.channel){ 69 | var channel = req.channel; 70 | connection.channel = channel; 71 | if(req.local){ 72 | connection.local = true; 73 | var channelPendingLocalConnections = pendingLocalConnections[channel] || (pendingLocalConnections[channel] = []); 74 | channelPendingLocalConnections.push(connection); 75 | } 76 | else { 77 | connection.remote = true; 78 | var channelPendingRemoteConnections = pendingRemoteConnections[channel] || (pendingRemoteConnections[channel] = []); 79 | channelPendingRemoteConnections.push(connection); 80 | } 81 | handlePendingConnectionsChanged(channel); 82 | connection.on("message", function(msg, flags) { 83 | var options = {}; 84 | flags.binary && (options.binary = true); 85 | flags.masked && (options.masked = true); 86 | if (flags && flags.binary) { 87 | connection.pair && (connection.pair.readyState == ws.OPEN) && connection.pair.send(msg, options); 88 | } 89 | else { 90 | connection.pair && (connection.pair.readyState == ws.OPEN) && connection.pair.send(msg, options); 91 | } 92 | }); 93 | connection.on("close", function(code, reason) { 94 | if(connection.pair){ 95 | connection.pair.close(); 96 | connection.pair = null; 97 | } 98 | else { 99 | handleConnectionClosed(connection); 100 | } 101 | connection = null; 102 | }); 103 | connection.on("error", function () { 104 | // TODO handle error and remove socket 105 | }); 106 | } 107 | else { 108 | connection.close(); 109 | } 110 | }; 111 | 112 | var verifyClient = function (info,callback) { 113 | var req = info.req; 114 | var url = req.url || ""; 115 | var channel = null; 116 | var verified; 117 | var code; 118 | if(url.indexOf("/local/") == 0){ 119 | channel = url.substr(7) || null; 120 | req.local = true; 121 | } 122 | else if(url.indexOf("/remote/") == 0){ 123 | channel = url.substr(8) || null; 124 | req.local = false; 125 | } 126 | if(channel){ 127 | req.channel = channel; 128 | verified = true; 129 | callback && callback(verified); 130 | } 131 | /*else{ 132 | verified = false; 133 | code = 400; 134 | } 135 | try{ 136 | callback(verified,code); 137 | } 138 | catch (e){ 139 | console.error("Error on verify client",e); 140 | }*/ 141 | }; 142 | 143 | var reset = function () { 144 | wsServer && wsServer.close(); 145 | wsServer = null; 146 | pendingLocalConnections = []; 147 | pendingRemoteConnections = []; 148 | }; 149 | 150 | var start = function () { 151 | reset(); 152 | wsServer = new WebSocketServer({ 153 | server: httpServer, 154 | verifyClient : verifyClient 155 | }).on("connection", handleConnectionReceived); 156 | this.emit("ready"); 157 | return this; 158 | }; 159 | 160 | var stop = function () { 161 | reset(); 162 | this.emit("stop"); 163 | return this; 164 | }; 165 | 166 | Object.defineProperty(this,"start", { 167 | get: function(){ 168 | return start; 169 | } 170 | }); 171 | 172 | Object.defineProperty(this,"stop", { 173 | get: function(){ 174 | return stop; 175 | } 176 | }); 177 | }; 178 | 179 | util.inherits(HbbTVApp2AppServer, events.EventEmitter); 180 | 181 | module.exports = HbbTVApp2AppServer; -------------------------------------------------------------------------------- /lib/hbbtv-cs-manager.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var dial = require("peer-dial"); 22 | var util = require("util"); 23 | var events = require("events"); 24 | var CsLauncherDialClient = require("./cs-launcher-dial-client.js"); 25 | var HbbTVDialClient = require("./hbbtv-dial-client.js"); 26 | var ws = require("ws"); 27 | var WebSocketServer = ws.Server; 28 | var enableCORS = function(req, res, next) { 29 | res.header("Access-Control-Allow-Origin", "*"); 30 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Application-URL"); 31 | next(); 32 | }; 33 | 34 | var HbbTVCSManager = function (httpServer, isCs) { 35 | //var prefix = expressApp.get("cs-manager-prefix") || ""; 36 | isCs = (isCs == true); 37 | var csLauncherDialClient = new CsLauncherDialClient(); 38 | var hbbTVDialClient = new HbbTVDialClient(); 39 | var self = this; 40 | var wsServer = null; 41 | var csLaunchers = {}; 42 | var tmpCsLaunchers = {}; 43 | var terminals = {}; 44 | var tmpTerminals = {}; 45 | var lastCsLauncherRefresh = 0; 46 | var lastHbbTVTerminalRefresh = 0; 47 | var discoveryTime = 5000; 48 | var readyCounter = isCs?1:0; 49 | var stopCounter = isCs?1:0; 50 | csLauncherDialClient.on("ready",function(){ 51 | readyCounter++ && self.emit("ready"); 52 | }).on("found",function(csLauncher){ 53 | csLaunchers[csLauncher.getAppLaunchURL()] = csLauncher; 54 | tmpCsLaunchers[csLauncher.getAppLaunchURL()] = csLauncher.getInfo(); 55 | }).on("disappear", function(deviceDescriptionUrl, csLauncher){ 56 | delete csLaunchers[deviceDescriptionUrl]; 57 | delete tmpCsLaunchers[deviceDescriptionUrl]; 58 | }).on("stop", function(){ 59 | stopCounter++ && self.emit("stop"); 60 | }).on("error", function (err) { 61 | self.emit("error",err); 62 | }); 63 | 64 | hbbTVDialClient.on("ready",function(){ 65 | readyCounter++ && self.emit("ready"); 66 | }).on("found",function(terminal){ 67 | terminals[terminal.getAppLaunchURL()] = terminal; 68 | tmpTerminals[terminal.getAppLaunchURL()] = terminal.getInfo(); 69 | }).on("disappear", function(deviceDescriptionUrl, terminal){ 70 | delete terminals[deviceDescriptionUrl]; 71 | delete tmpTerminals[deviceDescriptionUrl]; 72 | }).on("stop", function(){ 73 | stopCounter++ && self.emit("stop"); 74 | }).on("error", function (err) { 75 | self.emit("error",err); 76 | }); 77 | 78 | var handleConnectionReceived = function(connection) { 79 | connection.on("message", function(msg, flags) { 80 | // expect msg as jsonrpc request 81 | if(typeof msg == "string"){ 82 | try{ 83 | var req = JSON.parse(msg); 84 | var method = req.method; 85 | if(method == "discoverCSLaunchers"){ 86 | discoverCSLaunchers(connection,req); 87 | } 88 | else if(method == "discoverTerminals"){ 89 | discoverTerminals(connection,req); 90 | } 91 | else if(method == "launchCSApp"){ 92 | launchCSApp(connection,req); 93 | } 94 | else if(method == "launchHbbTVApp"){ 95 | launchHbbTVApp(connection,req); 96 | } 97 | } 98 | catch(err){ 99 | self.emit("error",err); 100 | } 101 | } 102 | }).on("close", function(code, reason) { 103 | 104 | }); 105 | }; 106 | 107 | var verifyClient = function (info,callback) { 108 | var req = info.req; 109 | var url = req.url || ""; 110 | if(url == "/hbbtvmanager"){ 111 | callback && callback(true); 112 | } 113 | /*else { 114 | callback && callback(false,400); 115 | }*/ 116 | }; 117 | 118 | var discoverCSLaunchers = function (connection, req) { 119 | var currentTime = new Date().getTime(); 120 | var timeElapsed = currentTime - lastCsLauncherRefresh; 121 | var timeout = 0; 122 | if(timeElapsed > discoveryTime){ 123 | lastCsLauncherRefresh = currentTime; 124 | tmpCsLaunchers = {}; 125 | csLauncherDialClient.refresh(); 126 | timeout = discoveryTime; 127 | } 128 | else { 129 | timeout = discoveryTime-timeElapsed; 130 | } 131 | setTimeout(function(){ 132 | var rsp = { 133 | "jsonrpc": "2.0", 134 | "result": tmpCsLaunchers, 135 | "id": req.id 136 | }; 137 | connection.send(JSON.stringify(rsp)); 138 | }, timeout); 139 | }; 140 | 141 | var launchCSApp = function (connection, req) { 142 | var launcherId = req.params[0]; 143 | var payload = req.params[1]; 144 | var csLauncher = csLaunchers[launcherId]; 145 | var code = null; 146 | // TODO check payload if it is conform with the HbbTV 2.0 Spec as described in 14.4.2 147 | if(csLauncher){ 148 | csLauncher.launchCsApp(payload, function (launchRes, err) { 149 | if(err){ 150 | code = 400; 151 | } 152 | else { 153 | code = 200; 154 | } 155 | }); 156 | } 157 | else { 158 | code = 404; 159 | } 160 | var rsp = { 161 | "jsonrpc": "2.0", 162 | "result": code, 163 | "id": req.id 164 | }; 165 | connection.send(JSON.stringify(rsp)); 166 | }; 167 | 168 | var discoverTerminals = function (connection, req) { 169 | var currentTime = new Date().getTime(); 170 | var timeElapsed = currentTime - lastHbbTVTerminalRefresh; 171 | var timeout = 0; 172 | if(timeElapsed > discoveryTime){ 173 | lastHbbTVTerminalRefresh = currentTime; 174 | tmpTerminals = {}; 175 | hbbTVDialClient.refresh(); 176 | timeout = discoveryTime; 177 | } 178 | else { 179 | timeout = discoveryTime-timeElapsed; 180 | } 181 | setTimeout(function(){ 182 | var rsp = { 183 | "jsonrpc": "2.0", 184 | "result": tmpTerminals, 185 | "id": req.id 186 | }; 187 | connection.send(JSON.stringify(rsp)); 188 | }, timeout); 189 | }; 190 | 191 | var launchHbbTVApp = function (connection, req) { 192 | var terminalId = req.params[0]; 193 | var options = req.params[1]; 194 | var terminal = terminals[terminalId]; 195 | var code = null; 196 | // TODO check options object 197 | if(terminal){ 198 | terminal.launchHbbTVApp(options, function (launchRes, err) { 199 | if(err){ 200 | code = 400; 201 | } 202 | else { 203 | code = 200; 204 | } 205 | }); 206 | } 207 | else { 208 | code = 404; 209 | } 210 | var rsp = { 211 | "jsonrpc": "2.0", 212 | "result": code, 213 | "id": req.id 214 | }; 215 | connection.send(JSON.stringify(rsp)); 216 | }; 217 | 218 | var start = function () { 219 | !isCs && csLauncherDialClient.start(); 220 | hbbTVDialClient.start(); 221 | wsServer = new WebSocketServer({ 222 | server: httpServer, 223 | verifyClient : verifyClient 224 | }).on("connection", handleConnectionReceived); 225 | return this; 226 | }; 227 | 228 | var stop = function () { 229 | !isCs && csLauncherDialClient.stop(); 230 | hbbTVDialClient.stop(); 231 | wsServer.close(); 232 | return this; 233 | }; 234 | 235 | Object.defineProperty(this,"start", { 236 | get: function(){ 237 | return start; 238 | } 239 | }); 240 | 241 | Object.defineProperty(this,"stop", { 242 | get: function(){ 243 | return stop; 244 | } 245 | }); 246 | }; 247 | 248 | util.inherits(HbbTVCSManager, events.EventEmitter); 249 | 250 | module.exports = HbbTVCSManager; -------------------------------------------------------------------------------- /lib/hbbtv-dial-client.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var dial = require("peer-dial"); 22 | var util = require("util"); 23 | var events = require("events"); 24 | var URL = require("url"); 25 | var builder = require('xmlbuilder'); 26 | 27 | var HbbTVTerminal = function (dialDevice, appInfo) { 28 | 29 | var appLaunchURL = dialDevice && dialDevice.applicationUrl && (dialDevice.applicationUrl+"/HbbTV") || null; 30 | var app2AppURL = appInfo && appInfo.additionalData && appInfo.additionalData.X_HbbTV_App2AppURL || null; 31 | var interDevSyncURL = appInfo && appInfo.additionalData && appInfo.additionalData.X_HbbTV_InterDevSyncURL || null; 32 | var userAgent = appInfo && appInfo.additionalData && appInfo.additionalData.X_HbbTV_UserAgent || null; 33 | var friendlyName = dialDevice && dialDevice.friendlyName || null; 34 | dialDevice && (dialDevice.appLaunchURL = appLaunchURL); 35 | dialDevice && (dialDevice.app2AppURL = app2AppURL); 36 | dialDevice && (dialDevice.userAgent = userAgent); 37 | dialDevice && (dialDevice.interDevSyncURL = interDevSyncURL); 38 | 39 | var getInfo = function () { 40 | return dialDevice || null; 41 | }; 42 | var getApp2AppURL = function () { 43 | return app2AppURL; 44 | }; 45 | var getInterDevSyncURL = function () { 46 | return interDevSyncURL; 47 | }; 48 | var getUserAgent = function () { 49 | return userAgent; 50 | }; 51 | var getAppLaunchURL = function () { 52 | return appLaunchURL; 53 | }; 54 | var getFriendlyName = function () { 55 | return friendlyName; 56 | }; 57 | 58 | var launchHbbTVApp = function (launchData,callback) { 59 | var orgId = launchData.orgId || ""; 60 | var appId = launchData.appId || ""; 61 | var appName = launchData.appName || ""; 62 | var appNameLanguage = launchData.appNameLanguage || ""; 63 | var appUrlBase = launchData.appUrlBase || ""; 64 | var appLocation = launchData.appLocation || ""; 65 | var appUrl = URL.parse(appUrlBase); 66 | if(appUrl.protocol && appUrl.hostname){ 67 | var mhp = { 68 | "mhp:ServiceDiscovery": { 69 | "@xmlns:mhp": "urn:dvb:mhp:2009", 70 | "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", 71 | "mhp:ApplicationDiscovery": { 72 | "@DomainName": appUrl.hostname, 73 | "mhp:ApplicationList": { 74 | "mhp:Application": { 75 | "mhp:appName": { 76 | "@Language": appNameLanguage, 77 | "#text": appName 78 | }, 79 | "mhp:applicationIdentifier": { 80 | "mhp:orgId": orgId, 81 | "mhp:appId": appId 82 | }, 83 | "mhp:applicationDescriptor": { 84 | "mhp:type": { 85 | "mhp:OtherApp": "application/vnd.hbbtv.xhtml+xml" 86 | }, 87 | "mhp:controlCode": "AUTOSTART", 88 | "mhp:visibility": "VISIBLE_ALL", 89 | "mhp:serviceBound": "false", 90 | "mhp:priority": "1", 91 | "mhp:version": "01", 92 | "mhp:mhpVersion": { 93 | "mhp:profile": "0", 94 | "mhp:versionMajor": "1", 95 | "mhp:versionMinor": "3", 96 | "mhp:versionMicro": "1" 97 | } 98 | }, 99 | "mhp:applicationTransport": { 100 | "@xsi:type": "mhp:HTTPTransportType", 101 | "mhp:URLBase": appUrlBase 102 | }, 103 | "mhp:applicationLocation": appLocation 104 | } 105 | } 106 | } 107 | } 108 | }; 109 | var launchReq = builder.create(mhp).end({ pretty: true}); 110 | dialDevice.launchApp("HbbTV",launchReq, "text/plain", function (launchRes, err) { 111 | if(typeof launchRes != "undefined"){ 112 | callback && callback(launchRes,err); 113 | } 114 | else if(err){ 115 | callback && callback(null,err); 116 | } 117 | }); 118 | } 119 | else { 120 | var err = new Error("mhp:applicationTransport->URLBase is mandatory and must be an valid URL"); 121 | callback && callback(null,err); 122 | } 123 | }; 124 | 125 | Object.defineProperty(this,"launchHbbTVApp", { 126 | get: function(){ 127 | return launchHbbTVApp; 128 | } 129 | }); 130 | 131 | Object.defineProperty(this,"getAppLaunchURL", { 132 | get: function(){ 133 | return getAppLaunchURL; 134 | } 135 | }); 136 | 137 | Object.defineProperty(this,"getApp2AppURL", { 138 | get: function(){ 139 | return getApp2AppURL; 140 | } 141 | }); 142 | 143 | Object.defineProperty(this,"getInterDevSyncURL", { 144 | get: function(){ 145 | return getInterDevSyncURL; 146 | } 147 | }); 148 | 149 | Object.defineProperty(this,"getUserAgent", { 150 | get: function(){ 151 | return getUserAgent; 152 | } 153 | }); 154 | 155 | Object.defineProperty(this,"getFriendlyName", { 156 | get: function(){ 157 | return getFriendlyName; 158 | } 159 | }); 160 | 161 | Object.defineProperty(this,"getInfo", { 162 | get: function(){ 163 | return getInfo; 164 | } 165 | }); 166 | }; 167 | 168 | var HbbTVDialClient = function () { 169 | var dialClient = new dial.Client(); 170 | var self = this; 171 | var terminals = {}; 172 | dialClient.on("ready",function(){ 173 | self.emit("ready"); 174 | }).on("found",function(deviceDescriptionUrl, headers){ 175 | dialClient.getDialDevice(deviceDescriptionUrl, function (dialDevice, err) { 176 | if(dialDevice){ 177 | dialDevice.getAppInfo("HbbTV", function (appInfo, err) { 178 | if(appInfo){ 179 | var terminal = new HbbTVTerminal(dialDevice, appInfo); 180 | terminals[deviceDescriptionUrl] = terminal; 181 | self.emit("found", terminal); 182 | } 183 | else if(err){ 184 | // TODO check if this is an error or not 185 | //var error = new Error("Error on get HbbTV App Info or DIAL device is not a HbbTV Terminal", err.message); 186 | //self.emit("error", error); 187 | } 188 | }); 189 | } 190 | else if(err){ 191 | var error = new Error("Error on get device description from "+deviceDescriptionUrl, err.message); 192 | self.emit("error", error); 193 | } 194 | }); 195 | }).on("disappear", function(deviceDescriptionUrl, headers){ 196 | var terminal = terminals[deviceDescriptionUrl]; 197 | delete terminals[deviceDescriptionUrl]; 198 | self.emit("disappear",deviceDescriptionUrl, terminal); 199 | }).on("stop", function(){ 200 | self.emit("stop"); 201 | }).on("error", function (err) { 202 | self.emit("error",err); 203 | }); 204 | 205 | var start = function () { 206 | dialClient.start(); 207 | return this; 208 | }; 209 | 210 | var refresh = function () { 211 | dialClient.refresh(); 212 | return this; 213 | }; 214 | 215 | var stop = function () { 216 | dialClient.stop(); 217 | return this; 218 | }; 219 | 220 | Object.defineProperty(this,"start", { 221 | get: function(){ 222 | return start; 223 | } 224 | }); 225 | 226 | Object.defineProperty(this,"refresh", { 227 | get: function(){ 228 | return refresh; 229 | } 230 | }); 231 | 232 | Object.defineProperty(this,"stop", { 233 | get: function(){ 234 | return stop; 235 | } 236 | }); 237 | }; 238 | 239 | util.inherits(HbbTVDialClient, events.EventEmitter); 240 | module.exports = HbbTVDialClient; -------------------------------------------------------------------------------- /lib/hbbtv-dial-server.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var dial = require("peer-dial"); 22 | var util = require("util"); 23 | var events = require("events"); 24 | var opn = require("opn"); 25 | var xml2js = require("xml2js"); 26 | var HbbTVDialServer = function (expressApp, isCsLauncher) { 27 | var self = this; 28 | var MANUFACTURER = "Fraunhofer FOKUS"; 29 | var MODEL_NAME = "HbbTV 2.0 Node.js Companion Screen Feature Emulator"; 30 | var FRIENDLY_NAME = global.FRIENDLY_NAME; // May be null, in which case the hostname is used. 31 | var port = expressApp.get("port") || 80; 32 | var prefix = expressApp.get("dial-prefix") || ""; 33 | var csManagerPrefix = expressApp.get("cs.manager-prefix") || ""; 34 | var opnParams = expressApp.get("opn-params"); 35 | isCsLauncher = (isCsLauncher == true); 36 | var apps = { 37 | "YouTube": { 38 | disabled: isCsLauncher, 39 | name: "YouTube", 40 | state: "stopped", 41 | allowStop: true, 42 | pid: null 43 | }, 44 | "HbbTV": { 45 | disabled: isCsLauncher, 46 | name: "HbbTV", 47 | state: "running", 48 | allowStop: false, 49 | additionalData: { 50 | "hbbtv:X_HbbTV_App2AppURL":"", 51 | "hbbtv:X_HbbTV_InterDevSyncURL": "", 52 | "hbbtv:X_HbbTV_UserAgent": "" 53 | }, 54 | namespaces: { 55 | "hbbtv": "urn:hbbtv:HbbTVCompanionScreen:2014" 56 | } 57 | }, 58 | "Famium": { 59 | disabled: !isCsLauncher, 60 | name: "Famium", 61 | state: "running", 62 | allowStop: false, 63 | additionalData: { 64 | "famium:X_FAMIUM_CS_OS_ID":"de.fhg.fokus.famium.applauncher/0.0.1" 65 | }, 66 | namespaces: { 67 | "famium": "http://famium.fokus.fraunhofer.de" 68 | } 69 | } 70 | }; 71 | 72 | var openUrl = function (url) { 73 | try{ 74 | opn(url, opnParams); 75 | } 76 | catch(err){ 77 | console.error("Error on open URL", err.message); 78 | } 79 | }; 80 | 81 | var launchApp = function (app, launchData, callback) { 82 | var self = this; 83 | if(app.name == "YouTube"){ 84 | app.pid = "run"; 85 | app.state = "starting"; 86 | openUrl("http://www.youtube.com/tv?"+launchData); 87 | app.state = "running"; 88 | callback && callback(app.pid); 89 | } 90 | else if(app.name == "HbbTV"){ 91 | xml2js.parseString(launchData, { 92 | trim: true, 93 | explicitArray: false, 94 | mergeAttrs: true, 95 | explicitRoot: false, 96 | tagNameProcessors: [function(tagName){ 97 | tagName = tagName.substr(tagName.indexOf(":")+1); 98 | return tagName; 99 | }], 100 | attrNameProcessors: [function(attrName){ 101 | attrName = attrName.substr(attrName.indexOf(":")+1); 102 | return attrName; 103 | }] 104 | },function (err, launchData) { 105 | if(err){ 106 | callback && callback(null,err); 107 | } 108 | else { 109 | try { 110 | var appUrlBase = launchData.ApplicationDiscovery.ApplicationList.Application.applicationTransport.URLBase || ""; 111 | var appLocation = launchData.ApplicationDiscovery.ApplicationList.Application.applicationLocation || ""; 112 | var url = appUrlBase && (appUrlBase+appLocation) || null; 113 | if(url){ 114 | var csManagerParameters = "port="+port+"&hostname="+self.hostname; 115 | openUrl(url+"#"+csManagerParameters); 116 | app.state = "running"; 117 | callback && callback(app.pid); 118 | } 119 | else { 120 | callback && callback(null, new Error("URLBase element in the XML MHP of the DIAL Launch Request is missing or empty")); 121 | } 122 | } 123 | catch (err){ 124 | callback && callback(null, err); 125 | } 126 | } 127 | }); 128 | } 129 | else if(app.name == "Famium"){ 130 | try{ 131 | launchData = JSON.parse(launchData); 132 | var url = null; 133 | //for(var i in launchData.launch){ 134 | for(var i=0; i. 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var HbbTVCsManager = require("./hbbtv-cs-manager.js"); 22 | var util = require("util"); 23 | 24 | var HbbTVTerminalManager = function (httpServer) { 25 | var isCs = true; 26 | HbbTVCsManager.call(this,httpServer,isCs); 27 | }; 28 | 29 | util.inherits(HbbTVTerminalManager, HbbTVCsManager); 30 | module.exports = HbbTVTerminalManager; -------------------------------------------------------------------------------- /lib/hbbtv.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var WebSocket = require("ws"); 22 | var HbbTVApp2AppServer = require("./hbbtv-app2app-server.js"); 23 | var HbbTVDialServer = require("./hbbtv-dial-server.js"); 24 | var HbbTVDialClient = require("./hbbtv-dial-client.js"); 25 | var CsLauncherDialServer = require("./cs-launcher-dial-server.js"); 26 | var CsLauncherDialClient = require("./cs-launcher-dial-client.js"); 27 | var HbbTVCsManager = require("./hbbtv-cs-manager.js"); 28 | var HbbTVTerminalManager = require("./hbbtv-terminal-manager.js"); 29 | 30 | module.exports.HbbTVApp2AppServer = HbbTVApp2AppServer; 31 | module.exports.HbbTVDialServer = HbbTVDialServer; 32 | module.exports.HbbTVDialClient = HbbTVDialClient; 33 | module.exports.CsLauncherDialServer = CsLauncherDialServer; 34 | module.exports.CsLauncherDialClient = CsLauncherDialClient; 35 | module.exports.HbbTVCsManager = HbbTVCsManager; 36 | module.exports.HbbTVTerminalManager = HbbTVTerminalManager; 37 | module.exports.WebSocket = WebSocket; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hbbtv", 3 | "description": "Nodejs implementation of the HbbTV Companion Screen", 4 | "version": "0.0.11", 5 | "author": { 6 | "name": "Louay Bassbouss", 7 | "email": "louay.bassbouss@fokus.fraunhofer.de" 8 | }, 9 | "homepage": "http://www.fokus.fraunhofer.de/fame", 10 | "license": "LGPL-3.0", 11 | "preferGlobal": true, 12 | "keywords": ["ssdp", "nsd", "discovery", "launch", "dial","hbbtv","hbbtv 2.0","hbbtv 2.0 cs","cs", "ws", "websocket", "app2app"], 13 | "main": "index", 14 | "dependencies" : { 15 | "peer-dial": "0.0.8", 16 | "node-uuid": "1.4.1", 17 | "ws": "0.7.2", 18 | "express": "4.20.0", 19 | "opn": "2.0.0", 20 | "xml2js": "0.5.0", 21 | "xmlbuilder": "2.6.4", 22 | "commander": "2.8.1" 23 | }, 24 | "bin": { 25 | "hbbtv": "bin/index.js" 26 | }, 27 | "repository": "https://github.com/fraunhoferfokus/node-hbbtv.git", 28 | "readmeFilename": "Readme.md", 29 | "_id": "hbbtv@0.0.11", 30 | "_from": "hbbtv@*" 31 | } 32 | -------------------------------------------------------------------------------- /test/app2app-local-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by lba on 16/06/15. 3 | */ 4 | 5 | (function(){ 6 | // this example code works in Node.js and Browser. 7 | if(typeof window == "undefined"){ 8 | // we are in Node.js. import required modules 9 | var hbbtv = require("../index.js"); 10 | WebSocket = hbbtv.WebSocket; 11 | } 12 | var run = function(){ 13 | var app2appLocalBaseUrl = "ws://localhost:8080/local/" ; 14 | var appEndpoint = "org.mychannel.myapp"; 15 | var createConnection = function (index) { 16 | var ws = new WebSocket(app2appLocalBaseUrl + appEndpoint); 17 | ws.binaryType = "arraybuffer"; 18 | ws.onopen = function(evt) { 19 | console.log("Connection ",index," waiting ..."); 20 | }; 21 | ws.onclose = function(evt) { 22 | console.log("Connection ",index," closed."); 23 | }; 24 | ws.onerror = function (evt) { 25 | console.log("Connection error."); 26 | }; 27 | ws.onmessage = function(evt) { 28 | if (evt.data == "pairingcompleted") { 29 | console.log("connection ",index," paired"); 30 | ws.onmessage = function(evt) { 31 | if(typeof evt.data == "string"){ 32 | console.log( "Received Message: " + evt.data); 33 | } 34 | else { 35 | var data = typeof Buffer != "undefined"?new Buffer(evt.data): new Int8Array(evt.data); 36 | console.log("Received Binary Message of " + data.length + " bytes", data); 37 | } 38 | }; 39 | ws.send("Hello from HbbTV App: "+index); 40 | createConnection(index+1); 41 | } else { 42 | console.log("Unexpected message received from terminal."); 43 | ws.close(); 44 | } 45 | }; 46 | }; 47 | createConnection(0); 48 | }; 49 | run(); 50 | })(); -------------------------------------------------------------------------------- /test/app2app-remote-client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by lba on 16/06/15. 3 | */ 4 | 5 | (function(){ 6 | // this example code works in Node.js and Browser. 7 | if(typeof window == "undefined"){ 8 | // we are in Node.js. import required modules 9 | var hbbtv = require("../index.js"); 10 | WebSocket = hbbtv.WebSocket; 11 | } 12 | var run = function(){ 13 | var app2appRemoteBaseUrl = "ws://localhost:8080/remote/" ; 14 | var appEndpoint = "org.mychannel.myapp"; 15 | var ws = new WebSocket(app2appRemoteBaseUrl + appEndpoint); 16 | ws.binaryType = "arraybuffer"; 17 | ws.onopen = function(evt) { 18 | console.log("Connection waiting ..."); 19 | }; 20 | ws.onclose = function(evt) { 21 | console.log("Connection closed."); 22 | }; 23 | ws.onerror = function (evt) { 24 | console.log("Connection error."); 25 | }; 26 | ws.onmessage = function(evt) { 27 | if (evt.data == "pairingcompleted") { 28 | console.log("connection paired"); 29 | ws.onmessage = function(evt) { 30 | console.log( "Received Message: " + evt.data); 31 | }; 32 | var data = "Hello from Companion Screen"; 33 | ws.send(data); 34 | var array = [0,1,2,3,4,5,6,7,8,9]; 35 | data = typeof Buffer != "undefined"?new Buffer(array): new Int8Array(array).buffer; 36 | ws.send(data); 37 | } else { 38 | console.log("Unexpected message received from terminal."); 39 | ws.close(); 40 | } 41 | }; 42 | } 43 | run(); 44 | })(); 45 | 46 | -------------------------------------------------------------------------------- /test/hbbtv-nodejs-client.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | var hbbtv = require("../index.js"); 22 | var HbbTVDialClient = hbbtv.HbbTVDialClient; 23 | var WebSocket = hbbtv.WebSocket; 24 | 25 | var hbbTVDialClient = new HbbTVDialClient().on("ready", function () { 26 | console.log("HbbTV DIAL Client is ready"); 27 | }).on("stop", function () { 28 | console.log("HbbTV DIAL Client is stopped"); 29 | }).on("found", function (terminal) { 30 | console.log("HbbTV Terminal ", terminal.getFriendlyName()," (", terminal.getAppLaunchURL(), ") found"); 31 | var channel = (""+Math.random()).substr(2,16); 32 | terminal.launchHbbTVApp({ 33 | "appUrlBase": "http://fraunhoferfokus.github.io/node-hbbtv/www/hbbtv-app.html", 34 | "appLocation": "?channel="+channel 35 | }, function (launchRes,err) { 36 | if(err){ 37 | console.error("Error on launch HbbTV App", err); 38 | } 39 | else { 40 | console.log("HbbTV App launched successfully: ",launchRes || ""); 41 | createConnection(terminal, channel); 42 | } 43 | }); 44 | }).on("error", function (err) { 45 | console.error(err); 46 | }); 47 | 48 | var createConnection = function (terminal, channel) { 49 | var app2appRemoteBaseUrl = terminal.getApp2AppURL(); 50 | var ws = new WebSocket(app2appRemoteBaseUrl + channel); 51 | ws.binaryType = "arraybuffer"; 52 | ws.onopen = function(evt) { 53 | console.log("Connection waiting ..."); 54 | }; 55 | ws.onclose = function(evt) { 56 | console.log("Connection closed."); 57 | }; 58 | ws.onerror = function (evt) { 59 | console.log("Connection error."); 60 | }; 61 | ws.onmessage = function(evt) { 62 | if (evt.data == "pairingcompleted") { 63 | console.log("pairing complete"); 64 | ws.onmessage = function(evt) { 65 | console.log( "Received Message: " + evt.data); 66 | }; 67 | var data = "Hello from Companion Screen"; 68 | ws.send(data); 69 | var array = [0,1,2,3,4,5,6,7,8,9]; 70 | data = typeof Buffer != "undefined"?new Buffer(array): new Int8Array(array).buffer; 71 | ws.send(data); 72 | } else { 73 | console.log("Unexpected message received from terminal."); 74 | ws.close(); 75 | } 76 | }; 77 | }; 78 | 79 | hbbTVDialClient.start(); -------------------------------------------------------------------------------- /www/cs-app.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 26 | 27 | Companion Screen App 28 | 29 | 30 | 31 | 32 |

Sample CS Web Application

33 |
34 | 35 |
36 |
37 |
38 |
    39 | 40 |
41 |
42 |
43 | 44 |
45 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /www/hbbtv-app.html: -------------------------------------------------------------------------------- 1 | 2 | 24 | 25 | 26 | 27 | HbbTV App 28 | 29 | 30 | 31 | 37 | 38 | 39 |

Sample HbbTV Application

40 |
41 | 42 |
43 |
44 |
45 |
    46 | 47 |
48 |
49 |
50 | 51 |
52 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /www/js/hbbtv-manager-polyfill.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * 3 | * Copyright (c) 2015 Louay Bassbouss, Fraunhofer FOKUS, All rights reserved. 4 | * 5 | * This library is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 3.0 of the License, or (at your option) any later version. 9 | * 10 | * This library is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public 16 | * License along with this library. If not, see . 17 | * 18 | * AUTHORS: Louay Bassbouss (louay.bassbouss@fokus.fraunhofer.de) 19 | * 20 | ******************************************************************************/ 21 | (function(){ 22 | 23 | var parseParameters = function(query){ 24 | var dict = {}; 25 | query = query.substr(query.lastIndexOf("#")+1); 26 | if(query){ 27 | var params = query.split("&"); 28 | for (var i = 0; i < params.length; i++) { 29 | var index = params[i].indexOf("="); 30 | var key = index>-1?params[i].substr(0,index):params[i]; 31 | var value = index>-1?params[i].substr(index+1):""; 32 | if(typeof dict[key] == "undefined"){ 33 | dict[key] = value; 34 | } 35 | else if(typeof dict[key] == "string"){ 36 | dict[key] = [dict[key],value]; 37 | } 38 | else if(typeof dict[key] == "object"){ 39 | dict[key].push(value); 40 | } 41 | }; 42 | } 43 | return dict; 44 | }; 45 | 46 | var connect = function () { 47 | ws && ws.close(); 48 | ws = new WebSocket(hbbtvCsManagerUrl); 49 | ws.onopen = function(evt) { 50 | //console.log("Connection to cs manager established"); 51 | }; 52 | ws.onclose = function(evt) { 53 | //console.log("Connection to cs manager closed"); 54 | //window.close(); 55 | if(ws = this){ 56 | ws = null; 57 | } 58 | }; 59 | ws.onerror = function (evt) { 60 | console.error("Error on connect to cs manager"); 61 | }; 62 | ws.onmessage = function(evt) { 63 | try{ 64 | var rsp = JSON.parse(evt.data); 65 | handleRpcResponse(rsp); 66 | } 67 | catch(err){ 68 | console.error("Error on parsing or handling rpc response",err); 69 | } 70 | }; 71 | }; 72 | 73 | var sendRpcRequest = function (req, callback) { 74 | if(!req.id){ 75 | req.id = rpcCounter++; 76 | } 77 | if(callback && ws){ 78 | pendingRpcRequests[req.id] = { 79 | req: req, 80 | callback: callback 81 | }; 82 | ws.send(JSON.stringify(req)); 83 | return true; 84 | } 85 | return false; 86 | }; 87 | 88 | var handleRpcResponse = function (rsp) { 89 | var id = rsp.id; 90 | var pendingReq = pendingRpcRequests[id]; 91 | if(pendingReq){ 92 | if(pendingReq.callback){ 93 | try{ 94 | var req = pendingReq.req || null; 95 | pendingReq.callback.call(req,rsp); 96 | } 97 | catch (err){ 98 | //console.error("the ws response is not a valid rpc message",err); 99 | } 100 | 101 | } 102 | } 103 | }; 104 | 105 | var hash = location.hash.substr(location.hash.lastIndexOf("#")+1); 106 | var hashParameters = parseParameters(hash); 107 | var port = hashParameters.port; 108 | var hostname = hashParameters.hostname; 109 | var app2AppLocalUrl = port && "ws://127.0.0.1:"+port+"/local/" || null; 110 | var app2AppRemoteUrl = port && hostname && "ws://"+hostname+":"+port+"/remote/" || null; 111 | var hbbtvCsManagerUrl = "ws://127.0.0.1:"+port+"/hbbtvmanager"; 112 | var userAgent = navigator.userAgent; 113 | var appLaunchUrl = port && hostname && "http://"+hostname+":"+port+"/dial/apps/HbbTV" || null; 114 | var ws = null; 115 | var rpcCounter = 1; 116 | var pendingRpcRequests = {}; 117 | var csLauncherCounter = 1; 118 | var discoveredLaunchers = {}; 119 | var terminalCounter = 1; 120 | var discoveredTerminals = {}; 121 | /** 122 | * Config is set after hbbtv is set 123 | */ 124 | var config = null; 125 | /** 126 | * A DiscoveredTerminal object shall have the following properties: 127 | * - readonly Number enum_id: A unique ID for a discovered HbbTV terminal 128 | * - readonly String friendly_name: A discovered terminal may provide a friendly name, e.g. “Muttleys TV”, for an HbbTV application to make use of. 129 | * - readonly String X_HbbTV_App2AppURL: The remote service endpoint on the discovered HbbTV terminal for application to application communication 130 | * - readonly String X_HbbTV_InterDevSyncURL: The remote service endpoint on the discovered HbbTV terminal for inter-device synchronisation 131 | * - readonly String X_HbbTV_UserAgent: The User Agent string of the discovered HbbTV terminal 132 | */ 133 | var DiscoveredTerminal = function(enum_id, friendly_name, X_HbbTV_App2AppURL, X_HbbTV_InterDevSyncURL, X_HbbTV_UserAgent){ 134 | Object.defineProperty(this, "enum_id", { 135 | get: function () { 136 | return enum_id; 137 | } 138 | }); 139 | Object.defineProperty(this, "friendly_name", { 140 | get: function () { 141 | return friendly_name; 142 | } 143 | }); 144 | Object.defineProperty(this, "X_HbbTV_App2AppURL", { 145 | get: function () { 146 | return X_HbbTV_App2AppURL; 147 | } 148 | }); 149 | Object.defineProperty(this, "X_HbbTV_InterDevSyncURL", { 150 | get: function () { 151 | return X_HbbTV_InterDevSyncURL; 152 | } 153 | }); 154 | Object.defineProperty(this, "X_HbbTV_UserAgent", { 155 | get: function () { 156 | return X_HbbTV_UserAgent; 157 | } 158 | }); 159 | }; 160 | /** 161 | * A DiscoveredCSLauncher object shall have the following properties: 162 | * - readonly Number enum_id: A unique ID for a CS Launcher Application 163 | * - readonly String friendly_name: A CS Launcher Application may provide a friendly name, e.g. “Muttleys Tablet”, for an HbbTV application to make use of 164 | * - readonly String CS_OS_id: The CS OS identifier string, as described in clause 14.4.1 of the HbbTV 2.0 Spec 165 | */ 166 | var DiscoveredCSLauncher = function(enum_id, friendly_name, CS_OS_id){ 167 | Object.defineProperty(this, "enum_id", { 168 | get: function () { 169 | return enum_id; 170 | } 171 | }); 172 | Object.defineProperty(this, "friendly_name", { 173 | get: function () { 174 | return friendly_name; 175 | } 176 | }); 177 | Object.defineProperty(this, "CS_OS_id", { 178 | get: function () { 179 | return CS_OS_id; 180 | } 181 | }); 182 | }; 183 | 184 | 185 | /** 186 | * Boolean discoverCSLaunchers(function onCSDiscovery) 187 | * callback onCSDiscovery(Number enum_id, String friendly_name, String CS_OS_id ) 188 | */ 189 | var discoverCSLaunchers = function(onCSDiscovery){ 190 | return sendRpcRequest({ 191 | jsonrpc: "2.0", 192 | method: "discoverCSLaunchers", 193 | params: [] 194 | }, function (rsp) { 195 | var csLaunchers = rsp.result; 196 | var res = []; 197 | for(var appUrl in csLaunchers){ 198 | var oldLauncher = discoveredLaunchers[appUrl]; 199 | var launcher = csLaunchers[appUrl]; 200 | launcher.id = appUrl; 201 | var enumId = oldLauncher && oldLauncher.enum_id || csLauncherCounter++; 202 | var newCsLauncher = new DiscoveredCSLauncher(enumId, launcher.friendlyName, launcher.csOsId); 203 | discoveredLaunchers[appUrl] = newCsLauncher; 204 | discoveredLaunchers[enumId] = launcher; 205 | res.push(newCsLauncher); 206 | } 207 | onCSDiscovery && onCSDiscovery.call(null,res); 208 | }); 209 | }; 210 | 211 | /** 212 | * Boolean discoverTerminals(function onTerminalDiscovery) 213 | * callback onTerminalDiscovery (Number enum_id,String friendly_name,DiscoveredTerminalEndpoints endpoints ) 214 | */ 215 | var discoverTerminals = function(onTerminalDiscovery){ 216 | return sendRpcRequest({ 217 | jsonrpc: "2.0", 218 | method: "discoverTerminals", 219 | params: [] 220 | }, function (rsp) { 221 | var terminals = rsp.result; 222 | var res = []; 223 | for(var appUrl in terminals){ 224 | var oldTerminal = discoveredTerminals[appUrl]; 225 | var terminal = terminals[appUrl]; 226 | terminal.id = appUrl; 227 | var enumId = oldTerminal && oldTerminal.enumId || terminalCounter++; 228 | var newTerminal = new DiscoveredTerminal(enumId, terminal.friendlyName, terminal.app2AppURL, terminal.interDevSyncURL, terminal.userAgent); 229 | discoveredTerminals[appUrl] = newTerminal; 230 | discoveredTerminals[enumId] = terminal; 231 | res.push(newTerminal); 232 | } 233 | onTerminalDiscovery && onTerminalDiscovery.call(null,res); 234 | }); 235 | }; 236 | 237 | /** 238 | * Boolean launchCSApp(Integer enum_id, String payload, function onCSLaunch) 239 | * callback onCSLaunch(int enum_id, int error_code) 240 | * Error Codes Values: 241 | * 0: op_rejected 242 | * 2: op_not_guaranteed 243 | * 3: invalid_id 244 | * 4: general_error 245 | */ 246 | var launchCSApp = function(enumId,payload,onCSLaunch){ 247 | var csLauncher = discoveredLaunchers[enumId]; 248 | var code = null; 249 | if(!csLauncher || typeof payload != "string"){ 250 | code = 3; 251 | onCSLaunch && onCSLaunch.call(null,enumId,code); 252 | return false; 253 | } 254 | return sendRpcRequest({ 255 | jsonrpc: "2.0", 256 | method: "launchCSApp", 257 | params: [csLauncher.id, payload] 258 | }, function (rsp) { 259 | var code = rsp.result; 260 | // TODO check code 261 | onCSLaunch && onCSLaunch.call(null,enumId,code); 262 | }); 263 | }; 264 | 265 | /** 266 | * Boolean launchHbbTVApp(Integer enum_id, Object options, function onCSLaunch) 267 | * callback onCSLaunch(int enum_id, int error_code) 268 | * Error Codes Values: 269 | * 0: op_rejected 270 | * 2: op_not_guaranteed 271 | * 3: invalid_id 272 | * 4: general_error 273 | */ 274 | var launchHbbTVApp = function(enumId,options,onHbbTVLaunch){ 275 | var terminal = discoveredTerminals[enumId]; 276 | var code = null; 277 | if(!terminal){ 278 | code = 3; 279 | onHbbTVLaunch && onHbbTVLaunch.call(null,enumId,code); 280 | return false; 281 | } 282 | return sendRpcRequest({ 283 | jsonrpc: "2.0", 284 | method: "launchHbbTVApp", 285 | params: [terminal.id, options] 286 | }, function (rsp) { 287 | var code = rsp.result; 288 | // TODO 289 | onHbbTVLaunch && onHbbTVLaunch.call(null,enumId,code); 290 | }); 291 | }; 292 | 293 | /** 294 | * String getInterDevSyncURL() 295 | * Returns the URL of the CSS-CII service endpoint for the terminal that the calling HbbTV application is running on. 296 | */ 297 | var getInterDevSyncURL =function(){ 298 | console.warn("HbbTVCSManager.getInterDevSyncURL is not supported yet"); 299 | return ""; 300 | }; 301 | 302 | /** 303 | * String getAppLaunchURL() 304 | * Returns the URL of the application launch service endpoint for the terminal that the calling HbbTV application is running on. 305 | */ 306 | var getAppLaunchURL = function(){ 307 | return appLaunchUrl; 308 | }; 309 | 310 | /** 311 | * String getApp2AppLocalBaseURL() 312 | * Returns the base URL of the application to application communication service local endpoint. 313 | * The URL retrieved by this method shall end with a slash (‘/’) character. 314 | */ 315 | var getApp2AppLocalBaseURL =function(){ 316 | return app2AppLocalUrl; 317 | }; 318 | 319 | /** 320 | * String getApp2AppRemoteBaseURL() 321 | * Returns the base URL of the application to application communication service remote endpoint. 322 | * The URL retrieved by this method shall end with a slash (‘/’) character 323 | */ 324 | var getApp2AppRemoteBaseURL =function(){ 325 | return app2AppRemoteUrl; 326 | }; 327 | 328 | var HbbTVCSManager = function(){ 329 | Object.defineProperty(this, "discoverCSLaunchers", { 330 | get: function () { 331 | return discoverCSLaunchers; 332 | } 333 | }); 334 | 335 | Object.defineProperty(this, "discoverTerminals", { 336 | get: function () { 337 | return discoverTerminals; 338 | } 339 | }); 340 | 341 | Object.defineProperty(this, "launchCSApp", { 342 | get: function () { 343 | return launchCSApp; 344 | } 345 | }); 346 | 347 | Object.defineProperty(this, "launchHbbTVApp", { 348 | get: function () { 349 | return launchHbbTVApp; 350 | } 351 | }); 352 | 353 | Object.defineProperty(this, "getInterDevSyncURL", { 354 | get: function () { 355 | return getInterDevSyncURL; 356 | } 357 | }); 358 | 359 | Object.defineProperty(this, "getAppLaunchURL", { 360 | get: function () { 361 | return getAppLaunchURL; 362 | } 363 | }); 364 | 365 | Object.defineProperty(this, "getApp2AppLocalBaseURL", { 366 | get: function () { 367 | return getApp2AppLocalBaseURL; 368 | } 369 | }); 370 | 371 | Object.defineProperty(this, "getApp2AppRemoteBaseURL", { 372 | get: function () { 373 | return getApp2AppRemoteBaseURL; 374 | } 375 | }); 376 | }; 377 | 378 | var HbbTVTerminalManager = function(){ 379 | Object.defineProperty(this, "discoverTerminals", { 380 | get: function () { 381 | return discoverTerminals; 382 | } 383 | }); 384 | 385 | Object.defineProperty(this, "launchHbbTVApp", { 386 | get: function () { 387 | return launchHbbTVApp; 388 | } 389 | }); 390 | }; 391 | 392 | if(port && hostname){ 393 | window.oipfObjectFactory = window.oipfObjectFactory || {}; 394 | window.oipfObjectFactory.createCSManager = oipfObjectFactory.createCSManager || function(){ 395 | return new HbbTVCSManager(); 396 | }; 397 | var oldIsObjectSupported = oipfObjectFactory.isObjectSupported; 398 | window.oipfObjectFactory.isObjectSupported = function(mimeType){ 399 | if(mimeType == "application/hbbtvCSManager"){ 400 | return true; 401 | } 402 | else { 403 | return oldIsObjectSupported && oldIsObjectSupported.app(this,arguments); 404 | } 405 | }; 406 | connect(); 407 | } 408 | else if(port){ 409 | window.hbbtv = window.hbbtv || {}; 410 | window.hbbtv.createTerminalManager = function(){ 411 | return new HbbTVTerminalManager(); 412 | }; 413 | connect(); 414 | } 415 | else { 416 | console.warn("hash parameters 'port' and/or 'hostname' are not detected. " + 417 | "hbbtv-manager-polyfill.js can be used in HbbTV Apps when the hash " + 418 | "parameters 'port' and 'hostname' are specified and in CS Web Apps " + 419 | "when only the 'port' hash parameter is specified. These parameters " + 420 | "will be automatically set when the HbbTV App is launched through the " + 421 | "HbbTVDialServer or the CS Web App is launched through the CsLauncherDialServer. " + 422 | "The hash parameters needs to be set manually if the application is launched by the user."); 423 | } 424 | })(); -------------------------------------------------------------------------------- /www/js/hbbtvlib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HbbTV library v1.0 3 | * 4 | * (C) 2009, IRT GmbH 5 | * 6 | * Overview: 7 | * 8 | * You need to add the OIPF application manager and the oipf configuration 9 | * embedded object to your HTML DOM tree e.g.: 10 | * 11 |
12 | 13 | 14 |
15 | * 16 | * Before using any other function call the initializer first. 17 | * 18 | * function hbbtvlib_intialize() 19 | * Creates and initializes HbbTV, i.e. OIPF DAE, embedded objects. 20 | * Always call this function prior to other functions of this library. 21 | * 22 | * function hbbtvlib_show() 23 | * Shows the application and requests keys via the keyset object. 24 | * 25 | * function hbbtvlib_hide() 26 | * Hides the application and releases keys via the keyset object. 27 | * 28 | * function hbbtvlib_createApp() 29 | * Create applications with the OIPF Application Manager 30 | * 31 | * function hbbtvlib_closeApp() 32 | * Destroys this application. 33 | * 34 | * function hbbtvlib_init_broadcast() 35 | * Convenience function that integrates the broadcast video in your application. 36 | * 37 | * function hbbtvlib_release_broadcast() 38 | * Convenience function that removes the broadcast video from your application. 39 | * 40 | * function hbbtvlib_current_service() 41 | * Returns the DVB service ID. 42 | */ 43 | 44 | /** 45 | * The last error occured in hbbtvlib. If one of the functions returns false, 46 | * the error can be retrieved via this property. 47 | */ 48 | var hbbtvlib_lastError = "no error"; 49 | 50 | /** 51 | * @return true if OIPF functions are available 52 | */ 53 | function hbbtvlib_initialize() { 54 | try { 55 | int_createOipfObjs(); 56 | 57 | var appMgr = int_objs[int_objTypes.appMan]; 58 | if (appMgr && typeof(appMgr.getOwnerApplication) != "undefined") 59 | int_app = appMgr.getOwnerApplication(document); 60 | else { 61 | hbbtvlib_lastError = "no application manager"; 62 | int_app = null; 63 | return false; 64 | } 65 | return int_initKeysetObj(); 66 | } catch (e) { 67 | hbbtvlib_lastError = e; 68 | int_app = null; 69 | } 70 | return false; 71 | }; 72 | 73 | /** 74 | * This function does everything HbbTV requires an application to do 75 | * when it wants to get visible. 76 | * 77 | * @return 78 | */ 79 | function hbbtvlib_show() { 80 | try { 81 | int_app.show(); 82 | int_keyset.setValue(int_ksVisible); 83 | return true; 84 | } catch (e) { 85 | hbbtvlib_lastError = e; 86 | return false; 87 | } 88 | } 89 | 90 | /** 91 | * This function does everything HbbTV requires an application to do 92 | * when it wants to transition to the hidden state. 93 | * 94 | * @return 95 | */ 96 | function hbbtvlib_hide() { 97 | try { 98 | int_app.hide(); 99 | int_keyset.setValue(int_ksHidden); 100 | return true; 101 | } catch (e) { 102 | hbbtvlib_lastError = e; 103 | return false; 104 | } 105 | } 106 | 107 | /** 108 | * Overrides the default keysets. 109 | * Default is RED for hidden state, all for visible state. 110 | * 111 | * @param visibleSet a keyset bitmask 112 | * @param hiddenSet a keyset bitmask 113 | * @return 114 | */ 115 | function hbbtvlib_setKeysets(visibleSet, hiddenSet) { 116 | if (typeof visibleSet == "Number") int_ksVisible = visibleSet; 117 | if (typeof hiddenSet == "Number") int_ksHidden = hiddenSet; 118 | }; 119 | 120 | 121 | /** 122 | * Starts a new application and destroys this application. 123 | * 124 | * @param dvbUrl including organization and application ids 125 | * @param httpUrl a fallback URL 126 | * 127 | * @return false 128 | */ 129 | function hbbtvlib_createApp(dvbUrl, httpUrl) { 130 | if (int_app) { 131 | try { 132 | if (dvbUrl && int_app.createApplication(dvbUrl, false)) { 133 | int_app.destroyApplication(); 134 | return true; 135 | } 136 | } catch (e) { 137 | hbbtvlib_lastError = e; 138 | } 139 | try { 140 | if (httpUrl && int_app.createApplication(httpUrl, false)) { 141 | int_app.destroyApplication(); 142 | return true; 143 | } 144 | } catch (e) { 145 | hbbtvlib_lastError = e; 146 | } 147 | } else { 148 | document.location.href = httpUrl; 149 | } 150 | return false; 151 | }; 152 | 153 | /** 154 | * Destroys this application. 155 | * 156 | * @param fallBackUrl 157 | * @return 158 | */ 159 | function hbbtvlib_closeApp(fallBackUrl) { 160 | if (int_app) { 161 | try { 162 | int_app.destroyApplication(); 163 | } catch (e) { 164 | hbbtvlib_lastError = e; 165 | } 166 | } else if (fallBackUrl) { 167 | document.location.href = fallBackUrl; 168 | } else { 169 | window.close(); 170 | } 171 | return false; 172 | }; 173 | 174 | /** 175 | * Handle OIPF DAE Broadcast video 176 | */ 177 | var int_bc_video = null; 178 | var int_bc_container = null; 179 | 180 | /** 181 | * Creates and initializes a broadcast video inside the element 182 | * identified by the containerId. If no bc video can be included 183 | * the dummy picture is added instead of the bc. 184 | * 185 | * @param parentId the id of the HTML container where the video/broadcast object will be added 186 | * @param objId id which shall be set for the video/broadcast object 187 | * @param dummyPic an optional picture to be shown if video/broadcast can not be added 188 | * 189 | * @return the video/broadcast object or false 190 | */ 191 | function hbbtvlib_init_broadcast (parentId, objId, dummyPic) { 192 | try { 193 | int_bc_container = document.getElementById(parentId); 194 | if (!int_bc_container) return false; 195 | 196 | // root container for video/broadcast object 197 | int_bc_container.innerHTML = ' '; 198 | int_bc_video = document.getElementById(objId); 199 | 200 | if (typeof (int_bc_video.fullScreen) != 'undefined') { 201 | int_bc_video.setFullScreen(false); 202 | } 203 | if (typeof (int_bc_video.bindToCurrentChannel) != 'undefined') { 204 | int_bc_video.bindToCurrentChannel(); 205 | } 206 | 207 | // check if OIPF video object is supported 208 | if (typeof (int_bc_video.currentChannel) != 'undefined') return int_bc_video; 209 | 210 | } catch (e) { 211 | hbbtvlib_lastError = e; 212 | } 213 | if (dummyPic) int_bc_container.innerHTML = 'TV Bild'; 214 | return false; 215 | }; 216 | 217 | /** 218 | * Releases the video/broadcast object and removes the object from the DOM. 219 | */ 220 | function hbbtvlib_release_broadcast () { 221 | try { 222 | if (int_bc_video && typeof (int_bc_video.release) != "undefined") { 223 | int_bc_video.release(); 224 | int_bc_video = null; 225 | } 226 | if (int_bc_container) { 227 | int_bc_container.innerHTML = ""; 228 | int_bc_container = null; 229 | } 230 | } catch (e) { 231 | hbbtvlib_lastError = e; 232 | } 233 | }; 234 | 235 | /** 236 | * Requires hbbtvlib_init_broadcast 237 | * 238 | * @return the DVB service id of the current presented service or -1 239 | */ 240 | function hbbtvlib_current_service () { 241 | try { 242 | return int_bc_video.currentChannel.sid; 243 | } catch (e) { 244 | hbbtvlib_lastError = e; 245 | } 246 | return -1; 247 | }; 248 | 249 | /** 250 | * Tunes to a DVB service identified by the DVB Triplet. The application may 251 | * get killed due to the application life cycle, i.e. if it is not signalled 252 | * with their application ID on the tuned service. 253 | * 254 | * @param onid the original network id 255 | * @param tsid the transport stream id 256 | * @param sid the service id 257 | * 258 | * @return false if there is no video broadcast object available or there is no 259 | * channel found for this triplet 260 | */ 261 | function hbbtvlib_tuneTo(onid, tsid, sid) { 262 | try { 263 | var chLst = int_bc_video.getChannelConfig().channelList; 264 | 265 | onid = (typeof(onid) == 'number') ? onid : parseInt(onid, 10); 266 | tsid = (typeof(tsid) == 'number') ? tsid : parseInt(tsid, 10); 267 | sid = (typeof(sid) == 'number') ? sid : parseInt(sid, 10); 268 | 269 | var ch = chLst.getChannelByTriplet(onid, tsid, sid); 270 | 271 | if (ch == null) { 272 | hbbtvlib_lastError = "Tuning failed, no channel object found for given DVB triplet."; 273 | return false; 274 | } 275 | 276 | int_bc_video.setChannel(ch, false); 277 | 278 | return true; 279 | }catch (e) { 280 | hbbtvlib_lastError = "Tuning failed due to " + e; 281 | } 282 | return false; 283 | }; 284 | 285 | /* 286 | * Library internal (private) properties and functions. 287 | */ 288 | 289 | /** 290 | * Array of OIPF DAE embedded objects. Access Key is the mime-type without "application/". 291 | */ 292 | var int_objs = new Array(); 293 | 294 | var int_objTypes = { 295 | appMan: "oipfApplicationManager", 296 | config: "oipfConfiguration" 297 | }; 298 | 299 | /** 300 | * OIPF DAE Application object 301 | * - to show/hide the application 302 | * - create or destroy applications 303 | * - providing the keyset object 304 | */ 305 | var int_app = null; 306 | 307 | /** 308 | * The OIPF keyset object used to request keys from terminal. 309 | * 310 | * Usually this is the RED button in hidden mode, and up to all 311 | * available keys in visible mode. 312 | */ 313 | var int_keyset = null; 314 | 315 | var int_ksHidden = null; 316 | 317 | var int_ksVisible = null; 318 | 319 | /** 320 | * Internal function to create OIPF embedded objects, 321 | * like ApplicationManager etc., if not already existing. 322 | */ 323 | function int_createOipfObjs() { 324 | if (int_objs.length) return; 325 | // Look for existing objects 326 | var objects = document.getElementsByTagName("object"); 327 | for (var i=0; i