Потоковое видео с нескольких камер

Я работаю над проектом webRCT. Я использую функцию видеокомнаты шлюза Janus webRtc и базу данных RestAPI + Mysql для обработки сведений о камере и AngularJs для потоковой передачи видео и клиентского приложения для захвата видео.

Я создал HomeController.js для обработки деталей камеры. и janusController.js для обработки потокового видео.

Теперь в текущей ситуации я могу передавать видео с одной камеры. И несколько камер также поддерживаются, если я создаю несколько div на странице html5 вручную. но это не то, чем я хочу заниматься. Я хочу создать div на странице htlm5 с angularJs "ng-repeat" и хочу указать уникальный идентификатор для каждого div.

Вот важные части моих кодов.

HomeController.js

 $scope.getPeripheralList = function (devHardwareId){
    HomeService.getPeripheralList(devHardwareId)
    .then (function success(response){
        $scope.peripheralDetails = response.data;
        $scope.errorMessage = '';
    },
    function error(response) {
        $mdDialog.show(
            $mdDialog.alert()
              .parent(angular.element(document.querySelector('#popupContainer')))
              .clickOutsideToClose(true)
              .title('Error occured!!!!!!')
              .textContent(response.data.message)
              .ariaLabel('')
              .ok('Ok')
        );
    });
}

Ответ на вышеуказанный вызов.

[{"peripheralId":7,"perHardwareId":"Logitech HD Webcam C270","peripheralName":"Logitech HD Webcam C270","peripheralType":"Video","isActive":true,"device":{"deviceId":13,"deviceName":"DESKTOP-NJ02GI1","devHardwareId":"0A0027000007","isActive":true,"deviceIp":"192.168.2.19","creationDate":"2018-03-09T10:54:40.000+0000","lastModifiedDate":"2018-03-09T10:54:40.000+0000"}},{"peripheralId":8,"perHardwareId":"A4TECH USB2.0 PC Camera","peripheralName":"A4TECH USB2.0 PC Camera","peripheralType":"Video","isActive":true,"device":{"deviceId":13,"deviceName":"DESKTOP-NJ02GI1","devHardwareId":"0A0027000007","isActive":true,"deviceIp":"192.168.2.19","creationDate":"2018-03-09T10:54:40.000+0000","lastModifiedDate":"2018-03-09T10:54:40.000+0000"}}]

ЯнусКонтроллер.js

'use strict';


App.controller('JanusController', ['$scope', 'HomeService','uiGridConstants',"$mdDialog", function($scope, HomeService,uiGridConstants,$mdDialog) {

var server = null;
if(window.location.protocol === 'http:')
    server = "http://" + "192.168.2.10" + ":8088/janus";
else
    server = "https://" +"192.168.2.10" + ":8089/janus";

var janus = null;
var sfutest = null;
var opaqueId = "videoroomtest-"+Janus.randomString(12);

var started = false;

var myroom = 1234;  // Demo room
var myusername = null;
var myid = null;
var mystream = null;
// We use this other ID just to map our subscriptions to us
var mypvtid = null;

var feeds = [];
var bitrateTimer = [];

var doSimulcast = (getQueryStringValue("simulcast") === "no" || getQueryStringValue("simulcast") === "false");


    // Initialize the library (all console debuggers enabled)
    Janus.init({debug: "all", callback: function() {


                started = true;
                if(!Janus.isWebrtcSupported()) {
                bootbox.alert("No WebRTC support... ");
                return;
                }

                           // Create session
            janus = new Janus(
                {
                    server: server,
                    success: function() {
                        // Attach to video room test plugin
                        janus.attach( 
                            {
                                plugin: "janus.plugin.videoroom",//the unique package name of the plugin
                                opaqueId: opaqueId, // an optional opaque string meaningful to application
                                success: function(pluginHandle) { //the handle was successfully created and is ready to be used;
                                    sfutest = pluginHandle;
                                    Janus.log("Plugin attached! (" + sfutest.getPlugin() + ", id=" + sfutest.getId() + ")");
                                    Janus.log("  -- This is a publisher/manager");
                                    registerUsername()
                                    angular.element('#start').removeAttr('disabled').html("Stop")
                                        .click(function() {
                                            $(this).attr('disabled', true);
                                            janus.destroy();
                                        });
                                },

                                error: function(error) { //the handle was NOT successfully created;
                                    Janus.error("  -- Error attaching plugin...", error);
                                    bootbox.alert("Error attaching plugin... " + error);
                                },

                                mediaState: function(medium, on) {
                                    Janus.log("Janus " + (on ? "started" : "stopped") + " receiving our " + medium);
                                },

                                webrtcState: function(on) {
                                    Janus.log("Janus says our WebRTC PeerConnection is " + (on ? "up" : "down") + " now");
                                    //write codes to change bitrate here.
                                },


                                onmessage: function(msg, jsep) { //a message/event has been received from the plugin;
                                    Janus.debug(" ::: Got a message (publisher) :::");
                                    Janus.debug(msg);
                                    var event = msg["videoroom"];
                                    Janus.debug("Event: " + event);
                                    if(event != undefined && event != null) {
                                        if(event === "joined") {
                                            // Publisher/manager created, negotiate WebRTC and attach to existing feeds, if any
                                            myid = msg["id"];
                                            mypvtid = msg["private_id"];
                                            Janus.log("Successfully joined room " + msg["room"] + " with ID " + myid);
                                            publishOwnFeed(false); //not publishing a video
                                            // Any new feed to attach to?
                                            if(msg["publishers"] !== undefined && msg["publishers"] !== null) {
                                                var list = msg["publishers"];
                                                Janus.debug("Got a list of available publishers/feeds:");
                                                Janus.debug(list);
                                                for(var f in list) {
                                                    var id = list[f]["id"];
                                                    var display = list[f]["display"];
                                                    var audio = list[f]["audio_codec"];
                                                    var video = list[f]["video_codec"];
                                                    Janus.debug("  >> [" + id + "] " + display + " (audio: " + audio + ", video: " + video + ")");
                                                    newRemoteFeed(id, display, audio, video);
                                                }
                                            }
                                        } else if(event === "destroyed") {
                                            // The room has been destroyed
                                            Janus.warn("The room has been destroyed!");
                                            bootbox.alert("The room has been destroyed", function() {
                                                window.location.reload();
                                            });
                                        } else if(event === "event") {
                                            // Any new feed to attach to?
                                            if(msg["publishers"] !== undefined && msg["publishers"] !== null) {
                                                var list = msg["publishers"];
                                                Janus.debug("Got a list of available publishers/feeds:");
                                                Janus.debug(list);
                                                for(var f in list) {
                                                    var id = list[f]["id"];
                                                    var display = list[f]["display"];
                                                    var audio = list[f]["audio_codec"];
                                                    var video = list[f]["video_codec"];
                                                    Janus.debug("  >> [" + id + "] " + display + " (audio: " + audio + ", video: " + video + ")");
                                                    newRemoteFeed(id, display, audio, video);
                                                }
                                            } else if(msg["leaving"] !== undefined && msg["leaving"] !== null) {
                                                // One of the publishers has gone away?
                                                var leaving = msg["leaving"];
                                                Janus.log("Publisher left: " + leaving);
                                                var remoteFeed = null;
                                                for(var i=1; i<6; i++) {
                                                    if(feeds[i] != null && feeds[i] != undefined && feeds[i].rfid == leaving) {
                                                        remoteFeed = feeds[i];
                                                        break;
                                                    }
                                                }
                                                if(remoteFeed != null) {
                                                    Janus.debug("Feed " + remoteFeed.rfid + " (" + remoteFeed.rfdisplay + ") has left the room, detaching");
                                                    //$('#remote'+remoteFeed.rfindex).empty().hide();
                                                    $('#videoremote'+remoteFeed.rfindex).empty();
                                                    feeds[remoteFeed.rfindex] = null;
                                                    remoteFeed.detach();
                                                }
                                            } else if(msg["unpublished"] !== undefined && msg["unpublished"] !== null) {
                                                // One of the publishers has unpublished?
                                                var unpublished = msg["unpublished"];
                                                Janus.log("Publisher left: " + unpublished);
                                                if(unpublished === 'ok') {
                                                    // That's us
                                                    sfutest.hangup();
                                                    return;
                                                }
                                                var remoteFeed = null;
                                                for(var i=1; i<6; i++) {
                                                    if(feeds[i] != null && feeds[i] != undefined && feeds[i].rfid == unpublished) {
                                                        remoteFeed = feeds[i];
                                                        break;
                                                    }
                                                }
                                                if(remoteFeed != null) {
                                                    Janus.debug("Feed " + remoteFeed.rfid + " (" + remoteFeed.rfdisplay + ") has left the room, detaching");
                                                   // $('#remote'+remoteFeed.rfindex).empty().hide();
                                                    $('#videoremote'+remoteFeed.rfindex).empty();
                                                    //document.getElementById('videoremote'+remoteFeed.rfindex).empty();
                                                    feeds[remoteFeed.rfindex] = null;
                                                    remoteFeed.detach();
                                                }
                                            } 
                                            else if(msg["error"] !== undefined && msg["error"] !== null) {
                                                    bootbox.alert(msg["error_code" + "error"]);

                                            }
                                        }
                                    }
                                    if(jsep !== undefined && jsep !== null) {
                                        Janus.debug("Handling SDP as well...");
                                        Janus.debug(jsep);
                                        sfutest.handleRemoteJsep({jsep: jsep});
                                    }
                                },


                                onlocalstream: function(stream) {
                                 //we don't publish our stream here
                                },
                                onremotestream: function(stream) {
                                    // The publisher stream is sendonly, we don't expect anything here
                                },
                                oncleanup: function() {
                                   //
                                }
                            });
                    },
                    error: function(error) {
                        Janus.error(error);
                        bootbox.alert(error, function() {
                            window.location.reload();
                        });
                    },
                    destroyed: function() {
                        window.location.reload();
                    }
                }); 
            }


    });


/////////////////////////////////////////////////////////


function registerUsername() {
    var username = "stream";
        var register = { "request": "join", "room": myroom, "ptype": "publisher", "display": username };
        myusername = username;
        sfutest.send({"message": register});

}

//////////////////////////////////////////////////////////

function publishOwnFeed(useAudio) {
    // Publish our stream
    //$('#publish').attr('disabled', true).unbind('click');
    sfutest.createOffer(
        {
            // Add data:true here if you want to publish datachannels as well
            media: { audioRecv: false, videoRecv: false, audioSend: false, videoSend: false },  // Publishers are sendonly

            simulcast: doSimulcast,
            success: function(jsep) {
                Janus.debug("Got publisher SDP!");
                Janus.debug(jsep);
                var publish = { "request": "configure", "audio": useAudio, "video": true };

                sfutest.send({"message": publish, "jsep": jsep});
            },
            error: function(error) {
                Janus.error("WebRTC error:", error);
                if (useAudio) {
                     publishOwnFeed(false);
                } else {
                    bootbox.alert("WebRTC error... " + JSON.stringify(error));
                    angular.element('#publish').removeAttr('disabled').click(function() { publishOwnFeed(true); });
                }
            }
        });
}

function toggleMute() {
    //to mute
}

function unpublishOwnFeed() {
    // Unpublish our stream
}

function newRemoteFeed(id, display, audio, video) {
    // A new feed has been published, create a new plugin handle and attach to it as a listener
    var remoteFeed = null;
    janus.attach(
        {
            plugin: "janus.plugin.videoroom",
            opaqueId: opaqueId,
            success: function(pluginHandle) {
                remoteFeed = pluginHandle;
                Janus.log("Plugin attached! (" + remoteFeed.getPlugin() + ", id=" + remoteFeed.getId() + ")");
                // We wait for the plugin to send us an offer
                var listen = { "request": "join", "room": myroom, "ptype": "listener", "feed": id, "private_id": mypvtid };
                // In case you don't want to receive audio, video or data, even if the
                // publisher is sending them, set the 'offer_audio', 'offer_video' or
                // 'offer_data' properties to false (they're true by default), e.g.:
                //      listen["offer_video"] = false;
                // For example, if the publisher is VP8 and this is Safari, let's avoid video
                if(video !== "h264" && Janus.webRTCAdapter.browserDetails.browser === "safari") {
                    if(video)
                        video = video.toUpperCase()
                    toastr.warning("Publisher is using " + video + ", but Safari doesn't support it: disabling video");
                    listen["offer_video"] = false;
                }
                remoteFeed.send({"message": listen});
            },
            error: function(error) {
                Janus.error("  -- Error attaching plugin...", error);
                bootbox.alert("Error attaching plugin... " + error);
            },
            onmessage: function(msg, jsep) {
                Janus.debug(" ::: Got a message (listener) :::");
                Janus.debug(msg);
                var event = msg["videoroom"];
                Janus.debug("Event: " + event);
                if(msg["error"] !== undefined && msg["error"] !== null) {
                    bootbox.alert(msg["error"]);
                } 
                else if(event != undefined && event != null) {
                    if(event === "attached") {
                        // Subscriber created and attached
                        for(var i=1;i<6;i++) {
                            if(feeds[i] === undefined || feeds[i] === null) {
                                feeds[i] = remoteFeed;
                                remoteFeed.rfindex = i;
                                break;
                            }
                        }
                        remoteFeed.rfid = msg["id"];
                        remoteFeed.rfdisplay = msg["display"];
                        Janus.log("Successfully attached to feed " + remoteFeed.rfid + " (" + remoteFeed.rfdisplay + ") in room " + msg["room"]);
                    } else if(msg["error"] !== undefined && msg["error"] !== null) {
                        Janus.error(msg["error"]);
                    } else {
                        // What has just happened?
                    }
                }
                if(jsep !== undefined && jsep !== null) {
                    Janus.debug("Handling SDP as well...");
                    Janus.debug(jsep);
                    // Answer and attach
                    remoteFeed.createAnswer(
                        {
                            jsep: jsep,
                            // Add data:true here if you want to subscribe to datachannels as well
                            // (obviously only works if the publisher offered them in the first place)
                            media: { audioSend: false, videoSend: false },  // We want recvonly audio/video
                            success: function(jsep) {
                                Janus.debug("Got SDP!");
                                Janus.debug(jsep);
                                var body = { "request": "start", "room": myroom };
                                remoteFeed.send({"message": body, "jsep": jsep});
                            },
                            error: function(error) {
                                Janus.error("WebRTC error:", error);
                                bootbox.alert("WebRTC error... " + JSON.stringify(error));
                            }
                        });
                }
            },
            webrtcState: function(on) {
                Janus.log("Janus says this WebRTC PeerConnection (feed #" + remoteFeed.rfindex + ") is " + (on ? "up" : "down") + " now");
            },
            onlocalstream: function(stream) {
                // The subscriber stream is recvonly, we don't expect anything here
            },
            onremotestream: function(stream) {
                Janus.debug("Remote feed #" + remoteFeed.rfindex);
                if(angular.element('#remotevideo'+remoteFeed.rfindex).length > 0) {
                    // Been here already: let's see if anything changed
                    var videoTracks = stream.getVideoTracks();
                    if(videoTracks && videoTracks.length > 0 && !videoTracks[0].muted) {
                        $('#novideo'+remoteFeed.rfindex).remove();
                        if($("#remotevideo"+remoteFeed.rfindex).get(0).videoWidth)
                            $('#remotevideo'+remoteFeed.rfindex).show();
                    }
                    return;
                }
                // No remote video yet
                $('#videoremote'+remoteFeed.rfindex).append('<video class="rounded centered" id="waitingvideo' + remoteFeed.rfindex + '" width=320 height=240 />');
                $('#videoremote'+remoteFeed.rfindex).append('<video class="rounded centered relative hide" id="remotevideo' + remoteFeed.rfindex + '" width="100%" height="100%" autoplay/>');
                $('#videoremote'+remoteFeed.rfindex).append(
                    '<span class="label label-primary hide" id="curres'+remoteFeed.rfindex+'" style="position: absolute; bottom: 0px; left: 0px; margin: 15px;"></span>' +
                    '<span class="label label-info hide" id="curbitrate'+remoteFeed.rfindex+'" style="position: absolute; bottom: 0px; right: 0px; margin: 15px;"></span>');
                // Show the video, hide the spinner and show the resolution when we get a playing event
                $("#remotevideo"+remoteFeed.rfindex).bind("playing", function () {
                    if(remoteFeed.spinner !== undefined && remoteFeed.spinner !== null)
                        remoteFeed.spinner.stop();
                    remoteFeed.spinner = null;
                    $('#waitingvideo'+remoteFeed.rfindex).remove();
                    if(this.videoWidth)
                        $('#remotevideo'+remoteFeed.rfindex).removeClass('hide').show();
                    var width = this.videoWidth;
                    var height = this.videoHeight;
                    $('#curres'+remoteFeed.rfindex).removeClass('hide').text(width+'x'+height).show();
                    if(Janus.webRTCAdapter.browserDetails.browser === "firefox") {
                        // Firefox Stable has a bug: width and height are not immediately available after a playing
                        setTimeout(function() {
                            var width = $("#remotevideo"+remoteFeed.rfindex).get(0).videoWidth;
                            var height = $("#remotevideo"+remoteFeed.rfindex).get(0).videoHeight;
                            $('#curres'+remoteFeed.rfindex).removeClass('hide').text(width+'x'+height).show();
                        }, 2000);
                    }
                });
                Janus.attachMediaStream($('#remotevideo'+remoteFeed.rfindex).get(0), stream);
                var videoTracks = stream.getVideoTracks();
                if(videoTracks === null || videoTracks === undefined || videoTracks.length === 0 || videoTracks[0].muted) {
                    // No remote video
                    $('#remotevideo'+remoteFeed.rfindex).hide();
                    $('#videoremote'+remoteFeed.rfindex).append(
                        '<div id="novideo'+remoteFeed.rfindex+'" class="no-video-container">' +
                            '<i class="fa fa-video-camera fa-5 no-video-icon" style="height: 100%;"></i>' +
                            '<span class="no-video-text" style="font-size: 16px;">No remote video available</span>' +
                        '</div>');
                }
                if(Janus.webRTCAdapter.browserDetails.browser === "chrome" || Janus.webRTCAdapter.browserDetails.browser === "firefox" ||
                        Janus.webRTCAdapter.browserDetails.browser === "safari") {
                    $('#curbitrate'+remoteFeed.rfindex).removeClass('hide').show();
                    bitrateTimer[remoteFeed.rfindex] = setInterval(function() {
                        // Display updated bitrate, if supported
                        var bitrate = remoteFeed.getBitrate();
                        $('#curbitrate'+remoteFeed.rfindex).text(bitrate);
                        // Check if the resolution changed too
                        var width = $("#remotevideo"+remoteFeed.rfindex).get(0).videoWidth;
                        var height = $("#remotevideo"+remoteFeed.rfindex).get(0).videoHeight;
                        if(width > 0 && height > 0)
                            $('#curres'+remoteFeed.rfindex).removeClass('hide').text(width+'x'+height).show();
                    }, 1000);
                }
            },
            oncleanup: function() {
                Janus.log(" ::: Got a cleanup notification (remote feed " + id + ") :::");
                if(remoteFeed.spinner !== undefined && remoteFeed.spinner !== null)
                    remoteFeed.spinner.stop();
                remoteFeed.spinner = null;
                $('#remotevideo'+remoteFeed.rfindex).remove();
                $('#waitingvideo'+remoteFeed.rfindex).remove();
                $('#novideo'+remoteFeed.rfindex).remove();
                $('#curbitrate'+remoteFeed.rfindex).remove();
                $('#curres'+remoteFeed.rfindex).remove();
                if(bitrateTimer[remoteFeed.rfindex] !== null && bitrateTimer[remoteFeed.rfindex] !== null) 
                    clearInterval(bitrateTimer[remoteFeed.rfindex]);
                bitrateTimer[remoteFeed.rfindex] = null;
                remoteFeed.simulcastStarted = false;
                $('#simulcast'+remoteFeed.rfindex).remove();
            }
        });
}





}]);

из home.html

<td ng-controller="JanusController" ng-show="toggle">

                                                <div class="panel-body relative" id="videoremote1"></div>
                                                {{$parent.p.perHardwareId}}
                                            </td>

Я новичок во всех этих технологиях, и это мой университетский проект. Я должен продемонстрировать это в течение этой недели. так что мне нужна помощь от кого-то эксперта.

если я использую этот div, то он создает div с перитикулярным периферийным идентификатором.

<td ng-controller="JanusController" ng-show="toggle">

                                                <div class="panel-body relative" id="videoremote" ng-attr-id="{{$parent.p.perHardwareId}}"></div>
                                                {{$parent.p.perHardwareId}}
                                            </td>

это файл janus.js https://github.com/meetecho/janus-gateway/blob/master/html/janus.js


person Tharuka    schedule 14.03.2018    source источник
comment
код в 'home.html' будет транслировать видео правильно?   -  person Nishanth    schedule 14.03.2018
comment
Да. это работает для одной камеры @Nishanth   -  person Tharuka    schedule 14.03.2018


Ответы (2)


Если код home.html работает для вас, вы можете просто перебрать его, используя ng-repeat

<td ng-controller="JanusController" ng-show="toggle" ng-repeat="item  in peripheralDetails ">
      <div class="panel-body relative" id="videoremote_+{{item.perHardwareId}}"></div>
     {{item.perHardwareId}}
</td>`

И вы можете получить компонент, используя videoremote_{{perHardwareId}}

Например, если ваш peripheralId is 7, то идентификатор элемента будет videoremote_7

person Nishanth    schedule 14.03.2018
comment
Это не работает, потому что ng-repeat, генерирующий классы повторения внутри ‹td›, а также в JanusController.js, мы также должны учитывать переменную $remoteVideo. @Нишант - person Tharuka; 14.03.2018
comment
Идентификатор perHardware также является уникальным значением в базе данных. - person Tharuka; 14.03.2018
comment
если вы не хотите повторять классы контроллеров, держите этот ng-контроллер в table tag вместо td - person Nishanth; 14.03.2018
comment
в порядке. Вы заметили, что в JanusController есть цикл for для получения фида [i], как я могу им управлять? для поддержки большого количества данных. Я думаю, у вас есть моя проблема, этот проект будет завершен как система видеонаблюдения и будет иметь количество устройств, и каждое устройство будет иметь количество периферийных устройств. Прошу вас проверить мой ответ json в вопросе. - person Tharuka; 14.03.2018
comment
Я не могу обновить свой вопрос со всей html-страницей. этот сайт разрешает только 30000 символов только на вопрос q. @Нишант - person Tharuka; 14.03.2018
comment
Все, что вам нужно, это уникальный идентификатор для каждого div. Итак, <td ng-controller="JanusController" ng-show="toggle" ng-repeat="item in peripheralDetails "> <div class="panel-body relative" id="videoremote_+{{item.perHardwareId}}"></div> {{item.perHardwareId}} </td> даст вам уникальный идентификатор - person Nishanth; 14.03.2018
comment
Как я могу изменить JanusController для этого? @Нишант - person Tharuka; 15.03.2018
comment
поделитесь кодом контроллера Janus. Изменение контроллера Janus на пользовательскую директиву облегчит - person Nishanth; 15.03.2018

Это правильный ответ для меня.

<div class="row">
<div class="col-xs-12 col-sm-6 col-md-6" ng-controller="JanusController" ng-repeat="p in peripheralDetails track by p.perHardwareId">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><span class="glyphicon glyphicon-camera"></span> {{p.perHardwareId}}  <span class="label label-info hide" id="remote1"></span></h3>
</div>
<div class="panel-body" id="videoremote_{{p.peripheralId}}">
</div>
<div class="panel-footer">
<button id="startButton" class="btn btn-success" type="submit" ng-click="peripheralRequest(p.device.devHardwareId,p.perHardwareId,'Start')">Start</button>
<button id="stopButton"  class = "btn btn-danger" type="submit" ng-click="peripheralRequest(p.device.devHardwareId,p.perHardwareId,'Stop')">Stop</button>
</div>
</div>
</div>
</div>

И я установил идентификатор потока в JanusController.js таким же, как id="videoremote_{{p.peripheralId}}.

person Tharuka    schedule 22.03.2018