Android ExoPlayer播放列表使用LoopingMediaSource引发IndexOutOfBoundsException

Android ExoPlayer播放列表使用LoopingMediaSource引发IndexOutOfBoundsException,android,exoplayer,Android,Exoplayer,我必须播放一次视频A,播放完后,无限期地循环播放视频B。我正在尝试为此使用ConcatenatingMediaSource: private SimpleExoPlayer initPlayer(ViewGroup layout, int playerViewId, ExoPlayer.EventListener eventListener) { // 1. Create a default TrackSelector TrackSelector trackSelector =

我必须播放一次视频A,播放完后,无限期地循环播放视频B。我正在尝试为此使用ConcatenatingMediaSource:

private SimpleExoPlayer initPlayer(ViewGroup layout, int playerViewId, ExoPlayer.EventListener eventListener) {
    // 1. Create a default TrackSelector
    TrackSelector trackSelector = new DefaultTrackSelector();

    // 2. Create a default LoadControl
    LoadControl loadControl = new DefaultLoadControl();

    // 3. Create the player
    this.player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, loadControl);

    SimpleExoPlayerView simpleExoPlayerView = (SimpleExoPlayerView) layout.findViewById(playerViewId);
    // Bind the player to the view.
    simpleExoPlayerView.setUseController(false);
    simpleExoPlayerView.setPlayer(player);

    if (eventListener != null)
        player.addListener(eventListener);
    // Prepare the player with the source.
    player.setPlayWhenReady(true);

    return player;
}

public void startPlayer(String firstURL, String loopingURL) {
    initProxy();

    DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
    MediaSource firstSource = getVideoPlayerMediaSource(bandwidthMeter, firstURL);
    MediaSource secondSource = new LoopingMediaSource(getVideoPlayerMediaSource(bandwidthMeter, loopingURL));

    ConcatenatingMediaSource concatenatedSource =
            new ConcatenatingMediaSource(firstSource, secondSource);

    player.setPlayWhenReady(true);
    player.prepare(concatenatedSource);
    player.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);

    setPlayerPlaying(true);
}

private void initProxy() {
    if (proxy == null)
        proxy = VideoCache.getProxy(getContext());
}

@NonNull
private MediaSource getVideoPlayerMediaSource(DefaultBandwidthMeter bandwidthMeter, String videoUrl) {
    DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getContext(),
            Util.getUserAgent(getContext(), "com.myapp"), bandwidthMeter);

    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

    Uri url = Uri.parse(videoUrl);
    MediaSource videoSource;

    if (videoUrl.contains(".mp4")) {
        url = Uri.parse(proxy.getProxyUrl(videoUrl));
        videoSource = new ExtractorMediaSource(url,
                dataSourceFactory, extractorsFactory, null, null);
    } else {
        videoSource = new HlsMediaSource(url, dataSourceFactory, null, null);
    }


    return videoSource;
}
但这会引发:

Internal runtime error.
java.lang.IndexOutOfBoundsException
    at com.google.android.exoplayer2.util.Assertions.checkIndex(Assertions.java:66)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.getPeriodPosition(ExoPlayerImplInternal.java:1077)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.getPeriodPosition(ExoPlayerImplInternal.java:1059)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.getPeriodPosition(ExoPlayerImplInternal.java:1050)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.handleSourceInfoRefreshed(ExoPlayerImplInternal.java:872)
    at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:320)
    at android.os.Handler.dispatchMessage(Handler.java:98)
    at android.os.Looper.loop(Looper.java:148)
    at android.os.HandlerThread.run(HandlerThread.java:61)
    at com.google.android.exoplayer2.util.PriorityHandlerThread.run(PriorityHandlerThread.java:40)
只有当使用secondSource作为
LoopingMediaSource
时,问题才会发生。它在没有它的情况下工作,但显然不会循环第二个视频


(ExoPlayer版本r2.3.1)

尝试为LoopingMediaSource使用另一个构造函数,在该构造函数中指定循环计数并查看其是否有效:

public LoopingMediaSource(MediaSource childSource, int loopCount) {
    Assertions.checkArgument(loopCount > 0);
    this.childSource = childSource;
    this.loopCount = loopCount;
}
看起来您正在收到一条HandleSourceInfoRefresh消息,该消息是从LoopingMediaSource prepareSource()方法发送的:

但这会创建一个新的LoopingTimeline对象,并将其传递给loopCount,在您的示例中为0。LoopingTimeline类重写getWindowCount():

如果loopCount为0,则返回0


如果这样做有效,那么您可以通过传递(Integer.MAX_VALUE-1)作为loopCount,让视频无限循环,因为LoopingTimeline无论如何都会限制循环计数。

尝试为LoopingMediaSource使用另一个构造函数,在这里您指定一个循环计数,然后看看这是否有效:

public LoopingMediaSource(MediaSource childSource, int loopCount) {
    Assertions.checkArgument(loopCount > 0);
    this.childSource = childSource;
    this.loopCount = loopCount;
}
看起来您正在收到一条HandleSourceInfoRefresh消息,该消息是从LoopingMediaSource prepareSource()方法发送的:

但这会创建一个新的LoopingTimeline对象,并将其传递给loopCount,在您的示例中为0。LoopingTimeline类重写getWindowCount():

如果loopCount为0,则返回0


如果这样做有效,那么您可以通过传递(Integer.MAX_值-1)作为loopCount使视频无限循环,因为LoopingTimeline无论如何都会限制循环计数。

重要提示:使用
Integer.MAX_值-1
(他们指出了这一点)作为
LoopingMediaSource
构造函数的第二个参数。非常感谢@罗米纳夫:好的。我编辑了答案以反映这一点。谢谢重要提示:使用
Integer.MAX_VALUE-1
(他们指出了这一点)作为
LoopingMediaSource
构造函数的第二个参数。非常感谢@罗米纳夫:好的。我编辑了答案以反映这一点。谢谢
@Override
public int getWindowCount() {
  return childWindowCount * loopCount;
}