2016-09-21 115 views
4

我想使用ExoPlayer2播放列表有可能在音樂上改變曲目(添加或從播放列表中刪除它們)並更改循環設置。動態播放列表與Exoplayer 2

因爲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)... 

正試圖訪問的索引= mediaSources.size()。

你能幫我嗎?

回答

2

我最終管理它。 從數組到列表的轉換過程中,這是我的錯。 我不得不使用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); 
    } 

} 
} 
+3

您能否分享完整的實施DynamicMediaSource? –

+0

這將是非常好的,如果你可以分享完整的代碼:) – lelloman

+0

嗨,我編輯了DynamicMediaSource的完整實現的答案。希望它能幫助別人。 – Manuela