mirror of
https://github.com/samsonjs/media.git
synced 2026-04-13 12:35:48 +00:00
Change SeekWindow -> Window, and add window duration.
- This avoids the need to have to use the timeline to calculate a window duration, which can be awkward. - Window now represents a window of availability with an isSeekable flag, rather than a window of seekability. - Promoted Timeline and Window to top package; they're pretty important :). ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=130278509
This commit is contained in:
parent
b120bea029
commit
1a909fd163
24 changed files with 160 additions and 161 deletions
|
|
@ -22,6 +22,7 @@ import com.google.android.exoplayer2.ExoPlayer;
|
|||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.RendererCapabilities;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
|
||||
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
||||
|
|
@ -33,7 +34,6 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
|
|||
import com.google.android.exoplayer2.metadata.id3.TxxxFrame;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||
|
|
@ -94,11 +94,11 @@ import java.util.Locale;
|
|||
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
|
||||
boolean isFinal = timeline.isFinal();
|
||||
int periodCount = timeline.getPeriodCount();
|
||||
int seekWindowCount = timeline.getSeekWindowCount();
|
||||
int windowCount = timeline.getWindowCount();
|
||||
Log.d(TAG, "sourceInfo[isFinal=" + isFinal + ", startTime=" + timeline.getAbsoluteStartTime()
|
||||
+ ", periodCount=" + periodCount + ", seekWindows: " + seekWindowCount);
|
||||
for (int seekWindowIndex = 0; seekWindowIndex < seekWindowCount; seekWindowIndex++) {
|
||||
Log.d(TAG, " " + timeline.getSeekWindow(seekWindowIndex));
|
||||
+ ", periodCount=" + periodCount + ", windows: " + windowCount);
|
||||
for (int windowIndex = 0; windowIndex < windowCount; windowIndex++) {
|
||||
Log.d(TAG, " " + timeline.getWindow(windowIndex));
|
||||
}
|
||||
Log.d(TAG, "]");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
|||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.drm.HttpMediaDrmCallback;
|
||||
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
|
||||
|
|
@ -51,7 +52,6 @@ import com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryExcep
|
|||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.dash.DashMediaSource;
|
||||
import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource;
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
|||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
|||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
|||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Renderer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ package com.google.android.exoplayer2;
|
|||
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
|
||||
/**
|
||||
* An extensible media player exposing traditional high-level media player functionality, such as
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import android.util.Log;
|
|||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.ExoPlayerImplInternal.PlaybackInfo;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import com.google.android.exoplayer2.ExoPlayer.ExoPlayerMessage;
|
|||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
|||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Frame;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.text.TextRenderer;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelector;
|
||||
|
|
|
|||
|
|
@ -13,10 +13,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.source;
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaSource.Listener;
|
||||
|
||||
/**
|
||||
* The player's timeline consisting of one or more periods. Instances are immutable.
|
||||
|
|
@ -87,15 +86,15 @@ public interface Timeline {
|
|||
int getIndexOfPeriod(Object id);
|
||||
|
||||
/**
|
||||
* Returns the number of seek windows that can be accessed via {@link #getSeekWindow(int)}.
|
||||
* Returns the number of windows that can be accessed via {@link #getWindow(int)}.
|
||||
*/
|
||||
int getSeekWindowCount();
|
||||
int getWindowCount();
|
||||
|
||||
/**
|
||||
* Returns the {@link SeekWindow} at {@code index}, which represents positions that can be seeked
|
||||
* to in the timeline. The seek windows may change when
|
||||
* {@link MediaSource.Listener#onSourceInfoRefreshed(Timeline, Object)} is called.
|
||||
* Returns the {@link Window} at {@code index}, which represents positions that can be seeked
|
||||
* to in the timeline. The windows may change when
|
||||
* {@link Listener#onSourceInfoRefreshed(Timeline, Object)} is called.
|
||||
*/
|
||||
SeekWindow getSeekWindow(int index);
|
||||
Window getWindow(int index);
|
||||
|
||||
}
|
||||
|
|
@ -13,27 +13,26 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer2.source;
|
||||
package com.google.android.exoplayer2;
|
||||
|
||||
/**
|
||||
* A window of times the player can seek to.
|
||||
* A window of available media. Instances are immutable.
|
||||
*/
|
||||
public final class SeekWindow {
|
||||
|
||||
public static final SeekWindow UNSEEKABLE = createWindowFromZero(0);
|
||||
public final class Window {
|
||||
|
||||
/**
|
||||
* Creates a new {@link SeekWindow} containing times from zero up to {@code durationUs} in the
|
||||
* first period.
|
||||
* Creates a new {@link Window} containing times from zero up to {@code durationUs} in the first
|
||||
* period.
|
||||
*
|
||||
* @param durationUs The duration of the window, in microseconds.
|
||||
* @param isSeekable Whether seeking is supported within the window.
|
||||
*/
|
||||
public static SeekWindow createWindowFromZero(long durationUs) {
|
||||
return createWindow(0, 0, 0, durationUs);
|
||||
public static Window createWindowFromZero(long durationUs, boolean isSeekable) {
|
||||
return createWindow(0, 0, 0, durationUs, durationUs, isSeekable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link SeekWindow} representing the specified time range.
|
||||
* Creates a new {@link Window} representing the specified time range.
|
||||
*
|
||||
* @param startPeriodIndex The index of the period containing the start of the window.
|
||||
* @param startTimeUs The start time of the window in microseconds, relative to the start of the
|
||||
|
|
@ -41,10 +40,15 @@ public final class SeekWindow {
|
|||
* @param endPeriodIndex The index of the period containing the end of the window.
|
||||
* @param endTimeUs The end time of the window in microseconds, relative to the start of the
|
||||
* specified end period.
|
||||
* @param durationUs The duration of the window in microseconds.
|
||||
* @param isSeekable Whether seeking is supported within the window.
|
||||
*/
|
||||
public static SeekWindow createWindow(int startPeriodIndex, long startTimeUs,
|
||||
int endPeriodIndex, long endTimeUs) {
|
||||
return new SeekWindow(startPeriodIndex, startTimeUs / 1000, endPeriodIndex, endTimeUs / 1000);
|
||||
public static Window createWindow(int startPeriodIndex, long startTimeUs,
|
||||
int endPeriodIndex, long endTimeUs, long durationUs, boolean isSeekable) {
|
||||
return new Window(startPeriodIndex, startTimeUs / 1000, endPeriodIndex,
|
||||
endTimeUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (endTimeUs / 1000),
|
||||
durationUs == C.UNSET_TIME_US ? ExoPlayer.UNKNOWN_TIME : (durationUs / 1000),
|
||||
isSeekable);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -65,24 +69,35 @@ public final class SeekWindow {
|
|||
* {@link #endPeriodIndex}, in milliseconds.
|
||||
*/
|
||||
public final long endTimeMs;
|
||||
/**
|
||||
* The duration of the window in milliseconds, or {@link C#UNSET_TIME_US} if unknown.
|
||||
*/
|
||||
public final long durationMs;
|
||||
/**
|
||||
* Whether it's possible to seek within the window.
|
||||
*/
|
||||
public final boolean isSeekable;
|
||||
|
||||
private SeekWindow(int startPeriodIndex, long startTimeMs, int endPeriodIndex, long endTimeMs) {
|
||||
private Window(int startPeriodIndex, long startTimeMs, int endPeriodIndex, long endTimeMs,
|
||||
long durationMs, boolean isSeekable) {
|
||||
this.startPeriodIndex = startPeriodIndex;
|
||||
this.startTimeMs = startTimeMs;
|
||||
this.endPeriodIndex = endPeriodIndex;
|
||||
this.endTimeMs = endTimeMs;
|
||||
this.durationMs = durationMs;
|
||||
this.isSeekable = isSeekable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new seek window that is offset by the specified number of periods.
|
||||
* Returns a new window that is offset by the specified number of periods.
|
||||
*
|
||||
* @param periodCount The number of periods to add to {@link #startPeriodIndex} and
|
||||
* {@link #endPeriodIndex} when constructing the copy.
|
||||
* @return A new seek window that is offset by the specified number of periods.
|
||||
* @return A new window that is offset by the specified number of periods.
|
||||
*/
|
||||
public SeekWindow copyOffsetByPeriodCount(int periodCount) {
|
||||
return new SeekWindow(startPeriodIndex + periodCount, startTimeMs, endPeriodIndex + periodCount,
|
||||
endTimeMs);
|
||||
public Window copyOffsetByPeriodCount(int periodCount) {
|
||||
return new Window(startPeriodIndex + periodCount, startTimeMs, endPeriodIndex + periodCount,
|
||||
endTimeMs, durationMs, isSeekable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -103,16 +118,18 @@ public final class SeekWindow {
|
|||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SeekWindow other = (SeekWindow) obj;
|
||||
Window other = (Window) obj;
|
||||
return other.startPeriodIndex == startPeriodIndex
|
||||
&& other.startTimeMs == startTimeMs
|
||||
&& other.endPeriodIndex == endPeriodIndex
|
||||
&& other.endTimeMs == endTimeMs;
|
||||
&& other.endTimeMs == endTimeMs
|
||||
&& other.durationMs == durationMs
|
||||
&& other.isSeekable == isSeekable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SeekWindow[" + startPeriodIndex + ", " + startTimeMs + ", " + endPeriodIndex + ", "
|
||||
return "Window[" + startPeriodIndex + ", " + startTimeMs + ", " + endPeriodIndex + ", "
|
||||
+ endTimeMs + "]";
|
||||
}
|
||||
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Window;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -130,21 +132,21 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
private final Timeline[] timelines;
|
||||
private final boolean isFinal;
|
||||
private final int[] sourceOffsets;
|
||||
private final SeekWindow[] seekWindows;
|
||||
private final Window[] windows;
|
||||
|
||||
public ConcatenatedTimeline(Timeline[] timelines) {
|
||||
boolean isFinal = true;
|
||||
int[] sourceOffsets = new int[timelines.length];
|
||||
int sourceOffset = 0;
|
||||
ArrayList<SeekWindow> concatenatedSeekWindows = new ArrayList<>();
|
||||
ArrayList<Window> concatenatedWindows = new ArrayList<>();
|
||||
for (int i = 0; i < timelines.length; i++) {
|
||||
Timeline timeline = timelines[i];
|
||||
isFinal &= timeline.isFinal();
|
||||
// Offset the seek windows so they are relative to the source.
|
||||
int seekWindowCount = timeline.getSeekWindowCount();
|
||||
for (int j = 0; j < seekWindowCount; j++) {
|
||||
SeekWindow sourceSeekWindow = timeline.getSeekWindow(j);
|
||||
concatenatedSeekWindows.add(sourceSeekWindow.copyOffsetByPeriodCount(sourceOffset));
|
||||
// Offset the windows so they are relative to the source.
|
||||
int windowCount = timeline.getWindowCount();
|
||||
for (int j = 0; j < windowCount; j++) {
|
||||
Window sourceWindow = timeline.getWindow(j);
|
||||
concatenatedWindows.add(sourceWindow.copyOffsetByPeriodCount(sourceOffset));
|
||||
}
|
||||
sourceOffset += timeline.getPeriodCount();
|
||||
sourceOffsets[i] = sourceOffset;
|
||||
|
|
@ -152,7 +154,7 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
this.timelines = timelines;
|
||||
this.isFinal = isFinal;
|
||||
this.sourceOffsets = sourceOffsets;
|
||||
seekWindows = concatenatedSeekWindows.toArray(new SeekWindow[concatenatedSeekWindows.size()]);
|
||||
windows = concatenatedWindows.toArray(new Window[concatenatedWindows.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -208,13 +210,13 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getSeekWindowCount() {
|
||||
return seekWindows.length;
|
||||
public int getWindowCount() {
|
||||
return windows.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekWindow getSeekWindow(int index) {
|
||||
return seekWindows[index];
|
||||
public Window getWindow(int index) {
|
||||
return windows[index];
|
||||
}
|
||||
|
||||
private int getSourceIndexForPeriod(int periodIndex) {
|
||||
|
|
|
|||
|
|
@ -297,9 +297,8 @@ import java.util.Arrays;
|
|||
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
||||
durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0
|
||||
: largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US;
|
||||
sourceListener.onSourceInfoRefreshed(seekMap.isSeekable()
|
||||
? SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs)
|
||||
: SinglePeriodTimeline.createUnseekableFinalTimeline(0, durationUs), null);
|
||||
sourceListener.onSourceInfoRefreshed(
|
||||
new SinglePeriodTimeline(0, durationUs, seekMap.isSeekable()), null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -382,9 +381,8 @@ import java.util.Arrays;
|
|||
tracks = new TrackGroupArray(trackArray);
|
||||
prepared = true;
|
||||
callback.onPrepared(this);
|
||||
sourceListener.onSourceInfoRefreshed(seekMap.isSeekable()
|
||||
? SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs)
|
||||
: SinglePeriodTimeline.createUnseekableFinalTimeline(0, durationUs), null);
|
||||
sourceListener.onSourceInfoRefreshed(
|
||||
new SinglePeriodTimeline(0, durationUs, seekMap.isSeekable()), null);
|
||||
}
|
||||
|
||||
private void copyLengthFromLoader(ExtractingLoadable loadable) {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ import android.net.Uri;
|
|||
import android.os.Handler;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||
import com.google.android.exoplayer2.extractor.Extractor;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
|
|
@ -135,7 +135,7 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
|
|||
@Override
|
||||
public void prepareSource(MediaSource.Listener listener) {
|
||||
sourceListener = listener;
|
||||
timeline = SinglePeriodTimeline.createNonFinalTimeline(0);
|
||||
timeline = new SinglePeriodTimeline(this, C.UNSET_TIME_US, false);
|
||||
listener.onSourceInfoRefreshed(timeline, null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import java.io.IOException;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
|
|
|||
|
|
@ -17,68 +17,45 @@ package com.google.android.exoplayer2.source;
|
|||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Window;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
||||
/**
|
||||
* A {@link Timeline} consisting of a single period and seek window.
|
||||
* A {@link Timeline} consisting of a single period and window.
|
||||
*/
|
||||
public final class SinglePeriodTimeline implements Timeline {
|
||||
|
||||
/**
|
||||
* Returns a new timeline with one period of unknown duration and an empty seek window.
|
||||
*
|
||||
* @param id The identifier for the period.
|
||||
* @return A new timeline with one period of unknown duration.
|
||||
*/
|
||||
public static Timeline createNonFinalTimeline(Object id) {
|
||||
return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US, SeekWindow.UNSEEKABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new timeline with one period of unknown duration and the specified seek window.
|
||||
*
|
||||
* @param id The identifier for the period.
|
||||
* @param seekWindow The seek window.
|
||||
* @return A new timeline with one period of unknown duration.
|
||||
*/
|
||||
public static Timeline createNonFinalTimeline(Object id, SeekWindow seekWindow) {
|
||||
return new SinglePeriodTimeline(id, false, C.UNSET_TIME_US, seekWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a final timeline with one period of known duration and an empty seek window.
|
||||
*
|
||||
* @param id The identifier for the period.
|
||||
* @param durationUs The duration of the period, in microseconds.
|
||||
* @return A new, unseekable, final timeline with one period.
|
||||
*/
|
||||
public static Timeline createUnseekableFinalTimeline(Object id, long durationUs) {
|
||||
return new SinglePeriodTimeline(id, true, durationUs, SeekWindow.UNSEEKABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a final timeline with one period of known duration and a seek window extending from
|
||||
* zero to its duration.
|
||||
*
|
||||
* @param id The identifier for the period.
|
||||
* @param durationUs The duration of the period, in microseconds.
|
||||
* @return A new, seekable, final timeline with one period.
|
||||
*/
|
||||
public static Timeline createSeekableFinalTimeline(Object id, long durationUs) {
|
||||
return new SinglePeriodTimeline(id, true, durationUs,
|
||||
SeekWindow.createWindowFromZero(durationUs));
|
||||
}
|
||||
|
||||
private final Object id;
|
||||
private final boolean isFinal;
|
||||
private final long durationUs;
|
||||
private final SeekWindow seekWindow;
|
||||
private final Window window;
|
||||
|
||||
private SinglePeriodTimeline(Object id, boolean isFinal, long durationUs, SeekWindow seekWindow) {
|
||||
/**
|
||||
* Creates a final timeline with one period of known duration and a window extending from
|
||||
* zero to its duration.
|
||||
*
|
||||
* @param id The identifier for the period.
|
||||
* @param durationUs The duration of the period, in microseconds.
|
||||
* @param isSeekable Whether seeking is supported within the period.
|
||||
*/
|
||||
public SinglePeriodTimeline(Object id, long durationUs, boolean isSeekable) {
|
||||
this(id, durationUs, Window.createWindowFromZero(durationUs, isSeekable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a final timeline with one period of known duration and a window extending from
|
||||
* zero to its duration.
|
||||
*
|
||||
* @param id The identifier for the period.
|
||||
* @param durationUs The duration of the period, in microseconds.
|
||||
* @param window The available window within the period.
|
||||
*/
|
||||
public SinglePeriodTimeline(Object id, long durationUs, Window window) {
|
||||
this.id = Assertions.checkNotNull(id);
|
||||
this.isFinal = isFinal;
|
||||
this.durationUs = durationUs;
|
||||
this.seekWindow = seekWindow;
|
||||
this.window = window;
|
||||
this.isFinal = true; // TODO: Remove.
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -126,13 +103,13 @@ public final class SinglePeriodTimeline implements Timeline {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getSeekWindowCount() {
|
||||
public int getWindowCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekWindow getSeekWindow(int index) {
|
||||
return seekWindow;
|
||||
public Window getWindow(int index) {
|
||||
return window;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.source;
|
|||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
|
|
@ -78,7 +79,7 @@ public final class SingleSampleMediaSource implements MediaSource {
|
|||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.eventSourceId = eventSourceId;
|
||||
timeline = SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs);
|
||||
timeline = new SinglePeriodTimeline(0, durationUs, true);
|
||||
}
|
||||
|
||||
// MediaSource implementation.
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ import android.util.Log;
|
|||
import android.util.SparseArray;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Window;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.SeekWindow;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.DashManifestParser;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Period;
|
||||
|
|
@ -59,7 +59,7 @@ public final class DashMediaSource implements MediaSource {
|
|||
/**
|
||||
* The interval in milliseconds between invocations of
|
||||
* {@link MediaSource.Listener#onSourceInfoRefreshed(Timeline, Object)} when the source's
|
||||
* {@link SeekWindow} is changing dynamically (for example, for incomplete live streams).
|
||||
* {@link Window} is changing dynamically (for example, for incomplete live streams).
|
||||
*/
|
||||
private static final int NOTIFY_MANIFEST_INTERVAL_MS = 5000;
|
||||
/**
|
||||
|
|
@ -89,7 +89,7 @@ public final class DashMediaSource implements MediaSource {
|
|||
private long manifestLoadEndTimestamp;
|
||||
private DashManifest manifest;
|
||||
private Handler handler;
|
||||
private SeekWindow seekWindow;
|
||||
private Window window;
|
||||
private long elapsedRealtimeOffsetMs;
|
||||
|
||||
private int firstPeriodId;
|
||||
|
|
@ -151,20 +151,20 @@ public final class DashMediaSource implements MediaSource {
|
|||
|
||||
@Override
|
||||
public Position getDefaultStartPosition(int index) {
|
||||
if (seekWindow == null) {
|
||||
if (window == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (index == 0 && manifest.dynamic) {
|
||||
// The stream is live, so return a position a position offset from the live edge.
|
||||
int periodIndex = seekWindow.endPeriodIndex;
|
||||
long positionMs = seekWindow.endTimeMs - LIVE_EDGE_OFFSET_MS;
|
||||
while (positionMs < 0 && periodIndex > seekWindow.startPeriodIndex) {
|
||||
int periodIndex = window.endPeriodIndex;
|
||||
long positionMs = window.endTimeMs - LIVE_EDGE_OFFSET_MS;
|
||||
while (positionMs < 0 && periodIndex > window.startPeriodIndex) {
|
||||
periodIndex--;
|
||||
positionMs += manifest.getPeriodDurationMs(periodIndex);
|
||||
}
|
||||
positionMs = Math.max(positionMs,
|
||||
periodIndex == seekWindow.startPeriodIndex ? seekWindow.startTimeMs : 0);
|
||||
periodIndex == window.startPeriodIndex ? window.startTimeMs : 0);
|
||||
return new Position(periodIndex, positionMs * 1000);
|
||||
}
|
||||
return new Position(index, 0);
|
||||
|
|
@ -370,7 +370,7 @@ public final class DashMediaSource implements MediaSource {
|
|||
}
|
||||
|
||||
private void refreshSourceInfo() {
|
||||
// Update the seek window.
|
||||
// Update the window.
|
||||
int lastPeriodIndex = manifest.getPeriodCount() - 1;
|
||||
PeriodSeekInfo firstPeriodSeekInfo = PeriodSeekInfo.createPeriodSeekInfo(manifest.getPeriod(0),
|
||||
manifest.getPeriodDurationUs(0));
|
||||
|
|
@ -379,7 +379,7 @@ public final class DashMediaSource implements MediaSource {
|
|||
long currentStartTimeUs;
|
||||
long currentEndTimeUs;
|
||||
if (manifest.dynamic && !lastPeriodSeekInfo.isIndexExplicit) {
|
||||
// The seek window is changing so post a Runnable to update it.
|
||||
// The window is changing so post a Runnable to update it.
|
||||
handler.postDelayed(refreshSourceInfoRunnable, NOTIFY_MANIFEST_INTERVAL_MS);
|
||||
|
||||
long minStartPositionUs = firstPeriodSeekInfo.availableStartTimeUs;
|
||||
|
|
@ -395,8 +395,13 @@ public final class DashMediaSource implements MediaSource {
|
|||
currentStartTimeUs = firstPeriodSeekInfo.availableStartTimeUs;
|
||||
currentEndTimeUs = lastPeriodSeekInfo.availableEndTimeUs;
|
||||
}
|
||||
seekWindow = SeekWindow.createWindow(0, currentStartTimeUs, lastPeriodIndex, currentEndTimeUs);
|
||||
sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, manifest, seekWindow),
|
||||
long windowDurationUs = currentEndTimeUs - currentStartTimeUs;
|
||||
for (int i = 0; i < manifest.getPeriodCount() - 1; i++) {
|
||||
windowDurationUs += manifest.getPeriodDurationUs(i);
|
||||
}
|
||||
window = Window.createWindow(0, currentStartTimeUs, lastPeriodIndex, currentEndTimeUs,
|
||||
windowDurationUs, true);
|
||||
sourceListener.onSourceInfoRefreshed(new DashTimeline(firstPeriodId, manifest, window),
|
||||
manifest);
|
||||
}
|
||||
|
||||
|
|
@ -481,12 +486,12 @@ public final class DashMediaSource implements MediaSource {
|
|||
|
||||
private final int firstPeriodId;
|
||||
private final DashManifest manifest;
|
||||
private final SeekWindow seekWindow;
|
||||
private final Window window;
|
||||
|
||||
public DashTimeline(int firstPeriodId, DashManifest manifest, SeekWindow seekWindow) {
|
||||
public DashTimeline(int firstPeriodId, DashManifest manifest, Window window) {
|
||||
this.firstPeriodId = firstPeriodId;
|
||||
this.manifest = manifest;
|
||||
this.seekWindow = seekWindow;
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -531,13 +536,13 @@ public final class DashMediaSource implements MediaSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getSeekWindowCount() {
|
||||
public int getWindowCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekWindow getSeekWindow(int index) {
|
||||
return seekWindow;
|
||||
public Window getWindow(int index) {
|
||||
return window;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import android.text.TextUtils;
|
|||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.CompositeSequenceableLoader;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.SampleStream;
|
||||
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
|
||||
|
|
@ -280,10 +280,8 @@ import java.util.List;
|
|||
trackGroups = new TrackGroupArray(trackGroupArray);
|
||||
callback.onPrepared(this);
|
||||
|
||||
// TODO[playlists]: Calculate the seek window.
|
||||
Timeline timeline = isLive
|
||||
? SinglePeriodTimeline.createUnseekableFinalTimeline(0, durationUs)
|
||||
: SinglePeriodTimeline.createSeekableFinalTimeline(0, durationUs);
|
||||
// TODO[playlists]: Calculate the window.
|
||||
Timeline timeline = new SinglePeriodTimeline(0, durationUs, !isLive);
|
||||
sourceListener.onSourceInfoRefreshed(timeline, playlist);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ package com.google.android.exoplayer2.source.hls;
|
|||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
|
|
@ -62,9 +63,10 @@ public final class HlsMediaSource implements MediaSource {
|
|||
|
||||
@Override
|
||||
public void prepareSource(MediaSource.Listener listener) {
|
||||
// TODO: Defer until the playlist has been loaded.
|
||||
sourceListener = listener;
|
||||
listener.onSourceInfoRefreshed(SinglePeriodTimeline.createNonFinalTimeline(this), null);
|
||||
// TODO: Defer until the playlist has been loaded.
|
||||
listener.onSourceInfoRefreshed(
|
||||
new SinglePeriodTimeline(this, C.UNSET_TIME_US, false), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ import android.os.Handler;
|
|||
import android.os.SystemClock;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Window;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.SeekWindow;
|
||||
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest.StreamElement;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser;
|
||||
|
|
@ -72,7 +72,7 @@ public final class SsMediaSource implements MediaSource,
|
|||
|
||||
private long manifestLoadStartTimestamp;
|
||||
private SsManifest manifest;
|
||||
private SeekWindow seekWindow;
|
||||
private Window window;
|
||||
|
||||
private Handler manifestRefreshHandler;
|
||||
|
||||
|
|
@ -114,12 +114,12 @@ public final class SsMediaSource implements MediaSource,
|
|||
|
||||
@Override
|
||||
public Position getDefaultStartPosition(int index) {
|
||||
if (seekWindow == null) {
|
||||
if (window == null) {
|
||||
return null;
|
||||
}
|
||||
if (manifest.isLive) {
|
||||
long startPositionUs = Math.max(seekWindow.startTimeMs,
|
||||
seekWindow.endTimeMs - LIVE_EDGE_OFFSET_MS) * 1000;
|
||||
long startPositionUs = Math.max(window.startTimeMs,
|
||||
window.endTimeMs - LIVE_EDGE_OFFSET_MS) * 1000;
|
||||
return new Position(0, startPositionUs);
|
||||
}
|
||||
return Position.DEFAULT;
|
||||
|
|
@ -183,18 +183,20 @@ public final class SsMediaSource implements MediaSource,
|
|||
startTimeUs = Math.min(startTimeUs, element.getStartTimeUs(0));
|
||||
}
|
||||
}
|
||||
if (startTimeUs == Long.MAX_VALUE) {
|
||||
timeline = SinglePeriodTimeline.createNonFinalTimeline(this);
|
||||
if (startTimeUs == Long.MAX_VALUE || manifest.dvrWindowLengthUs == C.UNSET_TIME_US
|
||||
|| manifest.dvrWindowLengthUs == 0) {
|
||||
timeline = new SinglePeriodTimeline(0, C.UNSET_TIME_US, false);
|
||||
} else {
|
||||
timeline = SinglePeriodTimeline.createNonFinalTimeline(this,
|
||||
SeekWindow.createWindow(0, startTimeUs, 0, startTimeUs + manifest.dvrWindowLengthUs));
|
||||
long periodDurationUs = startTimeUs + manifest.dvrWindowLengthUs;
|
||||
Window window = Window.createWindow(0, startTimeUs, 0, periodDurationUs,
|
||||
manifest.dvrWindowLengthUs, true);
|
||||
timeline = new SinglePeriodTimeline(0, periodDurationUs, window);
|
||||
}
|
||||
} else if (manifest.durationUs == C.UNSET_TIME_US) {
|
||||
timeline = SinglePeriodTimeline.createUnseekableFinalTimeline(0, C.UNSET_TIME_US);
|
||||
} else {
|
||||
timeline = SinglePeriodTimeline.createSeekableFinalTimeline(0, manifest.durationUs);
|
||||
boolean isSeekable = manifest.durationUs != C.UNSET_TIME_US;
|
||||
timeline = new SinglePeriodTimeline(0, manifest.durationUs, isSeekable);
|
||||
}
|
||||
seekWindow = timeline.getSeekWindow(0);
|
||||
window = timeline.getWindow(0);
|
||||
sourceListener.onSourceInfoRefreshed(timeline, manifest);
|
||||
scheduleManifestRefresh();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
|||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
|
||||
/**
|
||||
* A helper class for periodically updating a {@link TextView} with debug information obtained from
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ import com.google.android.exoplayer2.ExoPlayer;
|
|||
import com.google.android.exoplayer2.ExoPlayerFactory;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.audio.AudioTrack;
|
||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||
import com.google.android.exoplayer2.drm.DrmSessionManager;
|
||||
import com.google.android.exoplayer2.playbacktests.util.HostActivity.HostedTest;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.Timeline;
|
||||
import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection;
|
||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
|
||||
|
|
|
|||
Loading…
Reference in a new issue