javascript - Multi Party - Video Conference with peerJS - Stack Overflow

I tried webRTC video conference with peerJS and succeeded in one-to-one conference, i need some assista

I tried webRTC video conference with peerJS and succeeded in one-to-one conference, i need some assistance to take forward this for multiparty video conference, here i try to explain what i did please assist from where i struck

here i am posting my full code so that anyone can use by modifying few info,

INTENSION

  1. Alice login and calls to Bob
  2. Bob Joins Alice Call
  3. Alice calls Carole (using Add Call Button)
  4. Carole Joins Alice Call
  5. Create Conference between Alice, Bob and Carole(using Join Call button)

achived

  1. Alice login and calls to Bob
  2. Bob Joins Alice Call
  3. Alice calls Carole
  4. Carole Joins Alice

mycase

  • Signup and created Alice.
  • Alice logins. goes to CALLS using navigation.
  • creates Bob and Carole using Add contact.
  • Goes to Phone book made call to BOB.
  • Bob joined Alice.
  • Alice made another call to Carole using add call.
  • Carole joins Alice.
  • Alice has bob and carole where bob and carole missing each other.
  • yet to merge their streams!

PENDING & need Assistance

  1. Create Conference between Alice, Bob and Carole when i click on join conference bob and carole too should have alice

how to make conference between Alice, Bob and Carole ?

/*-----------------
    indexedDB
------------------*/
var iDB;
const openiDB = indexedDB.open('webRTC',1)
openiDB.onupgradeneeded = function(){
    iDB = openiDB.result
    if(!iDB.objectStoreNames.contains('contacts')){
            iDB.createObjectStore('contacts',{keyPath:'mobile',unique:true})
    }
    if(!iDB.objectStoreNames.contains('callLog')){
        iDB.createObjectStore('callLog',{keyPath:'id',autoIncrement:true})
    }
    if(!iDB.objectStoreNames.contains('chatLog')){
        iDB.createObjectStore('chatLog',{keyPath:'mobile'})
    }
}
openiDB.onsuccess = function(){
    iDB = openiDB.result
}
openiDB.onerror = function(){
    console.log(openiDB.error)
}

/*-----------------
    jQuery
------------------*/
$(document).ready(function(){
    /*-----------------
        functions
    ------------------*/
    function gIndex(){
        $("body").append(
            $("<footer>").text("2020 All rights reserved to SIKANDAR")
        )
    }
    function uIndex(){
        $("main").empty().append(
            $("<div>").attr('class','error'),
            $("<article>").attr('class','welCome').append(
                $("<h2>").attr('class','aH2').text("Hello "+user.fName),
                $("<aside>").attr('class','msg')
            ),
            $("<nav>").append(
                $("<span>").attr({'class':'navIcon','id':'meetings'}).text("MEETINGS"),
                $("<span>").attr({'class':'navIcon','id':'calls'}).text("CALLS"),
 $("<span>").attr({'id':'logo'}).text("HOME"),
                $("<span>").attr({'class':'navIcon','id':'chats'}).text("CHATS"),
                $("<span>").attr({'class':'navIcon','id':'settings'}).text("MENU")
            ),
            $("<section>").attr('class','modal').append(
                $("<span>").attr('class','mClose').text("X"),
                $("<div>").attr('class','mWrap').append(
                    $("<aside>").attr('class','contacts').append(
                        $("<div>").attr('class','formContact'),
                        $("<div>").attr('class','showContacts')
                    ),
                    $("<aside>").attr('class','players').append(
                        $("<div>").attr('class','row').append(
                            $("<video playsinline muted>").attr({'controls':false,'id':'lCam','preload':'none'})
                        ),
                        $("<div>").attr('class','row'),
                        $("<div>").attr('class','row2').append(
                            $("<button>").attr('id','aCall').text("ADD"),
                            $("<button>").attr('id','jCall').text("JOIN").prop('disabled',true),
                            $("<button>").attr('id','eCall').text("END")
                        )
                    )
                ),
            ),
            $("<section>").attr('class','iScreen').append()
        )
        $('.modal,.contacts,.players,.iScreen,.error').hide()
    }
    function lCamCheck(cT,iCall){
        let lCam = $("#lCam")[0]
        switch(cT){
            case 'ining':{
                switch(lCam.srcObject){
                    case null:{
                        navigator.mediaDevices.getUserMedia(constraints).then(stream=>{
                            lCam.srcObject = stream;
                            iCall.answer(stream)
                            lCam.play()
                        }).catch(error=>console.error(error))
                        break;
                    }
                    default:{
                        iCall.answer(lCam.srcObject)
                        break;
                    }
                }
                break;
            }
            case 'outgoing':{
                switch(lCam.srcObject){
                    case null:{
                        navigator.mediaDevices.getUserMedia(constraints).then(stream=>{
                            lCam.srcObject = stream;
                            let outGoing = socket.call(iCall,stream)
                            lCam.play()
                            outGoing.on('stream',rStream=>{
                                rCamCheck(iCall,rStream)
                                $("button[value='"+iCall+"']").prop('disabled',true)
                                $(".players").show()
                            },err=>console.error(err))
                            addLog(iCall,'outgoing')
                        }).catch(error=>console.error(error))
                        break;
                    }
                    default:{
                        let outGoing = socket.call(iCall,lCam.srcObject)
                        lCam.play()
                        outGoing.on('stream',rStream=>{
                            rCamCheck(iCall,rStream)
                            $("button[value='"+iCall+"']").prop('disabled',true)
                        },err=>console.error(err))
                        addLog(iCall,'outgoing')
                        break;
                    }
                }
                break;
            }
        }
    }
    function rCamCheck(m,rS){
        let rCam = $("video[name='"+m+"']")[0]
        if(!rCam){
            $(".players .row:nth-child(2)").append(
                $("<video playinline>").attr({'class':'rCam','name':m,'controls':false,'preload':'none'})
            )
            $("video[name='"+m+"']")[0].srcObject = rS
            $("video[name='"+m+"']")[0].play()
        }else{ // function is repeating two times hence writing else
            
            $("video[name='"+m+"']")[0].pause()
            //$("video[name='"+m+"']")[0].srcObject = rS
            $("video[name='"+m+"']")[0].play()
            console.log(socket)
        }
    }
    function addLog(m,t){
        let dt = new Date()
        let log = {
            'date' : dt.toLocaleDateString('en-CA'),
            'time' : dt.toLocaleTimeString('en-CA',{hour12:false}),
            'mobile': m,
            'type' : t
        }
        let addLog = iDB.transaction(["callLog"],'readwrite').objectStore("callLog").add(log)
        addLog.onerror = function(){
            console.error(addLog.error)
        }
    }
    function showLog(){
        let sLog = iDB.transaction(["callLog"]).objectStore("callLog").getAll()
        sLog.onsuccess = function(){
            if(sLog.result.length>0){
                $(".callLog").empty().append(
                    $("<div>").attr('class','row1').append(
                        $("<span>").attr('class','cSpan').text("DATE"),
                        $("<span>").attr('class','cSpan').text("TIME"),
                        $("<span>").attr('class','cSpan').text("MOBILE"),
                        $("<span>").attr('class','cSpan').text("TYPE")
                    )
                )
                $.each(sLog.result,function(k,v){
                    $("<div>").attr('class','row1').append(
                        $("<span>").attr('class','cSpan').text(v.date),
                        $("<span>").attr('class','cSpan').text(v.time),
                        $("<span>").attr('class','cSpan').text(v.mobile),
                        $("<span>").attr('class','cSpan').text(v.type)
                    ).appendTo(".callLog")
                })
            }else{
                $(".callLog").empty().append(
                    $("<div>").attr('class','row1').append(
                        $("<span>").attr('class','cSpan').text("DATE"),
                        $("<span>").attr('class','cSpan').text("TIME"),
                        $("<span>").attr('class','cSpan').text("MOBILE"),
                        $("<span>").attr('class','cSpan').text("TYPE")
                    ),
                    $("<div>").attr('class','row').text("No Call Records")
                )
            }
        }
        sLog.onerror = function(){
            console.log(sLog.error)
        }
    }
    function phBook(){
        let lCNT = iDB.transaction(["contacts"]).objectStore("contacts").getAll()
        lCNT.onsuccess = function(){
            $(".showContacts").empty().append(
                $("<div>").attr('class','row2').append(
                    $("<span>").text("DP"),$("<span>").text("NAME"),$("<span>").text("CALL")
                )
            )
            if(lCNT.result.length>0){
                $.each(lCNT.result,function(k,v){
                    $(".showContacts").append(
                        $("<div>").attr('class','row2').append(
                            $("<img>").attr({'alt':'DP'}),
                            $("<span>").text(v.fName),
                            $("<button>").attr({'value':v.mobile,'class':'vCall'}).text("CALL")
                        )
                    )
                })
            }else{
                $(".showContacts").append(
                    $("<div>").attr('class','row').text("No Contacts")
                )
            }
        }
        lCNT.onerror = function(){
            console.error(lCNT.error)
        }
    }
    function showLogin(){
        $("main").append(
            $("<article>").append(
                $("<aside>").attr('class','login').append(
                    $("<fieldset>").append(
                        $("<legend>").text("User Login"),
                        $("<form>").attr('id','formLogin').append( 
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'text','name':'lMobile','placeholder':'Enter Mobile Number',
                                    'pattern':'[0-9]{10}','title':'Enter 10 digit mobilenumbers without country code'
                                }).prop('required',true),
                                $("<label>").attr('for','lMobile').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({'type':'submit','value':'LOGIN'}),
                                $("<input>").attr({'type':'button','name':'signUP','value':'SIGNUP'})
                            )
                        )
                    )
                ),
                $("<aside>").attr('class','signup').append(
                    $("<fieldset>").append(
                        $("<legend>").text("User Registration"),
                        $("<form>").attr('id','formSignup').append(
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'text','name':'fName','placeholder':'Enter First Name'
                                }).prop('required',true),
                                $("<label>").attr('for','fName').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'text','name':'lName','placeholder':'Enter Last Name'
                                }).prop('required',true),
                                $("<label>").attr('for','lName').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({'type':'email','name':'email','placeholder':'Enter e-Mail Id'}),
                                $("<label>").attr('for','email').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'tel','name':'mobile','placeholder':'Enter Mobile Number',
                                    'pattern':'[0-9]{10}','title':'Enter 10 digit mobile number with out country code'
                                }).prop('required',true),
                                $("<label>").attr('for','mobile').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({'type':'submit','value':'REGISTER'}),
                                $("<input>").attr({'type':'reset','value':'reset'})
                            )
                        )   
                    )
                )
            )
        )
        $(".signup").hide()
    }

    /*-----------------
        appShell
    ------------------*/
    let user;let uAuth = localStorage.getItem('uAuth');
    if(typeof(user)=="undefined"){
        user = JSON.parse(localStorage.getItem('user'))
    }
    let iceServers = {
        'iceServers':[{'urls':'stun:stun.l.google:19302'},{'urls':'stun:stun1.l.google:19302'}]
    }
    //console.log(navigator.mediaDevices.getSupportedConstraints())
    let constraints = {
        'video':{'width':640,'height':480,'frameRate':{'max':24}},
        'audio':{'echoCancellation':true,'noiseSuppression':true,'sampleSize':8}
    }
    let socket; let peerJS = {'host':'localhost','port':9000,'path':'/myapp','config':iceServers};

    switch(uAuth){
        case null:{
            gIndex();
            showLogin()
            break;
        }
        case 'true':{
            uIndex()
            socket = new Peer(user.mobile,peerJS)
            socket.on('open',function(){
                console.log(socket.id+" Connceted")
            })
            socket.on('error',function(error){
                $(".error").text(error.message).show()
                //$(".aH1").css('color','firebrick')
                let lCam = $("#lCam")[0];let rCams = $("video")
                lCam.srcObject.getTracks().forEach(track=>{track.stop();$(".modal").hide()})
                if(rCams.length>1){
                    let rCam = $("video")
                    rCam[1].srcObject.getTracks().forEach(track=>track.stop())
                }
            })
            socket.on('call',async function(inComing){
                $('.iScreen').show()
                await lCamCheck('ining',inComing)
                await phBook()
                await inComing.on('stream',rStream=>{
                    rCamCheck(inComing.peer,rStream)
                    $(".modal,.players").show()
                    $("button[value='"+inComing.peer+"']").prop('disabled',true)
                },err=>console.error(err))
                await addLog(inComing.peer,'ining')
            })
        }
    }

    $(document).on('submit','form',function(e){
        e.preventDefault()
    })
    $(document).on('click','input[name="signUP"]',function(){
        $(".login").hide(),
        $(".signup").show()
    })
    $(document).on('submit','#formSignup',async function(){
        let nUser = await {
            fName : $("input[name='fName']").val(),
            lName : $("input[name='lName']").val(),
            email : $("input[name='email']").val(),
            mobile : $("input[name='mobile']").val()
        }
        await localStorage.setItem('user',JSON.stringify(nUser))
        location.reload()
    })
    $(document).on('submit','#formLogin',function(){
        let mobile = $("input[name='lMobile']").val();
        switch(user){
            case null:{
                $("input[name='lMobile']").val("")
                $("label[for='lMobile']").text(" please signup").css('color','firebrick')
                break;
            }
            default:{
                if(mobile == user.mobile){
                    localStorage.setItem('uAuth',true)
                    location.reload()
                }else{
                    $("input[name='lMobile']").val("")
                    $("label[for='lMobile']").text(" re-check mobile").css('color','firebrick')
                }
            }
        }
        
    })
    $(document).on('click','.navIcon',function(){
        let text = $(this).attr('id')
        $("article").remove()
        $("<article>").empty().attr("class",text).append(
            $("<h2>").attr('class','aH2').text(text.toUpperCase())
        ).insertBefore("nav")
    })
    $(document).on('click','#calls',function(){
        $(".calls").append(
            $("<aside>").append(
                $("<div>").attr('class','row2').append(
                    $("<img>").attr({
                        'alt':'addCNT','src':'assets/addContact.svg','type':'image/svg+xml','id':'aCNT','class':'cOPT'
                    }),
                    $("<img>").attr({
                        'alt':'phBook','src':'assets/phBook.svg','type':'image/svg+xml','id':'phBook','class':'cOPT'
                    })
                )
            ),
            $("<aside>").attr('class','callLog').append(
                $("<h3>").attr('class','aH3')
            )
        )
        showLog()
    })
    $(document).on('click','#aCNT',function(){
        $(".showContacts").hide()
        $(".formContact").empty().append(
            $("<fieldset>").append(
                $("<legend>").text("ADD CONTACT"),
                $("<form>").attr('id','formCNT').append(
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({
                            'type':'text','name':'fName','placeholder':'Enter First Name'
                        }).prop('required',true),
                        $("<label>").attr('for','fName')
                    ),
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({
                            'type':'text','name':'lName','placeholder':'Enter Last Name'
                        }).prop('required',true),
                        $("<label>").attr('for','lName')
                    ),
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({
                            'type':'tel','name':'mobile','placeholder':'Enter Mobile Number',
                            'pattern':'[0-9]{10}','title':'Enter 10 digit mobile number with out country code'
                        }).prop('required',true),
                        $("<label>").attr('for','mobile')
                    ),
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({'type':'submit','value':'ADD'}),
                        $("<input>").attr({'type':'reset','value':'reset'})
                    )
                )
            )
        )
        $(".modal,.contacts").show()
    })
    $(document).on('submit','#formCNT',function(){
        let contact = {
            fName: $("input[name='fName']").val(),
            lName: $("input[name='lName']").val(),
            mobile: $("input[name='mobile']").val()
        }
        let aCNT = iDB.transaction(["contacts"],'readwrite').objectStore("contacts").put(contact)
        aCNT.onsuccess = function(){
            $(".formContact").empty()
            $(".modal,.contacts").hide()
        }
        aCNT.onerror = function(){

        }
    })
    $(document).on('click','#phBook',function(){
        $(".modal,.contacts,.showContacts").show()
        phBook()
    })
    $(document).on('click','.mClose',function(){
        $(".modal").hide()
    })
    $(document).on('click','.vCall',function(){
        $(".contacts").hide()
        lCamCheck('outgoing',$(this).val())
        //check rcam from here if possible
    })
    $(document).on('click','#aCall',function(){
        $(".contacts").toggle()
    })
    $(document).on('click','#eCall',function(){
        location.reload(true)
    })
})
html,body{margin: 0;padding: 0;background-color: silver;}
header{background-color: teal;text-align: center;box-shadow: 0 10px 8px black;}
.aH1{margin: auto;}            
.aH2{margin: auto 10px;}
main{display: flex; flex-direction: column;margin-top: 20px;}
article{display: flex;flex-direction: column;}
aside{display: flex;flex-direction: column;padding: 5px;}
.row{display: flex; flex-direction: row;justify-content: center;margin:5px;}
.row>input{margin:5px}
.row1{display: flex;justify-content: space-around;}
.row2{display: flex;justify-content: space-between;margin:5px 0}
.cOPT{height:5vh}
.error{border-radius: 4px;border:2px dashed firebrick; background-color: rgba(0,0,0,0.6);color: white;text-align: center;}
nav{display: flex;align-items: center;position: fixed;
    bottom: 0;width: 100vw;justify-content: space-around;background-color: teal;border-top: 1px solid black;}
.navIcon{height:5.4vh}
video{width:30vw;transform: scaleX(-1);-webkit-transform: scaleX(-1);transform: rotateZ(1turn);-webkit-transform: rotateZ(1turn);}
.rCam{margin:5px}
footer{display: flex; justify-content: center;position: fixed;bottom: 0;width: 100vw;}
.modal{position: fixed;top: 0;left: 0;width: 100vw;display: flex;flex-direction: column;padding: 0.5%;
                background-color: rgba(0,0,0,0.6);height: 100vh;z-index: 1;justify-content: flex-start}
.mClose{color: red;text-align: right;cursor: default;font-size: 1.5em;}
.mWrap{color:white;overflow-y: scroll;flex-direction: column;width:99vw;margin-bottom:5.4vh}
.players>div:nth-child(3){flex-flow:row-wrap;}
<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="google" content="notranslate">
        <meta name="description" content="webRTC video social networking">
        <meta name="author" content="Sikandar">
        <script type="text/javascript" src="js/jquery-3.5.1.min.js"></script>
        <script type="text/javascript" src="js/peerjs.min.js"></script>
        <title>webRTC</title>
    </head>
    <body>
        <header><h1 class="aH1">webRTC</h1></header>
        <main></main>
    </body>
</html>

I tried webRTC video conference with peerJS and succeeded in one-to-one conference, i need some assistance to take forward this for multiparty video conference, here i try to explain what i did please assist from where i struck

here i am posting my full code so that anyone can use by modifying few info,

INTENSION

  1. Alice login and calls to Bob
  2. Bob Joins Alice Call
  3. Alice calls Carole (using Add Call Button)
  4. Carole Joins Alice Call
  5. Create Conference between Alice, Bob and Carole(using Join Call button)

achived

  1. Alice login and calls to Bob
  2. Bob Joins Alice Call
  3. Alice calls Carole
  4. Carole Joins Alice

mycase

  • Signup and created Alice.
  • Alice logins. goes to CALLS using navigation.
  • creates Bob and Carole using Add contact.
  • Goes to Phone book made call to BOB.
  • Bob joined Alice.
  • Alice made another call to Carole using add call.
  • Carole joins Alice.
  • Alice has bob and carole where bob and carole missing each other.
  • yet to merge their streams!

PENDING & need Assistance

  1. Create Conference between Alice, Bob and Carole when i click on join conference bob and carole too should have alice

how to make conference between Alice, Bob and Carole ?

/*-----------------
    indexedDB
------------------*/
var iDB;
const openiDB = indexedDB.open('webRTC',1)
openiDB.onupgradeneeded = function(){
    iDB = openiDB.result
    if(!iDB.objectStoreNames.contains('contacts')){
            iDB.createObjectStore('contacts',{keyPath:'mobile',unique:true})
    }
    if(!iDB.objectStoreNames.contains('callLog')){
        iDB.createObjectStore('callLog',{keyPath:'id',autoIncrement:true})
    }
    if(!iDB.objectStoreNames.contains('chatLog')){
        iDB.createObjectStore('chatLog',{keyPath:'mobile'})
    }
}
openiDB.onsuccess = function(){
    iDB = openiDB.result
}
openiDB.onerror = function(){
    console.log(openiDB.error)
}

/*-----------------
    jQuery
------------------*/
$(document).ready(function(){
    /*-----------------
        functions
    ------------------*/
    function gIndex(){
        $("body").append(
            $("<footer>").text("2020 All rights reserved to SIKANDAR")
        )
    }
    function uIndex(){
        $("main").empty().append(
            $("<div>").attr('class','error'),
            $("<article>").attr('class','welCome').append(
                $("<h2>").attr('class','aH2').text("Hello "+user.fName),
                $("<aside>").attr('class','msg')
            ),
            $("<nav>").append(
                $("<span>").attr({'class':'navIcon','id':'meetings'}).text("MEETINGS"),
                $("<span>").attr({'class':'navIcon','id':'calls'}).text("CALLS"),
 $("<span>").attr({'id':'logo'}).text("HOME"),
                $("<span>").attr({'class':'navIcon','id':'chats'}).text("CHATS"),
                $("<span>").attr({'class':'navIcon','id':'settings'}).text("MENU")
            ),
            $("<section>").attr('class','modal').append(
                $("<span>").attr('class','mClose').text("X"),
                $("<div>").attr('class','mWrap').append(
                    $("<aside>").attr('class','contacts').append(
                        $("<div>").attr('class','formContact'),
                        $("<div>").attr('class','showContacts')
                    ),
                    $("<aside>").attr('class','players').append(
                        $("<div>").attr('class','row').append(
                            $("<video playsinline muted>").attr({'controls':false,'id':'lCam','preload':'none'})
                        ),
                        $("<div>").attr('class','row'),
                        $("<div>").attr('class','row2').append(
                            $("<button>").attr('id','aCall').text("ADD"),
                            $("<button>").attr('id','jCall').text("JOIN").prop('disabled',true),
                            $("<button>").attr('id','eCall').text("END")
                        )
                    )
                ),
            ),
            $("<section>").attr('class','iScreen').append()
        )
        $('.modal,.contacts,.players,.iScreen,.error').hide()
    }
    function lCamCheck(cT,iCall){
        let lCam = $("#lCam")[0]
        switch(cT){
            case 'ining':{
                switch(lCam.srcObject){
                    case null:{
                        navigator.mediaDevices.getUserMedia(constraints).then(stream=>{
                            lCam.srcObject = stream;
                            iCall.answer(stream)
                            lCam.play()
                        }).catch(error=>console.error(error))
                        break;
                    }
                    default:{
                        iCall.answer(lCam.srcObject)
                        break;
                    }
                }
                break;
            }
            case 'outgoing':{
                switch(lCam.srcObject){
                    case null:{
                        navigator.mediaDevices.getUserMedia(constraints).then(stream=>{
                            lCam.srcObject = stream;
                            let outGoing = socket.call(iCall,stream)
                            lCam.play()
                            outGoing.on('stream',rStream=>{
                                rCamCheck(iCall,rStream)
                                $("button[value='"+iCall+"']").prop('disabled',true)
                                $(".players").show()
                            },err=>console.error(err))
                            addLog(iCall,'outgoing')
                        }).catch(error=>console.error(error))
                        break;
                    }
                    default:{
                        let outGoing = socket.call(iCall,lCam.srcObject)
                        lCam.play()
                        outGoing.on('stream',rStream=>{
                            rCamCheck(iCall,rStream)
                            $("button[value='"+iCall+"']").prop('disabled',true)
                        },err=>console.error(err))
                        addLog(iCall,'outgoing')
                        break;
                    }
                }
                break;
            }
        }
    }
    function rCamCheck(m,rS){
        let rCam = $("video[name='"+m+"']")[0]
        if(!rCam){
            $(".players .row:nth-child(2)").append(
                $("<video playinline>").attr({'class':'rCam','name':m,'controls':false,'preload':'none'})
            )
            $("video[name='"+m+"']")[0].srcObject = rS
            $("video[name='"+m+"']")[0].play()
        }else{ // function is repeating two times hence writing else
            
            $("video[name='"+m+"']")[0].pause()
            //$("video[name='"+m+"']")[0].srcObject = rS
            $("video[name='"+m+"']")[0].play()
            console.log(socket)
        }
    }
    function addLog(m,t){
        let dt = new Date()
        let log = {
            'date' : dt.toLocaleDateString('en-CA'),
            'time' : dt.toLocaleTimeString('en-CA',{hour12:false}),
            'mobile': m,
            'type' : t
        }
        let addLog = iDB.transaction(["callLog"],'readwrite').objectStore("callLog").add(log)
        addLog.onerror = function(){
            console.error(addLog.error)
        }
    }
    function showLog(){
        let sLog = iDB.transaction(["callLog"]).objectStore("callLog").getAll()
        sLog.onsuccess = function(){
            if(sLog.result.length>0){
                $(".callLog").empty().append(
                    $("<div>").attr('class','row1').append(
                        $("<span>").attr('class','cSpan').text("DATE"),
                        $("<span>").attr('class','cSpan').text("TIME"),
                        $("<span>").attr('class','cSpan').text("MOBILE"),
                        $("<span>").attr('class','cSpan').text("TYPE")
                    )
                )
                $.each(sLog.result,function(k,v){
                    $("<div>").attr('class','row1').append(
                        $("<span>").attr('class','cSpan').text(v.date),
                        $("<span>").attr('class','cSpan').text(v.time),
                        $("<span>").attr('class','cSpan').text(v.mobile),
                        $("<span>").attr('class','cSpan').text(v.type)
                    ).appendTo(".callLog")
                })
            }else{
                $(".callLog").empty().append(
                    $("<div>").attr('class','row1').append(
                        $("<span>").attr('class','cSpan').text("DATE"),
                        $("<span>").attr('class','cSpan').text("TIME"),
                        $("<span>").attr('class','cSpan').text("MOBILE"),
                        $("<span>").attr('class','cSpan').text("TYPE")
                    ),
                    $("<div>").attr('class','row').text("No Call Records")
                )
            }
        }
        sLog.onerror = function(){
            console.log(sLog.error)
        }
    }
    function phBook(){
        let lCNT = iDB.transaction(["contacts"]).objectStore("contacts").getAll()
        lCNT.onsuccess = function(){
            $(".showContacts").empty().append(
                $("<div>").attr('class','row2').append(
                    $("<span>").text("DP"),$("<span>").text("NAME"),$("<span>").text("CALL")
                )
            )
            if(lCNT.result.length>0){
                $.each(lCNT.result,function(k,v){
                    $(".showContacts").append(
                        $("<div>").attr('class','row2').append(
                            $("<img>").attr({'alt':'DP'}),
                            $("<span>").text(v.fName),
                            $("<button>").attr({'value':v.mobile,'class':'vCall'}).text("CALL")
                        )
                    )
                })
            }else{
                $(".showContacts").append(
                    $("<div>").attr('class','row').text("No Contacts")
                )
            }
        }
        lCNT.onerror = function(){
            console.error(lCNT.error)
        }
    }
    function showLogin(){
        $("main").append(
            $("<article>").append(
                $("<aside>").attr('class','login').append(
                    $("<fieldset>").append(
                        $("<legend>").text("User Login"),
                        $("<form>").attr('id','formLogin').append( 
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'text','name':'lMobile','placeholder':'Enter Mobile Number',
                                    'pattern':'[0-9]{10}','title':'Enter 10 digit mobilenumbers without country code'
                                }).prop('required',true),
                                $("<label>").attr('for','lMobile').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({'type':'submit','value':'LOGIN'}),
                                $("<input>").attr({'type':'button','name':'signUP','value':'SIGNUP'})
                            )
                        )
                    )
                ),
                $("<aside>").attr('class','signup').append(
                    $("<fieldset>").append(
                        $("<legend>").text("User Registration"),
                        $("<form>").attr('id','formSignup').append(
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'text','name':'fName','placeholder':'Enter First Name'
                                }).prop('required',true),
                                $("<label>").attr('for','fName').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'text','name':'lName','placeholder':'Enter Last Name'
                                }).prop('required',true),
                                $("<label>").attr('for','lName').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({'type':'email','name':'email','placeholder':'Enter e-Mail Id'}),
                                $("<label>").attr('for','email').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({
                                    'type':'tel','name':'mobile','placeholder':'Enter Mobile Number',
                                    'pattern':'[0-9]{10}','title':'Enter 10 digit mobile number with out country code'
                                }).prop('required',true),
                                $("<label>").attr('for','mobile').text("*")
                            ),
                            $("<div>").attr('class','row').append(
                                $("<input>").attr({'type':'submit','value':'REGISTER'}),
                                $("<input>").attr({'type':'reset','value':'reset'})
                            )
                        )   
                    )
                )
            )
        )
        $(".signup").hide()
    }

    /*-----------------
        appShell
    ------------------*/
    let user;let uAuth = localStorage.getItem('uAuth');
    if(typeof(user)=="undefined"){
        user = JSON.parse(localStorage.getItem('user'))
    }
    let iceServers = {
        'iceServers':[{'urls':'stun:stun.l.google.:19302'},{'urls':'stun:stun1.l.google.:19302'}]
    }
    //console.log(navigator.mediaDevices.getSupportedConstraints())
    let constraints = {
        'video':{'width':640,'height':480,'frameRate':{'max':24}},
        'audio':{'echoCancellation':true,'noiseSuppression':true,'sampleSize':8}
    }
    let socket; let peerJS = {'host':'localhost','port':9000,'path':'/myapp','config':iceServers};

    switch(uAuth){
        case null:{
            gIndex();
            showLogin()
            break;
        }
        case 'true':{
            uIndex()
            socket = new Peer(user.mobile,peerJS)
            socket.on('open',function(){
                console.log(socket.id+" Connceted")
            })
            socket.on('error',function(error){
                $(".error").text(error.message).show()
                //$(".aH1").css('color','firebrick')
                let lCam = $("#lCam")[0];let rCams = $("video")
                lCam.srcObject.getTracks().forEach(track=>{track.stop();$(".modal").hide()})
                if(rCams.length>1){
                    let rCam = $("video")
                    rCam[1].srcObject.getTracks().forEach(track=>track.stop())
                }
            })
            socket.on('call',async function(inComing){
                $('.iScreen').show()
                await lCamCheck('ining',inComing)
                await phBook()
                await inComing.on('stream',rStream=>{
                    rCamCheck(inComing.peer,rStream)
                    $(".modal,.players").show()
                    $("button[value='"+inComing.peer+"']").prop('disabled',true)
                },err=>console.error(err))
                await addLog(inComing.peer,'ining')
            })
        }
    }

    $(document).on('submit','form',function(e){
        e.preventDefault()
    })
    $(document).on('click','input[name="signUP"]',function(){
        $(".login").hide(),
        $(".signup").show()
    })
    $(document).on('submit','#formSignup',async function(){
        let nUser = await {
            fName : $("input[name='fName']").val(),
            lName : $("input[name='lName']").val(),
            email : $("input[name='email']").val(),
            mobile : $("input[name='mobile']").val()
        }
        await localStorage.setItem('user',JSON.stringify(nUser))
        location.reload()
    })
    $(document).on('submit','#formLogin',function(){
        let mobile = $("input[name='lMobile']").val();
        switch(user){
            case null:{
                $("input[name='lMobile']").val("")
                $("label[for='lMobile']").text(" please signup").css('color','firebrick')
                break;
            }
            default:{
                if(mobile == user.mobile){
                    localStorage.setItem('uAuth',true)
                    location.reload()
                }else{
                    $("input[name='lMobile']").val("")
                    $("label[for='lMobile']").text(" re-check mobile").css('color','firebrick')
                }
            }
        }
        
    })
    $(document).on('click','.navIcon',function(){
        let text = $(this).attr('id')
        $("article").remove()
        $("<article>").empty().attr("class",text).append(
            $("<h2>").attr('class','aH2').text(text.toUpperCase())
        ).insertBefore("nav")
    })
    $(document).on('click','#calls',function(){
        $(".calls").append(
            $("<aside>").append(
                $("<div>").attr('class','row2').append(
                    $("<img>").attr({
                        'alt':'addCNT','src':'assets/addContact.svg','type':'image/svg+xml','id':'aCNT','class':'cOPT'
                    }),
                    $("<img>").attr({
                        'alt':'phBook','src':'assets/phBook.svg','type':'image/svg+xml','id':'phBook','class':'cOPT'
                    })
                )
            ),
            $("<aside>").attr('class','callLog').append(
                $("<h3>").attr('class','aH3')
            )
        )
        showLog()
    })
    $(document).on('click','#aCNT',function(){
        $(".showContacts").hide()
        $(".formContact").empty().append(
            $("<fieldset>").append(
                $("<legend>").text("ADD CONTACT"),
                $("<form>").attr('id','formCNT').append(
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({
                            'type':'text','name':'fName','placeholder':'Enter First Name'
                        }).prop('required',true),
                        $("<label>").attr('for','fName')
                    ),
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({
                            'type':'text','name':'lName','placeholder':'Enter Last Name'
                        }).prop('required',true),
                        $("<label>").attr('for','lName')
                    ),
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({
                            'type':'tel','name':'mobile','placeholder':'Enter Mobile Number',
                            'pattern':'[0-9]{10}','title':'Enter 10 digit mobile number with out country code'
                        }).prop('required',true),
                        $("<label>").attr('for','mobile')
                    ),
                    $("<div>").attr('class','row').append(
                        $("<input>").attr({'type':'submit','value':'ADD'}),
                        $("<input>").attr({'type':'reset','value':'reset'})
                    )
                )
            )
        )
        $(".modal,.contacts").show()
    })
    $(document).on('submit','#formCNT',function(){
        let contact = {
            fName: $("input[name='fName']").val(),
            lName: $("input[name='lName']").val(),
            mobile: $("input[name='mobile']").val()
        }
        let aCNT = iDB.transaction(["contacts"],'readwrite').objectStore("contacts").put(contact)
        aCNT.onsuccess = function(){
            $(".formContact").empty()
            $(".modal,.contacts").hide()
        }
        aCNT.onerror = function(){

        }
    })
    $(document).on('click','#phBook',function(){
        $(".modal,.contacts,.showContacts").show()
        phBook()
    })
    $(document).on('click','.mClose',function(){
        $(".modal").hide()
    })
    $(document).on('click','.vCall',function(){
        $(".contacts").hide()
        lCamCheck('outgoing',$(this).val())
        //check rcam from here if possible
    })
    $(document).on('click','#aCall',function(){
        $(".contacts").toggle()
    })
    $(document).on('click','#eCall',function(){
        location.reload(true)
    })
})
html,body{margin: 0;padding: 0;background-color: silver;}
header{background-color: teal;text-align: center;box-shadow: 0 10px 8px black;}
.aH1{margin: auto;}            
.aH2{margin: auto 10px;}
main{display: flex; flex-direction: column;margin-top: 20px;}
article{display: flex;flex-direction: column;}
aside{display: flex;flex-direction: column;padding: 5px;}
.row{display: flex; flex-direction: row;justify-content: center;margin:5px;}
.row>input{margin:5px}
.row1{display: flex;justify-content: space-around;}
.row2{display: flex;justify-content: space-between;margin:5px 0}
.cOPT{height:5vh}
.error{border-radius: 4px;border:2px dashed firebrick; background-color: rgba(0,0,0,0.6);color: white;text-align: center;}
nav{display: flex;align-items: center;position: fixed;
    bottom: 0;width: 100vw;justify-content: space-around;background-color: teal;border-top: 1px solid black;}
.navIcon{height:5.4vh}
video{width:30vw;transform: scaleX(-1);-webkit-transform: scaleX(-1);transform: rotateZ(1turn);-webkit-transform: rotateZ(1turn);}
.rCam{margin:5px}
footer{display: flex; justify-content: center;position: fixed;bottom: 0;width: 100vw;}
.modal{position: fixed;top: 0;left: 0;width: 100vw;display: flex;flex-direction: column;padding: 0.5%;
                background-color: rgba(0,0,0,0.6);height: 100vh;z-index: 1;justify-content: flex-start}
.mClose{color: red;text-align: right;cursor: default;font-size: 1.5em;}
.mWrap{color:white;overflow-y: scroll;flex-direction: column;width:99vw;margin-bottom:5.4vh}
.players>div:nth-child(3){flex-flow:row-wrap;}
<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1">
        <meta name="mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="google" content="notranslate">
        <meta name="description" content="webRTC video social networking">
        <meta name="author" content="Sikandar">
        <script type="text/javascript" src="js/jquery-3.5.1.min.js"></script>
        <script type="text/javascript" src="js/peerjs.min.js"></script>
        <title>webRTC</title>
    </head>
    <body>
        <header><h1 class="aH1">webRTC</h1></header>
        <main></main>
    </body>
</html>

Share Improve this question edited Jul 13, 2020 at 11:07 Sikki asked Jul 5, 2020 at 16:03 SikkiSikki 632 silver badges12 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 5

You can store all ining stream and plays it.

Here's some simple example:

1. Start with defining your vars

var localStream;
var remoteStream = {};

2. Get your own local stream first:

navigator.medisDevices.getUserMedia({video: true, audio: true}).then((stream) =>{
    localStream = stream;
    
    //Plays it on your video
    $("#local-video").prop("srcObject", localStream);
});

3. Waiting and Manage call stream

This is when you already in the call, but someone joining the call, so they will run the codes in step 4, then you receive in this step 3.

peer.on("call", (call) => {
    call.answer(localStream);
    call.on("stream", (stream) => { //here's where you get the stream
        remoteStream[call.peer] = stream; //Update your record. 
        
        $("#video-" + call.peeer).remove(); //Remove remote video if exists before
        
        $("#video-list").append("<video id='video-"+ call.peeer +"' autoplay></video>"); //Create new video element
        $("#video-"+ call.peeer).prop("srcObject", stream); //Put stream to the video
    });
});

4. Making Call

This one will make a call once the page load. So, if user refresh the page, then it will autimatically make every single call to available contacts.

//You have you contacts in var contacts

contacts.forEach((x, y) => {
    call = peer.call(x.peerId, localStream);
    
    call.on("stream", () => {
        remoteStream[call.peer] = stream; //Update your record. 
        
        $("#video-" + call.peeer).remove(); //Remove remote video if exists before
        
        $("#video-list").append("<video id='video-"+ call.peeer +"' autoplay></video>"); //Create new video element
        $("#video-"+ call.peeer).prop("srcObject", stream); //Put stream to the video
    });
});

This is the basic how to handle multiple call. It just simply accpeting call, and exchange their stream to each other.

As you can see, the step 3 and 4 has similar technique of handling remote stream, but what makes they different is just step 3 is "waiting call" and step 4 is "making call". But these two receive the remote stream.

How to get the user Peer ID

Simplest way is, you have to create a room record in database. So, a room will have other contacts with 16 char unique (a-z,A-Z,0-9) peer id. So every user logged in, they know they peer id, and they know the other use peer id.

Fixing peer id is not so secure, because other people can use it. The best way to handle this is keep changing the peer id, maybe create expiry after finish call or send it via data channel or access with expiry token etc.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744941458a4602351.html

相关推荐

  • javascript - Multi Party - Video Conference with peerJS - Stack Overflow

    I tried webRTC video conference with peerJS and succeeded in one-to-one conference, i need some assista

    1天前
    50

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信