На сегодняшний день словами «виртуальная реальность» уже никого не удивишь. Футуристическое оборудование, которое еще пару десятков лет назад мы могли видеть только в фантастических фильмах, теперь доступно и простому обывателю. Даже те, у кого с деньгами совсем плохо, могут соорудить VR-гарнитуру из глины и палок того что есть под рукой, следуя множеству DIY-советов с YouTube.
Кроме чисто развлекательных плюшек, таких как игры и фильмы, VR может быть полезна во многих сферах нашей жизни. Например, в один прекрасный день, получив наследство от безвременно почившей тётушки, Вы решили приобрести виллу на Лазурном берегу. Но вот беда, у такого занятого человека, как Вы, совершенно нет времени посещать для просмотра каждый объект предложенный риэлтером. Вот в этом вопросе и может помочь виртуальная реальность. Риэлтер устанавливает в доме специальную камеру и Вы не покидая любимого дивана своего офиса можете осмотреть дом.
После просмотра недвижимости Вы можете поинтересоваться, чем же занимаются Ваши дети после школы и не закусил ли любимый котейка геранью на подоконнике. И опять на помощь придет виртуальная реальность — с помощью заранее установленной дома камеры можно с эффектом присутствия осмотреть все углы своего милого дома.
Давайте разберемся, как сделать так, что бы все работало, дети не убежали и кот не успел съесть герань.
Ключевое отличие VR-картинки
По сути, VR-медиапоток не отличается от обыкновенного: видеодорожка плюс аудиодорожка. Но есть особенность.
VR-поток предполагает стерео-картинку. Для этого используется специальная камера, с двумя объективами, каждый из которых обладает углом обзора 180 градусов
картинки с каждого из объективов собираются в один кадр,
поэтому плеер на устройстве зрителя должен из двух картинок сделать одну. Как видно, соотношение сторон кадра составляет 2:1, по FullHD картинке на каждый объектив.
Нюансы VR-трансляции
Да-да-да, могу все мели
Счесть, как пальцы на руке
Песенка из классического кино
Итак, речь идет о трансляции 4K потоков с высоким битрейтом. Что у нас есть? А есть у нас RTMP и WebRTC.
RTMP — дешево, надежно и практично. Под капотом использует TCP, что полезно при не очень хороших каналах. Есть разнообразные программные решения для публикующего клиента, как платные, так и бесплатные. Но, при всех достоинствах, RTMP дает высокие задержки. В одной из предыдущих статей мы отмечали задержку в 2-3 секунды для потока 720p. В каких-то случаях задержки приемлемы, но виртуальность будет уже немного отставать от реальности, и дети уже успеют разбежаться.
WebRTC — стильно, модно, молодежно. При хороших каналах задержки измеряются миллисекундами, реальность останется реальностью. Но есть нюансы.
Во-первых, по умолчанию WebRTC бегает поверх UDP, что при малейших ухудшениях качества канала приводит к потерям. А для 4K потока любой чих на канале — уже ухудшение. С этим можно справиться, перейдя на TCP транспорт, но дальше нас подстерегает…
Во-вторых, WebRTC транслируется из браузера, а мы с вами знаем, какой браузер сейчас самый популярный на планете (спойлер — давно не IE6). И в этом популярном браузере, причем на уровне движка, зашит максимальный битрейт публикации 2500 кбит/с. Этого вполне достаточно по мнению Google для FullHD, но маловато для 4K, и если битрейт не разогнать до 5 — 10 Мбит/с, зрители увидят вместо виртуальной реальности движущиеся акварельные пятна. К счастью, есть специальные настройки для разгона битрейта, которые работают во всех большинстве браузеров на движке Chromium. Затем, преодолев это препятствие, мы упремся…
В третьих, мы упремся в пропускную способность каналов. Чтобы публиковать и играть VR-картинку, при упомянутом выше битрейте клиенту потребуются каналы от 20 Мбит, а лучше от 50, а еще лучше от 100 Мбит, причем в обе стороны — и на загрузку, и на выгрузку. Причем это должны быть не те параметры, о которых рассказывает провайдер в рекламных буклетах, а реальная пропускная способность от клиента до сервера. Кстати, здесь можно прочитать, как ее можно измерить без iperf и знания командной строки. На стороне сервера, таким образом, желательно иметь 10 Гбит, если рассчитывать на большое количество потоков.
Ну и в четвертых, что следует из требований к каналам. Транскодирование 4K потока займет слишком много ресурсов процессора на сервере. Поэтому нужно либо выбирать царь-сервер, что накладно по бюджету, либо использовать кодирование на видеокартах, что также весьма затратно, либо избегать транскодирования. То есть ни в коем случае не менять разрешение, частоту кадров и битрейт потоков при трансляции.
Итак, все мели сочтены, теперь
Давайте попробуем
Что тут думать, трясти надо!
Персонаж из детского мультика
Возьмем:
- специальную камеру
- специальный плеер
- сервер, умеющий транслировать WebRTC из браузера
Устанавливаем камеру на столе или штативе
Подключаем к ПК по USB 3.0 с питанием 1A, как для внешних дисков
Надо сказать, камера весьма чувствительна к оборудованию, и если на ноутбуке 2 порта USB 3.0, с одним из них она вполне может отказаться работать. Кабель для подключения также должен быть не совсем китайским, все пины, предусмотренные стандартом, должны быть на месте. И даже при идеальном подключении, камера может выдавать помеху, как старый пузатый телевизор в грозу. Зато для Windows 10 драйверов не требуется, честный Plug-n-Play.
На стороне сервера установим необходимые границы битрейта
1 2 | webrtc_cc_min_bitrate=5000000 webrtc_cc_max_bitrate=10000000 |
Откроем в браузере Chrome страницу примера, позволяющего выставить нужные параметры для захвата потока с камеры, и установим
- разрешение 3072×1536
- FPS 24 (при трансляции из браузера камера поддерживает только такой FPS)
- транспорт TCP
и настроим параметры SDP для того, чтобы браузер не резал нам осетра битрейт, на стороне клиента
1 | x-google-max-bitrate=10000;x-google-min-bitrate=5000 |
или на стороне сервера (в этом случае настройки будут действовать для всех публикующих клиентов)
1 2 | webrtc_sdp_min_bitrate_bps=5000000 webrtc_sdp_max_bitrate_bps=10000000 |
Опубликуем поток с камеры
Теперь попробуем проиграть поток обычным WebRTC плеером
Все хорошо… Почти. Виртуальность какая-то не совсем виртуальная. Что ж, теперь играем этот же поток в специальном плеере, внедренном на страницу
Так гораздо лучше (и котики есть). Если играть поток на мобильном устройстве или в VR-очках, можно покрутить головой, а мы в браузере на ПК покрутим мышкой. Где-то в этой комнате спрятался ребенок. Посмотрим налево
Теперь направо
И вверх
А где же малыш? Да вот он, за шторкой прячется. Но котик все видит!
Кода, нужно больше кода!
На стороне клиента кода немного.
Публикуем поток со страницы браузера
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | session.createStream({ name: streamName, display: localVideo, cacheLocalResources: true , transport: "TCP" , sdpHook: rewriteSdp, constraints: { audio: true , video: { width: 3072, height: 1536, frameRate: 24 } } }).publish(); |
Функция замены параметров SDP браузера
1 2 3 4 5 6 7 | function rewriteSdp(sdp) { var sdpStringFind = "a=fmtp:(.*) (.*)" ; var sdpStringReplace = "a=fmtp:$1 $2;x-google-max-bitrate=10000;x-google-min-bitrate=5000" ; var newSDP = sdp.sdpString.toString(); newSDP = newSDP.replace(new RegExp(sdpStringFind, "g" ), sdpStringReplace); return newSDP; } |
Пример страницы с VR-плеером
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | <!DOCTYPE html> <html> < head > <title>WebRTC Delight< /title > <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <script type = "text/javascript" src= "../../../../flashphoner.js" >< /script > <script type = "text/javascript" src= "../../dependencies/jquery/jquery-1.12.0.js" >< /script > <script type = "text/javascript" src= "../../dependencies/js/utils.js" >< /script > <script src= "dl8-player.js" async>< /script > <meta name= "dl8-custom-format" content= '{"name": "STEREO_TERPON","base":"STEREO_MESH","params":{"uri": "03198702.json"}}' > < /head > <body> <div style= "width: 50%;" id = "display" > <dl8-live-video id = "remoteVideo" format = "STEREO_TERPON" > < source > < /dl8-live-video > < /div > <input class= "form-control" type = "text" id = "playStream" placeholder= "Stream Name" > <button id = "playBtn" type = "button" class= "btn btn-default" disabled>Play< /button > <button id = "stopBtn" type = "button" class= "btn btn-default" disabled>Stop< /button > <script> Flashphoner.init({flashMediaProviderSwfLocation: '../../../../media-provider.swf' }); var SESSION_STATUS = Flashphoner.constants.SESSION_STATUS; var STREAM_STATUS = Flashphoner.constants.STREAM_STATUS; var STREAM_STATUS_INFO = Flashphoner.constants.STREAM_STATUS_INFO; var playBtn = document.getElementById( 'playBtn' ); var display = document.getElementById( 'display' ); var dl8video = null; var url = setURL(); document.addEventListener( 'x-dl8-evt-ready' , function () { dl8video = document.getElementById( 'remoteVideo' ); $( '#playBtn' ).prop( 'disabled' , false ).click( function () { playStream(); }); }); function playStream() { $( '#playBtn' ).prop( 'disabled' , true ); $( '#stopBtn' ).prop( 'disabled' , false ); var video = dl8video.contentElement; Flashphoner.createSession({urlServer: url}).on(SESSION_STATUS.ESTABLISHED, function (session) { var session = Flashphoner.getSessions()[0]; session.createStream({ name: document.getElementById( 'playStream' ).value, display: display, remoteVideo: video, transport: "TCP" }).on(STREAM_STATUS.PLAYING, function (stream) { dl8video.start(); $( '#stopBtn' ).prop( 'disabled' , false ).click( function () { $( '#playBtn' ).prop( 'disabled' , false ); $( '#stopBtn' ).prop( 'disabled' , true ); stream.stop(); dl8video. exit (); }); }).play(); }) } < /script > < /body > < /html > |
Обычно при создании стрима для плеера (вызов session.createStream()) мы передаем div-элемент, в который будет вмонтирован video-элемент для воспроизведения стрима по WebRTC. Но VR-плеер использует свой video-элемент, и нам нужно каким-то образом пробросить его в код используемого API. Для этого мы напрямую передаем video-элемент стороннего плеера в функцию session.createStream() параметром remoteVideo
В качестве серверной части используется demo.flashphoner.com, примеры веб-приложений доступны в подвале по ссылкам.
Удачи в осмотре на месте, не сходя с места!
Ссылки
Пример для публикации потока с нужными параметрами
Пример интеграции VR-плеера в WebRTC-стриминг