mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Support looping with LoopingMediaSource
Now you can do cool things (if you really want to!) like play a video twice, then play a second video, then loop the whole thing, all seamlessly. new LoopingMediaSource( new LoopingMediaSource(firstVideoSource, 2), secondVideoSource)); You can also just loop, which is probably more useful :). Issue: #490 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=132049599
This commit is contained in:
parent
fa500791c5
commit
884bcb649e
1 changed files with 162 additions and 0 deletions
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.util.Log;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.source.MediaPeriod.Callback;
|
||||
import com.google.android.exoplayer2.upstream.Allocator;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Loops a {@link MediaSource}.
|
||||
*/
|
||||
public final class LoopingMediaSource implements MediaSource {
|
||||
|
||||
private static final String TAG = "LoopingMediaSource";
|
||||
|
||||
private final MediaSource childSource;
|
||||
private final int loopCount;
|
||||
|
||||
private int childPeriodCount;
|
||||
|
||||
/**
|
||||
* Loops the provided source indefinitely.
|
||||
*
|
||||
* @param childSource The {@link MediaSource} to loop.
|
||||
*/
|
||||
public LoopingMediaSource(MediaSource childSource) {
|
||||
this(childSource, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops the provided source a specified number of times.
|
||||
*
|
||||
* @param childSource The {@link MediaSource} to loop.
|
||||
* @param loopCount The desired number of loops. Must be strictly positive. The actual number of
|
||||
* loops will be capped at the maximum value that can achieved without causing the number of
|
||||
* periods exposed by the source to exceed {@link Integer#MAX_VALUE}.
|
||||
*/
|
||||
public LoopingMediaSource(MediaSource childSource, int loopCount) {
|
||||
Assertions.checkArgument(loopCount > 0);
|
||||
this.childSource = childSource;
|
||||
this.loopCount = loopCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareSource(final Listener listener) {
|
||||
childSource.prepareSource(new Listener() {
|
||||
@Override
|
||||
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
|
||||
childPeriodCount = timeline.getPeriodCount();
|
||||
listener.onSourceInfoRefreshed(new LoopingTimeline(timeline, loopCount), manifest);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeThrowSourceInfoRefreshError() throws IOException {
|
||||
childSource.maybeThrowSourceInfoRefreshError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
|
||||
long positionUs) {
|
||||
return childSource.createPeriod(index % childPeriodCount, callback, allocator, positionUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releasePeriod(MediaPeriod mediaPeriod) {
|
||||
childSource.releasePeriod(mediaPeriod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSource() {
|
||||
childSource.releaseSource();
|
||||
}
|
||||
|
||||
private static final class LoopingTimeline extends Timeline {
|
||||
|
||||
private final Timeline childTimeline;
|
||||
private final int childPeriodCount;
|
||||
private final int childWindowCount;
|
||||
private final int loopCount;
|
||||
|
||||
public LoopingTimeline(Timeline childTimeline, int loopCount) {
|
||||
this.childTimeline = childTimeline;
|
||||
childPeriodCount = childTimeline.getPeriodCount();
|
||||
childWindowCount = childTimeline.getWindowCount();
|
||||
// This is the maximum number of loops that can be performed without overflow.
|
||||
int maxLoopCount = Integer.MAX_VALUE / childPeriodCount;
|
||||
if (loopCount > maxLoopCount) {
|
||||
if (loopCount != Integer.MAX_VALUE) {
|
||||
Log.w(TAG, "Capped loops to avoid overflow:" + loopCount + " -> " + maxLoopCount);
|
||||
}
|
||||
this.loopCount = maxLoopCount;
|
||||
} else {
|
||||
this.loopCount = loopCount;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWindowCount() {
|
||||
return childWindowCount * loopCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Window getWindow(int windowIndex, Window window, boolean setIds) {
|
||||
childTimeline.getWindow(windowIndex % childWindowCount, window, setIds);
|
||||
int periodIndexOffset = (windowIndex / childWindowCount) * childPeriodCount;
|
||||
window.firstPeriodIndex += periodIndexOffset;
|
||||
window.lastPeriodIndex += periodIndexOffset;
|
||||
return window;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPeriodCount() {
|
||||
return childPeriodCount * loopCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
||||
childTimeline.getPeriod(periodIndex % childPeriodCount, period, setIds);
|
||||
int loopCount = (periodIndex / childPeriodCount);
|
||||
period.windowIndex += loopCount * childWindowCount;
|
||||
if (setIds) {
|
||||
period.uid = Pair.create(loopCount, period.uid);
|
||||
}
|
||||
return period;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndexOfPeriod(Object uid) {
|
||||
if (!(uid instanceof Pair)) {
|
||||
return C.INDEX_UNSET;
|
||||
}
|
||||
Pair<?, ?> loopCountAndChildUid = (Pair<?, ?>) uid;
|
||||
if (!(loopCountAndChildUid.first instanceof Integer)) {
|
||||
return C.INDEX_UNSET;
|
||||
}
|
||||
int loopCount = (Integer) loopCountAndChildUid.first;
|
||||
int periodIndexOffset = loopCount * childPeriodCount;
|
||||
return childTimeline.getIndexOfPeriod(loopCountAndChildUid.second) + periodIndexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue