Practical uses of WebRTC Canvas streaming

In this article we will once again return to the tired topic of webinars and webinar hosting tools. And no, we’re not about to code a whole new system for webinar hosting – 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.

At first glance, the solutions are readily available. All you seem to need is to add a canvas to the page (HTML 5 element «Canvas»), and start drawing. Well, adding it is easy enough, but the question is how to transmit the drawing process into WebRTC.

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.

What is Canvas?

Let’s see what Wikipedia has to say about it.

Canvas 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.

The pros of using Canvas are as follows:

  • it supports hardware acceleration;

  • it is capable of manipulating any and each pixel separately;

The cons are as follows:

  • it overloads the processor and RAM;

  • its garbage collector is limited and provides no option to clear memory;

  • it requires manual handling of object events;

  • it doesn’t run well at high resolutions;

  • it requires each element to be rendered separately.

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.

Quirks of stream capture

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.

Also note, that the stream cannot consist of one frame. That means once you’ve put a static image on the Canvas, the stream won’t form because there is only one frame in it. The solution is to render the static images cyclically.

For instance, the following function continually refreshes a Canvas-rendered image at 30 fps:

function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 / 30); // drawing at 30fps     }

Another point has to do with a browser security measure known as Same-origin Policy.

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.

This issue can be resolved using the Cross-Origin Resource Sharing (CORS) technology, but that solution is beyond the scope of this article.

How-to guides for readying Canvas

As is tradition, we are going to provide how-to guides. Of those, there are two:

  1. Text rendering via Canvas and WebRTC-flavored streaming

  2. Image rendering via Canvas and consequent WebRTC streaming

HTML-related parts are going to be virtually identical. Create a HTML file named canvas-streaming-min.html

In the page’s head, write down the access to scripts:

<script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="canvas-streaming-min.js"></script>

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:

<body onload="init_page()">         <canvas width="480" height="320" id="canvas"></canvas>         <div id="localDisplay" hidden></div>         <br>      </body>

For a simple example, let’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:

<img src="2307589.png" alt="logo" id="myimage" >

Here’s the complete code of an HTML page for a canvas with text:

<!DOCTYPE html> <html lang="en"> <head>         <script type="text/javascript" src="../../../../flashphoner.js"></script>         <script type="text/javascript" src="canvas-streaming-min.js"></script> </head> <body onload="init_page()">         <canvas width="480" height="320" id="canvas"></canvas>         <div id="localDisplay" hidden></div>     </body> </html>

For a canvas with images:

<!DOCTYPE html> <html lang="en"> <head>         <script type="text/javascript" src="../../../../flashphoner.js"></script>         <script type="text/javascript" src="canvas-streaming-min.js"></script> </head> <body onload="init_page()">         <canvas width="480" height="320" id="canvas"></canvas>         <div id="localDisplay" hidden></div>         <br>         <img src="2307589.png" alt="logo" id="myimage" >       </body> </html>

Now, let us move to JS scripts.

Text rendering via Canvas with WebRTC spin

Determine the constants and the global variables:

var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;

Write «Hello World» on the canvas. Draw a yellow rectangle sized 320px by 176px, font – Arial, text size – 30px, color — blue. Use the «loop()» function (mentioned above) to keep redrawing the canvas at 30 fps:

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 a WCS server via WebSocket and publish the stream of the canvas:

//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(); }

Here’s the full JS script code for rendering a canvas with text and capturing it for the stream:

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(); }

Hello, world!

  1. Open a created Webpage. In this example we are not using buttons, so the canvas is rendered immediately upon page initialization:

  2. In another browser tab, open the demo-example «Player» on you WCS server. Specify the name of the stream that is capturing the canvas and start the stream. Here’s the image of the original canvas and how the stream should look in the player:

Rendering images via Canvas and streaming via WebRTC

Determine the constants and the global variables:

var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;

Upon receiving the image from the webpage, start cyclically rendering it on the canvas at 30 fps:

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 a WCS server via WebSocket and publish the stream of the canvas:

//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(); }

Here’s a full JS script code for rendering a canvas with images and capturing it for the stream:

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(); }

Hello, world! Part two

  1. 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):

  2. Open the demo-example «Player» on your WCS server. Specify the name of the stream that is capturing the canvas and start the stream. Here’s the image of the original canvas and how the stream should look in the player:

Conclusion

With this article we didn’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.

Good streaming to you!

Links

Demo

WCS on Amazon EC2

WCS on DigitalOcean

WCS in Docker

Quick deployment and testing of the server

Stream Capturing and publish from an HTML5 Canvas element (whiteboard)

ссылка на оригинал статьи https://habr.com/ru/company/flashphoner/blog/568682/

Практическое применение WebRTC Canvas стриминга

В этой статье поднимем несколько надоевшую тему вебинаров и инструментов для их проведения. Нет. Писать систему для проведения вебинара не будем. Их уже до нас написано превеликое множество. Обсудим возможность подключить к вебинару рисовалку, чтобы можно было делать пометки от руки и транслировать все это дело в поток.

На первый взгляд решение лежит на поверхности. Надо только добавить на страницу холст — элемент HTML 5 «Canvas», и на нем рисовать. Добавить-то добавили, а как завернуть то, что нарисовано, в WebRTC?

Итак, давайте разберем, что такое WebRTC стриминг с холста, или Canvas стриминг, и какие в нем могут быть подводные камни.

Что такое Canvas?

Если верить Википедии, то:

Canvas (англ. canvas — «холст», рус. канва́с) — элемент HTML5, предназначенный для создания растрового двухмерного изображения при помощи скриптов, обычно на языке JavaScript. Используется, как правило, для отрисовки графиков для статей.

Из плюсов использования Canvas можно выделить:

  • имеет аппаратное ускорение;

  • можно манипулировать каждым пикселем;

Минусы:

  • чрезмерно нагружает процессор и оперативную память;

  • из-за ограничения сборщика мусора нет возможности очистить память;

  • необходимо самому обрабатывать события с объектами;

  • плохая производительность при высоком разрешении;

  • приходится отрисовывать отдельно каждый элемент.

Минусов получилось больше, чем плюсов. Но несмотря на это, у Canvas есть огромное преимущество в использовании — можно стримить поток со слайдами и делать пометки на этих слайдах в процессе вебинара.

Особенности захвата потока

При захвате потока из элемента Canvas браузер считает этот элемент черным. Поэтому текст, который будет написан на холсте черными буквами, при захвате потока сольется с фоном и в поток будет транслироваться черный экран. То же самое может произойти при отрисовке .png изображения с прозрачным фоном и черным рисунком.

Кроме этого, поток не может состоять из одного кадра. Поэтому, когда вы один раз что-то вывели на Canvas, и это изображение статично там висит, поток не может сформироваться, т.к. был всего один кадр. Решение — отрисовывать статичный контент циклично.

Например, ниже функция, которая реализует обновление картинки, отрисованной на Сanvas с частотой 30 fps:

function loop(){         canvasContext.drawImage(img, 10, 10);         setTimeout(loop, 1000 / 30); // drawing at 30fps     }

Еще один нюанс напрямую связан с политикой безопасности браузеров — «Политикой одного источника».

Изображение, которое нужно нарисовать на холсте, необходимо разместить локально на веб-сервере или на веб-сервере в том же домене. Если изображение, которое будет отрисовано на холсте загружается из Интернета, браузер выдаст исключение.

Обойти эту проблему можно используя технологию Cross-Origin Resource Sharing (CORS), но это выходит за рамки нашей статьи.

Рецепты по приготовлению Canvas

По традиции, дадим готовые рецепты. Рецепта сегодня два:

  1. Отрисовка текста на Canvas и стриминг под соусом WebRTC;

  2. Отрисовка на Canvas изображения и дальнейший стриминг по WebRTC.

HTML часть будет практически идентична. Создаем HTML файл canvas-streaming-min.html

В хэде страницы пропишем обращения к скриптам:

<script type="text/javascript" src="../../../../flashphoner.js"></script> <script type="text/javascript" src="canvas-streaming-min.js"></script>

В боди — размещаем HTML 5 элемент Canvas и div элемент для публикации потока. Инициализируем Flashphoner API при загрузке HTML страницы:

<body onload="init_page()">         <canvas width="480" height="320" id="canvas"></canvas>         <div id="localDisplay" hidden></div>         <br>      </body>

Для простоты примера в HTML файле для холста с изображением добавим картинку, которая и будет отрисована на холсте. Файл картинки мы разместили в той же папке на сервере, что и файлы примера:

<img src="2307589.png" alt="logo" id="myimage" >

Полный код HTML страницы для канваса с текстом:

<!DOCTYPE html> <html lang="en"> <head>         <script type="text/javascript" src="../../../../flashphoner.js"></script>         <script type="text/javascript" src="canvas-streaming-min.js"></script> </head> <body onload="init_page()">         <canvas width="480" height="320" id="canvas"></canvas>         <div id="localDisplay" hidden></div>     </body> </html>

Для канваса с изображением:

<!DOCTYPE html> <html lang="en"> <head>         <script type="text/javascript" src="../../../../flashphoner.js"></script>         <script type="text/javascript" src="canvas-streaming-min.js"></script> </head> <body onload="init_page()">         <canvas width="480" height="320" id="canvas"></canvas>         <div id="localDisplay" hidden></div>         <br>         <img src="2307589.png" alt="logo" id="myimage" >       </body> </html>

Теперь переходим к написанию JS скриптов.

Отрисовка текста на Canvas и стриминг под соусом WebRTC

Определяем константы и глобальные переменные:

var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;

Пишем на Canvas фразу «Hello World». Нарисуем желтый прямоугольник 320px на 176px, текст напишем шрифтом Arial, кегль 30px, цвет — синий. Используем функцию «loop()» которую уже упоминали выше, чтобы перерисовывать канвас с частотой 30 fps:

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     })();     }

Подключаемся к WCS серверу через WebSocket и публикуем поток с канваса:

//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(); }

Полный код JS скрипта для отрисовки холста с текстом и захвата его в поток:

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(); }

Привет мир!

  1. Откройте созданную Web-страницу. В этом примере мы не используем кнопок, поэтому канвас отрисовывается сразу при инициализации страницы:

  2. В другой вкладке браузера откройте демо-пример «Player» на вашем WCS сервере. Укажите имя потока, в который был захвачен canvas и запустите воспроизведение. На скриншоте исходный канвас и вид потока в плеере:

Отрисовка на Canvas изображения и дальнейший стриминг по WebRTC

Определяем константы и глобальные переменные:

var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var session; var canvas; var localDisplay;

Получаем картинку с web-страницы и циклично отрисовываем ее на канвасе, чтобы получить 30 кадров в секунду:

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     })();     }

Подключаемся к WCS серверу через WebSocket и публикуем поток с канваса:

//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(); }

Полный код JS скрипта для отрисовки холста с изображением и захвата его в поток:

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(); }

Привет мир! часть вторая

  1. Откройте созданную Web-страницу. Канвас будет отрисован сразу при инициализации страницы. Картинка (в нижней части страницы) отображается на холсте (в верхней части страницы):

  1. Откройте демо-пример «Player» на вашем WCS сервере. Укажите имя потока, в который был захвачен canvas и запустите воспроизведение. На скриншоте исходный канвас и вид потока в плеере:

Заключение

В этой статье мы не ставили целью показать что-то принципиально новое. Но среди обращений в нашу техническую поддержку бывают вопросы по стримингу с канваса, и в этой статье мы разобрали минимальные примеры кода, с помощью которых можно использовать этот функционал в своем проекте.

Хорошего стриминга!

Ссылки

Наш демо сервер

WCS на Amazon EC2 — Быстрое развертывание WCS на базе Amazon

WCS на DigitalOcean — Быстрое развертывание WCS на базе DigitalOcean

WCS в Docker — Запуск WCS как Docker контейнера

Документация по быстрому развертыванию и тестированию WCS сервера

Документация по захвату и трансляции видеопотока с элемента HTML5 Canvas (whiteboard)

ссылка на оригинал статьи https://habr.com/ru/company/flashphoner/blog/568680/

Психи на работе

Привет, Хабр! Меня зовут Андрей Ревяшко, я СТО сети Эльдорадо. Мы регулярно встречаемся с командой и обсуждаем различные окологиковские темы. Совсем недавно мы говорили про психотипы членов команды разработки и их влияние на общий результат. Поделюсь этим материалом и с вами. Уверен, что часть из вас точно возьмет его на вооружение.

На что обратить внимание, подбирая идеальную команду. Разбор психотипов

1978 год. NASA озадачилось созданием идеальной команды астронавтов, которые отправятся в космос и не поругаются из-за разного темперамента или взглядов на жизнь, что приведет к огромным финансовым убыткам. А мы представляем, в какие доллары с, как минимум, шестью нулями обходится один такой полет. В ведомстве обратились к доктору психологии Тайби Кэлеру, который в начале 70-х открыл Process communication model (РСМ), и попросили его продемонстрировать эффективность модели при отборе будущих космонавтов. Результат превзошел все ожидания. До сих пор команды астронавтов NASA формируются на основе этой модели.

Кэлер выделил 6 функций (или, можно еще сказать граней), которые, по его мнению, присутствуют в каждом человеке: логик, упорный, деятель, мечтатель, душевный, бунтарь. Далее, в зависимости комбинаций и яркой выраженности той или иной функции, можно определить психотип любой отдельно взятой личности. Честно – мне эта модель очень нравится, но у нее есть один большой минус – она очень сложна и дорога в изучении. Поэтому я выбрал для обсуждения с коллегами работу отечественного автора – Александра Афанасьева, который тоже совершил своеобразный прорыв в психологии, описав его в книге «Синтаксис любви: типология личности и прогноз парных отношений». Забегая вперед, скажу, что это не столько пособие по формированию семьи, сколько отсылка к первопричине возникновения симпатий или конфликтов в любом виде социума.

В отличие от Кэлера, Афанасьев выделяет всего 4 психических модуля (функции) человека: эмоция, логика, физика, воля. И у каждого есть некое восприятие/определение: эмоция (душа), логика (разум), физика (тело и физический мир в целом), воля (дух). 

Приведу выдержку из книги Афанасьева: «Воля, Логика, Физика, Эмоция — в психике личности не являются чем-то равнозначным, расположенным по горизонтали, а представляют собой иерархию или, говоря иначе, четырех ступенчатый порядок функций, где каждая функция, в зависимости от ее положения на ступенях лестницы, по-своему выглядит и действует». Попытаюсь внести ясность.

Первые две ступени воспринимается личностью, как сильные стороны. Последние две функции определяются больше, как слабость. Причем по факту это может быть вовсе не так, но речь идет именно о восприятии.

Чтобы дальше было интереснее, предлагаю самостоятельно пройти тест в открытом источнике: здесь.

Как расположение функций выглядит в обычной жизни?

Первое место – функция всегда избыточна и первой включается в конфликтных ситуациях. К примеру, одна сторона всегда кричит (Эмоция на первом месте), а вторая швыряет в стену какие-то предметы или даже может толкнуть оппонента (высокая Физика). Первая функция – она как монолог, не терпит вмешательств извне. Сразу скажу: абсолютно у всех айтишников первая функция – Логика (любители поспорить).

Второе место – функция уравновешена и подразумевает диалог, гармонию. К примеру, в случае второй Логики уже не будет бесконечного спора как с первой Логикой. С таким человеком комфортнее вести диалог.

Последние, как я уже говорил, две функции характеризуются как слабость.

Функция, которая находится на третьем месте, воспринимается самим человеком как недостаток. Например, люди с третьей логикой могут быть очень умными в реальности, но ощущают себя абсолютно глупыми, пустышками. Такие часто стремятся себя прокачать через несколько высших образований. Поэтому, если человеку с первой Логикой сказать, что он недалекий, он этого даже не заметит скорее всего, а вот того, у кого Логика на третьем месте, такие высказывания могут ранить на всю жизнь. Как удар ниже пояса.

Рассмотрим историю Наполеона Бонапарта. По настоянию императора, люди, которые представляли его в обществе, обязаны были в первую очередь говорить, что он член Национального института наук и искусств по классу физики и математики и только потом перечислялись регалии военного толка – победитель, завоеватель, император и пр. Таким образом, он все время подчеркивал, что он вообще-то умный! Так, в частности, проявлялась его третья Логика

У Иосифа Сталина, кстати, третьей функцией была Воля. Ему все время казалось, что его кто-то недостаточно уважает. Следуя теории Афанасьева, во что это вылилось? Каждый, кто сомневался в правоте Иосифа Виссарионовича, зачастую отправлялся в ГУЛАГ.

С третьей же Физикой человек может, бояться побоев, но это только восприятие. К примеру,  чтобы купировать этот воспринимаемый страх, он может начать заниматься рукопашным боем, тем самым, минимизируя проблему восприятия побоев. Такие люди более старательны в достижении поставленных целей, но порой случаются катастрофические перекосы — так стремятся к совершенству своей третьей функции, что начинают перебарщивать.

Когда Эмоция находится на третьем месте, то повышать голос до ультразвука в присутствии таких личностей точно не стоит. Люди с третьей эмоцией считаются самыми незащищенными. С ними лучше даже не шутить — чувство юмора может страдать. Не дай Бог на защиту третьей Эмоции придет первая Физика.

Функция на четвертом месте воспринимается человеком как пустяк — есть и слава Богу. Она же первой отключается во время стрессовых ситуаций. К примеру, у вышеупомянутого Наполеона Бонапарта Эмоция была именно четвертой функцией, и в его случае это хорошо, так как войну Эмоция в битве будет только мешать.

Интересный момент – люди иногда спрашивают: «Если у меня логика на 4 месте, значит я самый глупый в мире человек?» Вовсе нет. И тут мы возвращаемся к началу текста – речь идет сугубо о восприятии, подчеркиваю! Есть крайне недалекие люди с первой логикой и гении с четвертой.

Вы наверняка заметили, что я акцентировал внимание именно на третьей функции. Из всего сказанного выше можно сделать простой, но важный, вывод – третья функция – это, по сути, больное место человека. Зная, куда не надо давить, но где «подсластив» в хорошем смысле слова, можно легко найти подход к человеку.

На закрепление материала. Если получилось определить, какая функция у нужного вам человека находится на третьем месте, то вот простая инструкция по общению с ним:

Третья Эмоция – не истерить.

Третья Воля – обозначить его значимость.

Третья Логика – подчеркнуть разум.

Третья Физика – сделать комплимент.

Как наперед определить, что из себя представляет человек?

Функция Эмоция:

1 место – романтики. Они верят в астрологию, пишут стихи, создают высокие произведения искусства. Сюда можно записать многих выдающихся итальянцев, да и в целом автор труда обозначил, что у итальянцев, в большинстве своем, функция Эмоция на высоте.

2 место – актеры. Очень часто встречается среди актеров кино. Людям со второй Эмоцией всегда нужен зритель, они с легкостью идут выступать на конференции.

3 место – люди-сухари (в плане эмоций). Все от того, что третья Эмоция воспринимается как слабое место.

4 место – сложные отношения с юмором.

 

Функция Логика:

1 место – догматики, все айтишники без исключений.

2 место – хорошие собеседники и ораторы. Отличный пример: Фидель Кастро, который поставил рекорд Гиннеса по продолжительности доклада — беспрерывно говорить в течение 4 с половиной  часов.

3 место – скептики. При возникновении немой паузы, им ее всегда надо заполнить. Как пример, Борис Николаевич с известным “понимаешь ли”. Кстати, к ним относятся самые большие почитатели кроссвордов.

4 место – школяры. Их больше всего на нашей планете. Думать не хотят, не потому что глупые, а потому что зачем напрягаться по пустякам?

Функция Физика:

1 место – собственники. Их можно вычислить по брендам, которые вычурно выставляются на показ: машины, вещи, аксессуары и далее по списку. По статистике, очень азартные люди. К сожалению, этот азарт порой пересекается с уголовным миром.

2 место – труженики, у них самое адекватное восприятие труда. Сюда можно отнести японцев. Кстати говоря, отличным для спортсменов случай будет вторая Физика при первой Воле. Как думаете почему? Пишите в комментариях — подискутируем.

3 место – недотроги, с боязнью физического контакта, побоев. Им свойственны коммуны, объединения в общины. Автор выделяет китайцев как людей, в большинстве своем, с третьей Физикой. Касаемо боязни побоев — возможно, это и стало отправной точкой в создании немалого количества рукопашных техник на территории Китая.

4 место – лентяи. Встречал в иных источниках заявления, что не стоит брать людей с низкой позицией функции Физика на работу, ибо они лентяи, но это не так. Все потому, что они концентрируется на одном деле настолько сильно, что до остального уже нет интереса. Мне и самому с моей четвертой Физикой периодически прилетает от жены “хватит бездельничать — весь день сидишь за компом”. И самые распространенные (по моему опыту) психотипы ИТшников тоже с Физикой на 3 и на 4 местах (Лао-цзы, Энштейн, Августин).

Функция Воля:

1 место – цари. Им всегда будет мало власти, при этом жизненная установка выглядит так: власть – только я, все остальные – подчиненные. Как подсветил автор труда — первая Воля всегда за равноправие, имея в виду, что перед первой Волей все равны (в подчинении).

2 место – дворяне. Самая распространенная у айтишников, что хорошо. Это нормальное восприятие руководителя сверху и адекватное общение с людьми, находящимися на ступень ниже по иерархии.

3 место – мещане. Прекрасные продажники в силу того, что могут найти подход к каждому, умеют подстраиваться. Афанасьев в своей книге даже вспоминает историю с Гоголем, который писал, что только в России есть свое наименование особого поклона к разному роду людей.

4 место – крестьяне. Им, в плане восприятия какой-то позиции в структуре подчиненности, больше всех повезло, так как отсутствуют внутренние огорчения: сегодня ты руководитель первого звена, а завтра – на три ступени ниже оказался и ничего страшного (мне одному сейчас захотелось дополнить чау-чау-чау-чау, вспоминая ставшую мемом песню?)

Ну и на десерт расскажу, какие психотипы людей создают идеальные рабочие команды (в нашем случае ИТ команды), если мы говорим про работу, и более прочные отношения в семьях. Кстати, этот метод работает и в личной жизни.

Полуагапэ (где агапэ – одна из разновидностей любви, назовем это симпатией) – общение точно будет складываться. Здесь слабая функция одного психотипа перекрывает слабую функцию другого, а первая и четвертая равнозначны.

Пример:

ЛВЭФ (Эйнштейн) – ЛЭВФ (Паскаль) 

Полуагапэ потому, что конфликты (в данном случае не редко будут спорить из-за первых Логик) будут возникать с большей вероятностью, чем при агапэ
Полуагапэ потому, что конфликты (в данном случае не редко будут спорить из-за первых Логик) будут возникать с большей вероятностью, чем при агапэ

Полное агапэ – идеальное попадание. Человек с первой логикой спокойно сосуществует с тем, у кого она четвертая, никаких споров не возникает. Физика первая с физикой четвертой –взаимодействие без конфликтов. Такой дуэт дает возможность каждому участнику максимально самореализоваться + перекрытие слабых функций одного психотипа сильными уравновешенными функциями второго.

Пример: ФЭВЛ (Дюма) – ЛВЭФ (Эйнштейн).

Идеальные отношения как на работе, так и дома
Идеальные отношения как на работе, так и дома

Вместо заключения

На самом деле, верить в предложенную выше концепцию или нет – дело каждого. В своей работе я нередко пользовался описанной выше методикой и определенно видел позитивные результаты.  

ссылка на оригинал статьи https://habr.com/ru/company/mvideo/blog/568826/

Реактивный SQL с jOOQ 3.15 и R2DBC

Одна из самых больших новых функций недавно выпущенного jOOQ 3.15 — это поддержка реактивных запросов с помощью R2DBC. Это был очень популярный запрос функциональности, и мы наконец его выполнили.

Вы можете продолжать использовать jOOQ так, как вы привыкли, обеспечивая типобезопасный SQL, встроенный в Java, kotlin или scala, но выполнение ваших запросов больше не блокируется. Вместо этого ваш jOOQ ResultQuery или Query может использоваться как Publisher<R> или Publisher<Integer> в реализации reactive-streams (реактивных потоков) по вашему выбору.

Вместо (или в дополнение к) настройки вашего jOOQ DSLContext с помощью JDBC java.sql.Connection или javax.sql.DataSource просто настройте его с помощью R2DBC io.r2dbc.spi.Connection или io.r2dbc.spi.ConnectionFactory:

ConnectionFactory connectionFactory = ConnectionFactories.get(     ConnectionFactoryOptions         .parse("r2dbc:h2:file://localhost/~/r2dbc-test")         .mutate()         .option(ConnectionFactoryOptions.USER, "sa")         .option(ConnectionFactoryOptions.PASSWORD, "")         .build() );   DSLContext ctx = DSL.using(connectionFactory);

В качестве альтернативы используйте Spring Boot для автоматической настройки jOOQ следующим образом:

Конечно, хороший обзор: pic.twitter.com/tUgNkwzCK4

— Ангел Леонард (@anghelleonard) 15 июля 2021 г.

Используя этот DSLContext, вы можете строить свои запросы как обычно, но вместо того, чтобы вызывать обычные блокирующие методы execute() или fetch(), вы просто оберните запрос в Flux, например. Предположим, что вы запустили генератор кода jOOQ на своей H2 INFORMATION_SCHEMA, тогда вы можете написать:

record Table(String schema, String table) {}   Flux.from(ctx         .select(             INFORMATION_SCHEMA.TABLES.TABLE_SCHEMA,             INFORMATION_SCHEMA.TABLES.TABLE_NAME)         .from(INFORMATION_SCHEMA.TABLES))       // Type safe mapping from Record2<String, String> to Table::new     .map(Records.mapping(Table::new))     .doOnNext(System.out::println)     .subscribe();

jOOQ получит R2DBC Connection у ConnectionFactory и освободит ее после выполнения запроса, что позволит оптимизировать управление ресурсами, что в противном случае несколько сложно с R2DBC и reactor. Другими словами, приведенный выше код соответствует этому написанному вручную запросу:

Flux.usingWhen(         connectionFactory.create(),         c -> c.createStatement(                 """                 SELECT table_schema, table_name                 FROM information_schema.tables                 """              ).execute(),         c -> c.close()     )     .flatMap(it -> it.map((r, m) ->           new Table(r.get(0, String.class), r.get(1, String.class))     ))     .doOnNext(System.out::println)     .subscribe();

Оба напечатают что-то вроде следующего:

Table[schema=INFORMATION_SCHEMA, table=TABLE_PRIVILEGES] Table[schema=INFORMATION_SCHEMA, table=REFERENTIAL_CONSTRAINTS] Table[schema=INFORMATION_SCHEMA, table=TABLE_TYPES] Table[schema=INFORMATION_SCHEMA, table=QUERY_STATISTICS] Table[schema=INFORMATION_SCHEMA, table=TABLES] Table[schema=INFORMATION_SCHEMA, table=SESSION_STATE] Table[schema=INFORMATION_SCHEMA, table=HELP] Table[schema=INFORMATION_SCHEMA, table=COLUMN_PRIVILEGES] Table[schema=INFORMATION_SCHEMA, table=SYNONYMS] Table[schema=INFORMATION_SCHEMA, table=SESSIONS] Table[schema=INFORMATION_SCHEMA, table=IN_DOUBT] Table[schema=INFORMATION_SCHEMA, table=USERS] Table[schema=INFORMATION_SCHEMA, table=COLLATIONS] Table[schema=INFORMATION_SCHEMA, table=SCHEMATA] Table[schema=INFORMATION_SCHEMA, table=TABLE_CONSTRAINTS] Table[schema=INFORMATION_SCHEMA, table=INDEXES] Table[schema=INFORMATION_SCHEMA, table=ROLES] Table[schema=INFORMATION_SCHEMA, table=FUNCTION_COLUMNS] Table[schema=INFORMATION_SCHEMA, table=CONSTANTS] Table[schema=INFORMATION_SCHEMA, table=SEQUENCES] Table[schema=INFORMATION_SCHEMA, table=RIGHTS] Table[schema=INFORMATION_SCHEMA, table=FUNCTION_ALIASES] Table[schema=INFORMATION_SCHEMA, table=CATALOGS] Table[schema=INFORMATION_SCHEMA, table=CROSS_REFERENCES] Table[schema=INFORMATION_SCHEMA, table=SETTINGS] Table[schema=INFORMATION_SCHEMA, table=DOMAINS] Table[schema=INFORMATION_SCHEMA, table=KEY_COLUMN_USAGE] Table[schema=INFORMATION_SCHEMA, table=LOCKS] Table[schema=INFORMATION_SCHEMA, table=COLUMNS] Table[schema=INFORMATION_SCHEMA, table=TRIGGERS] Table[schema=INFORMATION_SCHEMA, table=VIEWS] Table[schema=INFORMATION_SCHEMA, table=TYPE_INFO] Table[schema=INFORMATION_SCHEMA, table=CONSTRAINTS]

Обратите внимание, что, если вы используете JDBC, а не R2DBC, вы можете продолжать использовать jOOQ API с библиотеками реактивных потоков в режиме блокировки точно так же, как указано выше, например, если ваша любимая СУБД еще не поддерживает реактивный драйвер R2DBC. В настоящее время список поддерживаемых драйверов согласно r2dbc.io включает:

Все из них интегрированы с jOOQ 3.15+.

Работоспособный пример

Поиграйте с примером, представленным здесь: https://github.com/jOOQ/jOOQ/tree/main/jOOQ-examples/jOOQ-r2dbc-example

Он использует следующую схему:

CREATE TABLE r2dbc_example.author (   id INT NOT NULL AUTO_INCREMENT,   first_name VARCHAR(100) NOT NULL,   last_name VARCHAR(100) NOT NULL,       CONSTRAINT pk_author PRIMARY KEY (id) );   CREATE TABLE r2dbc_example.book (   id INT NOT NULL AUTO_INCREMENT,   author_id INT NOT NULL,   title VARCHAR(100) NOT NULL,       CONSTRAINT pk_book PRIMARY KEY (id),   CONSTRAINT fk_book_author FOREIGN KEY (id)      REFERENCES r2dbc_example.author );

и выполняет следующий код

Flux.from(ctx         .insertInto(AUTHOR)         .columns(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME)         .values("John", "Doe")         .returningResult(AUTHOR.ID))     .flatMap(id -> ctx         .insertInto(BOOK)         .columns(BOOK.AUTHOR_ID, BOOK.TITLE)         .values(id.value1(), "Fancy Book"))     .thenMany(ctx         .select(              BOOK.author().FIRST_NAME,               BOOK.author().LAST_NAME,               BOOK.TITLE)         .from(BOOK))     .doOnNext(System.out::println)     .subscribe();

Чтобы вставить две записи и выбирать запись, получив в результате:

FIRST_NAME

LAST_NAME

TITLE

John

Doe

Fancy Book

ссылка на оригинал статьи https://habr.com/ru/post/568830/

Непредвзятые отзывы об ИТ-продуктах: а такое бывает?

Привет, Хабр! Сегодня большинство пользователей принимает решение о приобретении товара или заказе услуги после прочтения отзывов. Однако вопрос, насколько честны эти отзывы, и какой процент “заказухи” среди них остается открытым. Интересное решение этого вопроса предлагают такие платформы, как портал G2. Каждый квартал они публикуют свои G2 Grid Reports со специально отфильтрованными отзывами. И сегодня мы подробнее расскажем о летнем G2 Grid Report в области резервного копирования.

Купленные отзывы с завидной регулярностью появляются практически на всех площадках. Уже много лет за них критикуют тот же Amazon, а подготовка отзывов и оценок гордо называется “управлением репутацией” компанией в сети. Это, конечно, так же круто, как называть уборщиц “менеджерами по клинингу”, но, тем не менее, работает. И люди часто покупаются на те отзывы, которые им показывает сайт. 

Проект G2 Crowd пытается решить эту проблему в области обзоров софта для бизнеса за счет идентификации и аутентификации пользователей. В качестве основного инструмента проверки принадлежности человека, который пишет отзыв, используется социальная сеть LinkedIn. Таким образом, модераторы сайта проверяют, не принадлежит ли автор позитивного отзыва, например, про Acronis к штату Acronis, а также, не работает ли написавший разгромный отзыв в компании-конкуренте.

Механика сайта достаточно интересна. Сначала автоматизированные алгоритмы отсеивают отзывы, которые были определены, как ангажированные. Потом выборку просматривают модераторы. После публикации отзывы можно плюсовать и минусовать. В результате добавляется третий уровень коллективной оценки.

Более того, за счет интеграции с соцсетями G2 Crowd позволяет увидеть, кто из ваших знакомых или коллег писал какие-либо отзывы и найти их на G2.com. Кстати, довольно интересно, проверить, насколько активно ваши контакты из LinkedIn проявили себя на этом портале. Если такие найдутся, поделитесь, пожалуйста, информацией в комментариях.

Кстати, написать отзыв на G2 — это не такая уж простая задача. Чтобы сделать это нужно ответить на 65 вопросов о продукте. Но зато инициативные и ответственные тестеры накапливают баллы и получают возможность выиграть ценные призы. Больше баллов достается за проверенный отзыв — к которому был прикреплен скриншот, подтверждающий что пользователь действительно залогинен в том ПО, о котором он пишет обзор. 

От обзоров к исследованиям

Накопив определенное количество обзоров и оценок в более чем 300 категориях софта для бизнеса G2 перешли к исследованиям. Они дополняют обзоры на своем портале отзывами с других площадок и социальных сетей, которые агрегируются предварительно обученными алгоритмами и фильтруются по стандартным правилам. Возможность просмотреть статистику по компаниям схожего размера и смежных отраслей, по мнению владельцев площадки, помогает выбрать наиболее подходящий продукт.

На сегодняшний день алгоритм G2 достиг уже версии v3.0 и позволяет рассчитывать для каждого продукта такие параметры, как степень удовлетворенности пользователей и уровень присутствия на рынке. 

Уровень удовлетворенности определяется по совокупности параметров в порядке убывания их важности, таких как:

  • Удовлетворенность характеристиками для конечных пользователей на базе обзоров продукта

  • Популярность решения, исходя из количество отзывов на G2

  • Качество обзоров (более полные отзывы имеют больший вес)

  • Время публикации отзывов (чтобы придать более важности наиболее последним обзорам, отражающим реальную стадию развития продукта)

  • Удовлетворенность пользователей возможностями продукта для администраторов

  • Общий уровень удовлетворенности и Net Promoter Score​ (NPS)

Такой параметр, как присутствие на рынке (Market Presence) тоже определяется по целому ряду критериев. В их число входит 15 метрик из опроса G2, а также публично доступная информация и данные из сторонних источников. Оценка каждого продукта дополняется оценкой самого вендора. Она хоть и имеет меньший вес, но влияет на общий рейтинг.

Чтобы попасть в рейтинг продукт должен иметь как минимум 10 проверенных отзывов. А позиционирование решения определяется по специальной методике категоризации, с которой можно ознакомиться подробнее. Таким образом, посетители портала могут изучать продукты в сравнении друг с другом в интересующей их категории, просматривать отзывы и изучать оценки, поставленные реальными пользователями. 

Как суперпозиция всех этих данных нормализованные очки для каждой категории (все продукты ранжируются относительно друг друга и баллы — от 1 до 100 — являются относительной оценкой), продукты делятся на 4 квадранта: Лидеры (Leaders), Эффективные (High Performers), Претенденты (Contenders) и Нишевые решения (Niche)

Резервное копирование серверов и онлайн-бэкап

Нам в этой методике ближе всего такие категории, как серверный бэкап и онлайн-резервное копирование. К серверному бэкапу согласно методике относятся решения, которые позволяют сохранять серверные данные на удаленной площадке или на выделенных серверах, напрямую подключаются к защищаемому серверу и обеспечивают корпоративный уровень бэкапа. Изучить отчет по этой категории и сравнить позиции разных решений можно здесь

В категории онлайн-бэкап приведены решения, которые позволяют хранить копии файлов онлайн или в облаке, интегрируются с различными приложениями или запускают синхронизацию на эндпоинты, а также обеспечивают онлайн-доступ к данным и помогают управлять бэкапом. Изучить отчет по этой категории и сравнить позиции разных решений можно здесь

Заключение

Интересно, что технология работает в реальном времени. И хотя все летние отчеты G2 Grid были основаны на данных, полученных до 1 июня 2021 года (исследование публикуется раз в квартал), на страницах Online Backup и Server Backup можно изучить самые актуальные позиции. То есть система учитывает последние доработки и развитие продуктов, опять же, через отзывы реальных пользователей. Поэтому мы усиленно работаем над тем, чтобы сохранить лидирующие позиции в этом рейтинге.

Впрочем, интересно было бы узнать мнение хабра. Как вы считаете, полезен ли такой механизм оценки отзывов? Можно ли считать подобные рейтинги более информативными, чем исследования авторитетных аналитических фирм?

ссылка на оригинал статьи https://habr.com/ru/company/acronis/blog/568428/