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

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

相关推荐
Lei活在当下10 分钟前
【日常知识积累】Kotlin let 函数、inline 函数以及 DSL
android·kotlin·编程语言
世界美景23 分钟前
一种基于 ART 内存特征的 LSPosed/Xposed/分身环境 完美检测方案
android·安全·安卓·xposed
电商API_1800790524741 分钟前
淘宝商品视频提取API全解析:从授权到落地实战
爬虫·python·信息可视化·数据分析·音视频
2501_946230981 小时前
Cordova&OpenHarmony外观主题设置
android·javascript
小韩博2 小时前
小迪之盲注第44课
android·网络安全·adb
夏沫琅琊3 小时前
Android TestDPC 工程详解
android
键来大师3 小时前
Android16 AP热点修改默认密码为12345678
android·framework·rk3576·android16
李坤林3 小时前
Android KGI (Generic Kernel Image)
android
十二测试录3 小时前
Android和iOS测试区别
android·经验分享·ios·职场发展·ab测试
柒许宁安3 小时前
在 Cursor 中运行 Android 项目指南
android·java·个人开发