async handleQuotaExceeded_(mediaState, error) {
    const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);

    // The segment cannot fit into the SourceBuffer. Ideally, MediaSource would
    // have evicted old data to accommodate the segment; however, it may have
    // failed to do this if the segment is very large, or if it could not find
    // a suitable time range to remove.
    //
    // We can overcome the latter by trying to append the segment again;
    // however, to avoid continuous QuotaExceededErrors we must reduce the size
    // of the buffer going forward.
    //
    // If we've recently reduced the buffering goals, wait until the stream
    // which caused the first QuotaExceededError recovers. Doing this ensures
    // we don't reduce the buffering goals too quickly.

    const mediaStates = Array.from(this.mediaStates_.values());
    const waitingForAnotherStreamToRecover = mediaStates.some((ms) => {
      return ms != mediaState && ms.recovering;
    });

    if (!waitingForAnotherStreamToRecover) {
      const maxDisabledTime = this.getDisabledTime_(error);
      if (maxDisabledTime) {
        shaka.log.debug(logPrefix, 'Disabling stream due to quota', error);
      }
      const handled = this.playerInterface_.disableStream(
          mediaState.stream, maxDisabledTime);
      if (handled) {
        return;
      }
      if (this.config_.avoidEvictionOnQuotaExceededError) {
        // QuotaExceededError gets thrown if eviction didn't help to make room
        // for a segment. We want to wait for a while (4 seconds is just an
        // arbitrary number) before updating to give the playhead a chance to
        // advance, so we don't immediately throw again.
        this.scheduleUpdate_(mediaState, 4);
        return;
      }
      // Reduction schedule: 80%, 60%, 40%, 20%, 16%, 12%, 8%, 4%, fail.
      // Note: percentages are used for comparisons to avoid rounding errors.
      const percentBefore = Math.round(100 * this.bufferingScale_);
      if (percentBefore > 20) {
        this.bufferingScale_ -= 0.2;
      } else if (percentBefore > 4) {
        this.bufferingScale_ -= 0.04;
      } else {
        shaka.log.error(
            logPrefix, 'MediaSource threw QuotaExceededError too many times');
        mediaState.hasError = true;
        this.fatalError_ = true;
        this.playerInterface_.onError(error);
        return;
      }
      const percentAfter = Math.round(100 * this.bufferingScale_);
      shaka.log.warning(
          logPrefix,
          'MediaSource threw QuotaExceededError:',
          'reducing buffering goals by ' + (100 - percentAfter) + '%');
      mediaState.recovering = true;
      const presentationTime = this.playerInterface_.getPresentationTime();
      await this.evict_(mediaState, presentationTime);
    } else {
      shaka.log.debug(
          logPrefix,
          'MediaSource threw QuotaExceededError:',
          'waiting for another stream to recover...');
    }

    // QuotaExceededError gets thrown if eviction didn't help to make room
    // for a segment. We want to wait for a while (4 seconds is just an
    // arbitrary number) before updating to give the playhead a chance to
    // advance, so we don't immediately throw again.
    this.scheduleUpdate_(mediaState, 4);
  }
Handles a QUOTA_EXCEEDED_ERROR.

@param {shaka.media.StreamingEngine.MediaState_} mediaState
@param {!shaka.util.Error} error
@return {!Promise}
@private