Android平台下SeeJoPlayer视频播放器(功能介绍及源码解析)

更新说明:

    1、完美支持android1.5、android1.6、android2.0、android2.01、android2.1平台;
    2、完美支持320×480、480×800、480×854等各种分辨率(自适应屏幕分辨率);
    3、支持在线音视频播放,支持URL input和从浏览器调用SeeJoPlayer播放器播放在线音视频;
    4、自动转为横屏播放,为用户提供更好的观看体验;
    5、修改了没有SD卡程序出错的Bug;
    6、美化了视频播放列表和操作说明的界面。

    补充图片:
    




URL输入功能

    




浏览器中调用SeeJoPlayer播放


    感谢大家对SeeJoPlayer的大力支持!希望新版本能带给大家更好的体验!


    2010.01.24    SeeJoPlayer v1.0.0版:  

    SeeJoPlayer是我利用业余时间开发的一款免费的视频播放器。主要是现在在网上似乎找不到一个Android平台下的界面美观一点的视频 播放器。而作为智能手机操作系统的Android,没有一个像样一点的视频播放器,岂不糗大了。所以,我就写了这么一个砖头并开出源码,希望能引出高手们 的美玉来吧!

          下载APK程序           下载源代码           转到eoeMarket发布页

    


 第一部分:功能介绍


    SeeJoPlayer的优点主要在相对还算美观的界面和便捷的交互操作上。先说操作吧,它支持:

    1、全屏切换:       双击屏幕
    2、播放/暂停:     长按屏幕
    3、静音/恢复:     长按音量按钮
    4、播放列表:       控制面板最右边的按钮(暂不支持编辑功能)
    5、音量调节:       单击音量按钮,在弹出的音量显示区域触摸改变音量
 

    这些操作和PC上的播放器较为类似,希望大家能用得习惯。

    至于界面的话,多说无益,直接上图吧:



横屏

 



竖屏

 



全屏

 



非全屏

 



播放列表

 



介绍说明


    好了。功能介绍部分到此为止了。如果您觉得这个软件还行的话,欢迎下载使用!

          下载APK程序           下载源代码           转到eoeMarket发布页



第二部分:源码解析
 

    SeeJoPlayer不是一个完美的作品,可以说,它在很多地方都不尽如人意。当然一个完美的作品,也不是我写这款播放器的目的。我只是希望以此为引,结合大家共同的智慧开发出一款真正完美强大的Android平台下的国产视频播放器出来。

    SeeJoPlayer有许多不足之处,例如,它只支持系统默认的视频格式,因为它使用系统默认的解码器。这,一方面是因为如果通过软解码的话, 播放视频的效率会很受影响,另外最主要的原因当然还是个人水平、精力有限,没办法接着往下做了。如果大家觉得这份代码还多少有些参考价值的话,不妨拿去 用。只是希望当你们以此为参考,开发出真正强大的播放器出来的时候,别忘了如果能开放源码的话,一定开放出来。毕竟开源软件就好比能够进化的物种,提供你 的DNA出来,让我们共同的软件变得越来越完美吧!

    好了,废话不说了。播放器的全部源码本文中已经提供了下载地址。下面,我就其中我觉得可能值得关注的地方做一些解释。

    一、VideoView与视频比例缩放:

    以前在论坛上也看到有人问过如何实现视频按比例缩放的问题。的确,如果仅仅使用VideoView可能达不到我们想要达到的效果。这就需要我们对 VideoView做一些改动,简单的说就是另外写一个类似VideoView的类出来(庆幸Android是开源的)。

    我们可以很方便的获得VideoView的源代码,最简单的方法是直接在GoogleCodeSearch 上 找“VideoView.java”。所以重写VideoView的过程其实只是在原来的基础上进行一些修改而已,并非一个很麻烦的工作。为什么 Android自带的VideoView会保持视频的长宽比而不能让我们很方便的自定义比例呢?我猜想可能Google做Android也是一个很仓促的 工程,许多代码并没有考虑得太成熟。

    VideoView的源码中有这样一段代码:

 1 @Override
 2      protected   void  onMeasure( int  widthMeasureSpec,  int  heightMeasureSpec)  {
 3          // Log.i("@@@@", "onMeasure");
 4          int  width  =  getDefaultSize(mVideoWidth, widthMeasureSpec);
 5          int  height  =  getDefaultSize(mVideoHeight, heightMeasureSpec);
 6          if  (mVideoWidth  >   0   &&  mVideoHeight  >   0 {
 7              if  ( mVideoWidth  *  height   >  width  *  mVideoHeight )  {
 8                  // Log.i("@@@", "image too tall, correcting");
 9                 height  =  width  *  mVideoHeight  /  mVideoWidth;
10             }
  else   if  ( mVideoWidth  *  height   <  width  *  mVideoHeight )  {
11                  // Log.i("@@@", "image too wide, correcting");
12                 width  =  height  *  mVideoWidth  /  mVideoHeight;
13             }
  else   {
14                  // Log.i("@@@", "aspect ratio is correct: " +
15                          // width+"/"+height+"="+
16                          // mVideoWidth+"/"+mVideoHeight);
17             }

18         }

19          // Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height);
20         setMeasuredDimension(width, height);
21     }

22


    这就是为什么长宽比不能改变的原因了。因为在OnMeasure的时候,就对这个长宽比进行了处理。

    我们把其中处理的代码屏蔽掉,视频大小就可以随着VideoView的长宽改变而改变了。

 1 @Override
 2      protected   void  onMeasure( int  widthMeasureSpec,  int  heightMeasureSpec)  {
 3          // Log.i("@@@@", "onMeasure");
 4          int  width  =  getDefaultSize(mVideoWidth, widthMeasureSpec);
 5          int  height  =  getDefaultSize(mVideoHeight, heightMeasureSpec);
 6          /* if (mVideoWidth > 0 && mVideoHeight > 0) {
 7             if ( mVideoWidth * height  > width * mVideoHeight ) {
 8                 //Log.i("@@@", "image too tall, correcting");
 9                 height = width * mVideoHeight / mVideoWidth;
10             } else if ( mVideoWidth * height  < width * mVideoHeight ) {
11                 //Log.i("@@@", "image too wide, correcting");
12                 width = height * mVideoWidth / mVideoHeight;
13             } else {
14                 //Log.i("@@@", "aspect ratio is correct: " +
15                         //width+"/"+height+"="+
16                         //mVideoWidth+"/"+mVideoHeight);
17             }
18         } */

19          // Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height);
20         setMeasuredDimension(width,height);
21     }



    二、视频控制菜单与播放界面的层次问题:

    看到过一些别人写的视频播放器,其中有一些朋友老是简简单单的将VideoView和控制界面放在一个LinearLayout中。这样随着控制 界面的出现与否,VideoView会随之改变长宽,给人的体验并不很好。所以,我认为VideoView和控制界面最好不要放在同一个层次上。不要偷 懒,使用一个FrameLayout或者PopupWindow就可以解决这个问题。例如,我就简简单单地使用了PopupWindow,这个具体实现 上,就百花争鸣吧。

    三、视频文件扫描:

    视频文件的扫描,现在想来主要有两种方式:

    第一种就是直接读取媒体库中的视频文件数据库。当Android启动的时候,系统会自动扫描sdcard,并为媒体文件建立(或者更新)数据库。我们可以通过对应的URI来访问数据库,从而得到视频文件的列表:

 1 private  Uri videoListUri  =  MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
 2
 3
 4
 5 Cursor cursor  =  getContentResolver().query(videoListUri,  new  String[] { " _display_name " , " _data " } null null null );
 6          int  n  =  cursor.getCount();
 7         cursor.moveToFirst();
 8         LinkedList < MovieInfo >  playList2  =   new  LinkedList < MovieInfo > ();
 9          for ( int  i  =   0  ; i  !=  n ;  ++ i) {
10             MovieInfo mInfo  =   new  MovieInfo();
11             mInfo.displayName  =  cursor.getString(cursor.getColumnIndex( " _display_name " ));
12             mInfo.path  =  cursor.getString(cursor.getColumnIndex( " _data " ));
13             playList2.add(mInfo);
14             cursor.moveToNext();
15         }



    这种方法可能是最有效率的了,不过不知为何,媒体库中似乎没有扫描进本身支持的3GP视频格式(也可能我这里是一个特例) 。不过,正是因为这个原因,我才想到有可能需要另外一种最基本的扫描文件系统的方法来扫描视频文件。这就是文件系统的遍历:

 1      private   void  getVideoFile( final  LinkedList < MovieInfo >  list,File file) {
 2         
 3         file.listFiles( new  FileFilter() {
 4
 5             @Override
 6              public   boolean  accept(File file)  {
 7                  //  TODO Auto-generated method stub
 8                 String name  =  file.getName();
 9                  int  i  =  name.indexOf( ' . ' );
10                  if (i  !=   - 1 ) {
11                     name  =  name.substring(i);
12                      if (name.equalsIgnoreCase( " .mp4 " ) || name.equalsIgnoreCase( " .3gp " )) {
13                         
14                         MovieInfo mi  =   new  MovieInfo();
15                         mi.displayName  =  file.getName();
16                         mi.path  =  file.getAbsolutePath();
17                         list.add(mi);
18                          return   true ;
19                     }

20                 }
else   if (file.isDirectory()) {
21                     getVideoFile(list, file);
22                 }

23                  return   false ;
24             }

25         }
);
26     }


    当然,随着Android平台下的硬件设备越来越多,越来越强大。我们有理由相信,它以后将不仅仅只支持MP4和3GP格式的视频文件,所以我们必须使用两种方式结合的方法来获得最大的视频集合作为我们的视频列表。

    四、播放过程中进度条progress的设定:

    视频开始播放了,那么一个小麻烦出现了:什么时候设定进度条才更有效率?我这里有一种方法供大家参考,那就是通过Handler自己给自己发消息来达到不断设置进度条的目的。

 1     Handler myHandler  =   new  Handler() {
 2     
 3         @Override
 4          public   void  handleMessage(Message msg)  {
 5              //  TODO Auto-generated method stub
 6             
 7              switch (msg.what) {
 8             
 9                  case  PROGRESS_CHANGED:
10                     
11                      int  i  =  vv.getCurrentPosition();
12                     seekBar.setProgress(i);
13                     
14                     i /= 1000 ;
15                      int  minute  =  i / 60 ;
16                      int  hour  =  minute / 60 ;
17                      int  second  =  i % 60 ;
18                     minute  %=   60 ;
19                     playedTextView.setText(String.format( " %02d:%02d:%02d " , hour,minute,second));
20                     
21                     sendEmptyMessage(PROGRESS_CHANGED);
22                      break ;
23
24
25
26    

    
    当然,这种方法,需要首先发送一个初始消息来启动。

    五、全屏与非全屏:

    大家都知道,一般一个Activity设置全屏的方法有两种,一是在OnCreate中:

 1 @Override   
 2 public   void  onCreate(Bundle icicle)  {   
 3      super .onCreate(icicle);   
 4        
 5     requestWindowFeature(Window.FEATURE_NO_TITLE);           
 6     Window win  =  getWindow();   
 7     win.setFlags(WindowManager.LayoutParams.NO_STATUS_BAR_FLAG,   
 8             WindowManager.LayoutParams.NO_STATUS_BAR_FLAG);   
 9        
10     setContentView(R.layout.mylayout);  
11
12
13


    二是在AndroidManifest.xml中:

1 < activity android:name = " .MyActivity "  
2           android:label = ""
3           android:theme = " @android:style/Theme.NoTitleBar.Fullscreen " >


    然而,这两种方法都不能达到我们在视频播放过程中设置全屏与否的目的。因为它们都只能在初始化的时候决定全屏与否。那么我现在要说的就是第三种方法:

1 getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

 

1 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);


    这种方法就可以在Activity运行过程中,动态地改变全屏与否。

    六、音量调节:

    音量调节的方法其实很简单,不过有人问到,我就在这里顺便说下:

1         AudioManager am  =  (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
2         setIndex(am.getStreamVolume(AudioManager.STREAM_MUSIC));


    好了,就写这些了吧。可能这些知识有人知道,或者还有些盲点我没有讲到。欢迎大家与我联系,大家一起多多讨论交流,并且整个源码都开放出来了,大 家一定可以把来龙去脉弄得一清二楚的!最后,多谢大家听我罗嗦,欢迎使用SeeJoPlayer,欢迎阅读其源码!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值