Skip to content

Instantly share code, notes, and snippets.

@madeinfree
Created September 23, 2018 02:42
Show Gist options
  • Save madeinfree/864ab5438f8b3a41687accd17d61748f to your computer and use it in GitHub Desktop.
Save madeinfree/864ab5438f8b3a41687accd17d61748f to your computer and use it in GitHub Desktop.
use HTML5 MediaRecord API and SocketIO to build a simple voice room
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js'></script>
<script>
const socket = io('ws://localhost:8080');
const body = document.body
if (navigator.mediaDevices) {
const constraints = { audio: true }
const chunks = []
const work = async () => {
const buttonBlock = document.createElement('div')
const startBtn = document.createElement('button')
startBtn.textContent = '按著錄音'
startBtn.style.border = '1px solid #ccc'
startBtn.style.borderRadius = '3px'
buttonBlock.appendChild(startBtn)
body.appendChild(buttonBlock)
const stream = await navigator.mediaDevices.getUserMedia(constraints).then(stream => stream)
const mediaRecord = new MediaRecorder(stream)
startBtn.onmousedown = () => {
mediaRecord.start();
startBtn.textContent = '錄音中...'
}
startBtn.onmouseup = () => {
mediaRecord.stop()
startBtn.textContent = '按著錄音'
}
body.appendChild(document.createElement('br'))
mediaRecord.onstop = (e) => {
const blob = new Blob(chunks, { 'type': 'video/webm; codecs=opus' })
// to ArrayBuffer
const fr = new FileReader()
let uint8ArrayNew = null
fr.onload = (e) => {
uint8ArrayNew = new Uint8Array(e.target.result)
fetch('http://127.0.0.1:8080', {
method: 'POST',
body: uint8ArrayNew
}).then(response => {
console.log('send success..')
})
chunks.length = 0
}
fr.readAsArrayBuffer(blob)
}
mediaRecord.ondataavailable = (e) => {
chunks.push(e.data)
}
}
work()
socket.on('new-voice', (msg) => {
const fragment = document.createDocumentFragment()
const block = document.createElement('div')
const audio = document.createElement('audio');
const date = document.createElement('span')
const now = new Date()
date.textContent = now
audio.setAttribute('controls', '')
block.appendChild(audio)
block.appendChild(date)
fragment.appendChild(block)
body.appendChild(fragment)
const resBlob = new Blob([new Uint8Array(msg)], { 'type': 'audio/ogg; codecs=opus' })
const audioURL = URL.createObjectURL(resBlob)
audio.src = audioURL
})
socket.on('init', (msg) => {
console.log(msg)
})
}
</script>
</body>
</html>
const fs = require('fs');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const EventEmitter = require('events');
class VoiceEmitter extends EventEmitter {}
const voiceEmitter = new VoiceEmitter();
app.post('/', (req, res) => {
const chunks = [];
res.setHeader('Access-Control-Allow-Origin', '*');
req.on('data', chunk => {
chunks.push(chunk);
});
req.on('end', () => {
// fs.writeFileSync(`./upload/${new Date().getTime()}.webm`, chunks[0]);
res.end('OK');
voiceEmitter.emit('broadcast', chunks[0]);
});
});
voiceEmitter.on('broadcast', chunks => {
io.emit('new-voice', chunks);
});
let users = new WeakMap();
io.on('connection', function(socket) {
console.log('a user connected.');
users.set(socket, {
id: new Date().getTime()
});
socket.emit('init', users.get(socket).id);
socket.on('disconnect', reason => {
console.log('user disconnected.');
users.delete(socket);
});
});
http.listen(8080);
{
"name": "Nodejs-Media",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.16.3",
"socket.io": "^2.1.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment