master 0d95c6fd417c cached
11 files
17.0 KB
5.1k tokens
10 symbols
1 requests
Download .txt
Repository: Dirvann/webrtc-video-conference-simple-peer
Branch: master
Commit: 0d95c6fd417c
Files: 11
Total size: 17.0 KB

Directory structure:
gitextract_ufu3lduj/

├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── public/
│   ├── index.html
│   └── js/
│       └── main.js
├── src/
│   ├── app.js
│   ├── routes.js
│   └── socketController.js
└── ssl/
    ├── cert.pem
    └── key.pem

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env
.env.test

# parcel-bundler cache (https://parceljs.org/)
.cache

# Next.js build output
.next

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Dirk Vanbeveren

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# WebRTC Video Conferencing with simple-peer
A simple video conferencing example using simple-peer.
This project allows multiple devices to connect with eachother with audio and video using webrtc.
The package [simple-peer](https://github.com/feross/simple-peer) is used for webrtc.
The implementation of the signaling server is done with [socket.io](https://socket.io/)

## Demo
[Demo on heroku](https://dirvann-webrtc-video.herokuapp.com/)

## Running

run `npm install` and then `npm start` in the main directory.

Then open the browser at `localhost:3012` or `[your network ip/ public dns]:3012`.



## Configuration

Configurations can be found in `app.js` and `public/js/main.js`.

Replace the ssl certificates `ssl/key.pem` and `ssl/cert.pem` with your own.



================================================
FILE: package.json
================================================
{
  "name": "mywebrtc",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {
    "express": "^4.17.1",
    "httpolyglot": "^0.1.2",
    "simple-peer": "^9.7.0",
    "socket.io": "^2.4.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node src/app.js"
  },
  "author": "",
  "license": "ISC"
}


================================================
FILE: public/index.html
================================================
<html>

<head>
    <script lang="text/javascript" src="/socket.io/socket.io.js"></script>
    <script src="/simple-peer/simplepeer.min.js"></script>
    <style>
        .containers {
            display: grid;
            grid-gap: 5px;
            grid-template-columns: repeat(auto-fit, 1fr);
            grid-template-rows: repeat(auto-fit, 300px);
        }

        .container {
            display: flex;
        }

        .vid {
            flex: 0 1 auto;
            height: 400px;
        }

        .settings {
            background-color: #4CAF50;
            border: none;
            color: white;
            padding: 5px 10px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 14px;
            margin: 2px 2px;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <div id="videos" class="container">
        <video id="localVideo" class="vid" autoplay muted></video>
    </div>
    <br />
    <div style="display:block">
        <button id="switchButton" class="settings" onclick="switchMedia()">Switch
            Camera</button>
        <button id="muteButton" class="settings" onclick="toggleMute()">Unmuted</button>
        <button id="vidButton" class="settings" onclick="toggleVid()">Video Enabled</button>
    </div>
</body>
<footer>
    <script src='/js/main.js' lang="text/javascript"></script>


</footer>

</html>

================================================
FILE: public/js/main.js
================================================
/**
 * Socket.io socket
 */
let socket;
/**
 * The stream object used to send media
 */
let localStream = null;
/**
 * All peer connections
 */
let peers = {}

// redirect if not https
if(location.href.substr(0,5) !== 'https') 
    location.href = 'https' + location.href.substr(4, location.href.length - 4)


//////////// CONFIGURATION //////////////////

/**
 * RTCPeerConnection configuration 
 */

const configuration = {
    // Using From https://www.metered.ca/tools/openrelay/
    "iceServers": [
    {
      urls: "stun:openrelay.metered.ca:80"
    },
    {
      urls: "turn:openrelay.metered.ca:80",
      username: "openrelayproject",
      credential: "openrelayproject"
    },
    {
      urls: "turn:openrelay.metered.ca:443",
      username: "openrelayproject",
      credential: "openrelayproject"
    },
    {
      urls: "turn:openrelay.metered.ca:443?transport=tcp",
      username: "openrelayproject",
      credential: "openrelayproject"
    }
  ]
}

/**
 * UserMedia constraints
 */
let constraints = {
    audio: true,
    video: {
        width: {
            max: 300
        },
        height: {
            max: 300
        }
    }
}

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

constraints.video.facingMode = {
    ideal: "user"
}

// enabling the camera at startup
navigator.mediaDevices.getUserMedia(constraints).then(stream => {
    console.log('Received local stream');

    localVideo.srcObject = stream;
    localStream = stream;

    init()

}).catch(e => alert(`getusermedia error ${e.name}`))

/**
 * initialize the socket connections
 */
function init() {
    socket = io()

    socket.on('initReceive', socket_id => {
        console.log('INIT RECEIVE ' + socket_id)
        addPeer(socket_id, false)

        socket.emit('initSend', socket_id)
    })

    socket.on('initSend', socket_id => {
        console.log('INIT SEND ' + socket_id)
        addPeer(socket_id, true)
    })

    socket.on('removePeer', socket_id => {
        console.log('removing peer ' + socket_id)
        removePeer(socket_id)
    })

    socket.on('disconnect', () => {
        console.log('GOT DISCONNECTED')
        for (let socket_id in peers) {
            removePeer(socket_id)
        }
    })

    socket.on('signal', data => {
        peers[data.socket_id].signal(data.signal)
    })
}

/**
 * Remove a peer with given socket_id. 
 * Removes the video element and deletes the connection
 * @param {String} socket_id 
 */
function removePeer(socket_id) {

    let videoEl = document.getElementById(socket_id)
    if (videoEl) {

        const tracks = videoEl.srcObject.getTracks();

        tracks.forEach(function (track) {
            track.stop()
        })

        videoEl.srcObject = null
        videoEl.parentNode.removeChild(videoEl)
    }
    if (peers[socket_id]) peers[socket_id].destroy()
    delete peers[socket_id]
}

/**
 * Creates a new peer connection and sets the event listeners
 * @param {String} socket_id 
 *                 ID of the peer
 * @param {Boolean} am_initiator 
 *                  Set to true if the peer initiates the connection process.
 *                  Set to false if the peer receives the connection. 
 */
function addPeer(socket_id, am_initiator) {
    peers[socket_id] = new SimplePeer({
        initiator: am_initiator,
        stream: localStream,
        config: configuration
    })

    peers[socket_id].on('signal', data => {
        socket.emit('signal', {
            signal: data,
            socket_id: socket_id
        })
    })

    peers[socket_id].on('stream', stream => {
        let newVid = document.createElement('video')
        newVid.srcObject = stream
        newVid.id = socket_id
        newVid.playsinline = false
        newVid.autoplay = true
        newVid.className = "vid"
        newVid.onclick = () => openPictureMode(newVid)
        newVid.ontouchstart = (e) => openPictureMode(newVid)
        videos.appendChild(newVid)
    })
}

/**
 * Opens an element in Picture-in-Picture mode
 * @param {HTMLVideoElement} el video element to put in pip mode
 */
function openPictureMode(el) {
    console.log('opening pip')
    el.requestPictureInPicture()
}

/**
 * Switches the camera between user and environment. It will just enable the camera 2 cameras not supported.
 */
function switchMedia() {
    if (constraints.video.facingMode.ideal === 'user') {
        constraints.video.facingMode.ideal = 'environment'
    } else {
        constraints.video.facingMode.ideal = 'user'
    }

    const tracks = localStream.getTracks();

    tracks.forEach(function (track) {
        track.stop()
    })

    localVideo.srcObject = null
    navigator.mediaDevices.getUserMedia(constraints).then(stream => {

        for (let socket_id in peers) {
            for (let index in peers[socket_id].streams[0].getTracks()) {
                for (let index2 in stream.getTracks()) {
                    if (peers[socket_id].streams[0].getTracks()[index].kind === stream.getTracks()[index2].kind) {
                        peers[socket_id].replaceTrack(peers[socket_id].streams[0].getTracks()[index], stream.getTracks()[index2], peers[socket_id].streams[0])
                        break;
                    }
                }
            }
        }

        localStream = stream
        localVideo.srcObject = stream

        updateButtons()
    })
}

/**
 * Enable screen share
 */
function setScreen() {
    navigator.mediaDevices.getDisplayMedia().then(stream => {
        for (let socket_id in peers) {
            for (let index in peers[socket_id].streams[0].getTracks()) {
                for (let index2 in stream.getTracks()) {
                    if (peers[socket_id].streams[0].getTracks()[index].kind === stream.getTracks()[index2].kind) {
                        peers[socket_id].replaceTrack(peers[socket_id].streams[0].getTracks()[index], stream.getTracks()[index2], peers[socket_id].streams[0])
                        break;
                    }
                }
            }

        }
        localStream = stream

        localVideo.srcObject = localStream
        socket.emit('removeUpdatePeer', '')
    })
    updateButtons()
}

/**
 * Disables and removes the local stream and all the connections to other peers.
 */
function removeLocalStream() {
    if (localStream) {
        const tracks = localStream.getTracks();

        tracks.forEach(function (track) {
            track.stop()
        })

        localVideo.srcObject = null
    }

    for (let socket_id in peers) {
        removePeer(socket_id)
    }
}

/**
 * Enable/disable microphone
 */
function toggleMute() {
    for (let index in localStream.getAudioTracks()) {
        localStream.getAudioTracks()[index].enabled = !localStream.getAudioTracks()[index].enabled
        muteButton.innerText = localStream.getAudioTracks()[index].enabled ? "Unmuted" : "Muted"
    }
}
/**
 * Enable/disable video
 */
function toggleVid() {
    for (let index in localStream.getVideoTracks()) {
        localStream.getVideoTracks()[index].enabled = !localStream.getVideoTracks()[index].enabled
        vidButton.innerText = localStream.getVideoTracks()[index].enabled ? "Video Enabled" : "Video Disabled"
    }
}

/**
 * updating text of buttons
 */
function updateButtons() {
    for (let index in localStream.getVideoTracks()) {
        vidButton.innerText = localStream.getVideoTracks()[index].enabled ? "Video Enabled" : "Video Disabled"
    }
    for (let index in localStream.getAudioTracks()) {
        muteButton.innerText = localStream.getAudioTracks()[index].enabled ? "Unmuted" : "Muted"
    }
}


================================================
FILE: src/app.js
================================================
const fs = require('fs')
const path = require('path')
const express = require('express')
const app = express()
const httpolyglot = require('httpolyglot')
const https = require('https')

//////// CONFIGURATION ///////////

// insert your own ssl certificate and keys
const options = {
    key: fs.readFileSync(path.join(__dirname,'..','ssl','key.pem'), 'utf-8'),
    cert: fs.readFileSync(path.join(__dirname,'..','ssl','cert.pem'), 'utf-8')
}

const port = process.env.PORT || 3012

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

require('./routes')(app)

const httpsServer = httpolyglot.createServer(options, app)
const io = require('socket.io')(httpsServer)
require('./socketController')(io)


httpsServer.listen(port, () => {
    console.log(`listening on port ${port}`)
})







================================================
FILE: src/routes.js
================================================
const path = require('path')
const express = require('express')

module.exports = (app) => {

    // redirect http traffic to https traffic
    /*app.use('*', (req, res, next) => {
        if(!req.socket.encrypted){
            console.log('unsecure connection: redirecting..')
            res.redirect('https://' + req.headers.host + req.path)
        } else {
            next()
        }
    })*/

    app.use(express.static(path.join(__dirname, '..','public')))
    app.use(express.static(path.join(__dirname, '..','node_modules')))
}

================================================
FILE: src/socketController.js
================================================

peers = {}


module.exports = (io) => {
    io.on('connect', (socket) => {
        console.log('a client is connected')


        // Initiate the connection process as soon as the client connects

        peers[socket.id] = socket

        // Asking all other clients to setup the peer connection receiver
        for(let id in peers) {
            if(id === socket.id) continue
            console.log('sending init receive to ' + socket.id)
            peers[id].emit('initReceive', socket.id)
        }

        /**
         * relay a peerconnection signal to a specific socket
         */
        socket.on('signal', data => {
            console.log('sending signal from ' + socket.id + ' to ', data)
            if(!peers[data.socket_id])return
            peers[data.socket_id].emit('signal', {
                socket_id: socket.id,
                signal: data.signal
            })
        })

        /**
         * remove the disconnected peer connection from all other connected clients
         */
        socket.on('disconnect', () => {
            console.log('socket disconnected ' + socket.id)
            socket.broadcast.emit('removePeer', socket.id)
            delete peers[socket.id]
        })

        /**
         * Send message to client to initiate a connection
         * The sender has already setup a peer connection receiver
         */
        socket.on('initSend', init_socket_id => {
            console.log('INIT SEND by ' + socket.id + ' for ' + init_socket_id)
            peers[init_socket_id].emit('initSend', socket.id)
        })
    })
}

================================================
FILE: ssl/cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIICAzCCAWwCCQDUB0G6UpqG9DANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMCAXDTIwMDUwMzE1MjA0MloYDzIyOTQwMjE2MTUyMDQyWjBFMQsw
CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu
ZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1
NxC8z2k7jEQkF0YkuOSqCBtc4dsb+RgPSkXu6NFMf8g59Sg7EBK29ledRlrszF1j
9ztpgvZCy2PwpJCWu5efCacifRpDege2fPACDre/v04vWfRCzQzgwu21kM9tBPyf
VZlM1Z5tjcbmAKrE8/DSMWxgiBP/o343UpZkQ5VLYQIDAQABMA0GCSqGSIb3DQEB
CwUAA4GBAK+uEcDRU4BnLkr47TZ4mYm7F9h2OXAqf/9FXrH6iwBqiVenG9j8GHop
lLZ0uAkTGUkSKlqe7imaRogKOKWzB89BTl4M3NqheeIlR5rgb8T6h73CIZViVdyF
nid/BTSwWY3eu9qeIL6tLf4977ExfIUZCVlL739pasTx6wXQ1h5i
-----END CERTIFICATE-----


================================================
FILE: ssl/key.pem
================================================
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQC1NxC8z2k7jEQkF0YkuOSqCBtc4dsb+RgPSkXu6NFMf8g59Sg7
EBK29ledRlrszF1j9ztpgvZCy2PwpJCWu5efCacifRpDege2fPACDre/v04vWfRC
zQzgwu21kM9tBPyfVZlM1Z5tjcbmAKrE8/DSMWxgiBP/o343UpZkQ5VLYQIDAQAB
AoGBAKdQplWOM51Vjvy7uHnjdM1BGvKRXlrfjMVhUFyb/dNiEB7jKjgOSRlBLff6
o60LrBbGiMaMso9Nd4MSjqV9oykYQ3zhdSV/W8W7nA5OhXzgJF4cngkrwnxWhLYA
mqH8GJWCvz7/7A8Ckeutx8Zn1GTJWcd/CfSd9SDNGAJqJA4BAkEA3RYhdATWwjhZ
KsWwFMVzG7fBqCvJLO/Ep9kIvpLtiaQjyNtftS8Nw91IpEVodTAtxxiVBiFRiFxS
3yTWVUOEsQJBANHVDejleEYvT/Rc7aN0bhJVcZuhaN9qPDIXY5UIQTYR1NzgcQKA
qCo7u0x0+vQiYJ15YqrRo8ZkdqvyCNlmnbECQQC2KrYF0rbh8WwHQjyD4O2nuRFo
cCujSyzO4JXD8WyoLQcPSTLjJ5JAAOUJ9ebMKJaPpkGke2+i2++szb2NI8UBAkEA
kKoBmAK0hDbUOdXjpGB+DrfHxpNmmTlF3QcRCcuSIfPzPICkiSQoTE24GMNBzRTy
ZT8tzjUQY5QZ2PvaLAA6UQJBAJXLnYMIX0ZkO1nG6mMIWoZtrLpfg+pSFa0VUsHs
7G3iYf9v31Wd6HipMRzg/q63m90nwI5emTYe+EEo4lkGnz8=
-----END RSA PRIVATE KEY-----
Download .txt
gitextract_ufu3lduj/

├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── public/
│   ├── index.html
│   └── js/
│       └── main.js
├── src/
│   ├── app.js
│   ├── routes.js
│   └── socketController.js
└── ssl/
    ├── cert.pem
    └── key.pem
Download .txt
SYMBOL INDEX (10 symbols across 1 files)

FILE: public/js/main.js
  function init (line 84) | function init() {
  function removePeer (line 121) | function removePeer(socket_id) {
  function addPeer (line 147) | function addPeer(socket_id, am_initiator) {
  function openPictureMode (line 178) | function openPictureMode(el) {
  function switchMedia (line 186) | function switchMedia() {
  function setScreen (line 223) | function setScreen() {
  function removeLocalStream (line 247) | function removeLocalStream() {
  function toggleMute (line 266) | function toggleMute() {
  function toggleVid (line 275) | function toggleVid() {
  function updateButtons (line 285) | function updateButtons() {
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (19K chars).
[
  {
    "path": ".gitignore",
    "chars": 1610,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n\n# Diagnostic reports (https://nodejs."
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2020 Dirk Vanbeveren\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 766,
    "preview": "# WebRTC Video Conferencing with simple-peer\nA simple video conferencing example using simple-peer.\nThis project allows "
  },
  {
    "path": "package.json",
    "chars": 396,
    "preview": "{\n  \"name\": \"mywebrtc\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"expres"
  },
  {
    "path": "public/index.html",
    "chars": 1437,
    "preview": "<html>\n\n<head>\n    <script lang=\"text/javascript\" src=\"/socket.io/socket.io.js\"></script>\n    <script src=\"/simple-peer/"
  },
  {
    "path": "public/js/main.js",
    "chars": 7585,
    "preview": "/**\n * Socket.io socket\n */\nlet socket;\n/**\n * The stream object used to send media\n */\nlet localStream = null;\n/**\n * A"
  },
  {
    "path": "src/app.js",
    "chars": 765,
    "preview": "const fs = require('fs')\nconst path = require('path')\nconst express = require('express')\nconst app = express()\nconst htt"
  },
  {
    "path": "src/routes.js",
    "chars": 538,
    "preview": "const path = require('path')\nconst express = require('express')\n\nmodule.exports = (app) => {\n\n    // redirect http traff"
  },
  {
    "path": "src/socketController.js",
    "chars": 1580,
    "preview": "\npeers = {}\n\n\nmodule.exports = (io) => {\n    io.on('connect', (socket) => {\n        console.log('a client is connected')"
  },
  {
    "path": "ssl/cert.pem",
    "chars": 757,
    "preview": "-----BEGIN CERTIFICATE-----\nMIICAzCCAWwCCQDUB0G6UpqG9DANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB\nVTETMBEGA1UECAwKU29tZS1TdGF"
  },
  {
    "path": "ssl/key.pem",
    "chars": 891,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBAAKBgQC1NxC8z2k7jEQkF0YkuOSqCBtc4dsb+RgPSkXu6NFMf8g59Sg7\nEBK29ledRlrszF1j9ztpgvZ"
  }
]

About this extraction

This page contains the full source code of the Dirvann/webrtc-video-conference-simple-peer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (17.0 KB), approximately 5.1k tokens, and a symbol index with 10 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!