Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 2 Next »

Controls encapsulate code responsible for managing local media and room config. This includes creation of new tracks, stopping existing tracks and preparation of room config before connecting to server.


Wrapper function which keeps controls inside the closure.

const createControls = function(config) {

Create controls object which will hold all the html handles. Also initialize tables for displaying local tracks configuration.

const controls = {
    entrance: {
        url: document.getElementById("url"),
        roomName: document.getElementById("roomName"),
        roomPin: document.getElementById("roomPin"),
        nickName: document.getElementById("nickName"),
        enter: document.getElementById("startButton")
    },
    addVideoTrack: {
        source: document.getElementById("addVideoTrackSource"),
        width: document.getElementById("addVideoTrackWidth"),
        height: document.getElementById("addVideoTrackHeight"),
        codec: document.getElementById("addVideoTrackCodec")
    },
    addAudioTrack: {
        source: document.getElementById("addAudioTrackSource"),
        channels: document.getElementById("addAudioTrackChannels")
    },
    addVideoEncoding: {
        rid: document.getElementById("addVideoTrackEncodingRid"),
        active: document.getElementById("addVideoTrackEncodingActive"),
        maxBitrate: document.getElementById("addVideoTrackEncodingMaxBitrate"),
        resolutionScale: document.getElementById("addVideoTrackEncodingResolutionScale")
    },
    tables: {
        video: $('#videoTracksTable').DataTable({
            "sDom": 't',
            "columns": [
                {
                    "className":      'details-control',
                    "orderable":      false,
                    "data":           null,
                    "defaultContent": ''
                },
                {"data": "source"},
                {"data": "width"},
                {"data": "height"},
                {"data": "codec"},
                {"data": "action"}
            ]
        }),
        audio: $('#audioTracksTable').DataTable({
            "sDom": 't',
            "columns": [
                {"data": "source"},
                {"data": "channels"},
                {"data": "action"}
            ]
        }),
        encodings: $('#videoTrackEncodingsTable').DataTable({
            "sDom": 't',
            "columns": [
                {"data": "rid"},
                {"data": "active"},
                {"data": "maxBitrate"},
                {"data": "resolutionScale"},
                {"data": "action"}
            ]
        })
    }
}

Populate entrance modal with provided config

//apply room config
controls.entrance.url.value = config.room.url;
controls.entrance.roomName.value = config.room.name;
controls.entrance.roomPin.value = config.room.pin;
controls.entrance.nickName.value = config.room.nickName;

addAudioTrackRow function

Function that will add new audio tracks to controls and notify main script.

const addAudioTrackRow = function(track) {
    getMedia([track]).then(function(stream){
        let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
        const row = controls.tables.audio.row.add({
            source: track.source,
            channels: track.channels,
            action: button,
            stream: stream
        }).node();
        controls.tables.audio.draw();

        $('#' + stream.id + "-button").on('click', function(){
            //terminate stream
            console.log("terminate stream " + stream.id);
            let track = stream.getAudioTracks()[0];
            track.stop();
            track.dispatchEvent(new Event("ended"));
        });
        stream.getTracks()[0].onended = function() {
            controls.tables.audio.row(row).remove().draw();
        }
        trackCallback({
            stream: stream,
            encodings: track.encodings
        });
    });
}

Request local media from the WebRTC API

getMedia([track]).then(function(stream){

Add track to the audio tracks table

let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
const row = controls.tables.audio.row.add({
    source: track.source,
    channels: track.channels,
    action: button,
    stream: stream
}).node();
controls.tables.audio.draw();

Subscribe to "click" event. Once "Delete" button is clicked stop the track and dispatch "ended" event.

         $('#' + stream.id + "-button").on('click', function(){
            //terminate stream
            console.log("terminate stream " + stream.id);
            let track = stream.getAudioTracks()[0];
            track.stop();
            track.dispatchEvent(new Event("ended"));
        }); 

Subscribe to track's "ended" event and clean the table once track is ended.

stream.getTracks()[0].onended = function() {
    controls.tables.audio.row(row).remove().draw();
}

Notify main script we have a new local track

trackCallback({
    stream: stream,
    encodings: track.encodings
});

addVideoTrackRow function

Function that will add new video tracks to controls and notify main script. This is the same as addAudioTrackRow function except it adds video.

const addVideoTrackRow = function(track) {
    getMedia([track]).then(function(stream){
        let button = '<button id="' + stream.id + '-button" class="btn btn-primary">Delete</button>';
        const row = controls.tables.video.row.add({
            source: track.source,
            width: track.width,
            height: track.height,
            codec: track.codec,
            action: button,
            stream: stream,
            encodings: track.encodings
        }).node();
        controls.tables.video.draw();

        $('#' + stream.id + "-button").on('click', function(){
            //terminate stream
            console.log("terminate stream " + stream.id);
            let track = stream.getVideoTracks()[0];
            track.stop();
            track.dispatchEvent(new Event("ended"));
        });
        stream.getTracks()[0].addEventListener("ended", function() {
            controls.tables.video.row(row).remove().draw();
        });
        trackCallback({
            stream: stream,
            encodings: track.encodings
        });
    });
}

format function

Helper function  to format video encodings so we can display them in the table nicely

const format = function(d) {
    if (!d.encodings) {
        return;
    }
    let details =  '<table cellpadding="5" cellspacing="0" border="0" style="padding-left:50px;">';
    d.encodings.forEach(function(encoding){
        details += '<tr>';
        for (const [key, value] of Object.entries(encoding)) {
            details += '<td>'+ key + '</td>'+
                '<td>'+ value + '</td>';
        }
        details += '</tr>';
    });
    details +='</table>';
    return details;
}

Add open and close handler for video tracks details

// Add event listener for opening and closing details
$('#videoTracksTableBody').on('click', 'td.details-control', function () {
    let tr = $(this).closest('tr');
    let row = controls.tables.video.row(tr);
    if (row.child.isShown()) {
        // This row is already open - close it
        row.child.hide();
        tr.removeClass('shown');
    } else {
        // Open this row
        row.child(format(row.data())).show();
        tr.addClass('shown');
    }
});

Add all configured audio tracks to the table

config.media.audio.tracks.forEach(function(track){
    addAudioTrackRow(track);
})

Add all configured video tracks to the table

config.media.video.tracks.forEach(function(track){
    addVideoTrackRow(track);
})

Define helper function for muting forms

const muteForm = function(form) {
    for (const [key, value] of Object.entries(form)) {
        value.disabled = true;
    }
}

Define helper function for unmuting forms

const unmuteForm = function(form) {
    for (const [key, value] of Object.entries(form)) {
        value.disabled = false;
    }
}

Define function that mutes entrance inputs

const muteInput = function() {
    muteForm(controls.entrance);
}

Define function that will assemble room config

const roomConfig = function() {
    return {
        url: controls.entrance.url.value,
        roomName: controls.entrance.roomName.value,
        pin: controls.entrance.roomPin.value,
        nickname: controls.entrance.nickName.value
    }
}

Define function that will return all available local video tracks

const getVideoStreams = function() {
    let streams = [];
    controls.tables.video.rows().every(function(rowIdx, tableLoop, rowLoop) {
        let data = this.data();
        streams.push({
            stream: data.stream,
            encodings: data.encodings
        });
    });
    return streams;
}

Define function that will return all available local audio tracks

const getAudioStreams = function() {
    let streams = [];
    controls.tables.audio.rows().every(function(rowIdx, tableLoop, rowLoop) {
        let data = this.data();
        streams.push({
            stream: data.stream,
            encodings: []
        });
    });
    return streams;
}

Subscribe to "click" event of add video track button. This function will parse input and add new track to the table

document.getElementById("addVideoTrack").addEventListener("click", function(e){
    let encodings = [];
    controls.tables.encodings.rows().every(function() {
        let encoding = this.data();
        encodings.push({
            rid: encoding.rid,
            active: encoding.active,
            maxBitrate: encoding.maxBitrate,
            scaleResolutionDownBy: encoding.resolutionScale
        })
    });
    let track = {
        source: controls.addVideoTrack.source.value,
        width: controls.addVideoTrack.width.value,
        height: controls.addVideoTrack.height.value,
        codec: controls.addVideoTrack.codec.value,
        encodings: encodings
    }
    addVideoTrackRow(track);
});

Add ability to remove encodings from the encodings table

$("#videoTrackEncodingsTable").on("click", ".remove", function(){
    controls.tables.encodings.row($(this).parents('tr')).remove().draw();
});

Subscribe to "click" event of add encoding button. This function will parse input and add a new encoding to the encodings table

document.getElementById("addVideoTrackEncoding").addEventListener("click", function(){
    let button = '<button class="btn btn-primary remove">Delete</button>';
    controls.tables.encodings.row.add({
        rid: controls.addVideoEncoding.rid.value,
        active: controls.addVideoEncoding.active.value,
        maxBitrate: controls.addVideoEncoding.maxBitrate.value,
        resolutionScale: controls.addVideoEncoding.resolutionScale.value,
        action: button
    }).draw();
});

Subscribe to "click" event of add audio track button. This function will parse input and add new track to the table

document.getElementById("addAudioTrack").addEventListener("click", function(e){
    let encodings = [];
    let track = {
        source: controls.addAudioTrack.source.value,
        channels: controls.addAudioTrack.channels.value,
        encodings: encodings
    }
    addAudioTrackRow(track);
});

Define function that will help to pass the callback function for the new tracks

const onTrack = function(callback) {
    trackCallback = callback;
}

Export functions for the main script

return {
    muteInput: muteInput,
    roomConfig: roomConfig,
    getAudioStreams: getAudioStreams,
    getVideoStreams: getVideoStreams,
    onTrack: onTrack
}

getMedia function

Requests local media streams from the WebRTC API.

const getMedia = async function(tracks) {
    //convert to constraints
    let screen = false;
    const constraints= {};
    tracks.forEach(function(track){
        if (track.source === "mic") {
            //audio
            constraints.audio = {};
            constraints.audio.stereo = track.channels !== 1
        } else if (track.source === "camera") {
            constraints.video = {
                width: track.width,
                height: track.height
            };
        } else if (track.source === "screen") {
            constraints.video = {
                width: track.width,
                height: track.height
            };
            screen = true;
        }
    });

    //get access to a/v
    let stream;
    if (screen) {
        stream = await navigator.mediaDevices.getDisplayMedia(constraints);
    } else {
        stream = await navigator.mediaDevices.getUserMedia(constraints);
    }
    return stream;
}



  • No labels