• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.ext.cast;
17 
18 import android.util.SparseArray;
19 import android.util.SparseIntArray;
20 import androidx.annotation.Nullable;
21 import com.google.android.exoplayer2.C;
22 import com.google.android.exoplayer2.Timeline;
23 import java.util.Arrays;
24 
25 /**
26  * A {@link Timeline} for Cast media queues.
27  */
28 /* package */ final class CastTimeline extends Timeline {
29 
30   /** Holds {@link Timeline} related data for a Cast media item. */
31   public static final class ItemData {
32 
33     /** Holds no media information. */
34     public static final ItemData EMPTY = new ItemData();
35 
36     /** The duration of the item in microseconds, or {@link C#TIME_UNSET} if unknown. */
37     public final long durationUs;
38     /**
39      * The default start position of the item in microseconds, or {@link C#TIME_UNSET} if unknown.
40      */
41     public final long defaultPositionUs;
42     /** Whether the item is live content, or {@code false} if unknown. */
43     public final boolean isLive;
44 
ItemData()45     private ItemData() {
46       this(
47           /* durationUs= */ C.TIME_UNSET, /* defaultPositionUs */
48           C.TIME_UNSET,
49           /* isLive= */ false);
50     }
51 
52     /**
53      * Creates an instance.
54      *
55      * @param durationUs See {@link #durationsUs}.
56      * @param defaultPositionUs See {@link #defaultPositionUs}.
57      * @param isLive See {@link #isLive}.
58      */
ItemData(long durationUs, long defaultPositionUs, boolean isLive)59     public ItemData(long durationUs, long defaultPositionUs, boolean isLive) {
60       this.durationUs = durationUs;
61       this.defaultPositionUs = defaultPositionUs;
62       this.isLive = isLive;
63     }
64 
65     /**
66      * Returns a copy of this instance with the given values.
67      *
68      * @param durationUs The duration in microseconds, or {@link C#TIME_UNSET} if unknown.
69      * @param defaultPositionUs The default start position in microseconds, or {@link C#TIME_UNSET}
70      *     if unknown.
71      * @param isLive Whether the item is live, or {@code false} if unknown.
72      */
copyWithNewValues(long durationUs, long defaultPositionUs, boolean isLive)73     public ItemData copyWithNewValues(long durationUs, long defaultPositionUs, boolean isLive) {
74       if (durationUs == this.durationUs
75           && defaultPositionUs == this.defaultPositionUs
76           && isLive == this.isLive) {
77         return this;
78       }
79       return new ItemData(durationUs, defaultPositionUs, isLive);
80     }
81   }
82 
83   /** {@link Timeline} for a cast queue that has no items. */
84   public static final CastTimeline EMPTY_CAST_TIMELINE =
85       new CastTimeline(new int[0], new SparseArray<>());
86 
87   private final SparseIntArray idsToIndex;
88   private final int[] ids;
89   private final long[] durationsUs;
90   private final long[] defaultPositionsUs;
91   private final boolean[] isLive;
92 
93   /**
94    * Creates a Cast timeline from the given data.
95    *
96    * @param itemIds The ids of the items in the timeline.
97    * @param itemIdToData Maps item ids to {@link ItemData}.
98    */
CastTimeline(int[] itemIds, SparseArray<ItemData> itemIdToData)99   public CastTimeline(int[] itemIds, SparseArray<ItemData> itemIdToData) {
100     int itemCount = itemIds.length;
101     idsToIndex = new SparseIntArray(itemCount);
102     ids = Arrays.copyOf(itemIds, itemCount);
103     durationsUs = new long[itemCount];
104     defaultPositionsUs = new long[itemCount];
105     isLive = new boolean[itemCount];
106     for (int i = 0; i < ids.length; i++) {
107       int id = ids[i];
108       idsToIndex.put(id, i);
109       ItemData data = itemIdToData.get(id, ItemData.EMPTY);
110       durationsUs[i] = data.durationUs;
111       defaultPositionsUs[i] = data.defaultPositionUs == C.TIME_UNSET ? 0 : data.defaultPositionUs;
112       isLive[i] = data.isLive;
113     }
114   }
115 
116   // Timeline implementation.
117 
118   @Override
getWindowCount()119   public int getWindowCount() {
120     return ids.length;
121   }
122 
123   @Override
getWindow(int windowIndex, Window window, long defaultPositionProjectionUs)124   public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
125     long durationUs = durationsUs[windowIndex];
126     boolean isDynamic = durationUs == C.TIME_UNSET;
127     return window.set(
128         /* uid= */ ids[windowIndex],
129         /* tag= */ ids[windowIndex],
130         /* manifest= */ null,
131         /* presentationStartTimeMs= */ C.TIME_UNSET,
132         /* windowStartTimeMs= */ C.TIME_UNSET,
133         /* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
134         /* isSeekable= */ !isDynamic,
135         isDynamic,
136         isLive[windowIndex],
137         defaultPositionsUs[windowIndex],
138         durationUs,
139         /* firstPeriodIndex= */ windowIndex,
140         /* lastPeriodIndex= */ windowIndex,
141         /* positionInFirstPeriodUs= */ 0);
142   }
143 
144   @Override
getPeriodCount()145   public int getPeriodCount() {
146     return ids.length;
147   }
148 
149   @Override
getPeriod(int periodIndex, Period period, boolean setIds)150   public Period getPeriod(int periodIndex, Period period, boolean setIds) {
151     int id = ids[periodIndex];
152     return period.set(id, id, periodIndex, durationsUs[periodIndex], 0);
153   }
154 
155   @Override
getIndexOfPeriod(Object uid)156   public int getIndexOfPeriod(Object uid) {
157     return uid instanceof Integer ? idsToIndex.get((int) uid, C.INDEX_UNSET) : C.INDEX_UNSET;
158   }
159 
160   @Override
getUidOfPeriod(int periodIndex)161   public Integer getUidOfPeriod(int periodIndex) {
162     return ids[periodIndex];
163   }
164 
165   // equals and hashCode implementations.
166 
167   @Override
equals(@ullable Object other)168   public boolean equals(@Nullable Object other) {
169     if (this == other) {
170       return true;
171     } else if (!(other instanceof CastTimeline)) {
172       return false;
173     }
174     CastTimeline that = (CastTimeline) other;
175     return Arrays.equals(ids, that.ids)
176         && Arrays.equals(durationsUs, that.durationsUs)
177         && Arrays.equals(defaultPositionsUs, that.defaultPositionsUs)
178         && Arrays.equals(isLive, that.isLive);
179   }
180 
181   @Override
hashCode()182   public int hashCode() {
183     int result = Arrays.hashCode(ids);
184     result = 31 * result + Arrays.hashCode(durationsUs);
185     result = 31 * result + Arrays.hashCode(defaultPositionsUs);
186     result = 31 * result + Arrays.hashCode(isLive);
187     return result;
188   }
189 
190 }
191