Android鬼点子-Vitamio视频播放框架一篇就入门


       视频播放是手机端很常用的需求,Vitamio是搜索里面词条出现最多的了。Vitamio是一款 Android 与 iOS 平台上的全能多媒体开发框架,全面支持硬件解码与 GPU 渲染。Vitamio 凭借其简洁易用的 API 接口赢得了全球众多开发者的青睐。到目前,全球已经有超过一万款应用在使用 Vitamio,覆盖用户超过 5亿。

       本文主要介绍Vitamio在A你droid平台基本集成。

  1. 下载
  1. 导入:下载回来后大家可能发现没有.project工程文件,可以通过File -> Import -> Android -> Existing Android Code Into Workspace来导入工程,然后改一下工程名称即可。

  2. 将VitamioBundle工程作为Android Library引入。

    1
    2
    3
    4
    dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile project(':vitamio')
    }

    注:作为Android Library引入是官方推荐的方式,当然也可以jar包的形式引入。

到此基本的准备工作就完成了。但是我在这里遇到了一点小问题,在引入的vitamio工程中,JNILib的路径是libs

1
2
3
4
5
6
7
8
9
10
11
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
jniLibs.srcDirs = ['libs']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
}
}

在我工程项目中还引用了其他框架,其他框架中也使用了JNI,但是那个框架的JNI路径不是在libs,两者有冲突,这里需要开发者手动调整一下。


下面是代码部分:

       我使用的是SurfaceView作为播放控件

1
2
3
4
5
6
7
8
9
10
11
12
<io.vov.vitamio.widget.CenterLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<SurfaceView
android:id="@+id/surface"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
</SurfaceView>
</io.vov.vitamio.widget.CenterLayout>

外部嵌套FrameLayout,放入其他控制用的控件,就可以实现了视频的播放控制。

1.首先初始化

1
Vitamio.isInitialized(this);

2.全屏设置

1
2
3
4
5
6
requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.player_layout);
butterknife.ButterKnife.bind(this);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

3.初始化对象

1
2
3
4
5
6
7
8
9
private MediaPlayer mMediaPlayer;
private SurfaceView mPreview;
private SurfaceHolder holder;
mPreview = (SurfaceView) findViewById(R.id.surface);
holder = mPreview.getHolder();
holder.addCallback(this);
holder.setFormat(PixelFormat.RGBA_8888);

4.关键播放代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void playVideo(String url) {
try {
// Create a new media player and set the listeners
mMediaPlayer = new MediaPlayer(this);
mMediaPlayer.setDataSource(url);
mMediaPlayer.setDisplay(holder);
mMediaPlayer.prepareAsync();
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnVideoSizeChangedListener(this);
mMediaPlayer.setOnInfoListener(this);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
} catch (Exception e) {
Log.e(TAG, "error: " + e.getMessage(), e);
}
}

这里绑定了几个监听器,用来监听播放状态。

MediaPlayer.OnSeekCompleteListener播放完毕监听:

1
2
3
4
5
6
7
8
@Override
public void onSeekComplete(MediaPlayer mp) {
mP.setVisibility(View.VISIBLE);
mDotView.setVisibility(View.INVISIBLE);
if(mP.getText().equals(getResources().getString(R.string.暂停))){
mp.start();
}
}

MediaPlayer.OnInfoListener缓冲监听:

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
public boolean onInfo(MediaPlayer arg0, int arg1, int arg2) {
switch (arg1) {
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
//开始缓存,暂停播放
if (arg0.isPlaying()) {
//stopPlayer();
arg0.pause();
needResume = true;
//如果是显示控制界面的时候,把播放键换成等待条
if(isControlling){
mP.setVisibility(View.INVISIBLE);
}
mDotView.setVisibility(View.VISIBLE);
}
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
//缓存完成,继续播放
if (needResume){
mDotView.setVisibility(View.INVISIBLE);
if(isControlling){
if(mP.getText().equals(getResources().getText(R.string.暂停))){
mP.setVisibility(View.VISIBLE);
arg0.start();
needResume = false;
}
}else{
arg0.start();
needResume = false;
}
}
break;
case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:
//显示 下载速度
Log.d(TAG, "download rate:" + arg2);
break;
}
return true;
}

MediaPlayer.OnPreparedListener视频加载完毕监听,我在这里绑定了SeekBar(mTimeline)的实时进度控制,获取视频的长度,获取当前的播放进度

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
public void onPrepared(MediaPlayer mediaplayer) {
Log.d(TAG, "onPrepared called");
mIsVideoReadyToBePlayed = true;
if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
startVideoPlayback();
}
mLength.setText(formatTime.formatTime(mMediaPlayer.getDuration()));
mTimeline.setMax(new Long(mMediaPlayer.getDuration()).intValue());
mTimeline.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
//进度条抬起事件
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mP.setVisibility(View.INVISIBLE);
mMediaPlayer.seekTo(seekBar.getProgress());
mCurrent.setText(formatTime.formatTime(new Integer(seekBar.getProgress()).longValue()));
mDotView.setVisibility(View.VISIBLE);
isControllingTime = 0;
}
});
//当前播放进度跟踪
new Thread(new Runnable() {
@Override
public void run() {
while(mMediaPlayer != null ){
mHandler.post(new Runnable() {
@Override
public void run() {
if(mMediaPlayer != null && mMediaPlayer.isPlaying()){
mCurrent.setText(formatTime.formatTime(mMediaPlayer.getCurrentPosition()));
mTimeline.setProgress(new Long(mMediaPlayer.getCurrentPosition()).intValue());
}
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}

获取当前播放进度:mMediaPlayer.getCurrentPosition();
暂停播放:mMediaPlayer.pause();
开始(继续)播放:mMediaPlayer.start();
是否正在播放:mMediaPlayer.isPlaying();

       由于整个代码比较长,而且因为只是做Vitamio的部分,其中还有一些播放的逻辑都混合在了一起没有解耦,所以代码就不贴出来了,需要的可以去github去下载。其中Player.class是一个Activity是播放器的关键代码。代码未经过完整测试,有任何bug欢迎联系zpybless@163.com

movielineage

GitHub求关注,求点心!

       Vitamio也是使用了FFmpeg开源项目,除了Vitamio,BiliBili家的ijkplayer有兴趣的也可以了解一下。

文章目录
|