Я сидел перед монитором уже битый час, а может и два. Все началось со ссылки на чей-то твиттер, которую коллега любезно закинул мне в скайп. Потом случайно открыл новостной сайт, потом Facebook, за это время успела появиться еще пара новостей… В общем, спина уже затекла и пора было пойти размяться. В офисе было прохладно, тихо работали кондиционеры. Выходить на уличную жару совсем не хотелось и, разогнувшись, я доковылял до ближайшего кофейного автомата. Где-то на ресепшене прозвенел колокольчик.
Через пару минут я увидел Ольгу, сопровождающую джентельмена азиатской наружности. На вид ему было около пятидесяти. На слегка морщинистой голове восседала серая шляпа с короткими полями. Они явно шли ко мне. Поравнявшись с кофейным автоматом, который уже журчал в стаканчик моим капучино, джентельмен произнес на ломаном русском: Здраствуйте, я относительный проекта WebRTC. Моя зовут Суконако, и протянул руку. Что привело сюда этого японца, подумал я, ответив на рукопожатие, и пригласил гостя в свой кабинет. Дальше нам пришлось перейти на английский язык, который нам обоим был более понятен.
Собираем требования
Я: Итак, чем могу быть полезен?
С: Мы работаем с 2000 года в стриминге и Flex для большого количества пользователей. Мы используем Adobe Flash Media Server (FMS) и сейчас хотели бы использовать WebRTC.
Я: Можно подробнее о том, чего вы хотели бы достичь использованием WebRTC-сервера?
С: Нам требуется обычный медиасервер, который может принимать видеопотоки от одного пользователя и передавать их другим пользователям. Мы хотим видеочат.
Я: Без проблем, мы можем сделать решение на базе одного из WebRTC-серверов.
С: Adobe FMS нас полностью устраивает. Мы хотели бы расширить круг наших пользователей на WebRTC, не убирая FMS. Он работает хорошо.
В руках Суконако появился планшет, он подвинул его мне и ткнул пальцем в следующую схему:
C: Flex App – это доктор, Flex Apps – это пациенты, доктор использует веб камеру и консультирует сразу нескольких пациентов. Один из пациентов может запросить у доктора приватную консультацию, и тогда доктор остается один на один для приватной консультации с пациентом. В этот момент остальные пациенты не могут общаться с доктором и не видят его.
Странно, подумал я. О чем доктор может одновременно консультировать пациентов. Один жалуется о том, что болит ухо, второй сетует на гланды, потом тот, что с гландами нажимает на кнопку и переходит в режим приватной консультации. Однако принцип понятен. Сосредоточимся на технической стороне вопроса.
Я: Т.е. в результате устанавливается двухсторонняя видеосвязь доктора и пациента?
С: Не совсем. Пациент всегда видит доктора, но доктор может не видеть пациента. По умолчанию видео пациента отключено.
Я: Значит видео одностороннее – от доктора к пациенту?
С: В большинстве случаев да. Но иногда пациенты желают показать свое видео доктору. Это случается нечасто.
Я: Все понятно. Значит нужно чтобы и доктор и пациент могли использовать WebRTC-браузер, например Firefox или Google Chrome, а также IE, который будет работать через FMS. Верно?
С: Почти так. Все доктора используют разработанное нами стриминг-приложение на Flex. Пациенты же должны использовать либо наше приложение либо WebRTC.
Я: Т.е. в идеале, ваше приложение должно выглядеть так?
И набросал схему.
С: Да, верно. Это должно работать именно так. С одной стороны нативное Flex-приложение, а с другой WebRTC-браузер. Нас интересуют прежде всего браузеры на Android-смартфонах и iOS-устройствах. Вам наверное известно, что Flash так или иначе присутствует на всех десктопных браузерах: IE, Chrome, Firefox, Safari, но его нет на Android и iOS. Мы хотели бы сделать наш сервис доступным на мобильных браузерах и сохранить то, что работает хорошо на десктопах, т.е. FMS.
Я: На Android-браузерах WebRTC будет работать, а на iOS с этим проблема – WebRTC в iOS-браузерах не работает из-за ограничений платформы. Т.е. мы не сможем доставить WebRTC видеопоток на iOS и не можем стримить видео с вебкамеры iOS-браузера.
C: Постойте, мне известно что Safari не поддерживает WebRTC, но его поддержка есть в Google Chrome.
Я: Да, но не в случае iOS. Там Chrome натыкается на технические ограничения платформы и не имеет таких возможностей работы с WebRTC-видео, как на десктопе. Т.е. iOS браузер в данном случае не подходит. Почему бы вам не выгрузить собственное приложение в Apple App Store? Тогда пользователям iOS нужно будет всего лишь установить приложение и использовать чистый WebRTC, тот, что используется в Google Chrome?
С: К сожалению, мы не можем выгрузить приложение в App Store по внутренним причинам. Кроме этого, мы хотели бы дать нашим пользователям (пациентам) возможность не устанавливать дополнительных приложений на свой iPhone или iPad, а работать прямо из браузера. Какие у нас есть варианты?
В этот момент я подумал, о «внутренних причинах», которые не позволяют опубликовать приложение в App Store. Возможно область медицинских консультаций регулируется законодательством, и это действительно не так просто, выкатить в App Store приложение такого рода.
Вариантов в действительности было не много и лучшим из них было нативное приложение с поддержкой WebRTC. iOS Safari, как известно поддерживает HLS (Apple HTTP Live Streaming), но этот вариант был отброшен, т.к. в общении доктора с пациентом предполагался определенный реалтайм и живое общение, для которого HLS совершенно не подходит при задержке около 20 секунд.
Оставался последний вариант: вебсокеты. Websockets сейчас поддерживается почти во всех браузерах – это фактически TCP-канал, по которому можно доставить видео с задержкой сравнимой с RTMP, т.е 3 секунды, а не 20. С доставкой понятно. Еще бы воспроизвести этот поток в <video/> HTML5 элементе и было бы все замечательно.
Я: Вариант, похоже, только один – вебсокеты. Причем в этом случае пациенты не смогут отправить свое видео на сервер. Возможна только односторонняя доставка от доктора к пациенту. Можно попробовать HLS, но там задержка больше 20 секунд и вероятно вам это не подойдет.
С: Хорошо. Правильно ли я понял, что мы сможем играть живые потоки с FMS прямо на iOS Safari браузере? Пусть без WebRTC, но с небольшой задержкой, сравнимой с RTMP?
Я: Да, абсолютно верно. Но нам нужно некоторое время это проверить. Давайте договоримся, скажем на понедельник, и я покажу вам демо.
С: Мне бы хотелось увидеть возможность интеграции FMS одновременно с WebRTC и с Websockets, чтобы быть уверенным, что это будет работать на iOS и на Android. Возможно ли так сделать?
Я: Да, думаю все получится.
С: Спасибо за консультацию. В понедельник зайду в 10, если не возражаете и обсудим все вопросы, уже имея демо.
Я: Да, конечно, к этому времени все будет готово.
Ищем решение
Как видно из разговора, требования немного изменились и теперь предстояло прикрутить к Adobe AMS два способа доставки: WebRTC для браузеров на Android и Websockets для браузера Safari под iOS. Осталось найти недостающий элемент, который позволит построить это демо и связать все участвующие в нем технологии и протоколы.
Проводив японского гостя, я первым делом отправился смотреть спецификацию Adobe AMS. Нашел в ней много интересного, кроме слов WebRTC и Websocket.
Дальше, не долго думая, забил в Гугл три нужных кейворда: rtmp, webrtc и websockets. Гугл выплюнул несколько релевантных сайтов. Пригодными, как оказалось были всего два: проприетарный проект Flashphoner и описание опенсорсного прототипа с сайта Phoboslab
Первый кандидат
Начать решил с Phoboslab, где была во всех красках описана проблема воспроизведения стрима в iOS Safari и предлагалось решение, очень похожее на опенсорсное.
Решение строится на ffmpeg, node.js и клиентском javascript для декодирования и воспроизведения видеопотока. Все компоненты были действительно опенсорсные и схема выглядела многообещающей.
Я поднял виртуальный сервер на DO, собрал ffmpeg и установил node.js. Все это заняло около двух часов.
Видео в iOS Safari действительно играло, и играло не плохо. Мой iPhone5 слегка грелся, но JavaScript стабильно лопатил видеотрафик с Websocket и показывал его на web-странице.
Фактичеки происходило декодирование стрима на JavaScript и последующая отрисовка в элементе страницы браузера iOS Safari. Оставались открытыми следующие вопросы:
— Как забрать стрим с FMS
— Как добавить к стриму звук
— Что насчет WebRTC
И здесь меня ждало некоторое разочарование. Оказалось, что JavaScript-плеер играет (отрисовывает) только видео. Для аудио пришлось бы пускать дополнительный поток, после чего их нужно как-то синхронизировать. Но данное решение этого не предусматривало. Таким образом, данное решение не подходило для передачи видео от доктора из-за отсутствия звука.
Второй кандидат
Следующим подопытным был Web Call Server с обещанной поддержкой RTMP, WebRTC, Websocket протоколов. Осталось проверить, применима ли поддержка данных протоколов в моем конкретном случае и как это работает.
Первым делом решил проверить конвертацию RTMP видеопотока в Websocket, по аналогии с предыдущим тестом. Ведь если это получится сделать, то далее можно перенаправить RTMP поток с FMS на Web Call Server и тем самым решить одну из задач.
Я вооружился iPhone-ом и открыл одну из демо-страниц, где предлагалось попробовать сделать это с демо-сервера. По заверениям техподдержки, Web Call Server можно быстро установить на свою Linux-систему, но это заняло бы некоторое время, а демо давало возможность сразу понять работает это или нет.
Демо-интерфейс представлял собой обычное Flash-приложение с неказистым дизайном и простым функционалом, под названием Flash Streaming.
Из этого Flash приложения можно подключиться к серверу по протоколу RTMP и опубликовать стрим с веб-камеры. Опубликовать (Publish) – значит захватить видеопоток с веб-камеры браузера с помощью Flash Player и отправить данные на сервер в реальном времени по протоколу RTMP.
Судя по статусам ‘Connected’ и ‘Publishing’, соединение было успешно установлено и стрим с веб-камеры отправлялся на сервер. Дабы не светить в стриме своим фейсом, я использовал виртуальную камеру и серию из пятого (?) сезона Game of Thrones.
Далее осталось увидеть и услышать это видео на iPhone в браузере Safari. Для этого рекомендовалось использовать отдельный плеер под названием WS Player Minimal.
На плеере удалось получить приличную картинку и звук без искажений или рассинхрона.
Пожалуй, я достиг некоторого прогресса в своем ресерче:
Удалось протестировать доставку стрима RTMP-Websocket
Стрим был со звуком и видео и корректно отображался на Safari браузере
Осталось проверить играет ли стрим в WebRTC и можно было переходить к интеграции с Adobe FMS. Для проверки этого же стрима c WebRTC, я открыл демо Streamer And Player Minimal в браузере Chrome и выполнил аналогичную нехитрую процедуру, вставив имя потока, и нажав на воспроизведение.
Какаова же была моя радость, Кхалиси!
Теперь в моем арсенале была доставка RTMP потока на Chrome, а значит на Android по WebRTC, и на iOS Safari по вебсокетам. И в том и в другом случае картинка была вполне плавной, со звуком и теоретически годилась для развертывания консалтингового сервиса.
Следующим вопросом была работа с FMS. Понятно, что RTMP протокол должен быть единым для всех реализаций, но предстояло выяснить а) Может ли FMS перенаправить RTMP стрим на Flashphoner и б) примет ли Flashphoner этот стрим, как принимал его с Flash в тестах выше.
Интеграция с Adobe Media Server
С FMS пришлось повозиться. Его установка и тесты заняли у меня пару часов. Первое, что я сделал, это протестировал FMS с помощью FMLE и убедился что FMS я установил и настроил правильно, и RTMP видеопотоки ходят на нем без каких-либо препятствий.
Следующим шагом была настройка перенаправления RTMP потока на Flashphoner. Здесь пришлось немного напрячь мозг, вооружиться документацией Adobe по Action Script и заимплементить такой скрипт: main.asc.
var wcsServer = "wcs5-eu.flashphoner.com";
var netConnections = new Object();
var streams = new Object();
application.onConnect = function (client){
trace("onConnect "+client.id);
var nc = new NetConnection();
var obj = new Object();
obj.login = "Alice";
obj.appKey = "flashChatApp";
nc.connect("rtmp://"+wcsServer+":1935",obj);
nc.onStatus = function(info){
trace("onStatus info.code: "+info.code);
if (info.code=="NetConnection.Connect.Success"){
trace("connection opened "+wcsServer);
}
}
netConnections[client.id]=nc;
return true;
}
application.onDisconnect = function (client){
trace("onDisconnect "+client.id);
var nc = netConnections[client.id];
if (nc){
nc.close();
trace("disconnected "+client.id);
}
}
application.onPublish = function(client, myStream){
trace("onPublish "+myStream.name);
var nc = netConnections[client.id];
ns = new NetStream(nc);
ns.onStatus = function(info){
if (info.code == "NetStream.Publish.Start"){
trace("It is now publishing "+myStream.name);
}
}
ns.attach(myStream);
ns.publish(myStream.name);
streams[myStream.name]=ns;
trace("publish stream "+myStream.name+" to: "+wcsServer);
}
application.onUnpublish = function(client, myStream){
trace("onUnpublish "+myStream.name);
var ns = streams[myStream.name];
if (ns){
ns.publish(false);
trace("unpublished "+ns.name);
}
}
Скрипт достаточно простой и занимается тем, что делегирует входящие на FMS коннекты и видеопотоки Flashphoner-серверу. Например, при получении коннекта от приложения в методе onConnect, создается коннект к Flashphoner-серверу по RTMP.
При получении видеопотока onPublish, этот же видеопоток публикуется на Flashphoner.
При дисконнектах и остановке стримов, соответствующие вызовы делегируются для освобождения ресурсов. Так у меня получился мост, по которому будет прокидываться трафик между FMS и Flashphoner для дальнейшей раздачи по WebRTC и Websockets.
Для тестирования этой композиции я взял уже знакомый Flash-интерфейс Flash Streaming. Единственная разница была в том, что нужно было указать RTMP адрес FMS-сервера и далее положиться на скрипт main.asc, который делегирует этот видеопоток Flashphoner. В моем случае этим адресом был rtmp://my-fms:1935
При тестировании пришлось изрядно подебажить серверный скрипт main.asc от незнания Action Script и серверного программирования под FMS, но это все осталось в прошлом, и выше в листинге доступна рабочая на тот момент версия этого скрипта. FMS не подвел и передал RTMP стрим по назначению, что и позволило успешно проиграть его в Chrome и далее в Safari.
Установка Web Call Server
В итоге демо было готово. Оставалось поставить Web Call Server на своей системе чтобы избежать сбоев в момент презентации. Мало ли чего они там могут накрутить до понедельника.
На сайте разработчика нашлась инструкция по установке, состоящая из пяти пунктов. Пятый пункт с установкой SSL-сертификатов я опустил, т.к. пока не планировал использовать WebRTC-стриминг с камеры и микрофона.
Download Web Call Server 5
Install using the 'install.sh' script
Launch using 'service webcallserver start'
Open the web-interface http://host:9091 and activate your license
Установка прошла нормально. Я предусмотрительно отключил на тестовом сервере Firewall (service iptables stop) чтобы исключить проблемы с непрохождением трафика.
Через минуту после запуса сервера получилось открыть веб-интерфейс с админкой http://host:9091, активировать тестовую лицензию и получить у себя на Ubuntu демо-сервер, похожий на этот:
Дополнительный прогон тестов позволил убедиться, что схема работает и в моем окружении. С чувством выполненного дела, я завершил рабочий день и поставил напоминалку – еще раз проверить тесты в понедельник в 9:00 перед приходом Суконако.
Как проходила миграция и чем закончилось дело, могу написать во второй части, если будет интересно.
Ссылки на используемые инструменты:
FMS (Flash Media Server) он же AMS (Adobe Media Server) – RTMP медиа сервер.