В этой статье я поделюсь опытом работы с технологией WebRTC и медиасервером Kurento на этапе тестирования и внедрения. Расскажу с какими проблемами я столкнулся и как их решил. Я не расскажу о том как разработать приложение с нуля, но приведу много полезных ссылок. Уверен, что мой рассказ будет полезен тем, кто собирается работать с WebRTC.

Введение


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

Сервис “Телемедицина” один из таких модулей МИС. Опыта разработки видеоконференций и использования WebRTC не было и заказ был делегирован. Но спустя какое-то время, в силу различных обстоятельств эта компания прекратила поддержку видеоконференции. Без поддержки этот сервис был отключен и “пылился” в репозитории.

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

Приведу очень полезные ссылки по технологии WebRTC и сервера Kurento, которые мне помогли на старте:


Начало работы


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

Напомню, что у клиента уже функционирует медицинская информационная система (МИС), покрывающая огромное количество задач: от электронной очереди, рабочего места доктора, документооборота и PACS до подсистемы управления медицинским оборудованием.

Когда появилась необходимость разработать функционал видеоконференций для связи медицинского персонала диагностических центров (далее — ДЦ) с удаленными пациентами, было поставлено два обязательных условия:

Все конференции должны записываться и храниться на сервере.

Пациенты не должны устанавливать на свои устройства никакие дополнительные программы, кроме браузера, который в большинстве случаев уже предустановлен.

WebRTC работает из браузера без дополнительных программ или плагинов. А Kurento умеет записывать всё, то что проходит через него. И к тому же данный медиасервер обладает хорошим набором готовых библиотек для работы с его API посредством Java и JavaScript, что значительно упрощает разработку.

Разработка серверной части, а точнее её базиса, ещё до того, как я приступил к задаче, была передана клиентом на аутсорс в стороннюю компанию. Так появился “Управляющий сервер” (УС) — готовая серверная основа, которая и досталась мне на внедрение.

Общая идея взаимодействия изначально выглядела так:



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

Первый опыт реанимации


После развертывания в тестовой сети на виртуальных машинах и на нескольких “живых” компах, было проведено много испытаний и экспериментов — всё прекрасно работало. Соответственно наступила пора внедрения в реальную, работающую сеть.

Для тестирования мне в помощь была назначена ответственная жертва в виде доктора и его рабочего места. И второй же звонок через микросервис телемедицины привел к сбою!

Хорошо, что это произошло во время бета-теста и кроме меня и довольного приключениями доктора, этого никто не видел.

Что происходит и почему связь не налаживается понять было очень сложно. WebRTC не показывает отказа — она просто ждет, что вот-вот появится сигнал. По незнанию было очень сложно как-то отлаживаться: серверная часть работает хорошо, Kurento в логах молчит, клиенты ждут потока, но ничего не происходит.

Помог Хабр (хвала ему):


Очень жаль, что я не знал этих инструментов раньше.

Проанализировав данные логов и наблюдая за состояниями подключений, стало понятно, что и серверной части и клиентским скриптам не хватает реакции на события в системе WebRTC. А где эти события взять?

Разработчики сервера kurento предоставляют очень удобную библиотеку для работы с WebRTC на JavaScript: kurento-utils.js.

Для быстрого старта достаточно создать объект:

new kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(options, callback());

А чтобы получить доступ к событиям, надо переопределять внутренние методы библиотеки. Я максимально упростил код, чтобы было понятнее:


// Настройка параметров для WebRtcPeerRecvonly 
var options = {
    //генерация класса с переопределенными внутри методами
    peerConnection: getRTCPeerConnection(videoId), 
    remoteVideo: videoElement, //идентификатор
    //метод на ICE кандидатов
    onicecandidate: function (candidate) {
         onIceCandidate(candidate, videoId, true);
    },
    onerror: onError, //метод на ошибки
    mediaConstraints: { 
        // настройки
        video: true,
        audio: true}
};

// Создание WebRTC
incomeWebRtc[videoId] = new kurentoUtils.WebRtcPeer.WebRtcPeerRecvonly(
    options,
    function (error) {
        if (error) {
   	    return console.error(error);
        }
        this.generateOffer(
        function (error, sdpOffer) {
            //Обработка результата
        });
});

// Пробиваемся к методам обратной связи
function getRTCPeerConnection( videoId ){
    var configuration = {
        "iceServers": [
            {"url": "stun:" + stunServer},
            {"url": "turn:" + turnServer, credential: turnCredential, username: turnUsername}
        ]
    };

    var mypc = new RTCPeerConnection(configuration);

    // Переопределяем методы для обработки событий
    mypc.oniceconnectionstatechange = function(){
        state = this.iceConnectionState;
        console.info("### iceConnectionState " + videoId + " > " + this.iceConnectionState);
    };

    mypc.onsignalingstatechange = function(){
        state = this.signalingState || this.readyState;
        console.info("### SignalingState " + videoId + " > " + state);
    };

    mypc.onconnectionstatechange = function(){
        console.info("### ConnectionState " + videoId + " > " + this.connectionState);
    };

return mypc;
}

Кстати о сертификатах


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

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

Этот путь показался мне проще.

Второе тестирование (уже осторожное)


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

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

Вместе с увлеченным доктором и системным администратором клиента мы пытали систему. Даже не тестировали, а именно “пытали”. Создавали видеоконференции во всех возможных режимах и со всех доступных устройств. Привлекали в эту игру других врачей и часть персонала из удаленного офиса.

Главное было не проверить систему (она не работала), а собрать как можно больше данных. В результате выяснилось, что:

  1. Примерно 80% попыток создать видеоконференцию проходят успешно.
  2. Часть подключений с использованием ICE кандидатов на IPv6 не работают.
  3. Из 5-ти мобильных операторов работали только 2.

Всё оказалось просто — на одном Google далеко не уехать


Анализ собранной информации показал, что нестабильно работает подключение через TURN сервера от Google. То ли нагрузка на них большая, то ли это просто демо-сервера для тех, кто только начинает изучать технологию. Но как факт: очень частые отказы. Нужен свой TURN/STUN сервер!

Вторая причина сбоев — это адреса IPv6.local. Сервер kurento не принимает ICE- кандидаты с этими адресами. Хорошо, что перед отправкой все ICE-кандидаты проходят через код в моих руках и IPv6.local я просто отфильтровал.

Проблема мобильных операторов решается, опять же, своим TURN/STUN сервером.
У трёх из пяти мобильных операторов NAT симметричен, и WebRTC пробиться не может. Подробнее можно почитать здесь: Так ли страшен Symmetric NAT.

Обидно, что мой личный мобильник работает на симке оператора, который не озаботился симметричной защитой. Поэтому моё первоначальное тестирование не выявило этой проблемы.


TURN/STUN сервер


В качестве своего сервера был выбран пакет resiprocate-turn-server.

Долго не выбирали — он есть в стандартном репозитории ubuntu — простая установка и автоматические обновления. Но не очень удобная работа с аккаунтами для подключения: логины и пароли берутся только из файла, из-за чего приходится делать дополнительную утилиту или скрипт для экспорта из базы данных основного сервера.

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

Третий поход к клиенту


Поправил код, установил и настроил свой TURN/STUN сервер, разработал пул паролей и их раздачу клиентам при старте видеоконференции, и, после обновления продакшн-серверов, я пошел к уже знакомому доктору.

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

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

После всех доработок


Наконец, система функционирует и хорошо работает. Пошли первые отзывы от пользователей на местах. И сразу появилось огромное количество пожеланий и предложений по дизайну, дополнительным функциям и прочим планам к дальнейшей разработке. Работа закипела с новой силой!

Сейчас полная топология системы выглядит так:


ИТОГИ:


В завершении хочется сказать следующее:

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

Во-вторых, хвала Хабру! Работая над этим проектом, много информации я нашел на этом ресурсе. Все ссылки в этой статье ведут на него.

Сам же проект видеоконференций “Телемедицина” решено оставить на поддержке и развитии у нашей организации, мы не будет его отдавать на аутсорсинг. В перспективе еще очень много работы:


ВСЁ


Уверен, что мой опыт будет полезен не только для разработчиков под WebRTC + Kurento, но и для тех, кто собирается приступить к внедрению столь же сложных проектов. Уделяйте больше внимания тестированию в максимально приближенных к реальности условиям.

И учитывайте риски того, что команды поддержки Ваших микросервисов могут внезапно “пропасть” — это очень неожиданные и неприятные хлопоты.

Комментарии (17)


  1. kSx
    09.12.2019 20:44
    +1

    Coturn умеет проверять пароли по базе.


  1. apapacy
    09.12.2019 22:25
    +1

    По поводу Kurento как обстоят дела с производительносттью? В интернет есть несколько статей которые утверждают что это на уровне десятков коннектов для довольно мощного железа? (на тот случай если нужны сотни тысяч коннектов)


    1. Ogra
      09.12.2019 23:03

      У нас kurento дает пару сотен коннектов на 20-25% загрузки CPU. Железо нормальное — Intel® Xeon® CPU D-1541 @ 2.10GHz, не слабое, но и не мощное.


  1. ExH
    10.12.2019 02:44

    Kurento — не стабильная, не развивающаяся (https://www.kurento.org/blog/kurento-67-moving-forward) платформа, склонная к зависаниям на ровном месте при низких (от 100 пользователей) нагрузках.
    Чего стоит баг (https://github.com/Kurento/bugtracker/issues/247), который правили больше года и так до сих пор и не исправили.


    1. PaulIsh
      10.12.2019 06:13

      Что порекомендуете взамен?


      1. ExH
        10.12.2019 12:51

        Рекомендую не связываться с webrtc.


        1. PaulIsh
          10.12.2019 13:13

          Хорошо. Тогда какие предлагаете использовать инструменты для организации видео/аудио связи в рамках телемедицинских консультаций врач-врач и врач-пациент? С учетом того, что система сделана на web.


          1. MightyRavendark
            10.12.2019 16:07
            +3

            Можно попробовать использовать TrueConf — у них есть возможность создания «комнат» врач-пациент через API, и встраивание WebRTC-плагина (через iframe) на страницу вашего сервиса.
            Также у них на сайте есть целый раздел посвященный телемедицине — trueconf.ru/telemedicina.html


          1. ExH
            11.12.2019 03:01
            +1

            Как мне кажется лучше всего использовать websocket и fmp4/lhls.
            Как раз будет обеспечена минимальная задержка и контролируемое соединение.
            У нас это отлично работает в большинстве браузеров.


            1. apapacy
              11.12.2019 12:39

              По поводу минимальной задержки — hls это до 1-й минуты. lhls вроде бы должен этот недостаток преодолевать, на насколько? Это 10 секунд? 5 секунд?
              Нам же нужен связный диалог.


              1. ExH
                11.12.2019 13:15
                +1

                В случае с hls, мы делаем чанки по 2 секунды и плейлист из 10 чанков.
                Получается средняя задержка около 4-х секунд. Максимальная задержка может доходить до 20 секунд из-за необходимости обработать rtmp, играть с ключевых кадров и т.п.

                В случае lhls, мы делаем чанки (frames) по 0,1 секунды. Средняя задержка около 1-й секунды. Максимальная задержка может доходить до 6 секунд по вышеуказанным причинам.


            1. MightyRavendark
              11.12.2019 16:18
              +2

              У вас HLS работает в обе стороны? Т.е. и на прием и на отправку? Тут человеку надо двустороннее общение с врачом…


              1. ExH
                11.12.2019 17:43

                Для двухстороннего общения раньше использовался flash и rtmp.
                hls не рилтайм формат, его не стоит использовать для общения людей.

                Сейчас для двухстороннего общения можно использовать webrtc или websocket/lhls/fmp4.


                1. lieff
                  11.12.2019 18:08

                  А не пробовали кейс переключения сетей? Как вебсокеты к этому отнесутся, особенно на мобилах?


                  1. ExH
                    11.12.2019 18:37

                    Всё как обычно, соединение рвётся и подключается.
                    С http было бы точно также, если бы соединение порвалось в середине передачи чанка.


      1. apapacy
        10.12.2019 14:49

        С моей точки зрения зависит от объемов потока и мощности разработчика.


        Как наиболее простой вариант это купить готовый сервис — правда сразу возникает вопрос а какой именно.
        При заведомо не очень больших объемах — coturn. Из опыта разработки и эксплуатации одного приложения на coturn столкнулся с двумя проблемами: 1) нет простого способа горизонтального масштабирования, поэтому рано или поздно будет перегрузка без возможности масштабировать сервис 2) мобильным разработчикам сложно реализовать надежную логику реконнектов
        Ну и, наконец, если Вы мощная компания (Google, Яндекс) — то вы просто пилите что-то свое и выкладываете в общий доступ для всеобщего пользования.


  1. t3mnikov
    10.12.2019 14:07
    +1

    В своем проекте использую OpenVidu от тех же разработчиков. Так же они предлагают использовать COTURN как выше упоминалось. OpenVidu я доволен