...
На скриншоте ниже представлен пример клиента участника конференции, к которой присоединились два других участника.
На странице воспроизводятся три видео
...
Для разбора кода возьмем версию файла conference.js с хешем ecbadc390771d4, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.212218.
Скрипт конференции использует RoomApi, предназначенное для видеочатов, конференций, вебинаров и других приложений, которые предполагают нахождение пользователей в одной виртуальной "комнате". Для того, чтобы использовать RoomApi, необходимо подключить скрипт flashphoner-room-api.js
...
1. Инициализация API.
Flashphoner.init() code
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(); |
2. Подключение к серверу.RoomApi.connectЗапрос доступа к камере и микрофону
Flashphoner.getMediaAccess() code
Code Block | ||||
---|---|---|---|---|
| ||||
connection = RoomApi.connect({urlServer: Flashphoner.getMediaAccess(null, localDisplay).then(function() { createConnection(url, username: username); }).on(SESSION_STATUS.FAILED, catch(function(sessionerror) { ... }); |
3. Получение от сервера события, подтверждающего успешное соединение
ConnectionStatusEvent ESTABLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection = 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() console.error("User not allowed media access: "+error); $("#failedInfo").text("User not allowed media access. Refresh the page"); onLeft(); joinRoom(); }}); |
43. Присоединение Подключение к конференциисерверу.
connectionRoomApi.joinconnect() codeПри присоединении передается имя "комнаты" конференции (берется из параметра в URL страницы клиента, или генерируется случайное имя).
Code Block | ||||
---|---|---|---|---|
| ||||
function createConnection(url, username) { connection = RoomApi.joinconnect({nameurlServer: getRoomName()url, recordusername: isRecord()username}).on(ROOMSESSION_EVENTSTATUS.STATEFAILED, function(roomsession){ ... }); } |
54. Получение от сервера события, описывающего статус комнаты
RoomStatusEvent STATE code
При получении данного события:
- определяется количество участников конференции с помощью метода room.getParticipants(), который возвращает массив объектов participant,
- если к конференции уже присоединилось максимально допустимое количество участников, производится выход из "комнаты" при помощи room.leave()
- если количество участников меньше максимально допустимого, начинается публикация видеопотока
...
language | js |
---|---|
theme | RDark |
...
подтверждающего успешное соединение
ConnectionStatusEvent ESTABLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection = RoomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.FAILED, function(session){ var participants = room.getParticipants();... }).on(SESSION_STATUS.DISCONNECTED, function(session) { console.log("Current number of participants in the room: " + participants.length)... }).on(SESSION_STATUS.ESTABLISHED, function(session) { setStatus('#status', session.status()); 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: ";joinRoom(); }); |
5. Присоединение к конференции.
connection.join() code
При присоединении передается имя "комнаты" конференции (берется из параметра в URL страницы клиента, или генерируется случайное имя).
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).on(ROOM_EVENT.STATE, function(room){
...
}); |
6. Получение от сервера события, описывающего статус комнаты
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){
...
}); |
7. Проверка режима 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);
});
} |
8. Публикация видеопотока.
room.publish() code
При публикации передаем div-элемент, в котором будет отображаться видео с камеры.
Code Block | ||||
---|---|---|---|---|
| ||||
room.publish({ display: display, constraints: constraints, record: false, receiveVideo: false, for (var i = 0; i < participants.length; i++receiveAudio: false }).on(STREAM_STATUS.FAILED, function (stream) { console.warn("Local installParticipant(participants[i]stream failed!"); chatState += participants[i].name(setStatus("#localStatus", stream.status()); onMediaStopped(room); if (i != participants.length - 1 }).on(STREAM_STATUS.PUBLISHING, function (stream) { setStatus("#localStatus", stream.status()); chatState += "," onMediaPublished(stream); }).on(STREAM_STATUS.UNPUBLISHED, function(stream) { } }setStatus("#localStatus", stream.status()); addMessage("chat", chatStateonMediaStopped(room); } else { addMessage("chat", " room is empty"); } publishLocalMedia(room);}); |
9. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате
RoomStatusEvent JOINED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).on(ROOM_EVENT.STATE, function(room){ onJoined(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){ ... }); |
6. Проверка режима Low Power Mode при публикации на мобильном устройстве
Flashphoner.playFirstVideo() 10. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником
RoomStatusEvent PUBLISHED code
Code Block | ||||||
---|---|---|---|---|---|---|
| if (Browser.isSafariWebRTC()) {
var display = document.getElementById("localDisplay");
Flashphoner.playFirstVideo(display, true, PRELOADER_URL).then(function()
| |||||
connection.join({name: getRoomName(), record: isRecord()}).on(ROOM_EVENT.STATE, function(room){ publishLocalMedia(room);... }).on(ROOM_EVENT.JOINED, function(participant){ ... }).catch(function (error) on(ROOM_EVENT.LEFT, function(participant){ ... }).on(ROOM_EVENT.PUBLISHED, function(participant){ console.log("Can't atomatically publish local stream, use Publish button");playParticipantsStream(participant); }).on(ROOM_EVENT.FAILED, function(room, info){ for (var i = 0; i < display.children.length; i++) { ... }).on(ROOM_EVENT.MESSAGE, function(message){ ... }); |
11. Проверка режима Low Power Mode при воспроизведении на мобильном устройстве
Flashphoner.playFirstVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
if (display.children[i]Browser.isSafariWebRTC()) { consoleFlashphoner.log("remove cached instance id " + display.children[i].id); playFirstVideo(pDisplay, false, PRELOADER_URL).then(function() { display.removeChild(display.children[i]playStream(participant, pDisplay); } }).catch(function (error) { } onMediaStopped(room); // Low Power }); } |
7. Публикация видеопотока.
room.publish() code
При публикации передаем div-элемент, в котором будет отображаться видео с камеры.
Code Block | ||||
---|---|---|---|---|
| ||||
room.publish({ Mode detected, user action is needed to start playback in this mode #WCS-2639 display: display, constraints: constraints, console.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 (streampDisplay.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) { setStatusconsole.log("#localStatus", stream.status()remove cached instance id " + pDisplay.children[i].id); onMediaStopped(room); }); |
8. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате
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){
...
}).on(ROOM_EVENT.FAILED, function(room, info){
...
}).on(ROOM_EVENT.MESSAGE, function(message){
...
}); |
9. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником
RoomStatusEvent PUBLISHED code
Code Block | ||||
---|---|---|---|---|
| ||||
connection.join({name: getRoomName(), record: isRecord()}).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){
...
}); |
10. Проверка режима Low Power Mode при воспроизведении на мобильном устройстве
Flashphoner.playFirstVideo() code
Code Block | ||||
---|---|---|---|---|
| ||||
pDisplay.removeChild(pDisplay.children[i]); } } onParticipantStopped(participant); }); } |
12. Воспроизведение видеопотока.
participant.play() code
В метод передаются следующие параметры:
- display - div-элемент, в котором будет отображаться видео;
- options.unmutePlayOnStart - параметр, разрешающий (по умолчанию) или запрещающий (например, в Android Edge) автоматическое включение звука при проигрывании
- options.constraints.audio.deviceId - устройство вывода звука (в примере указано устройство по умолчанию)
Если автоматическое включение звука запрещено, пользователь должен нажать кнопку для включения звука
Code Block | ||||
---|---|---|---|---|
| ||||
var options = { unmutePlayOnStart: true, if (Browser.isSafariWebRTC()) constraints: { audio: { Flashphoner.playFirstVideo(pDisplay, false, PRELOADER_URL).then(function() { deviceId: 'default' } playStream(participant, pDisplay); } }; // }).catch(function (error) { Leave participant stream muted in Android Edge browser #WCS-3445 if (Browser.isChromiumEdge() && Browser.isAndroid()) { // Low Power Mode detected, useroptions.unmutePlayOnStart action= isfalse; needed to start playback in this mode #WCS-2639} participant.getStreams()[0].play(display, options).on(STREAM_STATUS.PLAYING, function (playingStream) { console.log("Can't atomatically play participant" + participant.name() + " stream, use Play button");var video = document.getElementById(playingStream.id()) video.addEventListener('resize', function (event) { for (var i = 0; i < pDisplay.children.length; i++) {resizeVideo(event.target); }); // Set up participant if (pDisplay.children[i]) {Stop/Play button if (playBtn) { console.log("remove cached instance id " + pDisplay.children[i].id);$(playBtn).text("Stop").off('click').click(function() { pDisplay.removeChild(pDisplay.children[i]$(this).prop('disabled', true); playingStream.stop(); } }).prop('disabled', false); } } // Set up participant audio toggle button #WCS-3445 if onParticipantStopped(participantaudioBtn); { });$(audioBtn).text("Audio").off('click').click(function() { } |
11. Воспроизведение видеопотока.
participant.play() code
Параметром передается div-элемент, в котором будет отображаться видео.
Code Block | ||||
---|---|---|---|---|
| ||||
participant.getStreams()[0].play(display).on(STREAM_STATUS.PLAYING, function (playingStream) { if (playingStream.isRemoteAudioMuted()) { document.getElementById( playingStream.id()).addEventListener('resize', function (event) { unmuteRemoteAudio(); } else { resizeVideo(event.target); }playingStream.muteRemoteAudio(); if (button) { $(button).text("Stop").off('click').click(function(){} $(this}).prop('disabled', true);disabled', false); } // Start participant playingStream.stop();audio state checking timer #WCS-3445 })participantState.prop('disabled', falsestartMutedCheck(playingStream); } }).on(STREAM_STATUS.STOPPED, function () { onParticipantStopped(participant); }).on(STREAM_STATUS.FAILED, function () { onParticipantStopped(participant); }); |
1213. Остановка публикации видеопотока.
stream.stop() code
Code Block | ||||
---|---|---|---|---|
| ||||
function onMediaPublished(stream) { $("#localStopBtn").text("Stop").off('click').click(function(){ $(this).prop('disabled', true); stream.stop(); }).prop('disabled', false); ... } |
1314. Получение от сервера события, подтверждающего остановку публикации.
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); }); |
1415. Выход из комнаты конференции.
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); ... } |
1516. Включение/выключение аудио и видео для публикуемого потока.
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); } |
1617. Отправка текстового сообщения.
participant.sendMessage() code
При нажатии на кнопку Send
...