Android EXOPlayer 音视频库

简介


播放视频和音乐是安卓设备上常用的功能,android库的MediaPlayer提供可以使用少量代码解决的方案,它也提供低级媒体APIs像MediaCodec、AudioTrack和MediaDrm来自定义媒体播放器。

ExoPlayer是一个开源的,基于安卓低级媒体APIs的应用程序级别的媒体播放器。这个开源工程包涵两部分:

  • ExoPlayer library - 这一部分包涵工程核心源码。
  • Demo app - 这部分包涵使用ExoPlayer的例子。

本指南介绍了ExoPlayer库及其用途,提供具体的示例。 涉及使用ExoPlayer的优缺点。 它展示了如何使用ExoPlayer播放DASH,SmoothStreaming和HLS自适应流,以及如MP4,M4A,FMP4,WebM,MKV,MP3,Ogg,WAV,MPEG-TS,MPEG-PS,FLV和ADTSAAC)。 它还讨论了ExoPlayer事件,消息,定制和DRM支持。

优缺点


ExoPlayer 相比安卓中的MediaPlayer有很多的优点:

  • 支持通过HTTP(DASH)和SmoothStreaming进行动态自适应流,而MediaPlayer不支持。
  • 支持HLS的高级特性,例如正确处理#EXT-X-DISCONTINUITY 标签
  • 支持无缝合并、连接和循环媒体
  • 可以在应用程序中轻松的更新播放器的版本,因为ExoPlayer是一个使用在你的apk中的一个库,你可以选择使用什么版本
  • 较少的设备异常
  • 支持android4.4和以上版本的Widevine通用加密,可以自定义和扩展播放器。
  • 可以快速集成很多官方扩展库,例如:IMA扩展程序使用Interactive Media Ads SDK轻松定制你的内容。

注意它也有一些缺点:

  • ExoPlayer的标准的音视频依赖安卓中的MediaCodec的API,它在android4.1(API level 16)中发布,因此不能使用ExoPlayer在更早的版本
  • Widevine通用加密在Android4.4(API level 19)及更高版本可用。

库预览


ExoPlayer库的核心是ExoPlayer的接口,其中定义了包涵传统播放器的功能,例如缓冲媒体、播放、暂停、定位等。ExoPlayer没有设定可以播放的媒体类型、存储的方式和位置以及渲染的方式,也没有直接实现加载和播放媒体。而是在播放器被创建或者准备播放时将这些工作代理给注入其中的组件来实现,常见的ExoPlayer组件实现是:

  • 定义要播放的媒体源MediaSource,加载媒体,和从哪里加载的媒体能被读。在开始播放时将MediaSource通过ExoPlayer.prepare注入。
  • 在播放器创建的时候,将呈现各个组件的媒体渲染器注入。
  • TrackSelector,由它选择MediaSource提供的轨道(track),以供渲染器使用。在创建播放器时,将TranckSelector注入。
  • LoadControl,用于控制MediaSource缓冲时间已经缓冲量。当创建播放器时,将LoadControl注入。

这个库提供了常见组件的使用,但也可以实现自定义的实现。例如,可以注入自定义的LoadControl来更改播放器的缓冲策略,或者注入自定义的渲染器(Renderer)来使用Android不支持的视频编码器。注入组件的概念是实现播放器功能的模块化,可以支持仅仅添加部分模块更能进入播放器,而默认提供的方案是上面提出的几点。例如,默认的MediaSource实现要求使用构造方法提供一个或多个DataSource。但是通过自定义工厂可以提供从不非标准或不同的网络上加载数据。

入门使用


简单的使用ExoPlayer包涵以下步骤:

  • 添加ExoPlayer作为您项目的依赖。
  • 创建SimpleExoPlayer实例。
  • 添加播放器到view中(用于视频输出和用户输入)。
  • 使用MediaSource来构建播放器。
  • 完成使用后释放播放器。

这些步骤将在下面更详细地概述。 有关完整的示例,请参阅ExoPlayer演示程序中的PlayerActivity。

添加ExoPlayer依赖到项目中

第一步确认你的的build.gradle文件包涵JCenter和Google库。

repositories {
    jcenter()
    google()
}

然后添加gradle编译依赖到build.gradle文件中,下面的将会添加ExoPlayer库的所有依赖:

compile 'com.google.android.exoplayer:exoplayer:r2.X.X'

r2.x.x指的是版本号。或者可以仅仅依赖自己需要的库。例如下面仅仅依赖了Core,DASH和UI模块:

compile 'com.google.android.exoplayer:exoplayer-core:r2.X.X'
compile 'com.google.android.exoplayer:exoplayer-dash:r2.X.X'
compile 'com.google.android.exoplayer:exoplayer-ui:r2.X.X'

所有可以使用的库模块包涵在下面的列表中:

  • exoplayer-core: 核心模块(必选)
  • exoplayer-dash: 支持 DASH 内容
  • exoplayer-hls: 支持HLS 内容
  • exoplayer-smoothstreaming: 支持 SmoothStreaming 内容
    -exoplayer-ui: UI 和资源

除了这些库,ExoPlayer还提供扩展库的功能,详情可以浏览 extensions directory

创建播放器

可以使用ExoPlayerFactory创建ExoPlayer实例。大多数的场景是使用ExoPlayerFactory.newSimpleInstance方法创建,这个方法会返回SimpleExoPlayer,它继承自ExoPlayer并提供了高级播放器的功能。代码实例如下:

// 1. Create a default TrackSelector
Handler mainHandler = new Handler();

BandwidthMeter bandwidthMeter =  new DefaultBandwidthMeter();

TrackSelection.Factory videoTrackSelectionFactory =  new AdaptiveTrackSelection.Factory(bandwidthMeter);

TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);

// 2. Create the player
SimpleExoPlayer player =  ExoPlayerFactory.newSimpleInstance(context, trackSelector);
添加player到view中

ExoPlayer库提供一个SimpleExoPlayerView,它封装了播放视频的播放控制视图和视频渲染界面,SimpleExoPlayerView可以包含在应用程序的布局xml中。将播放器绑定到视图实例如下:

// Bind the player to the view.
simpleExoPlayerView.setPlayer(player);

如果您需要对播放器控件和渲染视频的Surface进行细粒度控制,您可以分别使用SimpleExoPlayer的setVideoSurfaceView,setVideoTextureView,setVideoSurfaceHolder和setVideoSurface方法直接设置播放器的SurfaceView,TextureView,SurfaceHolder或Surface。 您可以使用PlaybackControlView作为独立组件,或实现与播放器直接交互的自己的播放控件。 setTextOutput和setId3Output可用于在播放过程中接收字幕和ID3元数据输出。

准备播放器

在ExoPlayer中,每个媒体都由MediaSource表示。 要播放一块媒体,您必须先创建一个相应的MediaSource,然后将此对象传递给ExoPlayer.prepare。 ExoPlayer库为DASH(DashMediaSource),SmoothStreaming(SsMediaSource),HLS(HlsMediaSource)和常规媒体文件(ExtractorMediaSource)提供MediaSource实现。 这些实现将在本指南的后面更详细地描述。 以下代码显示如何使用适合播放MP4文件的MediaSource准备播放器。

// Measures bandwidth during playback. Can be null if not required.
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();

// Produces DataSource instances through which media data is loaded.
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context,
    Util.getUserAgent(context, "yourApplicationName"), bandwidthMeter);

// Produces Extractor instances for parsing the media data.
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

// This is the MediaSource representing the media to be played.
MediaSource videoSource = new ExtractorMediaSource(mp4VideoUri,
    dataSourceFactory, extractorsFactory, null, null);

// Prepare the player with the source.
player.prepare(videoSource);
控制播放器

播放器准备完毕后,可以通过播放器上的方法来控制播放。 例如setPlayWhenReady可以用于启动和暂停播放,seekTo方法可以用于在媒体中查找,setRepeatMode可用于控制是否以及如何循环,并且可以使用setPlaybackParameters来调整播放速度和音量。
如果播放器绑定到SimpleExoPlayerView或PlaybackControlView,则用户与这些组件的交互将导致播放器上的相应方法被调用。

释放播放器

重要的是在不再需要时释放播放器,以释放有限的资源,如视频解码器供其他应用使用。 这可以通过调用ExoPlayer.release来完成。

MediaSource


在ExoPlayer中,每个媒体都由MediaSource表示。 ExoPlayer库为DASH(DashMediaSource),SmoothStreaming(SsMediaSource),HLS(HlsMediaSource)和常规媒体文件(ExtractorMediaSource)提供MediaSource实现。在ExoPlayer演示程序中,可以在PlayerActivity中找到实例化的所有示例(四个)。

MediaSource实例被设计为不能重复使用。如果你想用同样的媒体准备一个播放器多次,那么每次都要使用一个新的实例。

除了上述的MediaSource实现之外,ExoPlayer库还提供了MergingMediaSource,LoopingMediaSource,ConcatenatingMediaSource和DynamicConcatenatingMediaSource。这些MediaSource实现通过组合实现更复杂的播放功能。一些常见的用例如下所述。注意,虽然在视频播放的上下文中描述了以下示例,但它们同样适用于仅音频播放,实也适用于任何支持的媒体类型的播放。

加载字幕

一个视频文件和一个单独的字幕文件,MergingMediaSource可以用于将它们合并成一个单独的源进行播放。

// Build the video MediaSource.
MediaSource videoSource = new ExtractorMediaSource(videoUri, ...);
// Build the subtitle MediaSource.
Format subtitleFormat = Format.createTextSampleFormat(
    id, // An identifier for the track. May be null.
    MimeTypes.APPLICATION_SUBRIP, // The mime type. Must be set correctly.
    selectionFlags, // Selection flags for the track.
    language); // The subtitle language. May be null.
MediaSource subtitleSource = new SingleSampleMediaSource(
    subtitleUri, dataSourceFactory, subtitleFormat, C.TIME_UNSET);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
    new MergingMediaSource(videoSource, subtitleSource);
循环视频

要无限期循环,通常最好使用ExoPlayer.setRepeatMode而不是LoopingMediaSource。

一个视频可以使用LoopingMediaSource无缝地循环固定次数。 以下示例播放视频两次。

MediaSource source = new ExtractorMediaSource(videoUri, ...);
// Plays the video twice.
LoopingMediaSource loopingSource = new LoopingMediaSource(source, 2);
播放一系列视频

ConcatenatingMediaSource可连续播放两个或多个单独的MediaSources。 以下示例依次播放两个视频。 两个视频源之间是无缝的转换。 并且不要求连接的两个源具有相同的格式(例如,包含480p H264的视频文件与包含720p VP9的视频文件连接在一起播放)。内容源甚至可以是不同的类型(例如,将视频与仅音频串联连接在一起)。

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, secondSource);

DynamicConcatenatingMediaSource类似于ConcatenatingMediaSource,但是它允许在播放之前和播放期间动态添加,删除和移动MediaSources。 DynamicConcatenatingMediaSource非常适合用户播放时修改播放列表的播放列表使用情况。
** MediaSource实例不应该多次添加到DynamicConcatenatingMediaSource,或者先前已被删除的重新添加。 应该改用新的实例。**

高级组合

可以进一步将复合MediaSources组合在一起,用于更不寻常的用例。 给定两个视频A和B,以下示例显示了如何一起使用LoopingMediaSource和ConcatenatingMediaSource来播放序列(A,A,B)。

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSourceTwice, secondSource);

以下示例是等效的

MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, firstSource, secondSource);

重要的是避免在组合中多次使用相同的MediaSource实例,除非根据文档明确允许。 在上面的示例中使用firstSource两次是一个这样的情况,因为ConcatenatingMediaSource的Javadoc明确声明允许重复的条目。 然而,一般来说,组合物形成的对象的图形应该是树。 允许在组合中使用多个等效的MediaSource实例。

播放器事件


播放过程中可以监听ExoPlayer的player各种状态的事件,这些事件可以用来做为更新用户界面组件的触发器,比如播放控制。大多组件也能报告他们自己的低级事件,这些可以被用来监听性能。

高级事件

ExoPlayer可以使用addListener和removeListener方法来添加和删除EventListener。播放状态改变的时候会通知注册的监听器,包括播放失败的发生事件。
开发人员应该使用监听来实现自定义的播放控制和更新播放器的状态改变。一个app也应该将出现的错误展示给用户看。

使用SimpleExoPlayer时,可以在播放器上设置其他侦听器。 特别地,addVideoListener允许应用程序接收与视频呈现有关的事件,这些事件可能对于调整UI(例如,正在渲染到哪个视频的表面的宽高比)。 侦听器也可以设置为接收调试信息,例如通过调用setVideoDebugListener和setAudioDebugListener。

低级事件

除了高级别的事件监听外,ExoPlayer库提供的其他组件也允许自己的事件被监听。通常需要将Handler对象传递给这些组件,这些组件决定了调用侦听器方法的线程。在大多数情况下,您应该在主线程处理相关事件。

向组件发送消息


ExoPlayer组件允许在播放过程中更改配置。 通过使用sendMessages或blockingSendMessages方法将消息通过ExoPlayer传递到组件来进行这些更改。 这种方法确保了线程安全性,并且可以修改播放器执行操作的顺序。

自定义


ExoPlayer与Android MediaPlayer的主要优点之一是能够自定义和扩展播放器,以更好地适应开发人员的用例。 ExoPlayer库专门设计了这一点,定义了许多接口和抽象基类,使应用程序开发人员可以轻松地替换库提供的默认实现。 以下是构建自定义组件的一些用例:

  • Renderer - 可能需要实现自定义渲染器来处理由库提供的默认实现不支持的媒体类型。
  • TrackSelector - 实现自定义TrackSelector允许应用程序开发人员更改Renderer渲染器选择的MediaSource的轨道。
  • LoadControl - 实现自定义LoadControl允许应用程序开发人员更改播放器的缓冲策略。
  • Extractor - 如果您需要支持库当前不支持的容器格式,请考虑实现一个自定义Extractor类,然后可以将其与ExtractorMediaSource一起使用来播放该类型的媒体。
  • MediaSource - 如果希望获取媒体样本以自定义方式提供给渲染器,或者希望实现自定义MediaSource合成行为,则实现自定义MediaSource类可能适用。
  • DataSource - ExoPlayer的上游包已经包含了许多用于不同用例的DataSource实现。 您可能希望实现自己的DataSource类,以其他方式加载数据,例如通过自定义协议,使用自定义HTTP堆栈或从自定义持久缓存加载数据。
定制指南
  • 如果自定义组件需要将事件报告回应用程序,我们建议您使用与现有ExoPlayer组件相同的模型,其中将事件侦听器与Handler一起传递到组件的构造函数。

  • 我们建议自定义组件使用与现有ExoPlayer组件相同的型号,以便在播放过程中重新配置应用程序,如上面向组件发送消息所述。 为此,您应该实现一个ExoPlayerComponent并接收其handleMessage方法中的配置更改。 您的应用程序应通过调用ExoPlayer的sendMessages和blockingSendMessages方法来传递配置更改。

数字版权管理


在Android 4.4(API级别19)及更高版本上,ExoPlayer支持数字版权管理(DRM)保护播放。为了使用ExoPlayer播放受DRM保护的内容,您的应用程序必须在实例化播放器时注入DrmSessionManager。 ExoPlayerFactory提供允许这种工厂方法。 DrmSessionManager对象负责提供DrmSession实例,该实例为解密提供了MediaCrypto对象,并确保所需的解密密钥对所使用的底层DRM模块可用。

ExoPlayer库提供了一个名为DefaultDrmSessionManager的DrmSessionManager的默认实现,它使用MediaDrm。会话管理器支持设备上存在模块化DRM组件的任何DRM方案。所有Android设备都需要支持Widevine模块化DRM(具有L3安全性,尽管许多设备也支持L1)。某些设备可能支持其他方案,如PlayReady。所有Android TV设备都支持PlayReady。

ExoPlayer演示应用程序中的PlayerActivity演示了如何在实例化播放器时创建和注入DefaultDrmSessionManager。

参考连接:
https://developer.android.com/guide/topics/media/exoplayer.html
https://google.github.io/ExoPlayer/guide.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,563评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,609评论 4 59
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 详见原文 关于cooking 关于成长 关于爱情 关于健康 关于其他 以上基于Google翻译,所以……
    改不回去的Shawnlau阅读 216评论 0 0
  • 愛是沒有需求,只願他好。願他做真實完整的自己,願他快樂開心,只鼓勵讚美,支持信任。
    羽書阅读 173评论 0 0