MediaPlayer 是 Android 中的一个多媒体播放类,能通过它控制音视频流或本地音视频资源的播放过程。
Play
示例一
图片旋转音乐
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
|
public class MediaPlayerManager {
public static final int MEDIA_STATUS_PLAY = 0; public static final int MEDIA_STATUS_PAUSE = 1; public static final int MEDIA_STATUS_STOP = 2; public int MEDIA_STATUS = MEDIA_STATUS_STOP;
private static final int H_PROGRESS = 1000; private MediaPlayer mediaPlayer; private OnMusicProgressListener musicProgressListener;
private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message message) { switch (message.what){ case H_PROGRESS: if (musicProgressListener != null){ int currentPosition = getCurrentPosition(); int pos = (int) (((float)currentPosition) / ((float)getDuration())*100); musicProgressListener.OnProgress(currentPosition,pos); mHandler.sendEmptyMessageDelayed(H_PROGRESS,1000); } break; } return false; } });
public MediaPlayerManager(){ mediaPlayer = new MediaPlayer(); }
public boolean isPlaying(){ return mediaPlayer.isPlaying(); }
public void startPlay(AssetFileDescriptor path){ try { mediaPlayer.reset();
mediaPlayer.setDataSource(path.getFileDescriptor(), path.getStartOffset(), path.getLength()); mediaPlayer.prepare(); mediaPlayer.start(); MEDIA_STATUS = MEDIA_STATUS_PLAY; mHandler.sendEmptyMessage(H_PROGRESS); } catch (IOException e) { LogUtils.e(e.toString()); e.printStackTrace(); } }
public void startPlay(String path){ try { mediaPlayer.reset(); mediaPlayer.setDataSource(path); mediaPlayer.prepare(); mediaPlayer.start(); MEDIA_STATUS = MEDIA_STATUS_PLAY; mHandler.sendEmptyMessage(H_PROGRESS); } catch (IOException e) { LogUtils.e(e.toString()); e.printStackTrace(); } }
public void pausePlay(){ if (isPlaying()){ mediaPlayer.pause(); MEDIA_STATUS = MEDIA_STATUS_PAUSE; mHandler.removeMessages(H_PROGRESS); } }
public void continuePlay(){ mediaPlayer.start(); MEDIA_STATUS = MEDIA_STATUS_PLAY; mHandler.sendEmptyMessage(H_PROGRESS); }
public void stopPlay(){ mediaPlayer.stop(); MEDIA_STATUS = MEDIA_STATUS_STOP; mHandler.removeMessages(H_PROGRESS); }
public int getCurrentPosition(){ return mediaPlayer.getCurrentPosition(); }
public int getDuration(){ return mediaPlayer.getDuration(); }
public void setLooping(boolean isLooping){ mediaPlayer.setLooping(isLooping); }
public void seekTo(int ms){ mediaPlayer.seekTo(ms); }
public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener){ mediaPlayer.setOnCompletionListener(listener); }
public void setOnErrorListener(MediaPlayer.OnErrorListener listener){ mediaPlayer.setOnErrorListener(listener); }
public void setOnProgressListener(OnMusicProgressListener listener){ musicProgressListener = listener; }
public interface OnMusicProgressListener{ void OnProgress(int progress,int pos); } }
|
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 40 41 42 43 44 45 46 47
| private MediaPlayerManager mGuideMusic; private ObjectAnimator mAnim;
private void startMusic() {
mGuideMusic = new MediaPlayerManager(); mGuideMusic.setLooping(true); AssetFileDescriptor file = getResources().openRawResourceFd(R.raw.guide); mGuideMusic.startPlay(file);
mGuideMusic.setOnComplteionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mGuideMusic.startPlay(file); } });
mAnim = AnimUtils.rotation(iv_music_switch); mAnim.start(); }
@Override public void onClick(View view) { switch (view.getId()){ case R.id.iv_music_switch: if(mGuideMusic.MEDIA_STATUS == MediaPlayerManager.MEDIA_STATUS_PAUSE){ mAnim.start(); mGuideMusic.continuePlay(); iv_music_switch.setImageResource(R.drawable.img_guide_music); }else if(mGuideMusic.MEDIA_STATUS == MediaPlayerManager.MEDIA_STATUS_PLAY){ mAnim.pause(); mGuideMusic.pausePlay(); iv_music_switch.setImageResource(R.drawable.img_guide_music_off); } break; } }
@Override protected void onDestroy() { super.onDestroy(); mGuideMusic.stopPlay(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public class AnimUtils {
public static ObjectAnimator rotation(View view) { ObjectAnimator mAnim = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f); mAnim.setDuration(2 * 1000); mAnim.setRepeatMode(ValueAnimator.RESTART); mAnim.setRepeatCount(ValueAnimator.INFINITE); mAnim.setInterpolator(new LinearInterpolator()); return mAnim; } }
|
###示例二
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
class PlayActivity : BaseActivity() {
private var type = 1
private val mediaPlayer = MediaPlayer()
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_play)
initView() initData() }
private fun initData() { initMediaPlayer() initVideoView() }
private fun initVideoView() { val uri = Uri.parse("android.resource://$packageName/${R.raw.video}") videoView.setVideoURI(uri) }
private fun initMediaPlayer() { val assetMessager = assets val fd = assetMessager.openFd("music.mp3") mediaPlayer.setDataSource(fd.fileDescriptor,fd.startOffset,fd.length) mediaPlayer.prepare() }
private fun initView() { btnType.setOnClickListener{ if (type == 1){ type = 2 btnPlay.setText("视频 Play") btnPause.setText("视频 Pause") btnStop.setText("视频 Stop") }else if (type == 2){ type = 1 btnPlay.setText("音频 Play") btnPause.setText("音频 Pause") btnStop.setText("音频 Stop") }else { type = -1 btnPlay.setText("-1") btnPause.setText("-1") btnStop.setText("-1") } }
btnPlay.setOnClickListener{ if (type == 1){ if (!mediaPlayer.isPlaying){ mediaPlayer.start() } }else if (type == 2){ if(!videoView.isPlaying){ videoView.start() } }else { Toast.makeText(this,"error",Toast.LENGTH_SHORT).show() } }
btnPause.setOnClickListener{ if (type == 1){ if (mediaPlayer.isPlaying){ mediaPlayer.pause() } }else if (type == 2){ if(videoView.isPlaying){ videoView.pause() } }else { Toast.makeText(this,"error",Toast.LENGTH_SHORT).show() } }
btnStop.setOnClickListener{ if (type == 1){ if (mediaPlayer.isPlaying){ mediaPlayer.reset() initMediaPlayer() } }else if (type == 2){ if(videoView.isPlaying){ videoView.resume() } }else { Toast.makeText(this,"error",Toast.LENGTH_SHORT).show() } } }
override fun onDestroy() { super.onDestroy()
mediaPlayer.stop() mediaPlayer.release()
videoView.suspend() }
companion object{ fun actionStart(context: Context){ val intent = Intent(context, PlayActivity::class.java) context.startActivity(intent) } } }
|
状态图及生命周期
MediaPlayer 用于控制视频/音频文件及流的播放,由状态机进行管理。它有下面的一些状态:
- Idle 状态及 End 状态:
- 在 MediaPlayer 创建实例或者调用 reset() 后,播放器就被创建了,这是处于 Idle(就绪)状态,调用 release() 后,就会变成 End(结束)状态,在这两种状态之间的就是 MediaPlayer 的生命周期。
- Error (错误)状态:
- 当处于 Idle 状态时,上层应用程序调用一些方法时,用户提供的回调函数 OnErrorListener.onError 将触发 MediaPlayer 状态到 Error 状态(或者其他原因导致播放失败时),可从中获取错误原因,并且可以调用 reset() 重新恢复到 Idle 状态。
- 当不再使用 MediaPlayer 时,需调用 release() ,以便 MediaPlayer 资源得到合理释放,因为当处于 End 时,代表本次生命周期已经终止,MediaPlayer 将不能再被使用,这时不能再回到其他状态。
- Initialized(初始化)状态:
- 当调用 setDataSource() 时,由 Idle 状态将变为 Initialized 状态,如果在非 Idle 状态时调用此方法,会抛出异常。
- Prepared(准备)状态:
- 由 Initialized 状态到达此状态有两种方式,同步(prepare() -> Prepared 状态)和异步(prepareAsync() -> Preparing 状态,这是一个瞬时状态,可理解为时间比较短。 -> Prepared 状态)。
- 前者主要使用本地音视频文件,后者主要使用网络数据,需要缓冲数据。
- 当应用层事先注册过 setOnPrepareListener 时,播放器内部将回调用户设置的 OnPrepareListener 中的 onPrapared()。
- 此状态时,上层应用可设置一些属性,如音视频的音量、screenOnWhilePlaying、looping 等。
- Started(播放控制)状态:
- 由 Prepared 状态时调用 start() 并成功返回时,开始播放控制状态。
- 回调函数为 OnBufferingUpdateListener.onBufferingUpdate,此函数主要使应用程序保持跟踪音视频流的 buffering(缓冲)。
- 在播放控制时可以是 Paused 和 Stopped 状态的,且当前的播放进度可以被调整(seekTo())。
- Paused(暂停)状态:
- 调用 MediaPlayer.pause() 时,进入此状态,这个过程是瞬时的,反之在播放器内部是异步过程的。
- 在状态更新并调用 isPlaying 函数前,将有一些耗时。已经缓冲过的数据流,也要耗费数秒。
- 可调用 start() 恢复到 Started 状态,playback 恢复到之前暂停时的位置,并开始播放。
- Stopped(停止)状态:
- 当调用 stop() 时,MediaPlayer 无论正处于 Started、Paused、Prepared 或 PlaybackCompleted 中的哪种状态,都将进入 Stopped 状态。
- 此状态时,playback 将不能开始,直到重新调用 prepare() 或 prepareAsync() 并处于 Prepared 状态时才可以开始。
- PlaybackCompleted(播放完成)状态:
- 可通过 getCurrentPosition() 跟踪和获取播放器的播放进度。
- 当在 Started 状态中,事先调用 setLooping(boolean) 并设置为 true,表示循环播放,MediaPlayer 依然处于 Started 状态,如果设置为 false,表示不循环播放,回调函数为 OnCompletion.onCompletion,并进入 PlaybackCompleted 状态。
- 此状态中,调用 start() ,将重启播放器从头开始播放数据。
备注:
- Seek 操作:
- 在 Seek 操作完成后,如果事先在 MediaPlayer 注册了 setOnSeekCompleteListener,播放器内部将回调 OnSeekComplete.onSeekComplete 函数。
- seekTo() 在 Prepared、Paused、PlaybackCompleted 状态时都可被调用。
- 播放器内部将回调的函数,需要用户事先通过 setOnXXXListener 来进行注册。
- 当已经处于 Started 、Paused、Stopped 状态时,此时再调用相应函数 start() 、pause()、stop() 是没有任何作用的,依然处于当前状态中。
从创建到 setDataSource 过程
create 到 setDisplay 过程
通过 getService 从 ServiceManager 获取对应的 MediaPlayerService,然后调用 native_setup 函数创建播放器,接着调用 setDataSource 把 URL 地址传入底层。当准备好后,通过 setDisplay 传入 SurfaceHolder,以便将解码出的数据放到 SurfaceHolder 中的 Surface。最后显示在 SurfaceView 上。
创建过程
实例化 MediaPlayer 有两种方式:
1、使用 new 的方式:
MediaPlayer mp = newMediaPlayer();
2、使用 create 的方式:这时不需要再调用 setDataSource 了,更适用于使用现成的资源。
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);
MediaPlayer.java1 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
| public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder, AudioAttributes audioAttributes, int audioSessionId) {
try { MediaPlayer mp = new MediaPlayer(); final AudioAttributes aa = audioAttributes != null ? audioAttributes : new AudioAttributes.Builder().build(); mp.setAudioAttributes(aa); mp.setAudioSessionId(audioSessionId); mp.setDataSource(context, uri); if (holder != null) { mp.setDisplay(holder); } mp.prepare(); return mp; } catch (IOException ex) { Log.d(TAG, "create failed:", ex); } catch (IllegalArgumentException ex) { Log.d(TAG, "create failed:", ex); } catch (SecurityException ex) { Log.d(TAG, "create failed:", ex); }
return null; }
|
备注
参考资料:
Android 音视频开发