Commit 5f1a2c71 by olly Committed by Oliver Woodman

Finalize V2 ExoPlayer API

There's still some internal to clean up to do, and in particular
it remains a TODO to be able to handle seek calls before the
timeline is set (for this CL, such calls are dropped). This change
does however finalize the API.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=131171318
parent 298464eb
Showing with 635 additions and 823 deletions
......@@ -22,8 +22,8 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
......@@ -57,16 +57,22 @@ import java.util.Locale;
MappingTrackSelector.EventListener, MetadataRenderer.Output<List<Id3Frame>> {
private static final String TAG = "EventLogger";
private static final int MAX_TIMELINE_ITEM_LINES = 3;
private static final NumberFormat TIME_FORMAT;
static {
TIME_FORMAT = NumberFormat.getInstance(Locale.US);
TIME_FORMAT.setMinimumFractionDigits(2);
TIME_FORMAT.setMaximumFractionDigits(2);
TIME_FORMAT.setGroupingUsed(false);
}
private final Timeline.Window window;
private final Timeline.Period period;
private final long startTimeMs;
public EventLogger() {
window = new Timeline.Window();
period = new Timeline.Period();
startTimeMs = SystemClock.elapsedRealtime();
}
......@@ -89,13 +95,24 @@ import java.util.Locale;
}
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
int periodCount = timeline.getPeriodCount();
int windowCount = timeline.getWindowCount();
Log.d(TAG, "sourceInfo[startTime=" + timeline.getAbsoluteStartTime() + ", periodCount="
+ periodCount + ", windows: " + windowCount);
for (int windowIndex = 0; windowIndex < windowCount; windowIndex++) {
Log.d(TAG, " " + timeline.getWindow(windowIndex));
Log.d(TAG, "sourceInfo [periodCount=" + periodCount + ", windowCount=" + windowCount);
for (int i = 0; i < Math.min(periodCount, MAX_TIMELINE_ITEM_LINES); i++) {
timeline.getPeriod(i, period);
Log.d(TAG, " " + "period [" + getTimeString(period.getDurationMs()) + "]");
}
if (periodCount > MAX_TIMELINE_ITEM_LINES) {
Log.d(TAG, " ...");
}
for (int i = 0; i < Math.min(windowCount, MAX_TIMELINE_ITEM_LINES); i++) {
timeline.getWindow(i, window);
Log.d(TAG, " " + "window [" + getTimeString(window.getDurationMs()) + ", "
+ window.isSeekable + ", " + window.isDynamic + "]");
}
if (windowCount > MAX_TIMELINE_ITEM_LINES) {
Log.d(TAG, " ...");
}
Log.d(TAG, "]");
}
......@@ -333,7 +350,7 @@ import java.util.Locale;
}
private static String getTimeString(long timeMs) {
return TIME_FORMAT.format((timeMs) / 1000f);
return timeMs == C.TIME_UNSET ? "?" : TIME_FORMAT.format((timeMs) / 1000f);
}
private static String getStateString(int state) {
......
......@@ -40,9 +40,8 @@ import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.MediaWindow;
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;
......@@ -127,7 +126,7 @@ public class PlayerActivity extends Activity implements OnKeyListener, OnTouchLi
private boolean playerNeedsSource;
private boolean shouldRestorePosition;
private int playerPeriod;
private int playerWindow;
private long playerPosition;
// Activity lifecycle
......@@ -283,9 +282,9 @@ public class PlayerActivity extends Activity implements OnKeyListener, OnTouchLi
player.setVideoSurfaceHolder(surfaceView.getHolder());
if (shouldRestorePosition) {
if (playerPosition == C.TIME_UNSET) {
player.seekToDefaultPositionForPeriod(playerPeriod);
player.seekToDefaultPosition(playerWindow);
} else {
player.seekInPeriod(playerPeriod, playerPosition);
player.seekTo(playerWindow, playerPosition);
}
}
player.setPlayWhenReady(true);
......@@ -376,13 +375,13 @@ public class PlayerActivity extends Activity implements OnKeyListener, OnTouchLi
debugViewHelper.stop();
debugViewHelper = null;
shouldRestorePosition = false;
MediaTimeline playerTimeline = player.getCurrentTimeline();
if (playerTimeline != null) {
MediaWindow window = playerTimeline.getWindow(player.getCurrentWindowIndex());
Timeline timeline = player.getCurrentTimeline();
if (timeline != null) {
playerWindow = player.getCurrentWindowIndex();
Timeline.Window window = timeline.getWindow(playerWindow, new Timeline.Window());
if (!window.isDynamic) {
shouldRestorePosition = true;
playerPeriod = player.getCurrentPeriodIndex();
playerPosition = window.isSeekable ? player.getCurrentPositionInPeriod() : C.TIME_UNSET;
playerPosition = window.isSeekable ? player.getCurrentPosition() : C.TIME_UNSET;
}
}
player.release();
......@@ -417,7 +416,7 @@ public class PlayerActivity extends Activity implements OnKeyListener, OnTouchLi
}
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
// Do nothing.
}
......
......@@ -22,8 +22,8 @@ import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.MediaTimeline;
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.trackselection.DefaultTrackSelector;
......@@ -96,7 +96,7 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
}
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
// Do nothing.
}
......
......@@ -22,8 +22,8 @@ import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.MediaTimeline;
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.trackselection.DefaultTrackSelector;
......@@ -96,7 +96,7 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
}
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
// Do nothing.
}
......
......@@ -22,8 +22,8 @@ import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.MediaTimeline;
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.trackselection.DefaultTrackSelector;
......@@ -115,7 +115,7 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
}
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
// Do nothing.
}
......
......@@ -117,10 +117,10 @@ public interface ExoPlayer {
*/
void onPlayerStateChanged(boolean playWhenReady, int playbackState);
// TODO[playlists]: Should source-initiated resets also cause this to be called?
// TODO: Should be windowIndex and position in the window.
/**
* Called when the player's position changes due to a discontinuity (seeking or playback
* transitioning to the next period).
* Called when the player's position changes due to a discontinuity (i.e. due to seeking,
* playback transitioning to the next window, or a source induced discontinuity).
*
* @param periodIndex The index of the period being played.
* @param positionMs The playback position in that period, in milliseconds.
......@@ -133,7 +133,7 @@ public interface ExoPlayer {
* @param timeline The source's timeline.
* @param manifest The loaded manifest.
*/
void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest);
void onSourceInfoRefreshed(Timeline timeline, Object manifest);
/**
* Called when an error occurs. The playback state will transition to {@link #STATE_IDLE}
......@@ -249,7 +249,7 @@ public interface ExoPlayer {
* @param mediaSource The {@link MediaSource} to play.
* @param resetPosition Whether the playback position should be reset to the source's default
* position. If false, playback will start from the position defined by
* {@link #getCurrentPeriodIndex()} and {@link #getCurrentPositionInPeriod()}.
* {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
*/
void setMediaSource(MediaSource mediaSource, boolean resetPosition);
......@@ -278,35 +278,6 @@ public interface ExoPlayer {
boolean isLoading();
/**
* Seeks to a position specified in milliseconds in the current period.
*
* @param positionMs The seek position.
*/
@Deprecated
void seekInCurrentPeriod(long positionMs);
/**
* Seeks to the default position associated with the specified period. The position can depend on
* the type of source passed to {@link #setMediaSource(MediaSource)}. For live streams it will
* typically be the live edge of the window to which the period belongs. For other streams it will
* typically be the start of the period.
*
* @param periodIndex The index of the period whose associated default position should be seeked
* to.
*/
@Deprecated
void seekToDefaultPositionForPeriod(int periodIndex);
/**
* Seeks to a position specified in milliseconds in the specified period.
*
* @param periodIndex The index of the period.
* @param positionMs The seek position relative to the start of the period.
*/
@Deprecated
void seekInPeriod(int periodIndex, long positionMs);
/**
* Seeks to the default position associated with the current window. The position can depend on
* the type of source passed to {@link #setMediaSource(MediaSource)}. For live streams it will
* typically be the live edge of the window. For other streams it will typically be the start of
......@@ -333,7 +304,7 @@ public interface ExoPlayer {
void seekTo(long positionMs);
/**
* Seeks to a position specified in milliseconds in the specified seek window.
* Seeks to a position specified in milliseconds in the specified window.
*
* @param windowIndex The index of the window.
* @param positionMs The seek position relative to the start of the window.
......@@ -376,9 +347,9 @@ public interface ExoPlayer {
void blockingSendMessages(ExoPlayerMessage... messages);
/**
* Returns the current {@link MediaTimeline}, or {@code null} if there is no timeline.
* Returns the current {@link Timeline}, or {@code null} if there is no timeline.
*/
MediaTimeline getCurrentTimeline();
Timeline getCurrentTimeline();
/**
* Returns the current manifest. The type depends on the {@link MediaSource} passed to
......@@ -386,46 +357,9 @@ public interface ExoPlayer {
*/
Object getCurrentManifest();
// Period based.
/**
* Returns the index of the current period.
*/
@Deprecated
int getCurrentPeriodIndex();
/**
* Returns the duration of the current period in milliseconds, or {@link C#TIME_UNSET} if the
* duration is not known.
*/
@Deprecated
long getCurrentPeriodDuration();
/**
* Returns the playback position in the current period, in milliseconds.
*/
@Deprecated
long getCurrentPositionInPeriod();
/**
* Returns an estimate of the position in the current period up to which data is buffered, or
* {@link C#TIME_UNSET} if no estimate is available.
*/
@Deprecated
long getBufferedPositionInPeriod();
/**
* Returns an estimate of the percentage in the current period up to which data is buffered, or 0
* if no estimate is available.
*/
@Deprecated
int getBufferedPercentageInPeriod();
// MediaWindow based.
/**
* Returns the index of the seek window associated with the current period, or
* {@link C#INDEX_UNSET} if the timeline is not set.
* Returns the index of the window associated with the current period, or {@link C#INDEX_UNSET} if
* the timeline is not set.
*/
int getCurrentWindowIndex();
......@@ -436,8 +370,8 @@ public interface ExoPlayer {
long getDuration();
/**
* Returns the playback position in the current seek window, in milliseconds, or
* {@link C#TIME_UNSET} if the timeline is not set.
* Returns the playback position in the current window, in milliseconds, or {@link C#TIME_UNSET}
* if the timeline is not set.
*/
long getCurrentPosition();
......
/*
* 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;
/**
* The player's timeline consisting of one or more periods. Instances are immutable.
*/
public interface MediaTimeline {
/**
* Returns the number of periods in the timeline.
*/
int getPeriodCount();
/**
* Returns the absolute start time of the timeline in milliseconds.
*/
long getAbsoluteStartTime();
/**
* Returns the duration of the period at {@code periodIndex} in the timeline, in milliseconds, or
* {@link C#TIME_UNSET} if not known.
*
* @param periodIndex The index of the period.
* @return The duration of the period in milliseconds, or {@link C#TIME_UNSET}.
*/
long getPeriodDurationMs(int periodIndex);
/**
* Returns the duration of the period at {@code periodIndex} in the timeline, in microseconds, or
* {@link C#TIME_UNSET} if not known.
*
* @param periodIndex The index of the period.
* @return The duration of the period in microseconds, or {@link C#TIME_UNSET}.
*/
long getPeriodDurationUs(int periodIndex);
/**
* Returns a unique identifier for the period at {@code periodIndex}, or {@code null} if the
* period at {@code periodIndex} is not known. The identifier is stable across timeline changes.
*
* @param periodIndex A period index.
* @return An identifier for the period, or {@code null} if the period is not known.
*/
Object getPeriodId(int periodIndex);
/**
* Returns the {@link MediaWindow} to which the period with the specified index belongs.
*
* @param periodIndex The period index.
* @return The corresponding window.
*/
MediaWindow getPeriodWindow(int periodIndex);
/**
* Returns the index of the window to which the period with the specified index belongs.
*
* @param periodIndex The period index.
* @return The index of the corresponding window.
*/
int getPeriodWindowIndex(int periodIndex);
/**
* Returns the index of the period identified by {@code id}, or {@link C#INDEX_UNSET} if the
* period is not in the timeline.
*
* @param id An identifier for a period.
* @return The index of the period, or {@link C#INDEX_UNSET} if the period was not found.
*/
int getIndexOfPeriod(Object id);
/**
* Returns the number of windows that can be accessed via {@link #getWindow(int)}.
*/
int getWindowCount();
/**
* Returns the {@link MediaWindow} at the specified index.
*
* @param windowIndex The window index.
*/
MediaWindow getWindow(int windowIndex);
/**
* Returns the index of the first period belonging to the window at the specified index.
*
* @param windowIndex The window index.
* @return The index of the first period in the window.
*/
int getWindowFirstPeriodIndex(int windowIndex);
/**
* Returns the index of the last period belonging to the window at the specified index.
*
* @param windowIndex The window index.
* @return The index of the last period in the window.
*/
int getWindowLastPeriodIndex(int windowIndex);
/**
* Returns the start position of the specified window in the first period belonging to it, in
* microseconds.
*
* @param windowIndex The window index.
* @return The start position of the window in the first period belonging to it.
*/
long getWindowOffsetInFirstPeriodUs(int windowIndex);
}
/*
* 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;
/**
* A window of available media. Instances are immutable.
*/
public final class MediaWindow {
/**
* Creates a new window consisting of a single period with the specified duration. The default
* start position is zero.
*
* @param durationUs The duration of the window, in microseconds.
* @param isSeekable Whether seeking is supported within the window.
* @param isDynamic Whether this seek window may change when the timeline is updated.
*/
public static MediaWindow createWindowFromZero(long durationUs, boolean isSeekable,
boolean isDynamic) {
return new MediaWindow(durationUs, isSeekable, isDynamic, 0);
}
/**
* The default position relative to the start of the window at which to start playback, in
* microseconds.
*/
public final long defaultStartPositionUs;
/**
* The duration of the window in microseconds, or {@link C#TIME_UNSET} if unknown.
*/
public final long durationUs;
/**
* Whether it's possible to seek within the window.
*/
public final boolean isSeekable;
/**
* Whether this seek window may change when the timeline is updated.
*/
public final boolean isDynamic;
/**
* @param durationUs The duration of the window in microseconds, or {@link C#TIME_UNSET} if
* unknown.
* @param isSeekable Whether seeking is supported within the window.
* @param isDynamic Whether this seek window may change when the timeline is updated.
* @param defaultStartPositionUs The default position relative to the start of the window at which
* to start playback, in microseconds.
*/
public MediaWindow(long durationUs, boolean isSeekable, boolean isDynamic,
long defaultStartPositionUs) {
this.durationUs = durationUs;
this.isSeekable = isSeekable;
this.isDynamic = isDynamic;
this.defaultStartPositionUs = defaultStartPositionUs;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (isSeekable ? 1 : 2);
result = 31 * result + (isDynamic ? 1 : 2);
result = 31 * result + (int) defaultStartPositionUs;
result = 31 * result + (int) durationUs;
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
MediaWindow other = (MediaWindow) obj;
return other.durationUs == durationUs
&& other.isSeekable == isSeekable
&& other.isDynamic == isDynamic
&& other.defaultStartPositionUs == defaultStartPositionUs;
}
@Override
public String toString() {
return "MediaWindow[" + durationUs + ", " + defaultStartPositionUs + ", " + isSeekable + ", "
+ isDynamic + "]";
}
}
......@@ -392,24 +392,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
}
@Override
@Deprecated
public void seekInCurrentPeriod(long positionMs) {
player.seekInCurrentPeriod(positionMs);
}
@Override
@Deprecated
public void seekToDefaultPositionForPeriod(int periodIndex) {
player.seekToDefaultPositionForPeriod(periodIndex);
}
@Override
@Deprecated
public void seekInPeriod(int periodIndex, long positionMs) {
player.seekInPeriod(periodIndex, positionMs);
}
@Override
public void seekToDefaultPosition() {
player.seekToDefaultPosition();
}
......@@ -450,36 +432,6 @@ public final class SimpleExoPlayer implements ExoPlayer {
}
@Override
@Deprecated
public int getCurrentPeriodIndex() {
return player.getCurrentPeriodIndex();
}
@Override
@Deprecated
public long getCurrentPeriodDuration() {
return player.getCurrentPeriodDuration();
}
@Override
@Deprecated
public long getCurrentPositionInPeriod() {
return player.getCurrentPositionInPeriod();
}
@Override
@Deprecated
public long getBufferedPositionInPeriod() {
return player.getBufferedPositionInPeriod();
}
@Override
@Deprecated
public int getBufferedPercentageInPeriod() {
return player.getBufferedPercentageInPeriod();
}
@Override
public int getCurrentWindowIndex() {
return player.getCurrentWindowIndex();
}
......@@ -505,7 +457,7 @@ public final class SimpleExoPlayer implements ExoPlayer {
}
@Override
public MediaTimeline getCurrentTimeline() {
public Timeline getCurrentTimeline() {
return player.getCurrentTimeline();
}
......
/*
* 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;
/**
* A media timeline. Instances are immutable.
*/
public abstract class Timeline {
/**
* Returns the number of windows in the timeline.
*/
public abstract int getWindowCount();
/**
* Populates a {@link Window} with data for the window at the specified index. Does not populate
* {@link Window#id}.
*
* @param windowIndex The index of the window.
* @param window The {@link Window} to populate. Must not be null.
* @return The populated {@link Window}, for convenience.
*/
public final Window getWindow(int windowIndex, Window window) {
return getWindow(windowIndex, window, false);
}
/**
* Populates a {@link Window} with data for the window at the specified index.
*
* @param windowIndex The index of the window.
* @param window The {@link Window} to populate. Must not be null.
* @param setIds Whether {@link Window#id} should be populated. If false, the field will be set to
* null. The caller should pass false for efficiency reasons unless the field is required.
* @return The populated {@link Window}, for convenience.
*/
public abstract Window getWindow(int windowIndex, Window window, boolean setIds);
/**
* Returns the number of periods in the timeline.
*/
public abstract int getPeriodCount();
/**
* Populates a {@link Period} with data for the period at the specified index. Does not populate
* {@link Period#id} and {@link Period#uid}.
*
* @param periodIndex The index of the period.
* @param period The {@link Period} to populate. Must not be null.
* @return The populated {@link Period}, for convenience.
*/
public final Period getPeriod(int periodIndex, Period period) {
return getPeriod(periodIndex, period, false);
}
/**
* Populates a {@link Period} with data for the period at the specified index.
*
* @param periodIndex The index of the period.
* @param period The {@link Period} to populate. Must not be null.
* @param setIds Whether {@link Period#id} and {@link Period#uid} should be populated. If false,
* the fields will be set to null. The caller should pass false for efficiency reasons unless
* the fields are required.
* @return The populated {@link Period}, for convenience.
*/
public abstract Period getPeriod(int periodIndex, Period period, boolean setIds);
/**
* Returns the index of the period identified by its unique {@code id}, or {@link C#INDEX_UNSET}
* if the period is not in the timeline.
*
* @param uid A unique identifier for a period.
* @return The index of the period, or {@link C#INDEX_UNSET} if the period was not found.
*/
public abstract int getIndexOfPeriod(Object uid);
/**
* Holds information about a window of available media.
*/
public static final class Window {
/**
* An identifier for the window. Not necessarily unique.
*/
public Object id;
/**
* The start time of the presentation to which this window belongs in milliseconds since the
* epoch, or {@link C#TIME_UNSET} if unknown or not applicable. For informational purposes only.
*/
public long presentationStartTimeMs;
/**
* The windows start time in milliseconds since the epoch, or {@link C#TIME_UNSET} if unknown or
* not applicable. For informational purposes only.
*/
public long windowStartTimeMs;
/**
* Whether it's possible to seek within this window.
*/
public boolean isSeekable;
/**
* Whether this window may change when the timeline is updated.
*/
public boolean isDynamic;
/**
* The index of the first period that belongs to this window.
*/
public int firstPeriodIndex;
/**
* The index of the last period that belongs to this window.
*/
public int lastPeriodIndex;
private long defaultStartPositionUs;
private long durationUs;
private long positionInFirstPeriodUs;
/**
* Sets the data held by this window.
*/
public Window set(Object id, long presentationStartTimeMs, long windowStartTimeMs,
boolean isSeekable, boolean isDynamic, long defaultStartPositionUs, long durationUs,
int firstPeriodIndex, int lastPeriodIndex, long positionInFirstPeriodUs) {
this.id = id;
this.presentationStartTimeMs = presentationStartTimeMs;
this.windowStartTimeMs = windowStartTimeMs;
this.isSeekable = isSeekable;
this.isDynamic = isDynamic;
this.defaultStartPositionUs = defaultStartPositionUs;
this.durationUs = durationUs;
this.firstPeriodIndex = firstPeriodIndex;
this.lastPeriodIndex = lastPeriodIndex;
this.positionInFirstPeriodUs = positionInFirstPeriodUs;
return this;
}
/**
* Returns the default position relative to the start of the window at which to begin playback,
* in milliseconds.
*/
public long getDefaultStartPositionMs() {
return C.usToMs(defaultStartPositionUs);
}
/**
* Returns the default position relative to the start of the window at which to begin playback,
* in microseconds.
*/
public long getDefaultStartPositionUs() {
return defaultStartPositionUs;
}
/**
* Returns the duration of the window in milliseconds, or {@link C#TIME_UNSET} if unknown.
*/
public long getDurationMs() {
return C.usToMs(durationUs);
}
/**
* Returns the duration of this window in microseconds, or {@link C#TIME_UNSET} if unknown.
*/
public long getDurationUs() {
return durationUs;
}
/**
* Returns the position of the start of this window relative to the start of the first period
* belonging to it, in milliseconds.
*/
public long getPositionInFirstPeriodMs() {
return C.usToMs(positionInFirstPeriodUs);
}
/**
* Returns the position of the start of this window relative to the start of the first period
* belonging to it, in microseconds.
*/
public long getPositionInFirstPeriodUs() {
return positionInFirstPeriodUs;
}
}
/**
* Holds information about a media period.
*/
public static final class Period {
/**
* An identifier for the period. Not necessarily unique.
*/
public Object id;
/**
* A unique identifier for the period.
*/
public Object uid;
/**
* The index of the window to which this period belongs.
*/
public int windowIndex;
private long durationUs;
private long positionInWindowUs;
/**
* Sets the data held by this period.
*/
public Period set(Object id, Object uid, int windowIndex, long durationUs,
long positionInWindowUs) {
this.id = id;
this.uid = uid;
this.windowIndex = windowIndex;
this.durationUs = durationUs;
this.positionInWindowUs = positionInWindowUs;
return this;
}
/**
* Returns the duration of the period in milliseconds, or {@link C#TIME_UNSET} if unknown.
*/
public long getDurationMs() {
return C.usToMs(durationUs);
}
/**
* Returns the duration of this period in microseconds, or {@link C#TIME_UNSET} if unknown.
*/
public long getDurationUs() {
return durationUs;
}
/**
* Returns the position of the start of this period relative to the start of the window to which
* it belongs, in milliseconds. May be negative if the start of the period is not within the
* window.
*/
public long getPositionInWindowMs() {
return C.usToMs(positionInWindowUs);
}
/**
* Returns the position of the start of this period relative to the start of the window to which
* it belongs, in microseconds. May be negative if the start of the period is not within the
* window.
*/
public long getPositionInWindowUs() {
return positionInWindowUs;
}
}
}
......@@ -17,8 +17,7 @@ package com.google.android.exoplayer2.source;
import android.util.Pair;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.MediaWindow;
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.Util;
......@@ -32,18 +31,18 @@ import java.util.Map;
public final class ConcatenatingMediaSource implements MediaSource {
private final MediaSource[] mediaSources;
private final MediaTimeline[] timelines;
private final Timeline[] timelines;
private final Object[] manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private ConcatenatedMediaTimeline timeline;
private ConcatenatedTimeline timeline;
/**
* @param mediaSources The {@link MediaSource}s to concatenate.
*/
public ConcatenatingMediaSource(MediaSource... mediaSources) {
this.mediaSources = mediaSources;
timelines = new MediaTimeline[mediaSources.length];
timelines = new Timeline[mediaSources.length];
manifests = new Object[mediaSources.length];
sourceIndexByMediaPeriod = new HashMap<>();
}
......@@ -55,16 +54,16 @@ public final class ConcatenatingMediaSource implements MediaSource {
mediaSources[i].prepareSource(new Listener() {
@Override
public void onSourceInfoRefreshed(MediaTimeline sourceTimeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline sourceTimeline, Object manifest) {
timelines[index] = sourceTimeline;
manifests[index] = manifest;
for (MediaTimeline timeline : timelines) {
for (Timeline timeline : timelines) {
if (timeline == null) {
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new ConcatenatedMediaTimeline(timelines.clone());
timeline = new ConcatenatedTimeline(timelines.clone());
listener.onSourceInfoRefreshed(timeline, manifests.clone());
}
......@@ -105,21 +104,21 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
/**
* A {@link MediaTimeline} that is the concatenation of one or more {@link MediaTimeline}s.
* A {@link Timeline} that is the concatenation of one or more {@link Timeline}s.
*/
private static final class ConcatenatedMediaTimeline implements MediaTimeline {
private static final class ConcatenatedTimeline extends Timeline {
private final MediaTimeline[] timelines;
private final Timeline[] timelines;
private final int[] sourcePeriodOffsets;
private final int[] sourceWindowOffsets;
public ConcatenatedMediaTimeline(MediaTimeline[] timelines) {
public ConcatenatedTimeline(Timeline[] timelines) {
int[] sourcePeriodOffsets = new int[timelines.length];
int[] sourceWindowOffsets = new int[timelines.length];
int periodCount = 0;
int windowCount = 0;
for (int i = 0; i < timelines.length; i++) {
MediaTimeline timeline = timelines[i];
Timeline timeline = timelines[i];
periodCount += timeline.getPeriodCount();
sourcePeriodOffsets[i] = periodCount;
windowCount += timeline.getWindowCount();
......@@ -131,60 +130,49 @@ public final class ConcatenatingMediaSource implements MediaSource {
}
@Override
public long getAbsoluteStartTime() {
return timelines[0].getAbsoluteStartTime();
}
@Override
public int getPeriodCount() {
return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
@Override
public long getPeriodDurationMs(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
return timelines[sourceIndex].getPeriodDurationMs(periodIndex - firstPeriodIndexInSource);
public int getWindowCount() {
return sourceWindowOffsets[sourceWindowOffsets.length - 1];
}
@Override
public long getPeriodDurationUs(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
public Window getWindow(int windowIndex, Window window, boolean setIds) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
return timelines[sourceIndex].getPeriodDurationUs(periodIndex - firstPeriodIndexInSource);
timelines[sourceIndex].getWindow(windowIndex - firstWindowIndexInSource, window, setIds);
window.firstPeriodIndex += firstPeriodIndexInSource;
window.lastPeriodIndex += firstPeriodIndexInSource;
return window;
}
@Override
public Object getPeriodId(int periodIndex) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(periodIndex);
Object periodId = timelines[sourceIndex].getPeriodId(periodIndex - firstPeriodIndexInSource);
return Pair.create(sourceIndex, periodId);
}
@Override
public MediaWindow getPeriodWindow(int periodIndex) {
return getWindow(getPeriodWindowIndex(periodIndex));
public int getPeriodCount() {
return sourcePeriodOffsets[sourcePeriodOffsets.length - 1];
}
@Override
public int getPeriodWindowIndex(int periodIndex) {
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(periodIndex);
return (sourceIndex > 0 ? sourceWindowOffsets[sourceIndex - 1] : 0)
+ timelines[sourceIndex].getPeriodWindowIndex(periodIndex - firstPeriodIndexInSource);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines[sourceIndex].getPeriod(periodIndex - firstPeriodIndexInSource, period, setIds);
period.windowIndex += firstWindowIndexInSource;
if (setIds) {
period.uid = Pair.create(sourceIndex, period.uid);
}
return period;
}
@Override
public int getIndexOfPeriod(Object id) {
if (!(id instanceof Pair)) {
public int getIndexOfPeriod(Object uid) {
if (!(uid instanceof Pair)) {
return C.INDEX_UNSET;
}
Pair sourceIndexAndPeriodId = (Pair) id;
Pair<?, ?> sourceIndexAndPeriodId = (Pair<?, ?>) uid;
if (!(sourceIndexAndPeriodId.first instanceof Integer)) {
return C.INDEX_UNSET;
}
int sourceIndex = (int) sourceIndexAndPeriodId.first;
int sourceIndex = (Integer) sourceIndexAndPeriodId.first;
Object periodId = sourceIndexAndPeriodId.second;
if (sourceIndex < 0 || sourceIndex >= timelines.length) {
return C.INDEX_UNSET;
......@@ -194,44 +182,6 @@ public final class ConcatenatingMediaSource implements MediaSource {
: getFirstPeriodIndexInSource(sourceIndex) + periodIndexInSource;
}
@Override
public int getWindowCount() {
return sourceWindowOffsets[sourceWindowOffsets.length - 1];
}
@Override
public MediaWindow getWindow(int windowIndex) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
return timelines[sourceIndex].getWindow(windowIndex - firstWindowIndexInSource);
}
@Override
public int getWindowFirstPeriodIndex(int windowIndex) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
return firstPeriodIndexInSource + timelines[sourceIndex].getWindowFirstPeriodIndex(
windowIndex - firstWindowIndexInSource);
}
@Override
public int getWindowLastPeriodIndex(int windowIndex) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
return firstPeriodIndexInSource + timelines[sourceIndex].getWindowLastPeriodIndex(
windowIndex - firstWindowIndexInSource);
}
@Override
public long getWindowOffsetInFirstPeriodUs(int windowIndex) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
return timelines[sourceIndex].getWindowOffsetInFirstPeriodUs(
windowIndex - firstWindowIndexInSource);
}
private int getSourceIndexForPeriod(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
......
......@@ -299,7 +299,7 @@ import java.util.Arrays;
durationUs = largestQueuedTimestampUs == Long.MIN_VALUE ? 0
: largestQueuedTimestampUs + DEFAULT_LAST_SAMPLE_DURATION_US;
sourceListener.onSourceInfoRefreshed(
new SinglePeriodMediaTimeline(durationUs, seekMap.isSeekable()), null);
new SinglePeriodTimeline(durationUs, seekMap.isSeekable()), null);
}
}
......@@ -382,7 +382,7 @@ import java.util.Arrays;
tracks = new TrackGroupArray(trackArray);
prepared = true;
sourceListener.onSourceInfoRefreshed(
new SinglePeriodMediaTimeline(durationUs, seekMap.isSeekable()), null);
new SinglePeriodTimeline(durationUs, seekMap.isSeekable()), null);
callback.onPrepared(this);
}
......
......@@ -18,8 +18,8 @@ package com.google.android.exoplayer2.source;
import android.net.Uri;
import android.os.Handler;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaTimeline;
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.ExtractorsFactory;
......@@ -92,9 +92,11 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
private final int minLoadableRetryCount;
private final Handler eventHandler;
private final EventListener eventListener;
private final Timeline.Period period;
private MediaSource.Listener sourceListener;
private MediaTimeline timeline;
private Timeline timeline;
private boolean timelineHasDuration;
/**
* @param uri The {@link Uri} of the media stream.
......@@ -130,12 +132,13 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
this.minLoadableRetryCount = minLoadableRetryCount;
this.eventHandler = eventHandler;
this.eventListener = eventListener;
period = new Timeline.Period();
}
@Override
public void prepareSource(MediaSource.Listener listener) {
sourceListener = listener;
timeline = new SinglePeriodMediaTimeline(C.TIME_UNSET, false);
timeline = new SinglePeriodTimeline(C.TIME_UNSET, false);
listener.onSourceInfoRefreshed(timeline, null);
}
......@@ -166,13 +169,15 @@ public final class ExtractorMediaSource implements MediaSource, MediaSource.List
// MediaSource.Listener implementation.
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
if (this.timeline.getPeriodDurationUs(0) != C.TIME_UNSET
&& timeline.getPeriodDurationUs(0) == C.TIME_UNSET) {
public void onSourceInfoRefreshed(Timeline newTimeline, Object manifest) {
long newTimelineDurationUs = newTimeline.getPeriod(0, period).getDurationUs();
boolean newTimelineHasDuration = newTimelineDurationUs != C.TIME_UNSET;
if (timelineHasDuration && !newTimelineHasDuration) {
// Suppress source info changes that would make the duration unknown when it is already known.
return;
}
this.timeline = timeline;
timeline = newTimeline;
timelineHasDuration = newTimelineHasDuration;
sourceListener.onSourceInfoRefreshed(timeline, null);
}
......
......@@ -15,7 +15,7 @@
*/
package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.MediaTimeline;
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;
......@@ -36,7 +36,7 @@ public interface MediaSource {
* @param timeline The source's timeline.
* @param manifest The loaded manifest.
*/
void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest);
void onSourceInfoRefreshed(Timeline timeline, Object manifest);
}
......
......@@ -15,7 +15,7 @@
*/
package com.google.android.exoplayer2.source;
import com.google.android.exoplayer2.MediaTimeline;
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;
......@@ -31,6 +31,7 @@ public final class MergingMediaSource implements MediaSource {
private static final int PERIOD_COUNT_UNSET = -1;
private final MediaSource[] mediaSources;
private final Timeline.Window window;
private int periodCount;
......@@ -39,30 +40,26 @@ public final class MergingMediaSource implements MediaSource {
*/
public MergingMediaSource(MediaSource... mediaSources) {
this.mediaSources = mediaSources;
window = new Timeline.Window();
periodCount = PERIOD_COUNT_UNSET;
}
@Override
public void prepareSource(final Listener listener) {
mediaSources[0].prepareSource(new Listener() {
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
checkConsistentTimeline(timeline);
// All source timelines must match.
listener.onSourceInfoRefreshed(timeline, manifest);
}
});
for (int i = 1; i < mediaSources.length; i++) {
mediaSources[i].prepareSource(new Listener() {
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
checkConsistentTimeline(timeline);
}
});
}
}
......@@ -102,10 +99,10 @@ public final class MergingMediaSource implements MediaSource {
}
}
private void checkConsistentTimeline(MediaTimeline timeline) {
private void checkConsistentTimeline(Timeline timeline) {
int windowCount = timeline.getWindowCount();
for (int i = 0; i < windowCount; i++) {
Assertions.checkArgument(!timeline.getWindow(i).isDynamic);
Assertions.checkArgument(!timeline.getWindow(i, window, false).isDynamic);
}
int periodCount = timeline.getPeriodCount();
if (this.periodCount == PERIOD_COUNT_UNSET) {
......
/*
* 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.source;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.MediaWindow;
import com.google.android.exoplayer2.util.Assertions;
/**
* A {@link MediaTimeline} consisting of a single period and static window.
*/
public final class SinglePeriodMediaTimeline implements MediaTimeline {
private static final Object ID = new Object();
private final long offsetInFirstPeriodUs;
private final MediaWindow window;
/**
* Creates a timeline with one period of known duration and a window extending from zero to its
* duration.
*
* @param durationUs The duration of the period, in microseconds.
* @param isSeekable Whether seeking is supported within the period.
*/
public SinglePeriodMediaTimeline(long durationUs, boolean isSeekable) {
this(0, MediaWindow.createWindowFromZero(durationUs, isSeekable, false /* isDynamic */));
}
/**
* Creates a timeline with one period of known duration and a window extending from zero to its
* duration.
*
* @param offsetInFirstPeriodUs The offset of the start of the window in the period.
* @param window The available window within the period.
*/
public SinglePeriodMediaTimeline(long offsetInFirstPeriodUs, MediaWindow window) {
this.offsetInFirstPeriodUs = offsetInFirstPeriodUs;
this.window = window;
}
@Override
public long getAbsoluteStartTime() {
return 0;
}
@Override
public int getPeriodCount() {
return 1;
}
@Override
public long getPeriodDurationMs(int periodIndex) {
return C.usToMs(getPeriodDurationUs(periodIndex));
}
@Override
public long getPeriodDurationUs(int periodIndex) {
Assertions.checkIndex(periodIndex, 0, 1);
return window.durationUs == C.TIME_UNSET ? C.TIME_UNSET
: (offsetInFirstPeriodUs + window.durationUs);
}
@Override
public Object getPeriodId(int periodIndex) {
Assertions.checkIndex(periodIndex, 0, 1);
return ID;
}
@Override
public MediaWindow getPeriodWindow(int periodIndex) {
Assertions.checkIndex(periodIndex, 0, 1);
return window;
}
@Override
public int getPeriodWindowIndex(int periodIndex) {
Assertions.checkIndex(periodIndex, 0, 1);
return 0;
}
@Override
public int getIndexOfPeriod(Object id) {
return ID.equals(id) ? 0 : C.INDEX_UNSET;
}
@Override
public int getWindowCount() {
return 1;
}
@Override
public MediaWindow getWindow(int windowIndex) {
Assertions.checkIndex(windowIndex, 0, 1);
return window;
}
@Override
public int getWindowFirstPeriodIndex(int windowIndex) {
Assertions.checkIndex(windowIndex, 0, 1);
return 0;
}
@Override
public int getWindowLastPeriodIndex(int windowIndex) {
Assertions.checkIndex(windowIndex, 0, 1);
return 0;
}
@Override
public long getWindowOffsetInFirstPeriodUs(int windowIndex) {
Assertions.checkIndex(windowIndex, 0, 1);
return offsetInFirstPeriodUs;
}
}
/*
* 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.source;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.util.Assertions;
/**
* A {@link Timeline} consisting of a single period and static window.
*/
public final class SinglePeriodTimeline extends Timeline {
private static final Object ID = new Object();
private final long periodDurationUs;
private final long windowDurationUs;
private final long windowPositionInPeriodUs;
private final long windowDefaultStartPositionUs;
private final boolean isSeekable;
private final boolean isDynamic;
/**
* Creates a timeline of one period of known duration, and a static window starting at zero and
* extending to that duration.
*
* @param durationUs The duration of the period, in microseconds.
* @param isSeekable Whether seeking is supported within the period.
*/
public SinglePeriodTimeline(long durationUs, boolean isSeekable) {
this(durationUs, durationUs, 0, 0, isSeekable, false);
}
/**
* Creates a timeline with one period of known duration, and a window of known duration starting
* at a specified position in the period.
*
* @param periodDurationUs The duration of the period in microseconds.
* @param windowDurationUs The duration of the window in microseconds.
* @param windowPositionInPeriodUs The position of the start of the window in the period, in
* microseconds.
* @param windowDefaultStartPositionUs The default position relative to the start of the window at
* which to begin playback, in microseconds.
* @param isSeekable Whether seeking is supported within the window.
* @param isDynamic Whether the window may change when the timeline is updated.
*/
public SinglePeriodTimeline(long periodDurationUs, long windowDurationUs,
long windowPositionInPeriodUs, long windowDefaultStartPositionUs, boolean isSeekable,
boolean isDynamic) {
this.periodDurationUs = periodDurationUs;
this.windowDurationUs = windowDurationUs;
this.windowPositionInPeriodUs = windowPositionInPeriodUs;
this.windowDefaultStartPositionUs = windowDefaultStartPositionUs;
this.isSeekable = isSeekable;
this.isDynamic = isDynamic;
}
@Override
public int getWindowCount() {
return 1;
}
@Override
public Window getWindow(int windowIndex, Window window, boolean setIds) {
Assertions.checkIndex(windowIndex, 0, 1);
Object id = setIds ? ID : null;
return window.set(id, C.TIME_UNSET, C.TIME_UNSET, isSeekable, isDynamic,
windowDefaultStartPositionUs, windowDurationUs, 0, 0, windowPositionInPeriodUs);
}
@Override
public int getPeriodCount() {
return 1;
}
@Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
Assertions.checkIndex(periodIndex, 0, 1);
Object id = setIds ? ID : null;
return period.set(id, id, 0, periodDurationUs, -windowPositionInPeriodUs);
}
@Override
public int getIndexOfPeriod(Object uid) {
return ID.equals(uid) ? 0 : C.INDEX_UNSET;
}
}
......@@ -18,7 +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.MediaTimeline;
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;
......@@ -57,7 +57,7 @@ public final class SingleSampleMediaSource implements MediaSource {
private final Handler eventHandler;
private final EventListener eventListener;
private final int eventSourceId;
private final MediaTimeline timeline;
private final Timeline timeline;
public SingleSampleMediaSource(Uri uri, DataSource.Factory dataSourceFactory, Format format,
long durationUs) {
......@@ -79,7 +79,7 @@ public final class SingleSampleMediaSource implements MediaSource {
this.eventHandler = eventHandler;
this.eventListener = eventListener;
this.eventSourceId = eventSourceId;
timeline = new SinglePeriodMediaTimeline(durationUs, true);
timeline = new SinglePeriodTimeline(durationUs, true);
}
// MediaSource implementation.
......
......@@ -19,14 +19,14 @@ import android.net.Uri;
import android.text.TextUtils;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaTimeline;
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.SinglePeriodMediaTimeline;
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
......@@ -282,7 +282,7 @@ import java.util.List;
callback.onPrepared(this);
// TODO[playlists]: Calculate the window.
MediaTimeline timeline = new SinglePeriodMediaTimeline(durationUs, !isLive);
Timeline timeline = new SinglePeriodTimeline(durationUs, !isLive);
sourceListener.onSourceInfoRefreshed(timeline, playlist);
}
......
......@@ -23,7 +23,7 @@ import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.Eve
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.SinglePeriodMediaTimeline;
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.util.Assertions;
......@@ -64,7 +64,7 @@ public final class HlsMediaSource implements MediaSource {
public void prepareSource(MediaSource.Listener listener) {
sourceListener = listener;
// TODO: Defer until the playlist has been loaded.
listener.onSourceInfoRefreshed(new SinglePeriodMediaTimeline(C.TIME_UNSET, false), null);
listener.onSourceInfoRefreshed(new SinglePeriodTimeline(C.TIME_UNSET, false), null);
}
@Override
......
......@@ -19,15 +19,14 @@ import android.net.Uri;
import android.os.Handler;
import android.os.SystemClock;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.MediaWindow;
import com.google.android.exoplayer2.ParserException;
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.SinglePeriodMediaTimeline;
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
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;
......@@ -157,7 +156,7 @@ public final class SsMediaSource implements MediaSource,
for (int i = 0; i < mediaPeriods.size(); i++) {
mediaPeriods.get(i).updateManifest(manifest);
}
MediaTimeline timeline;
Timeline timeline;
if (manifest.isLive) {
long startTimeUs = Long.MAX_VALUE;
long endTimeUs = Long.MIN_VALUE;
......@@ -170,21 +169,20 @@ public final class SsMediaSource implements MediaSource,
}
}
if (startTimeUs == Long.MAX_VALUE) {
timeline = new SinglePeriodMediaTimeline(C.TIME_UNSET, false);
timeline = new SinglePeriodTimeline(C.TIME_UNSET, false);
} else {
if (manifest.dvrWindowLengthUs != C.TIME_UNSET
&& manifest.dvrWindowLengthUs > 0) {
startTimeUs = Math.max(startTimeUs, endTimeUs - manifest.dvrWindowLengthUs);
}
long durationUs = endTimeUs - startTimeUs;
long defaultInitialStartPositionUs = Math.max(0, durationUs - (liveEdgeOffsetMs * 1000));
MediaWindow window = new MediaWindow(durationUs, true /* isSeekable */,
true /* isDynamic */, defaultInitialStartPositionUs);
timeline = new SinglePeriodMediaTimeline(startTimeUs, window);
long defaultStartPositionUs = Math.max(0, durationUs - (liveEdgeOffsetMs * 1000));
timeline = new SinglePeriodTimeline(C.TIME_UNSET, durationUs, startTimeUs,
defaultStartPositionUs, true /* isSeekable */, true /* isDynamic */);
}
} else {
boolean isSeekable = manifest.durationUs != C.TIME_UNSET;
timeline = new SinglePeriodMediaTimeline(manifest.durationUs, isSeekable);
timeline = new SinglePeriodTimeline(manifest.durationUs, isSeekable);
}
sourceListener.onSourceInfoRefreshed(timeline, manifest);
scheduleManifestRefresh();
......
......@@ -19,8 +19,8 @@ import android.widget.TextView;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.decoder.DecoderCounters;
/**
......@@ -89,7 +89,7 @@ public final class DebugTextViewHelper implements Runnable, ExoPlayer.EventListe
}
@Override
public void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
// Do nothing.
}
......
......@@ -17,15 +17,14 @@ package com.google.android.exoplayer2.ui;
import android.view.View;
import android.view.View.OnClickListener;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.Timeline;
/**
* An {@link OnClickListener} that can be passed to
* {@link android.widget.MediaController#setPrevNextListeners(OnClickListener, OnClickListener)} to
* make the controller's previous and next buttons seek to the previous and next windows in the
* {@link MediaTimeline}.
* {@link Timeline}.
*/
public class MediaControllerPrevNextClickListener implements OnClickListener {
......@@ -50,15 +49,15 @@ public class MediaControllerPrevNextClickListener implements OnClickListener {
@Override
public void onClick(View v) {
int currentWindowIndex = player.getCurrentWindowIndex();
if (currentWindowIndex == C.INDEX_UNSET) {
Timeline timeline = player.getCurrentTimeline();
if (timeline == null) {
return;
}
MediaTimeline timeline = player.getCurrentTimeline();
int currentWindowIndex = player.getCurrentWindowIndex();
if (isNext) {
if (currentWindowIndex < timeline.getWindowCount() - 1) {
player.seekToDefaultPosition(currentWindowIndex + 1);
} else if (timeline.getWindow(currentWindowIndex).isDynamic) {
} else if (timeline.getWindow(currentWindowIndex, new Timeline.Window(), false).isDynamic) {
// Seek to the live edge.
player.seekToDefaultPosition();
}
......@@ -67,7 +66,7 @@ public class MediaControllerPrevNextClickListener implements OnClickListener {
&& player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS) {
player.seekToDefaultPosition(currentWindowIndex - 1);
} else {
player.seekTo(currentWindowIndex, 0);
player.seekTo(0);
}
}
}
......
......@@ -24,8 +24,8 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.MediaTimeline;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.audio.AudioTrack;
import com.google.android.exoplayer2.decoder.DecoderCounters;
......@@ -215,7 +215,7 @@ public abstract class ExoHostedTest implements HostedTest, ExoPlayer.EventListen
}
@Override
public final void onSourceInfoRefreshed(MediaTimeline timeline, Object manifest) {
public final void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
// Do nothing.
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment