"use strict";

function totalLength(a) {
    for (var b = 0, c = 0; c < a.length; c++) b += a[c].length;
    return b;
}

Object.defineProperty(exports, "__esModule", {
    value: !0
});

var dgram_1 = require("dgram"), HttpStreamer = function() {
    function a() {
        this._udpClient = null, this._outputUDPClient = null, this._options = null, this._srcStream = null, this._httpClient = new Map(), this._startBuffer = null, this._chunkSize = 0, 
        this._serialNumber = 0, this._streamId = 0, this._outputUDPQueue = [], this._outputUDPMaxRate = 0, this._outputUDPBytes = 0, this._timer = null, this._startTime = 0, 
        this._measureStartTime = 0, this._measuredBytes = 0, this._lastOutputUDPTime = 0, this._lastTickTime = 0, this._bytesToSendInTick = 0, this._isChainedSending = !1, 
        this._debug = !1, this.maxBufferSize = 512e3, this.bytesRead = 0, this.packetDropRatio = 0, this.push = this.push.bind(this), this._packetHeader = new Buffer(2), 
        this._packetHeader.write("mr");
    }
    return a.prototype.push = function(a) {
        var b = this;
        return this._outputUDPClient && this._maybeInitTimer(), this.packetDropRatio >= 1 ? void console.warn("HttpStreamer:drop") : this.packetDropRatio > 0 && Math.random() < this.packetDropRatio ? void console.warn("HttpStreamer:drop:" + this.packetDropRatio) : (this._startBuffer && (totalLength(this._startBuffer) > .8 * this.maxBufferSize ? (this._startBuffer = null, 
        console.warn("HttpStreamer::push: startBuffer is full, drop it")) : this._startBuffer.push(a)), this._httpClient.forEach(function(c, d) {
            function e() {
                if (!c.sending && c.queue.length) {
                    var a = c.queue.shift();
                    c.sending = !0, d.write(a, function(a) {
                        a && console.error("[error]HttpStreamer::push:res.wirte:id:" + c.id, a), c.sending = !1, e();
                    });
                }
            }
            var f = totalLength(c.queue);
            f > b.maxBufferSize && (console.warn("HttpStreamer::push:dropt data of " + f + " bytes, id: " + c.id), c.queue.length = 0), c.queue.push(a), e();
        }), void this._sendMrdp(a));
    }, a.prototype.addHttpClient = function(a, b) {
        var c = this;
        console.log("HttpStreamer::addHttpClient:id:" + b), this._httpClient.set(a, {
            response: a,
            sending: !1,
            id: b + "",
            queue: this._startBuffer ? this._startBuffer.slice(0) : []
        }), a.on("close", function() {
            return c._httpClient["delete"](a);
        });
    }, a.prototype.readStream = function(a, b) {
        var c = this;
        if (console.log("HttpStreamer::readStream"), this._udpClient || this._srcStream) throw "already listening";
        this._options = b || {}, this._startBuffer = [], this._chunkSize = this._options.chunkSize || 752, this._streamId = Math.round(Date.now() / 100) % 65536, this._serialNumber = 0, 
        this._srcStream = a;
        var d = function() {
            if (c._srcStream !== a) return void a.removeListener("readable", d);
            for (var b; b = a.read(c._chunkSize); ) c.bytesRead += b.length, c.push(b);
        };
        a.on("readable", d), this._prepareUdpOutput();
    }, a.prototype.listenUdp = function(a) {
        var b = this;
        return console.log("HttpStreamer::listenUdp:port:" + a.listenPort), new Promise(function(c, d) {
            if (b._udpClient || b._srcStream) return void d("already listening");
            b._options = a, b._startBuffer = [];
            var e = b._udpClient = dgram_1.createSocket("udp4");
            e.on("error", function(a) {
                console.error("HttpStreamer:listenUdp:", a), b.stop(), d(a);
            }), e.on("listening", c), e.on("message", function(a, c) {
                b.push(a);
            }), e.on("close", function() {
                console.log("HttpStreamer:listenUdp:closed"), e === b._udpClient && (b._udpClient = null);
            });
            try {
                a.listenMulticastAddress && e.addMembership(a.listenMulticastAddress), e.bind(a.listenPort), b._prepareUdpOutput();
            } catch (f) {
                console.error("HttpStreamer:listenUdp:bind:", f), b.stop(), d(f);
            }
        });
    }, a.prototype.stop = function() {
        var a = this;
        return console.log("HttpStreamer::stop"), this._startBuffer = null, clearInterval(this._timer), this._timer = null, this._outputUDPQueue = [], new Promise(function(b, c) {
            a._outputUDPClient && (a._outputUDPClient.close(), a._outputUDPClient = null), a._udpClient ? (a._udpClient.on("close", b), a._udpClient.close()) : a._srcStream ? (a._srcStream = null, 
            b(null)) : b(null);
        });
    }, a.prototype._maybeInitTimer = function() {
        null == this._timer && (this._timer = setInterval(this._onTimer.bind(this), 1), this._startTime = this._measureStartTime = Date.now(), this._measuredBytes = 0, 
        this._outputUDPMaxRate = 187.5);
    }, a.prototype._onTimer = function() {
        var a = Date.now();
        a - this._measureStartTime > 1e3 && this._adjustUDPMaxRate();
        var b = this._lastTickTime > 0 ? a - this._lastTickTime : 10;
        this._lastTickTime = a;
        var c = b * this._outputUDPMaxRate;
        this._bytesToSendInTick = Math.max(this._bytesToSendInTick, c), this._debug && console.log("_onTimer:\t" + b + "\t" + this._outputUDPQueue.length + "\t" + c), this._isChainedSending || (this._debug && console.log("_onTimer:start _chainedSendUDP"), 
        this._chainedSendUDP());
    }, a.prototype._chainedSendUDP = function() {
        var a = this;
        this._isChainedSending = !0;
        var b = this._outputUDPQueue[0];
        return b ? this._bytesToSendInTick <= 0 ? (this._isChainedSending = !1, void (this._debug && console.log("_chainedSendUDP:end:no quota"))) : (this._outputUDPQueue.shift(), 
        this._bytesToSendInTick -= b.length, void (this._outputUDPClient && this._outputUDPClient.send(b, 0, b.length, this._options.outputPort, this._options.outputMulticastAddress, function(c) {
            c && console.log(c);
            var d = Date.now(), e = d - a._lastOutputUDPTime;
            a._lastOutputUDPTime = d, a._outputUDPBytes += b.length, a._debug && console.log(">>>send:\t" + (a._lastOutputUDPTime - a._startTime) + "\t" + a._outputUDPBytes + "\t" + e), 
            a._bytesToSendInTick > 0 ? a._chainedSendUDP() : (a._isChainedSending = !1, a._debug && console.log("_chainedSendUDP:end:no quota"));
        }))) : (this._isChainedSending = !1, void (this._debug && console.log("_chainedSendUDP:end:no pack")));
    }, a.prototype._adjustUDPMaxRate = function() {
        var a = Date.now(), b = a - this._measureStartTime, c = this._measuredBytes / b;
        this._outputUDPMaxRate = 1.5 * c, console.log("HttpStreamer:_adjustUDPMasRate: input rate(kbps): " + 8 * c + ", max udp rate(kbps): " + 8 * this._outputUDPMaxRate), 
        this._measuredBytes = 0, this._measureStartTime = a;
    }, a.prototype._prepareUdpOutput = function() {
        var a = this;
        try {
            if (this._options.outputMulticastAddress && this._options.outputPort) {
                this._outputUDPClient && this._outputUDPClient.close();
                var b = this._outputUDPClient = dgram_1.createSocket("udp4");
                b.addMembership(this._options.outputMulticastAddress), console.log("HttpStreamer:_prepareUdpOutput:" + this._options.outputMulticastAddress + ":" + this._options.outputPort), 
                b.on("close", function() {
                    console.log("HttpStreamer:_prepareUdpOutput:closed"), b === a._outputUDPClient && (a._outputUDPClient = null);
                }), b.on("error", function(b) {
                    console.error("HttpStreamer:_prepareUdpOutput", b), a.stop();
                });
            } else console.warn("HttpStreamer:_prepareUdpOutput:not enough options, abort!");
        } catch (c) {
            console.error("HttpStreamer:_prepareUdpOutput:", c), this.stop();
        }
    }, a.prototype._sendPlainUdp = function(a) {
        this._outputUDPClient && this._outputUDPClient.send(a, 0, a.length, this._options.outputPort, this._options.outputMulticastAddress);
    }, a.prototype._sendMrdp = function(a) {
        var b = this, c = new Buffer(a.length + 8);
        this._packetHeader.copy(c), c.writeUInt16LE(this._streamId, 2), c.writeUInt32LE(this._serialNumber, 4), this._serialNumber = (this._serialNumber + 1) % kMaxInt, 
        a.copy(c, 8);
        var d = function() {
            b._outputUDPQueue.push(c), b._measuredBytes += c.length;
        };
        d(), setTimeout(d, 250), setTimeout(d, 425);
    }, a;
}();

exports.HttpStreamer = HttpStreamer;

var kMaxInt = Math.pow(2, 31);
//# sourceMappingURL=HttpStreamer.js.map