一步步自定义视频播放器——MediaPlayer的使用

本篇内容是我对几篇文章的总结,对MediaPlayer使用的整理。

mediaplayer高级使用
mediaplayer基本使用

Android下对于音频、视频的支持均需要使用到MediaPlayer,它主要用来控制Android下播放文件或流的类。

MediaPlayer是Android原生的多媒体播放器,可以用它来实现本地或者在线音视频的播放,同时它支持https和rtsp。

MediaPlayer处于Android多媒体包下”android.media.MediaPlayer”,仅有一个无参的构造函数,虽然仅为我们提供了一个无参的构造函数,为了方便我们初始化,还为我们提供了几个静态的create()方法用于完成MediaPlayer初始化的工作。

  • static MediaPlayer create(Context context,int resid):通过音频资源的Id来创建一个MediaPlayer实例。
  • static MediaPlayer create(Context context,Uri uri):通过一个音频资源的Uri地址来创建一个MediaPlayer实例。

  MediaPlayer除了通过上面两个create()方法在初始化的时候指定媒体资源,还可以通过MediaPlayer.setDataSource()方法为初始化后的MediaPlayer设置媒体资源,setDataSource()具有多个重载函数,适用于不同的媒体资源来源,以下讲解几个常用的,其他的可以查阅官方文档。

  • void setDataSource(String path):通过一个媒体资源的地址指定MediaPlayer的数据源,这里的path可以是一个本地路径,也可以是网络路径。
  • void setDataSource(Context context,Uri uri):通过一个Uri指定MediaPlayer的数据源,这里的Uri可以是网络路径或这一个内容提供者的Uri。
  • void setDataSource(FileDescriptor fd):通过一个FileDescriptor指定一个MediaPlayer的数据源。

下面是MediaPlayer的生命周期图

根据图片,我们知道当创建Mediaplayer时,调用reset()方法,MediaPlayer处于Idle状态。

调用release()方法,MediaPlayer处于End状态

Idle->End 就是MediaPlayer的完整的生命周期

注意:有些状态是可以双向转换的,有些只能单向环形转换。如果在某状态下,强行转换状态,会应发程序错误,例如在Preparing状态下切换到Started状态,是准备中强行开始播放,会出错。

MediaPlayer的相关方法及监听接口:

方法 介绍 状态
setDataSource 设置数据源 Initialized
prepare 准备播放,同步 Preparing —> Prepared
prepareAsync 准备播放,异步 Preparing —> Prepared
start 开始或恢复播放 Started
pause 暂停 Paused
stop 停止 Stopped
seekTo 到指定时间点位置 PrePared/Started
reset 重置播放器 Idle
setAudioStreamType 设置音频流类型 –
setDisplay 设置播放视频的Surface –
setVolume 设置声音 –
getBufferPercentage 获取缓冲半分比 –
getCurrentPosition 获取当前播放位置 –
getDuration 获取播放文件总时间 –
setLooping(boolean looping) 设置是否循环播放 –
isLooping() 判断是否循环播放 –
isPlaying() 判断是否正在播放 –
setWakeMode(Context context, int mode) 设置CPU唤醒的状态 –
内部回调接口 介绍 状态
OnPreparedListener 准备监听 Preparing ——>Prepared
OnVideoSizeChangedListener 视频尺寸变化监听 –
OnInfoListener 指示信息和警告信息监听 –
OnCompletionListener 播放完成监听 PlaybackCompleted
OnErrorListener 播放错误监听 Error
OnBufferingUpdateListener 缓冲更新监听 –
  1. MediaPlayer在直接new出来之后进入Idle状态,
    可以调用多个重载的setDataSource()方法从 Idle->Initialized状态
    (如果调用setDataSource方法的时候,MediaPlayer对象不是Idle状态,会抛出异常,可以调用reset()方法回到Idle状态)

  2. 调用prepared()和preparedAsyn()进入Prepared状态,prepared是同步方法,直接进入Prepared状态,preparedAsyn()是异步状态,先进入Preparing状态,通过OnPreparedListener.onPrepared()回调方法通知Prepared状态。

  3. 在Prepared状态下就可以调用start()方法进行播放了,此时进入started()状态,如果播放的是网络资源,Started状态下也会自动调用客户端注册的OnBufferingUpdateListener.OnBufferingUpdate()回调方法,对流播放缓冲的状态进行追踪。

  4. pause()方法和start()方法是对应的,调用pause()方法会进入Paused状态,调用start()方法重新进入Started状态,继续播放。
  5. stop()方法会使MdiaPlayer从Started、Paused、Prepared、PlaybackCompleted等状态进入到Stoped状态,播放停止。
  6. 当资源播放完毕时,如果调用了setLooping(boolean)方法,会自动进入Started状态重新播放,如果没有调用则会自动调用客户端播放器注册的OnCompletionListener.OnCompletion()方法,此时MediaPlayer进入PlaybackCompleted状态,在此状态里可以调用start()方法重新进入Started状态。

使用方式

  1. 创建MediaPlayer
  2. 设置数据源
  3. 设置音频流类型
  4. 调用准备 prepare
  5. 当处于就绪状态,调用start
1
2
3
4
5
6
7
8
9
10
11
12
13
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(path);
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

// 通过异步的方式装载媒体资源
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// 装载完毕回调
mediaPlayer.start();
}
});

注意:一定要回收资源,MediaPlayer是很消耗系统资源的,所以在使用完MediaPlayer,不要等待系统自动回收,最好是主动回收资源。

1
2
3
4
5
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}

MediaPlayer的音频焦点

  众所周知,Android是一个多任务的操作系统,所以对于音频的播放,也许有几个不同的媒体服务会同时播放,这样可能导致一个比较杂乱的声音环境,而错过一些重要的声音提醒。在Android2.2之后,Android提供了一种应用协商使用设备音频输出的机制,这种机制称为音频焦点。

  当应用程序需要输出音频或通知的时候,需要请求音频焦点,当请求得到音频焦点之后,监听音频焦点的变换,当音频焦点变换了,根据返回回来的音频焦点码进行相应的处理。音频焦点的注册使用音频管理器的AudioManager.requestAudioFocus()方法设定。

 AudioManager.OnAudioFocusChangeListener为音频焦点变换的监听器,其中需要实现一个方法:onAudioFocusChange(int focusChange)在音频焦点变换的时候回调。它有一个参数,为当前表示音频焦点对于当前应用的状态码,通过这个状态码指定对应的操作,有些时候音频状态改变了,并不一定需要停止音频的播放。

  focusChange有一下几种状态码:

  • AUDIOFOCUS_GAIN:获得音频焦点。
    • AUDIOFOCUS_LOSS:失去音频焦点,并且会持续很长时间。这是我们需要停止MediaPlayer的播放。
    • AUDIOFOCUS_LOSS_TRANSIENT:失去音频焦点,但并不会持续很长时间,需要暂停MediaPlayer的播放,等待重新获得音频焦点。
    • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时失去音频焦点,但是无需停止播放,只需降低声音方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
audioManager = (AudioManager) getSystemService(this.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(
new OnAudioFocusChangeListener() {

@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
// 获得音频焦点
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
// 还原音量
mediaPlayer.setVolume(1.0f, 1.0f);
break;

case AudioManager.AUDIOFOCUS_LOSS:
// 长久的失去音频焦点,释放MediaPlayer
if (mediaPlayer.isPlaying())
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
break;

case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 展示失去音频焦点,暂停播放等待重新获得音频焦点
if (mediaPlayer.isPlaying())
mediaPlayer.pause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 失去音频焦点,无需停止播放,降低声音即可
if (mediaPlayer.isPlaying()) {
mediaPlayer.setVolume(0.1f, 0.1f);
}
break;
}
}
}, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
0%