I'm trying to play audio mp3 file from webserver without downloading it to the filesystem. This is how I get this file and play it:
var url = require('url');
var http = require('http');
var player = require('./player.js');
function initPlayback(res) {
player.play(res);
player.onPlaybackEnds(function() {
console.log('Playback ends.');
});
}
exports.add = function(file_url) {
var options = {
host: url.parse(file_url).host,
port: 80,
path: url.parse(file_url).pathname
};
http.get(options, function(res) {
initPlayback(res);
});
};
player.js:
var fs = require('fs');
var lame = require('lame');
var Speaker = require('speaker');
var options = {
audio: {
channels: 2,
bitDepth: 16,
sampleRate: 44100
}
};
var decoderMp3 = lame.Decoder();
var speaker = new Speaker(options);
var onPlaybackEnds = function() {};
function playMp3(dataStream) {
speaker = new Speaker(options);
decoderMp3 = lame.Decoder();
dataStream.pipe(decoderMp3).pipe(speaker);
speaker.on('flush', function() {
onPlaybackEnds();
});
}
exports.play = function(dataStream) {
playMp3(dataStream);
};
exports.onPlaybackEnds = function(callback) {
onPlaybackEnds = callback;
};
The output I got from running the add("http://localhost/file.mp3") is:
Note: Illegal Audio-MPEG-Header 0x41504554 at offset 201915.
Note: Trying to resync...
Note: Hit end of (available) data during resync.
Playback ends.
It seems that before flush event some error happens. Track plays until the end. Yet when I try to run the same thing, it doesn't even start. Any ideas?
Related
I am using MediaRecorder to record chunks of my live video in webm format from MediaStream and converting these chunks to .ts files on the server using ffmpeg and then updating my playlist.m3u8 file with this code:
function generateM3u8Playlist(fileDataArr, playlistFp, isLive, cb) {
var durations = fileDataArr.map(function(fd) {
return fd.duration;
});
var maxT = maxOfArr(durations);
var meta = [
'#EXTM3U',
'#EXT-X-VERSION:3',
'#EXT-X-MEDIA-SEQUENCE:0',
'#EXT-X-ALLOW-CACHE:YES',
'#EXT-X-TARGETDURATION:' + Math.ceil(maxT),
];
fileDataArr.forEach(function(fd) {
meta.push('#EXTINF:' + fd.duration.toFixed(2) + ',');
meta.push(fd.fileName2);
});
if (!isLive) {
meta.push('#EXT-X-ENDLIST');
}
meta.push('');
meta = meta.join('\n');
fs.writeFile(playlistFp, meta, cb);
}
Here fileDataArr holds information for all the chunks that have been created.
After that i use this code to create a hls server :
var runStreamServer = (function(streamFolder) {
var executed = false;
return function(streamFolder) {
if (!executed) {
executed = true;
var HLSServer = require('hls-server')
var http = require('http')
var server = http.createServer()
var hls = new HLSServer(server, {
path: '/stream', // Base URI to output HLS streams
dir: 'C:\\Users\\Work\\Desktop\\live-stream\\webcam2hls\\videos\\' + streamFolder // Directory that input files are stored
})
console.log("We are going to stream from folder:" + streamFolder);
server.listen(8000);
console.log('Server Listening on Port 8000');
}
};
})();
The problem is that if i stop creating new chunks and then use the hls server link:
http://localhost:8000/stream/playlist.m3u8 then the video plays in VLC but if i try to play during the recording it keeps loading the file but does not play. I want it to play while its creating new chunks and updating playlist.m3u8. The quirk in generateM3u8Playlist function is that it adds '#EXT-X-ENDLIST' to the playlist file after i have stopped recording.
The software is still in production so its a bit messy code. Thank you for any answers.
The client side that generates blobs is as follows:
var mediaConstraints = {
video: true,
audio:true
};
navigator.getUserMedia(mediaConstraints, onMediaSuccess, onMediaError);
function onMediaSuccess(stream) {
console.log('will start capturing and sending ' + (DT / 1000) + 's videos when you press start');
var mediaRecorder = new MediaStreamRecorder(stream);
mediaRecorder.mimeType = 'video/webm';
mediaRecorder.ondataavailable = function(blob) {
var count2 = zeroPad(count, 5);
// here count2 just creates a blob number
console.log('sending chunk ' + name + ' #' + count2 + '...');
send('/chunk/' + name + '/' + count2 + (stopped ? '/finish' : ''), blob);
++count;
};
}
// Here we have the send function which sends our blob to server:
function send(url, blob) {
var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'text/plain';
xhr.setRequestHeader('Content-Type', 'video/webm');
//xhr.setRequestHeader("Content-Length", blob.length);
xhr.onload = function(e) {
if (this.status === 200) {
console.log(this.response);
}
};
xhr.send(blob);
}
The code that receives the XHR request is as follows:
var parts = u.split('/');
var prefix = parts[2];
var num = parts[3];
var isFirst = false;
var isLast = !!parts[4];
if ((/^0+$/).test(num)) {
var path = require('path');
shell.mkdir(path.join(__dirname, 'videos', prefix));
isFirst = true;
}
var fp = 'videos/' + prefix + '/' + num + '.webm';
var msg = 'got ' + fp;
console.log(msg);
console.log('isFirst:%s, isLast:%s', isFirst, isLast);
var stream = fs.createWriteStream(fp, { encoding: 'binary' });
/*stream.on('end', function() {
respond(res, ['text/plain', msg]);
});*/
//req.setEncoding('binary');
req.pipe(stream);
req.on('end', function() {
respond(res, ['text/plain', msg]);
if (!LIVE) { return; }
var duration = 20;
var fd = {
fileName: num + '.webm',
filePath: fp,
duration: duration
};
var fileDataArr;
if (isFirst) {
fileDataArr = [];
fileDataArrs[prefix] = fileDataArr;
} else {
var fileDataArr = fileDataArrs[prefix];
}
try {
fileDataArr.push(fd);
} catch (err) {
fileDataArr = [];
console.log(err.message);
}
videoUtils.computeStartTimes(fileDataArr);
videoUtils.webm2Mpegts(fd, function(err, mpegtsFp) {
if (err) { return console.error(err); }
console.log('created %s', mpegtsFp);
var playlistFp = 'videos/' + prefix + '/playlist.m3u8';
var fileDataArr2 = (isLast ? fileDataArr : lastN(fileDataArr, PREV_ITEMS_IN_LIVE));
var action = (isFirst ? 'created' : (isLast ? 'finished' : 'updated'));
videoUtils.generateM3u8Playlist(fileDataArr2, playlistFp, !isLast, function(err) {
console.log('playlist %s %s', playlistFp, (err ? err.toString() : action));
});
});
runStreamServer(prefix);
}
You don't show us how you use MediaRecorder to generate your "chunks" of data. Do you you use its ondataavailable event for this purpose?
If so, please keep this in mind: You must concatenate all the chunks handed to you by ondataavailable to get a valid .webm (or .matroska) data stream.
You can't just store an arbitrary chunk of data in a media file and expect it to play. Even ffmpeg needs all your chunks streamed to it to generate valid output. That's because the first couple of chunks contain the mandatory .webm initialization segment, and the other chunks do not.
I am trying to stream MP3 file from a nodeJS server using BinaryJS - http://binaryjs.com/
But, when I am decoding the buffers on the client side they are seems to be overlapping, Meaning that the new chunk of data is being played few milliseconds before the previous one ended, causing the audio to lag.
is there any way to make the client wait until the current buffer is finished before starting the new one?
Server:
var BinaryServer = require('binaryjs').BinaryServer;
var fs = require('fs');
var server = BinaryServer({port: 9000});
server.on('connection', function(client){
var file = fs.createReadStream(__dirname + '/Song.mp3', {
'flags': 'r',
'bufferSize': 4 * 1024
});
});
client.send(file);
});
Client:
var client = new BinaryClient('ws://localhost:9000');
window.AudioContext = window.AudioContext || window.webkitAudioContext;
var context = new AudioContext();
client.on('stream', function (stream, meta) {
var parts = [];
var last = 0;
stream.on('data', function (data) {
var source = context.createBufferSource();
context.decodeAudioData(data, function (buf) {
source.buffer = buf;
source.connect(context.destination);
source.loop = false;
source.start(last);
last += buf.duration;
source.onended = function() {
console.log('Your audio has finished playing');
};
},
function (e) {
"Error with decoding audio data" + e.err
});
parts.push(data);
});
stream.on('end', function () {
console.log(parts);
});
});
Not sure about this, but instead of initializing last to 0, you might want to initialize it to context.currentTime.
I have created WebSocket.js but unfortunately I am unable to test it due to unavailability of data. The requirement is the data comes from various sources so I have multiple sockets for that. Note: I want to particularly test my socket.onMessage behaves for different sockets. Please find the code snippet below:
var webSocket;
var txQueue = [];
var defaultReconnectTimeout = 1000; //will be multiplied by 2 and saved into reconnectTimeout on each try
var reconnectTimeout = defaultReconnectTimeout;
var registerWebSocketHandlers = function(webSocket) {
webSocket.onclose = function(){
setTimeout(service.reopen, reconnectTimeout *= 2);
};
webSocket.onopen = function(e) {
reconnectTimeout = defaultReconnectTimeout; //reset this
deferredSend();
};
webSocket.onerror = function(e) {
throw new Error("[WebSocket] An error occured " + e);
};
}
var uniqid = function() {
return (new Date().getTime()).toString(16);
}
var deferredSend = function() {
if(!service.isOpen()) {
$timeout(deferredSend, 100);
return;
}
while(txQueue.length && service.isOpen()) {
var payload = txQueue.shift();
webSocket.send(typeof payload === 'string' ? payload : JSON.stringify(payload));
}
};
var createNewWebSocketInstance = function(apiUrl){
var websocket = new $window.WebSocket(apiUrl);
websocket.id = uniqid();
return websocket;
}
// TODO: this is a bit hacky since we directly bind it to the raw window event
$window.onbeforeunload = function(e) {
service.close();
};
var service = {};
service.setMessageEventHandler = function(name,cb) {
instances[name].onmessage = function(msg) {
if(msg.data.indexOf('Status: connected') === 0)
{
return;
}
var jsonObj = JSON.parse(msg.data);
cb(jsonObj);
};
};
service.isOpen = function() {
return webSocket.readyState === 1;
};
service.send = function(msg) {
txQueue.push(msg);
deferredSend();
};
service.close = function() {
return webSocket.close();
};
service.reopen = function() {
// get old message handler
var msgHandler = webSocket.onmessage;
// try closing the previous WebSocket
service.close();
// open new WebSocket
openConnection();
// re-attach old handler to new WebSocket
webSocket.onmessage = msgHandler;
};
service.getId = function() {
return webSocket.id;
}
// Returns an already existing instance of the socket, if unavailable then creates a new one.
service.getInstance = function(name, config) {
if(!(name in instances)) {
instances[name] = createNewWebSocketInstance(config);
}
registerWebSocketHandlers(instances[name]);
return instances[name];
};
return service;
You can test WebSocket using websocket.html at websocket.org Echo Test Creating your own test
Using a text editor, copy the following code and save it as
websocket.html somewhere on your hard drive. Then simply open it in a browser. The page will automatically connect, send a message,
display the response, and close the connection.
See Linux - WebSocket test error.
I have the following code snippet which I need to extend to define multiple WebSockets and I am clueless as to how do I go about it:
var registerWebSocketHandlers = function(webSocket) {
webSocket.onclose = function(){
setTimeout(service.reopen, reconnectTimeout *= 2);
};
webSocket.onopen = function(e) {
icc.publish('webSocket.reconnect');
reconnectTimeout = defaultReconnectTimeout; //reset this
deferredSend();
};
webSocket.onerror = function(e) {
throw new Error("[WebSocket] An error occured " + e);
};
}
var openConnection = function() {
connectionWasOpenBefore = true;
webSocket = new $window.WebSocket(xyz);
webSocket.id = uniqid();
registerWebSocketHandlers(webSocket);
};
var uniqid = function() {
return (new Date().getTime()).toString(16);
}
service.setMessageEventHandler = function(cb) {
webSocket.onmessage = function(msg) {
if(msg.data.indexOf('Status: connected') === 0)
{
return;
}
var jsonObj = JSON.parse(msg.data);
cb(jsonObj);
};
};
How do I twist the code to suit the needs of multiple WebSockets and attaching the appropriate callback to it?
Use the multiton pattern.
var socketFactory = module.factory('SocketFactory', function($rootScope){
var factory = {};
var instances = {};
factory.getInstance = function(name, config){
if(!(name in instances)){
instances[name] = createNewWebSocketInstance(name, config);
}
return instances[name];
};
var createNewWebSocketInstance = function(name, config){
var webSocket = new $window.WebSocket(config.address);
webSocket.id = uniqid();
registerWebSocketHandlers(webSocket, name, config.handlers); //etc.
return webSocket;
};
var registerWebSocketHandlers = function(webSocket, name, handlers){
webSocket.onmessage = function(event){
$rootScope.$emit('SocketMessageReceived_' + name, event.data);
};
//etc...
};
return factory;
});
This will separate your different websockets by name. Use getInstance('whatever') to get a websocket labelled as 'whatever'.
var firstConfig = {url: '*****', username: '****', password: '****', etc: '****'};
// You only need to pass in the config the first time.
var firstWebSocket = SocketFactory.getInstance('firstSocket', firstConfig);
var secondConfig = {url: '####', username: '####', password: '####', etc: '####'};
var secondWebSocket = SocketFactory.getInstance('secondSocket', secondConfig);
Next, from any other area you can access the configured websockets by their instance names.
var firstWebSocket = SocketFactory.getInstance('firstSocket');
// It would probably be a good idea to add this listener in the SocketFactory instead and broadcast an event when there's a message so multiple listeners can respond to it.
firstWebSocket.onmessage = function(){...};
var secondWebSocket = SocketFactory.getInstance('secondSocket');
secondWebSocket.onmessage = function(){...};
I've tried to search through stackoverflow for a similar question but most people are asking about the client-side of the NTLMv2 protocol.
I'm implementing a proxy that is performing the server-side of the protocol to authenticate users connecting to the proxy.
I've coded a lot of the protocol but I'm now stuck because the documentation that should take me further is difficult to understand.
This is the best documentation I've found so far: http://www.innovation.ch/personal/ronald/ntlm.html, but how to deal with the LM and NT responses is oblivious to me.
The proxy is located on an application server. The domain server is a different machine.
Example code for the node proxy:
var http = require('http')
, request = require('request')
, ProxyAuth = require('./proxyAuth');
function handlerProxy(req, res) {
ProxyAuth.authorize(req, res);
var options = {
url: req.url,
method: req.method,
headers: req.headers
}
req.pipe(request(options)).pipe(res)
}
var server = http.createServer(handlerProxy);
server.listen(3000, function(){
console.log('Express server listening on port ' + 3000);
});
ProxyAuth.js code:
ProxyAuth = {
parseType3Msg: function(buf) {
var lmlen = buf.readUInt16LE(12);
var lmoff = buf.readUInt16LE(16);
var ntlen = buf.readUInt16LE(20);
var ntoff = buf.readUInt16LE(24);
var dlen = buf.readUInt16LE(28);
var doff = buf.readUInt16LE(32);
var ulen = buf.readUInt16LE(36);
var uoff = buf.readUInt16LE(40);
var hlen = buf.readUInt16LE(44);
var hoff = buf.readUInt16LE(48);
var domain = buf.slice(doff, doff+dlen).toString('utf8');
var user = buf.slice(uoff, uoff+ulen).toString('utf8');
var host = buf.slice(hoff, hoff+hlen).toString('utf8');
var lmresp = buf.slice(lmoff, lmoff+lmlen).toString('utf8');
var ntresp = buf.slice(ntoff, ntoff+ntlen).toString('utf8');
console.log(user, lmresp, ntresp);
/* NOW WHAT DO I DO? */
},
authorize: function(req, res) {
var auth = req.headers['authorization'];
if (!auth) {
res.writeHead(401, {
'WWW-Authenticate': 'NTLM',
});
res.end('<html><body>Proxy Authentication Required</body></html>');
}
else if(auth) {
var header = auth.split(' ');
var buf = new Buffer(header[1], 'base64');
var msg = buf.toString('utf8');
console.log("Decoded", msg);
if (header[0] == "NTLM") {
if (msg.substring(0,8) != "NTLMSSP\x00") {
res.writeHead(401, {
'WWW-Authenticate': 'NTLM',
});
res.end('<html><body>Header not recognized</body></html>');
}
// Type 1 message
if (msg[8] == "\x01") {
console.log(buf.toString('hex'));
var challenge = require('crypto').randomBytes(8);
var type2msg = "NTLMSSP\x00"+
"\x02\x00\x00\x00"+ // 8 message type
"\x00\x00\x00\x00"+ // 12 target name len/alloc
"\x00\x00\x00\x00"+ // 16 target name offset
"\x01\x82\x00\x00"+ // 20 flags
challenge.toString('utf8')+ // 24 challenge
"\x00\x00\x00\x00\x00\x00\x00\x00"+ // 32 context
"\x00\x00\x00\x00\x00\x00\x00\x00"; // 40 target info len/alloc/offset
type2msg = new Buffer(type2msg).toString('base64');
res.writeHead(401, {
'WWW-Authenticate': 'NTLM '+type2msg.trim(),
});
res.end();
}
else if (msg[8] == "\x03") {
console.log(buf.toString('hex'));
ProxyAuth.parseType3Msg(buf);
/* NOW WHAT DO I DO? */
}
}
else if (header[0] == "Basic") {
}
}
}
};
module.exports = ProxyAuth;
The /* NOW WHAT DO I DO? */ comment specifies where I am stuck.
I hope I put enough information there, but let me know if anything else is needed.