<commit_before>/**
 * Copyright 2015 CANAL+ Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import objectAssign from "object-assign";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { combineLatest } from "rxjs/observable/combineLatest";
import { merge } from "rxjs/observable/merge";
import { EmptyObservable } from "rxjs/observable/EmptyObservable";
import { FromObservable } from "rxjs/observable/FromObservable";
import { TimerObservable } from "rxjs/observable/TimerObservable";

import log from "../utils/log";
import { BufferingQueue } from "./buffering-queue";
import { BufferedRanges } from "./ranges";
import { SimpleSet } from "../utils/collections";
import {
  MediaError,
  ErrorTypes,
  ErrorCodes,
  IndexError,
} from "../errors";

const empty = EmptyObservable.create;
const from = FromObservable.create;
const timer = TimerObservable.create;

/**
 * Ratio used to know if an already loaded segment should be re-buffered.
 * We re-load the given segment if the current one times that ratio is inferior
 * to the new one.
 */
const BITRATE_REBUFFERING_RATIO = 1.5;

/**
 * _Low_ gap (from current position) from which the buffer will be _garbage
 * collected_ (read removed from the buffer).
 */
const GC_GAP_CALM  = 240;

/**
 * _High_ gap (from current position) from which the buffer will be _garbage
 * collected_ (read removed from the buffer) if the low one does not clean up
 * any buffer.
 */
const GC_GAP_BEEFY = 30;

/**
 * Manage a single buffer:
 *   - load the right segments through the pipeline on normal playback /
 *     seeking / as the adaptation chosen changes
 *   - add those to the sourceBuffer
 *   - clean up if too much segments have been loaded
 * @param {string} BufferType
 * @param {SourceBuffer} sourceBuffer
 * @param {Object} adaptation - adaptation choice for the given bufferType
 * @param {Object} pipeline - pipeline for the given bufferType
 * @param {Object} adapters - Adaptive observables
 * @param {Observable} timings - Timings observable. Mainly Used to re-trigger
 * a segment loading process.
 * @param {Observable} seekings - Seekings observable. Emit each time the player
 * is seeking.
 * @returns {Observable}
 */
function Buffer({
  bufferType,   // Buffer type (audio, video, text, image)
  sourceBuffer, // SourceBuffer object
  adaptation,   // Adaptation buffered
  pipeline,     // Segment pipeline
  adapters,     // adaptive observables
  timings,      // Timings observable
  seekings,     // Seekings observable
  isLive,
}) {

  // will be used to emit messages to the calling function
  const messageSubject = new Subject();

  // safety level (low and high water mark) size of buffer that won't
  // be flushed when switching representation for smooth transitions
  // and avoiding buffer underflows
  const LOW_WATER_MARK_PAD  = bufferType == "video" ? 4 : 1;
  const HIGH_WATER_MARK_PAD = bufferType == "video" ? 6 : 1;

  const {
    representations,
    wantedBufferAhead,
    maxBufferBehind,
    maxBufferAhead,
  } = adapters;
  const ranges = new BufferedRanges();
  const bufferingQueue = new BufferingQueue(sourceBuffer);

  /**
   * Buffer garbage collector algorithm. Tries to free up some part of
   * the ranges that are distant from the current playing time.
   * See: https://w3c.github.io/media-source/#sourcebuffer-prepare-append
   * @param {Object} timing
   * @param {Number} timing.ts - current timestamp
   * @param {BufferedRanges} timing.buffered - current buffered ranges
   * @param {Number} gcGap - delta gap from current timestamp from which we
   * should consider cleaning up.
   * @returns {Array.<Range>} - Ranges selected for clean up
   */
  function selectGCedRanges({ts, buffered}, gcGap) {
    const innerRange  = buffered.getRange(ts);
    const outerRanges = buffered.getOuterRanges(ts);

    const cleanedupRanges = [];

    // start by trying to remove all ranges that do not contain the
    // current time and respect the gcGap
    for (let i = 0; i < outerRanges.length; i++) {
      const outerRange = outerRanges[i];
      if (ts - gcGap < outerRange.end) { // FIXME?
        cleanedupRanges.push(outerRange);
      }
      else if (ts + gcGap > outerRange.start) {
        cleanedupRanges.push(outerRange);
      }
    }

    // try to clean up some space in the current range
    if (innerRange) {
      log.debug("buffer: gc removing part of inner range", cleanedupRanges);
      if (ts - gcGap > innerRange.start) {
        cleanedupRanges.push({
          start: innerRange.start,
          end: ts - gcGap,
        });
      }

      if (ts + gcGap < innerRange.end) {
        cleanedupRanges.push({
          start: ts + gcGap,
          end: innerRange.end,
        });
      }
    }<commit_msg>Buffer: add comment<commit_after>/**
 * Copyright 2015 CANAL+ Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import objectAssign from "object-assign";
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { combineLatest } from "rxjs/observable/combineLatest";
import { merge } from "rxjs/observable/merge";
import { EmptyObservable } from "rxjs/observable/EmptyObservable";
import { FromObservable } from "rxjs/observable/FromObservable";
import { TimerObservable } from "rxjs/observable/TimerObservable";

import log from "../utils/log";
import { BufferingQueue } from "./buffering-queue";
import { BufferedRanges } from "./ranges";
import { SimpleSet } from "../utils/collections";
import {
  MediaError,
  ErrorTypes,
  ErrorCodes,
  IndexError,
} from "../errors";

const empty = EmptyObservable.create;
const from = FromObservable.create;
const timer = TimerObservable.create;

/**
 * Ratio used to know if an already loaded segment should be re-buffered.
 * We re-load the given segment if the current one times that ratio is inferior
 * to the new one.
 */
const BITRATE_REBUFFERING_RATIO = 1.5;

/**
 * _Low_ gap (from current position) from which the buffer will be _garbage
 * collected_ (read removed from the buffer).
 */
const GC_GAP_CALM  = 240;

/**
 * _High_ gap (from current position) from which the buffer will be _garbage
 * collected_ (read removed from the buffer) if the low one does not clean up
 * any buffer.
 */
const GC_GAP_BEEFY = 30;

/**
 * Manage a single buffer:
 *   - load the right segments through the pipeline on normal playback /
 *     seeking / as the adaptation chosen changes
 *   - add those to the sourceBuffer
 *   - clean up if too much segments have been loaded
 * @param {string} BufferType
 * @param {SourceBuffer} sourceBuffer
 * @param {Object} adaptation - adaptation choice for the given bufferType
 * @param {Object} pipeline - pipeline for the given bufferType
 * @param {Object} adapters - Adaptive observables
 * @param {Observable} timings - Timings observable. Mainly Used to re-trigger
 * a segment loading process.
 * @param {Observable} seekings - Seekings observable. Emit each time the player
 * is seeking.
 * @returns {Observable}
 */
function Buffer({
  bufferType,   // Buffer type (audio, video, text, image)
  sourceBuffer, // SourceBuffer object
  adaptation,   // Adaptation buffered
  pipeline,     // Segment pipeline
  adapters,     // adaptive observables
  timings,      // Timings observable
  seekings,     // Seekings observable
  isLive,
}) {

  // will be used to emit messages to the calling function
  const messageSubject = new Subject();

  // safety level (low and high water mark) size of buffer that won't
  // be flushed when switching representation for smooth transitions
  // and avoiding buffer underflows
  const LOW_WATER_MARK_PAD  = bufferType == "video" ? 4 : 1;
  const HIGH_WATER_MARK_PAD = bufferType == "video" ? 6 : 1;

  const {
    representations,
    wantedBufferAhead,
    maxBufferBehind,
    maxBufferAhead,
  } = adapters;
  const ranges = new BufferedRanges();
  const bufferingQueue = new BufferingQueue(sourceBuffer);

  /**
   * Buffer garbage collector algorithm. Tries to free up some part of
   * the ranges that are distant from the current playing time.
   * See: https://w3c.github.io/media-source/#sourcebuffer-prepare-append
   * @param {Object} timing
   * @param {Number} timing.ts - current timestamp
   * @param {BufferedRanges} timing.buffered - current buffered ranges
   * @param {Number} gcGap - delta gap from current timestamp from which we
   * should consider cleaning up.
   * @returns {Array.<Range>} - Ranges selected for clean up
   */
  function selectGCedRanges({ts, buffered}, gcGap) {
    const innerRange  = buffered.getRange(ts);
    const outerRanges = buffered.getOuterRanges(ts);

    const cleanedupRanges = [];

    // start by trying to remove all ranges that do not contain the
    // current time and respect the gcGap
    // respect the gcGap? FIXME?
    for (let i = 0; i < outerRanges.length; i++) {
      const outerRange = outerRanges[i];
      if (ts - gcGap < outerRange.end) {
        cleanedupRanges.push(outerRange);
      }
      else if (ts + gcGap > outerRange.start) {
        cleanedupRanges.push(outerRange);
      }
    }

    // try to clean up some space in the current range
    if (innerRange) {
      log.debug("buffer: gc removing part of inner range", cleanedupRanges);
      if (ts - gcGap > innerRange.start) {
        cleanedupRanges.push({
          start: innerRange.start,
          end: ts - gcGap,
        });
      }

      if (ts + gcGap < innerRange.end) {
        cleanedupRanges.push({
          start: ts + gcGap,
          end: innerRange.end,
        });
      }
    }
{'fraction_non_alphanumeric': 0.09473585083341181, 'fraction_numerical': 0.003955174686882004, 'mean_word_length': 3.2446043165467624, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, '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': '6495099', 'n_tokens_mistral': 3100, 'n_tokens_neox': 3021, 'n_words': 1133}