diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 3a42311b26..f6adf560b9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -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 ### diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java index 68a10343e6..0635944640 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java @@ -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 diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java index 02aa4807a5..cd646daf42 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsMediaSource.java @@ -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); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/AdaptiveMediaSourceEventListener.java b/library/core/src/main/java/com/google/android/exoplayer2/source/AdaptiveMediaSourceEventListener.java index be07cbb5dc..2bc9d48726 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/AdaptiveMediaSourceEventListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/AdaptiveMediaSourceEventListener.java @@ -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. - *
- * 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 - * not be called in addition to this method. - *
- * 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 - * not 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); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java index 900ba5bd37..c0586b3a28 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java @@ -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); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java index 351416df6a..4ea20e242e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaSource.java @@ -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. + } + } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java new file mode 100644 index 0000000000..82e8781d70 --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MediaSourceEventListener.java @@ -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. + * + *
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 + * not be called in addition to this method. + * + *
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
+ * not 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;
+ }
+ }
+}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
index 47a2540c38..54a8fd96ae 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java
@@ -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
- * 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}.
+ *
+ * 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
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java
index ab1542c7a6..cbe971bc5d 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSpec.java
@@ -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);