repositories {
jcenter()
ExoPlayer对应的gradle地址如下:
compile 'com.google.android.exoplayer:exoplayer:r2.X.X'
2.创建一个SimpleExoPlayer实例
这里我们使用 ExoPlayerFactory.newSimpleInstance(Context, TrackSelector, LoadControl)
创建对应的ExoPlayer实例对象。
相关代码如下:
// 1. Create a default TrackSelector
BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveVideoTrackSelection.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.将player关联到到View上
ExoPlayer默认提供了一个实现SimpleExoPlayerView
,它包括PlaybackControlView
和用于视频渲染的Surface
,通过调用SimpleExoPlayerView.setPlayer(SimpleExoPlayer)
,就可以将SimpleExoPlayerView和Player进行绑定。
当然你可以对以上行为进行自定义,例如将PlaybackControlView作为单独的组件或者实现一个属于你自己的PlaybackControlView。
4.准备player
在ExoPlayer中,一个媒体片段用MediaSource表示,如果要播放一个片段,需要创建对应的MediaSource(ExoPlayer提供的实现有DashMediaSource、SsMediaSource、HlsMediaSource、ExtractorMediaSource)。如果我们需要播放一段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);
当ExoPlayer准备就绪后,我们可以通过player控制视频的播放、前进或后退。
5.释放资源
当你不需要player时,可以通过调用ExoPlayer.release
来释放资源。
之前已经提到过了,ExoPlayer中一个media片段用MediaSource
表示。除了之前提到过的MediaSource
,ExoPlayer中还提供了MergingMediaSource, LoopingMediaSource 和 ConcatenatingMediaSource等MediaSource,通过他们之间的相互组合,我们可以得到各种各样的播放效果
合并视频和字幕
MediaSource videoSource = new ExtractorMediaSource(videoUri, dataSourceFactory, extractorsFactory, null, null);
Format format = Format.createTextSampleFormat(null, MimeTypes.APPLICATION_TTML, null, Format.NO_VALUE, Format.NO_VALUE, "en", null);
MediaSource subtitleSource = new SingleSampleMediaSource(subtitleUri, dataSourceFactory, format, C.TIME_UNSET);
MergingMediaSource mergedSource = new MergingMediaSource(videoSource, subtitleSource);
循环播放视频
MediaSource source = new ExtractorMediaSource(videoUri, ...);
// Loops the video indefinitely.
LoopingMediaSource loopingSource = new LoopingMediaSource(source);
// Plays the video twice.
LoopingMediaSource loopingSource = new LoopingMediaSource(source,2);
连续播放视频
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.事件监听
在platback的过程中,应用可以可以根据ExoPlayer的状态做出相应的调整,对播放过程进行控制。为此,ExoPlayer提供了两个不同方式
事件回调(High Level Event)
ExoPlayer提供了一个ExoPlay.EventListener这一接口,通过addListener和removeListener可以添加和删除相关接口。这个接口可以监听player状态的更改,提供的方法如下。除此以外,SimpleExoPlayer提供了setVideoListener
负责接收与视频渲染有关的事件,setVideoDebugListener
和setAudioDebugListener
用于调试信息。
public interface ExoPlayer {
interface EventListener {
* Called when the timeline and/or manifest has been refreshed.
* Note that if the timeline has changed then a position discontinuity may also have occurred.
* For example the current period index may have changed as a result of periods being added or
* removed from the timeline. The will <em>not</em> be reported via a separate call to
* {@link #onPositionDiscontinuity()}.
* @param timeline The latest timeline. Never null, but may be empty.
* @param manifest The latest manifest. May be null.
void onTimelineChanged(Timeline timeline, Object manifest);
* Called when the available or selected tracks change.
* @param trackGroups The available tracks. Never null, but may be of length zero.
* @param trackSelections The track selections for each {@link Renderer}. Never null and always
* of length {@link #getRendererCount()}, but may contain null elements.
void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections);
* Called when the player starts or stops loading the source.
* @param isLoading Whether the source is currently being loaded.
void onLoadingChanged(boolean isLoading);
* Called when the value returned from either {@link #getPlayWhenReady()} or
* {@link #getPlaybackState()} changes.
* @param playWhenReady Whether playback will proceed when ready.
* @param playbackState One of the {@code STATE} constants defined in the {@link ExoPlayer}
* interface.
void onPlayerStateChanged(boolean playWhenReady, int playbackState);
* Called when an error occurs. The playback state will transition to {@link #STATE_IDLE}
* immediately after this method is called. The player instance can still be used, and
* {@link #release()} must still be called on the player should it no longer be required.
* @param error The error.
void onPlayerError(ExoPlaybackException error);
* Called when a position discontinuity occurs without a change to the timeline. A position
* discontinuity occurs when the current window or period index changes (as a result of playback
* transitioning from one period in the timeline to the next), or when the playback position
* jumps within the period currently being played (as a result of a seek being performed, or
* when the source introduces a discontinuity internally).
* When a position discontinuity occurs as a result of a change to the timeline this method is
* <em>not</em> called. {@link #onTimelineChanged(Timeline, Object)} is called in this case.
void onPositionDiscontinuity();
Handler(Low Level Event)
通过调用ExoPlayer.void sendMessages(ExoPlayerMessage... )
和ExoPlayer.void blockingSendMessages(ExoPlayerMessage... messages)
实现和ExoPlayer交互,再由内部的Handler进行消息的分发和处理。其实,在具体实现上,回调是通过Handler.handleMessage(Message)
实现的,两者在实现上是一样的。
5.使用Stetho对ExoPlayer进行网络调试
Stetho是FaceBook出品的Android调试利器,在它的配合下,可以使用Chrome进行调试。当然,ExoPlayer也支持Stetho调试,开启方式如下:
1.实现添加相关库依赖
由于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'
2.网络和Stetho的初始化
通过Stetho.initializeWithDefaults(Context)
完成Stetho的初始化。
创建OkHttpClient,并添加StethoInterceptor到NetworkInterceptor
Stetho.initializeWithDefaults(this);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor())
.build();
3.创建OkHttpDataSource
DataSource.Factory dataSourceFactory =
new OkHttpDataSourceFactory(okHttpClient, userAgent, bandwidthMeter);
4.查看调试结果
打开chrome://inspect
,我们就可以在Developer Tools
的Network
部分查看到相关的调试结果: