Sunday, February 24, 2019

https nodejs rtmp publisher use openssl

https nodejs rtmp publisher use openssl



新增https 功能
由於呢在chrome 呢 如果調用getUserMedia api 則必需要https 代理來小改造一下

<!doctype>
<html>
<head>
<title>MediaRecorder to RTMP Demo</title>
<script src="/socket.io/socket.io.js"></script>
</head>
<body style="max-width:800px;height:100%;margin:auto;">
<h1>
MediaRecorder to RTMP Demo
</h1>
<label for="option_width" >Size:</label>
<input type="text" id="option_width" value="480"/> &times;
<input type="text" id="option_height" value="360"/>
<br>
<label for="option_url" >RTMP Destination:</label>
<input type="text" id="option_url" value="rtmp://localhost/live"/>
<br>
<button id="button_start">Start streaming</button>
<hr/>
<div>
<p id="output_message"></p>
<video id="output_video" autoplay=true></video>
</div>
<hr/>
<textarea readonly="true" id="output_console" cols=91 rows=5>
</textarea>
<script>
function fail(str){alert(str+"\nPlease download the latest version of Firefox!");location.replace('http://mozilla.org/firefox');}
var output_console=document.getElementById('output_console'),
output_message=document.getElementById('output_message'),
output_video=document.getElementById('output_video'),
option_url=document.getElementById('option_url'),
option_width=document.getElementById('option_width'),
option_height=document.getElementById('option_height'),
button_start=document.getElementById('button_start'),
height=option_height.value,
width=option_width.value,
url=option_url.value='rtmp://'+location.host.split(':')[0]+':1935/live/test';
option_height.onchange=option_height.onkeyup=function(){height=1*this.value;}
option_width.onchange=option_width.onkeyup=function(){width=1*this.value;}
option_url.onchange=option_url.onkeyup=function(){url=this.value;}
button_start.onclick=requestMedia;
function video_show(stream){
if ("srcObject" in output_video) {
output_video.srcObject = stream;
} else {
output_video.src = window.URL.createObjectURL(stream);
}
output_video.addEventListener( "loadedmetadata", function (e) {
output_message.innerHTML="Local video source size:"+output_video.videoWidth+"x"+output_video.videoHeight;
}, false );
}
function show_output(str){
output_console.value+="\n"+str;
output_console.scrollTop = output_console.scrollHeight;
}
navigator.getUserMedia = (navigator.getUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia ||
navigator.webkitGetUserMedia);
if(!navigator.getUserMedia){fail('No getUserMedia() available.');}
if(!MediaRecorder){fail('No MediaRecorder available.');}
var mediaRecorder;
var socket = io.connect("https://localhost", {secure: true});
//({
// option 1
// ca: fs.readFileSync("https://localhost:443",'./abels-cert.pem'),
// option 2. WARNING: it leaves you vulnerable to MITM attacks!
// requestCert: false,
// rejectUnauthorized: false
//});
//var socket = io.
socket.on('message',function(m){
console.log('recv server message',m);
show_output('SERVER:'+m);
});
socket.on('fatal',function(m){
show_output('ERROR: unexpected:'+m);
alert('Error:'+m);
mediaRecorder.stop();
//should reload?
});
socket.on('ffmpeg_stderr',function(m){
show_output('FFMPEG:'+m);
});
socket.on('disconnect', function () {
show_output('ERROR: server disconnected!');
mediaRecorder.stop();
});
function requestMedia(){
var constraints = { audio: true,
video:{
width: { min: width, ideal: width, max: width },
height: { min: height, ideal: height, max: height },
}
};
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
video_show(stream);//only show locally, not remotely
socket.emit('config_rtmpDestination',url);
socket.emit('start','start');
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start(2000);
mediaRecorder.onstop = function(e) {
stream.stop();
}
mediaRecorder.ondataavailable = function(e) {
socket.emit("binarystream",e.data);
//chunks.push(e.data);
}
}).catch(function(err) {
console.log('The following error occured: ' + err);
show_output('Local getUserMedia ERROR:'+err);
});
}
</script>
</body>
</html>
view raw index.html hosted with ❤ by GitHub
var express = require('express');
var app = express();
//var http = require('http').Server(app);
//var ffmpeg = require('fluent-ffmpeg');
//var stream = require('stream');
var spawn = require('child_process').spawn;
var fs = require('fs');
app.use(express.static('static'));
const server = require('https').createServer({
key: fs.readFileSync('abels-key.pem'),
cert: fs.readFileSync('abels-cert.pem')
},app);
//testing
var io = require('socket.io')(server);
spawn('ffmpeg',['-h']).on('error',function(m){
console.error("FFMpeg not found in system cli; please install ffmpeg properly or make a softlink to ./!");
process.exit(-1);
});
io.on('connection', function(socket){
socket.emit('message','Hello from mediarecorder-to-rtmp server!');
socket.emit('message','Please set rtmp destination before start streaming.');
var ffmpeg_process, feedStream=false;
socket.on('config_rtmpDestination',function(m){
if(typeof m != 'string'){
socket.emit('fatal','rtmp destination setup error.');
return;
}
var regexValidator=/^rtmp:\/\/[^\s]*$/;//TODO: should read config
if(!regexValidator.test(m)){
socket.emit('fatal','rtmp address rejected.');
return;
}
socket._rtmpDestination=m;
socket.emit('message','rtmp destination set to:'+m);
});
//socket._vcodec='libvpx';//from firefox default encoder
socket.on('config_vcodec',function(m){
if(typeof m != 'string'){
socket.emit('fatal','input codec setup error.');
return;
}
if(!/^[0-9a-z]{2,}$/.test(m)){
socket.emit('fatal','input codec contains illegal character?.');
return;
}//for safety
socket._vcodec=m;
});
socket.on('start',function(m){
if(ffmpeg_process || feedStream){
socket.emit('fatal','stream already started.');
return;
}
if(!socket._rtmpDestination){
socket.emit('fatal','no destination given.');
return;
}
var ops=[
'-i','-',
'-c:v', 'libx264', '-preset', 'veryfast', '-tune', 'zerolatency',
'-an', //TODO: give up audio for now...
//'-async', '1',
'-filter_complex', 'aresample=44100', //necessary for trunked streaming?
//'-strict', 'experimental', '-c:a', 'aac', '-b:a', '128k',
'-bufsize', '1000',
'-f', 'flv', socket._rtmpDestination
];
console.log(socket._rtmpDestination);
ffmpeg_process=spawn('ffmpeg', ops);
feedStream=function(data){
ffmpeg_process.stdin.write(data);
//write exception cannot be caught here.
}
ffmpeg_process.stderr.on('data',function(d){
socket.emit('ffmpeg_stderr',''+d);
});
ffmpeg_process.on('error',function(e){
console.log('child process error'+e);
socket.emit('fatal','ffmpeg error!'+e);
feedStream=false;
socket.disconnect();
});
ffmpeg_process.on('exit',function(e){
console.log('child process exit'+e);
socket.emit('fatal','ffmpeg exit!'+e);
socket.disconnect();
});
});
socket.on('binarystream',function(m){
if(!feedStream){
socket.emit('fatal','rtmp not set yet.');
return;
}
feedStream(m);
});
socket.on('disconnect', function () {
feedStream=false;
if(ffmpeg_process)
try{
ffmpeg_process.stdin.end();
ffmpeg_process.kill('SIGINT');
}catch(e){console.warn('killing ffmoeg process attempt failed...');}
});
socket.on('error',function(e){
console.log('socket.io error:'+e);
});
});
io.on('error',function(e){
console.log('socket.io error:'+e);
});
//http.listen(8888, function(){
// console.log('http and websocket listening on *:8888');
//});
server.listen(443, function(){
console.log('https and websocket listening on *:443');
});
process.on('uncaughtException', function(err) {
// handle the error safely
console.log(err)
// Note: after client disconnect, the subprocess will cause an Error EPIPE, which can only be caught this way.
})
view raw server.js hosted with ❤ by GitHub