2009 年1 月,工信部宣布3G 牌照的正式发放,自此运营商全面布局各类业务竞争尤其激烈,3G 安防业务(手机视频)也包含其中,应该说手机视频监控业务是3G 时代的亮点业务,也是运营商炒作最热的亮点业务之一。然而,从消费和生活习惯来说,国内真正使用手机视频通话和手机电视的人群并不多,因此,手机视频监控受到运营商们的一致青睐,甚至被称为3G 业务中的“杀手级”业务!由于三大运营商有着巨大的客户资源,其推动的手机视频监控的业务得到了众多安防企业的追捧,各个安防厂商都希望在提供分得一杯羹。
目前国内很多的安防厂商都开发了自己的监控软件,但主要集中在windows mobile 和Symbian平台上面。Android手机系统自推出以来得到了很多手机厂商的支持和相应,Android系统得到了快速发展,而国内最大的移动运营商中国移动也推出了基于Android的操作系统OPhone。本文主要介绍如何在OPhone上实现视频监控。
登陆界面主要完成相关信息的输入,提供了记忆功能,方便用户快捷输入。
(1)图片替换
图片可以替换成其他的LOGO,方便不同的定制需求。只需修改android:background参数,指定相应的文件即可
<ImageView
android:id="@+id/ImageView01"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
android:layout_height="150dip"
android:background="@drawable/logo"
></ImageView>
(2)界面组织
界面编排采用了LinearLayout嵌套。使用LinearLayout中android:orientation参数进行横竖组织。
整体的XML布局采用了vertical方式
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"></LinearLayout>
在局部需要水平对齐的地方使用了horizontal方式
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" xmlns:android="http://schemas.android.com/apk/res/android"></LinearLayout>
(3)LinearLayout 和AbsoluteLayout 区别
Android 的UI 布局以Layout 作为容器,而LinearLayout和AbsoluteLayout 是两种最为常见的布局方式。LinearLayout以水平和垂直进行定位,采用的是相对坐标,而AbsoluteLayout 采用的是绝对坐标。如果要想实现软件界面在不同的手机上做到自适应,应该使用LinearLayout进行定位,避免使用AbsoluteLayout .

主界面
主界面采用了现在手机流行的九宫格布局方式。九宫格的布局看起来比较复杂,但是由于系统提供了GridView控件,使得实现起来变得非常简单。
GridView的XML文件布局如下
<GridView android:id="@+id/GridView01" android:layout_width="fill_parent" android:layout_height="fill_parent" android:numColumns="3" android:verticalSpacing="45dp" android:horizontalSpacing="35dp" android:columnWidth="80dp" android:gravity="center" android:layout_marginTop="10dip" android:layout_marginBottom="10dip" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:focusable="false"></GridView>
为GridView添加相应函数:
- gridview.setOnItemClickListener(new ItemClickListener());
-
- class ItemClickListener implements OnItemClickListener {
- public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
- {
- switch(arg2) {
- case 0: break;
- case 1: break;
- case 2: break;
- case 3: break;
- case 4: break;
- case 5: break;
- case 6: break;
- case 7: break;
- case 8: break;
- }
- }
- }
为switch的分支提供不同的响应函数即可。九个自界面均采用了activity进行显示, 需要在AndroidManifest.xml添加不同的activity, 否则运行的时候会出现异常. 由于笔者对Android平台开发不是很熟悉,在这个地方也花费了不少的时间。
由于篇幅和界面的相关性, 下面主要对现场界面和帮助界面进行说明,其他的不再重复

现场界面
现场界面是整个工程当中最关键的部分,也是工作量最大的部分。主要包含了网络协议实现,视频的解码, 视频的显示,音频的解码,音频的采集和播放,录像文件的读写和界面控制逻辑模块。
网络协议部分主要是用了TCP协议。在使用OPhone平台使用网络连接时, 需要在AndroidManifest.xml文件当中添加网络访问的能力,<uses-permission android:name="android.permission.INTERNET"></uses-permission>,否则会报异常错误。
下面介绍下在客户端在OPhone平台下用Socket连接数据通信的过程
(1)创建socket连接
m_Socket = new Socket(“192.168.3.123”,”8002”);
(2)获取Socket上的输入输出流,进行数据的收发
InputStream m_Reader = m_Socket.getInputStream();
(3)关闭连接
m_Socket.shutdownInput(); m_Socket.shutdownOutput(); m_Socket.close();
建议在步骤二完成后,创建一个单独的线程用于数据的接收,可以避免因长时间接收不到数据造成的阻塞现象。
- Thread thread = new Thread(null, doBackgroundThreadProcessing, "Background");
- thread.start();
-
- public Runnable doBackgroundThreadProcessing = new Runnable()
- {
- public void run() {
- backgroundThreadProcessing();
- }
- };
-
- public void backgroundThreadProcessing() {
- while(isSocketRunning) {
- try {
-
- int iReadLenght = m_Reader.read(test);
- } catch(Exception e) {
- e.printStackTrace();
- break;
- }
- }
- System.out.println("Socket Client Exit");
- }
视频和音频的解码, 录像文件的读写主要是采用了Java的JNI技术,移植了相关的库文件。
解码库的移植可以参考OPhone开*坛上的一篇《H264解码器移植到OPhone》的文章。由于以前在Wince和Symbian平台上均开发过类似的解码库, 所以不用在移植解码库上花费时间。 如果没有相关的解码库,可以参考ffmpeg项目中的libavcodec进行移植。 这里好像一笔带过, 但相信做过解码库移植的朋友肯定能体会其中的滋味, 呵呵。
这里给大家提供几个JNI当中常见的类型转换,避免在相同的问题上浪费时间。
(1). Char*转jbyteArray
- Thread thread = new Thread(null, doBackgroundThreadProcessing, "Background");
- thread.start();
-
- public Runnable doBackgroundThreadProcessing = new Runnable()
- {
- public void run()
- {
- backgroundThreadProcessing();
- }
- };
-
- public void backgroundThreadProcessing() {
- while(isSocketRunning) {
- try {
-
- int iReadLenght = m_Reader.read(test);
-
- } catch(Exception e) {
- e.printStackTrace(); break;
- }
- }
- System.out.println("Socket Client Exit");
- }
(2). jbyteArray转char*
- char * strBuffer = NULL;
- jsize len = env->GetArrayLength(pInBuffer);
- jbyte *arrayBody = env->GetByteArrayElements(pInBuffer, 0);
- if(len>0) {
- strBuffer = new char[len + 1];
- memcpy(strBuffer, arrayBody, len);
- strBuffer[len] = 0;
- }
在Wince和Symbian平台上, 视频的现实可以直接通过写屏幕的方式实现, 效率更高。但是在搜索了相关的信息后,发现在OPhone上没有提供写屏的相关函数,所以采用了贴图的方式, 即将解码完的RGB565的数据写成BMP, 然后再ImageView上显示, 效率并没有先前担心的那么差, 可以接受。
由于OPhone平台只允许在主线程中调用相关View的方法来更新界面。如果返回结果在新线程中获得,那么必须借助Handler来更新界面。所以视频的现实也采用了Handler来刷新显示。
- Handler myhandle = new Handler() {
- public void handleMessage(Message msg)
- {
- };
如果要全屏显示, 主要将bitmap进行缩放至屏幕大小,并进行90度的旋转即可
帮助界面主要采用了一个WebView显示一个网页。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<WebView
android:id="@+id/web"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Help"
/>
</LinearLayout>
显示一个单纯的文本的网页很简单,只需将help.html放到res目录的Raw文件夹下面,按下面的代码即可完成调用:
- WebView web = (WebView)findViewById(R.id.web);
- final String mimetype = "text/html";
- final String encoding = "UTF-8";
-
- String htmldata = "<html><body>boo</body></html>";
- {
- String data = loadResToString(R.raw.help, this);
- if (data != null) htmldata = data;
- }
-
- web.loadDataWithBaseURL("fake://not/needed",htmldata,mimetype, encoding,"");
但是如果要显示带图片的网页,就必须将网页相关的图片放到assets目录下, 并将help.html链接图片的地方改为file:///android_asset/xxx.JPG即可
