Описание
Web Call Server позволяет внедрить видеочат, который будет работать в большинстве современных веб-браузеров без установки дополнительного ПО, а также на мобильных устройствах, в ваш веб-проект.
Поддерживаемые платформы и браузеры
Chrome | Firefox | Safari 11 | Edge | |
---|---|---|---|---|
Windows | + | + | + | |
Mac OS | + | + | + | |
Android | + | + | ||
iOS | - | - | + |
Поддерживаемые кодеки
- Видео: H.264, VP8
- Аудио: Opus, G.711
Функции
- Видеочат
- Текстовый чат
- Видеоконференция
- Видеоконференция с отображением экрана пользователя
Схема работы
- Браузер участника 1 соединяется с сервером по Websocket и отправляет команду join.
- Браузер участника 1 может передавать поток по WebRTC для публикации в чат-комнате и получать потоки, опубликованные в комнате
- Браузер участника 2 соединяется с сервером при помощи Flash и отправляет команду join.
- Браузер участника 2 может передавать поток по RTMP для публикации в чат-комнате и получать потоки, опубликованные в комнате
Видеоконференция
1. Для теста используем:
- демо-сервер demo.flashphoner.com;
- веб-приложение Conference для организации видеоконференции
2. Откройте веб-приложение Conference. В поле "Login" введите произвольное имя пользователя, например test:
3. Нажмите кнопку Join. Установится соединение с сервером, отобразится надпись "ESTABLISHED", будет автоматически создана чат-комната:
Внизу экрана отобразится изображение с веб-камеры, текстовый чат и ссылка на комнату для приглашения других пользователей:
4. Скопируйте ссылку на чат-комнату и откройте ее в другой вкладке браузера. Введите имя пользователя, которое должно отличаться от имени создателя чат-комнаты, например, test2, и нажмите кнопку Join. На странице будет показано изображение с веб-камеры участника test (слева) и с веб-камеры участника test2 (внизу):
5. Введите в окне текстового чата участника test2 сообщение и нажмите кнопку Send:
6. На вкладке участника test введите ответное сообщение:
7. Убедитесь, что ответ получен:
8. Для выхода из-чат-комнаты нажмите кнопку Leave.
Видеочат
1. Для теста используем:
- демо-сервер demo.flashphoner.com;
- веб-приложение Two Way Video Chat для организации видеочата
2. Откройте веб-приложение Two Way Video Chat. В поле "Login" введите произвольное имя пользователя, например test:
3. Нажмите кнопку Join. Установится соединение с сервером, отобразится надпись "ESTABLISHED", будет автоматически создана чат-комната. Будет показано изображение с веб-камеры:
Внизу экрана отобразится текстовый чат и ссылка на комнату для приглашения других пользователей:
4. Скопируйте ссылку на чат-комнату и откройте ее в другой вкладке браузера. Введите имя пользователя, которое должно отличаться от имени создателя чат-комнаты, например, test2, и нажмите кнопку Join. На странице будет показано изображение с веб-камеры пользователя test крупно и с веб-камеры пользователя test2 (внизу слева):
5. Введите в окне текстового чата сообщение и нажмите кнопку Send:
6. На вкладке пользователя test введите ответное сообщение:
7. Убедитесь, что ответ получен:
8. Для выхода из-чат-комнаты нажмите кнопку Leave.
Видеоконференция с отображением экрана компьютера (screen sharing)
1. Для теста используем:
- демо-сервер demo.flashphoner.com;
- веб-приложение Two Way Video Chat and Screen для организации видеоконференции;
- браузер Chrome.
2. Откройте веб-приложение "Two Way Video Chat & Screen". Если активна кнопка Install Now, нажмите ее и установите расширение.
В поле "Login" введите произвольное имя пользователя, например test. Нажмите кнопку Join. Установится соединение с сервером, отобразится надпись "ESTABLISHED", будет автоматически создана чат-комната. Будет показано изображение с веб-камеры:
3. Скопируйте ссылку на чат-комнату и откройте ее в другой вкладке браузера. Введите имя пользователя, которое должно отличаться от имени создателя чат-комнаты, например, test2, и нажмите кнопку Join. На странице будет показано изображение с веб-камеры:
4. Нажмите кнопку "Share" и разрешите браузеру доступ к экрану или к окну приложения:
5. На вкладке пользователя test отобразится экран или окно приложения, к которому был разрешен доступ:
6. Для выхода из-чат-комнаты нажмите кнопку "Leave".
Последовательность выполнения операций (Call Flow)
Ниже описана последовательность вызовов при использовании примера Conference
1. Установка участником 1 соединения с сервером.
Flashphoner.roomApi.connect(); code
connection = Flashphoner.roomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.FAILED, function(session){ setStatus('#status', session.status()); onLeft(); }).on(SESSION_STATUS.DISCONNECTED, function(session) { setStatus('#status', session.status()); onLeft(); }).on(SESSION_STATUS.ESTABLISHED, function(session) { setStatus('#status', session.status()); joinRoom(); });
2. Получение участником 1 от сервера события, подтверждающего успешное соединение.
ConnectionStatusEvent ESTABLISHED code
connection = Flashphoner.roomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.FAILED, function(session){ ... }).on(SESSION_STATUS.DISCONNECTED, function(session) { ... }).on(SESSION_STATUS.ESTABLISHED, function(session) { setStatus('#status', session.status()); joinRoom(); });
3. Вход в чат-комнату участника 1.
connection.join(); code
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ ... });
4. Получение участником 1 от сервера события, описывающего состояние комнаты.
RoomStatusEvent STATE code
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ var participants = room.getParticipants(); console.log("Current number of participants in the room: " + participants.length); if (participants.length >= _participants) { console.warn("Current room is full"); $("#failedInfo").text("Current room is full."); room.leave().then(onLeft, onLeft); return false; } setInviteAddress(room.name()); if (participants.length > 0) { var chatState = "participants: "; for (var i = 0; i < participants.length; i++) { installParticipant(participants[i]); chatState += participants[i].name(); if (i != participants.length - 1) { chatState += ","; } } addMessage("chat", chatState); } else { addMessage("chat", " room is empty"); } publishLocalMedia(room); onJoined(room); ... });
5. Публикация медиапотока участником 1.
room.publish(); code
room.publish({ display: display, constraints: constraints, record: false, receiveVideo: false, receiveAudio: false ... });
6. Получение участником 1 от сервера события, подтверждающего успешную публикацию потока.
StreamStatusEvent PUBLISHING code
room.publish({ display: display, constraints: constraints, record: false, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.FAILED, function (stream) { ... }).on(STREAM_STATUS.PUBLISHING, function (stream) { setStatus("#localStatus", stream.status()); onMediaPublished(stream); }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { ... });
7. Отправка участником 1 потока по WebRTC
8. Установка участником 2 соединения с сервером.
9. Получение участником 2 от сервера события, подтверждающего успешное соединение.
10. Вход в чат-комнату участника 2.
11. Получение участником 2 от сервера события, описывающего состояние комнаты.
12. Получение участником 1 от сервера события о присоединении участника 2.
RoomStatusEvent JOINED code
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ ... }).on(ROOM_EVENT.JOINED, function(participant){ installParticipant(participant); addMessage(participant.name(), "joined"); }).on(ROOM_EVENT.LEFT, function(participant){ ... }).on(ROOM_EVENT.PUBLISHED, function(participant){ ... }).on(ROOM_EVENT.FAILED, function(room, info){ ... }).on(ROOM_EVENT.MESSAGE, function(message){ ... });
13. Получение участником 2 потока, опубликованного участником 1.
14. Публикация медиапотока участником 2.
15. Получение участником 2 от сервера события, подтверждающегог успешную публикацию потока.
16. Отправка участником 2 потока по WebRTC и получение его участником 1.
17. Выход участника 1 из чат-комнаты.
room.leave(); code
function onJoined(room) { $("#joinBtn").text("Leave").off('click').click(function(){ $(this).prop('disabled', true); room.leave().then(onLeft, onLeft); }).prop('disabled', false); ... }
18. Получение участниками комнаты от сервера события о выходе участника 1.
RoomStatusEvent LEFT code
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ ... }).on(ROOM_EVENT.JOINED, function(participant){ ... }).on(ROOM_EVENT.LEFT, function(participant){ //remove participant removeParticipant(participant); addMessage(participant.name(), "left"); }).on(ROOM_EVENT.PUBLISHED, function(participant){ ... }).on(ROOM_EVENT.FAILED, function(room, info){ ... }).on(ROOM_EVENT.MESSAGE, function(message){ ... });
Запись потоков, опубликованных участниками конференции
Видеопотоки, опубликованные каждым из участников конференции, могут быть записаны. Для этого необходимо установить параметр 'record' в 'true' при публикации потока:
room.publish({ display: display, constraints: constraints, record: true, receiveVideo: false, receiveAudio: false ... });
Поток от каждого участника записывается в отдельный файл. Особенность этих файлов при дальнейшей обработке в том, что публикация начинается не одновременно. Для того, чтобы дать возможность объединить потоки участников, потоки комнаты синхронизируются по первому опубликованному потоку.
Например, если участник User1 начал публиковать поток в 00:00:10, а участник User2 в 00:00:55, то второй пользователь получит в начале записи 45 секунд пустого видео (черный экран и тишина). Таким образом, файлы записи потоков User1.mp4 и User2.mp4 будут одинаковы по времени, и их можно будет объединить. Кроме того, потоки участников можно направлять в микшер во время публикации.
Тестирование записи потоков
1. Для теста используем:
- ваш WCS сервер, например test2.flashphoner.com;
- веб-приложение Conference
2. Включите запись потоков в веб-приложении Conference
3. Откройте веб-приложение Conference. В поле "Login" введите имя пользователя Alice и нажмите 'Join'. Опубликуется поток от пользователя Alice:
4. Скопируйте ссылку из поля "Invite":
5. В новом окне браузера перейдите по данной ссылке. В поле "Login" введите имя пользователя Bob и нажмите 'Join'. Отобразится поток от пользователя Alice, и опубликуется поток от пользователя Bob:
6. Нажмите 'Leave' на вкладке пользователя Bob, чтобы выйти из комнаты. Нажмите 'Leave' на вкладке пользователя Alice, чтобы завершить конференцию.
7. В каталоге /usr/local/FlashphonerWebCallServer/records располагаются файлы записи потоков:
-rw-r--r-- 1 root root 1569566 Jun 29 07:51 stream-34296a60-7b36-11e8-bd9c-31aaed48935e-db8mp51bajcidn9qmcnda3967k.mp4 -rw-r--r-- 1 root root 516509 Jun 29 07:51 stream-5aeb2351-7b36-11e8-b398-b74e804508b2-g97j81cgrf8h1m7jl7184fa788.mp4
Файл от пользователя Bob меньшего размера, поскольку в начале файла идет пустое видео для синхронизации. Загрузите файлы на ПК и воспроизведите.
8. Поток от Alice опубликован, Bob еще не вошел в комнату
9. Опубликован поток от Bob
Объединение записанных потоков при помощи ffmpeg
Файлы записей потоков могут быть объединены при помощи ffmpeg с сохранением хронологического порядка. Для этого при создании потока при помощи метода setTimeShift() указывается его сдвиг относительно времени создания комнаты, по умолчанию:
stream.setTimeShift(new Date().getTime() - room.getCreateDate().getTime());
Данным методом можно воспользоваться из клиентского приложения.
Записанные таким образом файлы потоков объединяются командой (пример для двух участников)
ffmpeg -i stream1.mp4 -i stream2.mp4 -filter_complex "[0:v]pad=iw*2:ih[int];[int][1:v]overlay=W/2:0[vid];[0:a][1:a]amerge[a]" -map [vid] -map "[a]" -ac 2 -strict -2 -c:v libx264 -crf 23 -preset veryfast output.mp4
Здесь
- stream1 - поток первого участника
- stream2 - поток второго участника
Для того, чтобы объединить файлы, полученные в ходе тестирования, введите команду:
ffmpeg -i stream-34296a60-7b36-11e8-bd9c-31aaed48935e-db8mp51bajcidn9qmcnda3967k.mp4 -i stream-5aeb2351-7b36-11e8-b398-b74e804508b2-g97j81cgrf8h1m7jl7184fa788.mp4 -filter_complex "[0:v]pad=iw*2:ih[int];[int][1:v]overlay=W/2:0[vid];[0:a][1:a]amerge[a]" -map [vid] -map "[a]" -ac 2 -strict -2 -c:v libx264 -crf 23 -preset veryfast output.mp4
Воспроизведите файл output.mp4: