Home Reference Source

src/controller/buffer-operation-queue.ts

import { logger } from '../utils/logger';
import type { BufferOperation, BufferOperationQueues, SourceBuffers, SourceBufferName } from '../types/buffer';

export default class BufferOperationQueue {
  private buffers: SourceBuffers;
  private queues: BufferOperationQueues = {
    video: [],
    audio: [],
    audiovideo: []
  };

  constructor (sourceBufferReference: SourceBuffers) {
    this.buffers = sourceBufferReference;
  }

  public append (operation: BufferOperation, type: SourceBufferName) {
    const queue = this.queues[type];
    queue.push(operation);
    if (queue.length === 1 && this.buffers[type]) {
      this.executeNext(type);
    }
  }

  public insertAbort (operation: BufferOperation, type: SourceBufferName) {
    const queue = this.queues[type];
    queue.unshift(operation);
    this.executeNext(type, true);
  }

  public appendBlocker (type: SourceBufferName) : Promise<{}> {
    let execute;
    const promise: Promise<{}> = new Promise((resolve) => {
      execute = resolve;
    });
    const operation: BufferOperation = {
      execute,
      onStart: () => {},
      onComplete: () => {},
      onError: () => {}
    };

    this.append(operation, type);
    return promise;
  }

  public executeNext (type: SourceBufferName, ignoreUpdating?: boolean) {
    const { buffers, queues } = this;
    const sb = buffers[type];
    console.assert(!sb || ignoreUpdating || !sb.updating, `${type} sourceBuffer must exist, and must not be updating`);

    const queue = queues[type];
    if (queue.length) {
      const operation: BufferOperation = queue[0];
      try {
        // Operations are expected to result in an 'updateend' event being fired. If not, the queue will lock. Operations
        // which do not end with this event must call _onSBUpdateEnd manually
        operation.execute();
      } catch (e) {
        logger.warn('[buffer-operation-queue]: Unhandled exception executing the current operation');
        operation.onError(e);

        // Only shift the current operation off, otherwise the updateend handler will do this for us
        if (!sb || !sb.updating) {
          queue.shift();
        }
      }
    }
  }

  public shiftAndExecuteNext (type: SourceBufferName) {
    this.queues[type].shift();
    this.executeNext(type);
  }

  public current (type: SourceBufferName) {
    return this.queues[type][0];
  }
}