Pass Handler together with Runnable callbacks for playlist commands.
We currently either use the app thread returned by the player or the thread the commands are called on depending on whether the media source is already prepared or not. This change lets the application decide which callback thread to use. As a side effect, we also don't longer need access the player instance passed to MediaSource.prepare. PiperOrigin-RevId: 227871111
This commit is contained in:
parent
fb81d629f0
commit
22599a6d6c
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
### 2.9.5 ###
|
### 2.9.5 ###
|
||||||
|
|
||||||
|
* Add `Handler` parameter to `ConcatenatingMediaSource` methods which take a
|
||||||
|
callback `Runnable`.
|
||||||
* HLS: Parse `CHANNELS` attribute from `EXT-X-MEDIA` tag.
|
* HLS: Parse `CHANNELS` attribute from `EXT-X-MEDIA` tag.
|
||||||
* ExtractorMediaSource: Fix issue that could cause the player to get stuck
|
* ExtractorMediaSource: Fix issue that could cause the player to get stuck
|
||||||
buffering at the end of the media.
|
buffering at the end of the media.
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source;
|
|||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.support.annotation.GuardedBy;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
@ -28,6 +29,7 @@ import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
|
|||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.EventDispatcher;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -53,22 +55,21 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
private static final int MSG_NOTIFY_LISTENER = 4;
|
private static final int MSG_NOTIFY_LISTENER = 4;
|
||||||
private static final int MSG_ON_COMPLETION = 5;
|
private static final int MSG_ON_COMPLETION = 5;
|
||||||
|
|
||||||
// Accessed on the app thread.
|
// Accessed on any thread.
|
||||||
private final List<MediaSourceHolder> mediaSourcesPublic;
|
private final List<MediaSourceHolder> mediaSourcesPublic;
|
||||||
|
@Nullable private Handler playbackThreadHandler;
|
||||||
|
|
||||||
// Accessed on the playback thread.
|
// Accessed on the playback thread only.
|
||||||
private final List<MediaSourceHolder> mediaSourceHolders;
|
private final List<MediaSourceHolder> mediaSourceHolders;
|
||||||
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
|
private final Map<MediaPeriod, MediaSourceHolder> mediaSourceByMediaPeriod;
|
||||||
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
|
private final Map<Object, MediaSourceHolder> mediaSourceByUid;
|
||||||
private final List<Runnable> pendingOnCompletionActions;
|
|
||||||
private final boolean isAtomic;
|
private final boolean isAtomic;
|
||||||
private final boolean useLazyPreparation;
|
private final boolean useLazyPreparation;
|
||||||
private final Timeline.Window window;
|
private final Timeline.Window window;
|
||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
|
|
||||||
@Nullable private Handler playbackThreadHandler;
|
|
||||||
@Nullable private Handler applicationThreadHandler;
|
|
||||||
private boolean listenerNotificationScheduled;
|
private boolean listenerNotificationScheduled;
|
||||||
|
private EventDispatcher<Runnable> pendingOnCompletionActions;
|
||||||
private ShuffleOrder shuffleOrder;
|
private ShuffleOrder shuffleOrder;
|
||||||
private int windowCount;
|
private int windowCount;
|
||||||
private int periodCount;
|
private int periodCount;
|
||||||
@ -127,7 +128,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
this.mediaSourceByUid = new HashMap<>();
|
this.mediaSourceByUid = new HashMap<>();
|
||||||
this.mediaSourcesPublic = new ArrayList<>();
|
this.mediaSourcesPublic = new ArrayList<>();
|
||||||
this.mediaSourceHolders = new ArrayList<>();
|
this.mediaSourceHolders = new ArrayList<>();
|
||||||
this.pendingOnCompletionActions = new ArrayList<>();
|
this.pendingOnCompletionActions = new EventDispatcher<>();
|
||||||
this.isAtomic = isAtomic;
|
this.isAtomic = isAtomic;
|
||||||
this.useLazyPreparation = useLazyPreparation;
|
this.useLazyPreparation = useLazyPreparation;
|
||||||
window = new Timeline.Window();
|
window = new Timeline.Window();
|
||||||
@ -141,19 +142,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* @param mediaSource The {@link MediaSource} to be added to the list.
|
* @param mediaSource The {@link MediaSource} to be added to the list.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSource(MediaSource mediaSource) {
|
public final synchronized void addMediaSource(MediaSource mediaSource) {
|
||||||
addMediaSource(mediaSourcesPublic.size(), mediaSource, null);
|
addMediaSource(mediaSourcesPublic.size(), mediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends a {@link MediaSource} to the playlist and executes a custom action on completion.
|
* Appends a {@link MediaSource} to the playlist and executes a custom action on completion.
|
||||||
*
|
*
|
||||||
* @param mediaSource The {@link MediaSource} to be added to the list.
|
* @param mediaSource The {@link MediaSource} to be added to the list.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* source has been added to the playlist.
|
* source has been added to the playlist.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSource(
|
public final synchronized void addMediaSource(
|
||||||
MediaSource mediaSource, @Nullable Runnable actionOnCompletion) {
|
MediaSource mediaSource, Handler handler, Runnable actionOnCompletion) {
|
||||||
addMediaSource(mediaSourcesPublic.size(), mediaSource, actionOnCompletion);
|
addMediaSource(mediaSourcesPublic.size(), mediaSource, handler, actionOnCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -164,7 +166,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* @param mediaSource The {@link MediaSource} to be added to the list.
|
* @param mediaSource The {@link MediaSource} to be added to the list.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSource(int index, MediaSource mediaSource) {
|
public final synchronized void addMediaSource(int index, MediaSource mediaSource) {
|
||||||
addMediaSource(index, mediaSource, null);
|
addPublicMediaSources(
|
||||||
|
index,
|
||||||
|
Collections.singletonList(mediaSource),
|
||||||
|
/* handler= */ null,
|
||||||
|
/* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,12 +179,14 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* @param index The index at which the new {@link MediaSource} will be inserted. This index must
|
* @param index The index at which the new {@link MediaSource} will be inserted. This index must
|
||||||
* be in the range of 0 <= index <= {@link #getSize()}.
|
* be in the range of 0 <= index <= {@link #getSize()}.
|
||||||
* @param mediaSource The {@link MediaSource} to be added to the list.
|
* @param mediaSource The {@link MediaSource} to be added to the list.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* source has been added to the playlist.
|
* source has been added to the playlist.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSource(
|
public final synchronized void addMediaSource(
|
||||||
int index, MediaSource mediaSource, @Nullable Runnable actionOnCompletion) {
|
int index, MediaSource mediaSource, Handler handler, Runnable actionOnCompletion) {
|
||||||
addMediaSources(index, Collections.singletonList(mediaSource), actionOnCompletion);
|
addPublicMediaSources(
|
||||||
|
index, Collections.singletonList(mediaSource), handler, actionOnCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -188,7 +196,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* sources are added in the order in which they appear in this collection.
|
* sources are added in the order in which they appear in this collection.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSources(Collection<MediaSource> mediaSources) {
|
public final synchronized void addMediaSources(Collection<MediaSource> mediaSources) {
|
||||||
addMediaSources(mediaSourcesPublic.size(), mediaSources, null);
|
addPublicMediaSources(
|
||||||
|
mediaSourcesPublic.size(),
|
||||||
|
mediaSources,
|
||||||
|
/* handler= */ null,
|
||||||
|
/* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -197,12 +209,13 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
*
|
*
|
||||||
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
|
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
|
||||||
* sources are added in the order in which they appear in this collection.
|
* sources are added in the order in which they appear in this collection.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* sources have been added to the playlist.
|
* sources have been added to the playlist.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSources(
|
public final synchronized void addMediaSources(
|
||||||
Collection<MediaSource> mediaSources, @Nullable Runnable actionOnCompletion) {
|
Collection<MediaSource> mediaSources, Handler handler, Runnable actionOnCompletion) {
|
||||||
addMediaSources(mediaSourcesPublic.size(), mediaSources, actionOnCompletion);
|
addPublicMediaSources(mediaSourcesPublic.size(), mediaSources, handler, actionOnCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -214,7 +227,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* sources are added in the order in which they appear in this collection.
|
* sources are added in the order in which they appear in this collection.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSources(int index, Collection<MediaSource> mediaSources) {
|
public final synchronized void addMediaSources(int index, Collection<MediaSource> mediaSources) {
|
||||||
addMediaSources(index, mediaSources, null);
|
addPublicMediaSources(index, mediaSources, /* handler= */ null, /* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,26 +237,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* be in the range of 0 <= index <= {@link #getSize()}.
|
* be in the range of 0 <= index <= {@link #getSize()}.
|
||||||
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
|
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
|
||||||
* sources are added in the order in which they appear in this collection.
|
* sources are added in the order in which they appear in this collection.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* sources have been added to the playlist.
|
* sources have been added to the playlist.
|
||||||
*/
|
*/
|
||||||
public final synchronized void addMediaSources(
|
public final synchronized void addMediaSources(
|
||||||
int index, Collection<MediaSource> mediaSources, @Nullable Runnable actionOnCompletion) {
|
int index,
|
||||||
for (MediaSource mediaSource : mediaSources) {
|
Collection<MediaSource> mediaSources,
|
||||||
Assertions.checkNotNull(mediaSource);
|
Handler handler,
|
||||||
}
|
Runnable actionOnCompletion) {
|
||||||
List<MediaSourceHolder> mediaSourceHolders = new ArrayList<>(mediaSources.size());
|
addPublicMediaSources(index, mediaSources, handler, actionOnCompletion);
|
||||||
for (MediaSource mediaSource : mediaSources) {
|
|
||||||
mediaSourceHolders.add(new MediaSourceHolder(mediaSource));
|
|
||||||
}
|
|
||||||
mediaSourcesPublic.addAll(index, mediaSourceHolders);
|
|
||||||
if (playbackThreadHandler != null && !mediaSources.isEmpty()) {
|
|
||||||
playbackThreadHandler
|
|
||||||
.obtainMessage(MSG_ADD, new MessageData<>(index, mediaSourceHolders, actionOnCompletion))
|
|
||||||
.sendToTarget();
|
|
||||||
} else if (actionOnCompletion != null) {
|
|
||||||
actionOnCompletion.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -259,26 +262,27 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* range of 0 <= index < {@link #getSize()}.
|
* range of 0 <= index < {@link #getSize()}.
|
||||||
*/
|
*/
|
||||||
public final synchronized void removeMediaSource(int index) {
|
public final synchronized void removeMediaSource(int index) {
|
||||||
removeMediaSource(index, null);
|
removePublicMediaSources(index, index + 1, /* handler= */ null, /* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a {@link MediaSource} from the playlist and executes a custom action on completion.
|
* Removes a {@link MediaSource} from the playlist and executes a custom action on completion.
|
||||||
*
|
*
|
||||||
* <p>Note: If you want to move the instance, it's preferable to use {@link #moveMediaSource(int,
|
* <p>Note: If you want to move the instance, it's preferable to use {@link #moveMediaSource(int,
|
||||||
* int, Runnable)} instead.
|
* int, Handler, Runnable)} instead.
|
||||||
*
|
*
|
||||||
* <p>Note: If you want to remove a set of contiguous sources, it's preferable to use {@link
|
* <p>Note: If you want to remove a set of contiguous sources, it's preferable to use {@link
|
||||||
* #removeMediaSourceRange(int, int, Runnable)} instead.
|
* #removeMediaSourceRange(int, int, Handler, Runnable)} instead.
|
||||||
*
|
*
|
||||||
* @param index The index at which the media source will be removed. This index must be in the
|
* @param index The index at which the media source will be removed. This index must be in the
|
||||||
* range of 0 <= index < {@link #getSize()}.
|
* range of 0 <= index < {@link #getSize()}.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* source has been removed from the playlist.
|
* source has been removed from the playlist.
|
||||||
*/
|
*/
|
||||||
public final synchronized void removeMediaSource(
|
public final synchronized void removeMediaSource(
|
||||||
int index, @Nullable Runnable actionOnCompletion) {
|
int index, Handler handler, Runnable actionOnCompletion) {
|
||||||
removeMediaSourceRange(index, index + 1, actionOnCompletion);
|
removePublicMediaSources(index, index + 1, handler, actionOnCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -296,7 +300,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
|
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
|
||||||
*/
|
*/
|
||||||
public final synchronized void removeMediaSourceRange(int fromIndex, int toIndex) {
|
public final synchronized void removeMediaSourceRange(int fromIndex, int toIndex) {
|
||||||
removeMediaSourceRange(fromIndex, toIndex, null);
|
removePublicMediaSources(
|
||||||
|
fromIndex, toIndex, /* handler= */ null, /* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -310,27 +315,15 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* removed. This index must be in the range of 0 <= index <= {@link #getSize()}.
|
* removed. This index must be in the range of 0 <= index <= {@link #getSize()}.
|
||||||
* @param toIndex The final range index, pointing to the first media source that will be left
|
* @param toIndex The final range index, pointing to the first media source that will be left
|
||||||
* untouched. This index must be in the range of 0 <= index <= {@link #getSize()}.
|
* untouched. This index must be in the range of 0 <= index <= {@link #getSize()}.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* source range has been removed from the playlist.
|
* source range has been removed from the playlist.
|
||||||
* @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} < 0,
|
* @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} < 0,
|
||||||
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
|
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
|
||||||
*/
|
*/
|
||||||
public final synchronized void removeMediaSourceRange(
|
public final synchronized void removeMediaSourceRange(
|
||||||
int fromIndex, int toIndex, @Nullable Runnable actionOnCompletion) {
|
int fromIndex, int toIndex, Handler handler, Runnable actionOnCompletion) {
|
||||||
Util.removeRange(mediaSourcesPublic, fromIndex, toIndex);
|
removePublicMediaSources(fromIndex, toIndex, handler, actionOnCompletion);
|
||||||
if (fromIndex == toIndex) {
|
|
||||||
if (actionOnCompletion != null) {
|
|
||||||
actionOnCompletion.run();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (playbackThreadHandler != null) {
|
|
||||||
playbackThreadHandler
|
|
||||||
.obtainMessage(MSG_REMOVE, new MessageData<>(fromIndex, toIndex, actionOnCompletion))
|
|
||||||
.sendToTarget();
|
|
||||||
} else if (actionOnCompletion != null) {
|
|
||||||
actionOnCompletion.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -342,7 +335,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* range of 0 <= index < {@link #getSize()}.
|
* range of 0 <= index < {@link #getSize()}.
|
||||||
*/
|
*/
|
||||||
public final synchronized void moveMediaSource(int currentIndex, int newIndex) {
|
public final synchronized void moveMediaSource(int currentIndex, int newIndex) {
|
||||||
moveMediaSource(currentIndex, newIndex, null);
|
movePublicMediaSource(
|
||||||
|
currentIndex, newIndex, /* handler= */ null, /* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -353,40 +347,29 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* in the range of 0 <= index < {@link #getSize()}.
|
* in the range of 0 <= index < {@link #getSize()}.
|
||||||
* @param newIndex The target index of the media source in the playlist. This index must be in the
|
* @param newIndex The target index of the media source in the playlist. This index must be in the
|
||||||
* range of 0 <= index < {@link #getSize()}.
|
* range of 0 <= index < {@link #getSize()}.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
|
||||||
* source has been moved.
|
* source has been moved.
|
||||||
*/
|
*/
|
||||||
public final synchronized void moveMediaSource(
|
public final synchronized void moveMediaSource(
|
||||||
int currentIndex, int newIndex, @Nullable Runnable actionOnCompletion) {
|
int currentIndex, int newIndex, Handler handler, Runnable actionOnCompletion) {
|
||||||
if (currentIndex == newIndex) {
|
movePublicMediaSource(currentIndex, newIndex, handler, actionOnCompletion);
|
||||||
if (actionOnCompletion != null) {
|
|
||||||
actionOnCompletion.run();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mediaSourcesPublic.add(newIndex, mediaSourcesPublic.remove(currentIndex));
|
|
||||||
if (playbackThreadHandler != null) {
|
|
||||||
playbackThreadHandler
|
|
||||||
.obtainMessage(MSG_MOVE, new MessageData<>(currentIndex, newIndex, actionOnCompletion))
|
|
||||||
.sendToTarget();
|
|
||||||
} else if (actionOnCompletion != null) {
|
|
||||||
actionOnCompletion.run();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears the playlist. */
|
/** Clears the playlist. */
|
||||||
public final synchronized void clear() {
|
public final synchronized void clear() {
|
||||||
clear(/* actionOnCompletion= */ null);
|
removeMediaSourceRange(0, getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the playlist and executes a custom action on completion.
|
* Clears the playlist and executes a custom action on completion.
|
||||||
*
|
*
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the playlist
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the playlist
|
||||||
* has been cleared.
|
* has been cleared.
|
||||||
*/
|
*/
|
||||||
public final synchronized void clear(@Nullable Runnable actionOnCompletion) {
|
public final synchronized void clear(Handler handler, Runnable actionOnCompletion) {
|
||||||
removeMediaSourceRange(0, getSize(), actionOnCompletion);
|
removeMediaSourceRange(0, getSize(), handler, actionOnCompletion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the number of media sources in the playlist. */
|
/** Returns the number of media sources in the playlist. */
|
||||||
@ -410,41 +393,24 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
* @param shuffleOrder A {@link ShuffleOrder}.
|
* @param shuffleOrder A {@link ShuffleOrder}.
|
||||||
*/
|
*/
|
||||||
public final synchronized void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
public final synchronized void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
||||||
setShuffleOrder(shuffleOrder, /* actionOnCompletion= */ null);
|
setPublicShuffleOrder(shuffleOrder, /* handler= */ null, /* actionOnCompletion= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a new shuffle order to use when shuffling the child media sources.
|
* Sets a new shuffle order to use when shuffling the child media sources.
|
||||||
*
|
*
|
||||||
* @param shuffleOrder A {@link ShuffleOrder}.
|
* @param shuffleOrder A {@link ShuffleOrder}.
|
||||||
|
* @param handler The {@link Handler} to run {@code actionOnCompletion}.
|
||||||
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the shuffle
|
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the shuffle
|
||||||
* order has been changed.
|
* order has been changed.
|
||||||
*/
|
*/
|
||||||
public final synchronized void setShuffleOrder(
|
public final synchronized void setShuffleOrder(
|
||||||
ShuffleOrder shuffleOrder, @Nullable Runnable actionOnCompletion) {
|
ShuffleOrder shuffleOrder, Handler handler, Runnable actionOnCompletion) {
|
||||||
Handler playbackThreadHandler = this.playbackThreadHandler;
|
setPublicShuffleOrder(shuffleOrder, handler, actionOnCompletion);
|
||||||
if (playbackThreadHandler != null) {
|
|
||||||
int size = getSize();
|
|
||||||
if (shuffleOrder.getLength() != size) {
|
|
||||||
shuffleOrder =
|
|
||||||
shuffleOrder
|
|
||||||
.cloneAndClear()
|
|
||||||
.cloneAndInsert(/* insertionIndex= */ 0, /* insertionCount= */ size);
|
|
||||||
}
|
|
||||||
playbackThreadHandler
|
|
||||||
.obtainMessage(
|
|
||||||
MSG_SET_SHUFFLE_ORDER,
|
|
||||||
new MessageData<>(/* index= */ 0, shuffleOrder, actionOnCompletion))
|
|
||||||
.sendToTarget();
|
|
||||||
} else {
|
|
||||||
this.shuffleOrder =
|
|
||||||
shuffleOrder.getLength() > 0 ? shuffleOrder.cloneAndClear() : shuffleOrder;
|
|
||||||
if (actionOnCompletion != null) {
|
|
||||||
actionOnCompletion.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompositeMediaSource implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Object getTag() {
|
public Object getTag() {
|
||||||
@ -458,13 +424,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
@Nullable TransferListener mediaTransferListener) {
|
@Nullable TransferListener mediaTransferListener) {
|
||||||
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
|
super.prepareSourceInternal(player, isTopLevelSource, mediaTransferListener);
|
||||||
playbackThreadHandler = new Handler(/* callback= */ this::handleMessage);
|
playbackThreadHandler = new Handler(/* callback= */ this::handleMessage);
|
||||||
applicationThreadHandler = new Handler(player.getApplicationLooper());
|
|
||||||
if (mediaSourcesPublic.isEmpty()) {
|
if (mediaSourcesPublic.isEmpty()) {
|
||||||
notifyListener();
|
notifyListener();
|
||||||
} else {
|
} else {
|
||||||
shuffleOrder = shuffleOrder.cloneAndInsert(0, mediaSourcesPublic.size());
|
shuffleOrder = shuffleOrder.cloneAndInsert(0, mediaSourcesPublic.size());
|
||||||
addMediaSourcesInternal(0, mediaSourcesPublic);
|
addMediaSourcesInternal(0, mediaSourcesPublic);
|
||||||
scheduleListenerNotification(/* actionOnCompletion= */ null);
|
scheduleListenerNotification();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,12 +474,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void releaseSourceInternal() {
|
public final synchronized void releaseSourceInternal() {
|
||||||
super.releaseSourceInternal();
|
super.releaseSourceInternal();
|
||||||
mediaSourceHolders.clear();
|
mediaSourceHolders.clear();
|
||||||
mediaSourceByUid.clear();
|
mediaSourceByUid.clear();
|
||||||
playbackThreadHandler = null;
|
playbackThreadHandler = null;
|
||||||
applicationThreadHandler = null;
|
|
||||||
shuffleOrder = shuffleOrder.cloneAndClear();
|
shuffleOrder = shuffleOrder.cloneAndClear();
|
||||||
windowCount = 0;
|
windowCount = 0;
|
||||||
periodCount = 0;
|
periodCount = 0;
|
||||||
@ -550,6 +514,98 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
return windowIndex + mediaSourceHolder.firstWindowIndexInChild;
|
return windowIndex + mediaSourceHolder.firstWindowIndexInChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal methods. Called from any thread.
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void addPublicMediaSources(
|
||||||
|
int index,
|
||||||
|
Collection<MediaSource> mediaSources,
|
||||||
|
@Nullable Handler handler,
|
||||||
|
@Nullable Runnable actionOnCompletion) {
|
||||||
|
Assertions.checkArgument((handler == null) == (actionOnCompletion == null));
|
||||||
|
for (MediaSource mediaSource : mediaSources) {
|
||||||
|
Assertions.checkNotNull(mediaSource);
|
||||||
|
}
|
||||||
|
List<MediaSourceHolder> mediaSourceHolders = new ArrayList<>(mediaSources.size());
|
||||||
|
for (MediaSource mediaSource : mediaSources) {
|
||||||
|
mediaSourceHolders.add(new MediaSourceHolder(mediaSource));
|
||||||
|
}
|
||||||
|
mediaSourcesPublic.addAll(index, mediaSourceHolders);
|
||||||
|
if (playbackThreadHandler != null && !mediaSources.isEmpty()) {
|
||||||
|
playbackThreadHandler
|
||||||
|
.obtainMessage(
|
||||||
|
MSG_ADD, new MessageData<>(index, mediaSourceHolders, handler, actionOnCompletion))
|
||||||
|
.sendToTarget();
|
||||||
|
} else if (actionOnCompletion != null && handler != null) {
|
||||||
|
handler.post(actionOnCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void removePublicMediaSources(
|
||||||
|
int fromIndex,
|
||||||
|
int toIndex,
|
||||||
|
@Nullable Handler handler,
|
||||||
|
@Nullable Runnable actionOnCompletion) {
|
||||||
|
Assertions.checkArgument((handler == null) == (actionOnCompletion == null));
|
||||||
|
Util.removeRange(mediaSourcesPublic, fromIndex, toIndex);
|
||||||
|
if (playbackThreadHandler != null) {
|
||||||
|
playbackThreadHandler
|
||||||
|
.obtainMessage(
|
||||||
|
MSG_REMOVE, new MessageData<>(fromIndex, toIndex, handler, actionOnCompletion))
|
||||||
|
.sendToTarget();
|
||||||
|
} else if (actionOnCompletion != null && handler != null) {
|
||||||
|
handler.post(actionOnCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void movePublicMediaSource(
|
||||||
|
int currentIndex,
|
||||||
|
int newIndex,
|
||||||
|
@Nullable Handler handler,
|
||||||
|
@Nullable Runnable actionOnCompletion) {
|
||||||
|
Assertions.checkArgument((handler == null) == (actionOnCompletion == null));
|
||||||
|
mediaSourcesPublic.add(newIndex, mediaSourcesPublic.remove(currentIndex));
|
||||||
|
if (playbackThreadHandler != null) {
|
||||||
|
playbackThreadHandler
|
||||||
|
.obtainMessage(
|
||||||
|
MSG_MOVE, new MessageData<>(currentIndex, newIndex, handler, actionOnCompletion))
|
||||||
|
.sendToTarget();
|
||||||
|
} else if (actionOnCompletion != null && handler != null) {
|
||||||
|
handler.post(actionOnCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@GuardedBy("this")
|
||||||
|
private void setPublicShuffleOrder(
|
||||||
|
ShuffleOrder shuffleOrder, @Nullable Handler handler, @Nullable Runnable actionOnCompletion) {
|
||||||
|
Assertions.checkArgument((handler == null) == (actionOnCompletion == null));
|
||||||
|
Handler playbackThreadHandler = this.playbackThreadHandler;
|
||||||
|
if (playbackThreadHandler != null) {
|
||||||
|
int size = getSize();
|
||||||
|
if (shuffleOrder.getLength() != size) {
|
||||||
|
shuffleOrder =
|
||||||
|
shuffleOrder
|
||||||
|
.cloneAndClear()
|
||||||
|
.cloneAndInsert(/* insertionIndex= */ 0, /* insertionCount= */ size);
|
||||||
|
}
|
||||||
|
playbackThreadHandler
|
||||||
|
.obtainMessage(
|
||||||
|
MSG_SET_SHUFFLE_ORDER,
|
||||||
|
new MessageData<>(/* index= */ 0, shuffleOrder, handler, actionOnCompletion))
|
||||||
|
.sendToTarget();
|
||||||
|
} else {
|
||||||
|
this.shuffleOrder =
|
||||||
|
shuffleOrder.getLength() > 0 ? shuffleOrder.cloneAndClear() : shuffleOrder;
|
||||||
|
if (actionOnCompletion != null && handler != null) {
|
||||||
|
handler.post(actionOnCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal methods. Called on the playback thread.
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private boolean handleMessage(Message msg) {
|
private boolean handleMessage(Message msg) {
|
||||||
if (playbackThreadHandler == null) {
|
if (playbackThreadHandler == null) {
|
||||||
@ -562,7 +618,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
(MessageData<Collection<MediaSourceHolder>>) Util.castNonNull(msg.obj);
|
(MessageData<Collection<MediaSourceHolder>>) Util.castNonNull(msg.obj);
|
||||||
shuffleOrder = shuffleOrder.cloneAndInsert(addMessage.index, addMessage.customData.size());
|
shuffleOrder = shuffleOrder.cloneAndInsert(addMessage.index, addMessage.customData.size());
|
||||||
addMediaSourcesInternal(addMessage.index, addMessage.customData);
|
addMediaSourcesInternal(addMessage.index, addMessage.customData);
|
||||||
scheduleListenerNotification(addMessage.actionOnCompletion);
|
scheduleListenerNotification(addMessage.handler, addMessage.actionOnCompletion);
|
||||||
break;
|
break;
|
||||||
case MSG_REMOVE:
|
case MSG_REMOVE:
|
||||||
MessageData<Integer> removeMessage = (MessageData<Integer>) Util.castNonNull(msg.obj);
|
MessageData<Integer> removeMessage = (MessageData<Integer>) Util.castNonNull(msg.obj);
|
||||||
@ -576,30 +632,29 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
for (int index = toIndex - 1; index >= fromIndex; index--) {
|
for (int index = toIndex - 1; index >= fromIndex; index--) {
|
||||||
removeMediaSourceInternal(index);
|
removeMediaSourceInternal(index);
|
||||||
}
|
}
|
||||||
scheduleListenerNotification(removeMessage.actionOnCompletion);
|
scheduleListenerNotification(removeMessage.handler, removeMessage.actionOnCompletion);
|
||||||
break;
|
break;
|
||||||
case MSG_MOVE:
|
case MSG_MOVE:
|
||||||
MessageData<Integer> moveMessage = (MessageData<Integer>) Util.castNonNull(msg.obj);
|
MessageData<Integer> moveMessage = (MessageData<Integer>) Util.castNonNull(msg.obj);
|
||||||
shuffleOrder = shuffleOrder.cloneAndRemove(moveMessage.index, moveMessage.index + 1);
|
shuffleOrder = shuffleOrder.cloneAndRemove(moveMessage.index, moveMessage.index + 1);
|
||||||
shuffleOrder = shuffleOrder.cloneAndInsert(moveMessage.customData, 1);
|
shuffleOrder = shuffleOrder.cloneAndInsert(moveMessage.customData, 1);
|
||||||
moveMediaSourceInternal(moveMessage.index, moveMessage.customData);
|
moveMediaSourceInternal(moveMessage.index, moveMessage.customData);
|
||||||
scheduleListenerNotification(moveMessage.actionOnCompletion);
|
scheduleListenerNotification(moveMessage.handler, moveMessage.actionOnCompletion);
|
||||||
break;
|
break;
|
||||||
case MSG_SET_SHUFFLE_ORDER:
|
case MSG_SET_SHUFFLE_ORDER:
|
||||||
MessageData<ShuffleOrder> shuffleOrderMessage =
|
MessageData<ShuffleOrder> shuffleOrderMessage =
|
||||||
(MessageData<ShuffleOrder>) Util.castNonNull(msg.obj);
|
(MessageData<ShuffleOrder>) Util.castNonNull(msg.obj);
|
||||||
shuffleOrder = shuffleOrderMessage.customData;
|
shuffleOrder = shuffleOrderMessage.customData;
|
||||||
scheduleListenerNotification(shuffleOrderMessage.actionOnCompletion);
|
scheduleListenerNotification(
|
||||||
|
shuffleOrderMessage.handler, shuffleOrderMessage.actionOnCompletion);
|
||||||
break;
|
break;
|
||||||
case MSG_NOTIFY_LISTENER:
|
case MSG_NOTIFY_LISTENER:
|
||||||
notifyListener();
|
notifyListener();
|
||||||
break;
|
break;
|
||||||
case MSG_ON_COMPLETION:
|
case MSG_ON_COMPLETION:
|
||||||
List<Runnable> actionsOnCompletion = (List<Runnable>) Util.castNonNull(msg.obj);
|
EventDispatcher<Runnable> actionsOnCompletion =
|
||||||
Handler handler = Assertions.checkNotNull(applicationThreadHandler);
|
(EventDispatcher<Runnable>) Util.castNonNull(msg.obj);
|
||||||
for (int i = 0; i < actionsOnCompletion.size(); i++) {
|
actionsOnCompletion.dispatch(Runnable::run);
|
||||||
handler.post(actionsOnCompletion.get(i));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@ -607,34 +662,34 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleListenerNotification(@Nullable Runnable actionOnCompletion) {
|
private void scheduleListenerNotification() {
|
||||||
|
scheduleListenerNotification(/* handler= */ null, /* actionOnCompletion= */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleListenerNotification(
|
||||||
|
@Nullable Handler handler, @Nullable Runnable actionOnCompletion) {
|
||||||
if (!listenerNotificationScheduled) {
|
if (!listenerNotificationScheduled) {
|
||||||
Assertions.checkNotNull(playbackThreadHandler)
|
Assertions.checkNotNull(playbackThreadHandler)
|
||||||
.obtainMessage(MSG_NOTIFY_LISTENER)
|
.obtainMessage(MSG_NOTIFY_LISTENER)
|
||||||
.sendToTarget();
|
.sendToTarget();
|
||||||
listenerNotificationScheduled = true;
|
listenerNotificationScheduled = true;
|
||||||
}
|
}
|
||||||
if (actionOnCompletion != null) {
|
if (actionOnCompletion != null && handler != null) {
|
||||||
pendingOnCompletionActions.add(actionOnCompletion);
|
pendingOnCompletionActions.addListener(handler, actionOnCompletion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListener() {
|
private void notifyListener() {
|
||||||
listenerNotificationScheduled = false;
|
listenerNotificationScheduled = false;
|
||||||
List<Runnable> actionsOnCompletion =
|
EventDispatcher<Runnable> actionsOnCompletion = pendingOnCompletionActions;
|
||||||
pendingOnCompletionActions.isEmpty()
|
pendingOnCompletionActions = new EventDispatcher<>();
|
||||||
? Collections.emptyList()
|
|
||||||
: new ArrayList<>(pendingOnCompletionActions);
|
|
||||||
pendingOnCompletionActions.clear();
|
|
||||||
refreshSourceInfo(
|
refreshSourceInfo(
|
||||||
new ConcatenatedTimeline(
|
new ConcatenatedTimeline(
|
||||||
mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic),
|
mediaSourceHolders, windowCount, periodCount, shuffleOrder, isAtomic),
|
||||||
/* manifest= */ null);
|
/* manifest= */ null);
|
||||||
if (!actionsOnCompletion.isEmpty()) {
|
Assertions.checkNotNull(playbackThreadHandler)
|
||||||
Assertions.checkNotNull(playbackThreadHandler)
|
.obtainMessage(MSG_ON_COMPLETION, actionsOnCompletion)
|
||||||
.obtainMessage(MSG_ON_COMPLETION, actionsOnCompletion)
|
.sendToTarget();
|
||||||
.sendToTarget();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMediaSourcesInternal(
|
private void addMediaSourcesInternal(
|
||||||
@ -733,7 +788,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaSourceHolder.isPrepared = true;
|
mediaSourceHolder.isPrepared = true;
|
||||||
scheduleListenerNotification(/* actionOnCompletion= */ null);
|
scheduleListenerNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeMediaSourceInternal(int index) {
|
private void removeMediaSourceInternal(int index) {
|
||||||
@ -846,12 +901,15 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
|
|||||||
|
|
||||||
public final int index;
|
public final int index;
|
||||||
public final T customData;
|
public final T customData;
|
||||||
public final @Nullable Runnable actionOnCompletion;
|
@Nullable public final Handler handler;
|
||||||
|
@Nullable public final Runnable actionOnCompletion;
|
||||||
|
|
||||||
public MessageData(int index, T customData, @Nullable Runnable actionOnCompletion) {
|
public MessageData(
|
||||||
|
int index, T customData, @Nullable Handler handler, @Nullable Runnable actionOnCompletion) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.actionOnCompletion = actionOnCompletion;
|
|
||||||
this.customData = customData;
|
this.customData = customData;
|
||||||
|
this.handler = handler;
|
||||||
|
this.actionOnCompletion = actionOnCompletion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import static org.junit.Assert.fail;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import android.os.ConditionVariable;
|
import android.os.ConditionVariable;
|
||||||
|
import android.os.Handler;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
@ -417,7 +418,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
public void testCustomCallbackBeforePreparationAddSingle() {
|
public void testCustomCallbackBeforePreparationAddSingle() {
|
||||||
Runnable runnable = Mockito.mock(Runnable.class);
|
Runnable runnable = Mockito.mock(Runnable.class);
|
||||||
|
|
||||||
mediaSource.addMediaSource(createFakeMediaSource(), runnable);
|
mediaSource.addMediaSource(createFakeMediaSource(), new Handler(), runnable);
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,6 +428,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
|
|
||||||
mediaSource.addMediaSources(
|
mediaSource.addMediaSources(
|
||||||
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
||||||
|
new Handler(),
|
||||||
runnable);
|
runnable);
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
@ -435,7 +437,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
public void testCustomCallbackBeforePreparationAddSingleWithIndex() {
|
public void testCustomCallbackBeforePreparationAddSingleWithIndex() {
|
||||||
Runnable runnable = Mockito.mock(Runnable.class);
|
Runnable runnable = Mockito.mock(Runnable.class);
|
||||||
|
|
||||||
mediaSource.addMediaSource(/* index */ 0, createFakeMediaSource(), runnable);
|
mediaSource.addMediaSource(/* index */ 0, createFakeMediaSource(), new Handler(), runnable);
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,6 +448,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
mediaSource.addMediaSources(
|
mediaSource.addMediaSources(
|
||||||
/* index */ 0,
|
/* index */ 0,
|
||||||
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
||||||
|
new Handler(),
|
||||||
runnable);
|
runnable);
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
@ -455,7 +458,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
Runnable runnable = Mockito.mock(Runnable.class);
|
Runnable runnable = Mockito.mock(Runnable.class);
|
||||||
|
|
||||||
mediaSource.addMediaSource(createFakeMediaSource());
|
mediaSource.addMediaSource(createFakeMediaSource());
|
||||||
mediaSource.removeMediaSource(/* index */ 0, runnable);
|
mediaSource.removeMediaSource(/* index */ 0, new Handler(), runnable);
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +468,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
|
|
||||||
mediaSource.addMediaSources(
|
mediaSource.addMediaSources(
|
||||||
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}));
|
Arrays.asList(new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}));
|
||||||
mediaSource.moveMediaSource(/* fromIndex */ 1, /* toIndex */ 0, runnable);
|
mediaSource.moveMediaSource(/* fromIndex */ 1, /* toIndex */ 0, new Handler(), runnable);
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,7 +479,8 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
testRunner.prepareSource();
|
testRunner.prepareSource();
|
||||||
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
() -> mediaSource.addMediaSource(createFakeMediaSource(), timelineGrabber));
|
() ->
|
||||||
|
mediaSource.addMediaSource(createFakeMediaSource(), new Handler(), timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getWindowCount()).isEqualTo(1);
|
assertThat(timeline.getWindowCount()).isEqualTo(1);
|
||||||
} finally {
|
} finally {
|
||||||
@ -495,6 +499,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
mediaSource.addMediaSources(
|
mediaSource.addMediaSources(
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
||||||
|
new Handler(),
|
||||||
timelineGrabber));
|
timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getWindowCount()).isEqualTo(2);
|
assertThat(timeline.getWindowCount()).isEqualTo(2);
|
||||||
@ -511,7 +516,8 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
() ->
|
() ->
|
||||||
mediaSource.addMediaSource(/* index */ 0, createFakeMediaSource(), timelineGrabber));
|
mediaSource.addMediaSource(
|
||||||
|
/* index */ 0, createFakeMediaSource(), new Handler(), timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getWindowCount()).isEqualTo(1);
|
assertThat(timeline.getWindowCount()).isEqualTo(1);
|
||||||
} finally {
|
} finally {
|
||||||
@ -531,6 +537,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
/* index */ 0,
|
/* index */ 0,
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
new MediaSource[] {createFakeMediaSource(), createFakeMediaSource()}),
|
||||||
|
new Handler(),
|
||||||
timelineGrabber));
|
timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getWindowCount()).isEqualTo(2);
|
assertThat(timeline.getWindowCount()).isEqualTo(2);
|
||||||
@ -549,7 +556,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
|
|
||||||
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
() -> mediaSource.removeMediaSource(/* index */ 0, timelineGrabber));
|
() -> mediaSource.removeMediaSource(/* index */ 0, new Handler(), timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getWindowCount()).isEqualTo(0);
|
assertThat(timeline.getWindowCount()).isEqualTo(0);
|
||||||
} finally {
|
} finally {
|
||||||
@ -571,7 +578,9 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
|
|
||||||
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
() -> mediaSource.moveMediaSource(/* fromIndex */ 1, /* toIndex */ 0, timelineGrabber));
|
() ->
|
||||||
|
mediaSource.moveMediaSource(
|
||||||
|
/* fromIndex */ 1, /* toIndex */ 0, new Handler(), timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getWindowCount()).isEqualTo(2);
|
assertThat(timeline.getWindowCount()).isEqualTo(2);
|
||||||
} finally {
|
} finally {
|
||||||
@ -819,7 +828,7 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
testRunner.prepareSource();
|
testRunner.prepareSource();
|
||||||
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
final TimelineGrabber timelineGrabber = new TimelineGrabber(testRunner);
|
||||||
|
|
||||||
dummyMainThread.runOnMainThread(() -> mediaSource.clear(timelineGrabber));
|
dummyMainThread.runOnMainThread(() -> mediaSource.clear(new Handler(), timelineGrabber));
|
||||||
|
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.isEmpty()).isTrue();
|
assertThat(timeline.isEmpty()).isTrue();
|
||||||
@ -965,7 +974,8 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCustomCallbackBeforePreparationSetShuffleOrder() throws Exception {
|
public void testCustomCallbackBeforePreparationSetShuffleOrder() throws Exception {
|
||||||
Runnable runnable = Mockito.mock(Runnable.class);
|
Runnable runnable = Mockito.mock(Runnable.class);
|
||||||
mediaSource.setShuffleOrder(new ShuffleOrder.UnshuffledShuffleOrder(/* length= */ 0), runnable);
|
mediaSource.setShuffleOrder(
|
||||||
|
new ShuffleOrder.UnshuffledShuffleOrder(/* length= */ 0), new Handler(), runnable);
|
||||||
|
|
||||||
verify(runnable).run();
|
verify(runnable).run();
|
||||||
}
|
}
|
||||||
@ -981,7 +991,9 @@ public final class ConcatenatingMediaSourceTest {
|
|||||||
dummyMainThread.runOnMainThread(
|
dummyMainThread.runOnMainThread(
|
||||||
() ->
|
() ->
|
||||||
mediaSource.setShuffleOrder(
|
mediaSource.setShuffleOrder(
|
||||||
new ShuffleOrder.UnshuffledShuffleOrder(/* length= */ 3), timelineGrabber));
|
new ShuffleOrder.UnshuffledShuffleOrder(/* length= */ 3),
|
||||||
|
new Handler(),
|
||||||
|
timelineGrabber));
|
||||||
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
Timeline timeline = timelineGrabber.assertTimelineChangeBlocking();
|
||||||
assertThat(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true)).isEqualTo(0);
|
assertThat(timeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true)).isEqualTo(0);
|
||||||
} finally {
|
} finally {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user