{"id":326780,"date":"2021-07-21T09:01:09","date_gmt":"2021-07-21T09:01:09","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=326780"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=326780","title":{"rendered":"Practical uses of WebRTC Canvas streaming"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d33\/8f4\/126\/d338f4126185337e64ec2673c5d5d6c9.jpg\" width=\"1023\" height=\"683\"><figcaption><\/figcaption><\/figure>\n<p>In this article we will once again return to the tired topic of webinars and webinar hosting tools.  And no, we&#8217;re not about to code a whole new system for webinar hosting \u2013 there are already plenty of those. Instead, we will talk about connecting drawing software to the webinar, so that you could manually draw and broadcast the process.  <\/p>\n<p>At first glance, the solutions are readily available. All you seem to need is to add a canvas to the page (HTML 5 element &#171;Canvas&#187;), and start drawing. Well, adding it is easy enough, but the question is how to transmit the drawing process into WebRTC.  <\/p>\n<p>So, let us take a closer look at what WebRTC streaming from canvas (or Canvas streaming) is, and what pitfalls you might want to look out for.  <\/p>\n<h3>What is Canvas?<\/h3>\n<p>Let&#8217;s see what Wikipedia has to say about it. <\/p>\n<p><strong>Canvas<\/strong> is an HTML5 element, dedicated to creating raster-based 2D images using scripts, usually based on JavaScript. It is generally used for rendering graphs and figures for articles.<\/p>\n<p>The pros of using Canvas are as follows:<\/p>\n<ul>\n<li>\n<p>it supports hardware acceleration;<\/p>\n<\/li>\n<li>\n<p>it is capable of manipulating any and each pixel separately;<\/p>\n<\/li>\n<\/ul>\n<p>The cons are as follows: <\/p>\n<ul>\n<li>\n<p>it overloads the processor and RAM;<\/p>\n<\/li>\n<li>\n<p>its garbage collector is limited and provides no option to clear memory;<\/p>\n<\/li>\n<li>\n<p>it requires manual handling of object events;<\/p>\n<\/li>\n<li>\n<p>it doesn&#8217;t run well at high resolutions;<\/p>\n<\/li>\n<li>\n<p>it requires each element to be rendered separately.<\/p>\n<\/li>\n<\/ul>\n<p>The cons seem to outweigh the pros. However, despite that, Canvas does have a great advantage: it makes it possible to stream slide shows and draw over them during the webinar.<\/p>\n<h3>Quirks of stream capture<\/h3>\n<p>Upon capturing a stream from a Canvas element the browser considers it black. That means a black text will merge with the background upon capture and the stream will look like a black screen. The same thing can happen when rendering a .png image with transparent background and black lines. <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/742\/bd9\/428\/742bd9428a63e59961840d28450f5a3c.jpg\" width=\"800\" height=\"560\"><figcaption><\/figcaption><\/figure>\n<p>Also note, that the stream cannot consist of one frame. That means once you&#8217;ve put a static image on the Canvas, the stream won&#8217;t form because there is only one frame in it.  The solution is to render the static images cyclically. <\/p>\n<p>For instance, the following function continually refreshes a Canvas-rendered image at 30 fps: <\/p>\n<pre><code class=\"bash\">function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     }<\/code><\/pre>\n<p>Another point has to do with a browser security measure known as Same-origin Policy.  <\/p>\n<p>The image that is to be drawn on the Canvas must be hosted locally on the server or on a server within the same domain.  If the image to be rendered is downloaded from the Internet, the browser will signal exception. <\/p>\n<p>This issue can be resolved using the Cross-Origin Resource Sharing (CORS) technology, but that solution is beyond the scope of this article. <\/p>\n<h3>How-to guides for readying Canvas<\/h3>\n<p>As is tradition, we are going to provide how-to guides. Of those, there are two:<\/p>\n<ol>\n<li>\n<p>Text rendering via Canvas and WebRTC-flavored streaming<\/p>\n<\/li>\n<li>\n<p>Image rendering via Canvas and consequent WebRTC streaming<\/p>\n<\/li>\n<\/ol>\n<p>HTML-related parts are going to be virtually identical. Create a HTML file named canvas-streaming-min.html<\/p>\n<p>In the page&#8217;s head, write down the access to scripts: <\/p>\n<pre><code class=\"bash\">&lt;script type=\"text\/javascript\" src=\"..\/..\/..\/..\/flashphoner.js\"&gt;&lt;\/script&gt; &lt;script type=\"text\/javascript\" src=\"canvas-streaming-min.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n<p>In the body, deploy the HTML 5 Canvas element and div element for streaming. Set the Flashphoner API to initialize upon loading of the HTML page: <\/p>\n<pre><code class=\"bash\">&lt;body onload=\"init_page()\"&gt;         &lt;canvas width=\"480\" height=\"320\" id=\"canvas\"&gt;&lt;\/canvas&gt;         &lt;div id=\"localDisplay\" hidden&gt;&lt;\/div&gt;         &lt;br&gt;      &lt;\/body&gt;<\/code><\/pre>\n<p>For a simple example, let&#8217;s add an image to be rendered into the HTML file for the canvas. The image file is stored in the same server folder as the example files: <\/p>\n<pre><code class=\"bash\">&lt;img src=\"2307589.png\" alt=\"logo\" id=\"myimage\" &gt;<\/code><\/pre>\n<p>Here&#8217;s the complete code of an HTML page for a canvas with text:<\/p>\n<pre><code class=\"bash\">&lt;!DOCTYPE html&gt; &lt;html lang=\"en\"&gt; &lt;head&gt;         &lt;script type=\"text\/javascript\" src=\"..\/..\/..\/..\/flashphoner.js\"&gt;&lt;\/script&gt;         &lt;script type=\"text\/javascript\" src=\"canvas-streaming-min.js\"&gt;&lt;\/script&gt; &lt;\/head&gt; &lt;body onload=\"init_page()\"&gt;         &lt;canvas width=\"480\" height=\"320\" id=\"canvas\"&gt;&lt;\/canvas&gt;         &lt;div id=\"localDisplay\" hidden&gt;&lt;\/div&gt;     &lt;\/body&gt; &lt;\/html&gt;<\/code><\/pre>\n<p>For a canvas with images: <\/p>\n<pre><code class=\"bash\">&lt;!DOCTYPE html&gt; &lt;html lang=\"en\"&gt; &lt;head&gt;         &lt;script type=\"text\/javascript\" src=\"..\/..\/..\/..\/flashphoner.js\"&gt;&lt;\/script&gt;         &lt;script type=\"text\/javascript\" src=\"canvas-streaming-min.js\"&gt;&lt;\/script&gt; &lt;\/head&gt; &lt;body onload=\"init_page()\"&gt;         &lt;canvas width=\"480\" height=\"320\" id=\"canvas\"&gt;&lt;\/canvas&gt;         &lt;div id=\"localDisplay\" hidden&gt;&lt;\/div&gt;         &lt;br&gt;         &lt;img src=\"2307589.png\" alt=\"logo\" id=\"myimage\" &gt;       &lt;\/body&gt; &lt;\/html&gt;<\/code><\/pre>\n<p>Now, let us move to JS scripts. <\/p>\n<h4>Text rendering via Canvas with WebRTC spin<\/h4>\n<p>Determine the constants and the global variables:<\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;<\/code><\/pre>\n<p>Write &#171;Hello World&#187; on the canvas. Draw a yellow rectangle sized 320px by 176px, font \u2013 Arial, text size \u2013 30px, color &#8212; blue. Use the &#171;loop()&#187; function (mentioned above) to keep redrawing the canvas at 30 fps: <\/p>\n<pre><code class=\"bash\">function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     canvasContext.font = \"30px Arial\";     canvasContext.fillStyle = \"yellow\";     canvasContext.fillRect(0,0,320,176);     canvasContext.strokeStyle = 'black';     canvasContext.fillStyle = \"blue\";     (function loop(){         canvasContext.fillText(\"Hello World!\", 10, 50);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }<\/code><\/pre>\n<p>Connect to a WCS server via WebSocket and publish the stream of the canvas:<\/p>\n<pre><code class=\"bash\">\/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<p>Here&#8217;s the full JS script code for rendering a canvas with text and capturing it for the stream: <\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;   \/\/init api function init_page() {     Flashphoner.init({});     localDisplay = document.getElementById(\"localDisplay\");     canvas = document.getElementById(\"canvas\");     createCanvas();     connect();     }  \/\/create Canvas function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     canvasContext.font = \"30px Arial\";     canvasContext.fillStyle = \"yellow\";     canvasContext.fillRect(0,0,320,176);     canvasContext.strokeStyle = 'black';     canvasContext.fillStyle = \"blue\";     (function loop(){         canvasContext.fillText(\"Hello World!\", 10, 50);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }  \/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<h4>Hello, world!<\/h4>\n<ol>\n<li>\n<p>Open a created Webpage. In this example we are not using buttons, so the canvas is rendered immediately upon page initialization:  <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/021\/8c9\/a61\/0218c9a6135c0986c2acfbbdea11dc45.PNG\" width=\"683\" height=\"292\"><figcaption><\/figcaption><\/figure>\n<\/li>\n<li>\n<p>In another browser tab, open the demo-example &#171;Player&#187; on you WCS server. Specify the name of the stream that is capturing the canvas and start the stream. Here&#8217;s the image of the original canvas and how the stream should look in the player: <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/baa\/2a4\/46f\/baa2a446ff3b8c441f6fdafe504eb1e4.PNG\" width=\"1245\" height=\"872\"><figcaption><\/figcaption><\/figure>\n<\/li>\n<\/ol>\n<h4>Rendering images via Canvas and streaming via WebRTC<\/h4>\n<p>Determine the constants and the global variables: <\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;<\/code><\/pre>\n<p>Upon receiving the image from the webpage, start cyclically rendering it on the canvas at 30 fps:<\/p>\n<pre><code class=\"bash\">function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     var img = document.getElementById (\"myimage\");     (function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }<\/code><\/pre>\n<p>Connect to a WCS server via WebSocket and publish the stream of the canvas:<\/p>\n<pre><code class=\"bash\">\/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<p>Here&#8217;s a full JS script code for rendering a canvas with images and capturing it for the stream:<\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;   \/\/init api function init_page() {     Flashphoner.init({});     localDisplay = document.getElementById(\"localDisplay\");     canvas = document.getElementById(\"canvas\");     createCanvas();     connect();     }  \/\/create Canvas function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     var img = document.getElementById (\"myimage\");     (function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }  \/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<h4>Hello, world! Part two<\/h4>\n<ol>\n<li>\n<p>Open the created webpage. The canvas will be rendered immediately upon page initialization. The image (bottom of the page) is displayed on the canvas (top of the page):<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7cb\/ff4\/743\/7cbff4743df55e4463f69c6b6b31d6ea.PNG\" width=\"683\" height=\"646\"><figcaption><\/figcaption><\/figure>\n<\/li>\n<li>\n<p>Open the demo-example &#171;Player&#187; on your WCS server. Specify the name of the stream that is capturing the canvas and start the stream. Here&#8217;s the image of the original canvas and how the stream should look in the player:  <\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/09d\/9ab\/a66\/09d9aba6677a46ad3b509691a205dac3.PNG\" width=\"1257\" height=\"833\"><figcaption><\/figcaption><\/figure>\n<h3>Conclusion<\/h3>\n<p>With this article we didn&#8217;t aim to break new ground. Our tech support has received questions on canvas streaming and so we undertook to provide you with some basic code that might help you use the canvas functionality in your project. <\/p>\n<p>Good streaming to you!<\/p>\n<h3>Links<\/h3>\n<p><a href=\"https:\/\/demo.flashphoner.com\/admin\/login.html\">Demo<\/a><\/p>\n<p><a href=\"https:\/\/flashphoner.com\/amazon-ec2-support-in-web-call-server\">WCS on Amazon EC2<\/a><\/p>\n<p><a href=\"https:\/\/flashphoner.com\/web-call-server-on-digital-ocean-marketplace\">WCS on DigitalOcean<\/a><\/p>\n<p><a href=\"https:\/\/flashphoner.com\/support-web-call-server-in-docker\">WCS in Docker<\/a><\/p>\n<p><a href=\"https:\/\/docs.flashphoner.com\/display\/WCS52EN\/Quick+deployment+and+testing+of+the+server\">Quick deployment and testing of the server<\/a><\/p>\n<p><a href=\"https:\/\/docs.flashphoner.com\/display\/WCS52EN\/From+an+HTML5+Canvas+element+(whiteboard)+in+a+browser+via+WebRTC\">Stream Capturing and publish from an HTML5 Canvas element (whiteboard) <\/a><\/p>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/flashphoner\/blog\/568682\/\"> https:\/\/habr.com\/ru\/company\/flashphoner\/blog\/568682\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>In this article we will once again return to the tired topic of webinars and webinar hosting tools.  And no, we&#8217;re not about to code a whole new system for webinar hosting \u2013 there are already plenty of those. Instead, we will talk about connecting drawing software to the webinar, so that you could manually draw and broadcast the process.  <\/p>\n<p>At first glance, the solutions are readily available. All you seem to need is to add a canvas to the page (HTML 5 element &#171;Canvas&#187;), and start drawing. Well, adding it is easy enough, but the question is how to transmit the drawing process into WebRTC.  <\/p>\n<p>So, let us take a closer look at what WebRTC streaming from canvas (or Canvas streaming) is, and what pitfalls you might want to look out for.  <\/p>\n<h3>What is Canvas?<\/h3>\n<p>Let&#8217;s see what Wikipedia has to say about it. <\/p>\n<p><strong>Canvas<\/strong> is an HTML5 element, dedicated to creating raster-based 2D images using scripts, usually based on JavaScript. It is generally used for rendering graphs and figures for articles.<\/p>\n<p>The pros of using Canvas are as follows:<\/p>\n<ul>\n<li>\n<p>it supports hardware acceleration;<\/p>\n<\/li>\n<li>\n<p>it is capable of manipulating any and each pixel separately;<\/p>\n<\/li>\n<\/ul>\n<p>The cons are as follows: <\/p>\n<ul>\n<li>\n<p>it overloads the processor and RAM;<\/p>\n<\/li>\n<li>\n<p>its garbage collector is limited and provides no option to clear memory;<\/p>\n<\/li>\n<li>\n<p>it requires manual handling of object events;<\/p>\n<\/li>\n<li>\n<p>it doesn&#8217;t run well at high resolutions;<\/p>\n<\/li>\n<li>\n<p>it requires each element to be rendered separately.<\/p>\n<\/li>\n<\/ul>\n<p>The cons seem to outweigh the pros. However, despite that, Canvas does have a great advantage: it makes it possible to stream slide shows and draw over them during the webinar.<\/p>\n<h3>Quirks of stream capture<\/h3>\n<p>Upon capturing a stream from a Canvas element the browser considers it black. That means a black text will merge with the background upon capture and the stream will look like a black screen. The same thing can happen when rendering a .png image with transparent background and black lines. <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>Also note, that the stream cannot consist of one frame. That means once you&#8217;ve put a static image on the Canvas, the stream won&#8217;t form because there is only one frame in it.  The solution is to render the static images cyclically. <\/p>\n<p>For instance, the following function continually refreshes a Canvas-rendered image at 30 fps: <\/p>\n<pre><code class=\"bash\">function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     }<\/code><\/pre>\n<p>Another point has to do with a browser security measure known as Same-origin Policy.  <\/p>\n<p>The image that is to be drawn on the Canvas must be hosted locally on the server or on a server within the same domain.  If the image to be rendered is downloaded from the Internet, the browser will signal exception. <\/p>\n<p>This issue can be resolved using the Cross-Origin Resource Sharing (CORS) technology, but that solution is beyond the scope of this article. <\/p>\n<h3>How-to guides for readying Canvas<\/h3>\n<p>As is tradition, we are going to provide how-to guides. Of those, there are two:<\/p>\n<ol>\n<li>\n<p>Text rendering via Canvas and WebRTC-flavored streaming<\/p>\n<\/li>\n<li>\n<p>Image rendering via Canvas and consequent WebRTC streaming<\/p>\n<\/li>\n<\/ol>\n<p>HTML-related parts are going to be virtually identical. Create a HTML file named canvas-streaming-min.html<\/p>\n<p>In the page&#8217;s head, write down the access to scripts: <\/p>\n<pre><code class=\"bash\">&lt;script type=\"text\/javascript\" src=\"..\/..\/..\/..\/flashphoner.js\"&gt;&lt;\/script&gt; &lt;script type=\"text\/javascript\" src=\"canvas-streaming-min.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n<p>In the body, deploy the HTML 5 Canvas element and div element for streaming. Set the Flashphoner API to initialize upon loading of the HTML page: <\/p>\n<pre><code class=\"bash\">&lt;body onload=\"init_page()\"&gt;         &lt;canvas width=\"480\" height=\"320\" id=\"canvas\"&gt;&lt;\/canvas&gt;         &lt;div id=\"localDisplay\" hidden&gt;&lt;\/div&gt;         &lt;br&gt;      &lt;\/body&gt;<\/code><\/pre>\n<p>For a simple example, let&#8217;s add an image to be rendered into the HTML file for the canvas. The image file is stored in the same server folder as the example files: <\/p>\n<pre><code class=\"bash\">&lt;img src=\"2307589.png\" alt=\"logo\" id=\"myimage\" &gt;<\/code><\/pre>\n<p>Here&#8217;s the complete code of an HTML page for a canvas with text:<\/p>\n<pre><code class=\"bash\">&lt;!DOCTYPE html&gt; &lt;html lang=\"en\"&gt; &lt;head&gt;         &lt;script type=\"text\/javascript\" src=\"..\/..\/..\/..\/flashphoner.js\"&gt;&lt;\/script&gt;         &lt;script type=\"text\/javascript\" src=\"canvas-streaming-min.js\"&gt;&lt;\/script&gt; &lt;\/head&gt; &lt;body onload=\"init_page()\"&gt;         &lt;canvas width=\"480\" height=\"320\" id=\"canvas\"&gt;&lt;\/canvas&gt;         &lt;div id=\"localDisplay\" hidden&gt;&lt;\/div&gt;     &lt;\/body&gt; &lt;\/html&gt;<\/code><\/pre>\n<p>For a canvas with images: <\/p>\n<pre><code class=\"bash\">&lt;!DOCTYPE html&gt; &lt;html lang=\"en\"&gt; &lt;head&gt;         &lt;script type=\"text\/javascript\" src=\"..\/..\/..\/..\/flashphoner.js\"&gt;&lt;\/script&gt;         &lt;script type=\"text\/javascript\" src=\"canvas-streaming-min.js\"&gt;&lt;\/script&gt; &lt;\/head&gt; &lt;body onload=\"init_page()\"&gt;         &lt;canvas width=\"480\" height=\"320\" id=\"canvas\"&gt;&lt;\/canvas&gt;         &lt;div id=\"localDisplay\" hidden&gt;&lt;\/div&gt;         &lt;br&gt;         &lt;img src=\"2307589.png\" alt=\"logo\" id=\"myimage\" &gt;       &lt;\/body&gt; &lt;\/html&gt;<\/code><\/pre>\n<p>Now, let us move to JS scripts. <\/p>\n<h4>Text rendering via Canvas with WebRTC spin<\/h4>\n<p>Determine the constants and the global variables:<\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;<\/code><\/pre>\n<p>Write &#171;Hello World&#187; on the canvas. Draw a yellow rectangle sized 320px by 176px, font \u2013 Arial, text size \u2013 30px, color &#8212; blue. Use the &#171;loop()&#187; function (mentioned above) to keep redrawing the canvas at 30 fps: <\/p>\n<pre><code class=\"bash\">function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     canvasContext.font = \"30px Arial\";     canvasContext.fillStyle = \"yellow\";     canvasContext.fillRect(0,0,320,176);     canvasContext.strokeStyle = 'black';     canvasContext.fillStyle = \"blue\";     (function loop(){         canvasContext.fillText(\"Hello World!\", 10, 50);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }<\/code><\/pre>\n<p>Connect to a WCS server via WebSocket and publish the stream of the canvas:<\/p>\n<pre><code class=\"bash\">\/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<p>Here&#8217;s the full JS script code for rendering a canvas with text and capturing it for the stream: <\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;   \/\/init api function init_page() {     Flashphoner.init({});     localDisplay = document.getElementById(\"localDisplay\");     canvas = document.getElementById(\"canvas\");     createCanvas();     connect();     }  \/\/create Canvas function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     canvasContext.font = \"30px Arial\";     canvasContext.fillStyle = \"yellow\";     canvasContext.fillRect(0,0,320,176);     canvasContext.strokeStyle = 'black';     canvasContext.fillStyle = \"blue\";     (function loop(){         canvasContext.fillText(\"Hello World!\", 10, 50);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }  \/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<h4>Hello, world!<\/h4>\n<ol>\n<li>\n<p>Open a created Webpage. In this example we are not using buttons, so the canvas is rendered immediately upon page initialization:  <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<\/li>\n<li>\n<p>In another browser tab, open the demo-example &#171;Player&#187; on you WCS server. Specify the name of the stream that is capturing the canvas and start the stream. Here&#8217;s the image of the original canvas and how the stream should look in the player: <\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<\/li>\n<\/ol>\n<h4>Rendering images via Canvas and streaming via WebRTC<\/h4>\n<p>Determine the constants and the global variables: <\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;<\/code><\/pre>\n<p>Upon receiving the image from the webpage, start cyclically rendering it on the canvas at 30 fps:<\/p>\n<pre><code class=\"bash\">function createCanvas() {     var canvasContext = canvas.getContext (\"2d\");     var img = document.getElementById (\"myimage\");     (function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 \/ 30); \/\/ drawing at 30fps     })();     }<\/code><\/pre>\n<p>Connect to a WCS server via WebSocket and publish the stream of the canvas:<\/p>\n<pre><code class=\"bash\">\/\/Connect to WCS server over webSockets function connect() {     session = Flashphoner.createSession({         urlServer: \"wss:\/\/demo.flashphoner.com:8443\" \/\/specify the address of your WCS     }).on(SESSION_STATUS.ESTABLISHED, function(session) {         publishStream(session);     }); }  \/\/Capturing stream of Canvas element function publishStream(session) {     publishStream = session.createStream({         name: \"test-canvas\",         display: localDisplay,         constraints: {             audio: false,             video: false,             customStream: canvas.captureStream(30)         }     });     publishStream.publish(); }<\/code><\/pre>\n<p>Here&#8217;s a full JS script code for rendering a canvas with images and capturing it for the stream:<\/p>\n<pre><code class=\"bash\">var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;   \/\/init api function init_page() {     Flashphoner.init({});     localDisplay = document.getElementById(\"localDisplay\");  <\/code><\/pre>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-326780","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/326780","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=326780"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/326780\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=326780"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=326780"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=326780"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}