Android实现播放本地视频

Android实现播放本地视频

完成《Android实现录像功能》和《Android实现选择视频功能》后,已经实现了神马笔记插入视频的功能,接下来完成视频笔记的最后一个功能——播放。

一、目标

播放视频笔记中的视频。

二、播放视频

播放本地视频可以使用VideoViewMediaPlayer + SurfaceView两种方式。

目前VideoView已经足够满足要求,因此采用VideoView方式。

1. 播放单独视频

设置视频路径setVideoURI()后,调用start()方法即可播放视频。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
VideoItem item = this.getItem();
if (item == null) {
return;
}

if (!isReady) {
videoView.setVideoURI(item.getUri());
isReady = true;
}

{
videoView.start();

actionBtn.setVisibility(View.INVISIBLE);
// photoView.setVisibility(View.INVISIBLE); // when rendering start, make it invisible
videoView.setVisibility(View.VISIBLE);
}

{
item.setPlaying(true);
}

2. 暂停播放视频

调用pause()接口,暂停播放。

1
2
3
4
5
6
7
8
9
10
VideoItem item = this.getItem();
if (item == null) {
return;
}

if (videoView.isPlaying()) {
videoView.pause();

actionBtn.setVisibility(View.VISIBLE);
}

3. 处理视频播放结束

释放资源并调整界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
VideoItem item = this.getItem();
if (item != null) {
item.setPlaying(false);
}

{
actionBtn.setVisibility(View.VISIBLE);
photoView.setVisibility(View.VISIBLE);
// videoView.setVisibility(View.INVISIBLE); // keep it visible to avoid black slash
}

{
videoView.stopPlayback();
this.isReady = false;
}

4. 处理Detach事件

VideoView在detach时,会释放MediaPlayer,不进行处理的话会显示黑屏。

因此需要调整界面。

1
2
3
4
5
6
7
public void surfaceDestroyed(SurfaceHolder holder)
{
// after we return from this we can't use the surface any more
mSurfaceHolder = null;
if (mMediaController != null) mMediaController.hide();
release(true);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
super.onViewDetachedFromWindow();

VideoItem item = this.getItem();
if (item != null) {
item.setPlaying(false);
}

{
actionBtn.setVisibility(View.VISIBLE);
photoView.setVisibility(View.VISIBLE);
videoView.setVisibility(View.INVISIBLE);
}

{
videoView.stopPlayback();
this.isReady = false;
}

5. 处理onStop事件

Activity切换到后台时,同样会释放MediaPlayer,因此需要调整界面。

三、播放多个视频

播放单个视屏时,需要处理用户播放及暂停视频,以及视频播放结束、Detach事件、onStop事件等事件。

Android支持同时播放多个视频,使用多个VideoView可以同时播放多个视频。

神马笔记对视频播放进行了显示,同一个时间只能播放一个视频。

因此,播放新视频的同时,停止上一个视频的播放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void setPlayingItem(ChatItem item, boolean stopPrevious) {
if (playingItem == item) {
return;
}

ChatItem previous = playingItem;
this.playingItem = item;

if (previous != null && stopPrevious) {
int position = previous.getParent().indexOf(previous);
recyclerView.getAdapter().notifyItemChanged(position);

ChatViewHolder holder = ChatViewHolderUtils.find(recyclerView, previous);
if (holder != null) {
holder.stop();
}
}
}

四、视频和音频同时播放

Android使用多个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
26
27
28
29
public void setPlaying(boolean value) {
VideoHelper videoHelper = getVideoHelper();

if (value) {

// stop audio
{
parent.getTapeHelper().stop();
parent.getTapeHelper().resetSpeaker();
}

{
videoHelper.setPlayingItem(this, true);
}

} else {
if (isPlaying()) {

// reset speaker
{
parent.getTapeHelper().setSpeakerOn(parent.getTapeHelper().isSpeakerOn(), true);
}

{
videoHelper.setPlayingItem(null, false);
}
}
}
}
  • 播放音频时,停止视频播放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void start() {
{
TapeHelper tapeHelper = getTapeHelper();

tapeHelper.setSpeakerOn(tapeHelper.isSpeakerOn(), true);
tapeHelper.setWakeLock(true);
tapeHelper.setScreenOn(true);
}

{
parent.getVideoHelper().stop();
}

{
AudioPlayer player = this.getAudioPlayer();
player.setTarget(this.getFile());
player.start();
}
}

五、处理扬声器

播放音频时,神马笔记允许选择听筒或者扬声器进行输出。

但播放视频只使用扬声器。

六、处理录音

Android允许录音和播放视频同时进行。

同样的,神马笔记对此进行了限制。

同样的,二选一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void requestTape() {
this.tapeHelper.stop();
this.videoHelper.stop();
this.tapeHelper.resetSpeaker();

PermissionHelper helper = new PermissionHelper(getActivity());
helper.setPermission(Manifest.permission.RECORD_AUDIO);

final boolean isGranted = helper.isGranted();
PermissionHelper.OnPermissionListener consumer = (h) -> {

bottomBar.chatBar.setVisibility(View.INVISIBLE);

popupTape.show(isGranted);
};

String requestMsg = "请允许录制音频,以发送语音。";
String deniedMsg = "请在「权限管理」,设置允许「麦克风」,以发送语音。";

helper.setRequestMessage(requestMsg);
helper.setDeniedMessage(deniedMsg);
helper.setOnPermissionListener(consumer);
helper.request();
}

七、开发过程回顾

首先,我们处理了单个视频的播放,允许用户播放及暂停,然后处理视频播放结束后,用户可以重新开始播放,并且处理了detach和onStop两个事件。

接下来,处理多个视频播放,对此进行了限制,同一时间只能播放一个视频。

然后,处理视屏和音频播放问题,同一时间只能播放一种。

再来,处理扬声器问题,播放视频时,只会将音频输出到扬声器。

最后,处理视频和录音的问题,同样是二选一。

分类 关系 说明
视频 单个 处理用户播放及暂停,并且处理结束事件、detach事件和onStop事件。
多个 一次只允许播放一个视频。
相关 音频 视频和音频,二选一。
扬声器 只输出到扬声器,不能使用听筒。
录音 视频和录音,二选一。

八、接下来

提交视频笔记版本测试,顺利的话,明天就能发布。

九、Finally

须菩提。 菩萨应如是布施。不住于相。 何以故。 若菩萨不住相布施。 其福德不可思量。