"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MuteHandlerBase = void 0;
const crypto_1 = require("crypto");
const __1 = require("../..");
class MuteHandlerBase {
    constructor(output, props) {
        Object.defineProperty(this, "output", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: output
        });
        Object.defineProperty(this, "props", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: props
        });
        Object.defineProperty(this, "id", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: (0, crypto_1.randomUUID)().toString()
        });
        Object.defineProperty(this, "buffer", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "index", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 0
        });
        Object.defineProperty(this, "ended", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: false
        });
        Object.defineProperty(this, "baseTime", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "currentTimestamp", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "internalStats", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: {}
        });
        /**ms */
        Object.defineProperty(this, "lastCommittedTime", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 0
        });
        Object.defineProperty(this, "lastExecutionTime", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 0
        });
        /**ms */
        Object.defineProperty(this, "interval", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "bufferDuration", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "bufferLength", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        /**ms */
        Object.defineProperty(this, "lastFrameReceivedAt", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: 0
        });
        Object.defineProperty(this, "processInput", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: ({ frame, eol }) => {
                if (!frame) {
                    this.stop();
                    return [{ eol }];
                }
                if (this.ended) {
                    return [];
                }
                if (this.baseTime == undefined) {
                    this.baseTime = frame.time;
                    this.currentTimestamp = this.baseTime;
                    this.lastExecutionTime = this.baseTime;
                    this.lastCommittedTime = this.baseTime;
                    this.lastFrameReceivedAt = Date.now();
                }
                // 過去のフレームを捨てる
                if (frame.time < this.lastCommittedTime) {
                    return [];
                }
                // NTPの同期ずれが疑われるので捨てる
                const now = Date.now();
                const gap = 5000; // RTCP SR interval;
                const lastCommittedElapsed = frame.time - this.lastCommittedTime;
                const lastFrameReceivedElapsed = now - this.lastFrameReceivedAt;
                if (gap < lastFrameReceivedElapsed && lastCommittedElapsed < gap) {
                    this.internalStats["invalidFrameTime"] = {
                        count: (this.internalStats["invalidFrameTime"]?.count ?? 0) + 1,
                        at: new Date().toISOString(),
                        lastCommittedElapsed,
                        lastFrameReceivedElapsed,
                    };
                    return [];
                }
                this.lastFrameReceivedAt = now;
                const elapsed = frame.time - this.baseTime;
                const index = (0, __1.int)(elapsed / this.bufferDuration) % this.bufferLength;
                this.buffer[index].push(frame);
                const lastExecution = frame.time - this.lastExecutionTime;
                if (lastExecution >= this.interval) {
                    const times = (0, __1.int)(lastExecution / this.bufferDuration) - 1;
                    this.lastExecutionTime = this.currentTimestamp;
                    for (let i = 0; i < times; i++) {
                        this.executeTask();
                        this.lastExecutionTime += this.bufferDuration;
                    }
                }
                return [];
            }
        });
        this.interval = props.interval;
        this.bufferDuration = this.interval / 2;
        this.bufferLength = this.props.bufferLength * 2;
        this.buffer = [...new Array(this.bufferLength)].map(() => []);
    }
    toJSON() {
        return { ...this.internalStats, id: this.id };
    }
    executeTask() {
        const { ptime, dummyPacket } = this.props;
        const buffer = this.buffer[this.index].sort((a, b) => a.time - b.time);
        const last = buffer.at(-1);
        const expect = last
            ? last.time +
                // offset
                ptime
            : this.currentTimestamp;
        if (expect < this.currentTimestamp + this.bufferDuration) {
            for (let time = expect; time < this.currentTimestamp + this.bufferDuration; time += ptime) {
                buffer.push({
                    time,
                    data: dummyPacket,
                    isKeyframe: true,
                });
            }
        }
        this.currentTimestamp += this.bufferDuration;
        this.internalStats["mute"] = new Date().toISOString();
        this.buffer[this.index] = [];
        buffer.forEach((frame) => {
            this.output({ frame });
            this.lastCommittedTime = frame.time;
        });
        this.index++;
        if (this.index === this.bufferLength) {
            this.index = 0;
        }
    }
    stop() {
        this.ended = true;
        this.buffer = [];
        this.output = undefined;
    }
}
exports.MuteHandlerBase = MuteHandlerBase;
//# sourceMappingURL=mute.js.map