Пример видеоконференции
Данный пример может использоваться для организации видеоконференции для трех участников на Web Call Server
Участник видеоконференции может публиковать следующие типы потоков
- WebRTC
- RTMFP
- RTMP
На скриншоте ниже представлен пример клиента участника конференции, к которой присоединились два других участника.
На странице вопроизводятся три видео
- нижнее - видео с камеры данного участника
- два верхних - видео от других двух участников (Bob и Cindy)
В поле Invite указана ссылка с именем "комнаты" конференции.
Код примера
Код данного примера находится на WCS-сервере по следующему пути:
/usr/local/FlashphonerWebCallServer/client2/examples/demo/streaming/conference
conference.css - файл стилей
conference.html - страница участника конференции
conference.js - скрипт, обеспечивающий работу конференции
Тестировать данный пример можно по следующему адресу:
https://host:8888/client2/examples/demo/streaming/conference/conference.html
Здесь host - адрес WCS-сервера.
Работа с кодом примера
Для разбора кода возьмем версию файла conference.js с хешем cf0daabc6b86e21d5a2f9e4605366c8b7f0d27eb, которая находится здесь и доступна для скачивания в соответствующей сборке 0.3.18.1894.
Скрипт конференции использует roomApi, предназначенное для видеочатов, конференций, вебинаров и других приложений, которые предполагают нахождение пользователей в одной виртуальной "комнате".
При подключении пользователя к конференции, используется метод roomApi.connect(), в отличии от прямого подключения к серверу методом createSession().
При присоединении к новой "комнате" методом roomApi.join(), создается объект room для работы с этой "комнатой". Для работы с участниками конференции используются объекты Participant.
Все события, происходящие в "комнате" (присоединение/выход пользователя, отправленные сообщения), транслируются другим участникам, подключенным к этой "комнате".
Например, в следующем коде подключаемся к "комнате" и запрашиваем список других участников:
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ var participants = room.getParticipants(); ...
Здесь получаем данные другого участника, который только что присоединился:
}).on(ROOM_EVENT.JOINED, function(participant){ installParticipant(participant); addMessage(participant.name(), "joined"); ...
1. Инициализация API.
Flashphoner.init() код
Flashphoner.init({flashMediaProviderSwfLocation: '../../../../media-provider.swf'});
2. Подключение к серверу.
Flashphoner.roomApi.connect() код
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(); });
3. Получение от сервера события, подтверждающего успешное соединение
ConnectionStatusEvent ESTABLISHED код
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) { ... }).on(SESSION_STATUS.ESTABLISHED, function(session) { ... });
4. Присоединение к конференции.
connection.join() код
При присоединении передается имя "комнаты" конференции (берется из параметра в URL страницы клиента, или генерируется случайное имя).
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ ... });
5. Получение от сервера события, описывающего статус комнаты
RoomStatusEvent STATE код
При получении данного события:
- определяется количество участников конференции с помощью метода room.getParticipants(), который возвращает массив объектов participant,
- если к конференции уже присоединилось максимально допустимое количество участников, производится выход из "комнаты" при помощи room.leave()
- если количество участников меньше максимально допустимого, начинается публикация видеопотока
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); }).on(ROOM_EVENT.JOINED, function(participant){ ... }).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){ ... });
6. Публикация видеопотока.
room.publish() код
При публикации передаем div-элемент localDisplay, в котором будет отображаться видео с камеры.
room.publish(document.getElementById("localDisplay")).on(STREAM_STATUS.FAILED, function (stream) { console.warn("Local stream failed!"); setStatus("#localStatus", stream.status()); onMediaStopped(room); }).on(STREAM_STATUS.PUBLISHING, function (stream) { setStatus("#localStatus", stream.status()); onMediaPublished(stream); }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { setStatus("#localStatus", stream.status()); onMediaStopped(room); });
7. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате
RoomStatusEvent JOINED код
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){ ... });
8. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником
RoomStatusEvent PUBLISHED код
connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){ ... }).on(ROOM_EVENT.JOINED, function(participant){ ... }).on(ROOM_EVENT.LEFT, function(participant){ ... }).on(ROOM_EVENT.PUBLISHED, function(participant){ playParticipantsStream(participant); }).on(ROOM_EVENT.FAILED, function(room, info){ ... }).on(ROOM_EVENT.MESSAGE, function(message){ ... });
9. Воспроизведение видеопотока.
participant.play() код
Параметром передается div-элемент, в котором будет отображаться видео.
function playParticipantsStream(participant) { if (participant.play) { $("[id$=Name]").each(function (index, value) { if ($(value).text() == participant.name()) { var p = value.id.replace('Name', ''); var pDisplay = p + 'Display'; participant.play(document.getElementById(pDisplay)).on(STREAM_STATUS.PLAYING, function (playingStream) { document.getElementById(playingStream.id()).addEventListener('resize', function (event) { resizeVideo(event.target); }); }); } }); } }
10. Остановка публикации видеопотока.
stream.stop() код
function onMediaPublished(stream) { $("#localStopBtn").text("Stop").off('click').click(function(){ $(this).prop('disabled', true); stream.stop(); }).prop('disabled', false); ... }
11. Получение от сервера события, подтверждающего остановку публикации.
StreamStatusEvent UNPUBLISHED код
room.publish(document.getElementById("localDisplay")).on(STREAM_STATUS.FAILED, function (stream) { ... }).on(STREAM_STATUS.PUBLISHING, function (stream) { ... }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { setStatus("#localStatus", stream.status()); onMediaStopped(room); });
12. Выход из комнаты конференции.
room.leave() код
function onJoined(room) { $("#joinBtn").text("Leave").off('click').click(function(){ $(this).prop('disabled', true); room.leave().then(onLeft, onLeft); }).prop('disabled', false); ... }
13. Включение/выключение аудио и видео для публикуемого потока. line 187, line 196
stream.isAudioMuted(), stream.isVideoMuted(), stream.muteAudio(), stream.unmuteAudio(), stream.muteVideo(), stream.unmuteVideo() код
function onMediaPublished(stream) { ... $("#localAudioToggle").text("Mute A").off('click').click(function(){ if (stream.isAudioMuted()) { $(this).text("Mute A"); stream.unmuteAudio(); } else { $(this).text("Unmute A"); stream.muteAudio(); } }).prop('disabled', false); $("#localVideoToggle").text("Mute V").off('click').click(function() { if (stream.isVideoMuted()) { $(this).text("Mute V"); stream.unmuteVideo(); } else { $(this).text("Unmute V"); stream.muteVideo(); } }).prop('disabled',false); }
14. Отправка текстового сообщения.
participant.sendMessage() код
При нажатии на кнопку Send
- определяется массив участников конференции с помощью метода room.getParticipants()
- отправляется сообщение каждому участнику
function onJoined(room) { ... $('#sendMessageBtn').off('click').click(function(){ var message = field('message'); addMessage(connection.username(), message); $('#message').val(""); //broadcast message var participants = room.getParticipants(); for (var i = 0; i < participants.length; i++) { participants[i].sendMessage(message); } }).prop('disabled',false); $('#failedInfo').text(""); }