summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJibon Costa <jiboncosta57@gmail.com>2020-04-01 13:33:59 +0600
committerJibon Costa <jiboncosta57@gmail.com>2020-04-01 13:33:59 +0600
commitaf776feee533e0d8244840c751b1a2254c1d432b (patch)
tree431389a5bfae8ef25858d8db99c427503897380e
parent1b9b7e254aaba5fcb9ff3bbd79fa5613cdc406e4 (diff)
downloadbbb-recorder-af776feee533e0d8244840c751b1a2254c1d432b.tar.gz
bbb-recorder-af776feee533e0d8244840c751b1a2254c1d432b.zip
live rtmp reconnect & google chrome use
-rw-r--r--README.md16
-rw-r--r--background.js224
-rwxr-xr-xdependencies_check.sh3
-rw-r--r--export.js26
-rw-r--r--ffmpegServer.js16
-rw-r--r--liveJoin.js25
-rw-r--r--liveRTMP.js20
7 files changed, 208 insertions, 122 deletions
diff --git a/README.md b/README.md
index f37b0b1..46dca48 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,17 @@ Bigbluebutton recordings export to `webm` or `mp4` & live broadcasting. This is
### Dependencies
1. xvfb (`apt install xvfb`)
-2. npm modules listed in package.json
+2. Google Chrome stable
+3. npm modules listed in package.json
+
+The latest Google Chrome stable build should be used.
+
+```sh
+curl -sS -o - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add
+echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list
+apt-get -y update
+apt-get -y install google-chrome-stable
+```
### Usage
@@ -71,10 +81,10 @@ node liveRTMP.js "https://BBB_HOST/bigbluebutton/api/join?meetingId=MEETING_ID..
Check the process of websocket server, `ffmpeg` should start sending data to RTMP server.
**Note:**
-If you do nothing in meeting room that time `ffmpeg` may exit with error. Actually I don't have much experience on `ffmpeg` to resolve those problems. Please contribute your experience.
+If you do nothing in meeting room that time `ffmpeg` may exit with error & will try to reconnect again. Actually I don't have much experience on `ffmpeg` to resolve those problems. Please contribute your experience.
### How it will work?
-When you will run the command that time `chromium` browser will be open in background & visit the link & perform screen recording. So, if you have set 10 seconds then it will record 10 seconds only. Later it will give you file as webm or mp4.
+When you will run the command that time `Chrome` browser will be open in background & visit the link & perform screen recording. So, if you have set 10 seconds then it will record 10 seconds only. Later it will give you file as webm or mp4.
**Note: It will use extra CPU to process chrome & ffmpeg.**
diff --git a/background.js b/background.js
index 253e81e..5895f19 100644
--- a/background.js
+++ b/background.js
@@ -4,110 +4,144 @@ let recorder = null;
let filename = null;
let ws;
let liveSteam = false;
+let ffmpegServer;
+let doDownload = true;
+
chrome.runtime.onConnect.addListener(port => {
- port.onMessage.addListener(msg => {
- console.log(msg);
- switch (msg.type) {
- case 'SET_EXPORT_PATH':
- filename = msg.filename
- break
- case 'FFMPEG_SERVER':
- let ffmpegServer = msg.ffmpegServer
- ws = new WebSocket(ffmpegServer);
- liveSteam = true;
- break
- case 'REC_STOP':
- recorder.stop()
- break
- case 'REC_START':
- if(liveSteam){
- recorder.start(1000);
- }else{
- recorder.start();
- }
- break
- case 'REC_CLIENT_PLAY':
- if(recorder){
- return
- }
- const tab = port.sender.tab
- tab.url = msg.data.url
- chrome.desktopCapture.chooseDesktopMedia(['tab', 'audio'], streamId => {
- // Get the stream
- navigator.webkitGetUserMedia({
- audio: {
- mandatory: {
- chromeMediaSource: 'system'
- }
- },
- video: {
- mandatory: {
- chromeMediaSource: 'desktop',
- chromeMediaSourceId: streamId,
- minWidth: 1280,
- maxWidth: 1280,
- minHeight: 720,
- maxHeight: 720,
- minFrameRate: 60,
- }
- }
- }, stream => {
- var chunks = [];
- recorder = new MediaRecorder(stream, {
- videoBitsPerSecond: 2500000,
- ignoreMutedMedia: true,
- mimeType: 'video/webm'
- });
- recorder.ondataavailable = function (event) {
- if (event.data.size > 0) {
- chunks.push(event.data);
- if(liveSteam){
- ws.send(event.data);
- }
+ port.onMessage.addListener(msg => {
+ console.log(msg);
+ switch (msg.type) {
+
+ case 'SET_EXPORT_PATH':
+ filename = msg.filename
+ break
+
+ case 'FFMPEG_SERVER':
+ ffmpegServer = msg.ffmpegServer
+ startWebsock();
+ break
+
+ case 'REC_STOP':
+ doDownload = true;
+ recorder.stop()
+ break
+
+ case 'REC_START':
+ if (liveSteam) {
+ recorder.start(1000);
+ } else {
+ recorder.start();
}
- };
+ break
- recorder.onstop = function () {
- if(liveSteam){
- ws.close();
+ case 'REC_CLIENT_PLAY':
+ if (recorder) {
+ return
}
+ const tab = port.sender.tab
+ tab.url = msg.data.url
+ chrome.desktopCapture.chooseDesktopMedia(['tab', 'audio'], streamId => {
+ // Get the stream
+ navigator.webkitGetUserMedia({
+ audio: {
+ mandatory: {
+ chromeMediaSource: 'system'
+ }
+ },
+ video: {
+ mandatory: {
+ chromeMediaSource: 'desktop',
+ chromeMediaSourceId: streamId,
+ minWidth: 1280,
+ maxWidth: 1280,
+ minHeight: 720,
+ maxHeight: 720,
+ minFrameRate: 60,
+ }
+ }
+ }, stream => {
+ var chunks = [];
+ recorder = new MediaRecorder(stream, {
+ videoBitsPerSecond: 2500000,
+ ignoreMutedMedia: true,
+ mimeType: 'video/webm;codecs=h264'
+ });
+ recorder.ondataavailable = function (event) {
+ if (event.data.size > 0) {
+ chunks.push(event.data);
+ if (liveSteam) {
+ ws.send(event.data);
+ }
+ }
+ };
- var superBuffer = new Blob(chunks, {
- type: 'video/webm'
- });
+ recorder.onstop = function () {
+ if (liveSteam) {
+ ws.close();
+ }
- var url = URL.createObjectURL(superBuffer);
- // var a = document.createElement('a');
- // document.body.appendChild(a);
- // a.style = 'display: none';
- // a.href = url;
- // a.download = 'test.webm';
- // a.click();
+ if(!doDownload){
+ chunks = [];
+ return;
+ }
- chrome.downloads.download({
- url: url,
- filename: filename
- }, ()=>{
- });
- }
+ var superBuffer = new Blob(chunks, {
+ type: 'video/webm'
+ });
- }, error => console.log('Unable to get user media', error))
- })
- break
- default:
- console.log('Unrecognized message', msg)
- }
- })
+ var url = URL.createObjectURL(superBuffer);
+ // var a = document.createElement('a');
+ // document.body.appendChild(a);
+ // a.style = 'display: none';
+ // a.href = url;
+ // a.download = 'test.webm';
+ // a.click();
- chrome.downloads.onChanged.addListener(function(delta) {
- if (!delta.state ||(delta.state.current != 'complete')) {
- return;
- }
- try{
- port.postMessage({downloadComplete: true})
- }
- catch(e){}
- });
+ chrome.downloads.download({
+ url: url,
+ filename: filename
+ }, () => {
+ });
+ }
+
+ }, error => console.log('Unable to get user media', error))
+ })
+ break
+ default:
+ console.log('Unrecognized message', msg)
+ }
+ })
+
+ chrome.downloads.onChanged.addListener(function (delta) {
+ if (!delta.state || (delta.state.current != 'complete')) {
+ return;
+ }
+ try {
+ port.postMessage({ downloadComplete: true })
+ }
+ catch (e) { }
+ });
})
+
+function startWebsock() {
+
+ ws = new WebSocket(ffmpegServer);
+ liveSteam = true;
+
+ ws.onmessage = function (e) {
+ console.log(e.data);
+
+ if (e.data == "ffmpegClosed") {
+
+ doDownload = false;
+ recorder.stop();
+
+ setTimeout(function () {
+ startWebsock();
+ recorder.start(1000);
+ }, 500)
+ }
+ }
+}
diff --git a/dependencies_check.sh b/dependencies_check.sh
index 5f60992..b734df3 100755
--- a/dependencies_check.sh
+++ b/dependencies_check.sh
@@ -37,7 +37,8 @@ libappindicator1
libnss3
lsb-release
xdg-utils
-wget"
+wget
+xvfb"
declare -a neededPackages
diff --git a/export.js b/export.js
index 39ebd38..549c58a 100644
--- a/export.js
+++ b/export.js
@@ -2,7 +2,9 @@ const puppeteer = require('puppeteer');
const Xvfb = require('xvfb');
var exec = require('child_process').exec;
const fs = require('fs');
-const homedir = require('os').homedir();
+const os = require('os');
+const homedir = os.homedir();
+const platform = os.platform();
var xvfb = new Xvfb({
silent: true,
@@ -26,16 +28,25 @@ var options = {
],
}
+if(platform == "linux"){
+ options.executablePath = "/usr/bin/google-chrome"
+}else if(platform == "darwin"){
+ options.executablePath = "/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
+}
+
+
async function main() {
try{
- xvfb.startSync()
+ if(platform == "linux"){
+ xvfb.startSync()
+ }
var url = process.argv[2],
exportname = process.argv[3],
duration = process.argv[4],
convert = process.argv[5]
- if(!url){ url = 'http://tobiasahlin.com/spinkit/' }
- if(!exportname){ exportname = 'spinner.webm' }
+ if(!url){ url = 'https://www.mynaparrot.com/' }
+ if(!exportname){ exportname = 'export.webm' }
if(!duration){ duration = 10 }
if(!convert){ convert = false }
@@ -76,7 +87,10 @@ async function main() {
await page.waitForSelector('html.downloadComplete', {timeout: 0})
await page.close()
await browser.close()
- xvfb.stopSync()
+
+ if(platform == "linux"){
+ xvfb.stopSync()
+ }
if(convert){
convertAndCopy(exportname)
@@ -107,7 +121,7 @@ function convertAndCopy(filename){
console.log(copyTo);
console.log(copyFrom);
- var cmd = "ffmpeg -y -i '" + copyFrom + "' -preset veryfast -movflags faststart -profile:v high -level 4.2 -max_muxing_queue_size 9999 -vf mpdecimate -vsync vfr '" + copyTo + "'";
+ var cmd = "ffmpeg -y -i '" + copyFrom + "' -c:v libx264 -preset veryfast -movflags faststart -profile:v high -level 4.2 -max_muxing_queue_size 9999 -vf mpdecimate -vsync vfr '" + copyTo + "'";
console.log("converting using: " + cmd);
diff --git a/ffmpegServer.js b/ffmpegServer.js
index ef8c3a6..c4acb1b 100644
--- a/ffmpegServer.js
+++ b/ffmpegServer.js
@@ -36,22 +36,22 @@ wss.on('connection', function connection(ws, req) {
// FFmpeg will read input video from STDIN
'-i', '-',
- // Chromium doesn't support H.264, set the video codec to 'libx264'
- // or similar to transcode it to H.264 here on the server.
- '-vcodec', 'libx264',
+ // If we're encoding H.264 in-browser, we can set the video codec to 'copy'
+ // so that we don't waste any CPU and quality with unnecessary transcoding.
+ '-vcodec', 'copy',
//No browser currently supports encoding AAC, so we must transcode the audio to AAC here on the server.
'-acodec', 'aac',
- // FLV is the container format used in conjunction with RTMP
- '-f', 'flv',
-
'-max_muxing_queue_size', '99999',
'-preset', 'veryfast',
//'-vf', 'mpdecimate', '-vsync', 'vfr',
//'-vf', 'mpdecimate,setpts=N/FRAME_RATE/TB',
-
+
+ // FLV is the container format used in conjunction with RTMP
+ '-f', 'flv',
+
// The output RTMP URL.
// For debugging, you could set this to a filename like 'test.flv', and play
// the resulting file with VLC.
@@ -61,6 +61,8 @@ wss.on('connection', function connection(ws, req) {
// If FFmpeg stops for any reason, close the WebSocket connection.
ffmpeg.on('close', (code, signal) => {
console.log('FFmpeg child process closed, code ' + code + ', signal ' + signal);
+ //console.log("reconnecting...")
+ ws.send("ffmpegClosed")
ws.terminate();
});
diff --git a/liveJoin.js b/liveJoin.js
index eaf241e..ed08a15 100644
--- a/liveJoin.js
+++ b/liveJoin.js
@@ -2,7 +2,9 @@ const puppeteer = require('puppeteer');
const Xvfb = require('xvfb');
var exec = require('child_process').exec;
const fs = require('fs');
-const homedir = require('os').homedir();
+const os = require('os');
+const homedir = os.homedir();
+const platform = os.platform();
var xvfb = new Xvfb({
silent: true,
@@ -26,16 +28,24 @@ var options = {
],
}
+if(platform == "linux"){
+ options.executablePath = "/usr/bin/google-chrome"
+}else if(platform == "darwin"){
+ options.executablePath = "/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
+}
+
async function main() {
try{
- xvfb.startSync()
+ if(platform == "linux"){
+ xvfb.startSync()
+ }
var url = process.argv[2],
exportname = process.argv[3],
duration = process.argv[4],
convert = process.argv[5]
- if(!url){ url = 'http://tobiasahlin.com/spinkit/' }
- if(!exportname){ exportname = 'spinner.webm' }
+ if(!url){ url = 'https://www.mynaparrot.com/' }
+ if(!exportname){ exportname = 'live.webm' }
//if(!duration){ duration = 10 }
if(!convert){ convert = false }
@@ -87,7 +97,10 @@ async function main() {
await page.waitForSelector('html.downloadComplete', {timeout: 0})
await page.close()
await browser.close()
- xvfb.stopSync()
+
+ if(platform == "linux"){
+ xvfb.stopSync()
+ }
if(convert){
convertAndCopy(exportname)
@@ -118,7 +131,7 @@ function convertAndCopy(filename){
console.log(copyTo);
console.log(copyFrom);
- var cmd = "ffmpeg -y -i '" + copyFrom + "' -preset veryfast -movflags faststart -profile:v high -level 4.2 -max_muxing_queue_size 9999 -vf mpdecimate -vsync vfr '" + copyTo + "'";
+ var cmd = "ffmpeg -y -i '" + copyFrom + "' -c:v libx264 -preset veryfast -movflags faststart -profile:v high -level 4.2 -max_muxing_queue_size 9999 -vf mpdecimate -vsync vfr '" + copyTo + "'";
console.log("converting using: " + cmd);
diff --git a/liveRTMP.js b/liveRTMP.js
index 4e35857..7c83b23 100644
--- a/liveRTMP.js
+++ b/liveRTMP.js
@@ -3,7 +3,9 @@ const Xvfb = require('xvfb');
var exec = require('child_process').exec;
const fs = require('fs');
var config = JSON.parse(fs.readFileSync("config.json", 'utf8'));
-const homedir = require('os').homedir();
+const os = require('os');
+const homedir = os.homedir();
+const platform = os.platform();
const ffmpegServer = config.ffmpegServer + ":" + config.ffmpegServerPort + "/auth/" + config.auth;
@@ -28,15 +30,22 @@ var options = {
`--window-size=${width},${height}`,
],
}
+if(platform == "linux"){
+ options.executablePath = "/usr/bin/google-chrome"
+}else if(platform == "darwin"){
+ options.executablePath = "/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome"
+}
async function main() {
try{
- xvfb.startSync()
+ if(platform == "linux"){
+ xvfb.startSync()
+ }
var url = process.argv[2],
duration = process.argv[3],
exportname = 'liveMeeting.webm'
- if(!url){ url = 'http://tobiasahlin.com/spinkit/' }
+ if(!url){ url = 'https://www.mynaparrot.com/' }
//if(!duration){ duration = 10 }
const browser = await puppeteer.launch(options)
@@ -92,8 +101,11 @@ async function main() {
await page.waitForSelector('html.downloadComplete', {timeout: 0})
await page.close()
await browser.close()
- xvfb.stopSync()
+ if(platform == "linux"){
+ xvfb.stopSync()
+ }
+
fs.unlinkSync(homedir + "/Downloads/liveMeeting.webm");
}catch(err) {