播放视频和音乐是Android设备上的热门活动。 Android框架提供了MediaPlayer作为以最少代码播放媒体的快速解决方案。 它还提供低级媒体API,如MediaCodec,AudioTrack和MediaDrm,可用于构建自定义媒体播放器解决方案。
一:ExoPlayer简介
ExoPlayer是一个开源的应用程序级媒体播放器,构建在Android的低级媒体API之上。 它提供了Android的MediaPlayer API的替代方法,用于在本地和通过Internet播放音频和视频。 ExoPlayer支持Android的MediaPlayer API目前不支持的功能,包括DASH和SmoothStreaming自适应播放。 与MediaPlayer API不同,ExoPlayer易于自定义和扩展,并且可以通过Play商店应用程序更新进行更新。
二:ExoPlayer支持的格式:
MP4,M4A,FMP4,WebM,MKV,MP3,Ogg,WAV,MPEG-TS,MPEG-PS,FLV和ADTS AAC
三:ExoPlayer优缺点
ExoPlayer具有许多优于Android内置的MediaPlayer的优点:
支持通过HTTP的动态自适应流(DASH)和SmoothStreaming,MediaPlayer不支持这两种格式。还支持许多其他格式。有关详情,请参阅支持的格式页面。
支持高级HLS功能,如正确处理#EXT-X-DISCONTINUITY标记。
无缝合并,连接和循环媒体的能力。
自定义和扩展播放器以适应您的用例的能力。 ExoPlayer是专门为此而设计的,允许许多组件被自定义实现替代。
轻松更新播放器以及您的应用程序。因为ExoPlayer是您在应用程序apk中包含的库,您可以控制使用哪个版本,并且您可以轻松地更新到较新版本作为常规应用程序更新的一部分。
更少的设备特定问题。
支持Android 4.4(API级别19)及更高版本上的Widevine常用加密。
重要的是要注意,还有一些缺点:
ExoPlayer的标准音频和视频组件依赖于Android的MediaCodec API,它是在Android 4.1(API级别16)中发布的。因此,他们不在早期版本的Android上工作。 Widevine通用加密适用于Android 4.4(API级别19)及更高版本。
四:库概述
ExoPlayer库的核心是ExoPlayer接口。 ExoPlayer公开了传统的高级媒体播放器功能,例如缓冲媒体,播放,暂停和搜索的能力。实现被设计为对所播放的媒体的类型(如何以及在何处被存储以及如何被呈现)进行少量假设(并且因此对其施加少许限制)。而不是直接实现媒体的加载和呈现,ExoPlayer实现将这项工作委托给在创建播放器或准备播放时注入的组件。所有ExoPlayer实现共同的组件有:
定义要播放的媒体,加载媒体以及从中可读取加载的媒体的MediaSource。在播放开始时通过ExoPlayer.prepare注入MediaSource。
呈现媒体的各个组件的渲染器。渲染器在创建播放器时注入。
TrackSelector,用于选择MediaSource提供的由每个可用渲染器使用的轨道。创建播放器时注入TrackSelector。
LoadControl控制MediaSource何时缓冲更多介质,以及缓冲多少介质。创建播放器时会注入LoadControl。
该库为常见用例提供这些组件的默认实现,如下面更详细描述的。 ExoPlayer可以使用这些组件,但如果需要非标准行为,也可以使用自定义实现构建。例如,可以注入自定义LoadControl来更改播放器的缓冲策略,或者可以注入自定义渲染器以使用Android本机不支持的视频编解码器。
注入实现播放器功能的组件的概念存在于整个库中。上面列出的组件的默认实现将工作委托给进一步注入的组件。这允许许多子组件被单独地替换为定制实现。例如,默认的MediaSource实现需要一个或多个DataSource工厂通过它们的构造函数注入。通过提供自定义工厂,可以从非标准源或通过不同的网络堆栈加载数据。
五:将ExoPlayer添加到项目依赖中
1. ExoPlayer发布在Jcenter上。所以需要在项目根目录的build.gralde文件中添加如下代码:
respositories{
jcenter();
}
ExoPlayer对应的gradle地址如下:
compile 'com.google.android.exoplayer:exoplayer:r2.X.X'
其中r2.X.X是您的首选版本。 有关最新版本,请参阅项目的版本。 有关更多详细信息,请参阅Bintray上的项目。2. 创建SimpleExoPlayer实例
使用ExoPlayerFactory创建一个ExoPlayer实例。ExoPlayerFactory提供了一系列方法来创建具有不同级别的自定义的ExoPlayer实例。对于绝大多数使用情况,库提供的默认Renderer实现就足够了。对于这种情况,应该使用 ExoPlayerFactory.newSimpleInstance方法之一。这些方法返回SimpleExoPlayer,它扩展了ExoPlayer以添加额外的高级别播放器功能。下面的代码是创建SimpleExoPlayer的示例。
// 1.创建一个默认TrackSelector
Handler mainHandler = new Handler();
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
// 2.创建一个默认的LoadControl
LoadControl loadControl = new DefaultLoadControl();
// 3.创建播放器
SimpleExoPlayer player = ExoPlayerFactory.newSimpleInstance(context,trackSelector,loadControl);
3. 将player关联到View上
ExoPlayer库提供了一个SimpleExoPlayerView,它封装了一个PlaybackControlView和一个Surface渲染视频。 SimpleExoPlayerView可以包含在应用程序的布局xml中。 将播放器绑定到视图的过程非常简单:
simpleExoPlayerView.setPlayer(player);
如果需要对播放器控件和渲染视频的Surface进行细粒度控制,则可以分别使用SimpleExoPlayer的setVideoSurfaceView,setVideoTextureView,setVideoSurfaceHolder和setVideoSurface方法直接设置播放器的目标SurfaceView,TextureView,SurfaceHolder或Surface。 您可以使用PlaybackControlView作为独立组件,或实现自己的播放控件,直接与播放器交互。 setTextOutput和setId3Output可用于在播放期间接收字幕和ID3元数据输出。
4.准备player
在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(this,
Util.getUserAgent(this, "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);
当ExoPlayer准备就绪后,我们可以通过player控制视频的播放、前进或后退。
ExoPlayer.release
来释放资源。
2.循环播放视频
视频可以使用LoopingMediaSource无缝地循环。以下示例无限期循环视频,也可以在创建LoopingMediaSource时
指定有限循环计数
有可能进一步结合复合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);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);
以下示例是等效的,表明可以有多种方式实现相同的结果。
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);
// Loops the sequence indefinitely.
LoopingMediaSource compositeSource = new LoopingMediaSource(concatenatedSource);
重要的是,避免在组合中多次使用相同的MediaSource实例,除非根据文档明确允许。 在上面的示例中使用firstSource两次
是一个这样的情况,因为ConcatenatingMediaSource的Javadoc显式地声明允许重复条目。 然而,一般来说,由组合物形
成的对象的图应该是树。 允许在合成中使用多个等效的MediaSource实例。
八:事件监听
ExoPlayer提供了一个ExoPlay.EventListener这一接口,通过addListener和removeListener可以添加和删除相关接口。这个接
口可以监听player状态的更改,提供的方法如下。除此以外,SimpleExoPlayer提供了setVideoListener
负责接收与视频渲
染 有关的事件,setVideoDebugListener
和setAudioDebugListener
用于调试信息。
ExoPlayer提供了一个ExoPlay.EventListener这一接口,通过addListener和removeListener可以添加和删除相关接口。这个接
口可以监听player状态的更改,提供的方法如下。除此以外,SimpleExoPlayer提供了setVideoListener
负责接收与视频渲染
有关的事件
,setVideoDebugListener
和setAudioDebugListener
用于调试信息。
2.Handler(Low Level Event)
通过调用ExoPlayer.void sendMessages(ExoPlayerMessage... )
和
ExoPlayer.void blockingSendMessages(ExoPlayerMessage... messages)
实现和ExoPlayer交互,再由内部的Handler
进行消息的分发和处理。其实,在具体实现上,回调是通过Handler.handleMessage(Message)
实现的,两者在实现上是一
样的。
Stetho是FaceBook出品的Android调试利器,在它的配合下,可以使用Chrome进行调试。当然,ExoPlayer也支持Stetho调试,开启方式如下:
由于ExoPlayer的数据读取是由接口com.google.android.exoplayer2.upstream.DataSource
实现的,这里的我们采用的类
是com.google.android.exoplayer2.ext.okhttp.OkHttpDataSourceFactory
,所以需要添加如下依赖
compile 'com.google.android.exoplayer:extension-okhttp:r2.1.1'
compile 'com.squareup.okhttp3:okhttp:3.5.0'
compile 'com.facebook.stetho:stetho:1.4.2'
compile 'com.facebook.stetho:stetho-okhttp3:1.4.2'
|
通过Stetho.initializeWithDefaults(Context)
完成Stetho的初始化。
创建OkHttpClient,并添加StethoInterceptor到NetworkInterceptor
Stetho.initializeWithDefaults(
this
);
OkHttpClient okHttpClient =
new
OkHttpClient.Builder()
.addNetworkInterceptor(
new
StethoInterceptor())
.build();
|
DataSource.Factory dataSourceFactory =
new
OkHttpDataSourceFactory(okHttpClient, userAgent, bandwidthMeter);
|
打开chrome://inspect
,我们就可以在Developer Tools
的Network
部分查看到相关的调试结果: