Files
discord.js/src/Voice/VoiceConnection.js
2015-11-07 22:02:33 +00:00

274 lines
6.3 KiB
JavaScript

"use strict";
var WebSocket = require("ws");
var dns = require("dns");
var udp = require("dgram");
var Opus = require('node-opus');
var fs = require("fs");
var ffmpeg = require('fluent-ffmpeg');
var AudioEncoder = require("./AudioEncoder.js");
var VoicePacket = require("./VoicePacket.js");
var StreamIntent = require("./StreamIntent.js");
var EventEmitter = require("events");
class VoiceConnection extends EventEmitter{
constructor(channel, client, session, token, server, endpoint) {
super();
if(!Opus){
console.log("HEY! WATCH OUT\n\n discord.js needs node-opus, you don't have it installed.");
}
this.id = channel.id;
this.voiceChannel = channel;
this.client = client;
this.session = session;
this.token = token;
this.server = server;
this.endpoint = endpoint.replace(":80", "");
this.vWS = null; // vWS means voice websocket
this.ready = false;
this.vWSData = {};
this.opus = new Opus.OpusEncoder(48000, 1);
this.encoder = new AudioEncoder();
this.udp = null;
this.playingIntent = null;
this.playing = false;
this.streamTime = 0;
this.streamProc = null;
this.init();
}
stopPlaying() {
this.playing=false;
this.playingIntent = null;
if(this.streamProc)
this.streamProc.kill();
}
playRawStream(stream) {
var self = this;
var startTime = Date.now();
var sequence = 0;
var time = 0;
var count = 0;
var length = 20;
if (self.playingIntent) {
self.stopPlaying();
}
self.playing = true;
var retStream = new StreamIntent();
var onWarning = false;
self.playingIntent = retStream;
function send() {
if (!self.playingIntent || !self.playing) {
self.setSpeaking(false);
retStream.emit("end");
return;
}
try {
var buffer = stream.read(1920);
if (!buffer) {
setTimeout(send, length * 10); // give chance for some data in 200ms to appear
return;
}
if (buffer.length !== 1920) {
if (onWarning) {
retStream.emit("end");
stream.destroy();
self.setSpeaking(false);
return;
} else {
onWarning = true;
setTimeout(send, length * 10); // give chance for some data in 200ms to appear
return;
}
}
count++;
sequence + 10 < 65535 ? sequence += 1 : sequence = 0;
time + 9600 < 4294967295 ? time += 960 : time = 0;
self.sendBuffer(buffer, sequence, time, (e) => { });
var nextTime = startTime + (count * length);
self.streamTime = count * length;
setTimeout(send, length + (nextTime - Date.now()));
if (!self.playing)
self.setSpeaking(true);
retStream.emit("time", self.streamTime);
} catch (e) {
retStream.emit("error", e);
}
}
self.setSpeaking(true);
send();
return retStream;
}
setSpeaking(value) {
this.playing = value;
this.vWS.send(JSON.stringify({
op: 5,
d: {
speaking: value,
delay: 0
}
}));
}
sendPacket(packet, callback = function (err) { }) {
var self = this;
self.playing = true;
try {
self.udp.send(packet, 0, packet.length, self.vWSData.port, self.endpoint, callback);
} catch (e) {
self.playing = false;
callback(e);
return false;
}
}
sendBuffer(rawbuffer, sequence, timestamp, callback) {
var self = this;
self.playing = true;
try {
var buffer = self.encoder.opusBuffer(rawbuffer);
var packet = new VoicePacket(buffer, sequence, timestamp, self.vWSData.ssrc);
return self.sendPacket(packet, callback);
} catch (e) {
self.playing = false;
self.emit("error", e);
return false;
}
}
test() {
this.playFile("C:/users/amish/desktop/audio.mp3")
.then(stream => {
stream.on("time", time => {
console.log("Time", time);
})
})
}
playFile(stream, callback = function (err, str) { }) {
var self = this;
return new Promise((resolve, reject) => {
this.encoder
.encodeFile(stream)
.catch(error)
.then(data => {
self.streamProc = data.proc;
var intent = self.playRawStream(data.stream);
resolve(intent);
callback(null, intent);
});
function error(e = true) {
reject(e);
callback(e);
}
})
}
init() {
var self = this;
dns.lookup(this.endpoint, (err, address, family) => {
self.endpoint = address;
var vWS = self.vWS = new WebSocket("wss://" + this.endpoint, null, { rejectUnauthorized: false });
var udpClient = self.udp = udp.createSocket("udp4");
var firstPacket = true;
var discordIP = "", discordPort = "";
udpClient.bind({ exclusive: true });
udpClient.on('message', function (msg, rinfo) {
var buffArr = JSON.parse(JSON.stringify(msg)).data;
if (firstPacket === true) {
for (var i = 4; i < buffArr.indexOf(0, i); i++) {
discordIP += String.fromCharCode(buffArr[i]);
}
discordPort = msg.readUIntLE(msg.length - 2, 2).toString(10);
var wsDiscPayload = {
"op": 1,
"d": {
"protocol": "udp",
"data": {
"address": discordIP,
"port": Number(discordPort),
"mode": self.vWSData.modes[0] //Plain
}
}
}
vWS.send(JSON.stringify(wsDiscPayload));
firstPacket = false;
}
});
vWS.on("open", () => {
vWS.send(JSON.stringify({
op: 0,
d: {
server_id: self.server.id,
user_id: self.client.internal.user.id,
session_id: self.session,
token: self.token
}
}));
});
var KAI;
vWS.on("message", (msg) => {
var data = JSON.parse(msg);
switch (data.op) {
case 2:
self.vWSData = data.d;
KAI = setInterval(() => {
vWS.send(JSON.stringify({
op: 3,
d: null
}));
}, data.d.heartbeat_interval);
var udpPacket = new Buffer(70);
udpPacket.writeUIntBE(data.d.ssrc, 0, 4);
udpClient.send(udpPacket, 0, udpPacket.length, data.d.port, self.endpoint, err => {
if(err)
self.emit("error", err)
});
break;
case 4:
self.ready = true;
self.mode = data.d.mode;
self.emit("ready", self);
break;
}
});
});
}
}
module.exports = VoiceConnection;