Android鬼点子-Vitamio视频播放框架一篇就入门 | GreendaMi'Blog
视频播放是手机端很常用的需求,Vitamio是搜索里面词条出现最多的了。Vitamio是一款 Android 与 iOS 平台上的全能多媒体开发框架,全面支持硬件解码与 GPU 渲染。Vitamio 凭借其简洁易用的 API 接口赢得了全球众多开发者的青睐。到目前,全球已经有超过一万款应用在使用 Vitamio,覆盖用户超过 5亿。
本文主要介绍Vitamio在A你droid平台基本集成。
导入:下载回来后大家可能发现没有.project工程文件,可以通过File -> Import -> Android -> Existing Android Code Into Workspace来导入工程,然后改一下工程名称即可。
将VitamioBundle工程作为Android Library引入。
1234dependencies {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 | 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() { public void onProgressChanged(SeekBar seekBar, int i, boolean b) { } public void onStartTrackingTouch(SeekBar seekBar) { } //进度条抬起事件 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() { public void run() { while(mMediaPlayer != null ){ mHandler.post(new Runnable() { 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

GitHub求关注,求点心!
Vitamio也是使用了FFmpeg开源项目,除了Vitamio,BiliBili家的ijkplayer有兴趣的也可以了解一下。