Android开发—SeeJoPlayer视频播放器源码解析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
2020.03.27 v1.2.0 beta版:
更新说明:
一、完美支持android1.五、android1.六、android2.0、android2.0一、android2.1平台;
2、完美支持320×480、480×800、480×854等各类分辨率(自适应屏幕分辨率);
3、支持在线音视频播放,支持URL input和从浏览器调用SeeJoPlayer播放器播放在线音视频;
4、自动转为横屏播放,为用户提供更好的观看体验;
5、修改了没有SD卡程序出错的Bug;
6、美化了视频播放列表和操作说明的界面。
第一部份:功能介绍
SeeJoPlayer的优势要紧在相对还算美观的界面和便利的交互操作上。
先说操作吧,它支持:
一、全屏切换: 双击屏幕
二、播放/暂停: 长按屏幕
3、静音/恢复: 长按音量按钮
4、播放列表: 操纵面板最右边的按钮(暂不支持编辑功能)
五、音量调剂: 单击音量按钮,在弹出的音量显示区域触摸改变音量
这些操作和PC上的播放器较为类似,希望大伙儿能用得适应。
至于界面的话,多说无益,直接上图吧:
第二部份:源码解析
一、VideoView与视频比例缩放:
咱们能够很方便的取得VideoView的源代码,最简单的方式是直接在上找“.java”。
因此重写VideoView的进程其实只是在原先的基础上进行一些修改罢了,并非一个很麻烦的工作。
什么缘故Android自带的VideoView会维持视频的长宽比而不能让咱们很方便的自概念比例呢?我猜想可能Google 做Android也是一个很仓促的工程,许多代码并无考虑得太成熟。
VideoView的源码中有如此一段代码:
1 @Override
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
protected
void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
//Log.i("@@@@", "onMeasure");
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
if(mVideoWidth >0&& mVideoHeight >0){
if( mVideoWidth * height > width * mVideoHeight ){
//Log.i("@@@", "image too tall, correcting");
height = width * mVideoHeight / mVideoWidth;
}
else
if( mVideoWidth * height < width * mVideoHeight ){
//Log.i("@@@", "image too wide, correcting");
width = height * mVideoWidth / mVideoHeight;
}
else
{
//Log.i("@@@", "aspect ratio is correct: " +
//width+"/"+height+"="+
//mVideoWidth+"/"+mVideoHeight);
}
}
//Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height); setMeasuredDimension(width, height);
}
这确实是什么缘故长宽比不能改变的缘故了。
因为在OnMeasure的时候,就对那个长宽比进行了处置。
咱们把其中处置的代码屏蔽掉,视频大小就能够够随着VideoView的长宽改变而改变了。
1 2 3 4 5 6 7 protected
void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ //Log.i("@@@@", "onMeasure");
int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
8 9
10
11
12 int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
setMeasuredDimension(width,height);
}
二、视频操纵菜单与播放界面的层次问题:
看到过一些他人写的视频播放器,其中有一些朋友老是简简单单的将VideoView和操纵界面放在一个LinearLayout中。
如此随着操纵界面的出现与否,VideoView会随之改变长宽,给人的体验并非专门好。
因此,我以为VideoView和操纵界面最好不要放在同一个层次上。
不要偷懒,利用一个FrameLayout或PopupWindow就能够够解决那个问题。
例如,我就简简单单地利用了PopupWindow,那个具体实现上,就百花争鸣吧。
三、视频文件扫描:
视频文件的扫描,此刻想来要紧有两种方式:
第一种确实是直接读取媒体库中的视频文件数据库。
当Android启动的时候,系统会自动扫描sdcard,并为媒体文件成立(或更新)数据库。
咱们能够通过对应的URI来访问数据库,从而取得视频文件的列表:
1 2 3 4 5 6 7 8 9
10
11 private Uri videoListUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Cursor cursor =getContentResolver().query(videoListUri, new String[]{"_display_name","_data"}, null, null, null);
int n = cursor.getCount();
cursor.moveToFirst();
LinkedList<MovieInfo> playList2 =new LinkedList<MovieInfo>();
for(int i =0; i != n ;++i){
MovieInfo mInfo =new MovieInfo();
mInfo.displayName= cursor.getString(cursor.getColumnIndex("_display_name")); mInfo.path= cursor.getString(cursor.getColumnIndex("_data"));
playList2.add(mInfo);
12 cursor.moveToNext();
}
这种方式可能是最有效率的了,只是不知为何,媒体库中似乎没有扫描进本身支持的3GP视频格式(也可能我那个地址是一个特例) 。
只是,正是因为那个缘故,我才想到有可能需要另外一种最大体的扫描文件系统的方式来扫描视频文件。
这确实是文件系统的遍历:
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 private void getVideoFile(final LinkedList<MovieInfo> list,File file){ file.listFiles(new FileFilter(){
@Override
public boolean accept(File file){
// TODO Auto-generated method stub
String name = file.getName();
int i = name.indexOf('.');
if(i !=-1){
name = name.substring(i);
if(name.equalsIgnoreCase(".mp4")||name.equalsIgnoreCase(".3gp")){ MovieInfo mi =new MovieInfo();
mi.displayName= file.getName();
mi.path= file.getAbsolutePath();
list.add(mi);
return true;
}
}else if(file.isDirectory()){
getVideoFile(list, file);
}
return false;
}
});
}
固然,随着Android平台下的硬件设备愈来愈多,愈来愈壮大。
咱们有理由相信,它以后将不单单只支持MP4和3GP格式的视频文件,因此咱们必需利用两种方式结合的方式来取得最大的视频集合作为咱们的视频列表。
四、播放进程中进度条progress的设定:
视频开始播放了,那么一个小麻烦显现了:何时设定进度条才更有效率?我那个地址有一种方式供大伙儿参考,那确实是通过Handler自己给自己发消息来达到不断设置进度条的目的。
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17 Handler myHandler =new Handler(){
@Override
public void handleMessage(Message msg){
// TODO Auto-generated method stub
switch(msg.what){
case PROGRESS_CHANGED:
int i = vv.getCurrentPosition();
seekBar.setProgress(i);
i/=1000;
int minute = i/60;
int hour = minute/60;
int second = i%60;
minute %=60;
playedTextView.setText(String.format("%02d:%02d:%02d", hour,minute,second)); sendEmptyMessage(PROGRESS_CHANGED);
break;
固然,这种方式,需要第一发送一个初始消息来启动。
五、全屏与非全屏:
大伙儿都明白,一样一个Activity设置全屏的方式有两种,一是在OnCreate 中:
1 2 3 4 5 6 7 8 9 @Override
public void onCreate(Bundle icicle){
super.onCreate(icicle);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Window win = getWindow();
win.setFlags(youtParams.NO_STATUS_BAR_FLAG, youtParams.NO_STATUS_BAR_FLAG); setContentView(yout.mylayout);
二是在AndroidManifest.xml中:
1 2 3 <activity android:name=".MyActivity"
android:label=""
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
但是,这两种方式都不能达到咱们在视频播放进程中设置全屏与否的目的。
因为它们都只能在初始化的时候决定全屏与否。
那么我此刻要说的确实是第三种方式:
1getWindow().addFlags(youtParams.FLAG_FULLS CREEN);
1getWindow().clearFlags(youtParams.FLAG_FULL SCREEN);
这种方法就可以在Activity运行过程中,动态地改变全屏与否。
六、音量调剂:
音量调剂的方式其实很简单,只是有人问到,我就在那个地址顺便说下:
1 AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
2 setIndex(am.getStreamVolume(AudioManager.STREAM_MUSIC));。