基于安卓平台,客户端视频监控的实现(二)

来源:互联网 发布:yy免费头像源码和制作 编辑:程序博客网 时间:2024/05/17 09:21

关于如何编译在官方有介绍,网上也有很多教程,不做赘述。
目前博主水平有限,目前主要是分析java部分代码和如何使用解码库。
先看下目录结构:
这里写图片描述

LibVLC是load解码库和so库的,提供调用的方法。

/* Load library before object instantiation */    static {        try {            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)                System.loadLibrary("iomx-gingerbread");            else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2)                System.loadLibrary("iomx-hc");            else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2)                System.loadLibrary("iomx-ics");        } catch (Throwable t) {            Log.w(TAG, "Unable to load the iomx library: " + t);        }        try {            System.loadLibrary("vlcjni");        } catch (UnsatisfiedLinkError ule) {            Log.e(TAG, "Can't load vlcjni library: " + ule);            /// FIXME Alert user            System.exit(1);        } catch (SecurityException se) {            Log.e(TAG, "Encountered a security issue when loading vlcjni library: " + se);            /// FIXME Alert user            System.exit(1);        }    }//调用所有libvlc的方法之前必须进行init操作   /**     * Initialize the libVLC class.     *     * This function must be called before using any libVLC functions.     *     * @throws LibVlcException     */    public void init(Context context) throws LibVlcException {        Log.v(TAG, "Initializing LibVLC");        mDebugLogBuffer = new StringBuffer();        if (!mIsInitialized) {            if(!LibVlcUtil.hasCompatibleCPU(context)) {                Log.e(TAG, LibVlcUtil.getErrorMsg());                throw new LibVlcException();            }            nativeInit();            mMediaList = mPrimaryList = new MediaList(this);            setEventHandler(EventHandler.getInstance());            mIsInitialized = true;        }    }

播放实时视频,只需调用如下方法,传入一个rtps的url

/**     * Play an MRL directly.     *     * @param mrl MRL of the media to play.     */    public void playMyMRL(String mrl) {        // index=-1 will return options from libvlc instance without relying on MediaList        String[] options = new String[0];         mInternalMediaPlayerIndex = 0;        playMRL(mLibVlcInstance, mrl, options);    }

LibVlcUtil 主要是对设备CPU的检查。ABI,CPU_ABI是我们经常都会碰见的,例如我们在Android工程里面的libs库下面就有armeabi-v7a类型的库。这里的ABI是指Application Binary Interface。

  ABI描述了应用程序和操作系统之间,一个应用和它的库之间,或者应用的组成部分之间的低层接口。ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。

  ABI掩盖了各种细节,例如:调用约定(控制着函数的参数如何传送以及如何接受返回值);系统调用的编码和一个应用如何向操作系统进行系统调用;以及在一个完整的操作系统ABI中,对象文件的二进制格式、程序库等等。一个完整的ABI,像Intel二进制兼容标准 (iBCS) ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作体统上运行。其他的 ABI 标准化细节包括 C++ name decoration 和同一个平台上的编译器之间的调用约定,但是不包括跨平台的兼容性。

  简单的说,ABI规范了应用程序对寄存器的使用方法,Call procedure,以及如何进入trap。符合某一平台ABI规范的应用程序就可以在这一平台上运行。这一规范是针对binary,而不是source的。所以同样的高级语言代码,使用不同的toolchain,可以得到符合不同ABI规范的binary。关于这部分,还不明白的,自行Google(我也不是很清楚,免得误人子弟)。

VLCApplication做的事情不多,简单来说,做了两件事。第一个是一个本地配置的初始化,主要是Locale的配置,而且都是针对中文做特别处理,另外就是当系统内存不多的时候,回收一些bitmap的内存。剩下的就是保存一个全局的静态对象供其他类使用。下面给出系统内存低的时候,回收bitmap缓存。代码如下:

 // Are we using advanced debugging - locale?        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);        String p = pref.getString("set_locale", "");        if (p != null && !p.equals("")) {            Locale locale;            // workaround due to region code            if(p.equals("zh-TW")) {                locale = Locale.TRADITIONAL_CHINESE;            } else if(p.startsWith("zh")) {                locale = Locale.CHINA;            } else if(p.equals("pt-BR")) {                locale = new Locale("pt", "BR");            } else if(p.equals("bn-IN") || p.startsWith("bn")) {                locale = new Locale("bn", "IN");            } else {                /**                 * Avoid a crash of                 * java.lang.AssertionError: couldn't initialize LocaleData for locale                 * if the user enters nonsensical region codes.                 */                if(p.contains("-"))                    p = p.substring(0, p.indexOf('-'));                locale = new Locale(p);            }            Locale.setDefault(locale);            Configuration config = new Configuration();            config.locale = locale;            getBaseContext().getResources().updateConfiguration(config,                    getBaseContext().getResources().getDisplayMetrics());        }低内存的时候 /**     * Called when the overall system is running low on memory     */    @Override    public void onLowMemory() {        super.onLowMemory();        Log.w(TAG, "System is running low on memory");        BitmapCache.getInstance().clear();    }

具体的调用:

try {            EventHandler em = EventHandler.getInstance();            em.addHandler(handler);            mLoadingDialog = DialogUtil.createLoadingDialog(MainActivity.this,"正在获取信息,请稍后...");            mLoadingDialog.show();            mLibVLC = Util.getLibVlcInstance();            mLibVLC.init(getApplicationContext());            if (mLibVLC != null) {                String pathUri = "rtsp://192.168.103.39:10554/654321.sdp";//              String pathUri = "http://vod.cntv.lxdns.com/flash/mp4video50/TMS/2016/04/19/f1db9354900946a19a7fefc3bf040593_h264200000nero_aac16.mp4";//              String pathUri = "/storage/emulated/0/DCIM/Camera/VID_20170511_103045.mp4";                 mLibVLC.playMyMRL(pathUri);            }        } catch (LibVlcException e) {            e.printStackTrace();        }

初始化handler,libvlc初始化的时候,往下一层层调用,最后handler将信息返回,调用play播放方法的时候,我也没有搞清楚为什么那么慢,可能和网络有关,还是和服务器那边推流的速度,关于这一点仍然在研究,目前给的措施就是loading加载。
当接收到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:                    break;                case EventHandler.MediaPlayerPaused:                    break;                case EventHandler.MediaPlayerStopped:                    break;                case EventHandler.MediaPlayerEndReached:                    break;                case EventHandler.MediaPlayerVout:                    if (mLoadingDialog != null) {                        mLoadingDialog.dismiss();                        mLoadingDialog = null;                    }                    if (msg.getData().getInt("data") > 0) {                        Intent intent = new Intent();                        intent.setClass(getApplicationContext(),VideoPlayerActivity.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;            }        }    };

EventHandler类里面定义很多常量,根据项目的需求选择:

 /*     * Be sure to subscribe to events you need in the JNI too.     */    //public static final int MediaMetaChanged                = 0;    //public static final int MediaSubItemAdded               = 1;    //public static final int MediaDurationChanged            = 2;    //public static final int MediaParsedChanged              = 3;    //public static final int MediaFreed                      = 4;    //public static final int MediaStateChanged               = 5;    //public static final int MediaPlayerMediaChanged         = 0x100;    //public static final int MediaPlayerNothingSpecial       = 0x101;    //public static final int MediaPlayerOpening              = 0x102;    //public static final int MediaPlayerBuffering            = 0x103;    public static final int MediaPlayerPlaying                = 0x104;    public static final int MediaPlayerPaused                 = 0x105;    public static final int MediaPlayerStopped                = 0x106;    //public static final int MediaPlayerForward              = 0x107;    //public static final int MediaPlayerBackward             = 0x108;    public static final int MediaPlayerEndReached             = 0x109;    public static final int MediaPlayerEncounteredError       = 0x10a;    //public static final int MediaPlayerTimeChanged          = 0x10b;    public static final int MediaPlayerPositionChanged        = 0x10c;    //public static final int MediaPlayerSeekableChanged      = 0x10d;    //public static final int MediaPlayerPausableChanged      = 0x10e;    //public static final int MediaPlayerTitleChanged         = 0x10f;    //public static final int MediaPlayerSnapshotTaken        = 0x110;    //public static final int MediaPlayerLengthChanged        = 0x111;    public static final int MediaPlayerVout                   = 0x112;    //public static final int MediaListItemAdded              = 0x200;    //public static final int MediaListWillAddItem            = 0x201;    //public static final int MediaListItemDeleted            = 0x202;    //public static final int MediaListWillDeleteItem         = 0x203;    //public static final int MediaListViewItemAdded          = 0x300;    //public static final int MediaListViewWillAddItem        = 0x301;    //public static final int MediaListViewItemDeleted        = 0x302;    //public static final int MediaListViewWillDeleteItem     = 0x303;    //public static final int MediaListPlayerPlayed           = 0x400;    //public static final int MediaListPlayerNextItemSet      = 0x401;    //public static final int MediaListPlayerStopped          = 0x402;    //public static final int MediaDiscovererStarted          = 0x500;    //public static final int MediaDiscovererEnded            = 0x501;    //public static final int VlmMediaAdded                   = 0x600;    //public static final int VlmMediaRemoved                 = 0x601;    //public static final int VlmMediaChanged                 = 0x602;    //public static final int VlmMediaInstanceStarted         = 0x603;    //public static final int VlmMediaInstanceStopped         = 0x604;    //public static final int VlmMediaInstanceStatusInit      = 0x605;    //public static final int VlmMediaInstanceStatusOpening   = 0x606;    //public static final int VlmMediaInstanceStatusPlaying   = 0x607;    //public static final int VlmMediaInstanceStatusPause     = 0x608;    //public static final int VlmMediaInstanceStatusEnd       = 0x609;    //public static final int VlmMediaInstanceStatusError     = 0x60a;    public static final int CustomMediaListExpanding          = 0x2000;    public static final int CustomMediaListExpandingEnd       = 0x2001;    public static final int CustomMediaListItemAdded          = 0x2002;    public static final int CustomMediaListItemDeleted        = 0x2003;

但是必须确保jni中存在。其余就是添加移除handler的方法:

public void addHandler(Handler handler) {        if (!mEventHandler.contains(handler))            mEventHandler.add(handler);    }    public void removeHandler(Handler handler) {        mEventHandler.remove(handler);    }    /** This method is called by a native thread **/    public void callback(int event, Bundle b) {        b.putInt("event", event);        for (int i = 0; i < mEventHandler.size(); i++) {            Message msg = Message.obtain();            msg.setData(b);            mEventHandler.get(i).sendMessage(msg);        }    }

最后就是VideoPlayerActivity,视频播放的页面。其实我也不太会写博客,总想记录一些,学习一些,也知道自己写的不好,基本都是复制粘贴,只是遇到的一些坑能够写出来,让大家少走弯路,还是希望大神可以指点指点。不喜勿喷。

阅读全文
0 0