android 通过调用VLC的libvlcjni.so解码视频和rtsp流

前言
    我的小程序实现了用VLC的解码库播放远程RTSP流,并实现远程控制
  1. 前期准备
  • 下载NDK 地址:谷歌官方地址 
  •  clone vlc的的源码并编译成功 编译及源码下载详细请参考 VLC的官方wiki 
     2.  移植

  • 将vlc-android/libs/下拷贝到你的工程下合并libs目录
  • 需要将下图的vlc-android目录下的源码移植到自己的工程下
  • android 通过调用VLC的libvlcjni.so解码视频和rtsp流_第1张图片
   3. 下面是我的参考代码
      layout的xml文件
     
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.test.MainActivity" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/ip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="0dp"
            android:layout_weight="20"
            android:maxLines="1"
            android:singleLine="true" />

        <Button
            android:id="@+id/con"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="0dp"
            android:layout_toRightOf="@+id/ip"
            android:layout_weight="1"
            android:text= "连接" />
    </LinearLayout>

    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="50dp" />

</RelativeLayout>

主要要代码
    
package com.example.test;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;

import org.videolan.libvlc.EventHandler;
import org.videolan.libvlc.IVideoPlayer;
import org.videolan.libvlc.LibVLC;
import org.videolan.libvlc.LibVlcException;
import org.videolan.libvlc.LibVlcUtil;
import org.videolan.libvlc.Media;
import org.videolan.vlc.util.VLCInstance;
import org.videolan.vlc.util.WeakHandler;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity  implements IVideoPlayer{

	String TAG = "Lee";
	private SurfaceView  surfaceView;
	private SurfaceHolder surfaceHolder;


	private String mLocation;
	private LibVLC mLibVLC;
	private int savedIndexPosition = -1;
	private int mLastAudioTrack = -1;
	private int mLastSpuTrack = -2;
	private AudioManager mAudioManager;
        private int mAudioMax;
        private SharedPreferences mSettings;
        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 boolean mIsPlaying;
        private int mVideoHeight;
    <span style="white-space:pre">	</span>private int mVideoWidth;
    <span style="white-space:pre">	</span>private int mSarNum;
    <span style="white-space:pre">	</span>private int mSarDen;
    <span style="white-space:pre">	</span>private int mSurfaceAlign;
    <span style="white-space:pre">	</span>private static final int OVERLAY_TIMEOUT = 4000;
    <span style="white-space:pre">	</span>private static final int OVERLAY_INFINITE = 3600000;
    <span style="white-space:pre">	</span>private static final int FADE_OUT = 1;
    <span style="white-space:pre">	</span>private static final int SHOW_PROGRESS = 2;
    <span style="white-space:pre">	</span>private static final int SURFACE_SIZE = 3;
    <span style="white-space:pre">	</span>private static final int AUDIO_SERVICE_CONNECTION_SUCCESS = 5;
    <span style="white-space:pre">	</span>private static final int AUDIO_SERVICE_CONNECTION_FAILED = 6;
    <span style="white-space:pre">	</span>private static final int FADE_OUT_INFO = 4;
    <span style="white-space:pre">	</span>private EventHandler eventandler;
        /*socket 连接控制部分*/
	private Socket clientSocket = null;
	private OutputStream outStream = null;
    <span style="white-space:pre">	</span>int x = 0;
    <span style="white-space:pre">	</span>int y = 0;
    <span style="white-space:pre">	</span>int action = -1;
    <span style="white-space:pre">	</span>int hostw = 0;
    <span style="white-space:pre">	</span>int hosth = 0;
    <span style="white-space:pre">	</span>int surfx = 0;
    <span style="white-space:pre">	</span>int surfy = 0;
    <span style="white-space:pre">	</span>byte[] msgBuffer = null;
    <span style="white-space:pre">	</span>String out = null;
        boolean change = false;
        boolean connected = false;
        DisplayMetrics dm = null;
        public static Context instance;
        private SendThread mSendThread = null;

      <span style="white-space:pre">	</span>private String ip_adress;

        private EditText ip;
    <span style="white-space:pre">	</span>private Button con;


	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub

		x = (int) ev.getX() - surfx;
		y = (int) ev.getY() - surfy;
		action = ev.getAction();
		if ( x < 0 || y < 0 || x > hostw || y > hosth) {
			change = false;
		} else
			change = true;
        String out = Integer.toString(hostw) + ";"
		                     + Integer.toString(hosth) + ";"
        		             + Integer.toString(action) + ";"
		                     + Integer.toString(x) + ";"
        		             + Integer.toString(y) ;
		try {
			msgBuffer = out.getBytes("utf-8");
			if(change  && connected){
				outStream = clientSocket.getOutputStream();
		        outStream.write(msgBuffer);
		        Log.e("Send", "Send msg:" + out);;
			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return super.dispatchTouchEvent(ev);
	}




	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		instance = this;

		final InputMethodManager inputmanger = (InputMethodManager) getSystemService(instance.INPUT_METHOD_SERVICE);
		ip = (EditText) findViewById(R.id.ip);
		con = (Button) findViewById(R.id.con);
		dm = new DisplayMetrics();


		surfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
		surfaceHolder = surfaceView.getHolder();
		surfaceHolder.addCallback(mSurfaceCallback);
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);


		surfaceView.setKeepScreenOn(true);
	        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
	        int pitch;
	        String chroma = pref.getString("chroma_format", "");
	        if(LibVlcUtil.isGingerbreadOrLater() && chroma.equals("YV12")) {
	             surfaceHolder.setFormat(ImageFormat.YV12);
	             pitch = ImageFormat.getBitsPerPixel(ImageFormat.YV12) / 8;
	        } else if (chroma.equals("RV16")) {
	            surfaceHolder.setFormat(PixelFormat.RGB_565);
	            PixelFormat info = new PixelFormat();
	            PixelFormat.getPixelFormatInfo(PixelFormat.RGB_565, info);
	            pitch = info.bytesPerPixel;
	        } else {
	            surfaceHolder.setFormat(PixelFormat.RGBX_8888);
	            PixelFormat info = new PixelFormat();
	            PixelFormat.getPixelFormatInfo(PixelFormat.RGBX_8888, info);
	            pitch = info.bytesPerPixel;
	        }
	        mSurfaceAlign = 16 / pitch - 1;
	        try {
	            mLibVLC = VLCInstance.getLibVlcInstance();
	        } catch (LibVlcException e) {
	            Log.i(TAG, "LibVLC.getInstance() error:"+e.toString());
	            e.printStackTrace();
	            return ;
	        }

	        mLibVLC.eventVideoPlayerActivityCreated(true);

	        eventandler = EventHandler.getInstance();
	        eventandler.addHandler(mEventHandler);

       con.setOnClickListener(new Button.OnClickListener(){

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			ip_adress = ip.getText().toString();
            <span style="white-space:pre">		</span>mSendThread = new SendThread();
            <span style="white-space:pre">		</span>mSendThread.start();
			inputmanger.hideSoftInputFromWindow(ip.getWindowToken(), 0);

		}


       });

       ip.setOnEditorActionListener(new EditText.OnEditorActionListener() {

		@Override
		public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
			// TODO Auto-generated method stub
			if (actionId == EditorInfo.IME_ACTION_DONE) {
				inputmanger.hideSoftInputFromWindow(ip.getWindowToken(), 0);
				ip_adress = ip.getText().toString();
				mLocation = "rtsp://" + ip_adress + ":8554/h263ESVideoTest";
				mSendThread = new SendThread();
				mSendThread.start();

				return true;
			}
			return false;
		}


	});

	}


	@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 boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}


    public class SendThread extends Thread
    {
 	   @Override
		public void run() {
 		   try {
 			   clientSocket = new Socket(ip_adress,8888);
 			   Log.e(TAG, "连接成功");
                connected = true;
                con.setClickable(false);
 		   } catch (UnknownHostException e) {
 			   // TODO Auto-generated catch block
 			   connected = false;
 			   e.printStackTrace();
 		   } catch (IOException e) {
 			   // TODO Auto-generated catch block
 			   connected = false;
 			   e.printStackTrace();
 		   }

 	       String path = "rtsp://172.16.32.14:8554/h263ESVideoTest";
 	       String rtspurl = "rtsp://"  +  ip_adress + ":8554/h263ESVideoTest";
// 					pathUri = LibVLC.getInstance().nativeToURI(path);
// 					mLocation = LibVLC.PathToURI("/sdcard/boot.mp4");
// 					Log.e(TAG, "start rtsp");
		   if (connected) {
			mLibVLC.setMediaList();
		        mLibVLC.getMediaList().add(new Media(mLibVLC, rtspurl));
		        savedIndexPosition = mLibVLC.getMediaList().size() - 1;
		        mLibVLC.playIndex(savedIndexPosition);
		   }
 	   };
    }
    private final VideoEventHandler mEventHandler = new VideoEventHandler(this);
    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
	public void setSurfaceSize(int width, int height, int visible_width,
			int visible_height, int sar_num, int sar_den) {
		// TODO Auto-generated method stub
		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 SurfaceHolder.Callback mSurfaceCallback = new Callback() {
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            if(format == PixelFormat.RGBX_8888)
                Log.d(TAG, "Pixel format is RGBX_8888");
            else if(format == PixelFormat.RGB_565)
                Log.d(TAG, "Pixel format is RGB_565");
            else if(format == ImageFormat.YV12)
                Log.d(TAG, "Pixel format is YV12");
            else
                Log.d(TAG, "Pixel format is other/unknown");
            if(mLibVLC != null){
            	mLibVLC.attachSurface(surfaceHolder.getSurface(), MainActivity.this);
            }
            Log.e(TAG, "Surface changed");
            int xy[] = new int[2] ;
            surfaceView.getLocationOnScreen(xy);
            Log.e(TAG, "x: " + xy[0] + ", Y: " + xy[1]);
            surfx = xy[0];
            surfy = xy[1];
            hosth = surfaceView.getHeight();
            hostw = surfaceView.getWidth();

        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            if(mLibVLC != null)
                mLibVLC.detachSurface();
        }
    };

    public static Context getMainContex() {
        return  instance;
    }


 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 EventHandler.MediaPlayerPlaying:
                 Log.i(TAG, "MediaPlayerPlaying");
                 mIsPlaying = true;
                 break;
             case EventHandler.MediaPlayerPaused:
                 Log.i(TAG, "MediaPlayerPaused");
                 mIsPlaying = false;
                 break;
             case EventHandler.MediaPlayerStopped:
                 Log.i(TAG, "MediaPlayerStopped");
                 mIsPlaying = false;
                 break;
             case EventHandler.MediaPlayerEndReached:
                 Log.i(TAG, "MediaPlayerEndReached");
                 break;
             case EventHandler.MediaPlayerVout:
                 break;
             default:
                 Log.e(TAG, String.format("Event not handled (0x%x)", msg.getData().getInt("event")));
                 break;
         }
            super.handleMessage(msg);
        }
    }

 private void changeSurfaceSize() {
     // get screen size
     int dw = surfaceView.getWidth();
     int dh = surfaceView.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
     surfaceHolder.setFixedSize(alignedWidth, mVideoHeight);

     // set display size
     LayoutParams lp = surfaceView.getLayoutParams();
     lp.width = dw * alignedWidth / mVideoWidth;
     lp.height = dh;
     surfaceView.setLayoutParams(lp);
     surfaceView.invalidate();
 }

}

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

2.

本Demo实际与ARM-V7a编译的

String path = "rtsp://172.16.32.14:8554/h263ESVideoTest"; 是网络串流地址

如果播放本地视频请使用

mLocation = LibVLC.PathToURI("/sdcard/boot.mp4");转义为VLC可识别的Uri


3.具体的源码可参考
1.http://download.csdn.net/detail/qq410942197/8346919

 另:本demo还实现了向服务端同步本地的touch事件
        客户端可参考https://github.com/deeplee/VLC-Demo
        服务端可参考:https://github.com/deeplee/SocketServer

你可能感兴趣的:(NDK,移植,android应用,VLC,libvlcjni.so)