使用Exoplayer 2

时间:2016-09-21 09:02:35

标签: android exoplayer playlists exoplayer2.x

我想将ExoPlayer2与播放列表一起使用,可以动态地改变音轨(在播放列表中添加或删除它们)并更改循环设置。

由于ConcatenatingMediaSource有静态数组(而不是列表),我正在实现一个DynamicMediaSource,比如连接一个但是使用列表而不是数组和一个模式方法addSource来向列表添加一个以上的媒体源。

public void addSource(MediaSource mediaSource) {
    this.mediaSources.add(mediaSource);
    duplicateFlags = buildDuplicateFlags(this.mediaSources);
    if(!mediaSources.isEmpty())
        prepareSource(mediaSources.size() -1);
    else
        prepareSource(0);
}

当我调用addSource

                MediaSource ms = buildMediaSource(mynewuri, null);
                mediaSource.addSource(ms);

将轨道添加到数组中但似乎缺少某些东西,因为我总是在createPeriod方法中获取ArrayOutOfBoundsException。

在createPeriod中使用方法

mediaSources.get(sourceIndex)...

正在尝试访问index = mediaSources.size()。

你能帮助我吗?

1 个答案:

答案 0 :(得分:3)

我最终成功了。 在从数组到列表的转换过程中,这是我的错。 我不得不使用SparseArrays作为时间表和清单,一切都开始起作用了。

在DynamicMediaSource中,只需设置以下类型:

private final List<MediaSource> mediaSources;
private final SparseArray<Timeline> timelines;
private final SparseArray<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;

您必须使用稀疏数组将正确的值设置为方法中的时间轴和清单

private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
                                       Object sourceManifest) {
    // Set the timeline and manifest.
    timelines.put(sourceFirstIndex, sourceTimeline);
    manifests.put(sourceFirstIndex, sourceManifest);

    // Also set the timeline and manifest for any duplicate entries of the same source.
    for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
        if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
            timelines.put(i, sourceTimeline);
            manifests.put(i, sourceManifest);
        }
    }

    for(int i= 0; i<mediaSources.size(); i++){
        if(timelines.get(i) == null){
            // Don't invoke the listener until all sources have timelines.
            return;
        }
    }

    timeline = new DynamicTimeline(new ArrayList(asList(timelines)));
    listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
}

以下是DynamicMediaSource类的完整代码:

public final class DynamicMediaSource implements MediaSource {

private static final String TAG = "DynamicSource";

private final List<MediaSource> mediaSources;
private final List<Timeline> timelines;
private final List<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;

private Listener listener;
private DynamicTimeline timeline;

/**
 * @param mediaSources The {@link MediaSource}s to concatenate. It is valid for the same
 *                     {@link MediaSource} instance to be present more than once in the array.
 */
public DynamicMediaSource(MediaSource... mediaSources) {
    this.mediaSources = new ArrayList<MediaSource>(Arrays.asList(mediaSources));
    timelines = new ArrayList<Timeline>();
    manifests = new ArrayList<Object>();
    sourceIndexByMediaPeriod = new HashMap<>();
    duplicateFlags = buildDuplicateFlags(this.mediaSources);
}

public void addSource(MediaSource mediaSource) {
    this.mediaSources.add(mediaSource);
    duplicateFlags = buildDuplicateFlags(this.mediaSources);
    /*if(!mediaSources.isEmpty())
        prepareSource(mediaSources.size() -1);
    else
        prepareSource(0);*/
}

@Override
public void prepareSource(Listener listener) {
    this.listener = listener;
    for (int i = 0; i < mediaSources.size(); i++) {
        prepareSource(i);
        /*if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
            final int index = i;
            mediaSources.get(i).prepareSource(new Listener() {
                @Override
                public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
                    handleSourceInfoRefreshed(index, timeline, manifest);
                }
            });
        }*/
    }
}

private void prepareSource(int sourceindex) {
    if (duplicateFlags.get(sourceindex) == null || !duplicateFlags.get(sourceindex)) {
        final int index = sourceindex;
        mediaSources.get(sourceindex).prepareSource(new Listener() {
            @Override
            public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
                handleSourceInfoRefreshed(index, timeline, manifest);
            }
        });
    }
}

@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
    for (int i = 0; i < mediaSources.size(); i++) {
        if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
            mediaSources.get(i).maybeThrowSourceInfoRefreshError();
        }
    }
}

@Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
                                long positionUs) {
    int sourceIndex = timeline.getSourceIndexForPeriod(index);
    int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex);
    MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIndexInSource, callback,
            allocator, positionUs);
    sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
    return mediaPeriod;
}

@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
    int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
    sourceIndexByMediaPeriod.remove(mediaPeriod);
    mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
}

@Override
public void releaseSource() {
    for (int i = 0; i < mediaSources.size(); i++) {
        if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
            mediaSources.get(i).releaseSource();
        }
    }
}

private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
                                       Object sourceManifest) {
    // Set the timeline and manifest.
    timelines.add(sourceFirstIndex, sourceTimeline);
    manifests.add(sourceFirstIndex, sourceManifest);
    // Also set the timeline and manifest for any duplicate entries of the same source.
    for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
        if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
            timelines.add(i, sourceTimeline);
            manifests.add(i, sourceManifest);
        }
    }
    for (Timeline timeline : timelines) {
        if (timeline == null) {
            // Don't invoke the listener until all sources have timelines.
            return;
        }
    }
    timeline = new DynamicTimeline(new ArrayList(timelines));
    listener.onSourceInfoRefreshed(timeline, new ArrayList(manifests));
}

private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
    SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
    IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
    for (int i = 0; i < mediaSources.size(); i++) {
        MediaSource mediaSource = mediaSources.get(i);
        if (!sources.containsKey(mediaSource)) {
            sources.put(mediaSource, null);
        } else {
            duplicateFlags.setValueAt(i, true);
        }
    }
    return duplicateFlags;
}

/**
 * A {@link Timeline} that is the concatenation of one or more {@link Timeline}s.
 */
private static final class DynamicTimeline extends Timeline {

    private final List<Timeline> timelines;
    private final List<Integer> sourcePeriodOffsets;
    private final List<Integer> sourceWindowOffsets;

    public DynamicTimeline(List<Timeline> timelines) {
        List<Integer> sourcePeriodOffsets = new ArrayList<>();
        List<Integer> sourceWindowOffsets = new ArrayList<>();
        int periodCount = 0;
        int windowCount = 0;
        for (Timeline timeline : timelines) {
            periodCount += timeline.getPeriodCount();
            windowCount += timeline.getWindowCount();
            sourcePeriodOffsets.add(periodCount);
            sourceWindowOffsets.add(windowCount);
        }
        this.timelines = timelines;
        this.sourcePeriodOffsets = sourcePeriodOffsets;
        this.sourceWindowOffsets = sourceWindowOffsets;
    }

    @Override
    public int getWindowCount() {
        return sourceWindowOffsets.get(sourceWindowOffsets.size() - 1);
    }

    @Override
    public Window getWindow(int windowIndex, Window window, boolean setIds) {
        int sourceIndex = getSourceIndexForWindow(windowIndex);
        int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
        int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
        timelines.get(sourceIndex).getWindow(windowIndex - firstWindowIndexInSource, window, setIds);
        window.firstPeriodIndex += firstPeriodIndexInSource;
        window.lastPeriodIndex += firstPeriodIndexInSource;
        return window;
    }

    @Override
    public int getPeriodCount() {
        return sourcePeriodOffsets.get(sourcePeriodOffsets.size() - 1);
    }

    @Override
    public Period getPeriod(int periodIndex, Period period, boolean setIds) {
        int sourceIndex = getSourceIndexForPeriod(periodIndex);
        int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
        int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
        timelines.get(sourceIndex).getPeriod(periodIndex - firstPeriodIndexInSource, period, setIds);
        period.windowIndex += firstWindowIndexInSource;
        if (setIds) {
            period.uid = Pair.create(sourceIndex, period.uid);
        }
        return period;
    }

    @Override
    public int getIndexOfPeriod(Object uid) {
        if (!(uid instanceof Pair)) {
            return C.INDEX_UNSET;
        }
        Pair<?, ?> sourceIndexAndPeriodId = (Pair<?, ?>) uid;
        if (!(sourceIndexAndPeriodId.first instanceof Integer)) {
            return C.INDEX_UNSET;
        }
        int sourceIndex = (Integer) sourceIndexAndPeriodId.first;
        Object periodId = sourceIndexAndPeriodId.second;
        if (sourceIndex < 0 || sourceIndex >= timelines.size()) {
            return C.INDEX_UNSET;
        }
        int periodIndexInSource = timelines.get(sourceIndex).getIndexOfPeriod(periodId);
        return periodIndexInSource == C.INDEX_UNSET ? C.INDEX_UNSET
                : getFirstPeriodIndexInSource(sourceIndex) + periodIndexInSource;
    }

    private int getSourceIndexForPeriod(int periodIndex) {
        return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
    }

    private int getFirstPeriodIndexInSource(int sourceIndex) {
        return sourceIndex == 0 ? 0 : sourcePeriodOffsets.get(sourceIndex - 1);
    }

    private int getSourceIndexForWindow(int windowIndex) {
        return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
    }

    private int getFirstWindowIndexInSource(int sourceIndex) {
        return sourceIndex == 0 ? 0 : sourceWindowOffsets.get(sourceIndex - 1);
    }

}
}