...
Данный пример может использоваться для организации видеоконференции для трех участников на Web Call Server
Участник видеоконференции может публиковать следующие типы потоков WebRTC поток
...
На скриншоте ниже представлен пример клиента участника конференции, к которой присоединились два других участника.
На странице вопроизводятся воспроизводятся три видео
- нижнее - видео с камеры данного участника (Alice)
- два верхних - видео от других двух участников (Bob и Cindy)
...
Код примера
Код данного примера находится на WCS-сервере по следующему пути:
...
Для разбора кода возьмем версию файла conference.js с хешем 456b1c7ecbadc3, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.201212.
Скрипт конференции использует RoomApi, предназначенное для видеочатов, конференций, вебинаров и других приложений, которые предполагают нахождение пользователей в одной виртуальной "комнате". Для того, чтобы использовать RoomApi, необходимо подключить скрипт flashphoner-room-api.js
...
1. Инициализация API.
Flashphoner.init() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(); |
2. Подключение к серверу.
RoomApi.connect() code
Code Block | ||||
---|---|---|---|---|
| ||||
connection = 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(); ... }); |
3. Получение от сервера события, подтверждающего успешное соединение
ConnectionStatusEvent ESTABLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection = RoomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.ESTABLISHEDFAILED, function(session) { ... }).on(SESSION_STATUS.DISCONNECTED, function(session) { ... }).on(SESSION_STATUS.ESTABLISHED, function(session) { setStatus('#status', session.status()); joinRoom(); }); |
3. Получение от сервера события, подтверждающего успешное соединение
ConnectionStatusEvent ESTABLISHED code4. Присоединение к конференции.
connection.join() code
При присоединении передается имя "комнаты" конференции (берется из параметра в URL страницы клиента, или генерируется случайное имя).
Code Block | ||||
---|---|---|---|---|
| ||||
connection = RoomApi.connectjoin({urlServername: urlgetRoomName(), usernamerecord: usernameisRecord()}).on(SESSIONROOM_STATUSEVENT.FAILEDSTATE, function(sessionroom){ ... }).on(SESSION_STATUS.DISCONNECTED, function(session) { ... }).on(SESSION_STATUS.ESTABLISHED, function(session) { setStatus('#status', session; |
5. Получение от сервера события, описывающего статус комнаты
RoomStatusEvent STATE code
При получении данного события:
- определяется количество участников конференции с помощью метода room.getParticipants(), который возвращает массив объектов participant,
- если к конференции уже присоединилось максимально допустимое количество участников, производится выход из "комнаты" при помощи room.leave()
- если количество участников меньше максимально допустимого, начинается публикация видеопотока
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).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. Проверка режима Low Power Mode при публикации на мобильном устройстве
Flashphoner.playFirstVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
if (Browser.isSafariWebRTC()) {
var display = document.getElementById("localDisplay");
Flashphoner.playFirstVideo(display, true, PRELOADER_URL).then(function() {
publishLocalMedia(room);
}).catch(function (error) {
console.log("Can't atomatically publish local stream, use Publish button");
for (var i = 0; i < display.children.length; i++) {
if (display.children[i]) {
console.log("remove cached instance id " + display.children[i].id);
display.removeChild(display.children[i]);
}
}
onMediaStopped(room);
});
} |
7. Публикация видеопотока.
room.publish() code
При публикации передаем div-элемент, в котором будет отображаться видео с камеры.
Code Block | ||||
---|---|---|---|---|
| ||||
room.publish({ display: display, constraints: constraints, record: false, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.FAILED, function (stream) { console.warn("Local stream failed!"); setStatus("#localStatus", stream.status()); joinRoom(); }); |
4. Присоединение к конференции.
connection.join() code
При присоединении передается имя "комнаты" конференции (берется из параметра в URL страницы клиента, или генерируется случайное имя).
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).on(ROOM_EVENT.STATE, function(room){
...
}); |
5. Получение от сервера события, описывающего статус комнаты
RoomStatusEvent STATE code
При получении данного события:
...
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);
}); |
8. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате
RoomStatusEvent JOINED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord() record: isRecord()}).on(ROOM_EVENT.STATE, function(room){ ... }).on(ROOM_EVENT.STATEJOINED, function(roomparticipant){ var participants = room.getParticipants( installParticipant(participant); consoleaddMessage(participant.logname("Current number of participants in the room: " + participants.length);), "joined"); }).on(ROOM_EVENT.LEFT, function(participant){ ... }).on(ROOM_EVENT.PUBLISHED, function(participant){ if (participants.length >= _participants) ... }).on(ROOM_EVENT.FAILED, function(room, info){ console.warn("Current room is full");... }).on(ROOM_EVENT.MESSAGE, function(message){ ... }); |
9. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником
RoomStatusEvent PUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), $("#failedInfo"record: isRecord()}).text("Current room is full.");on(ROOM_EVENT.STATE, function(room){ room.leave(... }).then(onLefton(ROOM_EVENT.JOINED, onLeft); return false; } function(participant){ setInviteAddress(room.name());... }).on(ROOM_EVENT.LEFT, function(participant){ if (participants.length > 0) ... }).on(ROOM_EVENT.PUBLISHED, function(participant){ playParticipantsStream(participant); }).on(ROOM_EVENT.FAILED, function(room, info){ var chatState = "participants: "; ... }).on(ROOM_EVENT.MESSAGE, function(message){ for (var i = 0; i < participants.length; i++) { ... }); |
10. Проверка режима Low Power Mode при воспроизведении на мобильном устройстве
Flashphoner.playFirstVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
if installParticipant(participants[i]);(Browser.isSafariWebRTC()) { chatState += participants[i].name(); if (i != participants.length - 1Flashphoner.playFirstVideo(pDisplay, false, PRELOADER_URL).then(function() { chatState += ","; }playStream(participant, pDisplay); } addMessage("chat", chatState); } else).catch(function (error) { 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){// Low Power Mode detected, user action is needed to start playback in this mode #WCS-2639 ... }).on(ROOM_EVENT.FAILED, function(room, info){ ... }).on(ROOM_EVENT.MESSAGE, function(message){ ... }); |
6. Публикация видеопотока.
room.publish() code
При публикации передаем div-элемент, в котором будет отображаться видео с камеры.
Code Block | ||||
---|---|---|---|---|
| ||||
roomconsole.publish({ display: display, constraints: constraints,log("Can't atomatically play participant" + participant.name() + " stream, use Play button"); record: false, receiveVideo: false, for (var receiveAudio:i false = 0; i }).on(STREAM_STATUS.FAILED, function (stream< pDisplay.children.length; i++) { console.warn("Local stream failed!"); setStatus("#localStatus", stream.status()); onMediaStopped(room); }).on(STREAM_STATUS.PUBLISHING, function (stream if (pDisplay.children[i]) { setStatus("#localStatus", stream.status()); onMediaPublished(stream); }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { setStatus("#localStatus", stream.status()); onMediaStopped(room console.log("remove cached instance id " + pDisplay.children[i].id); }); |
7. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате
RoomStatusEvent JOINED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).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){pDisplay.removeChild(pDisplay.children[i]); ... }).on(ROOM_EVENT.FAILED, function(room, info){ } ... }).on(ROOM_EVENT.MESSAGE, function(message){ ... }); |
...
RoomStatusEvent PUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).on(ROOM_EVENT.STATE, function(room){ ... }).on(ROOM_EVENT.JOINED, function(participant){ } onParticipantStopped(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){ ... }); |
911. Воспроизведение видеопотока.
participant.play() code
Параметром передается div-элемент, в котором будет отображаться видео.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| function playParticipantsStream(participant) {
if (participant.play) {
| |||||||
$("[id$=Name]").each(function (index, valueparticipant.getStreams()[0].play(display).on(STREAM_STATUS.PLAYING, function (playingStream) { if ($(value).text() == participant.name()document.getElementById(playingStream.id()).addEventListener('resize', function (event) { var p = value.id.replace('Name', ''resizeVideo(event.target); }); varif pDisplay = p + 'Display';(button) { participant.play(document.getElementById(pDisplay)).on(STREAM_STATUS.PLAYING, function (playingStream) $(button).text("Stop").off('click').click(function(){ document.getElementById(playingStream.id()$(this).addEventListenerprop('resizedisabled', function (event) {true); resizeVideo(event.targetplayingStream.stop(); }).prop('disabled', false); }); }).on(STREAM_STATUS.STOPPED, function () { }onParticipantStopped(participant); }).on(STREAM_STATUS.FAILED, function }() { }onParticipantStopped(participant); } }); |
1012. Остановка публикации видеопотока.
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onMediaPublished(stream) { $("#localStopBtn").text("Stop").off('click').click(function(){ $(this).prop('disabled', true); stream.stop(); }).prop('disabled', false); ... } |
1113. Получение от сервера события, подтверждающего остановку публикации.
StreamStatusEvent UNPUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
room.publish({ display: display, constraints: constraints, record: false, receiveVideo: false, receiveAudio: false }).on(STREAM_STATUS.FAILED, function (stream) { ... }).on(STREAM_STATUS.PUBLISHING, function (stream) { ... }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { setStatus("#localStatus", stream.status()); onMediaStopped(room); }); |
1214. Выход из комнаты конференции.
room.leave() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onJoined(room) { $("#joinBtn").text("Leave").off('click').click(function(){ $(this).prop('disabled', true); room.leave().then(onLeft, onLeft); }).prop('disabled', false); ... } |
1315. Включение/выключение аудио и видео для публикуемого потока.
stream.isAudioMuted(), stream.isVideoMuted(), stream.muteAudio(), stream.unmuteAudio(), stream.muteVideo(), stream.unmuteVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
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); } |
1416. Отправка текстового сообщения.
participant.sendMessage() code
При нажатии на кнопку Send
...