在之前的文章ExoPlayer中常見MediaSource子類的區別和使用場景中介紹了Exoplayer中各種子MediaSource的使用場景,這篇我們著重詳細介紹下實現多路流混合播放的用法。常見的使用場景有:視頻文件+電影字幕、正片視頻+廣告視頻、背景視頻+背景音樂等。
初始化Exoplayer就不多說了,隨便查查文檔就知道。
ExoPlayer mExoPlayer = new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context)).build();
1.視頻文件+電影字幕(MergingMediaSource)
Uri videoUri = Uri.parse("https://example.com/video.mp4");
Uri subtitleUri = Uri.parse("https://example.com/subtitles.srt");MediaSource videoSource = new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(videoUri);MediaSource subtitleSource = new SingleSampleMediaSource.Factory(dataSourceFactory).createMediaSource(subtitleUri, Format.createTextSampleFormat("subs", MimeTypes.TEXT_SUBRIP, C.SELECTION_FLAG_DEFAULT, "en"));MediaSource mergedSource = new MergingMediaSource(videoSource, subtitleSource);
2.廣告視頻+正片視頻(ConcatenatingMediaSource)
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory);// 創建兩個視頻的 MediaSourceMediaSource video1Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/01.mp4"));MediaSource video2Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/02.mp4"));ConcatenatingMediaSource concatenatingMediaSource = new ConcatenatingMediaSource(video1Source,video2Source);mExoPlayer.setMediaSource(concatenatingMediaSource);
這樣就能讓兩個視頻按順序播放且無縫銜接,若還想它兩循環播放,可用LoopingMediaSource進一步封裝。
//無限循環
LoopingMediaSource loopingMediaSource = new LoopingMediaSource(concatenatingMediaSource);
//循環5次
LoopingMediaSource loopingMediaSource = new LoopingMediaSource(concatenatingMediaSource,5);
mExoPlayer.setMediaSource(loopingMediaSource);
3.視頻+音頻
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory);MediaSource video2Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/02.mp4"));
MediaSource audio1Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/audio/ori.mp2"));MergingMediaSource audioMerged = new MergingMediaSource(video2Source , audio1Source);
mExoPlayer.setMediaSource(audioMerged );
?上面是1個視頻+1個音頻,當然也可以支持1個視頻+多個音頻,比如電影中有多個不同語言的音軌
DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(dataSourceFactory);MediaSource video1Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/01.mp4"));// 合并兩個音頻源MediaSource audio1Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/audio/ori.mp2"));MediaSource audio2Source = mediaSourceFactory.createMediaSource(MediaItem.fromUri("asset://android_asset/audio/acc.mp2"));MergingMediaSource audioMerged = new MergingMediaSource(audio1Source,audio2Source);
//合并視頻和音頻MergingMediaSource finalSource = new MergingMediaSource(audioMerged,video1Source);mExoPlayer.setMediaSource(finalSource);
這樣就可以實現一個視頻混合多個音軌文件的播放了,那么如何動態切換不同音軌呢?TrackSelector
DefaultTrackSelector trackSelector = new DefaultTrackSelector(context);
mExoPlayer = new ExoPlayer.Builder(context, renderersFactory).setTrackSelector(trackSelector) .build();//查看所有音軌信息private class PlayerEventListener implements Player.Listener {@SuppressLint("UnsafeOptInUsageError")@Overridepublic void onTracksChanged(Tracks tracks) {audioList.clear();Player.Listener.super.onTracksChanged(tracks);ImmutableList<Tracks.Group> trackGroups = tracks.getGroups();for (int index = 0; index < trackGroups.size(); index++) {Tracks.Group group = trackGroups.get(index);for (int jIndex = 0; jIndex < group.length; jIndex++) {Format format = group.getTrackFormat(jIndex);LOG.info("onTracksChanged format=" + Format.toLogString(format));if (MimeTypes.isAudio(format.sampleMimeType)) {audioList.add(format);}}}currentTrackGroups = mExoPlayer.getCurrentTrackGroups();}mExoPlayer.addListener(new PlayerEventListener());// 用戶選擇第 index 個音軌TrackGroup selectedGroup = null;selectedGroup = currentTrackGroups.get(1); //根據需要選擇第幾個音軌// 應用新音軌mExoPlayer.setTrackSelectionParameters(mExoPlayer.getTrackSelectionParameters().buildUpon().setOverrideForType(new TrackSelectionOverride(selectedGroup, 0)) // 需要切換到的音軌索引.build());
?這里有個問題就是如果視頻和音頻時長不一致,特別是想混合多個音頻和多個視頻時就會出問題,無法播放,報錯如下:
E/ExoPlayerImplInternal(11191): Playback error
E/ExoPlayerImplInternal(11191): com.google.android.exoplayer2.ExoPlaybackException: Source error
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:684)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:660)
E/ExoPlayerImplInternal(11191): at android.os.Handler.dispatchMessage(Handler.java:98)
E/ExoPlayerImplInternal(11191): at android.os.Looper.loop(Looper.java:136)
E/ExoPlayerImplInternal(11191): at android.os.HandlerThread.run(HandlerThread.java:61)
E/ExoPlayerImplInternal(11191): Caused by: com.google.android.exoplayer2.source.MergingMediaSource$IllegalMergeException
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.MergingMediaSource.onChildSourceInfoRefreshed(MergingMediaSource.java:252)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.MergingMediaSource.onChildSourceInfoRefreshed(MergingMediaSource.java:52)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.CompositeMediaSource.lambda$prepareChildSource$0$com-google-android-exoplayer2-source-CompositeMediaSource(CompositeMediaSource.java:120)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.CompositeMediaSource$$ExternalSyntheticLambda0.onSourceInfoRefreshed(D8$$SyntheticClass:0)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.BaseMediaSource.refreshSourceInfo(BaseMediaSource.java:94)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.ConcatenatingMediaSource.updateTimelineAndScheduleOnCompletionActions(ConcatenatingMediaSource.java:746)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.ConcatenatingMediaSource.handleMessage(ConcatenatingMediaSource.java:716)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.ConcatenatingMediaSource.$r8$lambda$xvlxaabNVihM68DRWdn_WPenrXk(ConcatenatingMediaSource.java)
E/ExoPlayerImplInternal(11191): at com.google.android.exoplayer2.source.ConcatenatingMediaSource$$ExternalSyntheticLambda0.handleMessage(D8$$SyntheticClass:0)
E/ExoPlayerImplInternal(11191): ... 3 more
?這個主要是播放時長不一致,無法同步時序導致,下一篇再討論如何解決此類情況。