Add CompositeMediaSource to handle common tasks for composite media sources.
This removes some boiler-plate code for compostite sources and will also simplify resuing media source in the future (because this class can keep track of child listeners). Issue:#3498 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=183387123
This commit is contained in:
parent
ea21f72c62
commit
46c4ca7ddb
@ -20,12 +20,12 @@ import android.support.annotation.Nullable;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.source.CompositeMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
import com.google.android.exoplayer2.source.ads.AdsMediaSource;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MediaSource} that inserts ads linearly with a provided content media source.
|
* A {@link MediaSource} that inserts ads linearly with a provided content media source.
|
||||||
@ -33,9 +33,10 @@ import java.io.IOException;
|
|||||||
* @deprecated Use com.google.android.exoplayer2.source.ads.AdsMediaSource with ImaAdsLoader.
|
* @deprecated Use com.google.android.exoplayer2.source.ads.AdsMediaSource with ImaAdsLoader.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public final class ImaAdsMediaSource implements MediaSource {
|
public final class ImaAdsMediaSource extends CompositeMediaSource<Void> {
|
||||||
|
|
||||||
private final AdsMediaSource adsMediaSource;
|
private final AdsMediaSource adsMediaSource;
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new source that inserts ads linearly with the content specified by
|
* Constructs a new source that inserts ads linearly with the content specified by
|
||||||
@ -74,20 +75,10 @@ public final class ImaAdsMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareSource(final ExoPlayer player, boolean isTopLevelSource,
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
final Listener listener) {
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
adsMediaSource.prepareSource(player, false, new Listener() {
|
this.listener = listener;
|
||||||
@Override
|
prepareChildSource(/* id= */ null, adsMediaSource);
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline,
|
|
||||||
@Nullable Object manifest) {
|
|
||||||
listener.onSourceInfoRefreshed(ImaAdsMediaSource.this, timeline, manifest);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
|
||||||
adsMediaSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -101,8 +92,8 @@ public final class ImaAdsMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
protected void onChildSourceInfoRefreshed(
|
||||||
adsMediaSource.releaseSource();
|
Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
|
||||||
|
listener.onSourceInfoRefreshed(this, timeline, manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,10 @@ public final class ClippingMediaSourceTest extends InstrumentationTestCase {
|
|||||||
ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, startMs, endMs);
|
ClippingMediaSource mediaSource = new ClippingMediaSource(fakeMediaSource, startMs, endMs);
|
||||||
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
||||||
try {
|
try {
|
||||||
return testRunner.prepareSource();
|
Timeline clippedTimeline = testRunner.prepareSource();
|
||||||
|
testRunner.releaseSource();
|
||||||
|
fakeMediaSource.assertReleased();
|
||||||
|
return clippedTimeline;
|
||||||
} finally {
|
} finally {
|
||||||
testRunner.release();
|
testRunner.release();
|
||||||
}
|
}
|
||||||
|
@ -243,7 +243,7 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
|
|||||||
*/
|
*/
|
||||||
private static Timeline getConcatenatedTimeline(boolean isRepeatOneAtomic,
|
private static Timeline getConcatenatedTimeline(boolean isRepeatOneAtomic,
|
||||||
Timeline... timelines) throws IOException {
|
Timeline... timelines) throws IOException {
|
||||||
MediaSource[] mediaSources = new MediaSource[timelines.length];
|
FakeMediaSource[] mediaSources = new FakeMediaSource[timelines.length];
|
||||||
for (int i = 0; i < timelines.length; i++) {
|
for (int i = 0; i < timelines.length; i++) {
|
||||||
mediaSources[i] = new FakeMediaSource(timelines[i], null);
|
mediaSources[i] = new FakeMediaSource(timelines[i], null);
|
||||||
}
|
}
|
||||||
@ -251,7 +251,12 @@ public final class ConcatenatingMediaSourceTest extends TestCase {
|
|||||||
new FakeShuffleOrder(mediaSources.length), mediaSources);
|
new FakeShuffleOrder(mediaSources.length), mediaSources);
|
||||||
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
||||||
try {
|
try {
|
||||||
return testRunner.prepareSource();
|
Timeline timeline = testRunner.prepareSource();
|
||||||
|
testRunner.releaseSource();
|
||||||
|
for (int i = 0; i < mediaSources.length; i++) {
|
||||||
|
mediaSources[i].assertReleased();
|
||||||
|
}
|
||||||
|
return timeline;
|
||||||
} finally {
|
} finally {
|
||||||
testRunner.release();
|
testRunner.release();
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,10 @@ public class LoopingMediaSourceTest extends TestCase {
|
|||||||
LoopingMediaSource mediaSource = new LoopingMediaSource(fakeMediaSource, loopCount);
|
LoopingMediaSource mediaSource = new LoopingMediaSource(fakeMediaSource, loopCount);
|
||||||
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
MediaSourceTestRunner testRunner = new MediaSourceTestRunner(mediaSource, null);
|
||||||
try {
|
try {
|
||||||
return testRunner.prepareSource();
|
Timeline loopingTimeline = testRunner.prepareSource();
|
||||||
|
testRunner.releaseSource();
|
||||||
|
fakeMediaSource.assertReleased();
|
||||||
|
return loopingTimeline;
|
||||||
} finally {
|
} finally {
|
||||||
testRunner.release();
|
testRunner.release();
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ public class MergingMediaSourceTest extends TestCase {
|
|||||||
* forwards the first of the wrapped timelines.
|
* forwards the first of the wrapped timelines.
|
||||||
*/
|
*/
|
||||||
private static void testMergingMediaSourcePrepare(Timeline... timelines) throws IOException {
|
private static void testMergingMediaSourcePrepare(Timeline... timelines) throws IOException {
|
||||||
MediaSource[] mediaSources = new MediaSource[timelines.length];
|
FakeMediaSource[] mediaSources = new FakeMediaSource[timelines.length];
|
||||||
for (int i = 0; i < timelines.length; i++) {
|
for (int i = 0; i < timelines.length; i++) {
|
||||||
mediaSources[i] = new FakeMediaSource(timelines[i], null);
|
mediaSources[i] = new FakeMediaSource(timelines[i], null);
|
||||||
}
|
}
|
||||||
@ -74,6 +74,10 @@ public class MergingMediaSourceTest extends TestCase {
|
|||||||
Timeline timeline = testRunner.prepareSource();
|
Timeline timeline = testRunner.prepareSource();
|
||||||
// The merged timeline should always be the one from the first child.
|
// The merged timeline should always be the one from the first child.
|
||||||
assertThat(timeline).isEqualTo(timelines[0]);
|
assertThat(timeline).isEqualTo(timelines[0]);
|
||||||
|
testRunner.releaseSource();
|
||||||
|
for (int i = 0; i < mediaSources.length; i++) {
|
||||||
|
mediaSources[i].assertReleased();
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
testRunner.release();
|
testRunner.release();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.google.android.exoplayer2.source;
|
package com.google.android.exoplayer2.source;
|
||||||
|
|
||||||
import android.support.annotation.IntDef;
|
import android.support.annotation.IntDef;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
@ -31,7 +32,7 @@ import java.util.ArrayList;
|
|||||||
* positions. The wrapped source must consist of a single period that starts at the beginning of the
|
* positions. The wrapped source must consist of a single period that starts at the beginning of the
|
||||||
* corresponding window.
|
* corresponding window.
|
||||||
*/
|
*/
|
||||||
public final class ClippingMediaSource implements MediaSource, MediaSource.Listener {
|
public final class ClippingMediaSource extends CompositeMediaSource<Void> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a {@link ClippingMediaSource} cannot clip its wrapped source.
|
* Thrown when a {@link ClippingMediaSource} cannot clip its wrapped source.
|
||||||
@ -131,9 +132,10 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
Assertions.checkState(sourceListener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
Assertions.checkState(sourceListener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
||||||
sourceListener = listener;
|
sourceListener = listener;
|
||||||
mediaSource.prepareSource(player, false, this);
|
prepareChildSource(/* id= */ null, mediaSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -141,7 +143,7 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
|
|||||||
if (clippingError != null) {
|
if (clippingError != null) {
|
||||||
throw clippingError;
|
throw clippingError;
|
||||||
}
|
}
|
||||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
super.maybeThrowSourceInfoRefreshError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -160,14 +162,8 @@ public final class ClippingMediaSource implements MediaSource, MediaSource.Liste
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
protected void onChildSourceInfoRefreshed(
|
||||||
mediaSource.releaseSource();
|
Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
|
||||||
}
|
|
||||||
|
|
||||||
// MediaSource.Listener implementation.
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
|
|
||||||
if (clippingError != null) {
|
if (clippingError != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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.support.annotation.CallSuper;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composite {@link MediaSource} consisting of multiple child sources.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the id used to identify prepared child sources.
|
||||||
|
*/
|
||||||
|
public abstract class CompositeMediaSource<T> implements MediaSource {
|
||||||
|
|
||||||
|
private final HashMap<T, MediaSource> childSources;
|
||||||
|
private ExoPlayer player;
|
||||||
|
|
||||||
|
/** Create composite media source without child sources. */
|
||||||
|
protected CompositeMediaSource() {
|
||||||
|
childSources = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||||
|
for (MediaSource childSource : childSources.values()) {
|
||||||
|
childSource.maybeThrowSourceInfoRefreshError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@CallSuper
|
||||||
|
public void releaseSource() {
|
||||||
|
for (MediaSource childSource : childSources.values()) {
|
||||||
|
childSource.releaseSource();
|
||||||
|
}
|
||||||
|
childSources.clear();
|
||||||
|
player = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the source info of a child source has been refreshed.
|
||||||
|
*
|
||||||
|
* @param id The unique id used to prepare the child source.
|
||||||
|
* @param mediaSource The child source whose source info has been refreshed.
|
||||||
|
* @param timeline The timeline of the child source.
|
||||||
|
* @param manifest The manifest of the child source.
|
||||||
|
*/
|
||||||
|
protected abstract void onChildSourceInfoRefreshed(
|
||||||
|
@Nullable T id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares a child source.
|
||||||
|
*
|
||||||
|
* <p>{@link #onChildSourceInfoRefreshed(Object, MediaSource, Timeline, Object)} will be called
|
||||||
|
* when the child source updates its timeline and/or manifest with the same {@code id} passed to
|
||||||
|
* this method.
|
||||||
|
*
|
||||||
|
* <p>Any child sources that aren't explicitly released with {@link #releaseChildSource(Object)}
|
||||||
|
* will be released in {@link #releaseSource()}.
|
||||||
|
*
|
||||||
|
* @param id A unique id to identify the child source preparation. Null is allowed as an id.
|
||||||
|
* @param mediaSource The child {@link MediaSource}.
|
||||||
|
*/
|
||||||
|
protected void prepareChildSource(@Nullable final T id, final MediaSource mediaSource) {
|
||||||
|
Assertions.checkArgument(!childSources.containsKey(id));
|
||||||
|
childSources.put(id, mediaSource);
|
||||||
|
mediaSource.prepareSource(
|
||||||
|
player,
|
||||||
|
/* isTopLevelSource= */ false,
|
||||||
|
new Listener() {
|
||||||
|
@Override
|
||||||
|
public void onSourceInfoRefreshed(
|
||||||
|
MediaSource source, Timeline timeline, @Nullable Object manifest) {
|
||||||
|
onChildSourceInfoRefreshed(id, mediaSource, timeline, manifest);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases a child source.
|
||||||
|
*
|
||||||
|
* @param id The unique id used to prepare the child source.
|
||||||
|
*/
|
||||||
|
protected void releaseChildSource(@Nullable T id) {
|
||||||
|
MediaSource removedChild = childSources.remove(id);
|
||||||
|
removedChild.releaseSource();
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source;
|
package com.google.android.exoplayer2.source;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
@ -23,7 +24,6 @@ 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.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -32,13 +32,12 @@ import java.util.Map;
|
|||||||
* Concatenates multiple {@link MediaSource}s. It is valid for the same {@link MediaSource} instance
|
* Concatenates multiple {@link MediaSource}s. It is valid for the same {@link MediaSource} instance
|
||||||
* to be present more than once in the concatenation.
|
* to be present more than once in the concatenation.
|
||||||
*/
|
*/
|
||||||
public final class ConcatenatingMediaSource implements MediaSource {
|
public final class ConcatenatingMediaSource extends CompositeMediaSource<Integer> {
|
||||||
|
|
||||||
private final MediaSource[] mediaSources;
|
private final MediaSource[] mediaSources;
|
||||||
private final Timeline[] timelines;
|
private final Timeline[] timelines;
|
||||||
private final Object[] manifests;
|
private final Object[] manifests;
|
||||||
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
|
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
|
||||||
private final boolean[] duplicateFlags;
|
|
||||||
private final boolean isAtomic;
|
private final boolean isAtomic;
|
||||||
private final ShuffleOrder shuffleOrder;
|
private final ShuffleOrder shuffleOrder;
|
||||||
|
|
||||||
@ -84,36 +83,21 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||||||
timelines = new Timeline[mediaSources.length];
|
timelines = new Timeline[mediaSources.length];
|
||||||
manifests = new Object[mediaSources.length];
|
manifests = new Object[mediaSources.length];
|
||||||
sourceIndexByMediaPeriod = new HashMap<>();
|
sourceIndexByMediaPeriod = new HashMap<>();
|
||||||
duplicateFlags = buildDuplicateFlags(mediaSources);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
boolean[] duplicateFlags = buildDuplicateFlags(mediaSources);
|
||||||
if (mediaSources.length == 0) {
|
if (mediaSources.length == 0) {
|
||||||
listener.onSourceInfoRefreshed(this, Timeline.EMPTY, null);
|
listener.onSourceInfoRefreshed(this, Timeline.EMPTY, null);
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < mediaSources.length; i++) {
|
for (int i = 0; i < mediaSources.length; i++) {
|
||||||
if (!duplicateFlags[i]) {
|
if (!duplicateFlags[i]) {
|
||||||
final int index = i;
|
prepareChildSource(i, mediaSources[i]);
|
||||||
mediaSources[i].prepareSource(player, false, new Listener() {
|
|
||||||
@Override
|
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline,
|
|
||||||
Object manifest) {
|
|
||||||
handleSourceInfoRefreshed(index, timeline, manifest);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
|
||||||
for (int i = 0; i < mediaSources.length; i++) {
|
|
||||||
if (!duplicateFlags[i]) {
|
|
||||||
mediaSources[i].maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,22 +120,17 @@ public final class ConcatenatingMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
protected void onChildSourceInfoRefreshed(
|
||||||
for (int i = 0; i < mediaSources.length; i++) {
|
Integer sourceFirstIndex,
|
||||||
if (!duplicateFlags[i]) {
|
MediaSource mediaSource,
|
||||||
mediaSources[i].releaseSource();
|
Timeline sourceTimeline,
|
||||||
}
|
@Nullable Object sourceManifest) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
|
|
||||||
Object sourceManifest) {
|
|
||||||
// Set the timeline and manifest.
|
// Set the timeline and manifest.
|
||||||
timelines[sourceFirstIndex] = sourceTimeline;
|
timelines[sourceFirstIndex] = sourceTimeline;
|
||||||
manifests[sourceFirstIndex] = sourceManifest;
|
manifests[sourceFirstIndex] = sourceManifest;
|
||||||
// Also set the timeline and manifest for any duplicate entries of the same source.
|
// Also set the timeline and manifest for any duplicate entries of the same source.
|
||||||
for (int i = sourceFirstIndex + 1; i < mediaSources.length; i++) {
|
for (int i = sourceFirstIndex + 1; i < mediaSources.length; i++) {
|
||||||
if (mediaSources[i] == mediaSources[sourceFirstIndex]) {
|
if (mediaSources[i] == mediaSource) {
|
||||||
timelines[i] = sourceTimeline;
|
timelines[i] = sourceTimeline;
|
||||||
manifests[i] = sourceManifest;
|
manifests[i] = sourceManifest;
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,11 @@ import com.google.android.exoplayer2.ExoPlaybackException;
|
|||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.PlayerMessage;
|
import com.google.android.exoplayer2.PlayerMessage;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource.MediaSourceHolder;
|
||||||
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
|
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.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -41,7 +41,8 @@ import java.util.Map;
|
|||||||
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
|
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
|
||||||
* during playback. Access to this class is thread-safe.
|
* during playback. Access to this class is thread-safe.
|
||||||
*/
|
*/
|
||||||
public final class DynamicConcatenatingMediaSource implements MediaSource, PlayerMessage.Target {
|
public final class DynamicConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHolder>
|
||||||
|
implements PlayerMessage.Target {
|
||||||
|
|
||||||
private static final int MSG_ADD = 0;
|
private static final int MSG_ADD = 0;
|
||||||
private static final int MSG_ADD_MULTIPLE = 1;
|
private static final int MSG_ADD_MULTIPLE = 1;
|
||||||
@ -331,6 +332,7 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void prepareSource(ExoPlayer player, boolean isTopLevelSource,
|
public synchronized void prepareSource(ExoPlayer player, boolean isTopLevelSource,
|
||||||
Listener listener) {
|
Listener listener) {
|
||||||
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
@ -341,13 +343,6 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
maybeNotifyListener(null);
|
maybeNotifyListener(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
|
||||||
for (int i = 0; i < mediaSourceHolders.size(); i++) {
|
|
||||||
mediaSourceHolders.get(i).mediaSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||||
int mediaSourceHolderIndex = findMediaSourceHolderByPeriodIndex(id.periodIndex);
|
int mediaSourceHolderIndex = findMediaSourceHolderByPeriodIndex(id.periodIndex);
|
||||||
@ -378,10 +373,12 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
protected void onChildSourceInfoRefreshed(
|
||||||
for (int i = 0; i < mediaSourceHolders.size(); i++) {
|
MediaSourceHolder mediaSourceHolder,
|
||||||
mediaSourceHolders.get(i).mediaSource.releaseSource();
|
MediaSource mediaSource,
|
||||||
}
|
Timeline timeline,
|
||||||
|
@Nullable Object manifest) {
|
||||||
|
updateMediaSourceInternal(mediaSourceHolder, timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -459,12 +456,7 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
}
|
}
|
||||||
correctOffsets(newIndex, newTimeline.getWindowCount(), newTimeline.getPeriodCount());
|
correctOffsets(newIndex, newTimeline.getWindowCount(), newTimeline.getPeriodCount());
|
||||||
mediaSourceHolders.add(newIndex, newMediaSourceHolder);
|
mediaSourceHolders.add(newIndex, newMediaSourceHolder);
|
||||||
newMediaSourceHolder.mediaSource.prepareSource(player, false, new Listener() {
|
prepareChildSource(newMediaSourceHolder, newMediaSourceHolder.mediaSource);
|
||||||
@Override
|
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline newTimeline, Object manifest) {
|
|
||||||
updateMediaSourceInternal(newMediaSourceHolder, newTimeline);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMediaSourcesInternal(int index, Collection<MediaSource> mediaSources) {
|
private void addMediaSourcesInternal(int index, Collection<MediaSource> mediaSources) {
|
||||||
@ -505,7 +497,7 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
mediaSourceHolders.remove(index);
|
mediaSourceHolders.remove(index);
|
||||||
Timeline oldTimeline = holder.timeline;
|
Timeline oldTimeline = holder.timeline;
|
||||||
correctOffsets(index, -oldTimeline.getWindowCount(), -oldTimeline.getPeriodCount());
|
correctOffsets(index, -oldTimeline.getWindowCount(), -oldTimeline.getPeriodCount());
|
||||||
holder.mediaSource.releaseSource();
|
releaseChildSource(holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void moveMediaSourceInternal(int currentIndex, int newIndex) {
|
private void moveMediaSourceInternal(int currentIndex, int newIndex) {
|
||||||
@ -545,10 +537,8 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Data class to hold playlist media sources together with meta data needed to process them. */
|
||||||
* Data class to hold playlist media sources together with meta data needed to process them.
|
/* package */ static final class MediaSourceHolder implements Comparable<MediaSourceHolder> {
|
||||||
*/
|
|
||||||
private static final class MediaSourceHolder implements Comparable<MediaSourceHolder> {
|
|
||||||
|
|
||||||
public final MediaSource mediaSource;
|
public final MediaSource mediaSource;
|
||||||
public final Object uid;
|
public final Object uid;
|
||||||
@ -593,16 +583,14 @@ public final class DynamicConcatenatingMediaSource implements MediaSource, Playe
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Message used to post actions from app thread to playback thread. */
|
||||||
* Message used to post actions from app thread to playback thread.
|
private static final class MessageData<T> {
|
||||||
*/
|
|
||||||
private static final class MessageData<CustomType> {
|
|
||||||
|
|
||||||
public final int index;
|
public final int index;
|
||||||
public final CustomType customData;
|
public final T customData;
|
||||||
public final @Nullable EventDispatcher actionOnCompletion;
|
public final @Nullable EventDispatcher actionOnCompletion;
|
||||||
|
|
||||||
public MessageData(int index, CustomType customData, @Nullable Runnable actionOnCompletion) {
|
public MessageData(int index, T customData, @Nullable Runnable actionOnCompletion) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.actionOnCompletion = actionOnCompletion != null
|
this.actionOnCompletion = actionOnCompletion != null
|
||||||
? new EventDispatcher(actionOnCompletion) : null;
|
? new EventDispatcher(actionOnCompletion) : null;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source;
|
package com.google.android.exoplayer2.source;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
@ -22,21 +23,20 @@ import com.google.android.exoplayer2.Timeline;
|
|||||||
import com.google.android.exoplayer2.source.ShuffleOrder.UnshuffledShuffleOrder;
|
import com.google.android.exoplayer2.source.ShuffleOrder.UnshuffledShuffleOrder;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loops a {@link MediaSource} a specified number of times.
|
* Loops a {@link MediaSource} a specified number of times.
|
||||||
* <p>
|
*
|
||||||
* Note: To loop a {@link MediaSource} indefinitely, it is usually better to use
|
* <p>Note: To loop a {@link MediaSource} indefinitely, it is usually better to use {@link
|
||||||
* {@link ExoPlayer#setRepeatMode(int)}.
|
* ExoPlayer#setRepeatMode(int)}.
|
||||||
*/
|
*/
|
||||||
public final class LoopingMediaSource implements MediaSource {
|
public final class LoopingMediaSource extends CompositeMediaSource<Void> {
|
||||||
|
|
||||||
private final MediaSource childSource;
|
private final MediaSource childSource;
|
||||||
private final int loopCount;
|
private final int loopCount;
|
||||||
|
|
||||||
private int childPeriodCount;
|
private int childPeriodCount;
|
||||||
private boolean wasPrepareSourceCalled;
|
private Listener listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loops the provided source indefinitely. Note that it is usually better to use
|
* Loops the provided source indefinitely. Note that it is usually better to use
|
||||||
@ -62,22 +62,10 @@ public final class LoopingMediaSource implements MediaSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, final Listener listener) {
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, final Listener listener) {
|
||||||
Assertions.checkState(!wasPrepareSourceCalled, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
wasPrepareSourceCalled = true;
|
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
||||||
childSource.prepareSource(player, false, new Listener() {
|
this.listener = listener;
|
||||||
@Override
|
prepareChildSource(/* id= */ null, childSource);
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
|
|
||||||
childPeriodCount = timeline.getPeriodCount();
|
|
||||||
Timeline loopingTimeline = loopCount != Integer.MAX_VALUE
|
|
||||||
? new LoopingTimeline(timeline, loopCount) : new InfinitelyLoopingTimeline(timeline);
|
|
||||||
listener.onSourceInfoRefreshed(LoopingMediaSource.this, loopingTimeline, manifest);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
|
||||||
childSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -94,8 +82,14 @@ public final class LoopingMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
protected void onChildSourceInfoRefreshed(
|
||||||
childSource.releaseSource();
|
Void id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
|
||||||
|
childPeriodCount = timeline.getPeriodCount();
|
||||||
|
Timeline loopingTimeline =
|
||||||
|
loopCount != Integer.MAX_VALUE
|
||||||
|
? new LoopingTimeline(timeline, loopCount)
|
||||||
|
: new InfinitelyLoopingTimeline(timeline);
|
||||||
|
listener.onSourceInfoRefreshed(this, loopingTimeline, manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class LoopingTimeline extends AbstractConcatenatedTimeline {
|
private static final class LoopingTimeline extends AbstractConcatenatedTimeline {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package com.google.android.exoplayer2.source;
|
package com.google.android.exoplayer2.source;
|
||||||
|
|
||||||
import android.support.annotation.IntDef;
|
import android.support.annotation.IntDef;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
@ -28,10 +29,10 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges multiple {@link MediaSource}s.
|
* Merges multiple {@link MediaSource}s.
|
||||||
* <p>
|
*
|
||||||
* The {@link Timeline}s of the sources being merged must have the same number of periods.
|
* <p>The {@link Timeline}s of the sources being merged must have the same number of periods.
|
||||||
*/
|
*/
|
||||||
public final class MergingMediaSource implements MediaSource {
|
public final class MergingMediaSource extends CompositeMediaSource<Integer> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown when a {@link MergingMediaSource} cannot merge its sources.
|
* Thrown when a {@link MergingMediaSource} cannot merge its sources.
|
||||||
@ -98,16 +99,11 @@ public final class MergingMediaSource implements MediaSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
public void prepareSource(ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
for (int i = 0; i < mediaSources.length; i++) {
|
for (int i = 0; i < mediaSources.length; i++) {
|
||||||
final int sourceIndex = i;
|
prepareChildSource(i, mediaSources[i]);
|
||||||
mediaSources[sourceIndex].prepareSource(player, false, new Listener() {
|
|
||||||
@Override
|
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
|
|
||||||
handleSourceInfoRefreshed(sourceIndex, timeline, manifest);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,9 +112,7 @@ public final class MergingMediaSource implements MediaSource {
|
|||||||
if (mergeError != null) {
|
if (mergeError != null) {
|
||||||
throw mergeError;
|
throw mergeError;
|
||||||
}
|
}
|
||||||
for (MediaSource mediaSource : mediaSources) {
|
super.maybeThrowSourceInfoRefreshError();
|
||||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -139,21 +133,16 @@ public final class MergingMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
protected void onChildSourceInfoRefreshed(
|
||||||
for (MediaSource mediaSource : mediaSources) {
|
Integer id, MediaSource mediaSource, Timeline timeline, @Nullable Object manifest) {
|
||||||
mediaSource.releaseSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSourceInfoRefreshed(int sourceIndex, Timeline timeline, Object manifest) {
|
|
||||||
if (mergeError == null) {
|
if (mergeError == null) {
|
||||||
mergeError = checkTimelineMerges(timeline);
|
mergeError = checkTimelineMerges(timeline);
|
||||||
}
|
}
|
||||||
if (mergeError != null) {
|
if (mergeError != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pendingTimelineSources.remove(mediaSources[sourceIndex]);
|
pendingTimelineSources.remove(mediaSource);
|
||||||
if (sourceIndex == 0) {
|
if (mediaSource == mediaSources[0]) {
|
||||||
primaryTimeline = timeline;
|
primaryTimeline = timeline;
|
||||||
primaryManifest = manifest;
|
primaryManifest = manifest;
|
||||||
}
|
}
|
||||||
|
@ -24,10 +24,12 @@ import android.view.ViewGroup;
|
|||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.ExoPlayer;
|
import com.google.android.exoplayer2.ExoPlayer;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.source.CompositeMediaSource;
|
||||||
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
|
import com.google.android.exoplayer2.source.DeferredMediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
import com.google.android.exoplayer2.source.ExtractorMediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaPeriod;
|
import com.google.android.exoplayer2.source.MediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||||
import com.google.android.exoplayer2.upstream.Allocator;
|
import com.google.android.exoplayer2.upstream.Allocator;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
@ -39,10 +41,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/** A {@link MediaSource} that inserts ads linearly with a provided content media source. */
|
||||||
* A {@link MediaSource} that inserts ads linearly with a provided content media source.
|
public final class AdsMediaSource extends CompositeMediaSource<MediaPeriodId> {
|
||||||
*/
|
|
||||||
public final class AdsMediaSource implements MediaSource {
|
|
||||||
|
|
||||||
/** Factory for creating {@link MediaSource}s to play ad media. */
|
/** Factory for creating {@link MediaSource}s to play ad media. */
|
||||||
public interface MediaSourceFactory {
|
public interface MediaSourceFactory {
|
||||||
@ -107,7 +107,6 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
private final Timeline.Period period;
|
private final Timeline.Period period;
|
||||||
|
|
||||||
private Handler playerHandler;
|
private Handler playerHandler;
|
||||||
private ExoPlayer player;
|
|
||||||
private volatile boolean released;
|
private volatile boolean released;
|
||||||
|
|
||||||
// Accessed on the player thread.
|
// Accessed on the player thread.
|
||||||
@ -203,17 +202,12 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void prepareSource(final ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
public void prepareSource(final ExoPlayer player, boolean isTopLevelSource, Listener listener) {
|
||||||
|
super.prepareSource(player, isTopLevelSource, listener);
|
||||||
Assertions.checkArgument(isTopLevelSource);
|
Assertions.checkArgument(isTopLevelSource);
|
||||||
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
Assertions.checkState(this.listener == null, MEDIA_SOURCE_REUSED_ERROR_MESSAGE);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.player = player;
|
|
||||||
playerHandler = new Handler();
|
playerHandler = new Handler();
|
||||||
contentMediaSource.prepareSource(player, false, new Listener() {
|
prepareChildSource(new MediaPeriodId(/* periodIndex= */ 0), contentMediaSource);
|
||||||
@Override
|
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline, Object manifest) {
|
|
||||||
AdsMediaSource.this.onContentSourceInfoRefreshed(timeline, manifest);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -222,26 +216,14 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
|
||||||
contentMediaSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
for (MediaSource[] mediaSources : adGroupMediaSources) {
|
|
||||||
for (MediaSource mediaSource : mediaSources) {
|
|
||||||
if (mediaSource != null) {
|
|
||||||
mediaSource.maybeThrowSourceInfoRefreshError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator) {
|
||||||
if (adPlaybackState.adGroupCount > 0 && id.isAd()) {
|
if (adPlaybackState.adGroupCount > 0 && id.isAd()) {
|
||||||
final int adGroupIndex = id.adGroupIndex;
|
int adGroupIndex = id.adGroupIndex;
|
||||||
final int adIndexInAdGroup = id.adIndexInAdGroup;
|
int adIndexInAdGroup = id.adIndexInAdGroup;
|
||||||
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
if (adGroupMediaSources[adGroupIndex].length <= adIndexInAdGroup) {
|
||||||
Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup];
|
Uri adUri = adPlaybackState.adUris[id.adGroupIndex][id.adIndexInAdGroup];
|
||||||
final MediaSource adMediaSource =
|
MediaSource adMediaSource =
|
||||||
adMediaSourceFactory.createMediaSource(adUri, eventHandler, eventListener);
|
adMediaSourceFactory.createMediaSource(adUri, eventHandler, eventListener);
|
||||||
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
int oldAdCount = adGroupMediaSources[id.adGroupIndex].length;
|
||||||
if (adIndexInAdGroup >= oldAdCount) {
|
if (adIndexInAdGroup >= oldAdCount) {
|
||||||
@ -253,13 +235,7 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
}
|
}
|
||||||
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
|
adGroupMediaSources[adGroupIndex][adIndexInAdGroup] = adMediaSource;
|
||||||
deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<DeferredMediaPeriod>());
|
deferredMediaPeriodByAdMediaSource.put(adMediaSource, new ArrayList<DeferredMediaPeriod>());
|
||||||
adMediaSource.prepareSource(player, false, new MediaSource.Listener() {
|
prepareChildSource(id, adMediaSource);
|
||||||
@Override
|
|
||||||
public void onSourceInfoRefreshed(MediaSource source, Timeline timeline,
|
|
||||||
@Nullable Object manifest) {
|
|
||||||
onAdSourceInfoRefreshed(adMediaSource, adGroupIndex, adIndexInAdGroup, timeline);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
|
MediaSource mediaSource = adGroupMediaSources[adGroupIndex][adIndexInAdGroup];
|
||||||
DeferredMediaPeriod deferredMediaPeriod =
|
DeferredMediaPeriod deferredMediaPeriod =
|
||||||
@ -287,15 +263,8 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void releaseSource() {
|
public void releaseSource() {
|
||||||
|
super.releaseSource();
|
||||||
released = true;
|
released = true;
|
||||||
contentMediaSource.releaseSource();
|
|
||||||
for (MediaSource[] mediaSources : adGroupMediaSources) {
|
|
||||||
for (MediaSource mediaSource : mediaSources) {
|
|
||||||
if (mediaSource != null) {
|
|
||||||
mediaSource.releaseSource();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mainHandler.post(new Runnable() {
|
mainHandler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -304,6 +273,21 @@ public final class AdsMediaSource implements MediaSource {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onChildSourceInfoRefreshed(
|
||||||
|
MediaPeriodId mediaPeriodId,
|
||||||
|
MediaSource mediaSource,
|
||||||
|
Timeline timeline,
|
||||||
|
@Nullable Object manifest) {
|
||||||
|
if (mediaPeriodId.isAd()) {
|
||||||
|
int adGroupIndex = mediaPeriodId.adGroupIndex;
|
||||||
|
int adIndexInAdGroup = mediaPeriodId.adIndexInAdGroup;
|
||||||
|
onAdSourceInfoRefreshed(mediaSource, adGroupIndex, adIndexInAdGroup, timeline);
|
||||||
|
} else {
|
||||||
|
onContentSourceInfoRefreshed(timeline, manifest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private void onAdPlaybackState(AdPlaybackState adPlaybackState) {
|
private void onAdPlaybackState(AdPlaybackState adPlaybackState) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user