"use strict";

function onRequest(a, b) {
    console.log(a.method + " " + a.url);
    var c = utils_1.getPathParts(a.url), d = (mUrl.parse(a.url), router[a.method]), e = d ? d[c[0]] : null;
    if (d && !e && (e = d["@default"]), e) try {
        var f = e(a, b);
        f && f.then(function(a) {
            b.headersSent || (b.writeHead(200), b.end("" + a));
        }, function(a) {
            b.writeHead(500), b.end("" + a), console.error(a);
        });
    } catch (g) {
        b.writeHead(500), b.end("" + g), console.error(g);
    } else b.writeHead(404), b.end();
}

function handleGetScreenBroadcast(a, b) {
    console.log("handleGetScreenBroadcast: " + a.url);
    var c = utils_1.getPathParts(a.url), d = c[1] || utils_1.extractIpAddress(a.socket.remoteAddress);
    b.writeHead(200, "ok", {
        "Content-Type": "video/M2PT",
        "Cache-Control": "no-store"
    }), mediaService.addLiveStreamerClient(b, utils_1.isLocalAddress(a.socket.remoteAddress), d);
}

function handlePostRemoteDesktop(a, b) {
    return utils_1.readRequestBody(a).then(function(a) {
        return a ? mediaService.startScreenGrab() : mediaService.stopAll();
    });
}

function handleStatic(a, b) {
    b.writeHead(200), b.end("hello");
}

function main() {
    if (mPath.basename(process.argv[1]) === mPath.basename(module.filename)) {
        http_1.createServer().on("request", utils_1.trapError).on("request", onRequest).listen(kMainPort);
        console.log("listen " + kMainPort + ", test on http://localhost:" + kMainPort);
    }
}

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

var mPath = require("path"), http_1 = require("http"), mUrl = require("url"), os = require("os"), fs = require("fs"), utils_1 = require("./utils"), LiveStreamer_1 = require("./LiveStreamer"), kScreenBroadcastMaxWidth = 1280, kScreenBroadcastMaxHeight = 720, kIsWindows = os.type().toLowerCase().indexOf("windows") >= 0, MediaService = function() {
    function a() {
        var a = this;
        this.ffmpegCmd = "ffmpeg", this.dataDir = process.cwd(), this.bitrate = 0, this.width = 0, this.height = 0, this._recordSession = new RecordSession(), this._screenGrab = new utils_1.ProcWrapper("screenGrab", this.ffmpegCmd, []), 
        this._audioGrab = new utils_1.ProcWrapper("audioGrab", this.ffmpegCmd, []), this._liveStreamer = new LiveStreamer_1.LiveStreamer("screenGrab"), this._audioStreamer = new LiveStreamer_1.LiveStreamer("audioGrab"), 
        this._audioStreamer.maxBufferTime = 5, [ this._screenGrab, this._audioGrab ].forEach(function(b) {
            b.onCrash = function(c, d) {
                a.pauseAll().then(function() {
                    setImmediate(function() {
                        return a.onCrash(b.name, c, d);
                    }), a.isRecording && a._fireRecordError(new Error('MediaService: 子进程崩溃："' + b.name + '"， code=' + c + ", signal=" + d));
                });
            };
        }), this._audioStreamer.onInputTimeout = function() {
            a.isRecording && a.pauseRecord().then(function() {
                return a._fireRecordError(new Error("MediaService: 网络音频流输入超时"));
            });
        };
    }
    return Object.defineProperty(a.prototype, "isGrabbing", {
        get: function() {
            return this._screenGrab.isRunning;
        },
        enumerable: !0,
        configurable: !0
    }), a.prototype.stopAll = function() {
        var a = this;
        return this.pauseAll().then(function() {
            return a.removeRecord();
        });
    }, a.prototype.pauseAll = function() {
        var a = [ this._screenGrab, this._audioGrab ].map(function(a) {
            return a.stop();
        }).concat([ this._liveStreamer.stop(!0), this._audioStreamer.stop(!0), this.pauseRecord() ]);
        return Promise.all(a);
    }, a.prototype.startScreenGrab = function() {
        var a = this;
        this._screenGrab.isRunning ? console.log("startScreenGrab(): is running, will restart") : console.log("startScreenGrab(): starting"), this._liveStreamer.isRunning || this._liveStreamer.start();
        var b = this.width || kScreenBroadcastMaxWidth, c = this.height || kScreenBroadcastMaxHeight, d = "min(" + b + "/iw\\," + c + "/ih)", e = 20, f = 3, g = e * f, h = this.bitrate || 600, i = h * f / 2, j = 28, k = [ "-loglevel", "error", "-probesize", "32", "-vsync", "passthrough", "-framerate", "" + e ].concat(kIsWindows ? [ "-f", "gdigrab", "-i", "desktop" ] : [ "-f", "x11grab", "-video_size", "1366x768", "-i", process.env.DISPLAY || ":0" ]).concat([ "-filter:v", "scale=w=trunc(iw*" + d + "/2)*2:h=trunc(ih*" + d + "/2)*2", "-f", "mpegts", "-muxdelay", "0", "-c:v", "libx264", "-maxrate:v", h + "K", "-bufsize:v", i + "K", "-crf", j + "", "-g", g + "", "-pix_fmt", "yuv420p", "-tune", "zerolatency", "-x264opts", "intra-refresh=1:slice-max-size=1300", "pipe:" ]);
        return this._screenGrab.cmd = this.ffmpegCmd || "ffmpeg", this._screenGrab.args = k, (this._screenGrab.isRunning ? this._screenGrab.stop() : Promise.resolve(null)).then(function() {
            return a._screenGrab.start();
        }).then(function() {
            a._liveStreamer.readSource(a._screenGrab.process.stdout, {
                chunkSize: 752,
                needStartBuffer: !0
            });
        });
    }, a.prototype.onCrash = function(a, b, c) {
        console.error('MediaService: sub process "' + a + '" crashed with code ' + b + ", signal " + c + ", will stop service.");
    }, a.prototype.onRecordError = function(a) {
        console.error("MediaService: onRecordError: ", a);
    }, a.prototype._fireRecordError = function(a) {
        var b = this;
        setImmediate(function() {
            return b.onRecordError(a);
        });
    }, a.prototype.addLiveStreamerClient = function(a, b, c) {
        this._liveStreamer.addClient(a, b, c);
    }, a.prototype.addAudioStreamerClient = function(a, b, c) {
        this._audioStreamer.addClient(a, b, c);
    }, a.prototype.addAudioSrc = function(a) {
        this._audioStreamer.readSource(a);
    }, a.prototype.startRecord = function(a) {
        var b = this, c = this._recordSession;
        return c.wait().then(function() {
            if ("running" === c.status) return Promise.resolve(null);
            if ("none" !== c.status) return Promise.reject(new Error("status: " + c.status));
            if (c.baseName = "screenRecord", c.sections = 0, c.baseDir = b.dataDir, c.audioType = a.audioUrls && a.audioUrls.length ? "remote" : a.audioDevices && a.audioDevices.length ? "local" : "none", 
            "none" !== c.audioType) {
                b._audioGrab.cmd = b.ffmpegCmd || "ffmpeg";
                var d = [ "-y", "-loglevel", "warning", "-analyzeduration", "100000" ], e = 0;
                if (a.audioDevices) {
                    for (var f = 0, g = a.audioDevices; f < g.length; f++) {
                        var h = g[f];
                        kIsWindows ? d.push("-f", "dshow", "-i", "audio=" + h) : d.push("-f", "pulse", "-i", h);
                    }
                    e += a.audioDevices.length;
                }
                if (a.audioUrls) {
                    for (var i = 0, j = a.audioUrls; i < j.length; i++) {
                        var k = j[i];
                        d.push("-i", k);
                    }
                    e += a.audioUrls.length;
                }
                d.push("-c:a", "aac", "-b:a", "64K", "placeholder"), b._audioGrab.args = d;
            } else b._audioGrab.args = [];
            return b.resumeRecord();
        });
    }, Object.defineProperty(a.prototype, "recordStatus", {
        get: function() {
            return this._recordSession.status;
        },
        enumerable: !0,
        configurable: !0
    }), Object.defineProperty(a.prototype, "canStartRecord", {
        get: function() {
            return "none" === this._recordSession.status;
        },
        enumerable: !0,
        configurable: !0
    }), Object.defineProperty(a.prototype, "isRecording", {
        get: function() {
            return "running" === this._recordSession.status;
        },
        enumerable: !0,
        configurable: !0
    }), Object.defineProperty(a.prototype, "isRecordPaused", {
        get: function() {
            return "paused" === this._recordSession.status;
        },
        enumerable: !0,
        configurable: !0
    }), a.prototype.pauseRecord = function() {
        var a = this, b = this._recordSession;
        return b.wait().then(function() {
            switch (b.status) {
              case "running":
                return a._audioStreamer.stop(), b.transit(a._audioGrab.stop().then(function() {
                    a._recordSession.outStreams.forEach(function(a) {
                        return a.destroy();
                    }), a._recordSession.outStreams.length = 0, a._recordSession.sections += 1;
                }), "paused");

              case "paused":
              case "none":
              case "stopped":
                return Promise.resolve();

              default:
                return Promise.reject(new Error("status: " + b.status));
            }
        });
    }, a.prototype.resumeRecord = function() {
        var a = this, b = this._recordSession;
        return b.wait().then(function() {
            switch (b.status) {
              case "running":
                return Promise.resolve();

              case "paused":
              case "none":
                return "none" !== b.audioType && (a._audioGrab.args[a._audioGrab.args.length - 1] = b.audioPath(b.sections)), b.transit(a.pauseAll().then(function() {
                    return a.startScreenGrab();
                }).then(function() {
                    if ("remote" === b.audioType && a._audioStreamer.start(), "none" !== b.audioType) return a._audioGrab.start();
                }).then(function() {
                    var c = fs.openSync(b.videoPath(b.sections), "w"), d = fs.createWriteStream("", {
                        fd: c
                    });
                    b.outStreams.push(d), a.addLiveStreamerClient(d, !0, "screenRecord"), d.on("error", function(b) {
                        "ERR_STREAM_WRITE_AFTER_END" !== b.code && "ERR_STREAM_DESTROYED" !== b.code && (a._fireRecordError(b), a.stopRecord()["catch"](function(a) {}));
                    });
                }), "running");

              default:
                return Promise.reject(new Error("status: " + a._recordSession.status));
            }
        });
    }, a.prototype.stopRecord = function() {
        var a = this, b = this._recordSession;
        return b.wait().then(function() {
            switch (b.status) {
              case "stopped":
              case "none":
                return Promise.resolve(null);

              case "paused":
                return b.status = "stopped", Promise.resolve(null);

              case "running":
                return a.pauseRecord().then(function() {
                    return b.status = "stopped";
                });

              default:
                return Promise.reject(new Error("status: " + b.status));
            }
        });
    }, a.prototype.saveRecord = function(a) {
        var b = this, c = this._recordSession;
        return c.wait().then(function() {
            return "stopped" === c.status ? c.transit(b._mergeRecord(a), "none") : Promise.reject(new Error("status: " + c.status));
        });
    }, a.prototype._mergeRecord = function(a) {
        var b = this, c = this._recordSession, d = utils_1.range(c.sections).map(function(a) {
            return c.sectionPath(a);
        });
        return new Promise(function(a, d) {
            if (c.sections <= 0) throw new Error("无录制文件");
            var e = 0, f = function() {
                var g = c.audioPath(e), h = c.videoPath(e), i = c.sectionPath(e), j = [ "-y", "-loglevel", "warning" ];
                "none" !== c.audioType && j.push("-i", g), j.push("-i", h, "-c", "copy", i), utils_1.runAndWait(b.ffmpegCmd, j).then(function() {
                    return utils_1.rm(g);
                }).then(function() {
                    return utils_1.rm(h);
                }).then(function() {
                    e++, e < c.sections ? f() : a();
                }, d);
            };
            f();
        }).then(function() {
            var a = [ "-y", "-loglevel", "warning", "-i", "concat:" + d.join("|"), "-c", "copy", "-bsf:a", "aac_adtstoasc", "-movflags", "+faststart", c.recordPath ];
            return console.log("_mergeRecord:args: " + a.join(" ")), utils_1.runAndWait(b.ffmpegCmd, a);
        }).then(function() {
            return Promise.all(d.map(function(a) {
                return utils_1.rm(a);
            }));
        }).then(function() {
            if (a) {
                var b = mPath.join(c.baseDir, a) + mPath.extname(c.recordPath);
                return fs.renameSync(c.recordPath, b), c.finalPath = b;
            }
            return c.recordPath;
        });
    }, a.prototype.removeRecord = function() {
        var a = this;
        console.log("removeRecord()...");
        var b = this._recordSession;
        return b.wait().then(function() {
            switch (b.status) {
              case "stopped":
              case "none":
              case "paused":
                return a._doRemoveRecord();

              default:
                return Promise.reject(new Error("status: " + b.status));
            }
        });
    }, a.prototype._doRemoveRecord = function() {
        var a = this._recordSession, b = utils_1.range(Math.min(a.sections, 5)), c = b.map(function(b) {
            return a.sectionPath(b);
        }).concat(b.map(function(b) {
            return a.audioPath(b);
        })).concat(b.map(function(b) {
            return a.videoPath(b);
        }));
        return c.push(a.recordPath), a.finalPath && c.push(a.finalPath), a.status = "none", a.sections = 0, a.finalPath = null, Promise.all(c.map(function(a) {
            return utils_1.rm(a);
        }));
    }, Object.defineProperty(a.prototype, "packetDropRatio", {
        set: function(a) {
            this._liveStreamer.packetDropRatio = a;
        },
        enumerable: !0,
        configurable: !0
    }), a;
}();

exports.MediaService = MediaService;

var RecordSession = function() {
    function a() {
        this.status = "none", this.newStatus = "none", this.audioType = "none", this.sections = 0, this.outStreams = [];
    }
    return a.prototype.audioPath = function(a) {
        return mPath.join(this.baseDir, this.baseName + "-a-" + a + ".ts");
    }, a.prototype.videoPath = function(a) {
        return mPath.join(this.baseDir, this.baseName + "-v-" + a + ".ts");
    }, a.prototype.sectionPath = function(a) {
        return mPath.join(this.baseDir, this.baseName + "-s-" + a + ".ts");
    }, Object.defineProperty(a.prototype, "recordPath", {
        get: function() {
            return mPath.join(this.baseDir, this.baseName) + ".mp4";
        },
        enumerable: !0,
        configurable: !0
    }), a.prototype.wait = function() {
        return this.transition ? this.transition["catch"](function(a) {}) : Promise.resolve();
    }, a.prototype.transit = function(a, b) {
        var c = this;
        return this.newStatus = b, this.transition = this.transition ? this.transition.then(function() {
            return a;
        }) : a, this.transition.then(function(a) {
            return c.transition = null, c.newStatus = "none", c.status = b, a;
        }, function(a) {
            throw c.transition = null, c.newStatus = "none", console.error("RecordSession.transit:error:", a), a;
        }), this.transition;
    }, a;
}(), kMainPort = 7017, router = {
    __proto__: null,
    GET: {
        __proto__: null,
        screenBroadcast: handleGetScreenBroadcast,
        "@default": handleStatic
    },
    POST: {
        remoteDesktop: handlePostRemoteDesktop,
        __proto__: null
    }
}, mediaService = new MediaService();

main();
//# sourceMappingURL=MediaService.js.map