Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

Пример видеочата с отображением экрана

Данный пример может использоваться для видеочата между двумя участниками на Web Call Server с отображением экрана одного из участников.
Участник видеочата может публиковать следующие типы потоков

...

WebRTC поток с веб-камеры и одновременно с этим WebRTC поток с экрана или из окна приложения

Пример окна клиента, публикующего свой экран в браузере Chrome:

...

Пример окна клиента, получающего поток с экрана в браузере Chrome

Настройка расширения для публикации экрана браузера Chrome описана в примере Screen Sharing.

Код примера

Код данного примера находится на WCS-сервере по следующему пути:

...

Здесь host - адрес WCS-сервера.

Работа с кодом примера

Для разбора кода возьмем версию файла video-chat-and-screen-sharing.js с хешем c306c1bbf49bfcbd8e24be927ae95f63b7dbaaba 90771d4, которая находится здесь и доступна для скачивания в соответствующей сборке 2.0.5218.28.2747.

1. Инициализация API.

Flashphoner.init() код code

Code Block
languagejs
themeRDark
    try {
        Flashphoner.init({);
    } catch(e) {
       flashMediaProviderSwfLocation: '../../../../media-provider.swf',
            screenSharingExtensionId: extensionId
     $("#notifyFlash").text("Your browser doesn't support WebRTC technology needed for this example");
        return;
    });

2. Подключение к серверу.Запрос доступа к камере и микрофону

Flashphoner.roomApi.connectgetMediaAccess() код code

Code Block
languagejs
themeRDark
    connection = Flashphoner.roomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.FAILED, function(session)getMediaAccess(null, localDisplay).then(function() {
        setStatuscreateConnection('#status'url, session.status(username));
        onLeft();
    }).on(SESSION_STATUS.DISCONNECTED, catch(function(sessionerror) {
        setStatus('#status', session.status());
        onLeft(console.error("User not allowed media access: "+error);
    }).on(SESSION_STATUS.ESTABLISHED, function(session) {
  $("#failedInfo").text("User not allowed media access.  setStatus('#status', session.status()Refresh the page");
        joinRoomonLeft();
    });

3. Получение от сервера события, подтверждающего успешное соединениеConnectionStatusEvent ESTABLISHED код. Подключение к серверу.

RoomApi.connect() code

Code Block
languagejs
themeRDark
function createConnection(url, username) {
    connection = FlashphonerRoomApi.roomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.FAILED, function(session){
        setStatus('#status', session.status());...
        onLeft(});
}

4. Получение от сервера события, подтверждающего успешное соединение

ConnectionStatusEvent ESTABLISHED code

Code Block
languagejs
themeRDark
    }).connection = RoomApi.connect({urlServer: url, username: username}).on(SESSION_STATUS.DISCONNECTEDFAILED, function(session) {
        setStatus('#status', session.status());...
    }).on(SESSION_STATUS.DISCONNECTED, function(session) {
        onLeft();...
    }).on(SESSION_STATUS.ESTABLISHED, function(session) {
        setStatus('#status', session.status());
        joinRoom();
    });

45. Присоединение к комнате.

connection.join() код code

При присоединении передается имя "комнаты" конференции (берется из параметра в URL страницы клиента, или генерируется случайное имя).

Code Block
languagejs
themeRDark
    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;
        }
        room_ = room;
        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){
        installParticipant(participant);
        addMessage(participant.name(), "joined");
    }).on(ROOM_EVENT.LEFT, function(participant){
        //remove participant
        removeParticipant(participant);
        addMessage(participant.name(), "left");
    }).on(ROOM_EVENT.PUBLISHED, function(participant){
        playParticipantsStream(participant);
    }).on(ROOM_EVENT.FAILED, function(room, info){
        connection.disconnect();
        $('#failedInfo').text(info);
    }).on(ROOM_EVENT.MESSAGE, function(message){
        addMessage(message.from.name(), message.text);
    });

5. Получение от сервера события, описывающего статус комнаты

RoomStatusEvent STATE код

При получении данного события:

  • определяется количество участников конференции с помощью метода room.getParticipants(), который возвращает массив объектов participant,
  • если к конференции уже присоединилось максимально допустимое количество участников, производится выход из "комнаты" при помощи room.leave()
  • если количество участников меньше максимально допустимого, начинается публикация видеопотока
Code Block
languagejs
themeRDark
   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;
        }
        room_ = room;
        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){
        installParticipant(participant);
        addMessage(participant.name(), "joined");
    }).on(ROOM_EVENT.LEFT, function(participant){
        //remove participant
        removeParticipant(participant);
        addMessage(participant.name(), "left");
    }).on(ROOM_EVENT.PUBLISHED, function(participant){
        playParticipantsStream(participant);
    }).on(ROOM_EVENT.FAILED, function(room, info){
        connection.disconnect();
        $('#failedInfo').text(info);
    }).on(ROOM_EVENT.MESSAGE, function(message){
        addMessage(message.from.name(), message.text);
    });

...

room.publish() код

При публикации передаются параметры:

  • параметры видео: ширина, высота, количество кадров в секунду, источник (экран)
  • элемент страницы для отображения превью
Code Block
languagejs
themeRDark
 var constraints = {
        video: {
            width: parseInt($('#width').val()),
            height: parseInt($('#height').val()),
            frameRate: parseInt($('#fps').val()),
        },
        audio: $("#useMic").prop('checked')
    };
    constraints.video.type = "screen";
    if (Browser.isFirefox()){
        constraints.video.mediaSource = "screen";
    }
    room.publish({
        display: document.getElementById("preview"),
        constraints: constraints,
        name: "screenShare",
        cacheLocalResources: false
    }).on(STREAM_STATUS.FAILED, function (stream) {
        console.warn("Local stream failed!");
        onStopSharing();
    }).on(STREAM_STATUS.PUBLISHING, function (stream) {
        /*
         * User can stop sharing screen capture using Chrome "stop" button.
         * Catch onended video track event and stop publishing.
         */
        document.getElementById(stream.id()).srcObject.getVideoTracks()[0].onended = function (e) {
            stream.stop();
        };
        document.getElementById(stream.id()).addEventListener('resize', function(event){
            resizeVideo(event.target);
        });
        onStartSharing(stream);
    }).on(STREAM_STATUS.UNPUBLISHED, function(stream) {
        onStopSharing();
    });

7. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате

RoomStatusEvent JOINED код

Code Block
languagejs
themeRDark
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;
        }
        room_ = room;
        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){
        installParticipant(participant);
        addMessage(participant.name(), "joined");
    }).on(ROOM_EVENT.LEFT, function(participant){
        //remove participant
        removeParticipant(participant);
        addMessage(participant.name(), "left");
    }).on(ROOM_EVENT.PUBLISHED, function(participant){
        playParticipantsStream(participant);
    }).on(ROOM_EVENT.FAILED, function(room, info){
        connection.disconnect();
        $('#failedInfo').text(info);
    }).on(ROOM_EVENT.MESSAGE, function(message){
        addMessage(message.from.name(), message.text);
    });

...

});

6. Получение от сервера события, описывающего статус комнаты

RoomStatusEvent STATE code

При получении данного события:

  • определяется количество участников конференции с помощью метода room.getParticipants(), который возвращает массив объектов participant,
  • если к конференции уже присоединилось максимально допустимое количество участников, производится выход из "комнаты" при помощи room.leave()
  • если количество участников меньше максимально допустимого, начинается публикация видеопотока
Code Block
languagejs
themeRDark
   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;
        }
        room_ = room;
        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){
        installParticipant...
    }).on(ROOM_EVENT.LEFT, function(participant);{
        addMessage(participant.name(), "joined");...
    }).on(ROOM_EVENT.LEFTPUBLISHED, function(participant){
        //remove participant
        removeParticipant(participant); ...
    }).on(ROOM_EVENT.FAILED, function(room, info){
        addMessage(participant.name(), "left");...
    }).on(ROOM_EVENT.PUBLISHEDMESSAGE, function(participantmessage){
        playParticipantsStream(participant);...
    }).on(ROOM_EVENT.FAILED, function(room, info){
;

7. Публикация видеопотока с экрана.

room.publish() code

При публикации передаются параметры:

  • параметры видео: ширина, высота, количество кадров в секунду, источник (экран)
  • элемент страницы для отображения превью
Code Block
languagejs
themeRDark
    var constraints =  connection.disconnect();
{
         $('#failedInfo').text(info);video: {
    }).on(ROOM_EVENT.MESSAGE, function(message){
       width: addMessage(message.from.name(), message.text);
    });

...

participant.play() код

Параметром передается div-элемент, в котором будет отображаться видео, в зависимости от источника - веб-камера или экран

Code Block
languagejs
themeRDark
function playParticipantsStream(participant) {
    if (participant.getStreams().length > 0) {
parseInt($('#width').val()),
            height: parseInt($('#height').val()),
            forframeRate: (var i=0; i<participant.getStreams().length; i++) {
  parseInt($('#fps').val()),
          $("[id$=Name]").each(function (index, value) { withoutExtension: true
        },
        ifaudio: ($(value"#useMic").textprop() == participant.name()) {'checked')
    };
    constraints.video.type = "screen";
    if (Browser.isFirefox()){
     var p = valueconstraints.id.replace('Name', '')video.mediaSource = "screen";
    }
    var options = {
         var pDisplay = p + 'Display';
name: "screenShare",
        display: document.getElementById("preview"),
        constraints: constraints,
        // check if we already play this stream
   cacheLocalResources: false
    }
    if (isSafariMacOS()) {
        options.disableConstraintsNormalization = true;
    }
   if (documentroom.getElementByIdpublish(participantoptions).getStreams()[i].id()) == nullon(STREAM_STATUS.FAILED, function (stream) {
        ...
         });

8. Получение от сервера события, сигнализирующего о присоединении пользователя к чат-комнате

RoomStatusEvent JOINED code

Code Block
languagejs
themeRDark
    connection.join({name: getRoomName()}).on(ROOM_EVENT.STATE, function(room){
    // setup 1st stream to main div ...
    }).on(ROOM_EVENT.JOINED, function(participant){
        installParticipant(participant);
           if (participant.getStreams()[i].streamName().indexOf("screenShare") == -1) addMessage(participant.name(), "joined");
    }).on(ROOM_EVENT.LEFT, function(participant){
        ...
    }).on(ROOM_EVENT.PUBLISHED, function(participant){
            ...
   participant.getStreams()[i].play(document.getElementById(pDisplay) }).on(STREAMROOM_STATUSEVENT.PLAYINGFAILED, function(room, (playingStreaminfo) {
        ...
    }).on(ROOM_EVENT.MESSAGE, function(message){
        ...
      });

9. Получение от сервера события, сигнализирующего о публикации видеопотока другим участником

RoomStatusEvent PUBLISHED code

Code Block
languagejs
themeRDark
     documentconnection.getElementById(playingStream.idjoin({name: getRoomName()}).addEventListener('resize'on(ROOM_EVENT.STATE, function (eventroom) {
        ...
                            resizeVideo(event.target);
       }).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. Воспроизведение видеопотока от другого участника

participant.play() code

Параметром передается div-элемент, в котором будет отображаться видео, в зависимости от источника - веб-камера или экран

Code Block
languagejs
themeRDark
function playParticipantsStream(participant) {
    if (participant.getStreams().length > }0) else {
        for (var i=0; i<participant.getStreams().length; i++) {
               participant.getStreams()[i].play(document.getElementById("sharedDisplay")).on(STREAM_STATUS.PLAYING, function (playingStream$("[id$=Name]").each(function (index, value) {
                if ($(value).text() == participant.name()) {
                document.getElementById(playingStream.id()).addEventListener('resize', function (event) {
    var p = value.id.replace('Name', '');
                    var pDisplay = p + 'Display';
               resizeVideo(event.target);
     // check if we already play this stream
                     });if (document.getElementById(participant.getStreams()[i].id()) == null) {
                        // setup 1st stream  });to main div
                        }
    if (participant.getStreams()[i].streamName().indexOf("screenShare") == -1) {
                }
            participant.getStreams()[i].play(document.getElementById(pDisplay)).on(STREAM_STATUS.PLAYING, function (playingStream)  }{
            });
        }
    }
}

10. Остановка публикации видеопотока.

stream.stop() код

Code Block
languagejs
themeRDark
function onMediaPublished(stream) {
                $("#localStopBtn").text("Stop").off('click').click(function(){
document.getElementById(playingStream.id()).addEventListener('resize', function (event) {
                 $(this).prop('disabled', true);
        stream.stop();
    }).prop('disabled', false);
    $("#localAudioToggle").text("Mute A").off('click').click(function(){ resizeVideo(event.target);
        if (stream.isAudioMuted()) {
            $(this).text("Mute A");
            stream.unmuteAudio(});
             } else {
            $(this).text("Unmute A" });
            stream.muteAudio();
        }
    }).prop('disabled', false); else {
    $("#localVideoToggle").text("Mute V").off('click').click(function() {
        if (stream.isVideoMuted()) {
            $participant.getStreams(this).text[i].play(document.getElementById("Mute V");
  sharedDisplay")).on(STREAM_STATUS.PLAYING, function (playingStream) {
          stream.unmuteVideo();
        } else {
            $(thisdocument.getElementById(playingStream.id()).text("Unmute V");
   addEventListener('resize', function (event) {
         stream.muteVideo();
        }
    }).prop('disabled',false);
    $("#shareBtn").prop('disabled',false);
}

11. Получение от сервера события, подтверждающего остановку публикации.

StreamStatusEvent UNPUBLISHED код

Code Block
languagejs
themeRDark
 room.publish({
              display: document.getElementById("preview"),resizeVideo(event.target);
        constraints:  constraints,
        name: "screenShare",
        cacheLocalResources: false
    }).on(STREAM_STATUS.FAILED, function (stream) {
;
              console.warn("Local stream failed!");
        onStopSharing();
    }).on(STREAM_STATUS.PUBLISHING, function (stream) {
;
            /*
         * User can stop}
 sharing screen capture using Chrome "stop" button.
         * Catch onended video track}
 event and stop publishing.
         */
   }
     document.getElementById(stream.id()).srcObject.getVideoTracks()[0].onended = function (e) {       });
        }
    }
}

11. Остановка публикации видеопотока с экрана

stream.stop()

...

code

Code Block
languagejs
themeRDark
    room.publish(options).on(STREAM_STATUS.FAILED, function (stream) {
     };
   ...
     document}).getElementByIdon(stream.id()).addEventListener('resize'STREAM_STATUS.PUBLISHING, function (eventstream) {
        /*
    resizeVideo(event.target);
     * User can });
stop sharing screen capture using Chrome   onStartSharing(stream);"stop" button.
    }).on(STREAM_STATUS.UNPUBLISHED, function(stream) {
    * Catch onended video onStopSharing();
track event and stop });

12. Выход из комнаты чата.

room.leave() код

Code Block
languagejs
themeRDark
function onJoined(room) {
    $("#joinBtn").text("Leave").off('click').click(function(){publishing.
         */
        $(this).prop('disabled', true);
document.getElementById(stream.id()).srcObject.getVideoTracks()[0].onended = function (e) {
            roomstream.leavestop().then(onLeft, onLeft);
       }).prop('disabled', false) };
    $("#shareBtn").prop('disabled',false);    ...
    $('#sendMessageBtn'}).off('click').click(function(){
on(STREAM_STATUS.UNPUBLISHED, function(stream) {
         var message = field('message');
        addMessage(connection.username(), message); ...
    });

12. Получение от сервера события, подтверждающего остановку публикации.

StreamStatusEvent UNPUBLISHED code

Code Block
languagejs
themeRDark
    room.publish(options).on(STREAM_STATUS.FAILED, function (stream) {
        $('#message').val("");...
    }).on(STREAM_STATUS.PUBLISHING, function   //broadcast message
(stream) {
        ...
 var participants = room.getParticipants(); }).on(STREAM_STATUS.UNPUBLISHED, function(stream) {
        for (var i = 0; i < participants.length; i++onStopSharing();
    });

13. Выход из комнаты чата.

room.leave() code

Code Block
languagejs
themeRDark
function onJoined(room) {
    $("#joinBtn").text("Leave").off('click').click(function(){
        participants[i].sendMessage(message$(this).prop('disabled', true);
        } room.leave().then(onLeft, onLeft);
    }).prop('disabled', false);
    $('#failedInfo').text("");...
}