Use AdaptiveMediaSourceEventListener for ExtractorMediaSource
This is a step towards harmonizing the MediaSource Builders and (potentially) providing MediaSource factories. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=177783157
This commit is contained in:
parent
03b0d9d46c
commit
fbccdf594a
@ -27,6 +27,8 @@
|
||||
* Add optional parameter to `Player.stop` to reset the player when stopping.
|
||||
* Fix handling of playback parameters changes while paused when followed by a
|
||||
seek.
|
||||
* Use the same listener `MediaSourceEventListener` for all MediaSource
|
||||
implementations.
|
||||
|
||||
### 2.6.0 ###
|
||||
|
||||
|
@ -39,7 +39,6 @@ import com.google.android.exoplayer2.metadata.id3.PrivFrame;
|
||||
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
|
||||
import com.google.android.exoplayer2.metadata.id3.UrlLinkFrame;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||
@ -53,13 +52,15 @@ import java.io.IOException;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Logs player events using {@link Log}.
|
||||
*/
|
||||
/* package */ final class EventLogger implements Player.EventListener, MetadataOutput,
|
||||
AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener,
|
||||
ExtractorMediaSource.EventListener, AdsMediaSource.AdsListener,
|
||||
DefaultDrmSessionManager.EventListener {
|
||||
/** Logs player events using {@link Log}. */
|
||||
/* package */ final class EventLogger
|
||||
implements Player.EventListener,
|
||||
MetadataOutput,
|
||||
AudioRendererEventListener,
|
||||
VideoRendererEventListener,
|
||||
AdaptiveMediaSourceEventListener,
|
||||
AdsMediaSource.EventListener,
|
||||
DefaultDrmSessionManager.EventListener {
|
||||
|
||||
private static final String TAG = "EventLogger";
|
||||
private static final int MAX_TIMELINE_ITEM_LINES = 3;
|
||||
@ -324,13 +325,6 @@ import java.util.Locale;
|
||||
Log.d(TAG, "drmKeysLoaded [" + getSessionTimeString() + "]");
|
||||
}
|
||||
|
||||
// ExtractorMediaSource.EventListener
|
||||
|
||||
@Override
|
||||
public void onLoadError(IOException error) {
|
||||
printInternalError("loadError", error);
|
||||
}
|
||||
|
||||
// AdaptiveMediaSourceEventListener
|
||||
|
||||
@Override
|
||||
|
@ -52,8 +52,8 @@ public final class ImaAdsMediaSource implements MediaSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new source that inserts ads linearly with the content specified by
|
||||
* {@code contentMediaSource}.
|
||||
* Constructs a new source that inserts ads linearly with the content specified by {@code
|
||||
* contentMediaSource}.
|
||||
*
|
||||
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
||||
* @param dataSourceFactory Factory for data sources used to load ad media.
|
||||
@ -62,9 +62,13 @@ public final class ImaAdsMediaSource implements MediaSource {
|
||||
* @param eventHandler A handler for events. May be null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
*/
|
||||
public ImaAdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
|
||||
ImaAdsLoader imaAdsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
|
||||
@Nullable AdsMediaSource.AdsListener eventListener) {
|
||||
public ImaAdsMediaSource(
|
||||
MediaSource contentMediaSource,
|
||||
DataSource.Factory dataSourceFactory,
|
||||
ImaAdsLoader imaAdsLoader,
|
||||
ViewGroup adUiViewGroup,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable AdsMediaSource.EventListener eventListener) {
|
||||
adsMediaSource = new AdsMediaSource(contentMediaSource, dataSourceFactory, imaAdsLoader,
|
||||
adUiViewGroup, eventHandler, eventListener);
|
||||
}
|
||||
|
@ -16,306 +16,39 @@
|
||||
package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Interface for callbacks to be notified of adaptive {@link MediaSource} events.
|
||||
* Interface for callbacks to be notified of {@link MediaSource} events.
|
||||
*
|
||||
* @deprecated Use {@link MediaSourceEventListener}
|
||||
*/
|
||||
public interface AdaptiveMediaSourceEventListener {
|
||||
@Deprecated
|
||||
public interface AdaptiveMediaSourceEventListener extends MediaSourceEventListener {
|
||||
|
||||
/**
|
||||
* Called when a load begins.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds
|
||||
* to media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does
|
||||
* not belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the load began.
|
||||
*/
|
||||
void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
|
||||
int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
|
||||
long mediaEndTimeMs, long elapsedRealtimeMs);
|
||||
|
||||
/**
|
||||
* Called when a load ends.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds
|
||||
* to media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does
|
||||
* not belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the load ended.
|
||||
* @param loadDurationMs The duration of the load.
|
||||
* @param bytesLoaded The number of bytes that were loaded.
|
||||
*/
|
||||
void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
|
||||
int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
|
||||
long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded);
|
||||
|
||||
/**
|
||||
* Called when a load is canceled.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds
|
||||
* to media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does
|
||||
* not belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the load was
|
||||
* canceled.
|
||||
* @param loadDurationMs The duration of the load up to the point at which it was canceled.
|
||||
* @param bytesLoaded The number of bytes that were loaded prior to cancelation.
|
||||
*/
|
||||
void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
|
||||
int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
|
||||
long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded);
|
||||
|
||||
/**
|
||||
* Called when a load error occurs.
|
||||
* <p>
|
||||
* The error may or may not have resulted in the load being canceled, as indicated by the
|
||||
* {@code wasCanceled} parameter. If the load was canceled, {@link #onLoadCanceled} will
|
||||
* <em>not</em> be called in addition to this method.
|
||||
* <p>
|
||||
* This method being called does not indicate that playback has failed, or that it will fail. The
|
||||
* player may be able to recover from the error and continue. Hence applications should
|
||||
* <em>not</em> implement this method to display a user visible error or initiate an application
|
||||
* level retry ({@link Player.EventListener#onPlayerError} is the appropriate place to implement
|
||||
* such behavior). This method is called to provide the application with an opportunity to log the
|
||||
* error if it wishes to do so.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds
|
||||
* to media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does
|
||||
* not belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the error
|
||||
* occurred.
|
||||
* @param loadDurationMs The duration of the load up to the point at which the error occurred.
|
||||
* @param bytesLoaded The number of bytes that were loaded prior to the error.
|
||||
* @param error The load error.
|
||||
* @param wasCanceled Whether the load was canceled as a result of the error.
|
||||
*/
|
||||
void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
|
||||
int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
|
||||
long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded,
|
||||
IOException error, boolean wasCanceled);
|
||||
|
||||
/**
|
||||
* Called when data is removed from the back of a media buffer, typically so that it can be
|
||||
* re-buffered in a different format.
|
||||
*
|
||||
* @param trackType The type of the media. One of the {@link C} {@code TRACK_TYPE_*} constants.
|
||||
* @param mediaStartTimeMs The start time of the media being discarded.
|
||||
* @param mediaEndTimeMs The end time of the media being discarded.
|
||||
*/
|
||||
void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs);
|
||||
|
||||
/**
|
||||
* Called when a downstream format change occurs (i.e. when the format of the media being read
|
||||
* from one or more {@link SampleStream}s provided by the source changes).
|
||||
*
|
||||
* @param trackType The type of the media. One of the {@link C} {@code TRACK_TYPE_*} constants.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does
|
||||
* not belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaTimeMs The media time at which the change occurred.
|
||||
*/
|
||||
void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason,
|
||||
Object trackSelectionData, long mediaTimeMs);
|
||||
|
||||
/**
|
||||
* Dispatches events to a {@link AdaptiveMediaSourceEventListener}.
|
||||
*/
|
||||
final class EventDispatcher {
|
||||
/** Dispatches events to a {@link MediaSourceEventListener}. */
|
||||
final class EventDispatcher extends MediaSourceEventListener.EventDispatcher {
|
||||
|
||||
private final Handler handler;
|
||||
private final AdaptiveMediaSourceEventListener listener;
|
||||
private final long mediaTimeOffsetMs;
|
||||
private final MediaSourceEventListener listener;
|
||||
|
||||
public EventDispatcher(Handler handler, AdaptiveMediaSourceEventListener listener) {
|
||||
public EventDispatcher(@Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
|
||||
this(handler, listener, 0);
|
||||
}
|
||||
|
||||
public EventDispatcher(Handler handler, AdaptiveMediaSourceEventListener listener,
|
||||
public EventDispatcher(
|
||||
@Nullable Handler handler,
|
||||
@Nullable MediaSourceEventListener listener,
|
||||
long mediaTimeOffsetMs) {
|
||||
this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
|
||||
super(handler, listener, mediaTimeOffsetMs);
|
||||
this.handler = handler;
|
||||
this.listener = listener;
|
||||
this.mediaTimeOffsetMs = mediaTimeOffsetMs;
|
||||
}
|
||||
|
||||
public EventDispatcher copyWithMediaTimeOffsetMs(long mediaTimeOffsetMs) {
|
||||
return new EventDispatcher(handler, listener, mediaTimeOffsetMs);
|
||||
}
|
||||
|
||||
public void loadStarted(DataSpec dataSpec, int dataType, long elapsedRealtimeMs) {
|
||||
loadStarted(dataSpec, dataType, C.TRACK_TYPE_UNKNOWN, null, C.SELECTION_REASON_UNKNOWN,
|
||||
null, C.TIME_UNSET, C.TIME_UNSET, elapsedRealtimeMs);
|
||||
}
|
||||
|
||||
public void loadStarted(final DataSpec dataSpec, final int dataType, final int trackType,
|
||||
final Format trackFormat, final int trackSelectionReason, final Object trackSelectionData,
|
||||
final long mediaStartTimeUs, final long mediaEndTimeUs, final long elapsedRealtimeMs) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadStarted(dataSpec, dataType, trackType, trackFormat, trackSelectionReason,
|
||||
trackSelectionData, adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs), elapsedRealtimeMs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCompleted(DataSpec dataSpec, int dataType, long elapsedRealtimeMs,
|
||||
long loadDurationMs, long bytesLoaded) {
|
||||
loadCompleted(dataSpec, dataType, C.TRACK_TYPE_UNKNOWN, null, C.SELECTION_REASON_UNKNOWN,
|
||||
null, C.TIME_UNSET, C.TIME_UNSET, elapsedRealtimeMs, loadDurationMs, bytesLoaded);
|
||||
}
|
||||
|
||||
public void loadCompleted(final DataSpec dataSpec, final int dataType, final int trackType,
|
||||
final Format trackFormat, final int trackSelectionReason, final Object trackSelectionData,
|
||||
final long mediaStartTimeUs, final long mediaEndTimeUs, final long elapsedRealtimeMs,
|
||||
final long loadDurationMs, final long bytesLoaded) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadCompleted(dataSpec, dataType, trackType, trackFormat,
|
||||
trackSelectionReason, trackSelectionData, adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs), elapsedRealtimeMs, loadDurationMs, bytesLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCanceled(DataSpec dataSpec, int dataType, long elapsedRealtimeMs,
|
||||
long loadDurationMs, long bytesLoaded) {
|
||||
loadCanceled(dataSpec, dataType, C.TRACK_TYPE_UNKNOWN, null, C.SELECTION_REASON_UNKNOWN,
|
||||
null, C.TIME_UNSET, C.TIME_UNSET, elapsedRealtimeMs, loadDurationMs, bytesLoaded);
|
||||
}
|
||||
|
||||
public void loadCanceled(final DataSpec dataSpec, final int dataType, final int trackType,
|
||||
final Format trackFormat, final int trackSelectionReason, final Object trackSelectionData,
|
||||
final long mediaStartTimeUs, final long mediaEndTimeUs, final long elapsedRealtimeMs,
|
||||
final long loadDurationMs, final long bytesLoaded) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadCanceled(dataSpec, dataType, trackType, trackFormat,
|
||||
trackSelectionReason, trackSelectionData, adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs), elapsedRealtimeMs, loadDurationMs, bytesLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void loadError(DataSpec dataSpec, int dataType, long elapsedRealtimeMs,
|
||||
long loadDurationMs, long bytesLoaded, IOException error, boolean wasCanceled) {
|
||||
loadError(dataSpec, dataType, C.TRACK_TYPE_UNKNOWN, null, C.SELECTION_REASON_UNKNOWN,
|
||||
null, C.TIME_UNSET, C.TIME_UNSET, elapsedRealtimeMs, loadDurationMs, bytesLoaded,
|
||||
error, wasCanceled);
|
||||
}
|
||||
|
||||
public void loadError(final DataSpec dataSpec, final int dataType, final int trackType,
|
||||
final Format trackFormat, final int trackSelectionReason, final Object trackSelectionData,
|
||||
final long mediaStartTimeUs, final long mediaEndTimeUs, final long elapsedRealtimeMs,
|
||||
final long loadDurationMs, final long bytesLoaded, final IOException error,
|
||||
final boolean wasCanceled) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadError(dataSpec, dataType, trackType, trackFormat, trackSelectionReason,
|
||||
trackSelectionData, adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs), elapsedRealtimeMs, loadDurationMs, bytesLoaded,
|
||||
error, wasCanceled);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void upstreamDiscarded(final int trackType, final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onUpstreamDiscarded(trackType, adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void downstreamFormatChanged(final int trackType, final Format trackFormat,
|
||||
final int trackSelectionReason, final Object trackSelectionData,
|
||||
final long mediaTimeUs) {
|
||||
if (listener != null) {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDownstreamFormatChanged(trackType, trackFormat, trackSelectionReason,
|
||||
trackSelectionData, adjustMediaTime(mediaTimeUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private long adjustMediaTime(long mediaTimeUs) {
|
||||
long mediaTimeMs = C.usToMs(mediaTimeUs);
|
||||
return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs;
|
||||
public AdaptiveMediaSourceEventListener.EventDispatcher copyWithMediaTimeOffsetMs(
|
||||
long mediaTimeOffsetMs) {
|
||||
return new AdaptiveMediaSourceEventListener.EventDispatcher(
|
||||
handler, listener, mediaTimeOffsetMs);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.FormatHolder;
|
||||
@ -28,6 +29,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||
import com.google.android.exoplayer2.extractor.PositionHolder;
|
||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener;
|
||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
@ -74,11 +76,10 @@ import java.util.Arrays;
|
||||
private final Uri uri;
|
||||
private final DataSource dataSource;
|
||||
private final int minLoadableRetryCount;
|
||||
private final Handler eventHandler;
|
||||
private final ExtractorMediaSource.EventListener eventListener;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final Listener listener;
|
||||
private final Allocator allocator;
|
||||
private final String customCacheKey;
|
||||
@Nullable private final String customCacheKey;
|
||||
private final long continueLoadingCheckIntervalBytes;
|
||||
private final Loader loader;
|
||||
private final ExtractorHolder extractorHolder;
|
||||
@ -117,8 +118,7 @@ import java.util.Arrays;
|
||||
* @param dataSource The data source to read the media.
|
||||
* @param extractors The extractors to use to read the data source.
|
||||
* @param minLoadableRetryCount The minimum number of times to retry if a loading error occurs.
|
||||
* @param eventHandler A handler for events. May be null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param eventDispatcher A dispatcher to notify of events.
|
||||
* @param listener A listener to notify when information about the period changes.
|
||||
* @param allocator An {@link Allocator} from which to obtain media buffer allocations.
|
||||
* @param customCacheKey A custom key that uniquely identifies the original stream. Used for cache
|
||||
@ -126,15 +126,20 @@ import java.util.Arrays;
|
||||
* @param continueLoadingCheckIntervalBytes The number of bytes that should be loaded between each
|
||||
* invocation of {@link Callback#onContinueLoadingRequested(SequenceableLoader)}.
|
||||
*/
|
||||
public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors,
|
||||
int minLoadableRetryCount, Handler eventHandler,
|
||||
ExtractorMediaSource.EventListener eventListener, Listener listener,
|
||||
Allocator allocator, String customCacheKey, int continueLoadingCheckIntervalBytes) {
|
||||
public ExtractorMediaPeriod(
|
||||
Uri uri,
|
||||
DataSource dataSource,
|
||||
Extractor[] extractors,
|
||||
int minLoadableRetryCount,
|
||||
EventDispatcher eventDispatcher,
|
||||
Listener listener,
|
||||
Allocator allocator,
|
||||
@Nullable String customCacheKey,
|
||||
int continueLoadingCheckIntervalBytes) {
|
||||
this.uri = uri;
|
||||
this.dataSource = dataSource;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.eventDispatcher = eventDispatcher;
|
||||
this.listener = listener;
|
||||
this.allocator = allocator;
|
||||
this.customCacheKey = customCacheKey;
|
||||
@ -430,8 +435,22 @@ import java.util.Arrays;
|
||||
public int onLoadError(ExtractingLoadable loadable, long elapsedRealtimeMs,
|
||||
long loadDurationMs, IOException error) {
|
||||
copyLengthFromLoader(loadable);
|
||||
notifyLoadError(error);
|
||||
if (isLoadableExceptionFatal(error)) {
|
||||
boolean isErrorFatal = isLoadableExceptionFatal(error);
|
||||
eventDispatcher.loadError(
|
||||
loadable.dataSpec,
|
||||
C.DATA_TYPE_MEDIA,
|
||||
C.TRACK_TYPE_UNKNOWN,
|
||||
/* trackFormat= */ null,
|
||||
C.SELECTION_REASON_UNKNOWN,
|
||||
/* trackSelectionData= */ null,
|
||||
/* mediaStartTimeUs= */ 0,
|
||||
durationUs,
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
loadable.bytesLoaded,
|
||||
error,
|
||||
/* wasCanceled= */ isErrorFatal);
|
||||
if (isErrorFatal) {
|
||||
return Loader.DONT_RETRY_FATAL;
|
||||
}
|
||||
int extractedSamplesCount = getExtractedSamplesCount();
|
||||
@ -606,17 +625,6 @@ import java.util.Arrays;
|
||||
return e instanceof UnrecognizedInputFormatException;
|
||||
}
|
||||
|
||||
private void notifyLoadError(final IOException error) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onLoadError(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private final class SampleStreamImpl implements SampleStream {
|
||||
|
||||
private final int track;
|
||||
@ -663,7 +671,9 @@ import java.util.Arrays;
|
||||
|
||||
private boolean pendingExtractorSeek;
|
||||
private long seekTimeUs;
|
||||
private DataSpec dataSpec;
|
||||
private long length;
|
||||
private long bytesLoaded;
|
||||
|
||||
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
|
||||
ConditionVariable loadCondition) {
|
||||
@ -699,7 +709,8 @@ import java.util.Arrays;
|
||||
ExtractorInput input = null;
|
||||
try {
|
||||
long position = positionHolder.position;
|
||||
length = dataSource.open(new DataSpec(uri, position, C.LENGTH_UNSET, customCacheKey));
|
||||
dataSpec = new DataSpec(uri, position, C.LENGTH_UNSET, customCacheKey);
|
||||
length = dataSource.open(dataSpec);
|
||||
if (length != C.LENGTH_UNSET) {
|
||||
length += position;
|
||||
}
|
||||
@ -723,6 +734,7 @@ import java.util.Arrays;
|
||||
result = Extractor.RESULT_CONTINUE;
|
||||
} else if (input != null) {
|
||||
positionHolder.position = input.getPosition();
|
||||
bytesLoaded = positionHolder.position - dataSpec.absoluteStreamPosition;
|
||||
}
|
||||
Util.closeQuietly(dataSource);
|
||||
}
|
||||
|
@ -17,14 +17,18 @@ package com.google.android.exoplayer2.source;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||
import com.google.android.exoplayer2.extractor.Extractor;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
|
||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
|
||||
@ -40,10 +44,12 @@ import java.io.IOException;
|
||||
* Note that the built-in extractors for AAC, MPEG PS/TS and FLV streams do not support seeking.
|
||||
*/
|
||||
public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPeriod.Listener {
|
||||
|
||||
/**
|
||||
* Listener of {@link ExtractorMediaSource} events.
|
||||
*
|
||||
* @deprecated Use {@link MediaSourceEventListener}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface EventListener {
|
||||
|
||||
/**
|
||||
@ -89,8 +95,7 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final ExtractorsFactory extractorsFactory;
|
||||
private final int minLoadableRetryCount;
|
||||
private final Handler eventHandler;
|
||||
private final EventListener eventListener;
|
||||
private final EventDispatcher eventDispatcher;
|
||||
private final String customCacheKey;
|
||||
private final int continueLoadingCheckIntervalBytes;
|
||||
|
||||
@ -108,9 +113,9 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
||||
|
||||
private ExtractorsFactory extractorsFactory;
|
||||
private int minLoadableRetryCount;
|
||||
private Handler eventHandler;
|
||||
private EventListener eventListener;
|
||||
private String customCacheKey;
|
||||
@Nullable private Handler eventHandler;
|
||||
@Nullable private MediaSourceEventListener eventListener;
|
||||
@Nullable private String customCacheKey;
|
||||
private int continueLoadingCheckIntervalBytes;
|
||||
private boolean isBuildCalled;
|
||||
|
||||
@ -187,8 +192,24 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
||||
* @param eventHandler A handler for events.
|
||||
* @param eventListener A listener of events.
|
||||
* @return This builder.
|
||||
* @deprecated Use {@link #setEventListener(Handler, MediaSourceEventListener)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder setEventListener(Handler eventHandler, EventListener eventListener) {
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener == null ? null : new EventListenerWrapper(eventListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener to respond to {@link ExtractorMediaSource} events and the handler to
|
||||
* deliver these events.
|
||||
*
|
||||
* @param eventHandler A handler for events.
|
||||
* @param eventListener A listener of events.
|
||||
* @return This builder.
|
||||
*/
|
||||
public Builder setEventListener(Handler eventHandler, MediaSourceEventListener eventListener) {
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
return this;
|
||||
@ -270,12 +291,31 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
||||
public ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory,
|
||||
ExtractorsFactory extractorsFactory, int minLoadableRetryCount, Handler eventHandler,
|
||||
EventListener eventListener, String customCacheKey, int continueLoadingCheckIntervalBytes) {
|
||||
this(
|
||||
uri,
|
||||
dataSourceFactory,
|
||||
extractorsFactory,
|
||||
minLoadableRetryCount,
|
||||
eventHandler,
|
||||
eventListener == null ? null : new EventListenerWrapper(eventListener),
|
||||
customCacheKey,
|
||||
continueLoadingCheckIntervalBytes);
|
||||
}
|
||||
|
||||
private ExtractorMediaSource(
|
||||
Uri uri,
|
||||
DataSource.Factory dataSourceFactory,
|
||||
ExtractorsFactory extractorsFactory,
|
||||
int minLoadableRetryCount,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable MediaSourceEventListener eventListener,
|
||||
@Nullable String customCacheKey,
|
||||
int continueLoadingCheckIntervalBytes) {
|
||||
this.uri = uri;
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.extractorsFactory = extractorsFactory;
|
||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||
this.customCacheKey = customCacheKey;
|
||||
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
||||
}
|
||||
@ -294,9 +334,16 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
||||
@Override
|
||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||
Assertions.checkArgument(id.periodIndex == 0);
|
||||
return new ExtractorMediaPeriod(uri, dataSourceFactory.createDataSource(),
|
||||
extractorsFactory.createExtractors(), minLoadableRetryCount, eventHandler, eventListener,
|
||||
this, allocator, customCacheKey, continueLoadingCheckIntervalBytes);
|
||||
return new ExtractorMediaPeriod(
|
||||
uri,
|
||||
dataSourceFactory.createDataSource(),
|
||||
extractorsFactory.createExtractors(),
|
||||
minLoadableRetryCount,
|
||||
eventDispatcher,
|
||||
this,
|
||||
allocator,
|
||||
customCacheKey,
|
||||
continueLoadingCheckIntervalBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -332,4 +379,94 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
||||
new SinglePeriodTimeline(timelineDurationUs, timelineIsSeekable, false), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a deprecated {@link EventListener}, invoking its callback from the equivalent callback in
|
||||
* {@link MediaSourceEventListener}.
|
||||
*/
|
||||
private static final class EventListenerWrapper implements MediaSourceEventListener {
|
||||
private final EventListener eventListener;
|
||||
|
||||
public EventListenerWrapper(EventListener eventListener) {
|
||||
this.eventListener = Assertions.checkNotNull(eventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadStarted(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCompleted(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadCanceled(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadError(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded,
|
||||
IOException error,
|
||||
boolean wasCanceled) {
|
||||
eventListener.onLoadError(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownstreamFormatChanged(
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaTimeMs) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
|
||||
/** Interface for callbacks to be notified of {@link MediaSource} events. */
|
||||
public interface MediaSourceEventListener {
|
||||
/**
|
||||
* Called when a load begins.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds to
|
||||
* media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does not
|
||||
* belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the load began.
|
||||
*/
|
||||
void onLoadStarted(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs);
|
||||
|
||||
/**
|
||||
* Called when a load ends.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds to
|
||||
* media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does not
|
||||
* belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the load ended.
|
||||
* @param loadDurationMs The duration of the load.
|
||||
* @param bytesLoaded The number of bytes that were loaded.
|
||||
*/
|
||||
void onLoadCompleted(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded);
|
||||
|
||||
/**
|
||||
* Called when a load is canceled.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds to
|
||||
* media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does not
|
||||
* belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the load was
|
||||
* canceled.
|
||||
* @param loadDurationMs The duration of the load up to the point at which it was canceled.
|
||||
* @param bytesLoaded The number of bytes that were loaded prior to cancelation.
|
||||
*/
|
||||
void onLoadCanceled(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded);
|
||||
|
||||
/**
|
||||
* Called when a load error occurs.
|
||||
*
|
||||
* <p>The error may or may not have resulted in the load being canceled, as indicated by the
|
||||
* {@code wasCanceled} parameter. If the load was canceled, {@link #onLoadCanceled} will
|
||||
* <em>not</em> be called in addition to this method.
|
||||
*
|
||||
* <p>This method being called does not indicate that playback has failed, or that it will fail.
|
||||
* The player may be able to recover from the error and continue. Hence applications should
|
||||
* <em>not</em> implement this method to display a user visible error or initiate an application
|
||||
* level retry ({@link Player.EventListener#onPlayerError} is the appropriate place to implement
|
||||
* such behavior). This method is called to provide the application with an opportunity to log the
|
||||
* error if it wishes to do so.
|
||||
*
|
||||
* @param dataSpec Defines the data being loaded.
|
||||
* @param dataType One of the {@link C} {@code DATA_TYPE_*} constants defining the type of data
|
||||
* being loaded.
|
||||
* @param trackType One of the {@link C} {@code TRACK_TYPE_*} constants if the data corresponds to
|
||||
* media of a specific type. {@link C#TRACK_TYPE_UNKNOWN} otherwise.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does not
|
||||
* belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaStartTimeMs The start time of the media being loaded, or {@link C#TIME_UNSET} if
|
||||
* the load is not for media data.
|
||||
* @param mediaEndTimeMs The end time of the media being loaded, or {@link C#TIME_UNSET} if the
|
||||
* load is not for media data.
|
||||
* @param elapsedRealtimeMs The value of {@link SystemClock#elapsedRealtime} when the error
|
||||
* occurred.
|
||||
* @param loadDurationMs The duration of the load up to the point at which the error occurred.
|
||||
* @param bytesLoaded The number of bytes that were loaded prior to the error.
|
||||
* @param error The load error.
|
||||
* @param wasCanceled Whether the load was canceled as a result of the error.
|
||||
*/
|
||||
void onLoadError(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaStartTimeMs,
|
||||
long mediaEndTimeMs,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded,
|
||||
IOException error,
|
||||
boolean wasCanceled);
|
||||
|
||||
/**
|
||||
* Called when data is removed from the back of a media buffer, typically so that it can be
|
||||
* re-buffered in a different format.
|
||||
*
|
||||
* @param trackType The type of the media. One of the {@link C} {@code TRACK_TYPE_*} constants.
|
||||
* @param mediaStartTimeMs The start time of the media being discarded.
|
||||
* @param mediaEndTimeMs The end time of the media being discarded.
|
||||
*/
|
||||
void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs);
|
||||
|
||||
/**
|
||||
* Called when a downstream format change occurs (i.e. when the format of the media being read
|
||||
* from one or more {@link SampleStream}s provided by the source changes).
|
||||
*
|
||||
* @param trackType The type of the media. One of the {@link C} {@code TRACK_TYPE_*} constants.
|
||||
* @param trackFormat The format of the track to which the data belongs. Null if the data does not
|
||||
* belong to a track.
|
||||
* @param trackSelectionReason One of the {@link C} {@code SELECTION_REASON_*} constants if the
|
||||
* data belongs to a track. {@link C#SELECTION_REASON_UNKNOWN} otherwise.
|
||||
* @param trackSelectionData Optional data associated with the selection of the track to which the
|
||||
* data belongs. Null if the data does not belong to a track.
|
||||
* @param mediaTimeMs The media time at which the change occurred.
|
||||
*/
|
||||
void onDownstreamFormatChanged(
|
||||
int trackType,
|
||||
Format trackFormat,
|
||||
int trackSelectionReason,
|
||||
Object trackSelectionData,
|
||||
long mediaTimeMs);
|
||||
|
||||
/** Dispatches events to a {@link MediaSourceEventListener}. */
|
||||
class EventDispatcher {
|
||||
|
||||
@Nullable private final Handler handler;
|
||||
@Nullable private final MediaSourceEventListener listener;
|
||||
private final long mediaTimeOffsetMs;
|
||||
|
||||
public EventDispatcher(@Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
|
||||
this(handler, listener, 0);
|
||||
}
|
||||
|
||||
public EventDispatcher(
|
||||
@Nullable Handler handler,
|
||||
@Nullable MediaSourceEventListener listener,
|
||||
long mediaTimeOffsetMs) {
|
||||
this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
|
||||
this.listener = listener;
|
||||
this.mediaTimeOffsetMs = mediaTimeOffsetMs;
|
||||
}
|
||||
|
||||
public void loadStarted(DataSpec dataSpec, int dataType, long elapsedRealtimeMs) {
|
||||
loadStarted(
|
||||
dataSpec,
|
||||
dataType,
|
||||
C.TRACK_TYPE_UNKNOWN,
|
||||
null,
|
||||
C.SELECTION_REASON_UNKNOWN,
|
||||
null,
|
||||
C.TIME_UNSET,
|
||||
C.TIME_UNSET,
|
||||
elapsedRealtimeMs);
|
||||
}
|
||||
|
||||
public void loadStarted(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs) {
|
||||
if (listener != null && handler != null) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadStarted(
|
||||
dataSpec,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs),
|
||||
elapsedRealtimeMs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCompleted(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded) {
|
||||
loadCompleted(
|
||||
dataSpec,
|
||||
dataType,
|
||||
C.TRACK_TYPE_UNKNOWN,
|
||||
null,
|
||||
C.SELECTION_REASON_UNKNOWN,
|
||||
null,
|
||||
C.TIME_UNSET,
|
||||
C.TIME_UNSET,
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
bytesLoaded);
|
||||
}
|
||||
|
||||
public void loadCompleted(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs,
|
||||
final long loadDurationMs,
|
||||
final long bytesLoaded) {
|
||||
if (listener != null && handler != null) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadCompleted(
|
||||
dataSpec,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs),
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
bytesLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCanceled(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded) {
|
||||
loadCanceled(
|
||||
dataSpec,
|
||||
dataType,
|
||||
C.TRACK_TYPE_UNKNOWN,
|
||||
null,
|
||||
C.SELECTION_REASON_UNKNOWN,
|
||||
null,
|
||||
C.TIME_UNSET,
|
||||
C.TIME_UNSET,
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
bytesLoaded);
|
||||
}
|
||||
|
||||
public void loadCanceled(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs,
|
||||
final long loadDurationMs,
|
||||
final long bytesLoaded) {
|
||||
if (listener != null && handler != null) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadCanceled(
|
||||
dataSpec,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs),
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
bytesLoaded);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void loadError(
|
||||
DataSpec dataSpec,
|
||||
int dataType,
|
||||
long elapsedRealtimeMs,
|
||||
long loadDurationMs,
|
||||
long bytesLoaded,
|
||||
IOException error,
|
||||
boolean wasCanceled) {
|
||||
loadError(
|
||||
dataSpec,
|
||||
dataType,
|
||||
C.TRACK_TYPE_UNKNOWN,
|
||||
null,
|
||||
C.SELECTION_REASON_UNKNOWN,
|
||||
null,
|
||||
C.TIME_UNSET,
|
||||
C.TIME_UNSET,
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
bytesLoaded,
|
||||
error,
|
||||
wasCanceled);
|
||||
}
|
||||
|
||||
public void loadError(
|
||||
final DataSpec dataSpec,
|
||||
final int dataType,
|
||||
final int trackType,
|
||||
final Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final Object trackSelectionData,
|
||||
final long mediaStartTimeUs,
|
||||
final long mediaEndTimeUs,
|
||||
final long elapsedRealtimeMs,
|
||||
final long loadDurationMs,
|
||||
final long bytesLoaded,
|
||||
final IOException error,
|
||||
final boolean wasCanceled) {
|
||||
if (listener != null && handler != null) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onLoadError(
|
||||
dataSpec,
|
||||
dataType,
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaStartTimeUs),
|
||||
adjustMediaTime(mediaEndTimeUs),
|
||||
elapsedRealtimeMs,
|
||||
loadDurationMs,
|
||||
bytesLoaded,
|
||||
error,
|
||||
wasCanceled);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void upstreamDiscarded(
|
||||
final int trackType, final long mediaStartTimeUs, final long mediaEndTimeUs) {
|
||||
if (listener != null && handler != null) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onUpstreamDiscarded(
|
||||
trackType, adjustMediaTime(mediaStartTimeUs), adjustMediaTime(mediaEndTimeUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void downstreamFormatChanged(
|
||||
final int trackType,
|
||||
final Format trackFormat,
|
||||
final int trackSelectionReason,
|
||||
final Object trackSelectionData,
|
||||
final long mediaTimeUs) {
|
||||
if (listener != null && handler != null) {
|
||||
handler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onDownstreamFormatChanged(
|
||||
trackType,
|
||||
trackFormat,
|
||||
trackSelectionReason,
|
||||
trackSelectionData,
|
||||
adjustMediaTime(mediaTimeUs));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private long adjustMediaTime(long mediaTimeUs) {
|
||||
long mediaTimeMs = C.usToMs(mediaTimeUs);
|
||||
return mediaTimeMs == C.TIME_UNSET ? C.TIME_UNSET : mediaTimeOffsetMs + mediaTimeMs;
|
||||
}
|
||||
}
|
||||
}
|
@ -26,9 +26,9 @@ import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||
import com.google.android.exoplayer2.source.ExtractorMediaSource.EventListener;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
@ -44,10 +44,8 @@ import java.util.Map;
|
||||
*/
|
||||
public final class AdsMediaSource implements MediaSource {
|
||||
|
||||
/**
|
||||
* Listener for events relating to ad loading.
|
||||
*/
|
||||
public interface AdsListener {
|
||||
/** Listener for ads media source events. */
|
||||
public interface EventListener extends MediaSourceEventListener {
|
||||
|
||||
/**
|
||||
* Called if there was an error loading ads. The media source will load the content without ads
|
||||
@ -75,15 +73,13 @@ public final class AdsMediaSource implements MediaSource {
|
||||
private final MediaSource contentMediaSource;
|
||||
private final AdsLoader adsLoader;
|
||||
private final ViewGroup adUiViewGroup;
|
||||
@Nullable private final Handler eventHandler;
|
||||
@Nullable private final EventListener eventListener;
|
||||
private final Handler mainHandler;
|
||||
private final ComponentListener componentListener;
|
||||
private final AdMediaSourceFactory adMediaSourceFactory;
|
||||
private final Map<MediaSource, List<DeferredMediaPeriod>> deferredMediaPeriodByAdMediaSource;
|
||||
private final Timeline.Period period;
|
||||
@Nullable
|
||||
private final Handler eventHandler;
|
||||
@Nullable
|
||||
private final AdsListener eventListener;
|
||||
|
||||
private Handler playerHandler;
|
||||
private ExoPlayer player;
|
||||
@ -115,10 +111,10 @@ public final class AdsMediaSource implements MediaSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new source that inserts ads linearly with the content specified by
|
||||
* {@code contentMediaSource}.
|
||||
* <p>
|
||||
* Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
|
||||
* Constructs a new source that inserts ads linearly with the content specified by {@code
|
||||
* contentMediaSource}.
|
||||
*
|
||||
* <p>Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
|
||||
* non-{@code null} it will be notified of both ad tag and ad media load errors.
|
||||
*
|
||||
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
||||
@ -128,9 +124,13 @@ public final class AdsMediaSource implements MediaSource {
|
||||
* @param eventHandler A handler for events. May be null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
*/
|
||||
public AdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
|
||||
AdsLoader adsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
|
||||
@Nullable AdsListener eventListener) {
|
||||
public AdsMediaSource(
|
||||
MediaSource contentMediaSource,
|
||||
DataSource.Factory dataSourceFactory,
|
||||
AdsLoader adsLoader,
|
||||
ViewGroup adUiViewGroup,
|
||||
@Nullable Handler eventHandler,
|
||||
@Nullable EventListener eventListener) {
|
||||
this.contentMediaSource = contentMediaSource;
|
||||
this.adsLoader = adsLoader;
|
||||
this.adUiViewGroup = adUiViewGroup;
|
||||
@ -186,7 +186,7 @@ public final class AdsMediaSource implements MediaSource {
|
||||
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
||||
Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup];
|
||||
final MediaSource adMediaSource =
|
||||
adMediaSourceFactory.createAdMediaSource(adUri, mainHandler, componentListener);
|
||||
adMediaSourceFactory.createAdMediaSource(adUri, eventHandler, eventListener);
|
||||
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
||||
if (adIndexInAdGroup >= oldAdCount) {
|
||||
int adCount = adIndexInAdGroup + 1;
|
||||
@ -306,11 +306,8 @@ public final class AdsMediaSource implements MediaSource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for component events. All methods are called on the main thread.
|
||||
*/
|
||||
private final class ComponentListener implements AdsLoader.EventListener,
|
||||
AdMediaSourceLoadErrorListener {
|
||||
/** Listener for component events. All methods are called on the main thread. */
|
||||
private final class ComponentListener implements AdsLoader.EventListener {
|
||||
|
||||
@Override
|
||||
public void onAdPlaybackState(final AdPlaybackState adPlaybackState) {
|
||||
@ -374,20 +371,6 @@ public final class AdsMediaSource implements MediaSource {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for errors while loading an ad {@link MediaSource}.
|
||||
*/
|
||||
private interface AdMediaSourceLoadErrorListener {
|
||||
|
||||
/**
|
||||
* Called when an error occurs loading media data.
|
||||
*
|
||||
* @param error The load error.
|
||||
*/
|
||||
void onLoadError(IOException error);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for {@link MediaSource}s for loading ad media.
|
||||
*/
|
||||
@ -397,15 +380,13 @@ public final class AdsMediaSource implements MediaSource {
|
||||
* Creates a new {@link MediaSource} for loading the ad media with the specified {@code uri}.
|
||||
*
|
||||
* @param uri The URI of the ad.
|
||||
* @param handler A handler for listener events.
|
||||
* @param listener A listener for ad load errors. To have ad media source load errors notified
|
||||
* via the ads media source's listener, call this listener's onLoadError method from your
|
||||
* new media source's load error listener using the specified {@code handler}. Otherwise,
|
||||
* this parameter can be ignored.
|
||||
* @param handler A handler for listener events. May be null if delivery of events is not
|
||||
* required.
|
||||
* @param listener A listener for events. May be null if delivery of events is not required.
|
||||
* @return The new media source.
|
||||
*/
|
||||
MediaSource createAdMediaSource(Uri uri, Handler handler,
|
||||
AdMediaSourceLoadErrorListener listener);
|
||||
MediaSource createAdMediaSource(
|
||||
Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener);
|
||||
|
||||
/**
|
||||
* Returns the content types supported by media sources created by this factory. Each element
|
||||
@ -427,15 +408,11 @@ public final class AdsMediaSource implements MediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaSource createAdMediaSource(Uri uri, Handler handler,
|
||||
final AdMediaSourceLoadErrorListener listener) {
|
||||
return new ExtractorMediaSource.Builder(uri, dataSourceFactory).setEventListener(handler,
|
||||
new EventListener() {
|
||||
@Override
|
||||
public void onLoadError(IOException error) {
|
||||
listener.onLoadError(error);
|
||||
}
|
||||
}).build();
|
||||
public MediaSource createAdMediaSource(
|
||||
Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
|
||||
return new ExtractorMediaSource.Builder(uri, dataSourceFactory)
|
||||
.setEventListener(handler, listener)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.upstream;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -79,7 +80,7 @@ public final class DataSpec {
|
||||
* A key that uniquely identifies the original stream. Used for cache indexing. May be null if the
|
||||
* {@link DataSpec} is not intended to be used in conjunction with a cache.
|
||||
*/
|
||||
public final String key;
|
||||
@Nullable public final String key;
|
||||
/**
|
||||
* Request flags. Currently {@link #FLAG_ALLOW_GZIP} and
|
||||
* {@link #FLAG_ALLOW_CACHING_UNKNOWN_LENGTH} are the only supported flags.
|
||||
@ -113,7 +114,7 @@ public final class DataSpec {
|
||||
* @param length {@link #length}.
|
||||
* @param key {@link #key}.
|
||||
*/
|
||||
public DataSpec(Uri uri, long absoluteStreamPosition, long length, String key) {
|
||||
public DataSpec(Uri uri, long absoluteStreamPosition, long length, @Nullable String key) {
|
||||
this(uri, absoluteStreamPosition, absoluteStreamPosition, length, key, 0);
|
||||
}
|
||||
|
||||
@ -147,8 +148,8 @@ public final class DataSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a {@link DataSpec} where {@link #position} may differ from
|
||||
* {@link #absoluteStreamPosition}.
|
||||
* Construct a {@link DataSpec} where {@link #position} may differ from {@link
|
||||
* #absoluteStreamPosition}.
|
||||
*
|
||||
* @param uri {@link #uri}.
|
||||
* @param postBody {@link #postBody}.
|
||||
@ -158,8 +159,14 @@ public final class DataSpec {
|
||||
* @param key {@link #key}.
|
||||
* @param flags {@link #flags}.
|
||||
*/
|
||||
public DataSpec(Uri uri, byte[] postBody, long absoluteStreamPosition, long position, long length,
|
||||
String key, @Flags int flags) {
|
||||
public DataSpec(
|
||||
Uri uri,
|
||||
byte[] postBody,
|
||||
long absoluteStreamPosition,
|
||||
long position,
|
||||
long length,
|
||||
@Nullable String key,
|
||||
@Flags int flags) {
|
||||
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
||||
Assertions.checkArgument(position >= 0);
|
||||
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET);
|
||||
|
Loading…
x
Reference in New Issue
Block a user