content: /*
   * Copyright (C) 2016 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.exoplayer2.trackselection;
  
  import android.os.SystemClock;
  import androidx.annotation.Nullable;
  import com.google.android.exoplayer2.C;
  import com.google.android.exoplayer2.Timeline;
  import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
  import com.google.android.exoplayer2.source.TrackGroup;
  import com.google.android.exoplayer2.source.chunk.MediaChunk;
  import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
  import com.google.android.exoplayer2.upstream.BandwidthMeter;
  import java.util.List;
  import java.util.Random;
  import org.checkerframework.checker.nullness.compatqual.NullableType;
  
  /** An {@link ExoTrackSelection} whose selected track is updated randomly. */
  public final class RandomTrackSelection extends BaseTrackSelection {
  
    /** Factory for {@link RandomTrackSelection} instances. */
    public static final class Factory implements ExoTrackSelection.Factory {
  
      private final Random random;
  
      public Factory() {
        random = new Random();
      }
  
      /**
       * @param seed A seed for the {@link Random} instance used by the factory.
       */
      public Factory(int seed) {
        random = new Random(seed);
      }
  
      @Override
      public @NullableType ExoTrackSelection[] createTrackSelections(
          @NullableType Definition[] definitions,
          BandwidthMeter bandwidthMeter,
          MediaPeriodId mediaPeriodId,
          Timeline timeline) {
        return TrackSelectionUtil.createTrackSelectionsForDefinitions(
            definitions,
            definition ->
                new RandomTrackSelection(
                    definition.group, definition.tracks, definition.type, random));
      }
    }
  
    private final Random random;
  
    private int selectedIndex;
  
    /**
     * Creates a new instance.
     *
     * @param group The {@link TrackGroup}. Must not be null.
     * @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be
     *     null or empty. May be in any order.
     * @param type The {@link Type} of this track selection.
     * @param random A source of random numbers.
     */
    public RandomTrackSelection(TrackGroup group, int[] tracks, @Type int type, Random random) {
      super(group, tracks, type);
      this.random = random;
      selectedIndex = random.nextInt(length);
    }
  
    @Override
    public void updateSelectedTrack(
        long playbackPositionUs,
        long bufferedDurationUs,
        long availableDurationUs,
        List<? extends MediaChunk> queue,
        MediaChunkIterator[] mediaChunkIterators) {
      // Count the number of allowed formats.
      long nowMs = SystemClock.elapsedRealtime();
      int allowedFormatCount = 0;
      for (int i = 0; i < length; i++) {
        if (!isBlacklisted(i, nowMs)) {
          allowedFormatCount++;
        }
      }
  
      selectedIndex = random.nextInt(allowedFormatCount);
      if (allowedFormatCount != length) {
        // Adjust the format index to account for excluded formats.
        allowedFormatCount = 0;
        for (int i = 0; i < length; i++) {
          if (!isBlacklisted(i, nowMs) && selectedIndex == allowedFormatCount++) {
            selectedIndex = i;
            return;
          }
        }
      }
    }
  
    @Override
    public int getSelectedIndex() {
      return selectedIndex;
    }
  
    @Override
    public int getSelectionReason() {
      return C.SELECTION_REASON_ADAPTIVE;
    }
  
    @Override
    @Nullable
    public Object getSelectionData() {
      return null;
    }
  }
  