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.
|
* Add optional parameter to `Player.stop` to reset the player when stopping.
|
||||||
* Fix handling of playback parameters changes while paused when followed by a
|
* Fix handling of playback parameters changes while paused when followed by a
|
||||||
seek.
|
seek.
|
||||||
|
* Use the same listener `MediaSourceEventListener` for all MediaSource
|
||||||
|
implementations.
|
||||||
|
|
||||||
### 2.6.0 ###
|
### 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.TextInformationFrame;
|
||||||
import com.google.android.exoplayer2.metadata.id3.UrlLinkFrame;
|
import com.google.android.exoplayer2.metadata.id3.UrlLinkFrame;
|
||||||
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
|
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.TrackGroup;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||||
@ -53,13 +52,15 @@ import java.io.IOException;
|
|||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/** Logs player events using {@link Log}. */
|
||||||
* Logs player events using {@link Log}.
|
/* package */ final class EventLogger
|
||||||
*/
|
implements Player.EventListener,
|
||||||
/* package */ final class EventLogger implements Player.EventListener, MetadataOutput,
|
MetadataOutput,
|
||||||
AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener,
|
AudioRendererEventListener,
|
||||||
ExtractorMediaSource.EventListener, AdsMediaSource.AdsListener,
|
VideoRendererEventListener,
|
||||||
DefaultDrmSessionManager.EventListener {
|
AdaptiveMediaSourceEventListener,
|
||||||
|
AdsMediaSource.EventListener,
|
||||||
|
DefaultDrmSessionManager.EventListener {
|
||||||
|
|
||||||
private static final String TAG = "EventLogger";
|
private static final String TAG = "EventLogger";
|
||||||
private static final int MAX_TIMELINE_ITEM_LINES = 3;
|
private static final int MAX_TIMELINE_ITEM_LINES = 3;
|
||||||
@ -324,13 +325,6 @@ import java.util.Locale;
|
|||||||
Log.d(TAG, "drmKeysLoaded [" + getSessionTimeString() + "]");
|
Log.d(TAG, "drmKeysLoaded [" + getSessionTimeString() + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractorMediaSource.EventListener
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadError(IOException error) {
|
|
||||||
printInternalError("loadError", error);
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdaptiveMediaSourceEventListener
|
// AdaptiveMediaSourceEventListener
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,8 +52,8 @@ public final class ImaAdsMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new source that inserts ads linearly with the content specified by
|
* Constructs a new source that inserts ads linearly with the content specified by {@code
|
||||||
* {@code contentMediaSource}.
|
* contentMediaSource}.
|
||||||
*
|
*
|
||||||
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
* @param contentMediaSource The {@link MediaSource} providing the content to play.
|
||||||
* @param dataSourceFactory Factory for data sources used to load ad media.
|
* @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 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 eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
*/
|
||||||
public ImaAdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
|
public ImaAdsMediaSource(
|
||||||
ImaAdsLoader imaAdsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
|
MediaSource contentMediaSource,
|
||||||
@Nullable AdsMediaSource.AdsListener eventListener) {
|
DataSource.Factory dataSourceFactory,
|
||||||
|
ImaAdsLoader imaAdsLoader,
|
||||||
|
ViewGroup adUiViewGroup,
|
||||||
|
@Nullable Handler eventHandler,
|
||||||
|
@Nullable AdsMediaSource.EventListener eventListener) {
|
||||||
adsMediaSource = new AdsMediaSource(contentMediaSource, dataSourceFactory, imaAdsLoader,
|
adsMediaSource = new AdsMediaSource(contentMediaSource, dataSourceFactory, imaAdsLoader,
|
||||||
adUiViewGroup, eventHandler, eventListener);
|
adUiViewGroup, eventHandler, eventListener);
|
||||||
}
|
}
|
||||||
|
@ -16,306 +16,39 @@
|
|||||||
package com.google.android.exoplayer2.source;
|
package com.google.android.exoplayer2.source;
|
||||||
|
|
||||||
import android.os.Handler;
|
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 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 {
|
||||||
|
|
||||||
/**
|
/** Dispatches events to a {@link MediaSourceEventListener}. */
|
||||||
* Called when a load begins.
|
final class EventDispatcher extends MediaSourceEventListener.EventDispatcher {
|
||||||
*
|
|
||||||
* @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 {
|
|
||||||
|
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
private final AdaptiveMediaSourceEventListener listener;
|
private final MediaSourceEventListener listener;
|
||||||
private final long mediaTimeOffsetMs;
|
|
||||||
|
|
||||||
public EventDispatcher(Handler handler, AdaptiveMediaSourceEventListener listener) {
|
public EventDispatcher(@Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
|
||||||
this(handler, listener, 0);
|
this(handler, listener, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventDispatcher(Handler handler, AdaptiveMediaSourceEventListener listener,
|
public EventDispatcher(
|
||||||
|
@Nullable Handler handler,
|
||||||
|
@Nullable MediaSourceEventListener listener,
|
||||||
long mediaTimeOffsetMs) {
|
long mediaTimeOffsetMs) {
|
||||||
this.handler = listener != null ? Assertions.checkNotNull(handler) : null;
|
super(handler, listener, mediaTimeOffsetMs);
|
||||||
|
this.handler = handler;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.mediaTimeOffsetMs = mediaTimeOffsetMs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EventDispatcher copyWithMediaTimeOffsetMs(long mediaTimeOffsetMs) {
|
public AdaptiveMediaSourceEventListener.EventDispatcher copyWithMediaTimeOffsetMs(
|
||||||
return new EventDispatcher(handler, listener, mediaTimeOffsetMs);
|
long mediaTimeOffsetMs) {
|
||||||
}
|
return new AdaptiveMediaSourceEventListener.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.FormatHolder;
|
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.PositionHolder;
|
||||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
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.source.SampleQueue.UpstreamFormatChangedListener;
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
import com.google.android.exoplayer2.trackselection.TrackSelection;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
@ -74,11 +76,10 @@ import java.util.Arrays;
|
|||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
private final int minLoadableRetryCount;
|
private final int minLoadableRetryCount;
|
||||||
private final Handler eventHandler;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final ExtractorMediaSource.EventListener eventListener;
|
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private final Allocator allocator;
|
private final Allocator allocator;
|
||||||
private final String customCacheKey;
|
@Nullable private final String customCacheKey;
|
||||||
private final long continueLoadingCheckIntervalBytes;
|
private final long continueLoadingCheckIntervalBytes;
|
||||||
private final Loader loader;
|
private final Loader loader;
|
||||||
private final ExtractorHolder extractorHolder;
|
private final ExtractorHolder extractorHolder;
|
||||||
@ -117,8 +118,7 @@ import java.util.Arrays;
|
|||||||
* @param dataSource The data source to read the media.
|
* @param dataSource The data source to read the media.
|
||||||
* @param extractors The extractors to use to read the data source.
|
* @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 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 eventDispatcher A dispatcher to notify of events.
|
||||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
|
||||||
* @param listener A listener to notify when information about the period changes.
|
* @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 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
|
* @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
|
* @param continueLoadingCheckIntervalBytes The number of bytes that should be loaded between each
|
||||||
* invocation of {@link Callback#onContinueLoadingRequested(SequenceableLoader)}.
|
* invocation of {@link Callback#onContinueLoadingRequested(SequenceableLoader)}.
|
||||||
*/
|
*/
|
||||||
public ExtractorMediaPeriod(Uri uri, DataSource dataSource, Extractor[] extractors,
|
public ExtractorMediaPeriod(
|
||||||
int minLoadableRetryCount, Handler eventHandler,
|
Uri uri,
|
||||||
ExtractorMediaSource.EventListener eventListener, Listener listener,
|
DataSource dataSource,
|
||||||
Allocator allocator, String customCacheKey, int continueLoadingCheckIntervalBytes) {
|
Extractor[] extractors,
|
||||||
|
int minLoadableRetryCount,
|
||||||
|
EventDispatcher eventDispatcher,
|
||||||
|
Listener listener,
|
||||||
|
Allocator allocator,
|
||||||
|
@Nullable String customCacheKey,
|
||||||
|
int continueLoadingCheckIntervalBytes) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.dataSource = dataSource;
|
this.dataSource = dataSource;
|
||||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||||
this.eventHandler = eventHandler;
|
this.eventDispatcher = eventDispatcher;
|
||||||
this.eventListener = eventListener;
|
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.allocator = allocator;
|
this.allocator = allocator;
|
||||||
this.customCacheKey = customCacheKey;
|
this.customCacheKey = customCacheKey;
|
||||||
@ -430,8 +435,22 @@ import java.util.Arrays;
|
|||||||
public int onLoadError(ExtractingLoadable loadable, long elapsedRealtimeMs,
|
public int onLoadError(ExtractingLoadable loadable, long elapsedRealtimeMs,
|
||||||
long loadDurationMs, IOException error) {
|
long loadDurationMs, IOException error) {
|
||||||
copyLengthFromLoader(loadable);
|
copyLengthFromLoader(loadable);
|
||||||
notifyLoadError(error);
|
boolean isErrorFatal = isLoadableExceptionFatal(error);
|
||||||
if (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;
|
return Loader.DONT_RETRY_FATAL;
|
||||||
}
|
}
|
||||||
int extractedSamplesCount = getExtractedSamplesCount();
|
int extractedSamplesCount = getExtractedSamplesCount();
|
||||||
@ -606,17 +625,6 @@ import java.util.Arrays;
|
|||||||
return e instanceof UnrecognizedInputFormatException;
|
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 class SampleStreamImpl implements SampleStream {
|
||||||
|
|
||||||
private final int track;
|
private final int track;
|
||||||
@ -663,7 +671,9 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
private boolean pendingExtractorSeek;
|
private boolean pendingExtractorSeek;
|
||||||
private long seekTimeUs;
|
private long seekTimeUs;
|
||||||
|
private DataSpec dataSpec;
|
||||||
private long length;
|
private long length;
|
||||||
|
private long bytesLoaded;
|
||||||
|
|
||||||
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
|
public ExtractingLoadable(Uri uri, DataSource dataSource, ExtractorHolder extractorHolder,
|
||||||
ConditionVariable loadCondition) {
|
ConditionVariable loadCondition) {
|
||||||
@ -699,7 +709,8 @@ import java.util.Arrays;
|
|||||||
ExtractorInput input = null;
|
ExtractorInput input = null;
|
||||||
try {
|
try {
|
||||||
long position = positionHolder.position;
|
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) {
|
if (length != C.LENGTH_UNSET) {
|
||||||
length += position;
|
length += position;
|
||||||
}
|
}
|
||||||
@ -723,6 +734,7 @@ import java.util.Arrays;
|
|||||||
result = Extractor.RESULT_CONTINUE;
|
result = Extractor.RESULT_CONTINUE;
|
||||||
} else if (input != null) {
|
} else if (input != null) {
|
||||||
positionHolder.position = input.getPosition();
|
positionHolder.position = input.getPosition();
|
||||||
|
bytesLoaded = positionHolder.position - dataSpec.absoluteStreamPosition;
|
||||||
}
|
}
|
||||||
Util.closeQuietly(dataSource);
|
Util.closeQuietly(dataSource);
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,18 @@ package com.google.android.exoplayer2.source;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
|
||||||
import com.google.android.exoplayer2.extractor.Extractor;
|
import com.google.android.exoplayer2.extractor.Extractor;
|
||||||
import com.google.android.exoplayer2.extractor.ExtractorsFactory;
|
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.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.io.IOException;
|
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.
|
* 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 {
|
public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPeriod.Listener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener of {@link ExtractorMediaSource} events.
|
* Listener of {@link ExtractorMediaSource} events.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link MediaSourceEventListener}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public interface EventListener {
|
public interface EventListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,8 +95,7 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
|||||||
private final DataSource.Factory dataSourceFactory;
|
private final DataSource.Factory dataSourceFactory;
|
||||||
private final ExtractorsFactory extractorsFactory;
|
private final ExtractorsFactory extractorsFactory;
|
||||||
private final int minLoadableRetryCount;
|
private final int minLoadableRetryCount;
|
||||||
private final Handler eventHandler;
|
private final EventDispatcher eventDispatcher;
|
||||||
private final EventListener eventListener;
|
|
||||||
private final String customCacheKey;
|
private final String customCacheKey;
|
||||||
private final int continueLoadingCheckIntervalBytes;
|
private final int continueLoadingCheckIntervalBytes;
|
||||||
|
|
||||||
@ -108,9 +113,9 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
|||||||
|
|
||||||
private ExtractorsFactory extractorsFactory;
|
private ExtractorsFactory extractorsFactory;
|
||||||
private int minLoadableRetryCount;
|
private int minLoadableRetryCount;
|
||||||
private Handler eventHandler;
|
@Nullable private Handler eventHandler;
|
||||||
private EventListener eventListener;
|
@Nullable private MediaSourceEventListener eventListener;
|
||||||
private String customCacheKey;
|
@Nullable private String customCacheKey;
|
||||||
private int continueLoadingCheckIntervalBytes;
|
private int continueLoadingCheckIntervalBytes;
|
||||||
private boolean isBuildCalled;
|
private boolean isBuildCalled;
|
||||||
|
|
||||||
@ -187,8 +192,24 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
|||||||
* @param eventHandler A handler for events.
|
* @param eventHandler A handler for events.
|
||||||
* @param eventListener A listener of events.
|
* @param eventListener A listener of events.
|
||||||
* @return This builder.
|
* @return This builder.
|
||||||
|
* @deprecated Use {@link #setEventListener(Handler, MediaSourceEventListener)}.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Builder setEventListener(Handler eventHandler, EventListener eventListener) {
|
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.eventHandler = eventHandler;
|
||||||
this.eventListener = eventListener;
|
this.eventListener = eventListener;
|
||||||
return this;
|
return this;
|
||||||
@ -270,12 +291,31 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
|||||||
public ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory,
|
public ExtractorMediaSource(Uri uri, DataSource.Factory dataSourceFactory,
|
||||||
ExtractorsFactory extractorsFactory, int minLoadableRetryCount, Handler eventHandler,
|
ExtractorsFactory extractorsFactory, int minLoadableRetryCount, Handler eventHandler,
|
||||||
EventListener eventListener, String customCacheKey, int continueLoadingCheckIntervalBytes) {
|
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.uri = uri;
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
this.extractorsFactory = extractorsFactory;
|
this.extractorsFactory = extractorsFactory;
|
||||||
this.minLoadableRetryCount = minLoadableRetryCount;
|
this.minLoadableRetryCount = minLoadableRetryCount;
|
||||||
this.eventHandler = eventHandler;
|
this.eventDispatcher = new EventDispatcher(eventHandler, eventListener);
|
||||||
this.eventListener = eventListener;
|
|
||||||
this.customCacheKey = customCacheKey;
|
this.customCacheKey = customCacheKey;
|
||||||
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
this.continueLoadingCheckIntervalBytes = continueLoadingCheckIntervalBytes;
|
||||||
}
|
}
|
||||||
@ -294,9 +334,16 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
|||||||
@Override
|
@Override
|
||||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||||
Assertions.checkArgument(id.periodIndex == 0);
|
Assertions.checkArgument(id.periodIndex == 0);
|
||||||
return new ExtractorMediaPeriod(uri, dataSourceFactory.createDataSource(),
|
return new ExtractorMediaPeriod(
|
||||||
extractorsFactory.createExtractors(), minLoadableRetryCount, eventHandler, eventListener,
|
uri,
|
||||||
this, allocator, customCacheKey, continueLoadingCheckIntervalBytes);
|
dataSourceFactory.createDataSource(),
|
||||||
|
extractorsFactory.createExtractors(),
|
||||||
|
minLoadableRetryCount,
|
||||||
|
eventDispatcher,
|
||||||
|
this,
|
||||||
|
allocator,
|
||||||
|
customCacheKey,
|
||||||
|
continueLoadingCheckIntervalBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -332,4 +379,94 @@ public final class ExtractorMediaSource implements MediaSource, ExtractorMediaPe
|
|||||||
new SinglePeriodTimeline(timelineDurationUs, timelineIsSeekable, false), null);
|
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.Timeline;
|
||||||
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
|
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
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.MediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
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.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
@ -44,10 +44,8 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public final class AdsMediaSource implements MediaSource {
|
public final class AdsMediaSource implements MediaSource {
|
||||||
|
|
||||||
/**
|
/** Listener for ads media source events. */
|
||||||
* Listener for events relating to ad loading.
|
public interface EventListener extends MediaSourceEventListener {
|
||||||
*/
|
|
||||||
public interface AdsListener {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called if there was an error loading ads. The media source will load the content without ads
|
* 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 MediaSource contentMediaSource;
|
||||||
private final AdsLoader adsLoader;
|
private final AdsLoader adsLoader;
|
||||||
private final ViewGroup adUiViewGroup;
|
private final ViewGroup adUiViewGroup;
|
||||||
|
@Nullable private final Handler eventHandler;
|
||||||
|
@Nullable private final EventListener eventListener;
|
||||||
private final Handler mainHandler;
|
private final Handler mainHandler;
|
||||||
private final ComponentListener componentListener;
|
private final ComponentListener componentListener;
|
||||||
private final AdMediaSourceFactory adMediaSourceFactory;
|
private final AdMediaSourceFactory adMediaSourceFactory;
|
||||||
private final Map<MediaSource, List<DeferredMediaPeriod>> deferredMediaPeriodByAdMediaSource;
|
private final Map<MediaSource, List<DeferredMediaPeriod>> deferredMediaPeriodByAdMediaSource;
|
||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
@Nullable
|
|
||||||
private final Handler eventHandler;
|
|
||||||
@Nullable
|
|
||||||
private final AdsListener eventListener;
|
|
||||||
|
|
||||||
private Handler playerHandler;
|
private Handler playerHandler;
|
||||||
private ExoPlayer player;
|
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
|
* Constructs a new source that inserts ads linearly with the content specified by {@code
|
||||||
* {@code contentMediaSource}.
|
* contentMediaSource}.
|
||||||
* <p>
|
*
|
||||||
* Ad media is loaded using {@link ExtractorMediaSource}. If {@code eventListener} is
|
* <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.
|
* 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.
|
* @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 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 eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
*/
|
*/
|
||||||
public AdsMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory,
|
public AdsMediaSource(
|
||||||
AdsLoader adsLoader, ViewGroup adUiViewGroup, @Nullable Handler eventHandler,
|
MediaSource contentMediaSource,
|
||||||
@Nullable AdsListener eventListener) {
|
DataSource.Factory dataSourceFactory,
|
||||||
|
AdsLoader adsLoader,
|
||||||
|
ViewGroup adUiViewGroup,
|
||||||
|
@Nullable Handler eventHandler,
|
||||||
|
@Nullable EventListener eventListener) {
|
||||||
this.contentMediaSource = contentMediaSource;
|
this.contentMediaSource = contentMediaSource;
|
||||||
this.adsLoader = adsLoader;
|
this.adsLoader = adsLoader;
|
||||||
this.adUiViewGroup = adUiViewGroup;
|
this.adUiViewGroup = adUiViewGroup;
|
||||||
@ -186,7 +186,7 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
||||||
Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup];
|
Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup];
|
||||||
final MediaSource adMediaSource =
|
final MediaSource adMediaSource =
|
||||||
adMediaSourceFactory.createAdMediaSource(adUri, mainHandler, componentListener);
|
adMediaSourceFactory.createAdMediaSource(adUri, eventHandler, eventListener);
|
||||||
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
||||||
if (adIndexInAdGroup >= oldAdCount) {
|
if (adIndexInAdGroup >= oldAdCount) {
|
||||||
int adCount = adIndexInAdGroup + 1;
|
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. */
|
||||||
* Listener for component events. All methods are called on the main thread.
|
private final class ComponentListener implements AdsLoader.EventListener {
|
||||||
*/
|
|
||||||
private final class ComponentListener implements AdsLoader.EventListener,
|
|
||||||
AdMediaSourceLoadErrorListener {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAdPlaybackState(final AdPlaybackState adPlaybackState) {
|
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.
|
* 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}.
|
* Creates a new {@link MediaSource} for loading the ad media with the specified {@code uri}.
|
||||||
*
|
*
|
||||||
* @param uri The URI of the ad.
|
* @param uri The URI of the ad.
|
||||||
* @param handler A handler for listener events.
|
* @param handler A handler for listener events. May be null if delivery of events is not
|
||||||
* @param listener A listener for ad load errors. To have ad media source load errors notified
|
* required.
|
||||||
* via the ads media source's listener, call this listener's onLoadError method from your
|
* @param listener A listener for events. May be null if delivery of events is not required.
|
||||||
* new media source's load error listener using the specified {@code handler}. Otherwise,
|
|
||||||
* this parameter can be ignored.
|
|
||||||
* @return The new media source.
|
* @return The new media source.
|
||||||
*/
|
*/
|
||||||
MediaSource createAdMediaSource(Uri uri, Handler handler,
|
MediaSource createAdMediaSource(
|
||||||
AdMediaSourceLoadErrorListener listener);
|
Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the content types supported by media sources created by this factory. Each element
|
* 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
|
@Override
|
||||||
public MediaSource createAdMediaSource(Uri uri, Handler handler,
|
public MediaSource createAdMediaSource(
|
||||||
final AdMediaSourceLoadErrorListener listener) {
|
Uri uri, @Nullable Handler handler, @Nullable MediaSourceEventListener listener) {
|
||||||
return new ExtractorMediaSource.Builder(uri, dataSourceFactory).setEventListener(handler,
|
return new ExtractorMediaSource.Builder(uri, dataSourceFactory)
|
||||||
new EventListener() {
|
.setEventListener(handler, listener)
|
||||||
@Override
|
.build();
|
||||||
public void onLoadError(IOException error) {
|
|
||||||
listener.onLoadError(error);
|
|
||||||
}
|
|
||||||
}).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.upstream;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.IntDef;
|
import android.support.annotation.IntDef;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.lang.annotation.Retention;
|
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
|
* 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.
|
* {@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
|
* Request flags. Currently {@link #FLAG_ALLOW_GZIP} and
|
||||||
* {@link #FLAG_ALLOW_CACHING_UNKNOWN_LENGTH} are the only supported flags.
|
* {@link #FLAG_ALLOW_CACHING_UNKNOWN_LENGTH} are the only supported flags.
|
||||||
@ -113,7 +114,7 @@ public final class DataSpec {
|
|||||||
* @param length {@link #length}.
|
* @param length {@link #length}.
|
||||||
* @param key {@link #key}.
|
* @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);
|
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
|
* Construct a {@link DataSpec} where {@link #position} may differ from {@link
|
||||||
* {@link #absoluteStreamPosition}.
|
* #absoluteStreamPosition}.
|
||||||
*
|
*
|
||||||
* @param uri {@link #uri}.
|
* @param uri {@link #uri}.
|
||||||
* @param postBody {@link #postBody}.
|
* @param postBody {@link #postBody}.
|
||||||
@ -158,8 +159,14 @@ public final class DataSpec {
|
|||||||
* @param key {@link #key}.
|
* @param key {@link #key}.
|
||||||
* @param flags {@link #flags}.
|
* @param flags {@link #flags}.
|
||||||
*/
|
*/
|
||||||
public DataSpec(Uri uri, byte[] postBody, long absoluteStreamPosition, long position, long length,
|
public DataSpec(
|
||||||
String key, @Flags int flags) {
|
Uri uri,
|
||||||
|
byte[] postBody,
|
||||||
|
long absoluteStreamPosition,
|
||||||
|
long position,
|
||||||
|
long length,
|
||||||
|
@Nullable String key,
|
||||||
|
@Flags int flags) {
|
||||||
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
||||||
Assertions.checkArgument(position >= 0);
|
Assertions.checkArgument(position >= 0);
|
||||||
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET);
|
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user