ExoPlayer是个什么高大上的东西?怎么用?如何用?有哪些优缺点?相比IjkPlayer和Viatimo有那些区别?
ExoPlayer是什么?
ExoPlayer是一个开源媒体播放器,App等级的媒体API。
ExoPlayer的优缺点:
ExoPlayer相较于MediaPlayer有很多很多的优点:
- 支持动态的自适应流HTTP(DASH) 和 平滑流,任何目前MediaPlayer支持的视频格式(同时它还支持HTTP直播了(HLS),MP4,MP3,WebM,M4A,MPEG-TS 和 AAC).
- 支持高级的HLS特性,例如正确处理 EXT-X-DISCONTINUITY 标签;这个后面会举例。
- 支持自定义和扩治你的使用场景。ExoPlayer专门为此设计;
- 便于随着App的升级而升级。因为ExoPlayer是一个包含在你的应用中的库,对于你使用哪个版本有完全的控制权,并且你可以简单的跟随应用的升级而升级;
- 更少的适配性问题。
值得注意的时,ExoPlayer同时有些缺点:
- ExoPlayer的音频和视频组件依赖Android的 MediaCodec接口,该接口发布于Android4.1(API 等级16)。因此它不能工作于之前的Android版本。
- ExoPlayer目前还不支持自动检查需要播放的媒体格式。应用需要知道他想要播放的媒体格式去构建一个ExoPlayer去播放媒体。
ExoPlayer的使用方法:
如果需要使用ExoPlayer,你需要执行以下几步:
1. 添加依赖
2. 创建player
3. 关联视图
4. 准备播放
5. 释放
1. 添加依赖到项目中:
在project的build.gradle中添加:
repositories {
jcenter()
}
在app的build.gradle中添加最新版本的exopalyer包:
compile 'com.google.android.exoplayer:exoplayer:r2.X.X'
2. 创建player:
ExoPlayer是通过ExoPlayerFactory创建的,这个工厂类提供了一系列的方法可以创建各种个性化的player。绝大数情况下Renderer可以使用系统默认的,当然如果渲染器满足不了你的需求,你可以自定义。默认情况下可以直接使用ExoPlayerFactory.newSimpleInstance(),返回一个SimpleExoPlayer,SimpleExoPlayer是ExoPlayer的一个升级版,添加了一些强大的功能,可以直接使用。一个简单的SimpleExoPlayer的创建例子:
// 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 a default LoadControl
LoadControl loadControl = new DefaultLoadControl();
// 3. Create the player
SimpleExoPlayer player =
ExoPlayerFactory.newSimpleInstance(context, trackSelector, loadControl);
3. 将播放器和视图连接起来
SimpleExoPleyerView可以写在布局文件中,可以直接在代码中使用下面代码即可实现视图的绑定:
//绑定视图
simpleExoPlayerView.setPlayer(player);如果你想要更精确的对播放进行控制的话,可以使用Surface,SurfaceView,TextureView,surfaceHolder,使用SimpleExoplayer对应的方法如:我将视频播放和Surface联系起来,可以直接用setVideoSurface方法。还有setVideoSurfaceView, setVideoTextureView, setVideoSurfaceHolder方法可以选择使用。您可以使用PlaybackControlView作为一个独立的组件,或实现自己的播放控制,直接控制播放器。setTextOutput setId3Output可以用来接收标题和ID3元数据输出在回放期间。
4. 现在可以开始准备播放了。
有一点很重要,所有的资源都由MediaSource控制,所以第一步一定要创建对应的MediaSource。然后再将放入ExoPlayer,通过prepare方法来实现。
MediaSource主要有四种类型:
- ExtractorSampleSource - 用于MP3,M4A,WebM,MPEG-TS和AAC;
- HlsSampleSource - 用于HLS 播放;
- DashMediaSource - DASH的播放
- SsMeidaSource - 平滑流的播放
简单写一个如何播放MP4文件格式的代码:
// 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);
一旦准备完毕,可以通过控制器来控制播放状态,比如你可以使用setPlayWhenReady来开始和暂停播放……
5. 释放player
不需要的时候一定要释放掉,释放响应的资源如:视频解码器,来供其他应用程序使用。方法:ExoPlayer.release().
MediaSource的讲解
前面提到了四种MediaSource,这四种在官方的Demo中都有详细的实现方法,这里不再赘述。这里讲一下其他三种:MergingMediaSource,LoopingMediaSource and ConcatenatingMediaSource.适合更复杂的视频播放。
1. 带有视频字幕文件的视频播放
视频文件和字幕文件是分开的,可以使用MergingMediaSource进行操作。代码如下:
MediaSource videoSource = new ExtractorMediaSource(videoUri, ...);
MediaSource subtitleSource = new SingleSampleMediaSource(subtitleUri, ...);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
new MergingMediaSource(videoSource, subtitleSource);
2. 实现无缝视频播放
什么叫无缝视频播放,简单来说,当这个视频文件播放完成后,切换到另一个视频文件播放,这中间没有延迟。如果要实现这个功能,我们可以使用LoopingMediaSource ;下面是一个无限循环的看一个视频的代码:代码如下:
MediaSource source = new ExtractorMediaSource(videoUri, ...);
// Loops the video indefinitely.
LoopingMediaSource loopingSource = new LoopingMediaSource(source);
3.无缝地玩一个视频序列
你可以使不同视频文件播放起来像同一文件一样,没有视频的切换和延迟,ConcatenatingMediaSource就派上作用了,它对视频的类型没有要求,如第一个视频为480p,第二个视频可以为720p,也可以为480p没有强制规定。下面是使用ConcatenatingMediaSource来完成两个视频的无缝播放。(它可以连接视频与音频流);代码如下:
MediaSource firstSource = new ExtractorMediaSource(firstVideoUri, ...);
MediaSource secondSource = new ExtractorMediaSource(secondVideoUri, ...);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSource, secondSource);
4.高级操作
MediaSource不是单一的,是可以结合。怎么合并使用?
情景描述:给出两个视频A和B,可以一直按A.A.B的顺序循环下去,这个该怎么做?
思考:更具3和4可以使用LoopingMediaSource and ConcatenatingMediaSource
代码如下:
第一种方法:
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只允许实例化一次,不允许出现重复。如ConcatenatingMediaSource明确指出可以允许出现重复。如上例。
播放事件处理,低级监听,高级监听,这些今后再补充,特别指出,在Android 4,4 (API19)以后,ExoPlayer支持数字版权管理。如果需要这个功能,你的应用必须使用DrmSessionManager来实例化你的播放器。具体参考官方文档
ExoPlayer播m3u8和google.redirector?
如何播放HLS格式的视频:
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
AdaptiveVideoTrackSelection.Factory factory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector selector = new DefaultTrackSelector(factory);
DefaultLoadControl loadControl = new DefaultLoadControl();
SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(this, selector, loadControl);
mPlayerView.setPlayer(exoPlayer);
MediaSource mediaSource = new HlsMediaSource(Uri.parse(URL_HLS),
new DefaultDataSourceFactory(this, "HlsPlayActivity"),
null, null);
exoPlayer.prepare(mediaSource);
如何播放google.redirector……格式文件:
private static final DefaultBandwidthMeter bandwidthMeter= new DefaultBandwidthMeter();
DataSource.Factory mediaDataSourceFactory = new DefaultDataSourceFactory(this, bandwidthMeter,
new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "应用名"), bandwidthMeter);
AdaptiveVideoTrackSelection.Factory factory = new AdaptiveVideoTrackSelection.Factory(bandwidthMeter);
DefaultTrackSelector selector = new DefaultTrackSelector(factory);
DefaultLoadControl loadControl = new DefaultLoadControl();
SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(this, selector, loadControl);
// SimpleExoPlayerView mPlayerView
mPlayerView.setPlayer(exoPlayer);
MediaSource mMediaSource = new ExtractorMediaSource(Uri.parse(uri), mediaDataSourceFactory, new DefaultExtractorsFactory(),
null, null);
exoPlayer.prepare(mMediaSource);
//设置事件处理
exoPlayer.addListener(new ExoPlayer.EventListener() {
@Override
public void onTimelineChanged(Timeline timeline, Object manifest) {
Log.d(TAG, "onTimelineChanged() called with: timeline = [" + timeline + "], " +
"manifest = [" + manifest + "]");
}
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray
trackSelections) {
pbLoaded.setVisibility(View.INVISIBLE);
ivCover.setVisibility(View.INVISIBLE);
Log.d(TAG, "onTracksChanged() called with: trackGroups = [" + trackGroups + "], " +
"trackSelections = [" + trackSelections + "]");
}
@Override
public void onLoadingChanged(boolean isLoading) {
Log.d(TAG, "onLoadingChanged: "+isLoading);
}
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
Log.d(TAG, "onPlayerStateChanged: "+playWhenReady);
}
@Override
public void onPlayerError(ExoPlaybackException error) {
Log.d(TAG, "onPlayerError: ");
}
@Override
public void onPositionDiscontinuity() {
Log.d(TAG, "onPositionDiscontinuity: ");
}
});
总结:ExoPlayer也是最新学习的一个很好的开源框架,以前对于流媒体视频处理一直都是使用ijkPlayer,Vitamio。现在使用ijkPlayer的人也越来越多,第一免费,第二,支持的视频格式多,适配性强。当然,当时也是做快速开发,只要套上这个框架能使用就可以了,并没有做更多的深入。Exoplayer不说别的,Google开发的,你懂的。ExoPlayer相关文章会持续更新,不想再做一个浅尝辄止的IT男咯。本文是笔者学习所结,不全面之处还请见谅。有问题可以留言,看到及时回复。
参考资料:
ExoPlayerDemo下载地址
ExoPlayer官方文档
ExoPlayer开发者指导
ExoPlayer源码浅析