调用VLCjni.so播放rtsp视频流并录制成mp4文件

项目在调用VLCjni.so的过程中遇到了不少麻烦,最终成功了,所以写一篇学习笔记,今天是感恩节,感谢这些天自己在无数次失望后都没有放弃,哈哈。
先写一下过程吧:
1.包放进自己的项目里
这里写图片描述

2.启动播放界面

try {
                        // LibVLC.init(getApplicationContext());
                        EventHandler em = EventHandler.getInstance();
                        em.addHandler(handler);

                        mLibVLC = Util.getLibVlcInstance();

                        if (mLibVLC != null) {


                          String pathUri = "rtsp://192.168.56.1:554/CH1";                          
                            mLibVLC.playMyMRL(pathUri);
                        }

                    } catch (LibVlcException e) {
                        e.printStackTrace();
                    }

通过启动了一个handler来调用播放界面

Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            Log.d(TAG, "Event = " + msg.getData().getInt("event"));
            switch (msg.getData().getInt("event")) {
            case EventHandler.MediaPlayerPlaying:

            case EventHandler.MediaPlayerPaused:

                break;
            case EventHandler.MediaPlayerStopped:

                break;
            case EventHandler.MediaPlayerEndReached:

                break;
            case EventHandler.MediaPlayerVout:
                if (msg.getData().getInt("data") > 0) {
                    Intent intent = new Intent();
                    intent.setClass(getApplicationContext(),
                            MakeVideo3Activity.class);
                    startActivity(intent);
                    MainActivity.this.finish();
                }
                break;
            case EventHandler.MediaPlayerPositionChanged:
                break;
            case EventHandler.MediaPlayerEncounteredError:
                AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
                        .setTitle("提示信息")
                        .setMessage("无法连接到摄像头,请确保手机已经连接到摄像头所在的wifi热点")
                        .setNegativeButton("知道了",
                                new DialogInterface.OnClickListener() {

                                    @Override
                                    public void onClick(DialogInterface dialog,
                                            int which) {
                                        MainActivity.this.finish();
                                    }
                                }).create();
                dialog.setCanceledOnTouchOutside(false);
                dialog.show();
                break;
            default:
                Log.d(TAG, "Event not handled ");
                break;
            }
        }
    };

注意,这里跳转到的MakeVideo3Activity是一个播放器的界面

public class MakeVideo3Activity extends Activity implements OnClickListener,
        IVideoPlayer {

    public final static String TAG = "DEBUG/VideoPlayerActivity";

    private SurfaceHolder surfaceHolder = null;
    private LibVLC mLibVLC = null;

    private int mVideoHeight;
    private int mVideoWidth;
    private int mSarDen;
    private int mSarNum;
    private int mUiVisibility = -1;
    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 String[] mAudioTracks;

    private SurfaceView surfaceView = null;
    private FrameLayout mLayout;
    private TextView mTextTitle;
    private TextView mTextTime;

    private ImageView btnPlayPause;
    private ImageView btnSize;
    private TextView mTextShowInfo;

    private Button snapShot;//截图

    private Button videoRecord;//录像

    private boolean isRunRecording; //录像监听

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.video_player);
        setupView();

        if (Util.isICSOrLater())
            getWindow()
                    .getDecorView()
                    .findViewById(android.R.id.content)
                    .setOnSystemUiVisibilityChangeListener(
                            new OnSystemUiVisibilityChangeListener() {

                                @Override
                                public void onSystemUiVisibilityChange(
                                        int visibility) {
                                    if (visibility == mUiVisibility)
                                        return;
                                    setSurfaceSize(mVideoWidth, mVideoHeight,
                                            mSarNum, mSarDen);
                                    if (visibility == View.SYSTEM_UI_FLAG_VISIBLE) {
                                        Log.d(TAG, "onSystemUiVisibilityChange");
                                    }
                                    mUiVisibility = visibility;
                                }
                            });

        try {
//mLibVLC = LibVLC.getInstance();
//用来获取mLIbVLC的实例,其中会初始化LibVLC,在AndroidManifest.xml中要添加android:name="org.videolan.vlc.VLCApplication"这样程序启动时会调用VLCApplication使其生成实例,不会导致LibVLC.getInstance()出错。
            mLibVLC = LibVLC.getInstance();
            if (mLibVLC != null) {
                EventHandler em = EventHandler.getInstance();
                em.addHandler(eventHandler);
                handler.sendEmptyMessageDelayed(0, 1000);
                ListenRecording();
            }
        } catch (LibVlcException e) {
            e.printStackTrace();
        }



    }



    /**
     * 录像监听
     */
    protected void ListenRecording() {
        isRunRecording = true; 
        new Thread(new Runnable() {

            @Override
            public void run() {
                while(isRunRecording)
                try {

                    if(mLibVLC.videoIsRecording())
                    {
                        handlerRecord.sendEmptyMessage(0);
                    }
                    else
                    {
                        handlerRecord.sendEmptyMessage(1);
                    }
                    Thread.sleep(1*1000);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        super.onResume();
    }


    Handler handlerRecord = new Handler()
    {

        @Override
        public void handleMessage(Message msg) {
            switch(msg.what)
            {
            case 0:
                videoRecord.setText("停止录像"); 
                break;
            case 1:
                videoRecord.setText("开始录像");
                break;
            case 2:
                break;

            }
            super.handleMessage(msg);
        }

    };


    /**
     * 初始化组件
     */
    private void setupView() {
        surfaceView = (SurfaceView) findViewById(R.id.main_surface);
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.setFormat(PixelFormat.RGBX_8888);
        surfaceHolder.addCallback(mSurfaceCallback);
        mLayout = (FrameLayout) findViewById(R.id.video_player_overlay);
        mTextTitle = (TextView) findViewById(R.id.video_player_title);

        btnPlayPause = (ImageView) findViewById(R.id.video_player_playpause);
        btnSize = (ImageView) findViewById(R.id.video_player_size);
        mTextTime = (TextView) findViewById(R.id.video_player_time);
        mTextShowInfo = (TextView) findViewById(R.id.video_player_showinfo);


        btnPlayPause.setOnClickListener(this);
        btnSize.setOnClickListener(this);

        snapShot = (Button) findViewById(R.id.snapShot);
        snapShot.setOnClickListener(this); 

        videoRecord = (Button) findViewById(R.id.videoRecord);
        videoRecord.setOnClickListener(this); 

        mTextTitle.setText(getIntent().getStringExtra("name"));

        pathIsExist();
    }


    @Override
    public void onClick(View v) {

        switch (v.getId()) {
        case R.id.video_player_playpause:
            if (mLibVLC.isPlaying()) {
                mLibVLC.pause();
                btnPlayPause.setImageResource(R.drawable.ic_play_selector);
            } else {
                mLibVLC.play();
                btnPlayPause.setImageResource(R.drawable.ic_pause_selector);
            }

            break;
        case R.id.video_player_size:
            if (mCurrentSize < SURFACE_ORIGINAL) {
                mCurrentSize++;
            } else {
                mCurrentSize = 0;
            }
            changeSurfaceSize();
            break;

        case R.id.snapShot:
            snapShot();
            break;


        case R.id.videoRecord:
            videoRecord();
            break;

        }

    }


    /**
     * 路径是否存在  不存在则创建
     */
    private void pathIsExist()
    {
        File file = new File(BitmapUtils.getSDPath()+"/aaa/capture/") ;
        if(!file.exists())
            file.mkdirs();

       File file1 = new File(BitmapUtils.getSDPath()+"/aaa/video/") ;
        if(!file1.exists())
            file1.mkdirs();
    }


    /**
     * 截图
     */
    private void snapShot()
    {
        try {
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
            String name = df.format(new Date());
            name = BitmapUtils.getSDPath()+"/aaa/capture/"+name+".png";
            File file = new File(name);
            if(!file.exists())
                file.createNewFile();
            if(mLibVLC.takeSnapShot(name, 640, 360)) 
            {
            Toast.makeText(getApplicationContext(), "已保存", 1000).show();
            }
            else
            {
                Toast.makeText(getApplicationContext(), "截图失败", 1000).show();
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }



    /**
     * 录像和停止录像
     */
    private void videoRecord()
    {
        try {

            if(mLibVLC.videoIsRecording())
            {
                if(mLibVLC.videoRecordStop()) 
                {
                Toast.makeText(getApplicationContext(), "停止录像", 1000).show();
                }
                else
                {
                    Toast.makeText(getApplicationContext(), "停止录像失败", 1000).show();
                }
            }
            else
            {
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
                String name = df.format(new Date());
            if(mLibVLC.videoRecordStart(BitmapUtils.getSDPath()+"/aaa/video/"+name)) 
            {
            Toast.makeText(getApplicationContext(), "开始录像", 1000).show();
            }
            else
            {
                Toast.makeText(getApplicationContext(), "开始录像失败", 1000).show();
            }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }





    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int time = (int) mLibVLC.getTime();
            int length = (int) mLibVLC.getLength();
            showVideoTime(time, length);
            handler.sendEmptyMessageDelayed(0, 1000);
        }
    };

    private void showVideoTime(int t, int l) {
        mTextTime.setText(millisToString(t));
    }



    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        setSurfaceSize(mVideoWidth, mVideoHeight, mSarNum, mSarDen);
        super.onConfigurationChanged(newConfig);
    }

    /**
     * attach and disattach surface to the lib
     */
    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");
            mLibVLC.attachSurface(holder.getSurface(),
                    MakeVideo3Activity.this);
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            mLibVLC.detachSurface();
        }
    };

    public final Handler mHandler = new VideoPlayerHandler(this);

    private static class VideoPlayerHandler extends
            WeakHandler<MakeVideo3Activity> {
        public VideoPlayerHandler(MakeVideo3Activity owner) {
            super(owner);
        }

        @Override
        public void handleMessage(Message msg) {
            MakeVideo3Activity activity = getOwner();
            if (activity == null) // WeakReference could be GC'ed early
                return;

            switch (msg.what) {
            case SURFACE_SIZE:
                activity.changeSurfaceSize();
                break;
            }
        }
    };

    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;
        }
        if (dw * dh == 0)
            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;

        // // calculate aspect ratio
        // double ar = (double) mVideoWidth / (double) mVideoHeight;
        // // calculate display aspect ratio
        // double dar = (double) dw / (double) dh;

        switch (mCurrentSize) {
        case SURFACE_BEST_FIT:
            mTextShowInfo.setText(R.string.video_player_best_fit);
            if (dar < ar)
                dh = (int) (dw / ar);
            else
                dw = (int) (dh * ar);
            break;
        case SURFACE_FIT_HORIZONTAL:
            mTextShowInfo.setText(R.string.video_player_fit_horizontal);
            dh = (int) (dw / ar);
            break;
        case SURFACE_FIT_VERTICAL:
            mTextShowInfo.setText(R.string.video_player_fit_vertical);
            dw = (int) (dh * ar);
            break;
        case SURFACE_FILL:
            break;
        case SURFACE_16_9:
            mTextShowInfo.setText(R.string.video_player_16x9);
            ar = 16.0 / 9.0;
            if (dar < ar)
                dh = (int) (dw / ar);
            else
                dw = (int) (dh * ar);
            break;
        case SURFACE_4_3:
            mTextShowInfo.setText(R.string.video_player_4x3);
            ar = 4.0 / 3.0;
            if (dar < ar)
                dh = (int) (dw / ar);
            else
                dw = (int) (dh * ar);
            break;
        case SURFACE_ORIGINAL:
            mTextShowInfo.setText(R.string.video_player_original);
            dh = mVideoHeight;
            dw = mVideoWidth;
            break;
        }

        surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight);
        LayoutParams lp = surfaceView.getLayoutParams();
        lp.width = dw;
        lp.height = dh;
        surfaceView.setLayoutParams(lp);
        surfaceView.invalidate();
    }

    private final Handler eventHandler = new VideoPlayerEventHandler(this);

    private static class VideoPlayerEventHandler extends
            WeakHandler<MakeVideo3Activity> {
        public VideoPlayerEventHandler(MakeVideo3Activity owner) {
            super(owner);
        }

        @Override
        public void handleMessage(Message msg) {
            MakeVideo3Activity activity = getOwner();
            if (activity == null)
                return;
            Log.d(TAG, "Event = "+msg.getData().getInt("event"));
            switch (msg.getData().getInt("event")) {
            case EventHandler.MediaPlayerPlaying:
                Log.i(TAG, "MediaPlayerPlaying");
                break;
            case EventHandler.MediaPlayerPaused:
                Log.i(TAG, "MediaPlayerPaused");
                break;
            case EventHandler.MediaPlayerStopped:
                Log.i(TAG, "MediaPlayerStopped");
                break;
            case EventHandler.MediaPlayerEndReached:
                Log.i(TAG, "MediaPlayerEndReached");
                activity.finish();
                break;
            case EventHandler.MediaPlayerVout:
                activity.finish();
                break;
            default:
                Log.d(TAG, "Event not handled");
                break;
            }
            // activity.updateOverlayPausePlay();
        }
    }

    @Override
    protected void onDestroy() {
        if (mLibVLC != null) {
            mLibVLC.stop();
        }

        isRunRecording = false;
        EventHandler em = EventHandler.getInstance();
        em.removeHandler(eventHandler);

        super.onDestroy();
    };

    /**
     * Convert time to a string
     * 
     * @param millis
     *            e.g.time/length from file
     * @return formated string (hh:)mm:ss
     */
    public static String millisToString(long millis) {
        boolean negative = millis < 0;
        millis = java.lang.Math.abs(millis);

        millis /= 1000;
        int sec = (int) (millis % 60);
        millis /= 60;
        int min = (int) (millis % 60);
        millis /= 60;
        int hours = (int) millis;

        String time;
        DecimalFormat format = (DecimalFormat) NumberFormat
                .getInstance(Locale.US);
        format.applyPattern("00");
        if (millis > 0) {
            time = (negative ? "-" : "") + hours + ":" + format.format(min)
                    + ":" + format.format(sec);
        } else {
            time = (negative ? "-" : "") + min + ":" + format.format(sec);
        }
        return time;
    }

    public void setSurfaceSize(int width, int height, int sar_num, int sar_den) {
        if (width * height == 0)
            return;

        mVideoHeight = height;
        mVideoWidth = width;
        mSarNum = sar_num;
        mSarDen = sar_den;
        Message msg = mHandler.obtainMessage(SURFACE_SIZE);
        mHandler.sendMessage(msg);
    }

    @Override
    public void setSurfaceSize(int width, int height, int visible_width,
            int visible_height, int sar_num, int sar_den) {
        mVideoHeight = height;
        mVideoWidth = width;
        mSarNum = sar_num;
        mSarDen = sar_den;
        Message msg = mHandler.obtainMessage(SURFACE_SIZE);
        mHandler.sendMessage(msg);
    }

}

这些都是网上找到的东西,直接拿过来稍微改下播放地址就好,下面的就是关键了

4.修改lib库
这里写图片描述
关于vlc播放的四个库只能放进armeabi里面,之前我放进arm64里面结果就一直加载出错,所以直接把arm64这个文件夹给删掉了,具体原因还需要学习。

5.修改APPLICATION里的内容

//VLC需要
    @Override
    public void onLowMemory() {
        super.onLowMemory();
        Log.w(TAG, "System is running low on memory");

        BitmapCache.getInstance().clear();
    }
    public static Context getAppContext()
    {
        return instance;
    }
    /**
     * @return the main resources from the Application
     */
    public static Resources getAppResources()
    {
        if(instance == null) return null;
        return instance.getResources();
    }




    //以上是VLC

要在项目原有的APPLICATION里添加进这些内容,不添加会闪退的,因为调用VLC的实例时会使用,这样才能保证运行成功。

问题

vlcjni.so库为什么不能放进arm64里面?

如果放进arm64就会出现这样的错误

而且就算没有放进arm64文件夹里面,只要项目中存在arm64依然不能运行,还是会报加载.so的错误。原因在网上查到:
a.开发中出现找不到.so不对的Bug,原因是测试机是64位的,而arm64-v8a中的.so是32位的。
配置生成arm64-v8a的.so文件:
在jni/Application.mk中写入:APP_ABI := armeabi armeabi-v7a arm64-v8a,重新编译就可以了,如果没有这个文件就在jni目录下新建一个。
原博客链接
里面讲了很多关于动态库的知识。

b.通过百度查到知乎有一段关于arm64-v8a的解释:

arm64-v8a是可以向下兼容的,但前提是你的项目里面没有arm64-v8a的文件夹,如果你有两个文件夹armeabi和arm64-v8a,两个文件夹,armeabi里面有a.so 和 b.so,arm64-v8a里面只有a.so,那么arm64-v8a的手机在用到b的时候发现有arm64-v8a的文件夹,发现里面没有b.so,就报错了,所以这个时候删掉arm64-v8a文件夹,这个时候手机发现没有适配arm64-v8a,就会直接去找armeabi的so库,所以要么你别加arm64-v8a,要么armeabi里面有的so库,arm64-v8a里面也必须有
原博客链接

安卓中application的作用?

a.百度知道里的回答:
Application和Activity,Service一样是Android框架的一个系统组件,当Android程序启动时系统会创建一个Application对象,用来存储系统的一些信息。
Android系统自动会为每个程序运行时创建一个Application类的对象且只创建一个,所以Application可以说是单例(singleton)模式的一个类。
通常我们是不需要指定一个Application的,系统会自动帮我们创建,如果需要创建自己的Application,那也很简单!创建一个类继承Application并在AndroidManifest.xml文件中的application标签中进行注册(只需要给application标签增加name属性,并添加自己的 Application的名字即可)。
启动Application时,系统会创建一个PID,即进程ID,所有的Activity都会在此进程上运行。那么我们在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,我们在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变。

猜你喜欢

转载自blog.csdn.net/ulike_mfy/article/details/53317846