如果要同时播放2个音频甚至播放多个音频怎么办呢?比如同时播放伴奏声音和原唱声音,而且要做到多音轨同步。这里涉及一个核心问题,多音轨播放如何同步,因为每个音轨有对应时钟。Android平台的ExoPlayer扩展性非常好,虽然本身不支持多音轨播放,但是我们可以扩展。通过扩展TrackSelector、AudioRender、RenderFactory来支持多音轨。

1、自定义AudioRender

继承于MediaCodecAudioRenderer,在构造方法传入index记录音轨索引,重写getMediaClock方法,默认使用第一个音轨作为同步时钟。也就是解决上面讨论的核心问题。

public class MultiTrackAudioRender extends MediaCodecAudioRenderer {private final int index;public MultiTrackAudioRender(int index,Context context,MediaCodecSelector mediaCodecSelector,@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,boolean playClearSamplesWithoutKeys,boolean enableDecoderFallback,@Nullable Handler eventHandler,@Nullable AudioRendererEventListener eventListener,AudioSink audioSink) {super(context, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys,enableDecoderFallback, eventHandler, eventListener, audioSink);this.index = index;}@Nullable@Overridepublic MediaClock getMediaClock() {if (index == 0) {return super.getMediaClock();}return null;}}

2、自定义RenderFactory

继承于DefaultRenderersFactory,把自定义的AudioRender添加到renderList里。

public class MultiTrackRenderFactory extends DefaultRenderersFactory {private final static int count = 2;public MultiTrackRenderFactory(Context context) {super(context);}@Overrideprotected void buildAudioRenderers(Context context, int extensionRendererMode, MediaCodecSelector mediaCodecSelector,@Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,boolean playClearSamplesWithoutKeys, boolean enableDecoderFallback,AudioProcessor[] audioProcessors, Handler eventHandler,AudioRendererEventListener eventListener, ArrayList<Renderer> out) {// Create ProcessorChain, and add any AudioProcess you needDefaultAudioSink.DefaultAudioProcessorChain processorChain = new DefaultAudioSink.DefaultAudioProcessorChain(new SilenceSkippingAudioProcessor(),new SonicAudioProcessor());for (int i=0; i<count; i++) {AudioSink audioSink = new DefaultAudioSink(AudioCapabilities.getCapabilities(context),processorChain, true);// Using MultiTrackAudioRender and add into renderListMultiTrackAudioRender audioRender = new MultiTrackAudioRender(i, context, MediaCodecSelector.DEFAULT,drmSessionManager, playClearSamplesWithoutKeys, enableDecoderFallback,eventHandler, eventListener, audioSink);out.add(audioRender);}}
}

3、自定义TrackSelector

继承于DefaultTrackSelector,判断到媒体类型是音频,都添加renderList里。这样就实现支持多音轨播放。

public class MultiTrackSelector extends DefaultTrackSelector {public MultiTrackSelector(Context context) {super(context);}@Overridepublic TrackSelectorResult selectTracks(RendererCapabilities[] rendererCapabilities, TrackGroupArray trackGroups,MediaSource.MediaPeriodId periodId, Timeline timeline) throws ExoPlaybackException {if (trackGroups == null || rendererCapabilities == null) {return null;}LinkedList<Integer> renderList = new LinkedList<>();TrackSelection[] selections = new TrackSelection[rendererCapabilities.length];RendererConfiguration[] configs = new RendererConfiguration[rendererCapabilities.length];for (int i = 0; i < rendererCapabilities.length; i++) {// Add all audio tracks into renderListif(rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_AUDIO) {renderList.add(i);configs[i] = RendererConfiguration.DEFAULT;}}for (int i = 0; i < trackGroups.length; i++) {if (trackGroups.get(i).getFormat(0) != null&& MimeTypes.isAudio(trackGroups.get(i).getFormat(0).sampleMimeType)) {Integer index = renderList.poll();if (index != null) {selections[index] = new FixedTrackSelection(trackGroups.get(i), 0);}}}int[] rendererTrackGroupCounts = new int[rendererCapabilities.length + 1];TrackGroup[][] rendererTrackGroups = new TrackGroup[rendererCapabilities.length + 1][];@RendererCapabilities.Capabilities int[][][] rendererFormatSupports = new int[rendererCapabilities.length + 1][][];for (int i = 0; i < rendererTrackGroups.length; i++) {rendererTrackGroups[i] = new TrackGroup[trackGroups.length];rendererFormatSupports[i] = new int[trackGroups.length][];}// Determine the extent to which each renderer supports mixed mimeType adaptation.@RendererCapabilities.AdaptiveSupportint[] rendererMixedMimeTypeAdaptationSupports =getMixedMimeTypeAdaptationSupports(rendererCapabilities);for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {TrackGroup group = trackGroups.get(groupIndex);// Associate the group to a preferred renderer.int rendererIndex = findRenderer(rendererCapabilities, group);// Evaluate the support that the renderer provides for each track in the group.@RendererCapabilities.Capabilitiesint[] rendererFormatSupport =rendererIndex == rendererCapabilities.length? new int[group.length]: getFormatSupport(rendererCapabilities[rendererIndex], group);// Stash the results.int rendererTrackGroupCount = rendererTrackGroupCounts[rendererIndex];rendererTrackGroups[rendererIndex][rendererTrackGroupCount] = group;rendererFormatSupports[rendererIndex][rendererTrackGroupCount] = rendererFormatSupport;rendererTrackGroupCounts[rendererIndex]++;}// Create a track group array for each renderer, and trim each rendererFormatSupports entry.TrackGroupArray[] rendererTrackGroupArrays = new TrackGroupArray[rendererCapabilities.length];int[] rendererTrackTypes = new int[rendererCapabilities.length];for (int i = 0; i < rendererCapabilities.length; i++) {int rendererTrackGroupCount = rendererTrackGroupCounts[i];rendererTrackGroupArrays[i] =new TrackGroupArray(Util.nullSafeArrayCopy(rendererTrackGroups[i], rendererTrackGroupCount));rendererFormatSupports[i] =Util.nullSafeArrayCopy(rendererFormatSupports[i], rendererTrackGroupCount);rendererTrackTypes[i] = rendererCapabilities[i].getTrackType();}// Create a track group array for track groups not mapped to a renderer.int unmappedTrackGroupCount = rendererTrackGroupCounts[rendererCapabilities.length];TrackGroupArray unmappedTrackGroupArray =new TrackGroupArray(Util.nullSafeArrayCopy(rendererTrackGroups[rendererCapabilities.length], unmappedTrackGroupCount));// Package up the track information and selections.MappedTrackInfo mappedTrackInfo =new MappedTrackInfo(rendererTrackTypes,rendererTrackGroupArrays,rendererMixedMimeTypeAdaptationSupports,rendererFormatSupports,unmappedTrackGroupArray);return new TrackSelectorResult(configs, selections, mappedTrackInfo);}}

4、接入SimpleExoPlayer

创建MultiTrackSelector、MultiRenderFactory,作为参数传入到SimpleExoPlayer.Builder,来构造出SimpleExoPlayer。

DefaultTrackSelector trackSelector = new MultiTrackSelector(context);
RenderersFactory  renderersFactory = new MultiTrackRenderFactory(context);
SimpleExoPlayer.Builder builder    = new SimpleExoPlayer.Builder(context, renderersFactory, trackSelector, new DefaultLoadControlX(),DefaultBandwidthMeter.getSingletonInstance(context), handler.getLooper(), new AnalyticsCollector(Clock.DEFAULT), true, Clock.DEFAULT);
SimpleExoPlayer simpleExoPlayer = builder.build();

至此,实现多音轨同时播放。ExoPlayer架构设计合理,易于扩展,这是值得我们学习的地方。

扩展ExoPlayer实现多音轨同时播放相关推荐

  1. WMP12拖放扩展名未关联文件不能播放的一种原因

    家里两台电脑,有台能拖放一种视频文件,另一台却不能,始终不得其解.该扩展名跟实际编码格式无必然联系,所以未关联到WMP. 经反复比较,发现通过选择打开方式强制打开,WMP会说不认识该扩展名,但是能播放 ...

  2. ExoPlayer详解(官方文档-入门)

    目录 ExoPlayer详解系列文章-入门 一.前言 二.优缺点比较 三.概述 ExoPlayer详解--入门(官方文档) 添加ExoPlayer作为依赖项 1.添加依赖 2.添加ExoPlayer模 ...

  3. ExoPlayer尝鲜

    前言: 本文是根据ExoPlayer的HelloWorld文档翻译而来 Hello world! Another way to get started is to work through the E ...

  4. ExoPlayer播放器 开发者指南(官方权威指南译文)

    前言   因为公司项目原因,目前开始研究ExoPlayer的原理及实现.其中对DRM更是有所涉及,因此自己也好借此机会扩展自己的音视频知识,同时写出一些自己的技术总结与分享,希望对其他学习此播放器的朋 ...

  5. 【翻译】安卓新播放器EXOplayer介绍

    [翻译]安卓新播放器EXOplayer介绍 http://developer.android.com/guide/topics/media/exoplayer.html 前言: Playing vid ...

  6. ExoPlayer播放器剖析(五)ExoPlayer对AudioTrack的操作

    关联博客 ExoPlayer播放器剖析(一)进入ExoPlayer的世界 ExoPlayer播放器剖析(二)编写exoplayer的demo ExoPlayer播放器剖析(三)流程分析-从build到 ...

  7. android exoplayer在您的应用中播放视频,例如youtube

    视频应用程序的重要性(The Importance of Video Apps) There's a constant need for video-playing apps for entertai ...

  8. ExoPlayer 和 IjkPlayer 切换音轨和内嵌字幕的方法。

    目前公司所开发的app是用了ExoPlayer 和 IjkPlayer 两款播放器来回切换播放视频流的,有切换音轨的功能需求,所以就写了这篇博客. 首先是IjkPlayer切换音轨和内嵌字幕的方式: ...

  9. Android VR Player(全景视频播放器) [10]: VR全景视频渲染播放的实现(exoplayer,glsurfaceview,opengl es)

    前言 此博客的大部分内容来自我的毕业设计论文,因此语言上会偏正式一点,如果您有任何问题或建议,欢迎留言.在此感谢实验室的聂师兄,全景视频render部分的代码设计主要参考了他所编写的代码来完成,他对视 ...

  10. Android使用ExoPlayer播放音频实现报听写功能

    项目中需要实现报听写的功能,音频都是后台一个一个返回的链接,考虑到多个音频的连续播放,就选用了google的Exoplayer,主要实现随机播放,按次数播放(这里最多播放3次)的功能,ExoPlaye ...

最新文章

  1. 2018, 自动驾驶异常艰难的一年
  2. (七)Amazon Lightsail 部署LAMP应用程序之清除已安装服务
  3. 系列(五)—MySql
  4. 远程仓库---添加远程库
  5. mysql索引有哪些了解_Mysql索引(简单了解)
  6. python 数据分析库_五个 Python 常用数据分析库
  7. 数据表的创建(一对多,多对多)
  8. 小米要用 AI + IoT 做年轻人的第一套智能家居
  9. 微软称伊朗国家黑客攻击美国国防技术公司
  10. KCP - A Fast and Reliable ARQ Protocol
  11. 写给非网工的CCNA教程(8)跨LAN的通信
  12. 考勤统计,一张表查询每月的员工考勤数据
  13. flashFxp连接不上服务器。
  14. android 播放assets下视频,安卓播放assets文件里视频文件相关问题分析
  15. 范式通俗理解:1NF、2NF、3NF和BNCF
  16. 现在企业常用考勤软件
  17. SEUS 转换XML到JAVA
  18. Re32:读论文 Summarizing Legal Regulatory Documents using Transformers
  19. 矩阵乘法——矩阵快速幂
  20. 前端入门之(我与iscroll的不期而遇)

热门文章

  1. c# TcpClient 客户端断线重连类库
  2. 浅谈 Java 24个设计模式(23个GoF设计模式 + 简单工厂模式) 之 六个创建型模式...
  3. 【前端工具】实用的代码生成器
  4. goahead(嵌入式Web服务器)之asp、goform篇
  5. SPSS学习笔记【二】-回归分析
  6. windows便签 の 字体设置
  7. USB3300速度调试
  8. 单设施选址-重心法-Matlab
  9. MTCNN人脸检测与人脸对齐
  10. linux双网卡连不上网,linux 双网卡配置问题