Android Exoplayer 实现多个音视频文件混合播放以及音轨切换

在之前的文章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);
            // 创建两个视频的 MediaSource
            MediaSource 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")
        @Override
        public 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

这个主要是播放时长不一致,无法同步时序导致,下一篇再讨论如何解决此类情况。

相关推荐
阿巴斯甜13 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker14 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952715 小时前
Andorid Google 登录接入文档
android
黄林晴16 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android