<commit_before>import Log from '../utils/logger.js';

const State = {
    ERROR: -2,
    STARTING: -1,
    IDLE: 0,
    KEY_LOADING: 1,
    FRAG_LOADING: 2,
    WAITING_LEVEL: 3,
    PARSING: 4,
    PARSED: 5,
    APPENDING: 6,
    BUFFER_FLUSHING: 7
};

// Media Source Extension controller
class MSEController {

    static isSupported() {
        return window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
    }

    constructor() {
        this.TAG = this.constructor.name;

        this.e = {};
        this.e.onSourceOpen = this._onSourceOpen.bind(this);
        this.e.onSourceEnded = this._onSourceEnded.bind(this);
        this.e.onSourceClose = this._onSourceClose.bind(this);
        this.e.onSourceBufferError = this._onSourceBufferError.bind(this);
        this.e.onSourceBufferUpdateEnd = this._onSourceBufferUpdateEnd.bind(this);

        let ms = this._mediaSource = new window.MediaSource();
        ms.addEventListener('sourceopen', this.e.onSourceOpen);
        ms.addEventListener('sourceended', this.e.onSourceEnded);
        ms.addEventListener('sourceclose', this.e.onSourceClose);

        this._mediaSourceObjectURL = null;
        this._mediaElement = null;

        this._mimeTypes = {
            video: null,
            audio: null
        };
        this._sourceBuffers = {
            video: null,
            audio: null
        };
        this._pendingSegments = {
            video: [],
            audio: []
        };
    }

    destroy() {
        if (this._mediaSource) {
            let ms = this._mediaSource;
            if (ms.readyState === 'open') {
                try {
                    ms.endOfStream();
                } catch (error) {
                    Log.e(this.TAG, error.message);
                }
            }
            ms.removeEventListener('sourceopen', this.e.onSourceOpen);
            ms.removeEventListener('sourceended', this.e.onSourceEnded);
            ms.removeEventListener('sourceclose', this.e.onSourceClose);
        }
        if (this._mediaElement) {
            this.detachMediaElement();
        }
        this.e = null;
    }

    attachMediaElement(mediaElement) {
        this._mediaElement = mediaElement;
        this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource);
        mediaElement.src = this._mediaSourceObjectURL;
    }

    detachMediaElement() {
        this._mediaElement.src = '';
        this._mediaElement = null;
        if (this._mediaSourceObjectURL) {
            window.URL.revokeObjectURL(this._mediaSourceObjectURL);
            this._mediaSourceObjectURL = null;
        }
    }

    appendInitSegment(initSegment) {
        let is = initSegment;
        let mimeType = `${is.container};codecs=${is.codec}`;
        Log.v(this.TAG, 'Received Initialization Segment, mimetype: ' + mimeType);
        if (mimeType !== this._mimeTypes[is.type]) {
            if (!this._mimeTypes[is.type]) {  // empty, first chance create sourcebuffer
                this._mimeTypes[is.type] = mimeType;
                try {
                    // TODO: MediaSource readyState checking
                    let sb = this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType);
                    sb.addEventListener('error', this.e.onSourceBufferError);
                    sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd);
                } catch (error) {
                    Log.e(this.TAG, error.message);
                }
            } else {
                // TODO: manifest codec changed, fire error
                throw 'MSEController: Codec/Container changed in latest Initialization Segment!';
            }
        }

        this._pendingSegments[is.type].push(is);
        if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) {
            this._doAppendSegments();
        }
    }

    appendMediaSegment(mediaSegment) {
        let ms = mediaSegment;
        let sb = this._sourceBuffers[ms.type];

        if (!sb) {
            // TODO: trigger error callback<commit_msg>MSEController: Defer first init segment appending. Ensure all sourcebuffers to be created continuously<commit_after>import Log from '../utils/logger.js';

const State = {
    ERROR: -2,
    STARTING: -1,
    IDLE: 0,
    KEY_LOADING: 1,
    FRAG_LOADING: 2,
    WAITING_LEVEL: 3,
    PARSING: 4,
    PARSED: 5,
    APPENDING: 6,
    BUFFER_FLUSHING: 7
};

// Media Source Extension controller
class MSEController {

    static isSupported() {
        return window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
    }

    constructor() {
        this.TAG = this.constructor.name;

        this.e = {};
        this.e.onSourceOpen = this._onSourceOpen.bind(this);
        this.e.onSourceEnded = this._onSourceEnded.bind(this);
        this.e.onSourceClose = this._onSourceClose.bind(this);
        this.e.onSourceBufferError = this._onSourceBufferError.bind(this);
        this.e.onSourceBufferUpdateEnd = this._onSourceBufferUpdateEnd.bind(this);

        let ms = this._mediaSource = new window.MediaSource();
        ms.addEventListener('sourceopen', this.e.onSourceOpen);
        ms.addEventListener('sourceended', this.e.onSourceEnded);
        ms.addEventListener('sourceclose', this.e.onSourceClose);

        this._mediaSourceObjectURL = null;
        this._mediaElement = null;

        this._mimeTypes = {
            video: null,
            audio: null
        };
        this._sourceBuffers = {
            video: null,
            audio: null
        };
        this._pendingSegments = {
            video: [],
            audio: []
        };
    }

    destroy() {
        if (this._mediaSource) {
            let ms = this._mediaSource;
            if (ms.readyState === 'open') {
                try {
                    ms.endOfStream();
                } catch (error) {
                    Log.e(this.TAG, error.message);
                }
            }
            ms.removeEventListener('sourceopen', this.e.onSourceOpen);
            ms.removeEventListener('sourceended', this.e.onSourceEnded);
            ms.removeEventListener('sourceclose', this.e.onSourceClose);
        }
        if (this._mediaElement) {
            this.detachMediaElement();
        }
        this.e = null;
    }

    attachMediaElement(mediaElement) {
        this._mediaElement = mediaElement;
        this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource);
        mediaElement.src = this._mediaSourceObjectURL;
    }

    detachMediaElement() {
        this._mediaElement.src = '';
        this._mediaElement = null;
        if (this._mediaSourceObjectURL) {
            window.URL.revokeObjectURL(this._mediaSourceObjectURL);
            this._mediaSourceObjectURL = null;
        }
    }

    appendInitSegment(initSegment) {
        let is = initSegment;
        let mimeType = `${is.container};codecs=${is.codec}`;
        let firstInitSegment = false;

        Log.v(this.TAG, 'Received Initialization Segment, mimetype: ' + mimeType);
        if (mimeType !== this._mimeTypes[is.type]) {
            if (!this._mimeTypes[is.type]) {  // empty, first chance create sourcebuffer
                firstInitSegment = true;
                this._mimeTypes[is.type] = mimeType;
                try {
                    // TODO: MediaSource readyState checking
                    let sb = this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType);
                    sb.addEventListener('error', this.e.onSourceBufferError);
                    sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd);
                } catch (error) {
                    Log.e(this.TAG, error.message);
                }
            } else {
                // TODO: manifest codec changed, fire error
                throw 'MSEController: Codec/Container changed in latest Initialization Segment!';
            }
        }

        this._pendingSegments[is.type].push(is);
        if (!firstInitSegment) {  // append immediately only if init segment in subsequence
            if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) {
                this._doAppendSegments();
            }
        }
    }

    appendMediaSegment(mediaSegment) {
        let ms = mediaSegment;
        let sb = this._sourceBuffers[ms.type];

        if (!sb) {
            // TODO: trigger error callback
{'fraction_non_alphanumeric': 0.11223410506522506, 'fraction_numerical': 0.004700904924197908, 'mean_word_length': 1.9507628294036061, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2198140', 'n_tokens_mistral': 2448, 'n_tokens_neox': 2347, 'n_words': 420}