好几天没写博客了,处理了一点个人私事加上平时加班,基本上时间不充裕,上篇文章讲了一下用Mediaplayer来播放音乐,这次就讲讲使用Mediaplayer来和SurfaceView配合播放一个视频流媒体。MediaPlayer不仅可以播放视频,还可以与SurfaceView的配合,SurfaceView主要用于显示MediaPlayer播放的视频流媒体的画面渲染,两者可以一起协同播放视频。
基础维护
先来看下要实现的界面:
如果你看过上篇文章,就发现其实很简单的就是多了一个进度条,还有一个就是SurfaceView,就是下面那块黑色区域;
布局文件代码:
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
|
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
tools:context=
"com.example.googlevideo.MainActivity"
>
<EditText
android:id=
"@+id/edit_musicPath"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:hint=
"输入MV的路径"
/>
<SeekBar
android:id=
"@+id/seekBar_video"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
/>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"horizontal"
>
<Button
android:id=
"@+id/btn_Play"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"playEvent"
android:text=
"播放"
/>
<Button
android:id=
"@+id/btn_Pause"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"pauseEvent"
android:text=
"暂停"
/>
<Button
android:id=
"@+id/btn_Stop"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"stopEvent"
android:text=
"停止"
/>
<Button
android:id=
"@+id/btn_Replay"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"replayEvent"
android:text=
"重播"
/>
</LinearLayout>
<SurfaceView
android:id=
"@+id/surface_video"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
</LinearLayout>
|
Demo实现
实现Demo之前应该讲讲视频播放的原理,先确定视频的格式,这个和解码相关,不同的格式视频编码不同,然后通过编码格式进行解码,最后得到一帧一帧的图像,并把这些图像快速的显示在界面上,即为播放一段视频。SurfaceView在Android中就是完成这个功能的。SurfaceView是配合MediaPlayer使用的,MediaPlayer也提供了相应的方法设置SurfaceView显示图片,只需要为MediaPlayer指定SurfaceView显示图像即可。它的完整签名:void setDisplay(SurfaceHolder sh)。它需要传递一个SurfaceHolder对象,SurfaceHolder可以理解为SurfaceView装载需要显示的一帧帧图像的容器,它可以通过SurfaceHolder.getHolder()方法获得。使用MediaPlayer配合SurfaceView播放视频的步骤与播放使用MediaPlayer播放MP3大体一致,只需要额外设置显示的SurfaceView即可。
先准备一段能播放的视频:
播放视频效果图:
代码如下:
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
|
editText = (EditText) findViewById(R.id.edit_musicPath);
pathString = editText.getText().toString().trim();
File file =
new
File(pathString);
if
(file.exists()) {
try
{
mediaPlayer =
new
MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(pathString);
//通过SurfaceView获取的Holder
mediaPlayer.setDisplay(holder);
mediaPlayer.prepare();
mediaPlayer.start();
btn_PlayButton.setEnabled(
false
);
//设置Bar的最大值
int
max=mediaPlayer.getDuration();
seekBarVideo.setMax(max);
//定时器更新进度条
timer=
new
Timer();
timeTask=
new
TimerTask() {
@Override
public
void
run() {
// TODO Auto-generated method stub
seekBarVideo.setProgress(mediaPlayer.getCurrentPosition());
}
};
timer.schedule(timeTask,
0
,
500
);
//视频播放完之后重新设置显示
mediaPlayer.setOnCompletionListener(
new
OnCompletionListener() {
@Override
public
void
onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
btn_PlayButton.setEnabled(
true
);
}
});
}
catch
(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
Toast.makeText(
this
,
"Sorry,你输入的路径有问题,请仔细检查"
, Toast.LENGTH_SHORT)
.show();
}
|
SeekBar的使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//设置Bar的最大值
int
max=mediaPlayer.getDuration();
seekBarVideo.setMax(max);
//定时器更新进度条
timer=
new
Timer();
timeTask=
new
TimerTask() {
@Override
public
void
run() {
// TODO Auto-generated method stub
seekBarVideo.setProgress(mediaPlayer.getCurrentPosition());
}
};
timer.schedule(timeTask,
0
,
500
);
|
其中holder是SurfaceHolder,在onCreate中获取:
1
2
3
4
|
surfaceView = (SurfaceView) findViewById(R.id.surface_video);
holder = surfaceView.getHolder();
//兼容4.0以后的手机版本,本身是不维护的
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
如果正在播放视频,最小化的时候是会有声音的,需要在回调函数中处理一下:
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
|
holder.addCallback(
new
Callback() {
@Override
public
void
surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.i(tag,
"销毁了Holder"
);
if
(mediaPlayer!=
null
&&mediaPlayer.isPlaying()) {
currentPosition=mediaPlayer.getCurrentPosition();
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=
null
;
timer.cancel();
timeTask.cancel();
timer=
null
;
timeTask=
null
;
}
}
@Override
public
void
surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Log.i(tag,
"创建了Holder"
);
if
(currentPosition>
0
) {
try
{
mediaPlayer =
new
MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(pathString);
//通过SurfaceView获取的Holder
mediaPlayer.setDisplay(holder);
mediaPlayer.prepare();
mediaPlayer.start();
mediaPlayer.seekTo(currentPosition);
btn_PlayButton.setEnabled(
false
);
//视频播放完之后重新设置显示
mediaPlayer.setOnCompletionListener(
new
OnCompletionListener() {
@Override
public
void
onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
btn_PlayButton.setEnabled(
true
);
}
});
int
max=mediaPlayer.getDuration();
seekBarVideo.setMax(max);
//定时器更新进度条
timer=
new
Timer();
timeTask=
new
TimerTask() {
@Override
public
void
run() {
// TODO Auto-generated method stub
seekBarVideo.setProgress(mediaPlayer.getCurrentPosition());
}
};
timer.schedule(timeTask,
0
,
500
);
}
catch
(Exception e) {
// TODO: handle exception
}
}
}
@Override
public
void
surfaceChanged(SurfaceHolder holder,
int
format,
int
width,
int
height) {
// TODO Auto-generated method stub
Log.i(tag,
"改变了Holder"
);
}
});
|
进度条是SeekBar,如果需要快进或者后退的时候是需要将焦点赋值给mediaPlayer的:
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
|
seekBarVideo=(SeekBar) findViewById(R.id.seekBar_video);
seekBarVideo.setOnSeekBarChangeListener(
new
OnSeekBarChangeListener() {
@Override
public
void
onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
int
position=seekBar.getProgress();
if
(mediaPlayer!=
null
&&mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(position);
}
}
@Override
public
void
onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public
void
onProgressChanged(SeekBar seekBar,
int
progress,
boolean
fromUser) {
// TODO Auto-generated method stub
}
});
|
暂停事件:
1
2
3
4
5
6
7
8
9
10
11
|
public
void
pauseEvent(View view) {
if
(btn_PauseButton.getText().equals(
"继续"
)) {
mediaPlayer.start();
btn_PauseButton.setText(
"暂停"
);
return
;
}
if
(mediaPlayer !=
null
&& mediaPlayer.isPlaying()) {
mediaPlayer.pause();
btn_PauseButton.setText(
"继续"
);
}
}
|
停止事件:
1
2
3
4
5
6
7
8
9
10
11
|
public
void
stopEvent(View view) {
if
(mediaPlayer !=
null
&& mediaPlayer.isPlaying()) {
btn_PlayButton.setEnabled(
true
);
mediaPlayer.stop();
// 释放mediaplayer否则的话会占用内存
mediaPlayer.release();
mediaPlayer =
null
;
}
btn_PauseButton.setText(
"暂停"
);
btn_PlayButton.setEnabled(
true
);
}
|
重播事件:
1
2
3
4
5
6
7
8
9
10
|
public
void
replayEvent(View view) {
surfaceView.setVisibility(View.VISIBLE);
if
(mediaPlayer !=
null
&& mediaPlayer.isPlaying()) {
mediaPlayer.seekTo(
0
);
}
else
{
playEvent(view);
}
// 重播的时候应该设置播放的状态
btn_PlayButton.setEnabled(
true
);
}
|