{"id":149,"date":"2023-02-20T15:12:22","date_gmt":"2023-02-20T15:12:22","guid":{"rendered":"https:\/\/oiv10wp.athena-dev.net\/?page_id=149"},"modified":"2023-08-24T12:10:58","modified_gmt":"2023-08-24T12:10:58","slug":"remoteviz-getting-started-guide","status":"publish","type":"page","link":"https:\/\/developer.openinventor.com\/index.php\/general-documentation\/remoteviz-getting-started-guide\/","title":{"rendered":"RemoteViz Getting Started Guide"},"content":{"rendered":"<h1 id=\"table-of-contents\">Table of Contents<\/h1>\n<ul>\n<li><a href=\"#remoteviz-----image-streaming-api-for-the-cloud\">What is RemoteViz?<\/a><\/li>\n<li><a href=\"#basic-concepts\">Basic Concepts<\/a><\/li>\n<li><a href=\"#getting-started-with-remoteviz\">Getting Started with RemoteViz API (HelloCone)<\/a>\n<ul>\n<li><a href=\"#main\">Main<\/a><\/li>\n<li><a href=\"#service-settings\">Service Settings<\/a><\/li>\n<li><a href=\"#service-listener\">Service Listener<\/a><\/li>\n<li><a href=\"#renderarea\">RenderArea<\/a><\/li>\n<li><a href=\"#renderarea-listener\">RenderArea Listener<\/a><\/li>\n<li><a href=\"#create-a-scene-graph\">Scene Graph<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#client-web-viewer\">Client - Web Viewer<\/a><\/li>\n<li><a href=\"#build-and-run-the-example\">Build and Run the Example<\/a><\/li>\n<li><a href=\"#docker_demo\">Demo in Docker<\/a><\/li>\n<li><a href=\"#user-interaction\">User Interaction<\/a><\/li>\n<li><a href=\"#client-service-communication\">Client-Service Communication<\/a><\/li>\n<li><a href=\"#encoding-frames-for-streaming\">Frames Encoding<\/a><\/li>\n<li><a href=\"#secure-connection\">Secure Connection<\/a><\/li>\n<\/ul>\n<p><\/p>\n<\/p>\n<h2 id=\"remoteviz-image-streaming-api-for-the-cloud\">RemoteViz --- Image Streaming API for the Cloud<\/h2>\n<p>RemoteViz is an <em>image streaming<\/em> toolkit. RemoteViz enables true web-based 2D\/3D visualization that runs on a server and displays in the user's web browser. As images are rendered by the visualization software, RemoteViz streams them to the browser, using video encoding algorithms (H.264, VP9) to minimize required bandwidth, and GPU accelerated encoding to maximize performance. Remote desktop technologies can be used to \"lift and shift\" an existing desktop application into the Cloud, but that solution has many limitations. RemoteViz is designed to support visualization as a microservice in a modern web-based application. RemoteViz has many features but primarily solves three key problems.<\/p>\n<p>First, to be interactive, a visualization service like a 3D viewer must deliver at least twenty images per second to the user's machine and each image may contain millions of pixels. That's a lot of data. RemoteViz manages the encoding and transmission of images to minimize bandwidth, maximize frame rate,  and maintain image quality. Video encoding formats like H.264 and VP9 can dramatically reduce the bandwidth requirements. And using GPU acceleration can optimize performance, both for encoding (on the server-side) and for decoding (on the browser-side) the image stream. RemoteViz has no minimum bandwidth requirement and can adapt to the current network conditions by prioritizing either the image quality or the frame rate.<\/p>\n<p>Second, modern web-based applications want to display directly in the user's web browser. RemoteViz does not require any client software to be installed, just a small block of JavaScript that is downloaded as part of the web page HTML. One or more RemoteViz display areas can be defined on a single page and can be integrated with any user interface framework. The RemoteViz JavaScript object can automatically send user input events back to the visualization service and it can also exchange application defined messages with the service. RemoteViz uses the browser's native support for video formats to take advantage of local decoding acceleration. The image stream can be displayed on almost any local device (workstation, laptop, tablet or phone) as long as the browser supports HTML5.<\/p>\n<p>Third, and finally, RemoteViz allows web-based visualization applications to take full advantage of Cloud-native services.  RemoteViz can be used, for example, to quickly implement a 3D viewer as a standalone web-based application. But RemoteViz is really designed to integrate visualization into a web-based application using a modern microservice architecture. For example, we provide examples showing how to integrate a RemoteViz visualization service with commonly used services like authentication and load-balancing. A RemoteViz service can be deployed in a public or private Cloud or on-premises. RemoteViz is compatible with Docker, Kubernetes, and other tools.<\/p>\n<p><\/p>\n<h3 id=\"why-would-you-use-remoteviz\">Why would you use RemoteViz<\/h3>\n<p>As described in the previous section, we believe RemoteViz is the best solution because it enables the implementation of a Cloud-native application on the server-side, provides state of the art image streaming between the server and browser, and integrates with the local user interface on the browser side.<\/p>\n<p>But first, you might ask: Why implement a visualization application as a Cloud-native service using server-side rendering and image streaming? There are many general reasons for implementing an application in the Cloud, such as Cloud-native services, scalable resources, global access, and easy updating of application code. We don\u2019t need to repeat those here. There are also specific reasons for using server-side rendering.<\/p>\n<p>Number one is data. To use a local rendering solution, some portion of your application\u2019s data must be transferred to the user\u2019s machine. with server-side rendering, your data stays in the Cloud. Only rendered images are transferred to the user\u2019s machine. Many applications need to keep data on the server for security. Medical applications generally need to keep data on the server for privacy. Oil and gas applications generally need to keep data on the server because of the size. A single seismic volume may be hundreds of gigabytes or even a terabyte of data. Keeping one copy of the data in the Cloud allows users to share while reducing storage expense and time-consuming copies. Data stored in the Cloud can be efficiently streamed to the services that need it using high speed networks. Visualization libraries, like Open Inventor, are designed for this scenario. Using \"tiled\" data storage, Open Inventor allows random access to any part of a large volume data set and automatically computes the data blocks needed to render the best possible image based on the server hardware, camera position, image size, and other factors.<\/p>\n<p>Number two is hardware capability. When you use a local rendering solution, the application is limited to the CPU, GPU, and memory resources on the user\u2019s machine. Local rendering can be a good solution for applications with small data sets and relatively simple rendering requirements. For applications with larger data sets, the memory on the local machine may limit the amount of data that can be rendered or prevent the user from using full resolution data. For applications that require more complex rendering, like volume ray-casting, the GPU on the local machine may limit the image quality or even prevent the user from using some features. Using server-side rendering with RemoteViz streaming, users can view and interact with their data on almost any device. On the server-side, the application or the user can decide what kind of hardware instance to use. Users can have the capacity and rendering quality of a high-end workstation without the acquisition and maintenance costs.<\/p>\n<p>RemoteViz image streaming works with any visualization library. As an initial step in the migration to the Cloud, some customers have even ported their existing applications to use as a \"head-less\" render engine. Commands from the local user interface are passed to the render engine and RemoteViz is used to stream the rendered images to the user\u2019s browser. However, it is worth noting that the Open Inventor visualization library includes a highly optimized integration of RemoteViz streaming. For example, using Open Inventor, it is not necessary to upload each rendered image from the GPU before compression and encoding can be done. Using Open Inventor, RemoteViz is able to compress each rendered image on the GPU and only upload the resulting, much smaller, block of data. Open Inventor is also a powerful visualization and image processing library with many features specifically designed to increase the value of oil&amp;gas and medical applications.<\/p>\n<p><\/p>\n<h3 id=\"about-this-guide\">About This Guide<\/h3>\n<p>This guide is intended to walk you through creating your first basic RemoteViz application. It will not make you a RemoteViz expert or teach you how to write and deploy a real web application. However, we mention some \"next steps\" topics and provide links to more information about using RemoteViz.<\/p>\n<p>As a prerequisite for this guide, we expect that you are familiar with C++, Open Inventor programming, and have a basic understanding of JavaScript. You don\u2019t need advanced web development experience. You can run and test the example program on your local machine. You do not need access to the Cloud. Although the example code is in C++, RemoteViz is available for .NET (C#) and Java. The code will be very similar.<\/p>\n<p>Even though we use the Open Inventor library to provide visualization for our example program, RemoteViz can be used with any visualization library.<\/p>\n<p>Note: The example code assumes that you are using Open Inventor version 10.6 or higher. RemoteViz is available for older versions but there will be some differences in the method signatures.<\/p>\n<p><\/p>\n<h2 id=\"basic-concepts\">Basic Concepts<\/h2>\n<p>There are three components of a RemoteViz application: the service, the client, and a web server. The <strong>RemoteViz Service<\/strong> is basically an Open Inventor application with no viewer that runs on the server-side. It is in charge of data access, computing, and rendering. It renders the scene and sends it to the client via a web-socket connection. In most cases, <strong>RemoteViz Client<\/strong> is a web browser page hosting a render area. The client accepts the images and displays them on a render area. The client also captures the user input events and sends it to the service to be handled as Open Inventor events. In a production environment, a <strong>web-server<\/strong> hosts the client and manages web-socket communication between the client and the server.<\/p>\n<table>\n<thead>\n<tr>\n<th><\/th>\n<th><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><img decoding=\"async\" src=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/mechanism.png\" alt=\"RemoteViz architecture\"><\/td>\n<td>1. <strong>Service<\/strong> is in charge of executing the Open Inventor code <br \/> - Interprets events from the client <br \/> - Renders the scene using Open Inventor <br \/> - Sends images to the client <\/p>\n<p> 2. <strong>Web Server<\/strong> serves the web-page containing the render area <br \/> - Communicates between the service and the client <\/p>\n<p> 3. <strong>Client<\/strong> receives images and displays them <br \/> - Captures user requests &amp; system events <br \/> - Sends them to the RemoteViz service<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A RemoteViz application, by default, runs in Inventor Service mode and uses Open Inventor render engine. However, you may use your own render engine in a RemoteViz application by using the Independent Service run mode.<\/p>\n<p><\/p>\n<h2 id=\"getting-started-with-remoteviz\">Getting Started with RemoteViz<\/h2>\n<p>In this section, you will learn the basics of programming using the RemoteViz API. We will walk through how to create a simple rendering service. We will then implement listener classes to handle notifications from the service and the render area.<\/p>\n<p>This guide basically implements the \"HelloCone\" RemoteViz example program. You can find it in the <em>$(OIVHOME)\/examples\/source\/RemoteViz<\/em> directory.<\/p>\n<h3 id=\"main\">Main<\/h3>\n<p>First, include the header files for <em>Service<\/em>, <em>ServiceSettings<\/em>, and <em>ServiceListener<\/em> classes.<\/p>\n<pre><code class=\"lang-cpp\"><span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include<\/span> <span class=\"hljs-meta-string\">&lt;RemoteViz\/Rendering\/Service.h&gt;<\/span><\/span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include<\/span> <span class=\"hljs-meta-string\">&lt;RemoteViz\/Rendering\/ServiceSettings.h&gt;<\/span><\/span>\n<\/code><\/pre>\n<p>Now add the following <strong>using<\/strong> statement before the main function. Note that all RemoteViz classes are defined inside a namespace, so the <strong>using<\/strong> directive allows RemoteViz classes to be more conveniently used without <em>RemoteViz::Rendering<\/em> as an explicit qualifier.<\/p>\n<pre><code class=\"lang-cpp\"><span class=\"hljs-keyword\">using<\/span> RemoteViz::Rendering;\n<\/code><\/pre>\n<p>In the <strong>main( )<\/strong> function, define settings such as IP address and port number of the rendering service.<\/p>\n<pre><code class=\"lang-c\"><span class=\"hljs-comment\">\/\/ Use localhost address for service IP address<\/span>\n<span class=\"hljs-built_in\">std<\/span>::<span class=\"hljs-built_in\">string<\/span> serviceIPaddress = <span class=\"hljs-string\">\"127.0.0.1\"<\/span>;\n<span class=\"hljs-comment\">\/\/ Default value for service port<\/span>\n<span class=\"hljs-keyword\">unsigned<\/span> <span class=\"hljs-keyword\">short<\/span> servicePort = <span class=\"hljs-number\">8080<\/span>;\n<\/code><\/pre>\n<p>If the service IP and port number are passed as command line arguments, then you need to access the values from argument vector (argv).<\/p>\n<pre><code class=\"lang-c\"><span class=\"hljs-comment\">\/\/ Use passed values for the service IP address and port<\/span>\n<span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-comment\">(argc == 3)<\/span>\n{\n  serviceIPaddress = argv[<span class=\"hljs-number\">1<\/span>];\n  servicePort = atoi<span class=\"hljs-comment\">(argv[2])<\/span>;\n}\n<\/code><\/pre>\n<h3 id=\"service-settings\">Service Settings<\/h3>\n<p>Next, you have to create an instance of <code>RemoteViz::Rendering::ServiceSettings<\/code> to set the properties of rendering service.<\/p>\n<pre><code class=\"lang-c\"><span class=\"hljs-comment\">\/\/ Instantiate a service settings class<\/span>\nauto settings = std::make_shared&lt;ServiceSettings&gt;();\n<span class=\"hljs-comment\">\/\/ Set the IP address used by the service<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-title\">settings<\/span>-&gt;<\/span>setIP(serviceIPaddress);\n<span class=\"hljs-comment\">\/\/ Set the port used by the service<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-title\">settings<\/span>-&gt;<\/span>setPort(servicePort);\n<\/code><\/pre>\n<p>The <code>RemoteViz::Rendering::Service<\/code> class defines the rendering service. This class uses the singleton pattern to restrict its instantiation to a single unique object in the program, so there can only be one service per executable.<\/p>\n<p>Use the method <code>Service::instance()<\/code> to access the service object.<\/p>\n<p>Usually, you will attach a listener to the service object before using it. The listener receives notifications from the rendering service and responds accordingly. (More on the <em>ServiceListener<\/em> and <em>HelloConeServiceListener<\/em> classes in the next section.)<\/p>\n<pre><code class=\"lang-cpp\"><span class=\"hljs-comment\">\/\/ Create an object of ServiceListener class to manage the service events<\/span>\nauto serviceListener = std::make_shared&lt;HelloConeServiceListener&gt;();\n<span class=\"hljs-comment\">\/\/ Attach serviceListener to the service<\/span>\nService::<span class=\"hljs-keyword\">instance<\/span>()-&gt;addListener(serviceListener);\n<\/code><\/pre>\n<p>Before using the service, you must first open it with <code>Service::open()<\/code>.  You can then start the service by calling <code>Service::dispatch()<\/code> in a continuous loop until interrupted. The <code>dispatch()<\/code> method processes all RemoteViz events.<\/p>\n<pre><code class=\"lang-cpp\"><span class=\"hljs-comment\">\/\/ Open the service by using the settings<\/span>\n<span class=\"hljs-keyword\">if<\/span> (Service::<span class=\"hljs-keyword\">instance<\/span>()-&gt;open(settings))\n{\n  ...\n  <span class=\"hljs-comment\">\/\/ Main loop<\/span>\n  while(running)\n  {\n    Service::<span class=\"hljs-keyword\">instance<\/span>()-&gt;dispatch();\n    std::this_thread::sleep_for(std::chrono::milliseconds(<span class=\"hljs-number\">1<\/span>));\n  }\n  <span class=\"hljs-comment\">\/\/ Close the service<\/span>\n  Service::<span class=\"hljs-keyword\">instance<\/span>()-&gt;close();\n}\nelse\n{\n  std::cout &lt;&lt; <span class=\"hljs-string\">\"Error occurred during service initialization.\\n\"<\/span>;\n}\n<\/code><\/pre>\n<p>Notice that after the program exits the main loop due to some interruption, you must close the rendering service using <code>Service::close()<\/code>. This is the end of the <strong>main( )<\/strong> function.<\/p>\n<p>So far you have defined a rendering service but it does not render anything. Next, we will implement a <code>ServiceListener<\/code>, create a simple scene graph, and render it on a render area. Wait!! Before that, please allow me to introduce the <code>ServiceListener<\/code>, <code>RenderArea<\/code>, and <code>RenderAreaListener<\/code> classes of RemoteViz.<\/p>\n<h3 id=\"service-listener\">Service Listener<\/h3>\n<p>The service listener listens for events and notifications from the RemoteViz service and allows an application to respond. In your application, you will implement a listener class derived from the base class <code>RemoteViz::Rendering::ServiceListener<\/code> and attach the listener object to the rendering service.<\/p>\n<p>The listener is notified of events from the service and a typical sequence of calls to the service listener is:<\/p>\n<ul>\n<li><code>onConnectedClient()<\/code> - Client object has been created and is connected to the service<\/li>\n<li><code>onPendingCreateRenderArea()<\/code>\/<code>onPendingShareRenderArea()<\/code> - RenderArea object is about to be created or shared<\/li>\n<li><code>onInstantiatedRenderArea()<\/code> - RenderArea object has been created<\/li>\n<li><code>onInitializedClient()<\/code> - Client is running and bandwidth calibration has finished<br \/>\n Application is running ...<\/li>\n<li><code>onDisposingRenderArea()<\/code> - Connection to RenderArea has been closed so it is to be disposed<\/li>\n<li><code>onDisposedRenderArea()<\/code> - RenderArea object has been disposed<\/li>\n<li><code>onDisconnectedClient()<\/code> - Client object has been disposed<\/li>\n<\/ul>\n<p>Usually, you will define a listener class derived from <code>RemoteViz::Rendering::ServiceListener<\/code> and override the virtual function <code>onInstantiatedRenderArea()<\/code> to create a simple scene graph. The other virtual functions will inherit the base class implementation for now.<\/p>\n<pre><code class=\"lang-cpp\"><span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include<\/span> <span class=\"hljs-meta-string\">&lt;RemoteViz\/Rendering\/ServiceListener.h&gt;<\/span><\/span>\n<span class=\"hljs-meta\">#<span class=\"hljs-meta-keyword\">include<\/span> <span class=\"hljs-meta-string\">&lt;RemoteViz\/Rendering\/RenderAreaListener.h&gt;<\/span><\/span>\n...\n<span class=\"hljs-keyword\">class<\/span> HelloConeServiceListener : <span class=\"hljs-keyword\">public<\/span> RemoteViz::Rendering::ServiceListener\n{\n<span class=\"hljs-keyword\">public<\/span>:\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">onInstantiatedRenderArea<\/span><span class=\"hljs-params\">(<span class=\"hljs-built_in\">std<\/span>::<span class=\"hljs-built_in\">shared_ptr<\/span>&lt;RemoteViz::Rendering::RenderArea&gt; renderArea)<\/span> override\n  <\/span>{\n    <span class=\"hljs-comment\">\/\/\/ Implementation in next section<\/span>\n    ...\n  }\n};\n<\/code><\/pre>\n<h3 id=\"renderarea\">RenderArea<\/h3>\n<p><code>RemoteViz::Rendering::RenderArea<\/code> class defines a rendering area for the application to render an Open Inventor scene. This class provides access to the scene manager to define and manage the scene graph. You can call <code>getSceneManager()<\/code> to access the manager object.<\/p>\n<pre><code class=\"lang-cpp\">  <span class=\"hljs-comment\">\/\/ Access the scene manager and define a scene graph for RenderArea<\/span>\n  renderArea-&gt;getSceneManager()-&gt;setSceneGraph(...);\n  ...\n<\/code><\/pre>\n<p>This class also provides access to the touch manager for managing gesture events from touch screen devices. Use the <code>getTouchManager()<\/code> method to access the manager object. The touch manager only recognizes gestures that were registered. You can use the touch manager method <code>addRecognizer()<\/code> to register a custom gesture, but usually, you will just call <code>addDefaultRecognizers()<\/code> method to register all default gestures.<\/p>\n<pre><code class=\"lang-cpp\">  ...\n  renderArea-&gt;getTouchManager()-&gt;addDefaultRecognizers();\n  ...\n<\/code><\/pre>\n<h3 id=\"renderarea-listener\">RenderArea Listener<\/h3>\n<p><!--- What is RenderAreaListener and call sequence ---><\/p>\n<p>A RenderArea listener receives notifications from a <code>RenderArea<\/code> and allows an application to manage them by performing specific actions. You can categorize these notifications as follows:<\/p>\n<ul>\n<li>Connection: Triggered when a connection has been created, initialized and disposed<\/li>\n<li>Rendering: Triggered at the start\/end of rendering and when an image is sent to the client<\/li>\n<li>Input events: Triggered on mouse, keyboard and touch events<\/li>\n<li>Messages: Triggered when a message is sent from the client<\/li>\n<\/ul>\n<p>To manage these events and notifications, you need to implement a listener class derived from the base class <code>RenderAreaListener<\/code> and override relevant virtual methods. A typical sequence of calls to this listener is:<\/p>\n<ul>\n<li><code>onOpenedConnection()<\/code> - Connection object has been created<\/li>\n<li><code>onInitializedConnection()<\/code> - Connection is fully initialized<\/li>\n<li>While application is rendering ...\n<ul>\n<li><code>onPreRender()<\/code> - Scene will be rendered<\/li>\n<li><code>onPostRender()<\/code> - Scene has been rendered<\/li>\n<li><code>onSendingFrame()<\/code> - Rendered image will be sent to the client<\/li>\n<\/ul>\n<\/li>\n<li><code>onClosedConnection()<\/code>- Connection object has been disposed<\/li>\n<\/ul>\n<p>Usually you will attach a listener object to the <code>RenderArea<\/code> in the <code>ServiceListener::onInstantiatedRenderArea()<\/code> method.<\/p>\n<pre><code class=\"lang-cpp\">void onInstantiatedRenderArea(<span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:RenderArea&gt;<\/span> renderArea) override\n{\n  <span class=\"hljs-regexp\">\/\/<\/span> Instance of RenderAreaListener to manage the renderArea events (default behaviors).\n  auto renderAreaListener = <span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:make_shared&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:RenderAreaListener&gt;<\/span>();\n\n  <span class=\"hljs-regexp\">\/\/<\/span> Attach the renderAreaListener instance to the RenderArea\n  renderArea-&gt;addListener(renderAreaListener);\n  ...\n}\n<\/code><\/pre>\n<p>Next, we will create a scene graph for rendering and assign it to the <em>RenderArea<\/em> inside this virtual method.<\/p>\n<h3 id=\"create-a-scene-graph\">Create a Scene Graph<\/h3>\n<p>Use a <code>SceneExaminer<\/code> node as the <strong>root<\/strong> of the scene graph.<\/p>\n<p>Classic Open Inventor examiner viewers are not available for RemoteViz applications, so we will use a <code>SceneExaminer<\/code> to provide headlight and camera manipulations similar to the classic viewers.<\/p>\n<pre><code class=\"lang-cpp\">void onInstantiatedRenderArea(std::shared_ptr&lt;RemoteViz::Rendering::RenderArea&gt; renderArea) override\n{\n  ...\n  ...\n  <span class=\"hljs-comment\">\/\/ Instantiate a SceneExaminer to interact with the camera<\/span>\n  SceneExaminer *examiner = new SceneExaminer();\n\n  <span class=\"hljs-comment\">\/\/ Build a scene graph<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-title\">examiner<\/span>-&gt;<\/span>addChild(new SoCone());\n  <span class=\"hljs-function\"><span class=\"hljs-title\">examiner<\/span>-&gt;<\/span>addChild(new SoGradientBackground());\n\n  <span class=\"hljs-comment\">\/\/ Set the scene graph for renderArea<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-title\">renderArea<\/span>-&gt;<\/span><span class=\"hljs-function\"><span class=\"hljs-title\">getSceneManager<\/span>()-&gt;<\/span>setSceneGraph(examiner);\n\n  <span class=\"hljs-comment\">\/\/ Adjust camera to view-all of the scene<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-title\">examiner<\/span>-&gt;<\/span><span class=\"hljs-function\"><span class=\"hljs-title\">viewAll<\/span>(renderArea-&gt;<\/span><span class=\"hljs-function\"><span class=\"hljs-title\">getSceneManager<\/span>()-&gt;<\/span>getViewportRegion());\n  }\n<\/code><\/pre>\n<p>To manage events from touch screen devices, register gesture recognizers using the touch manager.<\/p>\n<pre><code class=\"lang-cpp\">void onInstantiatedRenderArea(std::shared_ptr&lt;RemoteViz::Rendering::RenderArea&gt; renderArea) override\n{\n  ...\n  <span class=\"hljs-comment\">\/\/ Add recognizers for gesture events<\/span>\n  renderArea-&gt;getTouchManager()-&gt;addDefaultRecognizers();\n  ...\n}\n<\/code><\/pre>\n<p>Besides the <strong>main( )<\/strong> function that creates and launches the rendering service, you now have implemented a basic RemoteViz service. Next, we will create a simple web-viewer for the client and introduce the JavaScript API used for developing the client viewer.<br \/>\n<!--- Download the basic RemoteViz example from ... ---><\/p>\n<h2 id=\"client-web-viewer\">Client - Web Viewer<\/h2>\n<p><!--- JavaScript API  ---><br \/>\n<!--- HTML code, body, head, javascript functions  ---><\/p>\n<p>In this section, we will create a web-viewer for client application using the RemoteViz Client API. We will first define HTML header and body, then implement a method to initialize a render area and connect to the service.<\/p>\n<h3 id=\"html-header\">HTML Header<\/h3>\n<p>You need to add the following code sample to define an HTML header in the <strong>index.html<\/strong>.  The first line specifies the <code>DOCTYPE<\/code> of a document. This lets a browser know the version of the HTML document.<\/p>\n<p>Here, you have to declare the web application as HTML5 by using the <code>&lt;!DOCTYPE html&gt;<\/code> declaration.<\/p>\n<pre><code class=\"lang-html\"><span class=\"hljs-meta\">&lt;!DOCTYPE html&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>RemoteViz Example<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"stylesheet\"<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/css\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"loader.css\"<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- include the loader css --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/javascript\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"RemoteVizClient.js\"<\/span>&gt;<\/span><span class=\"undefined\"><\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- include the Client API --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/javascript\"<\/span>&gt;<\/span><span class=\"undefined\">\n  ...\n  <\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<\/code><\/pre>\n<p>The <code>&lt;link&gt;<\/code> tag includes a reference to an external style sheet file <strong>loader.css<\/strong> which defines the spin animation while the page is loading. This CSS file is provided with RemoteViz example.<\/p>\n<p>The RemoteViz Client JavaScript API is loaded using the <code>&lt;script&gt;<\/code> tag and the location (URL) of JavaScript file <strong>RemoteVizClient.js<\/strong> is specified in the <code>src<\/code> attribute. This JavaScript API enables the development of HTML5 client application.<\/p>\n<p>Usually, you will implement the client-side scripts (JavaScript) inside the second <code>&lt;script&gt;<\/code> tag.<\/p>\n<h3 id=\"html-body\">HTML Body<\/h3>\n<p>Next, we will define a minimal HTML body that contains a couple of <code>&lt;div&gt;<\/code> elements. The \"loaderGroup\" div contains the graphic temporarily displayed during network calibration. The one with id \"TheDiv\" represents the canvas (container) for a render area.<\/p>\n<pre><code class=\"lang-html\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span> <span class=\"hljs-attr\">onload<\/span>=<span class=\"hljs-string\">\"init()\"<\/span> <span class=\"hljs-attr\">bgcolor<\/span>=<span class=\"hljs-string\">\"silver\"<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- loader notifying the network bandwidth calibration --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"loaderGroup\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"display:none;\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"loader\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"loaderText\"<\/span>&gt;<\/span>Network calibration<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"TheDiv\"<\/span>&gt;<\/span> <span class=\"hljs-comment\">&lt;!-- element displaying the renderArea --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span>\n<\/code><\/pre>\n<p>In the <code>&lt;body&gt;<\/code> element,  we associate the <code>init()<\/code> method with the <code>onload<\/code> event. So when the web page is loaded, the on-load event is triggered and the assigned script is executed.<\/p>\n<h3 id=\"initialize-renderarea\">Initialize RenderArea<\/h3>\n<p>Now let's implement the <code>init()<\/code> method inside the second <code>&lt;script&gt;<\/code> tag.  First, create an instance of a render area on the server and assign it to the <code>theRenderArea<\/code> variable.  The first argument, \"TheDiv\", is the id of the render area container which must be a <code>&lt;div&gt;<\/code> element; the second and third arguments are the requested width and height of the container.<\/p>\n<pre><code class=\"lang-html\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">script<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\/javascript\"<\/span>&gt;<\/span><span class=\"undefined\"><\/span>\n<\/code><\/pre>\n<pre><code class=\"lang-js\">  <span class=\"hljs-keyword\">var<\/span> theRenderArea = <span class=\"hljs-literal\">null<\/span>;\n  <span class=\"hljs-comment\">\/\/ This function is called immediately after the page is loaded.<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">()<\/span>\n  <\/span>{\n    <span class=\"hljs-comment\">\/\/ Initialization of the renderArea.<\/span>\n    theRenderArea = <span class=\"hljs-keyword\">new<\/span> RemoteVizRenderArea(<span class=\"hljs-string\">\"TheDiv\"<\/span>, <span class=\"hljs-number\">640<\/span>, <span class=\"hljs-number\">480<\/span>);\n    ...\n    ...\n  }\n<\/code><\/pre>\n<pre><code class=\"lang-html\"><span class=\"hljs-section\">&lt;\/script&gt;<\/span>\n<\/code><\/pre>\n<p>Next, add a listener to capture predefined messages\/events from the RemoteViz service. Only events related to connection and network calibration states are captured by the service handler.  For a complete list of predefined messages, please see <a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/class_remote_viz_render_area.html#a78b38a6904036817a708fdc0c5c02921\">ServiceHandler<\/a>.<\/p>\n<p>The following code sample defines a handler function <code>service()<\/code>. We use this function to display or hide the network calibration graphic.<\/p>\n<pre><code class=\"lang-js\">  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">init<\/span>(<span class=\"hljs-params\"><\/span>)\n  <\/span>{\n    ...\n    <span class=\"hljs-comment\">\/\/ Listen start and stop bandwidth calibration events to display or hide the loader.<\/span>\n    theRenderArea.addServiceListener(service);\n    ...\n  }\n  <span class=\"hljs-comment\">\/\/ This function is called when a predefined notification is received from the service<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">service<\/span>(<span class=\"hljs-params\">event<\/span>)\n  <\/span>{\n    <span class=\"hljs-keyword\">var<\/span> message = event[<span class=\"hljs-number\">0<\/span>];\n    <span class=\"hljs-keyword\">switch<\/span>(message) {\n      <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"startNetworkCalibration\"<\/span>:\n      <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"pendingNetworkCalibration\"<\/span>:\n        <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"loaderGroup\"<\/span>).style.display = <span class=\"hljs-string\">\"inline\"<\/span>;\n        <span class=\"hljs-keyword\">break<\/span>;\n      <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"finishedNetworkCalibration\"<\/span>:\n        <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">\"loaderGroup\"<\/span>).style.display = <span class=\"hljs-string\">\"none\"<\/span>;\n        <span class=\"hljs-keyword\">break<\/span>;\n    }\n  }\n<\/code><\/pre>\n<h3 id=\"connect-renderarea-to-service\">Connect RenderArea to Service<\/h3>\n<p>Finally, to connect the client application to the RemoteViz service, we will use the <code>connectTo()<\/code> method and provide the service URL.<\/p>\n<pre><code class=\"lang-js\">  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">()<\/span>\n  <\/span>{\n    <span class=\"hljs-comment\">\/\/ Initialize the renderArea on the server<\/span>\n    theRenderArea = <span class=\"hljs-keyword\">new<\/span> RemoteVizRenderArea(<span class=\"hljs-string\">\"TheDiv\"<\/span>, <span class=\"hljs-number\">640<\/span>, <span class=\"hljs-number\">480<\/span>);\n    ...\n    <span class=\"hljs-comment\">\/\/ Connects to the service. IP address and the port refer to those of the service<\/span>\n    theRenderArea.connectTo(<span class=\"hljs-string\">\"ws:\/\/127.0.0.1:8080\/TheCone\"<\/span>);\n  }\n<\/code><\/pre>\n<p>The service URL string must contain the address and port of the RemoteViz service. The address can be a domain name or an IP address. The URL may contain a render area identifier and a query string composed of a series of field-value pairs.<\/p>\n<p>The following URL, for example, includes a query string requesting specific height and width.<\/p>\n<pre><code class=\"lang-js\">  theRenderArea.connectTo(<span class=\"hljs-string\">\"ws:\/\/127.0.0.1:8080\/TheCone?requestedWidth=100&amp;requestedHeight=100\"<\/span>)<span class=\"hljs-comment\">;<\/span>\n<\/code><\/pre>\n<p>\"TheCone\" in the above URL specifies the identifier (Id) of the requested render area. If no render area with specified Id exists, the RemoteViz service will create one and <code>ServiceListener::onPendingCreateRenderArea<\/code> event will be triggered on the server-side. If the render area with specified Id already exists, then <code>ServiceListener::onPendingShareRenderArea<\/code> event will be triggered.<\/p>\n<p><\/p>\n<h2 id=\"build-and-run-the-example\">Build and Run the Example<\/h2>\n<p>Now that we have covered the basics of programming with the RemoteViz API and implemented a minimal rendering service as well as a client application, let's run a RemoteViz example locally.<\/p>\n<h3 id=\"build-the-demo\">Build the Demo<\/h3>\n<p>To build the demo example using Visual Studio on Windows, you will need to ...<\/p>\n<ul>\n<li>Open the solution (.sln) file located in <em>\\$(OIVHOME)\/examples\/source\/RemoteViz\/HelloCone\/HelloConeRenderingService\/<\/em><\/li>\n<li>Select either Debug or Release configuration and build the <strong>RemoteVizHelloConeRenderingService<\/strong> project.<\/li>\n<\/ul>\n<p>The service executable is created in the default output directory <em>\\$(OIVHOME)\/examples\/bin\/arch-Windows-x86_64-msvc15-\\$(Configuration)\/RemoteViz\/<\/em>.<\/p>\n<h3 id=\"run-the-demo\">Run the Demo<\/h3>\n<p>To start the rendering service, navigate to the output directory (using either File Explorer or PowerShell) and run <strong>RemoteVizHelloConeRenderingService.exe<\/strong>. If successful, you will see a message saying \"... Rendering Service is running\".<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManJava\/com\/openinventor\/remoteviz\/rendering\/doc-files\/RViz_HelloCone_Service.png\" alt=\"\"><\/p>\n<p>Notice that the service is using the IP address 127.0.0.1 and port 8080 as defined in <strong>main( )<\/strong> function.<\/p>\n<p><\/p>\n<p>Once the service is running, let's navigate to <em>\\Clients\\HTML5\\<\/em> and open <strong>index.html<\/strong> in your browser. If the render area is successfully created, you should first see the \"Network Calibration\" message and then an image of a gray cone on the render area.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManJava\/com\/openinventor\/remoteviz\/rendering\/doc-files\/RViz_HelloCone_Viewer.png\" alt=\"\"><\/p>\n<p>The rendering service renders the scene graph and sends the rendered image to the client. In this example, the scene graph consists of only <strong>SoCone<\/strong> and <strong>SoGradientBackground<\/strong> nodes.<\/p>\n<p><\/p>\n<h2 id=\"docker_demo\">Demo in Docker<\/h2>\n<p>Docker containers wrap a piece of software in a complete filesystem that contains everything needed to run: code, runtime, system tools, system libraries - anything that can be installed on a server. This guarantees that the software will always run the same, regardless of its environment.<\/p>\n<p>Containers running on a single machine share the same operating system kernel; they start instantly and use less RAM. Images are constructed from layered filesystems and share common files, making disk usage and image downloads much more efficient.<\/p>\n<p>Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build, users can create an automated build that executes several command-line instructions in succession.<\/p>\n<p>We provide the Dockerfile necessary to build a container image that can run a demo.<br \/>\nA linux distribution is required and NVIDIA graphics card too.<\/p>\n<p>To run a RemoteViz demo, an OpenGL context is required. To get an OpenGL context in a Docker container, you have to install the Nvidia container toolkit on the host machine and use the Open Inventor headless package.<\/p>\n<p>Run the RemoteVizHelloCone demo in a Docker container<br \/>\nBe sure you have copied your password.dat in <em>OIVHOME\/License<\/em> folder and set the Open Inventor environment variables: <em>OIVHOME<\/em>, <em>OIVARCH<\/em>.<\/p>\n<p>Go to the Docker folder in <em>OIVHOME\/examples\/source\/RemoteViz\/Docker<\/em>.<br \/>\nOpen the ReadMe.txt file and follow the instructions.<\/p>\n<p><\/p>\n<h2 id=\"user-interaction\">User Interaction<\/h2>\n<p>Usually, the client applications are highly interactive, and they must provide some way for the user to interact with the scene. We use a <code>SceneExaminer<\/code> node as the root of the scene graph. The <code>SceneExaminer<\/code> is an extension of the <code>SceneInteractor<\/code> node that provides camera and headlight manipulations like panning, zooming, and orbiting.<\/p>\n<p>SceneExaminer is either in NAVIGATION mode (the default, similar to viewing mode) or SELECTION mode. The user must press the ESC key to toggle between the two interaction modes. In NAVIGATION mode, user input events are automatically handled to modify the camera, and these events are not sent to the application scene graph. In SELECTION mode, all events are sent to the application scene graph, and the user input events can be captured and processed appropriately by the application on the server-side using <code>SoEventCallback<\/code> node.<\/p>\n<pre><code class=\"lang-cpp\">{\n  <span class=\"hljs-comment\">\/\/ Event callback node to handle input events<\/span>\n  SoEventCallback* eventCB = <span class=\"hljs-literal\">new<\/span> SoEventCallback;\n  eventCB-&gt;addEventCallback(SoMouseButtonEvent<span class=\"hljs-type\">::getClassTypeId<\/span>(), mouseEventCB);\n  <span class=\"hljs-params\">...<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Mouse button events handler<\/span>\n<span class=\"hljs-literal\">void<\/span> mouseEventCB(<span class=\"hljs-literal\">void<\/span>* userData, SoEventCallback* node)\n{\n  <span class=\"hljs-comment\">\/\/ Application implementation<\/span>\n  <span class=\"hljs-params\">...<\/span>\n}\n<\/code><\/pre>\n<p>You may also capture the user input events in the <code>RenderAreaListener<\/code> class, before sending (or not sending) them to the scene graph. The three types of events that can be intercepted are Mouse, Keyboard, and Touch events. Typically, you will overload the listener methods of the <code>RenderAreaListener<\/code> to intercept these events.<\/p>\n<p>The following code sample implements a listener that is triggered when a MouseUp event is received from the client. For a complete set of listener methods that you may override, please see <code>RenderAreaListener<\/code>.<\/p>\n<pre><code class=\"lang-cpp\"><span class=\"hljs-keyword\">bool<\/span> RenderAreaListener::onMouseDown(std::shared_ptr&lt;RemoteViz::Rendering::RenderArea&gt; renderArea,\nstd::shared_ptr&lt;RemoteViz::Rendering::Connection&gt; sender,<span class=\"hljs-keyword\">int<\/span> x,<span class=\"hljs-keyword\">int<\/span> y, SoMouseButtonEvent::Button button)\n{\n  <span class=\"hljs-comment\">\/\/ Application implementation<\/span>\n  ...\n  <span class=\"hljs-comment\">\/\/ Return true to process the event; forward it to be handled by the scene graph<\/span>\n  <span class=\"hljs-comment\">\/\/ Return false to ignore the event.<\/span>\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>;\n}\n<\/code><\/pre>\n<p><\/p>\n<h2 id=\"client-service-communication\">Client-Service Communication<\/h2>\n<p>The RemoteViz service and the client application can communicate by exchanging application specified messages, which can either be a binary encoded or text messages. The support of binary encoded messages is particularly useful when using tools like <a href=\"https:\/\/developers.google.com\/protocol-buffers\">Protocol Buffers<\/a> or <a href=\"https:\/\/thrift.apache.org\/\">Apache Thrift<\/a>.<\/p>\n<p>To send a message from the client application to the RemoteViz service, use the <code>RemoteVizRenderArea::sendMessage()<\/code> method. On the server-side, <code>RenderAreaListener::onReceivedMessage()<\/code> event will be triggered if a message from the client is received. Inside this event handler, you will parse the messages and handle them accordingly.<\/p>\n<p>The following code sample defines a javascript function that sends a text message to the RemoteViz service to switch the color of a rendered geometry, for example.<\/p>\n<pre><code class=\"lang-js\">  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">switchColor<\/span><span class=\"hljs-params\">()<\/span>\n  <\/span>{\n    <span class=\"hljs-keyword\">if<\/span> (isColorEnabled)\n    {\n      theRenderArea.sendMessage(<span class=\"hljs-string\">\"color off\"<\/span>);\n      isColorEnabled = <span class=\"hljs-literal\">false<\/span>;\n    }\n    <span class=\"hljs-keyword\">else<\/span>\n    {\n      theRenderArea.sendMessage(<span class=\"hljs-string\">\"color on\"<\/span>);\n      isColorEnabled = <span class=\"hljs-literal\">true<\/span>;\n    }\n  }\n<\/code><\/pre>\n<p>Likewise, to send a message from the service to the client applications, we will use the <code>sendMessage()<\/code> method of <code>RenderArea<\/code> on the server-side. To capture application specified messages from the service, you need to add a message listener on the client-side.<\/p>\n<p>The following code sample adds a listener on messages received from the service.<\/p>\n<pre><code class=\"lang-js\">  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">init<\/span><span class=\"hljs-params\">()<\/span>\n  <\/span>{\n    ...\n    <span class=\"hljs-comment\">\/\/ Add a listener on the received message event.<\/span>\n    theRenderArea.addMessageListener(messageReceive);\n    ...\n  }\n  <span class=\"hljs-comment\">\/\/ This function is called when messages are received from the service<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">messageReceive<\/span><span class=\"hljs-params\">(message)<\/span>\n  <\/span>{\n    <span class=\"hljs-comment\">\/\/ Application implementation<\/span>\n    ...\n  }\n<\/code><\/pre>\n<p><\/p>\n<h2 id=\"encoding-frames-for-streaming\">Encoding Frames for Streaming<\/h2>\n<p>When a client connects to the RemoteViz service, by default, RemoteViz will use PNG encoding for still frames and JPEG for interactive frames. In the latter case, it may be acceptable to use (for example) lossy JPEG encoding to maximize performance, then switch to loss-less PNG encoding for the \"still\" frame when the user interaction is finished.<\/p>\n<p>Video encoding (H.264 or VP9) generally provides better performance (frames per second) than image encoding (JPEG and PNG). However, this depends on many factors. Video encoded frames usually require less bandwidth, but the encoding of each frame can take more time.<\/p>\n<p>To define what encoder to use for still and interactive frames, you need to access a <code>FrameEncoders<\/code> object which is passed to the <code>RenderAreaListener<\/code> methods <code>onOpenedConnection()<\/code>, <code>onInitializedConnection()<\/code> and <code>onRefusedEncoder()<\/code>. In the <code>onOpenedConnection()<\/code> method, use the <code>FrameEncoders<\/code> object to request non-default encoders.<\/p>\n<pre><code class=\"lang-cpp\">void <span class=\"hljs-symbol\">RenderAreaListener:<\/span><span class=\"hljs-symbol\">:onOpenedConnection<\/span>(<span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:RenderArea&gt;<\/span>, <span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Connection&gt;<\/span>, <span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders&gt;<\/span> frameEncoders)\n{\n  <span class=\"hljs-regexp\">\/\/<\/span> Set H264 NVENC as frame encoder <span class=\"hljs-keyword\">for<\/span> this connection. It requires an NVIDIA GPU Kepler <span class=\"hljs-keyword\">or<\/span> higher <span class=\"hljs-keyword\">and<\/span> CUDA Toolkit <span class=\"hljs-number\">10.1<\/span> <span class=\"hljs-keyword\">or<\/span> higher.\n  frameEncoders-&gt;setInteractiveEncoder( <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:H264_NVENC<\/span> );\n  frameEncoders-&gt;setStillEncoder( <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:H264_NVENC<\/span> );\n}\n<\/code><\/pre>\n<p>You may use the <code>ConnectionSettings::isSupportedEncoders()<\/code> method to query if the proposed encoders are available. Note that, currently, it is not possible to mix video and image encoding. In other words, the interactive and still encoders must be both video encoders (H264, VP9) or both image encoders (JPEG, PNG).<\/p>\n<p>The <code>onRefusedEncoder()<\/code> method will be called if one of the encoders is not supported in the current environment or the combination of encoders is not supported. In that case, use the <code>FrameEncoders<\/code> object to determine which encoder is invalid (see <code>getStillEncoderStatus()<\/code> and <code>getInteractiveEncoderStatus()<\/code>), then set new encoders. <code>onRefusedEncoder()<\/code> will be called again if the requested encoders are not valid.<\/p>\n<pre><code class=\"lang-cpp\">void <span class=\"hljs-symbol\">RenderAreaListener:<\/span><span class=\"hljs-symbol\">:onRefusedEncoder<\/span>(<span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:RenderArea&gt;<\/span>, <span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Connection&gt;<\/span>, <span class=\"hljs-symbol\">std:<\/span><span class=\"hljs-symbol\">:shared_ptr&lt;RemoteViz<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders&gt;<\/span> frameEncoders)\n{\n  <span class=\"hljs-regexp\">\/\/<\/span> At least one of the frame encoders cannot be initialized, set others encoders.\n  if ( frameEncoders-&gt;getInteractiveEncoder() == <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:H264_NVENC<\/span> &amp;&amp;\n       frameEncoders-&gt;getStillEncoder() == <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:H264_NVENC<\/span> )\n  {\n    frameEncoders-&gt;setInteractiveEncoder( <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:JPEG<\/span> );\n    frameEncoders-&gt;setStillEncoder( <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:PNG<\/span> );\n  }\n  else if ( frameEncoders-&gt;getInteractiveEncoder() == <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:JPEG<\/span> &amp;&amp;\n            frameEncoders-&gt;getStillEncoder() == <span class=\"hljs-symbol\">RemoteViz:<\/span><span class=\"hljs-symbol\">:Rendering<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:FrameEncoders<\/span><span class=\"hljs-symbol\">:<\/span><span class=\"hljs-symbol\">:PNG<\/span> )\n  {\n    ...\n  }\n  ...\n}\n<\/code><\/pre>\n<p>Note that, currently, when using video encoding, the interactive and still encoders must be the same, e.g. both H264_NVENC.<\/p>\n<p><\/p>\n<h2 id=\"secure-connection\">Secure Connection<\/h2>\n<p>RemoteViz uses the WebSocket protocol to enable communication between the client and the service. In a production environment, it is highly recommended to use secure WebSocket. Using a secure WebSocket connection improves confidentiality and reliability because the connection is encrypted with Transport Layer Security (TLS), which reduces the risk of a variety of attacks such as man-in-the-middle tampering with your data.<\/p>\n<p>The WebSocket Secure (WSS) protocol is to WebSocket protocol (WS) what HyperText Transfer Protocol Secure (HTTPS) is to HyperText Transfer Protocol (HTTP). Like HTTPS, WSS requires a TLS certificate issued to a fully qualified domain name such as www.openinventor.com.<\/p>\n<p><strong>Service:<\/strong><\/p>\n<p>To enable a secure connection between the client and the rendering service, you need to use the <code>enableSecureConnection()<\/code> method of <code>RemoteViz::Rendering::ServiceSettings<\/code> on the server-side. This method takes two arguments: the path to a file containing the public TLS certificate and the path to a file containing the private key.<\/p>\n<pre><code class=\"lang-cpp\">int main()\n{\n  ...\n  <span class=\"hljs-comment\">\/\/ Enable secure connections<\/span>\n  settings-&gt;enableSecureConnection(<span class=\"hljs-string\">\"certificate.crt\"<\/span>, <span class=\"hljs-string\">\"private.key\"<\/span>);\n  ...\n}\n<\/code><\/pre>\n<p>If the private key is protected by a passphrase, you can provide the passphrase using the <code>ServiceListener::onRequestedPrivateKeyPassphrase()<\/code> listener.<\/p>\n<p><strong>Client:<\/strong><\/p>\n<p>To connect the client application to the RemoteViz service using a secure connection, you will use the <code>connectTo()<\/code> method with the domain name associated to your TLS certificate and the prefix \"wss:\/\/\" instead of \"ws:\/\/\".<\/p>\n<pre><code class=\"lang-js\">  <span class=\"hljs-comment\">\/\/ Connects to the service by using the secure WebSocket protocol (wss:\/\/)<\/span>\n  <span class=\"hljs-selector-tag\">theRenderArea<\/span><span class=\"hljs-selector-class\">.connectTo<\/span>(<span class=\"hljs-string\">\"wss:\/\/www.yourdomain.com\/TheCone\"<\/span>);\n<\/code><\/pre>\n<p>The last step is to setup a web server like Apache or Nginx to serve the web page <strong>index.html<\/strong> over your domain name and an HTTPS connection using your TLS certificate. The web server has also to be configured as a reverse proxy to redirect WSS incoming connections to the rendering service.<br \/>\nFurther information about <a href=\"https:\/\/developer.openinventor.com\/refmans\/latest\/RefManCpp\/group___reverse_proxy.html\">how to setup web server as reverse proxy<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Table of Contents What is RemoteViz? Basic Concepts Getting Started with RemoteViz API (HelloCone) Main Service Settings Service Listener RenderArea&hellip;<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":63,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-149","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/149","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/comments?post=149"}],"version-history":[{"count":5,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/149\/revisions"}],"predecessor-version":[{"id":460,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/149\/revisions\/460"}],"up":[{"embeddable":true,"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/pages\/63"}],"wp:attachment":[{"href":"https:\/\/developer.openinventor.com\/index.php\/wp-json\/wp\/v2\/media?parent=149"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}