vlc-android 中调用用libvlcjni.so实现流媒体播放,自己使用libvlcjni.so


最近公司搞的项目中涉及到流媒体播放,并且需要硬解码,所以想到了VLC这个开源项目。去官网下载了vlc-android源码进行编译,生成的apk安装在公司的设备上可以运行,不错不错,有现成的东西当然不会再去“造轮胎”,把编译后的android 工程导入eclipse 看了所有的代码,觉得对于我们只需要实现流媒体播放的来说显得有些累赘,这篇文章只需要实现流媒体播放的部分

关于源码下载和编译的部分可以查看:http://wiki.videolan.org/AndroidCompile

下面的代码有多部分是vlc-android工程源码,它们已经为我们封装好了要调用的jni函数和一些配置信息,这部分源码可以拿来就用。

1.创建一个android工程,界面很简单,就一个SurfaceView

MainActivity 的代码如下:

[java]  view plain copy
  1. public class MainActivity extends Activity implements SurfaceHolder.Callback{  
  2.     private SurfaceView mSurface;  
  3.     private SurfaceHolder mSurfaceHolder;  
  4.     private LibVLC mLibVLC;  
  5.     private EventManager mEventManger;  
  6.     private boolean mIsPlaying;  
  7.     private int mVideoHeight;    
  8.     private int mVideoWidth;    
  9.     private int mSarNum;  
  10.     private int mSarDen;  
  11.     private int mSurfaceAlign;  
  12.     private static final int SURFACE_SIZE = 3;              
  13.     private static final int SURFACE_BEST_FIT = 0;    
  14.     private static final int SURFACE_FIT_HORIZONTAL = 1;    
  15.     private static final int SURFACE_FIT_VERTICAL = 2;    
  16.     private static final int SURFACE_FILL = 3;    
  17.     private static final int SURFACE_16_9 = 4;    
  18.     private static final int SURFACE_4_3 = 5;    
  19.     private static final int SURFACE_ORIGINAL = 6;    
  20.     private int mCurrentSize = SURFACE_BEST_FIT;   
  21.     private static final String uri = "rtsp://217.146.95.166:554/live/ch6bqvga.3gp";  
  22.     private static final String TAG = "DTV";  
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_main);  
  27.         mSurface = (SurfaceView) findViewById(R.id.surface);  
  28.         mSurfaceHolder = mSurface.getHolder();  
  29.         mSurfaceHolder.addCallback(this);  
  30.         mSurface.setKeepScreenOn(true);  
  31.         SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);  
  32.         int pitch;  
  33.         String chroma = pref.getString("chroma_format""");  
  34.         if(Util.isGingerbreadOrLater() && chroma.equals("YV12")) {  
  35.             mSurfaceHolder.setFormat(ImageFormat.YV12);  
  36.             pitch = ImageFormat.getBitsPerPixel(ImageFormat.YV12) / 8;  
  37.         } else if (chroma.equals("RV16")) {  
  38.             mSurfaceHolder.setFormat(PixelFormat.RGB_565);  
  39.             PixelFormat info = new PixelFormat();  
  40.             PixelFormat.getPixelFormatInfo(PixelFormat.RGB_565, info);  
  41.             pitch = info.bytesPerPixel;  
  42.         } else {  
  43.             mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);  
  44.             PixelFormat info = new PixelFormat();  
  45.             PixelFormat.getPixelFormatInfo(PixelFormat.RGBX_8888, info);  
  46.             pitch = info.bytesPerPixel;  
  47.         }  
  48.         mSurfaceAlign = 16 / pitch - 1;  
  49.         enableIOMX(true);  
  50.         try {  
  51.               
  52.             mLibVLC = LibVLC.getInstance();       
  53.         } catch (LibVlcException e) {  
  54.             Log.i(TAG, "LibVLC.getInstance() error:"+e.toString());  
  55.             e.printStackTrace();  
  56.             return ;  
  57.         }  
  58.         mEventManger = EventManager.getInstance();  
  59.         mEventManger.addHandler(mEventHandler);  
  60.     }  
  61.       
  62.     private void enableIOMX(boolean enableIomx){  
  63.         SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(VLCApplication.getAppContext());  
  64.         Editor e = p.edit();  
  65.         e.putBoolean("enable_iomx", enableIomx);  
  66.         LibVLC.restart();  
  67.     }  
  68.     private DtvCallbackTask mDtvCallbackTask = new DtvCallbackTask(this) {  
  69.           
  70.         @Override  
  71.         public void run() {  
  72.             // TODO Auto-generated method stub  
  73.             int n = 25;  
  74.             while((n-- != 0)&& !mIsPlaying){  
  75.                 try {  
  76.                     Thread.sleep(200);  
  77.                 } catch (InterruptedException e) {  
  78.                     e.printStackTrace();  
  79.                 }  
  80.             }  
  81.             if(!mIsPlaying){  
  82.                 Log.i(TAG, "could not open media or internet not access");  
  83.             }  
  84.               
  85.         }  
  86.     };  
  87.     private final VideoEventHandler mEventHandler = new VideoEventHandler(this);  
  88.     private class VideoEventHandler extends WeakHandler<MainActivity>{  
  89.         public VideoEventHandler(MainActivity owner) {  
  90.             super(owner);  
  91.         }  
  92.         @Override  
  93.         public void handleMessage(Message msg) {  
  94.             MainActivity activity = getOwner();           
  95.              if(activity == nullreturn;  
  96.              switch (msg.getData().getInt("event")) {  
  97.              case EventManager.MediaPlayerPlaying:  
  98.                  Log.i(TAG, "MediaPlayerPlaying");  
  99.                  mIsPlaying = true;  
  100.                  break;  
  101.              case EventManager.MediaPlayerPaused:  
  102.                  Log.i(TAG, "MediaPlayerPaused");  
  103.                  mIsPlaying = false;  
  104.                  break;  
  105.              case EventManager.MediaPlayerStopped:  
  106.                  Log.i(TAG, "MediaPlayerStopped");  
  107.                  mIsPlaying = false;  
  108.                  break;  
  109.              case EventManager.MediaPlayerEndReached:  
  110.                  Log.i(TAG, "MediaPlayerEndReached");  
  111.                  break;  
  112.              case EventManager.MediaPlayerVout:  
  113.                  break;  
  114.              case EventManager.MediaPlayerPositionChanged:  
  115.                  //don't spam the logs  
  116.                  break;  
  117.              default:  
  118.                  Log.e(TAG, String.format("Event not handled (0x%x)", msg.getData().getInt("event")));  
  119.                  break;  
  120.          }  
  121.             super.handleMessage(msg);  
  122.         }  
  123.     }  
  124.   
  125.     @Override  
  126.     public boolean onCreateOptionsMenu(Menu menu) {  
  127.         // Inflate the menu; this adds items to the action bar if it is present.  
  128.         getMenuInflater().inflate(R.menu.main, menu);  
  129.         return true;  
  130.     }  
  131.       
  132.     @Override  
  133.     public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {                
  134.         mLibVLC.attachSurface(mSurfaceHolder.getSurface(), MainActivity.this,width,height);  
  135.         Log.i(TAG, " width="+ width+" height="+height);  
  136.     }  
  137.   
  138.   
  139.     @Override  
  140.     public void surfaceCreated(SurfaceHolder holder) {  
  141.         // TODO Auto-generated method stu  
  142.           
  143.     }  
  144.   
  145.   
  146.     @Override  
  147.     public void surfaceDestroyed(SurfaceHolder holder) {  
  148.         // TODO Auto-generated method stub  
  149.         mLibVLC.detachSurface();  
  150.           
  151.     }  
  152.      public void setSurfaceSize(int width, int height, int sar_num, int sar_den) {    
  153.          if (width * height == 0)  
  154.                 return;  
  155.             // store video size  
  156.             mVideoHeight = height;  
  157.             mVideoWidth = width;  
  158.             mSarNum = sar_num;  
  159.             mSarDen = sar_den;  
  160.             Message msg = mHandler.obtainMessage(SURFACE_SIZE);  
  161.             mHandler.sendMessage(msg);  
  162.         }    
  163.         
  164.         private final Handler mHandler = new VideoPlayerHandler(this);    
  165.         
  166.         private static class VideoPlayerHandler extends WeakHandler<MainActivity> {    
  167.             public VideoPlayerHandler(MainActivity owner) {    
  168.                 super(owner);    
  169.             }    
  170.         
  171.             @Override    
  172.             public void handleMessage(Message msg) {    
  173.                 MainActivity activity = getOwner();    
  174.                 if(activity == null// WeakReference could be GC'ed early    
  175.                     return;    
  176.         
  177.                 switch (msg.what) {    
  178.                     case SURFACE_SIZE:    
  179.                         activity.changeSurfaceSize();    
  180.                         break;    
  181.                 }    
  182.             }    
  183.         };    
  184.           @Override  
  185.         protected void onResume() {   
  186.             super.onResume();  
  187.             if(mLibVLC != null){  
  188.                 try{  
  189.                 mLibVLC.readMedia(uri, false);  
  190.                 }catch(Exception e){  
  191.                     Log.i(TAG,e.toString());  
  192.                     return;  
  193.                 }  
  194.                 mDtvCallbackTask.execute();  
  195.             }else {  
  196.                 return;  
  197.             }  
  198.               
  199.         }  
  200.             
  201.           private void changeSurfaceSize() {  
  202.               // get screen size  
  203.               int dw = getWindow().getDecorView().getWidth();  
  204.               int dh = getWindow().getDecorView().getHeight();  
  205.   
  206.               // getWindow().getDecorView() doesn't always take orientation into account, we have to correct the values  
  207.               boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;  
  208.               if (dw > dh && isPortrait || dw < dh && !isPortrait) {  
  209.                   int d = dw;  
  210.                   dw = dh;  
  211.                   dh = d;  
  212.               }  
  213.   
  214.               // sanity check  
  215.               if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {  
  216.                   Log.e(TAG, "Invalid surface size");  
  217.                   return;  
  218.               }  
  219.   
  220.               // compute the aspect ratio  
  221.               double ar, vw;  
  222.               double density = (double)mSarNum / (double)mSarDen;  
  223.               if (density == 1.0) {  
  224.                   /* No indication about the density, assuming 1:1 */  
  225.                   vw = mVideoWidth;  
  226.                   ar = (double)mVideoWidth / (double)mVideoHeight;  
  227.               } else {  
  228.                   /* Use the specified aspect ratio */  
  229.                   vw = mVideoWidth * density;  
  230.                   ar = vw / mVideoHeight;  
  231.               }  
  232.   
  233.               // compute the display aspect ratio  
  234.               double dar = (double) dw / (double) dh;  
  235.   
  236.               switch (mCurrentSize) {  
  237.                   case SURFACE_BEST_FIT:  
  238.                       if (dar < ar)  
  239.                           dh = (int) (dw / ar);  
  240.                       else  
  241.                           dw = (int) (dh * ar);  
  242.                       break;  
  243.                   case SURFACE_FIT_HORIZONTAL:  
  244.                       dh = (int) (dw / ar);  
  245.                       break;  
  246.                   case SURFACE_FIT_VERTICAL:  
  247.                       dw = (int) (dh * ar);  
  248.                       break;  
  249.                   case SURFACE_FILL:  
  250.                       break;  
  251.                   case SURFACE_16_9:  
  252.                       ar = 16.0 / 9.0;  
  253.                       if (dar < ar)  
  254.                           dh = (int) (dw / ar);  
  255.                       else  
  256.                           dw = (int) (dh * ar);  
  257.                       break;  
  258.                   case SURFACE_4_3:  
  259.                       ar = 4.0 / 3.0;  
  260.                       if (dar < ar)  
  261.                           dh = (int) (dw / ar);  
  262.                       else  
  263.                           dw = (int) (dh * ar);  
  264.                       break;  
  265.                   case SURFACE_ORIGINAL:  
  266.                       dh = mVideoHeight;  
  267.                       dw = (int) vw;  
  268.                       break;  
  269.               }  
  270.   
  271.               // align width on 16bytes  
  272.               int alignedWidth = (mVideoWidth + mSurfaceAlign) & ~mSurfaceAlign;  
  273.   
  274.               // force surface buffer size  
  275.               mSurfaceHolder.setFixedSize(alignedWidth, mVideoHeight);  
  276.   
  277.               // set display size  
  278.               LayoutParams lp = mSurface.getLayoutParams();  
  279.               lp.width = dw * alignedWidth / mVideoWidth;  
  280.               lp.height = dh;  
  281.               mSurface.setLayoutParams(lp);  
  282.               mSurface.invalidate();  
  283.           }  
  284.         @Override  
  285.         protected void onDestroy() {  
  286.             if(mLibVLC.isPlaying()){  
  287.                 mLibVLC.stop();  
  288.             }  
  289.             mLibVLC = null;  
  290.             super.onDestroy();  
  291.         }  
  292. }  

2.将vlc-android 中org.videolan.vlc包下面的这几个class 添加:

Aout.java

BitmapCache.java

EventManager.java

LibVLC.java

LibVlcException.java

TrackInfo.java

Util.java

VLCApplication.java

WeakHandler.java


3.将源码编译出的libs下的armeabi-v7a(如果设设备是arm6 或者以下,是armeabi)文件夹添加在android工程的libs下面

uri = "rtsp://217.146.95.166:554/live/ch6bqvga.3gp"是我在网上随便找的一个rtsp 流媒体地址

主要的部分是:

a. mLibVLC = LibVLC.getInstance();    用来获取mLIbVLC的实例,其中会初始化LibVLC,在AndroidManifest.xml中要添加android:name="org.videolan.vlc.VLCApplication"这样程序启动时会调用VLCApplication使其生成实例,不会导致LibVLC.getInstance()出错。

b.mLibVLC.readMedia(uri, false);调用这一句后如果uri地址可用,流媒体就开始在载入,并且播放,并不需要mLibVLC.play()。

c.mLibVLC.attachSurface(mSurfaceHolder.getSurface(), MainActivity.this,width,height);调用这句的时候如果视频不显示,界面突然退出,是因为没有添加:public void setSurfaceSize(int width, int height, int sar_num, int sar_den)这个函数(我编译源码的时候ANDROID_ABI=armeabi-v7a,ANDROID_ABI设置不同这个函数的参数不同),它在libvlcjni.c 的jni_SetAndroidSurfaceSize函数中调用,用来设置surfaceview大小的。

如果需要硬件解码,就需要添加以下方法:

[java]  view plain copy
  1. private void enableIOMX(boolean enableIomx){  
  2.     SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(VLCApplication.getAppContext());  
  3.     Editor e = p.edit();  
  4.     e.putBoolean("enable_iomx", enableIomx);  
  5.     LibVLC.restart();  
  6.    }  
将sharedpreferences 的key "enable_iomx'设置为true,因为libvlcjni.c 中通过函数libvlc_media_t *new_media(jlong instance, JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo)调用java 代码LibVLC.java 中的useIOMX()获取“enable_iomx”的值,然后判断是否用硬件解码。

在调试的过程中还会出现的错误是因为:Util.java 中String ANDROID_ABI = properties.getProperty("ANDROID_ABI");调用属性“ANDROID_ABI”的值时返回的是null导致,这主要发生在LibVLC.getInstance();时,自己判断一下,如果为ANDROID_ABI==null,就根据自己的设备选择赋值“armeabi-v7a”或者“armeabi”.


[html]  view plain copy
  1. mEventManger = EventManager.getInstance();  
  2. mEventManger.addHandler(mEventHandler);  
是用来添加播放事件的,当播放视频出现play,stop,pause等状态时,会接收到。

项目中碰到的问题就这些让我困惑了一阵,其余的可以通过谷歌或着度娘找到相应的方法。

你可能感兴趣的:(android-vlc,libvlcjni)