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

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

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

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

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

MainActivity 的代码如下:

public class MainActivity extends Activity implements SurfaceHolder.Callback{
	private SurfaceView mSurface;
	private SurfaceHolder mSurfaceHolder;
	private LibVLC mLibVLC;
	private EventManager mEventManger;
	private boolean mIsPlaying;
	private int mVideoHeight;  
	private int mVideoWidth;  
	private int mSarNum;
	private int mSarDen;
	private int mSurfaceAlign;
	private static final int SURFACE_SIZE = 3;  	      
	private static final int SURFACE_BEST_FIT = 0;  
	private static final int SURFACE_FIT_HORIZONTAL = 1;  
	private static final int SURFACE_FIT_VERTICAL = 2;  
	private static final int SURFACE_FILL = 3;  
	private static final int SURFACE_16_9 = 4;  
	private static final int SURFACE_4_3 = 5;  
	private static final int SURFACE_ORIGINAL = 6;  
	private int mCurrentSize = SURFACE_BEST_FIT; 
	private static final String uri = "rtsp://217.146.95.166:554/live/ch6bqvga.3gp";
	private static final String TAG = "DTV";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSurface = (SurfaceView) findViewById(R.id.surface);
        mSurfaceHolder = mSurface.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurface.setKeepScreenOn(true);
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
        int pitch;
        String chroma = pref.getString("chroma_format", "");
        if(Util.isGingerbreadOrLater() && chroma.equals("YV12")) {
            mSurfaceHolder.setFormat(ImageFormat.YV12);
            pitch = ImageFormat.getBitsPerPixel(ImageFormat.YV12) / 8;
        } else if (chroma.equals("RV16")) {
            mSurfaceHolder.setFormat(PixelFormat.RGB_565);
            PixelFormat info = new PixelFormat();
            PixelFormat.getPixelFormatInfo(PixelFormat.RGB_565, info);
            pitch = info.bytesPerPixel;
        } else {
            mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
            PixelFormat info = new PixelFormat();
            PixelFormat.getPixelFormatInfo(PixelFormat.RGBX_8888, info);
            pitch = info.bytesPerPixel;
        }
        mSurfaceAlign = 16 / pitch - 1;
        enableIOMX(true);
        try {
        	
			mLibVLC = LibVLC.getInstance();		
		} catch (LibVlcException e) {
			Log.i(TAG, "LibVLC.getInstance() error:"+e.toString());
			e.printStackTrace();
			return ;
		}
        mEventManger = EventManager.getInstance();
        mEventManger.addHandler(mEventHandler);
    }
    
    private void enableIOMX(boolean enableIomx){
    	SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(VLCApplication.getAppContext());
    	Editor e = p.edit();
    	e.putBoolean("enable_iomx", enableIomx);
    	LibVLC.restart();
    }
    private DtvCallbackTask mDtvCallbackTask = new DtvCallbackTask(this) {
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			int n = 25;
			while((n-- != 0)&& !mIsPlaying){
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			if(!mIsPlaying){
				Log.i(TAG, "could not open media or internet not access");
			}
			
		}
	};
    private final VideoEventHandler mEventHandler = new VideoEventHandler(this);
    private class VideoEventHandler extends WeakHandler<MainActivity>{
    	public VideoEventHandler(MainActivity owner) {
            super(owner);
        }
    	@Override
    	public void handleMessage(Message msg) {
    		MainActivity activity = getOwner();    		
    		 if(activity == null) return;
    		 switch (msg.getData().getInt("event")) {
             case EventManager.MediaPlayerPlaying:
                 Log.i(TAG, "MediaPlayerPlaying");
                 mIsPlaying = true;
                 break;
             case EventManager.MediaPlayerPaused:
                 Log.i(TAG, "MediaPlayerPaused");
                 mIsPlaying = false;
                 break;
             case EventManager.MediaPlayerStopped:
                 Log.i(TAG, "MediaPlayerStopped");
                 mIsPlaying = false;
                 break;
             case EventManager.MediaPlayerEndReached:
                 Log.i(TAG, "MediaPlayerEndReached");
                 break;
             case EventManager.MediaPlayerVout:
                 break;
             case EventManager.MediaPlayerPositionChanged:
                 //don't spam the logs
                 break;
             default:
                 Log.e(TAG, String.format("Event not handled (0x%x)", msg.getData().getInt("event")));
                 break;
         }
    		super.handleMessage(msg);
    	}
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {	          
		mLibVLC.attachSurface(mSurfaceHolder.getSurface(), MainActivity.this,width,height);
		Log.i(TAG, " width="+ width+" height="+height);
	}


	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// TODO Auto-generated method stu
		
	}


	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO Auto-generated method stub
		mLibVLC.detachSurface();
		
	}
	 public void setSurfaceSize(int width, int height, int sar_num, int sar_den) {  
		 if (width * height == 0)
	            return;
	        // store video size
	        mVideoHeight = height;
	        mVideoWidth = width;
	        mSarNum = sar_num;
	        mSarDen = sar_den;
	        Message msg = mHandler.obtainMessage(SURFACE_SIZE);
	        mHandler.sendMessage(msg);
	    }  
	  
	    private final Handler mHandler = new VideoPlayerHandler(this);  
	  
	    private static class VideoPlayerHandler extends WeakHandler<MainActivity> {  
	        public VideoPlayerHandler(MainActivity owner) {  
	            super(owner);  
	        }  
	  
	        @Override  
	        public void handleMessage(Message msg) {  
	        	MainActivity activity = getOwner();  
	            if(activity == null) // WeakReference could be GC'ed early  
	                return;  
	  
	            switch (msg.what) {  
	                case SURFACE_SIZE:  
	                    activity.changeSurfaceSize();  
	                    break;  
	            }  
	        }  
	    };  
	      @Override
	    protected void onResume() { 
	    	super.onResume();
	    	if(mLibVLC != null){
	    		try{
	    		mLibVLC.readMedia(uri, false);
	    		}catch(Exception e){
	    			Log.i(TAG,e.toString());
	    			return;
	    		}
	    		mDtvCallbackTask.execute();
			}else {
				return;
			}
	    	
	    }
	      
	      private void changeSurfaceSize() {
	          // get screen size
	          int dw = getWindow().getDecorView().getWidth();
	          int dh = getWindow().getDecorView().getHeight();

	          // getWindow().getDecorView() doesn't always take orientation into account, we have to correct the values
	          boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
	          if (dw > dh && isPortrait || dw < dh && !isPortrait) {
	              int d = dw;
	              dw = dh;
	              dh = d;
	          }

	          // sanity check
	          if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {
	              Log.e(TAG, "Invalid surface size");
	              return;
	          }

	          // compute the aspect ratio
	          double ar, vw;
	          double density = (double)mSarNum / (double)mSarDen;
	          if (density == 1.0) {
	              /* No indication about the density, assuming 1:1 */
	              vw = mVideoWidth;
	              ar = (double)mVideoWidth / (double)mVideoHeight;
	          } else {
	              /* Use the specified aspect ratio */
	              vw = mVideoWidth * density;
	              ar = vw / mVideoHeight;
	          }

	          // compute the display aspect ratio
	          double dar = (double) dw / (double) dh;

	          switch (mCurrentSize) {
	              case SURFACE_BEST_FIT:
	                  if (dar < ar)
	                      dh = (int) (dw / ar);
	                  else
	                      dw = (int) (dh * ar);
	                  break;
	              case SURFACE_FIT_HORIZONTAL:
	                  dh = (int) (dw / ar);
	                  break;
	              case SURFACE_FIT_VERTICAL:
	                  dw = (int) (dh * ar);
	                  break;
	              case SURFACE_FILL:
	                  break;
	              case SURFACE_16_9:
	                  ar = 16.0 / 9.0;
	                  if (dar < ar)
	                      dh = (int) (dw / ar);
	                  else
	                      dw = (int) (dh * ar);
	                  break;
	              case SURFACE_4_3:
	                  ar = 4.0 / 3.0;
	                  if (dar < ar)
	                      dh = (int) (dw / ar);
	                  else
	                      dw = (int) (dh * ar);
	                  break;
	              case SURFACE_ORIGINAL:
	                  dh = mVideoHeight;
	                  dw = (int) vw;
	                  break;
	          }

	          // align width on 16bytes
	          int alignedWidth = (mVideoWidth + mSurfaceAlign) & ~mSurfaceAlign;

	          // force surface buffer size
	          mSurfaceHolder.setFixedSize(alignedWidth, mVideoHeight);

	          // set display size
	          LayoutParams lp = mSurface.getLayoutParams();
	          lp.width = dw * alignedWidth / mVideoWidth;
	          lp.height = dh;
	          mSurface.setLayoutParams(lp);
	          mSurface.invalidate();
	      }
	    @Override
	    protected void onDestroy() {
	    	if(mLibVLC.isPlaying()){
	    		mLibVLC.stop();
	    	}
	    	mLibVLC = null;
	    	super.onDestroy();
	    }
}

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大小的。

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

 private void enableIOMX(boolean enableIomx){
    	SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(VLCApplication.getAppContext());
    	Editor e = p.edit();
    	e.putBoolean("enable_iomx", enableIomx);
    	LibVLC.restart();
    }
将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”.


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

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


你可能感兴趣的:(RTSP,vlc-android,流媒体播放,libvlcjni)