<commit_before>/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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.
 */
package com.google.android.exoplayer.hls;

import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
import com.google.android.exoplayer.chunk.Chunk;
import com.google.android.exoplayer.chunk.DataChunk;
import com.google.android.exoplayer.chunk.Format;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
import com.google.android.exoplayer.extractor.ts.TsExtractor;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util;

import android.net.Uri;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

/**
 * A temporary test source of HLS chunks.
 * <p>
 * TODO: Figure out whether this should merge with the chunk package, or whether the hls
 * implementation is going to naturally diverge.
 */
public class HlsChunkSource {

  /**
   * Interface definition for a callback to be notified of {@link HlsChunkSource} events.
   */
  public interface EventListener extends BaseChunkSampleSourceEventListener {}

  /**
   * Adaptive switching is disabled.
   * <p>
   * The initially selected variant will be used throughout playback.
   */
  public static final int ADAPTIVE_MODE_NONE = 0;

  /**
   * Adaptive switches splice overlapping segments of the old and new variants.
   * <p>
   * When performing a switch from one variant to another, overlapping segments will be requested
   * from both the old and new variants. These segments will then be spliced together, allowing
   * a seamless switch from one variant to another even if keyframes are misaligned or if keyframes
   * are not positioned at the start of each segment.
   * <p>
   * Note that where it can be guaranteed that the source content has keyframes positioned at the
   * start of each segment, {@link #ADAPTIVE_MODE_ABRUPT} should always be used in preference to
   * this mode.
   */
  public static final int ADAPTIVE_MODE_SPLICE = 1;

  /**
   * Adaptive switches are performed at segment boundaries.
   * <p>
   * For this mode to perform seamless switches, the source content is required to have keyframes
   * positioned at the start of each segment. If this is not the case a visual discontinuity may
   * be experienced when switching from one variant to another.
   * <p>
   * Note that where it can be guaranteed that the source content does have keyframes positioned at
   * the start of each segment, this mode should always be used in preference to
   * {@link #ADAPTIVE_MODE_SPLICE} because it requires fetching less data.
   */
  public static final int ADAPTIVE_MODE_ABRUPT = 3;

  /**
   * The default minimum duration of media that needs to be buffered for a switch to a higher
   * quality variant to be considered.
   */
  public static final long DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS = 5000;

  /**
   * The default maximum duration of media that needs to be buffered for a switch to a lower
   * quality variant to be considered.
   */
  public static final long DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS = 20000;

  /**
   * The default time for which a media playlist should be blacklisted.
   */
  public static final long DEFAULT_PLAYLIST_BLACKLIST_MS = 60000;

  private static final String TAG = "HlsChunkSource";
  private static final String AAC_FILE_EXTENSION = ".aac";
  private static final float BANDWIDTH_FRACTION = 0.8f;

  private final DataSource dataSource;
  private final HlsPlaylistParser playlistParser;
  private final BandwidthMeter bandwidthMeter;
  private final int adaptiveMode;
  private final String baseUri;
  private final int maxWidth;
  private final int maxHeight;
  private final long minBufferDurationToSwitchUpUs;
  private final long maxBufferDurationToSwitchDownUs;
  private final AudioCapabilities audioCapabilities;

  // A list of variants considered during playback, ordered by decreasing bandwidth. The following
  // three arrays are of the same length and are ordered in the same way (i.e. variantPlaylists[i],
  // variantLastPlaylistLoadTimesMs[i] and variantBlacklistTimes[i] all correspond to variants[i]).
  private final Variant[] variants;
  private final HlsMediaPlaylist[] variantPlaylists;
  private final long[] variantLastPlaylistLoadTimesMs;
  private final long[] variantBlacklistTimes;

  // The index in variants of the currently selected variant.
  private int selectedVariantIndex;

  private byte[] scratchSpace;
  private boolean live;
  private long durationUs;

  private Uri encryptionKeyUri;
  private byte[] encryptionKey;
  private String encryptionIvString;
  private byte[] encryptionIv;

  public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
      BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode,
      AudioCapabilities audioCapabilities) {
    this(dataSource, playlistUrl, playlist, bandwidthMeter, variantIndices, adaptiveMode,
        DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS,
        audioCapabilities);
  }

  /**
   * @param dataSource A {@link DataSource} suitable for loading the media data.
   * @param playlistUrl The playlist URL.
   * @param playlist The hls playlist.
   * @param bandwidthMeter provides an estimate of the currently available bandwidth.
   * @param variantIndices If {@code playlist} is a {@link HlsMasterPlaylist}, the subset of variant
   *     indices to consider, or null to consider all of the variants. For other playlist types
   *     this parameter is ignored.
   * @param adaptiveMode The mode for switching from one variant to another. One of
   *     {@link #ADAPTIVE_MODE_NONE}, {@link #ADAPTIVE_MODE_ABRUPT} and
   *     {@link #ADAPTIVE_MODE_SPLICE}.
   * @param minBufferDurationToSwitchUpMs The minimum duration of media that needs to be buffered
   *     for a switch to a higher quality variant to be considered.
   * @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered
   *     for a switch to a lower quality variant to be considered.
   * @param audioCapabilities The audio capabilities for playback on this device, or {@code null} if
   *     the default capabilities should be assumed.
   */
  public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
      BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode,
      long minBufferDurationToSwitchUpMs, long maxBufferDurationToSwitchDownMs,
      AudioCapabilities audioCapabilities) {
    this.dataSource = dataSource;
    this.bandwidthMeter = bandwidthMeter;
    this.adaptiveMode = adaptiveMode;
    this.audioCapabilities = audioCapabilities;
    minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000;
    maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
    baseUri = playlist.baseUri;
    playlistParser = new HlsPlaylistParser();

    if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
      variants = new Variant[] {new Variant(0, playlistUrl, 0, null, -1, -1)};
      variantPlaylists = new HlsMediaPlaylist[] {(HlsMediaPlaylist) playlist};
      variantLastPlaylistLoadTimesMs = new long[1];
      variantBlacklistTimes = new long[1];
      // We won't be adapting between different variants.<commit_msg>Fix GitHub issue #572<commit_after>/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * 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.
 */
package com.google.android.exoplayer.hls;

import com.google.android.exoplayer.C;
import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener;
import com.google.android.exoplayer.chunk.Chunk;
import com.google.android.exoplayer.chunk.DataChunk;
import com.google.android.exoplayer.chunk.Format;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ts.AdtsExtractor;
import com.google.android.exoplayer.extractor.ts.TsExtractor;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.UriUtil;
import com.google.android.exoplayer.util.Util;

import android.net.Uri;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

/**
 * A temporary test source of HLS chunks.
 * <p>
 * TODO: Figure out whether this should merge with the chunk package, or whether the hls
 * implementation is going to naturally diverge.
 */
public class HlsChunkSource {

  /**
   * Interface definition for a callback to be notified of {@link HlsChunkSource} events.
   */
  public interface EventListener extends BaseChunkSampleSourceEventListener {}

  /**
   * Adaptive switching is disabled.
   * <p>
   * The initially selected variant will be used throughout playback.
   */
  public static final int ADAPTIVE_MODE_NONE = 0;

  /**
   * Adaptive switches splice overlapping segments of the old and new variants.
   * <p>
   * When performing a switch from one variant to another, overlapping segments will be requested
   * from both the old and new variants. These segments will then be spliced together, allowing
   * a seamless switch from one variant to another even if keyframes are misaligned or if keyframes
   * are not positioned at the start of each segment.
   * <p>
   * Note that where it can be guaranteed that the source content has keyframes positioned at the
   * start of each segment, {@link #ADAPTIVE_MODE_ABRUPT} should always be used in preference to
   * this mode.
   */
  public static final int ADAPTIVE_MODE_SPLICE = 1;

  /**
   * Adaptive switches are performed at segment boundaries.
   * <p>
   * For this mode to perform seamless switches, the source content is required to have keyframes
   * positioned at the start of each segment. If this is not the case a visual discontinuity may
   * be experienced when switching from one variant to another.
   * <p>
   * Note that where it can be guaranteed that the source content does have keyframes positioned at
   * the start of each segment, this mode should always be used in preference to
   * {@link #ADAPTIVE_MODE_SPLICE} because it requires fetching less data.
   */
  public static final int ADAPTIVE_MODE_ABRUPT = 3;

  /**
   * The default minimum duration of media that needs to be buffered for a switch to a higher
   * quality variant to be considered.
   */
  public static final long DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS = 5000;

  /**
   * The default maximum duration of media that needs to be buffered for a switch to a lower
   * quality variant to be considered.
   */
  public static final long DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS = 20000;

  /**
   * The default time for which a media playlist should be blacklisted.
   */
  public static final long DEFAULT_PLAYLIST_BLACKLIST_MS = 60000;

  private static final String TAG = "HlsChunkSource";
  private static final String AAC_FILE_EXTENSION = ".aac";
  private static final float BANDWIDTH_FRACTION = 0.8f;

  private final DataSource dataSource;
  private final HlsPlaylistParser playlistParser;
  private final BandwidthMeter bandwidthMeter;
  private final int adaptiveMode;
  private final String baseUri;
  private final int maxWidth;
  private final int maxHeight;
  private final long minBufferDurationToSwitchUpUs;
  private final long maxBufferDurationToSwitchDownUs;
  private final AudioCapabilities audioCapabilities;

  // A list of variants considered during playback, ordered by decreasing bandwidth. The following
  // three arrays are of the same length and are ordered in the same way (i.e. variantPlaylists[i],
  // variantLastPlaylistLoadTimesMs[i] and variantBlacklistTimes[i] all correspond to variants[i]).
  private final Variant[] variants;
  private final HlsMediaPlaylist[] variantPlaylists;
  private final long[] variantLastPlaylistLoadTimesMs;
  private final long[] variantBlacklistTimes;

  // The index in variants of the currently selected variant.
  private int selectedVariantIndex;

  private byte[] scratchSpace;
  private boolean live;
  private long durationUs;

  private Uri encryptionKeyUri;
  private byte[] encryptionKey;
  private String encryptionIvString;
  private byte[] encryptionIv;

  public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
      BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode,
      AudioCapabilities audioCapabilities) {
    this(dataSource, playlistUrl, playlist, bandwidthMeter, variantIndices, adaptiveMode,
        DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS,
        audioCapabilities);
  }

  /**
   * @param dataSource A {@link DataSource} suitable for loading the media data.
   * @param playlistUrl The playlist URL.
   * @param playlist The hls playlist.
   * @param bandwidthMeter provides an estimate of the currently available bandwidth.
   * @param variantIndices If {@code playlist} is a {@link HlsMasterPlaylist}, the subset of variant
   *     indices to consider, or null to consider all of the variants. For other playlist types
   *     this parameter is ignored.
   * @param adaptiveMode The mode for switching from one variant to another. One of
   *     {@link #ADAPTIVE_MODE_NONE}, {@link #ADAPTIVE_MODE_ABRUPT} and
   *     {@link #ADAPTIVE_MODE_SPLICE}.
   * @param minBufferDurationToSwitchUpMs The minimum duration of media that needs to be buffered
   *     for a switch to a higher quality variant to be considered.
   * @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered
   *     for a switch to a lower quality variant to be considered.
   * @param audioCapabilities The audio capabilities for playback on this device, or {@code null} if
   *     the default capabilities should be assumed.
   */
  public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist,
      BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode,
      long minBufferDurationToSwitchUpMs, long maxBufferDurationToSwitchDownMs,
      AudioCapabilities audioCapabilities) {
    this.dataSource = dataSource;
    this.bandwidthMeter = bandwidthMeter;
    this.adaptiveMode = adaptiveMode;
    this.audioCapabilities = audioCapabilities;
    minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000;
    maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000;
    baseUri = playlist.baseUri;
    playlistParser = new HlsPlaylistParser();

    if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
      variants = new Variant[] {new Variant(0, playlistUrl, 0, null, -1, -1)};
      variantPlaylists = new HlsMediaPlaylist[1];
      variantLastPlaylistLoadTimesMs = new long[1];
      variantBlacklistTimes = new long[1];
      setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
      // We won't be adapting between different variants.
{'fraction_non_alphanumeric': 0.0661632297415045, 'fraction_numerical': 0.005053732210281731, 'mean_word_length': 4.282602025161093, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, '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': '2372581', 'n_tokens_mistral': 4845, 'n_tokens_neox': 4642, 'n_words': 1747}