• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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;
17 
18 import androidx.annotation.Nullable;
19 import com.google.android.exoplayer2.util.Util;
20 import java.util.Collections;
21 import java.util.List;
22 
23 /** Abstract base {@link Player} which implements common implementation independent methods. */
24 public abstract class BasePlayer implements Player {
25 
26   protected final Timeline.Window window;
27 
BasePlayer()28   public BasePlayer() {
29     window = new Timeline.Window();
30   }
31 
32   @Override
setMediaItem(MediaItem mediaItem)33   public void setMediaItem(MediaItem mediaItem) {
34     setMediaItems(Collections.singletonList(mediaItem));
35   }
36 
37   @Override
setMediaItem(MediaItem mediaItem, long startPositionMs)38   public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
39     setMediaItems(Collections.singletonList(mediaItem), /* startWindowIndex= */ 0, startPositionMs);
40   }
41 
42   @Override
setMediaItem(MediaItem mediaItem, boolean resetPosition)43   public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
44     setMediaItems(Collections.singletonList(mediaItem), resetPosition);
45   }
46 
47   @Override
setMediaItems(List<MediaItem> mediaItems, boolean resetPosition)48   public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
49     setMediaItems(
50         mediaItems, /* startWindowIndex= */ C.INDEX_UNSET, /* startPositionMs= */ C.TIME_UNSET);
51   }
52 
53   @Override
setMediaItems(List<MediaItem> mediaItems)54   public void setMediaItems(List<MediaItem> mediaItems) {
55     setMediaItems(mediaItems, /* resetPosition= */ true);
56   }
57 
58   @Override
addMediaItem(int index, MediaItem mediaItem)59   public void addMediaItem(int index, MediaItem mediaItem) {
60     addMediaItems(index, Collections.singletonList(mediaItem));
61   }
62 
63   @Override
addMediaItem(MediaItem mediaItem)64   public void addMediaItem(MediaItem mediaItem) {
65     addMediaItems(Collections.singletonList(mediaItem));
66   }
67 
68   @Override
moveMediaItem(int currentIndex, int newIndex)69   public void moveMediaItem(int currentIndex, int newIndex) {
70     if (currentIndex != newIndex) {
71       moveMediaItems(/* fromIndex= */ currentIndex, /* toIndex= */ currentIndex + 1, newIndex);
72     }
73   }
74 
75   @Override
removeMediaItem(int index)76   public void removeMediaItem(int index) {
77     removeMediaItems(/* fromIndex= */ index, /* toIndex= */ index + 1);
78   }
79 
80   @Override
play()81   public final void play() {
82     setPlayWhenReady(true);
83   }
84 
85   @Override
pause()86   public final void pause() {
87     setPlayWhenReady(false);
88   }
89 
90   @Override
isPlaying()91   public final boolean isPlaying() {
92     return getPlaybackState() == Player.STATE_READY
93         && getPlayWhenReady()
94         && getPlaybackSuppressionReason() == PLAYBACK_SUPPRESSION_REASON_NONE;
95   }
96 
97   @Override
seekToDefaultPosition()98   public final void seekToDefaultPosition() {
99     seekToDefaultPosition(getCurrentWindowIndex());
100   }
101 
102   @Override
seekToDefaultPosition(int windowIndex)103   public final void seekToDefaultPosition(int windowIndex) {
104     seekTo(windowIndex, /* positionMs= */ C.TIME_UNSET);
105   }
106 
107   @Override
seekTo(long positionMs)108   public final void seekTo(long positionMs) {
109     seekTo(getCurrentWindowIndex(), positionMs);
110   }
111 
112   @Override
hasPrevious()113   public final boolean hasPrevious() {
114     return getPreviousWindowIndex() != C.INDEX_UNSET;
115   }
116 
117   @Override
previous()118   public final void previous() {
119     int previousWindowIndex = getPreviousWindowIndex();
120     if (previousWindowIndex != C.INDEX_UNSET) {
121       seekToDefaultPosition(previousWindowIndex);
122     }
123   }
124 
125   @Override
hasNext()126   public final boolean hasNext() {
127     return getNextWindowIndex() != C.INDEX_UNSET;
128   }
129 
130   @Override
next()131   public final void next() {
132     int nextWindowIndex = getNextWindowIndex();
133     if (nextWindowIndex != C.INDEX_UNSET) {
134       seekToDefaultPosition(nextWindowIndex);
135     }
136   }
137 
138   @Override
stop()139   public final void stop() {
140     stop(/* reset= */ false);
141   }
142 
143   @Override
getNextWindowIndex()144   public final int getNextWindowIndex() {
145     Timeline timeline = getCurrentTimeline();
146     return timeline.isEmpty()
147         ? C.INDEX_UNSET
148         : timeline.getNextWindowIndex(
149             getCurrentWindowIndex(), getRepeatModeForNavigation(), getShuffleModeEnabled());
150   }
151 
152   @Override
getPreviousWindowIndex()153   public final int getPreviousWindowIndex() {
154     Timeline timeline = getCurrentTimeline();
155     return timeline.isEmpty()
156         ? C.INDEX_UNSET
157         : timeline.getPreviousWindowIndex(
158             getCurrentWindowIndex(), getRepeatModeForNavigation(), getShuffleModeEnabled());
159   }
160 
161   @Override
162   @Nullable
getCurrentTag()163   public final Object getCurrentTag() {
164     Timeline timeline = getCurrentTimeline();
165     return timeline.isEmpty() ? null : timeline.getWindow(getCurrentWindowIndex(), window).tag;
166   }
167 
168   @Override
169   @Nullable
getCurrentManifest()170   public final Object getCurrentManifest() {
171     Timeline timeline = getCurrentTimeline();
172     return timeline.isEmpty() ? null : timeline.getWindow(getCurrentWindowIndex(), window).manifest;
173   }
174 
175   @Override
getBufferedPercentage()176   public final int getBufferedPercentage() {
177     long position = getBufferedPosition();
178     long duration = getDuration();
179     return position == C.TIME_UNSET || duration == C.TIME_UNSET
180         ? 0
181         : duration == 0 ? 100 : Util.constrainValue((int) ((position * 100) / duration), 0, 100);
182   }
183 
184   @Override
isCurrentWindowDynamic()185   public final boolean isCurrentWindowDynamic() {
186     Timeline timeline = getCurrentTimeline();
187     return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isDynamic;
188   }
189 
190   @Override
isCurrentWindowLive()191   public final boolean isCurrentWindowLive() {
192     Timeline timeline = getCurrentTimeline();
193     return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isLive;
194   }
195 
196   @Override
getCurrentLiveOffset()197   public final long getCurrentLiveOffset() {
198     Timeline timeline = getCurrentTimeline();
199     if (timeline.isEmpty()) {
200       return C.TIME_UNSET;
201     }
202     long windowStartTimeMs = timeline.getWindow(getCurrentWindowIndex(), window).windowStartTimeMs;
203     if (windowStartTimeMs == C.TIME_UNSET) {
204       return C.TIME_UNSET;
205     }
206     return window.getCurrentUnixTimeMs() - window.windowStartTimeMs - getContentPosition();
207   }
208 
209   @Override
isCurrentWindowSeekable()210   public final boolean isCurrentWindowSeekable() {
211     Timeline timeline = getCurrentTimeline();
212     return !timeline.isEmpty() && timeline.getWindow(getCurrentWindowIndex(), window).isSeekable;
213   }
214 
215   @Override
getContentDuration()216   public final long getContentDuration() {
217     Timeline timeline = getCurrentTimeline();
218     return timeline.isEmpty()
219         ? C.TIME_UNSET
220         : timeline.getWindow(getCurrentWindowIndex(), window).getDurationMs();
221   }
222 
223   @RepeatMode
getRepeatModeForNavigation()224   private int getRepeatModeForNavigation() {
225     @RepeatMode int repeatMode = getRepeatMode();
226     return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode;
227   }
228 
229   /** Holds a listener reference. */
230   protected static final class ListenerHolder {
231 
232     /**
233      * The listener on which {link #invoke} will execute {@link ListenerInvocation listener
234      * invocations}.
235      */
236     public final Player.EventListener listener;
237 
238     private boolean released;
239 
ListenerHolder(Player.EventListener listener)240     public ListenerHolder(Player.EventListener listener) {
241       this.listener = listener;
242     }
243 
244     /** Prevents any further {@link ListenerInvocation} to be executed on {@link #listener}. */
release()245     public void release() {
246       released = true;
247     }
248 
249     /**
250      * Executes the given {@link ListenerInvocation} on {@link #listener}. Does nothing if {@link
251      * #release} has been called on this instance.
252      */
invoke(ListenerInvocation listenerInvocation)253     public void invoke(ListenerInvocation listenerInvocation) {
254       if (!released) {
255         listenerInvocation.invokeListener(listener);
256       }
257     }
258 
259     @Override
equals(@ullable Object other)260     public boolean equals(@Nullable Object other) {
261       if (this == other) {
262         return true;
263       }
264       if (other == null || getClass() != other.getClass()) {
265         return false;
266       }
267       return listener.equals(((ListenerHolder) other).listener);
268     }
269 
270     @Override
hashCode()271     public int hashCode() {
272       return listener.hashCode();
273     }
274   }
275 
276   /** Parameterized invocation of a {@link Player.EventListener} method. */
277   protected interface ListenerInvocation {
278 
279     /** Executes the invocation on the given {@link Player.EventListener}. */
invokeListener(Player.EventListener listener)280     void invokeListener(Player.EventListener listener);
281   }
282 }
283