一步步自定义视频播放器——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%